diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..0a83160bd --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1 @@ +FROM ghcr.io/processone/devcontainer:latest diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..c216cd0c0 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,7 @@ +{ + "name": "ejabberd", + "build": {"dockerfile": "Dockerfile"}, + "extensions": ["erlang-ls.erlang-ls"], + "postCreateCommand": ".devcontainer/prepare-container.sh", + "remoteUser": "vscode" +} diff --git a/.devcontainer/prepare-container.sh b/.devcontainer/prepare-container.sh new file mode 100755 index 000000000..d57a472de --- /dev/null +++ b/.devcontainer/prepare-container.sh @@ -0,0 +1,3 @@ +echo "export PATH=/workspaces/ejabberd/_build/relive:$PATH" >>$HOME/.bashrc +echo "COOKIE" >$HOME/.erlang.cookie +chmod 400 $HOME/.erlang.cookie diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..6abcb6de0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,46 @@ +.git +.win32 +.examples +*.swp +*~ +\#*# +.#* +.edts +*.dump +/Makefile +/config.log +/config.status +/config/releases.exs +/configure +/aclocal.m4 +/*.cache +/deps/ +/.deps-update/ +/ebin/ +/ejabberd.init +/ejabberd.service +/ejabberdctl +/ejabberdctl.example +/rel/ejabberd/ +/rel/overlays/ +/src/eldap_filter_yecc.erl +/vars.config +/dialyzer/ +/test/*.beam +/test/*.ctc +/logs/ +/priv/bin/captcha*sh +/priv/sql +/rel/ejabberd +/_build +/database/ +/.rebar +/rebar.lock +/log/ +Mnesia.nonode@nohost/ +# Binaries created with tools/make-{binaries,installers,packages}: +/ejabberd_*.deb +/ejabberd-*.rpm +/ejabberd-*.run +/ejabberd-*.tar.gz +/.github/container/Dockerfile diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE deleted file mode 100644 index d31eb5bb3..000000000 --- a/.github/ISSUE_TEMPLATE +++ /dev/null @@ -1,15 +0,0 @@ -> What version of ejabberd are you using? - - - -> What operating system (version) are you using? - - - -> How did you install ejabberd (source, package, distribution)? - - - -> What did not work as expected? Are there error messages in the log? What -> was the unexpected behavior? What was the expected result? - diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..d984ed09d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +assignees: '' + +--- + +Before creating a ticket, please consider if this should fit the [discussion forum](https://github.com/processone/ejabberd/discussions) better. + +## Environment + +- ejabberd version: 18.09 +- Erlang version: `erl +V` +- OS: Linux (Debian) +- Installed from: source | distro package | official deb/rpm | official binary installer | other + +## Configuration (only if needed): grep -Ev '^$|^\s*#' ejabberd.yml + +```yaml +loglevel: 4 +... +``` + +## Errors from error.log/crash.log + +No errors + +## Bug description + +Please, give us a precise description (what does not work, what is expected, etc.) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..0ac588c37 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,26 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: Kind:Feature +assignees: '' + +--- + +Before creating a ticket, please consider if this should fit the [discussion forum](https://github.com/processone/ejabberd/discussions) better. + +**Is your feature request related to a problem? Please describe.** + +A clear and concise description of what the problem is. Ex. I'm always frustrated when... + +**Describe the solution you'd like** + +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** + +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** + +Add any other context or screenshots about the feature request here. diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile new file mode 100644 index 000000000..1d238339a --- /dev/null +++ b/.github/container/Dockerfile @@ -0,0 +1,206 @@ +#' Define default build variables +ARG OTP_VSN='27.3.4.3' +ARG ELIXIR_VSN='1.18.4' +ARG UID='9000' +ARG USER='ejabberd' +ARG HOME="opt/$USER" +ARG BUILD_DIR="/$USER" +ARG VERSION='master' + +################################################################################ +#' Compile ejabberdapi +FROM docker.io/golang:1.25-alpine AS api +RUN go install -v \ + github.com/processone/ejabberd-api/cmd/ejabberd@master \ + && mv bin/ejabberd bin/ejabberdapi + +################################################################################ +#' build and install ejabberd directly from source +FROM docker.io/erlang:${OTP_VSN}-alpine AS ejabberd + +RUN apk -U add --no-cache \ + autoconf \ + automake \ + bash \ + build-base \ + curl \ + expat-dev \ + file \ + gd-dev \ + git \ + jpeg-dev \ + libpng-dev \ + libwebp-dev \ + linux-pam-dev \ + openssl-dev \ + sqlite-dev \ + yaml-dev \ + zlib-dev + +ARG ELIXIR_VSN +RUN wget -O - https://github.com/elixir-lang/elixir/archive/v$ELIXIR_VSN.tar.gz \ + | tar -xzf - + +WORKDIR /elixir-$ELIXIR_VSN +ENV ERL_FLAGS="+JPperf true" +RUN make install clean + +RUN mix local.hex --force \ + && mix local.rebar --force + +ARG BUILD_DIR +COPY / $BUILD_DIR/ + +WORKDIR $BUILD_DIR +RUN mv .github/container/ejabberdctl.template . \ + && mv .github/container/ejabberd.yml.example . \ + && ./autogen.sh \ + && ./configure --with-rebar=mix --enable-all \ + && make deps \ + && make rel + +WORKDIR /rootfs +ARG VERSION +ARG HOME +RUN mkdir -p $HOME $HOME-$VERSION \ + && cp -r $BUILD_DIR/_build/prod/rel/ejabberd/* $HOME-$VERSION \ + && mv $HOME-$VERSION/conf $HOME/conf + +RUN cp -p $BUILD_DIR/tools/captcha*.sh $HOME-$VERSION/lib + +RUN find "$HOME-$VERSION/bin" -name 'ejabberd' -delete \ + && find "$HOME-$VERSION/releases" -name 'COOKIE' -delete + +RUN wget -O "$HOME/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' + +#' Prepare ejabberd for runtime +RUN apk -U add --no-cache \ + git \ + libcap \ + openssl + +RUN mkdir -p usr/local/bin $HOME/conf $HOME/database $HOME/logs $HOME/upload + +COPY --from=api /go/bin/ejabberdapi usr/local/bin/ + +RUN if [ ! -d $HOME/.ejabberd-modules ]; \ + then \ + if [ -d $BUILD_DIR/.ejabberd-modules ]; \ + then cp -r $BUILD_DIR/.ejabberd-modules $HOME; \ + else git clone https://github.com/processone/ejabberd-contrib --depth 1 \ + $HOME/.ejabberd-modules/sources/ejabberd-contrib; \ + fi \ + fi + +RUN export PEM=$HOME/conf/server.pem \ + && openssl req -x509 \ + -batch \ + -nodes \ + -newkey rsa:4096 \ + -keyout $PEM \ + -out $PEM \ + -days 3650 \ + -subj "/CN=localhost" + +RUN sed -i 's|^#CTL_OVER_HTTP=|CTL_OVER_HTTP=../|' "$HOME/conf/ejabberdctl.cfg" + +RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \ + && setcap 'cap_net_bind_service=+ep' $(find $home_root_dir -name beam.smp) \ + && echo -e \ + "#!/bin/sh \ + \n[ -z \$ERLANG_NODE_ARG ] && export ERLANG_NODE_ARG=ejabberd@localhost \ + \nexport EMA=\"\$EJABBERD_MACRO_ADMIN\" \ + \nexport HOST=\"\${EJABBERD_MACRO_HOST:-localhost}\" \ + \nif [ -n \"\$EMA\" ] \ + \nthen \ + \n if [ \"\$EMA\" != \"\${EMA%%@*}\" ] \ + \n then \ + \n export USERNAME=\"\${EMA%%@*}\" \ + \n export HOST=\"\${EMA##*@}\" \ + \n else \ + \n export USERNAME=\"\$EMA\" \ + \n export SHOW_WARNING=\"true\" \ + \n fi \ + \nelif [ -n \"\$REGISTER_ADMIN_PASSWORD\" ] \ + \nthen \ + \n export USERNAME=\"admin\" \ + \nelse \ + \n export USERNAME=\"\$(od -A n -N 8 -t x8 /dev/urandom)\" \ + \nfi \ + \nexport EJABBERD_MACRO_ADMIN=\"\$USERNAME@\$HOST\" \ + \n[ -n \"\$SHOW_WARNING\" ] && echo \"WARNING: The EJABBERD_MACRO_ADMIN environment variable was set to '\$EMA', but it should include the host... I'll overwrite it to become '\$EJABBERD_MACRO_ADMIN'.\" \ + \n[ -n \"\$CTL_ON_CREATE\" ] && export SEPARATOR=\";\" \ + \n[ -n \"\$REGISTER_ADMIN_PASSWORD\" ] && export CTL_ON_CREATE=\"register \${EJABBERD_MACRO_ADMIN%%@*} \${EJABBERD_MACRO_ADMIN##*@} \$REGISTER_ADMIN_PASSWORD \$SEPARATOR \$CTL_ON_CREATE\" \ + \nexport CONFIG_DIR=/$HOME/conf \ + \nexport LOGS_DIR=/$HOME/logs \ + \nexport SPOOL_DIR=/$HOME/database \ + \nexec /$(find $home_root_dir -name ejabberdctl) \"\$@\"" \ + > usr/local/bin/ejabberdctl \ + && chmod +x usr/local/bin/* \ + && scanelf --needed --nobanner --format '%n#p' --recursive $home_root_dir \ + | tr ',' '\n' \ + | sort -u \ + | awk 'system("[ -e $home_root_dir" $1 " ]") == 0 { next } { print "so:" $1 }' \ + | sed -e "s|so:libc.so|so:libc.musl-$(uname -m).so.1|" \ + > /tmp/runDeps + +ARG UID +RUN chown -R $UID:$UID $HOME + +RUN cp /rootfs/$HOME-$VERSION/lib/captcha*.sh usr/local/bin/ +RUN mkdir $HOME/sql \ + && find /rootfs/$HOME-$VERSION/lib/ -name *.sql -exec cp {} $HOME/sql \; -exec cp {} $HOME/database \; + +################################################################################ +#' Remove erlang/OTP & rebar3 +FROM docker.io/erlang:${OTP_VSN}-alpine AS runtime +RUN apk del .erlang-rundeps \ + && rm -f $(which rebar3) \ + && find /usr -type d -name 'erlang' -exec rm -rf {} + \ + && find /usr -type l -exec test ! -e {} \; -delete + +#' Update alpine, finalize runtime environment +COPY --from=ejabberd /tmp/runDeps /tmp/runDeps +RUN apk -U upgrade --available --no-cache \ + && apk add --no-cache \ + $(cat /tmp/runDeps) \ + so:libcap.so.2 \ + so:libtdsodbc.so.0 \ + curl \ + tini \ + && rm /tmp/runDeps \ + && ln -fs /usr/lib/libtdsodbc.so.0 /usr/lib/libtdsodbc.so + +ARG USER +ARG UID +ARG HOME +RUN addgroup $USER -g $UID \ + && adduser -s /sbin/nologin -D -u $UID -h /$HOME -G $USER $USER + +RUN ln -fs /usr/local/bin/ /opt/ejabberd/bin +RUN rm -rf /home \ + && ln -fs /opt /home + +################################################################################ +#' Build together production image +FROM scratch +ARG USER +ARG HOME + +COPY --from=runtime / / +COPY --from=ejabberd /rootfs / + +HEALTHCHECK \ + --interval=1m \ + --timeout=5s \ + --start-period=5s \ + --retries=10 \ + CMD ejabberdctl status + +WORKDIR /$HOME +USER $USER +VOLUME ["/$HOME"] +EXPOSE 1880 1883 4369-4399 5210 5222 5269 5280 5443 + +ENTRYPOINT ["/sbin/tini","--","ejabberdctl"] +CMD ["foreground"] diff --git a/.github/container/ejabberd-container-install.bat b/.github/container/ejabberd-container-install.bat new file mode 100755 index 000000000..704011a6d --- /dev/null +++ b/.github/container/ejabberd-container-install.bat @@ -0,0 +1,294 @@ +@echo off + +:: +:: ejabberd container installer for Windows +:: ------------------------------------- +:: v0.4 +:: +:: This batch script downloads an ejabberd container image +:: and setups a docker container to run ejabberd. + +:: +:: 1. Download and install Docker: +:: +:: If you use Windows 10, download Docker Desktop from: +:: https://www.docker.com/ +:: +:: If you use Windows 7 or 8, download Docker Toolbox from: +:: https://github.com/docker/toolbox/releases +:: After installation, run Docker Quickstart Installer +:: + +:: +:: 2. Edit those options: + +:: Directory where your ejabberd deployment files will be installed +:: (configuration, database, logs, ...) +:: +:: In Windows 10 you can configure the path: + +set INSTALL_DIR_WINDOWS10=C:\ejabberd + +:: In older Windows, not configurable, it will be installed in: +:: C:\Users\%USERNAME%\ejabberd + +:: Please enter the desired ejabberd domain name. +:: The domain is the visible attribute that is added to the username +:: to form the Jabber Identifier (for example: user@example.net). +:: This computer must be known on the network with this address name. +:: You can later add more in conf/ejabberd.yml + +set HOST=localhost + +:: Please enter the administrator username for the current +:: ejabberd installation. A Jabber account with this username +:: will be created and granted administrative privileges. +:: Don't use blankspaces in the username. + +set USER=admin + +:: Please provide a password for that new administrator account + +set PASSWORD= + +:: By default this downloads 'latest' ejabberd version, +:: but you can set a specific version, for example '22.05' +:: or the bleeding edge 'master'. See available tags in +:: https://github.com/processone/ejabberd/pkgs/container/ejabberd + +set VERSION=latest + +:: This tells docker what ports ejabberd will use. +:: You can later configure them in conf/ejabberd.yml + +set PORTS=5180 5222 5269 5443 + +:: +:: 3. Now save this script and run it. +:: + +:: +:: 4. When installation is completed: +:: +:: If using Windows 10, open Docker Desktop and you can: +:: +:: - (>) START the ejabberd container +:: - Enter WebAdmin: click the ([->]) OPEN IN BROWSER button +:: - To try ejabberdctl, click the (>_) CLI button, then: ejabberdctl +:: - ([]) STOP the ejabberd container +:: +:: If using an old Windows, open Kitematic and you can: +:: +:: - START the ejabberd container +:: - Open your configuration, logs, ... in Settings > Volumes +:: - Enter WebAdmin in Settings > Hostname/Ports > click on the 5180 port +:: - Try ejabberdctl in EXEC, then: ejabberdctl +:: - STOP the ejabberd container +:: +:: You can delete the container and create it again running this script, +:: the configuration and database are maintained. +:: + +::=============================================================== +:: Check Windows version +:: +::=============================================================== + +set INSTALL_DIR_DOCKER=c/Users/%USERNAME%/ejabberd + +for /f "tokens=4-5 delims=. " %%i in ('ver') do set WVERSION=%%i.%%j +if "%wversion%" == "10.0" ( + echo === Preparing paths to install in Windows 10... + set INSTALL_DIR=%INSTALL_DIR_WINDOWS10% + set VC=-v %INSTALL_DIR_WINDOWS10%\conf:/opt/ejabberd/conf + set VD=-v %INSTALL_DIR_WINDOWS10%\database:/opt/ejabberd/database + set VL=-v %INSTALL_DIR_WINDOWS10%\logs:/opt/ejabberd/logs + set VM=-v %INSTALL_DIR_WINDOWS10%\ejabberd-modules:/opt/ejabberd/.ejabberd-modules + set DOCKERDOWNLOAD="First download and install Docker Desktop from https://www.docker.com/" +) else ( + echo === Preparing paths to install in Windows older than 10... + set INSTALL_DIR=C:\Users\%USERNAME%\ejabberd + set VC=-v "/%INSTALL_DIR_DOCKER%/conf:/opt/ejabberd/conf" + set VD=-v "/%INSTALL_DIR_DOCKER%/database:/opt/ejabberd/database" + set VL=-v "/%INSTALL_DIR_DOCKER%/logs:/opt/ejabberd/logs" + set VM=-v "/%INSTALL_DIR_DOCKER%/ejabberd-modules:/opt/ejabberd/.ejabberd-modules" + set DOCKERDOWNLOAD="First download and install Docker Toolbox from https://github.com/docker/toolbox/releases" +) +set VOLUMES=%VC% %VD% %VL% %VM% + +::=============================================================== +:: Check docker is installed +:: +::=============================================================== + +docker version >NUL +if %ERRORLEVEL% NEQ 0 ( + echo. + echo === ERROR: It seems docker is not installed!!! + echo. + echo %DOCKERDOWNLOAD% + echo === Then try to run this script again. + echo. + pause + exit 1 +) + +::=============================================================== +:: Check install options are correctly set +:: +::=============================================================== + +if [%PASSWORD%]==[] ( + echo. + echo === ERROR: PASSWORD not set!!! + echo. + echo === Please edit this script and set the PASSWORD. + echo === Then try to run this script again. + echo. + pause + exit 1 +) + +::=============================================================== +:: Download Docker image +:: +::=============================================================== + +set IMAGE=ghcr.io/processone/ejabberd:%VERSION% + +echo. +echo === Checking if the '%IMAGE%' container image was already downloaded... +docker image history %IMAGE% >NUL +if %ERRORLEVEL% NEQ 0 ( + echo === The '%IMAGE%' container image was not downloaded yet. + echo. + echo === Downloading the '%IMAGE%' container image, please wait... + docker pull %IMAGE% +) else ( + echo === The '%IMAGE%' container image was already downloaded. +) + +::=============================================================== +:: Create preliminary container +:: +::=============================================================== + +echo. +echo === Checking if the 'ejabberd' container already exists... +docker container logs ejabberd +if %ERRORLEVEL% EQU 0 ( + echo. + echo === The 'ejabberd' container already exists. + echo === Nothing to do, so installation finishes now. + echo === You can go to Docker Desktop and start the 'ejabberd' container. + echo. + pause + exit 1 +) else ( + echo === The 'ejabberd' container doesn't yet exist, + echo === so let's continue the installation process. +) + +echo. +if exist %INSTALL_DIR% ( + echo === The INSTALL_DIR %INSTALL_DIR% already exists. + echo === No need to create the preliminary 'ejabberd-pre' image. +) else ( + echo === The INSTALL_DIR %INSTALL_DIR% doesn't exist. + echo === Let's create the preliminary 'ejabberd-pre' image. + CALL :create-ejabberd-pre +) + +::=============================================================== +:: Create final container +:: +::=============================================================== + +echo. +echo === Creating the final 'ejabberd' container using %IMAGE% image... + +setlocal EnableDelayedExpansion +set PS= +for %%a in (%PORTS%) do ( + set PS=!PS! -p %%a:%%a +) + +docker create --name ejabberd --hostname localhost %PS% %VOLUMES% %IMAGE% + +echo. +echo === Installation completed. +echo. +pause + +EXIT /B %ERRORLEVEL% + +::=============================================================== +:: Function to create preliminary container +:: +::=============================================================== + +:create-ejabberd-pre + +echo. +echo === Creating a preliminary 'ejabberd-pre' container using %IMAGE% image... +docker create --name ejabberd-pre --hostname localhost %IMAGE% + +echo. +echo === Now 'ejabberd-pre' will be started. +docker container start ejabberd-pre + +echo. +echo === Waiting ejabberd to be running... +set /A timeout = 10 +set status=4 +goto :while + +:statusstart +docker exec -it ejabberd-pre ejabberdctl status +goto :statusend + +:while +if %status% GTR 0 ( + echo. + timeout /t 1 /nobreak >NUL + set /A timeout = timeout - 1 + if %timeout% EQU 0 ( + set status=-1 + ) else ( + goto :statusstart + :statusend + set status=%ERRORLEVEL% + ) + goto :while +) + +echo. +echo === Setting a few options... +docker exec -it ejabberd-pre sed -i "s!- localhost!- %HOST%!g" conf/ejabberd.yml +docker exec -it ejabberd-pre sed -i "s!^acl:!acl:\n admin:\n user:\n - \"%USER%@%HOST%\"!g" conf/ejabberd.yml +docker exec -it ejabberd-pre sed -i "s!5280!5180!g" conf/ejabberd.yml +docker exec -it ejabberd-pre sed -i "s!/admin!/!g" conf/ejabberd.yml +docker exec -it ejabberd-pre ejabberdctl reload_config + +echo. +echo === Registering the administrator account... +docker exec -it ejabberd-pre ejabberdctl register %USER% %HOST% %PASSWORD% +docker exec -it ejabberd-pre ejabberdctl stop + +echo. +echo === Copying conf, database, logs... +mkdir %INSTALL_DIR% +mkdir %INSTALL_DIR%\conf +mkdir %INSTALL_DIR%\database +mkdir %INSTALL_DIR%\logs +mkdir %INSTALL_DIR%\ejabberd-modules +docker cp ejabberd-pre:/opt/ejabberd/conf/ %INSTALL_DIR% +docker cp ejabberd-pre:/opt/ejabberd/database/ %INSTALL_DIR% +docker cp ejabberd-pre:/opt/ejabberd/logs/ %INSTALL_DIR% + +echo. +echo === Deleting the preliminary 'ejabberd-pre' container... +docker stop ejabberd-pre +docker rm ejabberd-pre + +EXIT /B 0 diff --git a/.github/container/ejabberd.yml.example b/.github/container/ejabberd.yml.example new file mode 100644 index 000000000..2f63a2b64 --- /dev/null +++ b/.github/container/ejabberd.yml.example @@ -0,0 +1,278 @@ +### +### ejabberd configuration file +### +### The parameters used in this configuration file are explained at +### +### https://docs.ejabberd.im/admin/configuration +### +### The configuration file is written in YAML. +### ******************************************************* +### ******* !!! WARNING !!! ******* +### ******* YAML IS INDENTATION SENSITIVE ******* +### ******* MAKE SURE YOU INDENT SECTIONS CORRECTLY ******* +### ******************************************************* +### Refer to http://en.wikipedia.org/wiki/YAML for the brief description. +### + +define_macro: + HOST: localhost + ## ADMIN: ... # set by /usr/local/bin/ejabberdctl + PORT_C2S: 5222 + PORT_C2S_TLS: 5223 + PORT_S2S: 5269 + PORT_HTTP_TLS: 5443 + PORT_HTTP: 5280 + PORT_BROWSER: 1880 + PORT_STUN: 5478 + PORT_MQTT: 1883 + PORT_PROXY65: 7777 + +hosts: + - HOST + +loglevel: info + +## If you already have certificates, list them here +# certfiles: +# - /etc/letsencrypt/live/domain.tld/fullchain.pem +# - /etc/letsencrypt/live/domain.tld/privkey.pem + +ca_file: /opt/ejabberd/conf/cacert.pem +certfiles: + - /opt/ejabberd/conf/server.pem + +listen: + - + port: PORT_C2S + ip: "::" + module: ejabberd_c2s + max_stanza_size: 262144 + shaper: c2s_shaper + access: c2s + starttls_required: true + - + port: PORT_C2S_TLS + ip: "::" + module: ejabberd_c2s + max_stanza_size: 262144 + shaper: c2s_shaper + access: c2s + tls: true + - + port: PORT_S2S + ip: "::" + module: ejabberd_s2s_in + max_stanza_size: 524288 + shaper: s2s_shaper + - + port: PORT_HTTP_TLS + ip: "::" + module: ejabberd_http + tls: true + request_handlers: + /admin: ejabberd_web_admin + /api: mod_http_api + /bosh: mod_bosh + /captcha: ejabberd_captcha + /upload: mod_http_upload + /ws: ejabberd_http_ws + - + port: PORT_HTTP + ip: "::" + module: ejabberd_http + request_handlers: + /admin: ejabberd_web_admin + /.well-known/acme-challenge: ejabberd_acme + - + port: PORT_BROWSER + ip: "::" + module: ejabberd_http + request_handlers: + /: ejabberd_web_admin + - + port: "unix:../sockets/ctl_over_http.sock" + module: ejabberd_http + unix_socket: + mode: '0600' + request_handlers: + /ctl: ejabberd_ctl + - + port: PORT_STUN + ip: "::" + transport: udp + module: ejabberd_stun + use_turn: true + ## The server's public IPv4 address: + # turn_ipv4_address: "203.0.113.3" + ## The server's public IPv6 address: + # turn_ipv6_address: "2001:db8::3" + - + port: PORT_MQTT + ip: "::" + module: mod_mqtt + backlog: 1000 + +s2s_use_starttls: optional + +acl: + local: + user_regexp: "" + loopback: + ip: + - 127.0.0.0/8 + - ::1/128 + admin: + user: + - ADMIN + +access_rules: + local: + allow: local + c2s: + deny: blocked + allow: all + announce: + allow: admin + configure: + allow: admin + muc_create: + allow: local + pubsub_createnode: + allow: local + trusted_network: + allow: loopback + +api_permissions: + "console commands": + from: ejabberd_ctl + who: all + what: "*" + "webadmin commands": + from: ejabberd_web_admin + who: admin + what: "*" + "admin access": + who: + access: + allow: + - acl: loopback + - acl: admin + oauth: + scope: "ejabberd:admin" + access: + allow: + - acl: loopback + - acl: admin + what: + - "*" + - "!stop" + - "!start" + "public commands": + who: + ip: 127.0.0.1/8 + what: + - status + - connected_users_number + +shaper: + normal: + rate: 3000 + burst_size: 20000 + fast: 100000 + +shaper_rules: + max_user_sessions: 10 + max_user_offline_messages: + 5000: admin + 100: all + c2s_shaper: + none: admin + normal: all + s2s_shaper: fast + +modules: + mod_adhoc: {} + mod_admin_extra: {} + mod_announce: + access: announce + mod_avatar: {} + mod_blocking: {} + mod_bosh: {} + mod_caps: {} + mod_carboncopy: {} + mod_client_state: {} + mod_configure: {} + mod_disco: {} + mod_fail2ban: {} + mod_http_api: {} + mod_http_upload: + put_url: https://@HOST_URL_ENCODE@:5443/upload + custom_headers: + "Access-Control-Allow-Origin": "https://@HOST@" + "Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS" + "Access-Control-Allow-Headers": "Content-Type" + mod_last: {} + mod_mam: + ## Mnesia is limited to 2GB, better to use an SQL backend + ## For small servers SQLite is a good fit and is very easy + ## to configure. Uncomment this when you have SQL configured: + ## db_type: sql + assume_mam_usage: true + default: always + mod_mqtt: {} + mod_muc: + access: + - allow + access_admin: + - allow: admin + access_create: muc_create + access_persistent: muc_create + access_mam: + - allow + default_room_options: + mam: true + mod_muc_admin: {} + mod_offline: + access_max_user_messages: max_user_offline_messages + mod_ping: {} + mod_privacy: {} + mod_private: {} + mod_proxy65: + access: local + max_connections: 5 + port: PORT_PROXY65 + mod_pubsub: + access_createnode: pubsub_createnode + plugins: + - flat + - pep + force_node_config: + ## Avoid buggy clients to make their bookmarks public + storage:bookmarks: + access_model: whitelist + mod_push: {} + mod_push_keepalive: {} + mod_register: + ## Only accept registration requests from the "trusted" + ## network (see access_rules section above). + ## Think twice before enabling registration from any + ## address. See the Jabber SPAM Manifesto for details: + ## https://github.com/ge0rg/jabber-spam-fighting-manifesto + ip_access: trusted_network + mod_roster: + versioning: true + mod_s2s_bidi: {} + mod_s2s_dialback: {} + mod_shared_roster: {} + mod_stream_mgmt: + resend_on_timeout: if_offline + mod_stun_disco: {} + mod_vcard: {} + mod_vcard_xupdate: {} + mod_version: + show_os: false + +### Local Variables: +### mode: yaml +### End: +### vim: set filetype=yaml tabstop=8 diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template new file mode 100755 index 000000000..b1f1d2179 --- /dev/null +++ b/.github/container/ejabberdctl.template @@ -0,0 +1,627 @@ +#!/bin/sh + +# define default configuration +POLL=true +ERL_MAX_PORTS=32000 +ERL_PROCESSES=250000 +ERL_MAX_ETS_TABLES=1400 +FIREWALL_WINDOW="" +INET_DIST_INTERFACE="" +ERLANG_NODE=ejabberd@localhost + +# define default environment variables +[ -z "$SCRIPT" ] && SCRIPT=$0 +SCRIPT_DIR="$(cd "$(dirname "$SCRIPT")" && pwd -P)" +# shellcheck disable=SC2034 +ERTS_VSN="{{erts_vsn}}" +ERL="{{erl}}" +EPMD="{{epmd}}" +IEX="{{iexpath}}" +COOKIE_FILE="$HOME"/.erlang.cookie +[ -n "$ERLANG_COOKIE" ] && [ ! -f "$COOKIE_FILE" ] && echo "$ERLANG_COOKIE" > "$COOKIE_FILE" && chmod 400 "$COOKIE_FILE" + +# check the proper system user is used +case $(id -un) in + "$INSTALLUSER") + EXEC_CMD="as_current_user" + ;; + root) + if [ -n "$INSTALLUSER" ] ; then + EXEC_CMD="as_install_user" + else + EXEC_CMD="as_current_user" + echo "WARNING: It is not recommended to run ejabberd as root" >&2 + fi + ;; + *) + if [ -n "$INSTALLUSER" ] ; then + echo "ERROR: This command can only be run by root or the user $INSTALLUSER" >&2 + exit 7 + else + EXEC_CMD="as_current_user" + fi + ;; +esac + +# parse command line parameters +while [ $# -gt 0 ]; do + case $1 in + -n|--node) ERLANG_NODE_ARG=$2; shift 2;; + -s|--spool) SPOOL_DIR=$2; shift 2;; + -l|--logs) LOGS_DIR=$2; shift 2;; + -f|--config) EJABBERD_CONFIG_PATH=$2; shift 2;; + -c|--ctl-config) EJABBERDCTL_CONFIG_PATH=$2; shift 2;; + -d|--config-dir) CONFIG_DIR=$2; shift 2;; + -t|--no-timeout) NO_TIMEOUT="--no-timeout"; shift;; + *) break;; + esac +done + +# define ejabberd variables if not already defined from the command line +: "${CONFIG_DIR:="{{config_dir}}"}" +: "${LOGS_DIR:="{{logs_dir}}"}" +: "${EJABBERD_CONFIG_PATH:="$CONFIG_DIR/ejabberd.yml"}" +: "${EJABBERDCTL_CONFIG_PATH:="$CONFIG_DIR/ejabberdctl.cfg"}" +# Allows passing extra Erlang command-line arguments in vm.args file +: "${VMARGS:="$CONFIG_DIR/vm.args"}" +# shellcheck source=ejabberdctl.cfg.example +[ -f "$EJABBERDCTL_CONFIG_PATH" ] && . "$EJABBERDCTL_CONFIG_PATH" +[ -n "$ERLANG_NODE_ARG" ] && ERLANG_NODE="$ERLANG_NODE_ARG" +[ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && S="-s" +: "${SPOOL_DIR:="{{spool_dir}}"}" +: "${EJABBERD_LOG_PATH:="$LOGS_DIR/ejabberd.log"}" + +# backward support for old mnesia spool dir path +: "${SPOOL_DIR_OLD:="$SPOOL_DIR/$ERLANG_NODE"}" +[ -r "$SPOOL_DIR_OLD/schema.DAT" ] && [ ! -r "$SPOOL_DIR/schema.DAT" ] && SPOOL_DIR="$SPOOL_DIR_OLD" + +# define erl parameters +ERLANG_OPTS="+K $POLL +P $ERL_PROCESSES $ERL_OPTIONS" +if [ -n "$FIREWALL_WINDOW" ] ; then + ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}" +fi +if [ -n "$INET_DIST_INTERFACE" ] ; then + INET_DIST_INTERFACE2=$("$ERL" $ERLANG_OPTS -noshell -eval 'case inet:parse_address("'$INET_DIST_INTERFACE'") of {ok,IP} -> io:format("~p",[IP]); _ -> ok end.' -s erlang halt) + if [ -n "$INET_DIST_INTERFACE2" ] ; then + if [ "$(echo "$INET_DIST_INTERFACE2" | grep -o "," | wc -l)" -eq 7 ] ; then + INET_DIST_INTERFACE2="$INET_DIST_INTERFACE2 -proto_dist inet6_tcp" + fi + ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_use_interface $INET_DIST_INTERFACE2" + fi +fi +[ -n "$ERL_DIST_PORT" ] && ERLANG_OPTS="$ERLANG_OPTS -erl_epmd_port $ERL_DIST_PORT -start_epmd false" +# if vm.args file exists in config directory, pass it to Erlang VM +[ -f "$VMARGS" ] && ERLANG_OPTS="$ERLANG_OPTS -args_file $VMARGS" +ERL_LIBS='{{libdir}}' +ERL_CRASH_DUMP="$LOGS_DIR"/erl_crash_$(date "+%Y%m%d-%H%M%S").dump +ERL_INETRC="$CONFIG_DIR"/inetrc + +# define ejabberd parameters +EJABBERD_OPTS="\ +$(sed '/^log_rotate_size/!d;s/:[ \t]*\([0-9]\{1,\}\).*/ \1/;s/:[ \t]*\(infinity\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ +$(sed '/^log_rotate_count/!d;s/:[ \t]*\([0-9]*\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ +$(sed '/^log_burst_limit_count/!d;s/:[ \t]*\([0-9]*\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ +$(sed '/^log_burst_limit_window_time/!d;s/:[ \t]*\([0-9]*[a-z]*\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ +$EJABBERD_OPTS" +[ -n "$EJABBERD_OPTS" ] && EJABBERD_OPTS="-ejabberd $EJABBERD_OPTS" +EJABBERD_OPTS="-mnesia dir \"$SPOOL_DIR\" $MNESIA_OPTIONS $EJABBERD_OPTS -s ejabberd" + +# export global variables +export EJABBERD_CONFIG_PATH +export EJABBERD_LOG_PATH +export EJABBERD_PID_PATH +export ERL_CRASH_DUMP +export ERL_EPMD_ADDRESS +export ERL_DIST_PORT +export ERL_INETRC +export ERL_MAX_PORTS +export ERL_MAX_ETS_TABLES +export CONTRIB_MODULES_PATH +export CONTRIB_MODULES_CONF_DIR +export ERL_LIBS +export SCRIPT_DIR + +set_dist_client() +{ + [ -n "$ERL_DIST_PORT" ] && ERLANG_OPTS="$ERLANG_OPTS -dist_listen false" +} + +# run command either directly or via su $INSTALLUSER +run_cmd() +{ + case $EXEC_CMD in + as_install_user) su -s /bin/sh -c '"$0" "$@"' "$INSTALLUSER" -- "$@" ;; + as_current_user) "$@" ;; + esac +} +exec_cmd() +{ + case $EXEC_CMD in + as_current_user) exec "$@" ;; + as_install_user) su -s /bin/sh -c 'exec "$0" "$@"' "$INSTALLUSER" -- "$@" ;; + esac +} +run_erl() +{ + NODE=$1; shift + run_cmd "$ERL" ${S:--}name "$NODE" $ERLANG_OPTS "$@" +} +exec_erl() +{ + NODE=$1; shift + exec_cmd "$ERL" ${S:--}name "$NODE" $ERLANG_OPTS "$@" +} +exec_iex() +{ + NODE=$1; shift + exec_cmd "$IEX" -${S:--}name "$NODE" --erl "$ERLANG_OPTS" "$@" +} + +# usage +debugwarning() +{ + if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then + echo "--------------------------------------------------------------------" + echo "" + echo "IMPORTANT: we will attempt to attach an INTERACTIVE shell" + echo "to an already running ejabberd node." + echo "If an ERROR is printed, it means the connection was not successful." + echo "You can interact with the ejabberd node if you know how to use it." + echo "Please be extremely cautious with your actions," + echo "and exit immediately if you are not completely sure." + echo "" + echo "To exit and detach this shell from ejabberd, press:" + echo " control+g and then q" + echo "" + #vt100 echo "Please do NOT use control+c in this debug shell !" + #vt100 echo "" + echo "--------------------------------------------------------------------" + echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:" + echo " EJABBERD_BYPASS_WARNINGS=true" + echo "Press return to continue" + read -r _ + echo "" + fi +} + +livewarning() +{ + if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then + echo "--------------------------------------------------------------------" + echo "" + echo "IMPORTANT: ejabberd is going to start in LIVE (interactive) mode." + echo "All log messages will be shown in the command shell." + echo "You can interact with the ejabberd node if you know how to use it." + echo "Please be extremely cautious with your actions," + echo "and exit immediately if you are not completely sure." + echo "" + echo "To stop ejabberd gracefully:" + echo " ejabberd:stop()." + echo "To quit erlang immediately, press:" + echo " control+g and then q" + echo "" + echo "--------------------------------------------------------------------" + echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:" + echo " EJABBERD_BYPASS_WARNINGS=true" + echo "Press return to continue" + read -r _ + echo "" + fi +} + +check_etop_result() +{ + result=$? + if [ $result -eq 1 ] ; then + echo "" + echo "It seems there was some problem running 'ejabberdctl etop'." + echo "Is the error message something like this?" + echo " Failed to load module 'etop' because it cannot be found..." + echo "Then probably ejabberd was compiled with development tools disabled." + echo "To use 'etop', recompile ejabberd with: ./configure --enable-tools" + echo "" + exit $result + fi +} + +check_iex_result() +{ + result=$? + if [ $result -eq 127 ] ; then + echo "" + echo "It seems there was some problem finding 'iex' binary from Elixir." + echo "Probably ejabberd was compiled with Rebar3 and Elixir disabled, like:" + echo " ./configure" + echo "which is equivalent to:" + echo " ./configure --with-rebar=rebar3 --disable-elixir" + echo "To use 'iex', recompile ejabberd enabling Elixir or using Mix:" + echo " ./configure --enable-elixir" + echo " ./configure --with-rebar=mix" + echo "" + exit $result + fi +} + +help() +{ + echo "" + echo "Commands to start an ejabberd node:" + echo " start Start in server mode" + echo " foreground Start in server mode (attached)" + echo " foreground-quiet Start in server mode (attached), show only critical messages" + echo " live Start in interactive mode, with Erlang shell" + echo " iexlive Start in interactive mode, with Elixir shell" + echo "" + echo "Commands to interact with a running ejabberd node:" + echo " debug Attach an interactive Erlang shell to a running node" + echo " iexdebug Attach an interactive Elixir shell to a running node" + echo " etop Attach to a running node and start Erlang Top" + echo " ping Send ping to the node, returns pong or pang" + echo " started|stopped Wait for the node to fully start|stop" + echo "" + echo "Optional parameters when starting an ejabberd node:" + echo " --config-dir dir Config ejabberd: $CONFIG_DIR" + echo " --config file Config ejabberd: $EJABBERD_CONFIG_PATH" + echo " --ctl-config file Config ejabberdctl: $EJABBERDCTL_CONFIG_PATH" + echo " --logs dir Directory for logs: $LOGS_DIR" + echo " --spool dir Database spool dir: $SPOOL_DIR" + echo " --node nodename ejabberd node name: $ERLANG_NODE" + echo "" +} + +# dynamic node name helper +uid() { + ERTSVERSION="$("$ERL" -version 2>&1 | sed 's|.* \([0-9]*[0-9]\).*|\1|g')" + if [ $ERTSVERSION -lt 11 ] ; then # otp 23.0 includes erts 11.0 + # Erlang/OTP lower than 23, which doesn's support dynamic node code + N=1 + PF=$(( $$ % 97 )) + while + case $# in + 0) NN="${PF}-${N}-${ERLANG_NODE}" + ;; + 1) NN="${PF}-${N}-${1}-${ERLANG_NODE}" + ;; + 2) NN="${PF}-${N}-${1}@${2}" + ;; + esac + N=$(( N + 1 + ( $$ % 5 ) )) + "$EPMD" -names 2>/dev/null | grep -q " ${NN%@*} " + do :; done + echo $NN + else + # Erlang/OTP 23 or higher: use native dynamic node code + # https://www.erlang.org/patches/otp-23.0#OTP-13812 + if [ "$ERLANG_NODE" != "${ERLANG_NODE%.*}" ]; then + echo "undefined@${ERLANG_NODE#*@}" + else + echo "undefined" + fi + fi +} + +# stop epmd if there is no other running node +stop_epmd() +{ + [ -n "$ERL_DIST_PORT" ] && return + "$EPMD" -names 2>/dev/null | grep -q name || "$EPMD" -kill >/dev/null +} + +# make sure node not already running and node name unregistered +# if all ok, ensure runtime directory exists and make it current directory +check_start() +{ + ECSIMAGE_DBPATH=$HOME/database/$ERLANG_NODE + [ ! -d "$ECSIMAGE_DBPATH" ] && ln -s $HOME/database $HOME/database/$ERLANG_NODE + [ -n "$ERL_DIST_PORT" ] && return + "$EPMD" -names 2>/dev/null | grep -q " ${ERLANG_NODE%@*} " && { + pgrep -f "$ERLANG_NODE" >/dev/null && { + echo "ERROR: The ejabberd node '$ERLANG_NODE' is already running." + exit 4 + } + pgrep beam >/dev/null && { + echo "ERROR: The ejabberd node '$ERLANG_NODE' is registered," + echo " but no related beam process has been found." + echo "Shutdown all other erlang nodes, and call 'epmd -kill'." + exit 5 + } + "$EPMD" -kill >/dev/null + } +} + +post_waiter_fork() +{ + (FIRST_RUN=$FIRST_RUN "$0" post_waiter)& +} + +post_waiter_waiting() +{ + $0 started + [ -n "$FIRST_RUN" ] && [ -n "$CTL_ON_CREATE" ] && (post_waiter_loop $CTL_ON_CREATE) + [ -n "$CTL_ON_START" ] && post_waiter_loop $CTL_ON_START +} + +post_waiter_loop() +{ + LIST=$@ + HEAD=${LIST%% ; *} + TAIL=${LIST#* ; } + HEAD2=${HEAD#\! *} + echo ":> ejabberdctl $HEAD2" + $0 $HEAD2 + ctlstatus=$? + if [ $ctlstatus -ne 0 ] ; then + if [ "$HEAD" != "$HEAD2" ] ; then + echo ":> FAILURE in command '$HEAD2' !!! Ignoring result" + else + echo ":> FAILURE in command '$HEAD' !!! Stopping ejabberd..." + $0 halt > /dev/null + exit $ctlstatus + fi + fi + [ "$HEAD" = "$TAIL" ] || post_waiter_loop $TAIL +} + +# allow sync calls +wait_status() +{ + wait_status_node "$ERLANG_NODE" $1 $2 $3 +} + +wait_status_node() +{ + CONNECT_NODE=$1 + shift + # args: status try delay + # return: 0 OK, 1 KO + timeout="$2" + status=4 + while [ "$status" -ne "$1" ] ; do + sleep "$3" + timeout=$((timeout - 1)) + if [ $timeout -eq 0 ] ; then + status="$1" + else + run_erl "$(uid ctl)" -hidden -noinput \ + -eval 'net_kernel:connect_node('"'$CONNECT_NODE'"')' \ + -s ejabberd_ctl \ + -extra "$CONNECT_NODE" $NO_TIMEOUT status > /dev/null + status="$?" + fi + done + [ $timeout -gt 0 ] +} + +exec_other_command() +{ + exec_other_command_node $ERLANG_NODE "$@" +} + +exec_other_command_node() +{ + CONNECT_NODE=$1 + shift + if [ -z "$CTL_OVER_HTTP" ] || [ ! -S "$CTL_OVER_HTTP" ] \ + || [ ! -x "$(command -v curl)" ] || [ -z "$1" ] || [ "$1" = "help" ] \ + || [ "$1" = "mnesia_info_ctl" ]|| [ "$1" = "print_sql_schema" ] ; then + run_erl "$(uid ctl)" -hidden -noinput \ + -eval 'net_kernel:connect_node('"'$CONNECT_NODE'"')' \ + -s ejabberd_ctl \ + -extra "$CONNECT_NODE" $NO_TIMEOUT "$@" + result=$? + case $result in + 3) help;; + *) :;; + esac + return $result + else + exec_ctl_over_http_socket "$@" + fi +} + +exec_ctl_over_http_socket() +{ + COMMAND=${1} + CARGS="" + while [ $# -gt 0 ]; do + [ -z "$CARGS" ] && CARGS="[" || CARGS="${CARGS}, " + CARGS="${CARGS}\"$1\"" + shift + done + CARGS="${CARGS}]" + TEMPHEADERS=temp-headers.log + curl \ + --unix-socket ${CTL_OVER_HTTP} \ + --header "Content-Type: application/json" \ + --header "Accept: application/json" \ + --data "${CARGS}" \ + --dump-header ${TEMPHEADERS} \ + --no-progress-meter \ + "http://localhost/ctl/${COMMAND}" + result=$(sed -n 's/.*status-code: \([0-9]*\).*/\1/p' < $TEMPHEADERS) + rm ${TEMPHEADERS} + case $result in + 2|3) exec_other_command help ${COMMAND};; + *) :;; + esac + exit $result +} + +# ensure we can change current directory to SPOOL_DIR +[ -f "$SPOOL_DIR/schema.DAT" ] || FIRST_RUN=true +[ -d "$SPOOL_DIR" ] || run_cmd mkdir -p "$SPOOL_DIR" +cd "$SPOOL_DIR" || { + echo "ERROR: can not access directory $SPOOL_DIR" + exit 6 +} + +printe() +{ + printf "\n" + printf "\e[1;40;32m==> %s\e[0m\n" "$1" +} + +## Function copied from tools/make-installers +user_agrees() +{ + question="$*" + + if [ -t 0 ] + then + printe "$question (y/n) [n]" + read -r response + case "$response" in + [Yy]|[Yy][Ee][Ss]) + return 0 + ;; + [Nn]|[Nn][Oo]|'') + return 1 + ;; + *) + echo 'Please respond with "yes" or "no".' + user_agrees "$question" + ;; + esac + else # Assume 'yes' if not running interactively. + return 0 + fi +} + +mnesia_change() +{ + ERLANG_NODE_OLD="$1" + [ "$ERLANG_NODE_OLD" = "" ] \ + && echo "Error: Please provide the old erlang node name, for example:" \ + && echo " ejabberdctl mnesia_change ejabberd@oldmachine" \ + && exit 1 + + SPOOL_DIR_BACKUP=$SPOOL_DIR/$ERLANG_NODE_OLD-backup/ + OLDFILE=$SPOOL_DIR_BACKUP/$ERLANG_NODE_OLD.backup + NEWFILE=$SPOOL_DIR_BACKUP/$ERLANG_NODE.backup + + printe "This changes your mnesia database from node name '$ERLANG_NODE_OLD' to '$ERLANG_NODE'" + + [ -d "$SPOOL_DIR_BACKUP" ] && printe "WARNING! A backup of old node already exists in $SPOOL_DIR_BACKUP" + + if ! user_agrees "Do you want to proceed?" + then + echo 'Operation aborted.' + exit 1 + fi + + printe "Starting ejabberd with old node name $ERLANG_NODE_OLD ..." + exec_erl "$ERLANG_NODE_OLD" $EJABBERD_OPTS -detached + wait_status_node $ERLANG_NODE_OLD 0 30 2 + result=$? + case $result in + 1) echo "There was a problem starting ejabberd with the old erlang node name. " \ + && echo "Check for log errors in $EJABBERD_LOG_PATH" \ + && exit $result;; + *) :;; + esac + exec_other_command_node $ERLANG_NODE_OLD "status" + + printe "Making backup of old database to file $OLDFILE ..." + mkdir $SPOOL_DIR_BACKUP + exec_other_command_node $ERLANG_NODE_OLD backup "$OLDFILE" + + printe "Changing node name in new backup file $NEWFILE ..." + exec_other_command_node $ERLANG_NODE_OLD mnesia_change_nodename "$ERLANG_NODE_OLD" "$ERLANG_NODE" "$OLDFILE" "$NEWFILE" + + printe "Stopping old ejabberd..." + exec_other_command_node $ERLANG_NODE_OLD "stop" + wait_status_node $ERLANG_NODE_OLD 3 30 2 && stop_epmd + + printe "Moving old mnesia spool files to backup subdirectory $SPOOL_DIR_BACKUP ..." + mv $SPOOL_DIR/*.DAT $SPOOL_DIR_BACKUP + mv $SPOOL_DIR/*.DCD $SPOOL_DIR_BACKUP + mv $SPOOL_DIR/*.LOG $SPOOL_DIR_BACKUP + + printe "Starting ejabberd with new node name $ERLANG_NODE ..." + exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -detached + wait_status 0 30 2 + exec_other_command "status" + + printe "Installing fallback of new mnesia..." + exec_other_command install_fallback "$NEWFILE" + + printe "Stopping new ejabberd..." + exec_other_command "stop" + wait_status 3 30 2 && stop_epmd + + printe "Finished, now you can start ejabberd normally" +} + +# main +case $1 in + start) + check_start + exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -detached + ;; + foreground) + check_start + post_waiter_fork + exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -noinput + ;; + foreground-quiet) + check_start + exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -noinput -ejabberd quiet true + ;; + live) + livewarning + check_start + exec_erl "$ERLANG_NODE" $EJABBERD_OPTS + ;; + debug) + debugwarning + set_dist_client + exec_erl "$(uid debug)" -hidden -remsh "$ERLANG_NODE" + ;; + etop) + set_dist_client + exec_erl "$(uid top)" -hidden -remsh "$ERLANG_NODE" \ + -eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \ + -s etop \ + -output text + check_etop_result + ;; + iexdebug) + debugwarning + set_dist_client + exec_iex "$(uid debug)" --remsh "$ERLANG_NODE" + check_iex_result + ;; + iexlive) + livewarning + exec_iex "$ERLANG_NODE" --erl "$EJABBERD_OPTS" + check_iex_result + ;; + ping) + PEER=${2:-$ERLANG_NODE} + [ "$PEER" = "${PEER%.*}" ] && PS="-s" + set_dist_client + exec_cmd "$ERL" ${PS:--}name "$(uid ping "$(hostname $PS)")" $ERLANG_OPTS \ + -noinput -hidden \ + -eval 'net_kernel:connect_node('"'$PEER'"')' \ + -eval 'io:format("~p~n",[net_adm:ping('"'$PEER'"')])' \ + -s erlang halt -output text + ;; + started) + set_dist_client + wait_status 0 30 2 # wait 30x2s before timeout + ;; + stopped) + set_dist_client + wait_status 3 30 2 && stop_epmd # wait 30x2s before timeout + ;; + mnesia_change) + mnesia_change $2 + ;; + post_waiter) + post_waiter_waiting + ;; + *) + set_dist_client + exec_other_command "$@" + ;; +esac diff --git a/.github/lock.yml b/.github/lock.yml new file mode 100644 index 000000000..626f54530 --- /dev/null +++ b/.github/lock.yml @@ -0,0 +1,38 @@ +# Configuration for Lock Threads - https://github.com/dessant/lock-threads + +# Number of days of inactivity before a closed issue or pull request is locked +daysUntilLock: 365 + +# Skip issues and pull requests created before a given timestamp. Timestamp must +# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable +skipCreatedBefore: false + +# Issues and pull requests with these labels will be ignored. Set to `[]` to disable +exemptLabels: [] + +# Label to add before locking, such as `outdated`. Set to `false` to disable +lockLabel: false + +# Comment to post before locking. Set to `false` to disable +lockComment: > + This thread has been automatically locked since there has not been + any recent activity after it was closed. Please open a new issue for + related bugs. + +# Assign `resolved` as the reason for locking. Set to `false` to disable +setLockReason: true + +# Limit to only `issues` or `pulls` +# only: issues + +# Optionally, specify configuration settings just for `issues` or `pulls` +# issues: +# exemptLabels: +# - help-wanted +# lockLabel: outdated + +# pulls: +# daysUntilLock: 30 + +# Repository to extend settings from +# _extends: repo diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..ebf9da68c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,301 @@ +name: CI + +on: + push: + paths-ignore: + - '.devcontainer/**' + - 'examples/**' + - 'lib/**' + - 'man/**' + - 'priv/**' + - '**.md' + pull_request: + paths-ignore: + - '.devcontainer/**' + - 'examples/**' + - 'lib/**' + - 'man/**' + - 'priv/**' + - '**.md' + +jobs: + + tests: + name: Tests + strategy: + fail-fast: false + matrix: + otp: ['25', '26', '27', '28'] + runs-on: ubuntu-24.04 + services: + redis: + image: public.ecr.aws/docker/library/redis + ports: + - 6379:6379 + + steps: + + - uses: actions/checkout@v5 + + - name: Test shell scripts + if: matrix.otp == '27' + run: | + shellcheck test/ejabberd_SUITE_data/gencerts.sh + shellcheck tools/captcha.sh + shellcheck ejabberd.init.template + shellcheck -x ejabberdctl.template + + - name: Get specific Erlang/OTP + uses: erlef/setup-beam@v1 + with: + otp-version: ${{ matrix.otp }} + + - name: Install MS SQL Server + run: | + docker run -d -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=ejabberd_Test1" \ + -v $(pwd)/test/docker/db/mssql/initdb/initdb_mssql.sql:/initdb_mssql.sql:ro \ + -v $(pwd)/sql/mssql.sql:/mssql.sql:ro \ + -v $(pwd)/sql/mssql.new.sql:/mssql.new.sql:ro \ + -p 1433:1433 --name ejabberd-mssql "mcr.microsoft.com/mssql/server:2019-latest" + sleep 10 + + - name: Prepare databases + run: | + docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql + docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.sql + sudo systemctl start mysql.service + sudo systemctl start postgresql.service + mysql -u root -proot -e "CREATE DATABASE ejabberd_test;" + mysql -u root -proot -e "CREATE USER 'ejabberd_test'@'localhost' + IDENTIFIED BY 'ejabberd_test';" + mysql -u root -proot -e "GRANT ALL ON ejabberd_test.* + TO 'ejabberd_test'@'localhost';" + pg_isready + sudo -u postgres psql -c "CREATE DATABASE ejabberd_test;" + sudo -u postgres psql -c "CREATE USER ejabberd_test + WITH PASSWORD 'ejabberd_test';" + sudo -u postgres psql -c "GRANT ALL PRIVILEGES + ON DATABASE ejabberd_test TO ejabberd_test;" + sudo -u postgres psql -c "GRANT ALL ON SCHEMA public TO ejabberd_test;" + sudo -u postgres psql -c "ALTER DATABASE ejabberd_test OWNER TO ejabberd_test;" + sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL + TABLES IN SCHEMA public + TO ejabberd_test;" + sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL + SEQUENCES IN SCHEMA public + TO ejabberd_test;" + + - name: Prepare libraries + run: | + sudo apt-get -qq update + sudo apt-get -y purge libgd3 nginx + sudo apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \ + libsqlite3-dev libwebp-dev libyaml-dev + + - name: Remove syntax_tools from release + run: sed -i 's|, syntax_tools||g' src/ejabberd.app.src.script + + - name: Cache Hex.pm + uses: actions/cache@v4 + with: + path: | + ~/.cache/rebar3/ + key: ${{matrix.otp}}-${{hashFiles('rebar.config')}} + + - name: Compile + run: | + ./autogen.sh + ./configure --with-rebar=./rebar3 \ + --prefix=/tmp/ejabberd \ + --enable-all \ + --disable-elixir \ + --disable-mssql \ + --disable-odbc + make + + - run: make install -s + - run: make hooks + - run: make options + - run: make xref + - run: make dialyzer + - run: make test-eunit + - run: make elvis + if: matrix.otp >= '26' + + - name: Check Production Release + run: | + make rel + RE=_build/prod/rel/ejabberd + $RE/bin/ejabberdctl start + $RE/bin/ejabberdctl started + $RE/bin/ejabberdctl stop + $RE/bin/ejabberdctl stopped + cat $RE/logs/ejabberd.log + grep -q "is stopped in" $RE/logs/ejabberd.log + + - name: Start Development Release + run: | + make dev + RE=_build/dev/rel/ejabberd + sed -i 's/starttls_required: true/starttls_required: false/g' $RE/conf/ejabberd.yml + $RE/bin/ejabberdctl start + $RE/bin/ejabberdctl started + $RE/bin/ejabberdctl register admin localhost admin + grep -q "is started in" $RE/logs/ejabberd.log + + - name: Run XMPP Interoperability Tests against CI server. + if: matrix.otp == '27' + continue-on-error: true + uses: XMPP-Interop-Testing/xmpp-interop-tests-action@v1.6.1 + with: + domain: 'localhost' + adminAccountUsername: 'admin' + adminAccountPassword: 'admin' + disabledSpecifications: RFC6121,XEP-0030,XEP-0045,XEP-0054,XEP-0060,XEP-0080,XEP-0115,XEP-0118,XEP-0215,XEP-0347,XEP-0363,XEP-0384 + + - name: Stop Development Release + if: always() + run: | + RE=_build/dev/rel/ejabberd + $RE/bin/ejabberdctl stop + $RE/bin/ejabberdctl stopped + cat $RE/logs/ejabberd.log + grep -q "is stopped in" $RE/logs/ejabberd.log + + - name: Run tests + id: ct + run: | + (cd priv && ln -sf ../sql) + sed -i -e 's/ct:pal/ct:log/' test/suite.erl + COMMIT=`echo $GITHUB_SHA | cut -c 1-7` + DATE=`date +%s` + REF_NAME=`echo $GITHUB_REF_NAME | tr "/" "_"` + NODENAME=$DATE@$GITHUB_RUN_NUMBER-$GITHUB_ACTOR-$REF_NAME-$COMMIT + LABEL=`git show -s --format=%s | cut -c 1-30` + ./rebar3 ct --name $NODENAME --label "$LABEL" + ./rebar3 cover + + - name: Check results + if: always() && (steps.ct.outcome != 'skipped') + id: ctresults + run: | + [[ -d _build ]] && ln -s _build/test/logs/last/ logs || true + ln `find logs/ -name suite.log` logs/suite.log + grep 'TEST COMPLETE' logs/suite.log + grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log + test $(find logs/ -empty -name error.log) + + - name: View logs failures + if: failure() && steps.ctresults.outcome == 'failure' + run: | + cat logs/suite.log | awk \ + 'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}' + find logs/ -name error.log -exec cat '{}' ';' + find logs/ -name exunit.log -exec cat '{}' ';' + + - name: Send to coveralls + if: matrix.otp == '27' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + DIAGNOSTIC=1 ./rebar3 as test coveralls send + curl -v -k https://coveralls.io/webhook \ + --header "Content-Type: application/json" \ + --data '{"repo_name":"$GITHUB_REPOSITORY", + "repo_token":"$GITHUB_TOKEN", + "payload":{"build_num":$GITHUB_RUN_ID, + "status":"done"}}' + + - name: Check for changes to trigger schema upgrade test + uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + sql: + - 'sql/**' + - 'src/mod_admin_update_sql.erl' + + - name: Prepare for schema upgrade test + id: prepupgradetest + if: ${{ steps.filter.outputs.sql == 'true' }} + run: | + [[ -d logs ]] && rm -rf logs + [[ -d _build/test/logs ]] && rm -rf _build/test/logs || true + sed -i 's|update_sql, false|update_sql, true|g' test/suite.erl + - name: Run DB tests on upgraded schema (mssql, mysql, pgsql) + run: CT_BACKENDS=mssql,mysql,pgsql make test + if: always() && steps.prepupgradetest.outcome != 'skipped' + id: ctupgradedschema + - name: Check results + if: always() && steps.ctupgradedschema.outcome != 'skipped' + run: | + [[ -d _build ]] && ln -s _build/test/logs/last/ logs || true + ln `find logs/ -name suite.log` logs/suite.log + grep 'TEST COMPLETE' logs/suite.log + grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log + test $(find logs/ -empty -name error.log) + - name: View logs failures + if: failure() && steps.ctupgradedschema.outcome != 'skipped' + run: | + cat logs/suite.log | awk \ + 'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}' + find logs/ -name error.log -exec cat '{}' ';' + find logs/ -name exunit.log -exec cat '{}' ';' + + - name: Prepare new schema + run: | + [[ -d logs ]] && rm -rf logs + [[ -d _build/test/logs ]] && rm -rf _build/test/logs || true + docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -Q "drop database [ejabberd_test];" + docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -Q "drop login [ejabberd_test];" + mysql -u root -proot -e "DROP DATABASE ejabberd_test;" + sudo -u postgres psql -c "DROP DATABASE ejabberd_test;" + docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql + docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.new.sql + mysql -u root -proot -e "CREATE DATABASE ejabberd_test;" + mysql -u root -proot -e "GRANT ALL ON ejabberd_test.* + TO 'ejabberd_test'@'localhost';" + sudo -u postgres psql -c "CREATE DATABASE ejabberd_test;" + sudo -u postgres psql -c "GRANT ALL PRIVILEGES + ON DATABASE ejabberd_test TO ejabberd_test;" + sudo -u postgres psql -c "GRANT ALL ON SCHEMA public TO ejabberd_test;" + sudo -u postgres psql -c "ALTER DATABASE ejabberd_test OWNER TO ejabberd_test;" + sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL + TABLES IN SCHEMA public + TO ejabberd_test;" + sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL + SEQUENCES IN SCHEMA public + TO ejabberd_test;" + sed -i 's|new_schema, false|new_schema, true|g' test/suite.erl + - name: Run DB tests on new schema (mssql, mysql, pgsql) + run: CT_BACKENDS=mssql,mysql,pgsql make test + id: ctnewschema + - name: Check results + if: always() && steps.ctnewschema.outcome != 'skipped' + run: | + [[ -d _build ]] && ln -s _build/test/logs/last/ logs || true + ln `find logs/ -name suite.log` logs/suite.log + grep 'TEST COMPLETE' logs/suite.log + grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log + test $(find logs/ -empty -name error.log) + - name: View logs failures + if: failure() && steps.ctnewschema.outcome != 'skipped' + run: | + cat logs/suite.log | awk \ + 'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}' + find logs/ -name error.log -exec cat '{}' ';' + find logs/ -name exunit.log -exec cat '{}' ';' + + - name: Upload CT logs + if: failure() + uses: actions/upload-artifact@v4 + with: + name: ejabberd-ct-logs-${{matrix.otp}} + # + # Appending the wildcard character ("*") is a trick to make + # "ejabberd-packages" the root directory of the uploaded ZIP file: + # + # https://github.com/actions/upload-artifact#upload-using-multiple-paths-and-exclusions + # + path: _build/test/logs + retention-days: 14 diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml new file mode 100644 index 000000000..0bb169ccc --- /dev/null +++ b/.github/workflows/container.yml @@ -0,0 +1,74 @@ +name: Container + +on: + push: + paths-ignore: + - '.devcontainer/**' + - 'examples/**' + - 'lib/**' + - 'man/**' + - 'priv/**' + - '**.md' + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + container: + name: Container + runs-on: ubuntu-22.04 + permissions: + packages: write + steps: + - name: Check out repository code + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Checkout ejabberd-contrib + uses: actions/checkout@v5 + with: + repository: processone/ejabberd-contrib + path: .ejabberd-modules/sources/ejabberd-contrib + + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Get git describe + id: gitdescribe + run: echo "ver=$(git describe --tags)" >> $GITHUB_OUTPUT + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + labels: | + org.opencontainers.image.revision=${{ steps.gitdescribe.outputs.ver }} + org.opencontainers.image.licenses=GPL-2.0 + org.opencontainers.image.vendor=ProcessOne + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + build-args: | + VERSION=${{ steps.gitdescribe.outputs.ver }} + cache-from: type=gha + cache-to: type=gha,mode=max + context: . + file: .github/container/Dockerfile + labels: ${{ steps.meta.outputs.labels }} + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} diff --git a/.github/workflows/installers.yml b/.github/workflows/installers.yml new file mode 100644 index 000000000..37c8983b4 --- /dev/null +++ b/.github/workflows/installers.yml @@ -0,0 +1,84 @@ +name: Installers + +on: + push: + paths-ignore: + - '.devcontainer/**' + - 'examples/**' + - 'lib/**' + - 'man/**' + - 'priv/**' + - '**.md' + pull_request: + paths-ignore: + - '.devcontainer/**' + - 'examples/**' + - 'lib/**' + - 'man/**' + - 'priv/**' + - '**.md' + +jobs: + binaries: + name: Binaries + runs-on: ubuntu-22.04 + steps: + - name: Cache build directory + uses: actions/cache@v4 + with: + path: ~/build/ + key: ${{runner.os}}-ct-ng-1.27.0 + - name: Install prerequisites + run: | + sudo apt-get -qq update + sudo apt-get -qq install makeself + # https://github.com/crosstool-ng/crosstool-ng/blob/master/testing/docker/ubuntu21.10/Dockerfile + sudo apt-get -qq install build-essential autoconf bison flex gawk + sudo apt-get -qq install help2man libncurses5-dev libtool libtool-bin + sudo apt-get -qq install python3-dev texinfo unzip + - name: Install FPM + run: | + gem install --no-document --user-install fpm + echo $HOME/.local/share/gem/ruby/*/bin >> $GITHUB_PATH + - name: Check out repository code + uses: actions/checkout@v5 + with: + fetch-depth: 0 + - name: Build binary archives + run: CHECK_DEPS=false tools/make-binaries + - name: Build DEB and RPM packages + run: tools/make-packages + - name: Build installers + run: tools/make-installers + - name: Collect packages + run: | + mkdir ejabberd-packages + mv ejabberd_*.deb ejabberd-*.rpm ejabberd-*.run ejabberd-packages + - name: Upload packages + uses: actions/upload-artifact@v4 + with: + name: ejabberd-packages + # + # Appending the wildcard character ("*") is a trick to make + # "ejabberd-packages" the root directory of the uploaded ZIP file: + # + # https://github.com/actions/upload-artifact#upload-using-multiple-paths-and-exclusions + # + path: ejabberd-packages* + retention-days: 14 + + release: + name: Release + needs: [binaries] + runs-on: ubuntu-22.04 + if: github.ref_type == 'tag' + steps: + - name: Download packages + uses: actions/download-artifact@v5 + with: + name: ejabberd-packages + - name: Draft Release + uses: softprops/action-gh-release@v2 + with: + draft: true + files: ejabberd-packages/* diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml new file mode 100644 index 000000000..cd599fe83 --- /dev/null +++ b/.github/workflows/runtime.yml @@ -0,0 +1,432 @@ +name: Runtime + +on: + push: + paths: + - '*' + - '!*.md' + - '.github/workflows/runtime.yml' + - 'checkouts/**' + - 'config/**' + - 'lib/**' + - 'm4/**' + - 'plugins/**' + - 'rel/**' + pull_request: + paths: + - '*' + - '!*.md' + - '.github/workflows/runtime.yml' + - 'checkouts/**' + - 'config/**' + - 'lib/**' + - 'm4/**' + - 'plugins/**' + - 'rel/**' + +jobs: + + rebars: + name: Rebars + strategy: + fail-fast: false + matrix: + otp: ['24', '25', '26', '27', '28'] + rebar: ['rebar', 'rebar3'] + exclude: + - otp: '24' + rebar: 'rebar' + - otp: '27' + rebar: 'rebar' + - otp: '28' + rebar: 'rebar' + runs-on: ubuntu-24.04 + container: + image: public.ecr.aws/docker/library/erlang:${{ matrix.otp }} + + steps: + + - uses: actions/checkout@v5 + + - name: Get old compatible Rebar binaries + if: matrix.otp < 24 + run: | + rm rebar + rm rebar3 + wget https://github.com/processone/ejabberd/raw/21.12/rebar + wget https://github.com/processone/ejabberd/raw/21.12/rebar3 + chmod +x rebar + chmod +x rebar3 + + - name: Get recent compatible Rebar binaries + if: matrix.otp > 23 && matrix.otp < 25 + run: | + rm rebar + rm rebar3 + wget https://github.com/processone/ejabberd/raw/24.12/rebar + wget https://github.com/processone/ejabberd/raw/24.12/rebar3 + chmod +x rebar + chmod +x rebar3 + + - name: Prepare libraries + run: | + apt-get -qq update + apt-get purge -y libgd3 nginx + apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \ + libsqlite3-dev libwebp-dev libyaml-dev + + - name: Cache Hex.pm + uses: actions/cache@v4 + with: + path: | + ~/.cache/rebar3/ + key: ${{matrix.otp}}-${{hashFiles('rebar.config')}} + + - name: Unlock eredis dependency + if: matrix.rebar == 'rebar3' && matrix.otp < 21 + run: rebar3 unlock eredis + + - name: Compile + run: | + ./autogen.sh + ./configure --with-rebar=./${{ matrix.rebar }} \ + --prefix=/tmp/ejabberd \ + --with-min-erlang=9.0.5 \ + --enable-all \ + --disable-elixir \ + --disable-tools \ + --disable-odbc + make + + - run: make xref + + - run: make dialyzer + + - name: Prepare rel (rebar2) + if: matrix.rebar == 'rebar' + run: | + mkdir -p _build/prod && ln -s `pwd`/rel/ _build/prod/rel + mkdir -p _build/dev && ln -s `pwd`/rel/ _build/dev/rel + + - name: Run rel + run: | + make rel + _build/prod/rel/ejabberd/bin/ejabberdctl start \ + && _build/prod/rel/ejabberd/bin/ejabberdctl started + _build/prod/rel/ejabberd/bin/ejabberdctl register user1 localhost s0mePass + _build/prod/rel/ejabberd/bin/ejabberdctl registered_users localhost > registered.log + _build/prod/rel/ejabberd/bin/ejabberdctl stop \ + && _build/prod/rel/ejabberd/bin/ejabberdctl stopped + + - name: Run dev + run: | + make dev + _build/dev/rel/ejabberd/bin/ejabberdctl start \ + && _build/dev/rel/ejabberd/bin/ejabberdctl started + _build/dev/rel/ejabberd/bin/ejabberdctl register user2 localhost s0mePass + _build/dev/rel/ejabberd/bin/ejabberdctl registered_users localhost >> registered.log + _build/dev/rel/ejabberd/bin/ejabberdctl stop \ + && _build/dev/rel/ejabberd/bin/ejabberdctl stopped + + - name: Run install + run: | + make install + /tmp/ejabberd/sbin/ejabberdctl start \ + && /tmp/ejabberd/sbin/ejabberdctl started + /tmp/ejabberd/sbin/ejabberdctl register user3 localhost s0mePass + /tmp/ejabberd/sbin/ejabberdctl registered_users localhost >> registered.log + /tmp/ejabberd/sbin/ejabberdctl stop \ + && /tmp/ejabberd/sbin/ejabberdctl stopped + + - name: View logs + run: | + echo "===> Registered:" + cat registered.log + echo "===> Prod:" + cat _build/prod/rel/ejabberd/logs/* + echo "===> Dev:" + cat _build/dev/rel/ejabberd/logs/* + echo "===> Install:" + cat /tmp/ejabberd/var/log/ejabberd/* + + - name: Check logs + run: | + grep -q '^user1$' registered.log + grep -q '^user2$' registered.log + grep -q '^user3$' registered.log + grep -q 'is started' _build/prod/rel/ejabberd/logs/ejabberd.log + grep -q 'is stopped' _build/prod/rel/ejabberd/logs/ejabberd.log + test $(find _build/prod/rel/ -empty -name error.log) + grep -q 'is started' _build/dev/rel/ejabberd/logs/ejabberd.log + grep -q 'is stopped' _build/dev/rel/ejabberd/logs/ejabberd.log + test $(find _build/dev/rel/ -empty -name error.log) + grep -q 'is started' /tmp/ejabberd/var/log/ejabberd/ejabberd.log + grep -q 'is stopped' /tmp/ejabberd/var/log/ejabberd/ejabberd.log + test $(find /tmp/ejabberd/var/log/ejabberd/ -empty -name error.log) + + - name: View logs failures + if: always() + run: | + cat _build/prod/rel/ejabberd/logs/ejabberd.log + cat _build/prod/rel/ejabberd/logs/error.log + cat _build/dev/rel/ejabberd/logs/ejabberd.log + cat _build/dev/rel/ejabberd/logs/error.log + cat /tmp/ejabberd/var/log/ejabberd/ejabberd.log + cat /tmp/ejabberd/var/log/ejabberd/error.log + + rebar3-elixir: + name: Rebar3+Elixir + strategy: + fail-fast: false + matrix: + elixir: ['1.14', '1.15', '1.16', '1.17', '1.18'] + runs-on: ubuntu-24.04 + container: + image: public.ecr.aws/docker/library/elixir:${{ matrix.elixir }} + + steps: + + - uses: actions/checkout@v5 + + - name: Prepare libraries + run: | + apt-get -qq update + apt-get -y purge libgd3 nginx + apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \ + libsqlite3-dev libwebp-dev libyaml-dev + + - name: Enable Module.Example and an Elixir dependency + run: | + sed -i "s|^modules:|modules:\n 'Ejabberd.Module.Example': {}|g" ejabberd.yml.example + cat ejabberd.yml.example + sed -i 's|^{deps, \[\(.*\)|{deps, [{decimal, ".*", {git, "https://github.com/ericmj/decimal", {branch, "main"}}},\n \1|g' rebar.config + cat rebar.config + + - name: Cache Hex.pm + uses: actions/cache@v4 + with: + path: | + ~/.cache/rebar3/ + key: ${{matrix.elixir}}-${{hashFiles('rebar.config')}} + + - name: Install Hex and Rebar3 manually on older Elixir + if: matrix.elixir <= '1.14' + run: | + mix local.hex --force + mix local.rebar --force + + - name: Compile + run: | + ./autogen.sh + ./configure --with-rebar=./rebar3 \ + --prefix=/tmp/ejabberd \ + --enable-all \ + --disable-odbc + make + + - run: make xref + + - name: Run rel + run: | + make rel + _build/prod/rel/ejabberd/bin/ejabberdctl start \ + && _build/prod/rel/ejabberd/bin/ejabberdctl started + _build/prod/rel/ejabberd/bin/ejabberdctl register user1 localhost s0mePass + _build/prod/rel/ejabberd/bin/ejabberdctl registered_users localhost > registered.log + _build/prod/rel/ejabberd/bin/ejabberdctl stop \ + && _build/prod/rel/ejabberd/bin/ejabberdctl stopped + + - name: Run dev + run: | + make dev + _build/dev/rel/ejabberd/bin/ejabberdctl start \ + && _build/dev/rel/ejabberd/bin/ejabberdctl started + _build/dev/rel/ejabberd/bin/ejabberdctl register user2 localhost s0mePass + _build/dev/rel/ejabberd/bin/ejabberdctl registered_users localhost >> registered.log + _build/dev/rel/ejabberd/bin/ejabberdctl stop \ + && _build/dev/rel/ejabberd/bin/ejabberdctl stopped + + - name: Run install + run: | + make install + /tmp/ejabberd/sbin/ejabberdctl start \ + && /tmp/ejabberd/sbin/ejabberdctl started + /tmp/ejabberd/sbin/ejabberdctl register user3 localhost s0mePass + /tmp/ejabberd/sbin/ejabberdctl registered_users localhost >> registered.log + /tmp/ejabberd/sbin/ejabberdctl stop \ + && /tmp/ejabberd/sbin/ejabberdctl stopped + + - name: View logs + if: always() + run: | + echo "===> Registered:" + cat registered.log + echo "===> Prod:" + cat _build/prod/rel/ejabberd/logs/* + echo "===> Dev:" + cat _build/dev/rel/ejabberd/logs/* + echo "===> Install:" + cat /tmp/ejabberd/var/log/ejabberd/* + + - name: Check logs + if: always() + run: | + grep -q '^user1$' registered.log + grep -q '^user2$' registered.log + grep -q '^user3$' registered.log + grep -q 'is started' _build/prod/rel/ejabberd/logs/ejabberd.log + grep -q 'is stopped' _build/prod/rel/ejabberd/logs/ejabberd.log + grep -q 'Stopping Ejabberd.Module.Example' _build/prod/rel/ejabberd/logs/ejabberd.log + test $(find _build/prod/ -empty -name error.log) + grep -q 'is started' _build/dev/rel/ejabberd/logs/ejabberd.log + grep -q 'is stopped' _build/dev/rel/ejabberd/logs/ejabberd.log + grep -q 'Stopping Ejabberd.Module.Example' _build/dev/rel/ejabberd/logs/ejabberd.log + test $(find _build/dev/ -empty -name error.log) + grep -q 'is started' /tmp/ejabberd/var/log/ejabberd/ejabberd.log + grep -q 'is stopped' /tmp/ejabberd/var/log/ejabberd/ejabberd.log + grep -q 'Stopping Ejabberd.Module.Example' /tmp/ejabberd/var/log/ejabberd/ejabberd.log + test $(find /tmp/ejabberd/var/log/ejabberd/ -empty -name error.log) + + - name: View logs failures + if: failure() + run: | + cat _build/prod/rel/ejabberd/logs/ejabberd.log + cat _build/prod/rel/ejabberd/logs/error.log + cat _build/dev/rel/ejabberd/logs/ejabberd.log + cat _build/dev/rel/ejabberd/logs/error.log + cat /tmp/ejabberd/var/log/ejabberd/ejabberd.log + cat /tmp/ejabberd/var/log/ejabberd/error.log + + mix: + name: Mix + strategy: + fail-fast: false + matrix: + elixir: ['1.14', '1.15', '1.16', '1.17', '1.18'] + runs-on: ubuntu-24.04 + container: + image: public.ecr.aws/docker/library/elixir:${{ matrix.elixir }} + + steps: + + - uses: actions/checkout@v5 + + - name: Prepare libraries + run: | + apt-get -qq update + apt-get -y purge libgd3 nginx + apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \ + libsqlite3-dev libwebp-dev libyaml-dev + + - name: Remove Elixir Matchers + run: | + echo "::remove-matcher owner=elixir-mixCompileWarning::" + echo "::remove-matcher owner=elixir-credoOutputDefault::" + echo "::remove-matcher owner=elixir-mixCompileError::" + echo "::remove-matcher owner=elixir-mixTestFailure::" + echo "::remove-matcher owner=elixir-dialyzerOutputDefault::" + + - name: Enable Module.Example and an Elixir dependency + run: | + sed -i "s|^modules:|modules:\n 'Ejabberd.Module.Example': {}|g" ejabberd.yml.example + cat ejabberd.yml.example + sed -i 's|^{deps, \(.*\)|{deps, \1\n {decimal, ".*", {git, "https://github.com/ericmj/decimal", {branch, "main"}}}, |g' rebar.config + cat rebar.config + + - name: Cache Hex.pm + uses: actions/cache@v4 + with: + path: | + ~/.hex/ + key: ${{matrix.elixir}}-${{hashFiles('mix.exs')}} + + - name: Install Hex and Rebar3 manually on older Elixir + if: matrix.elixir <= '1.14' + run: | + mix local.hex --force + mix local.rebar --force + + - name: Compile + run: | + ./autogen.sh + ./configure --with-rebar=mix \ + --prefix=/tmp/ejabberd \ + --enable-all + make + + - run: make xref + + - run: make dialyzer + + - run: make edoc + + - name: Run rel + run: | + make rel + _build/prod/rel/ejabberd/bin/ejabberdctl start \ + && _build/prod/rel/ejabberd/bin/ejabberdctl started + _build/prod/rel/ejabberd/bin/ejabberdctl register user1 localhost s0mePass + _build/prod/rel/ejabberd/bin/ejabberdctl registered_users localhost > registered.log + _build/prod/rel/ejabberd/bin/ejabberdctl stop \ + && _build/prod/rel/ejabberd/bin/ejabberdctl stopped + + - name: Run dev + run: | + make dev + _build/dev/rel/ejabberd/bin/ejabberdctl start \ + && _build/dev/rel/ejabberd/bin/ejabberdctl started + _build/dev/rel/ejabberd/bin/ejabberdctl register user2 localhost s0mePass + _build/dev/rel/ejabberd/bin/ejabberdctl registered_users localhost >> registered.log + _build/dev/rel/ejabberd/bin/ejabberdctl stop \ + && _build/dev/rel/ejabberd/bin/ejabberdctl stopped + + - name: Run install + run: | + make install + /tmp/ejabberd/sbin/ejabberdctl start \ + && /tmp/ejabberd/sbin/ejabberdctl started + /tmp/ejabberd/sbin/ejabberdctl register user3 localhost s0mePass + /tmp/ejabberd/sbin/ejabberdctl registered_users localhost >> registered.log + /tmp/ejabberd/sbin/ejabberdctl stop \ + && /tmp/ejabberd/sbin/ejabberdctl stopped + + - name: View logs + if: always() + run: | + echo "===> Registered:" + cat registered.log + echo "===> Prod:" + cat _build/prod/rel/ejabberd/logs/* + echo "===> Dev:" + cat _build/dev/rel/ejabberd/logs/* + echo "===> Install:" + cat /tmp/ejabberd/var/log/ejabberd/* + + - name: Check logs + if: always() + run: | + grep -q '^user1$' registered.log + grep -q '^user2$' registered.log + grep -q '^user3$' registered.log + grep -q 'is started' _build/prod/rel/ejabberd/logs/ejabberd.log + grep -q 'is stopped' _build/prod/rel/ejabberd/logs/ejabberd.log + grep -q 'Stopping Ejabberd.Module.Example' _build/prod/rel/ejabberd/logs/ejabberd.log + test $(find _build/prod/ -empty -name error.log) + grep -q 'is started' _build/dev/rel/ejabberd/logs/ejabberd.log + grep -q 'is stopped' _build/dev/rel/ejabberd/logs/ejabberd.log + grep -q 'Stopping Ejabberd.Module.Example' _build/dev/rel/ejabberd/logs/ejabberd.log + test $(find _build/dev/ -empty -name error.log) + grep -q 'is started' /tmp/ejabberd/var/log/ejabberd/ejabberd.log + grep -q 'is stopped' /tmp/ejabberd/var/log/ejabberd/ejabberd.log + grep -q 'Stopping Ejabberd.Module.Example' /tmp/ejabberd/var/log/ejabberd/ejabberd.log + test $(find /tmp/ejabberd/var/log/ejabberd/ -empty -name error.log) + + - name: View logs failures + if: failure() + run: | + cat _build/prod/rel/ejabberd/logs/ejabberd.log + cat _build/prod/rel/ejabberd/logs/error.log + cat _build/dev/rel/ejabberd/logs/ejabberd.log + cat _build/dev/rel/ejabberd/logs/error.log + cat /tmp/ejabberd/var/log/ejabberd/ejabberd.log + cat /tmp/ejabberd/var/log/ejabberd/error.log diff --git a/.gitignore b/.gitignore index 38d6d77f8..0f69a0aa3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,45 +5,45 @@ \#*# .#* .edts +.tool-versions *.dump /Makefile +/doc /config.log /config.status +/config/releases.exs /configure /aclocal.m4 -/contrib/extract_translations/extract_translations.beam /*.cache /deps/ -/doc/*.aux -/doc/*.haux -/doc/*.html -/doc/*.htoc -/doc/*.idx -/doc/*.ilg -/doc/*.ind -/doc/*.log -/doc/*.out -/doc/*.pdf -/doc/*.toc -/doc/contributed_modules.tex -/doc/version.tex +/.deps-update/ +/.ejabberd-modules/ /ebin/ /ejabberd.init /ejabberd.service +/ejabberdctl /ejabberdctl.example -XmppAddr.hrl /rel/ejabberd/ -/src/XmppAddr.asn1db -/src/XmppAddr.erl -/src/ejabberd.app.src +/rel/overlays/ /src/eldap_filter_yecc.erl /vars.config /dialyzer/ /test/*.beam /test/*.ctc /logs/ +/priv/bin/captcha*sh /priv/sql /rel/ejabberd +/recompile.log /_build -/mnesiadb +/database/ /.rebar +/log/ +Mnesia.nonode@nohost/ +/TAGS +/tags +# Binaries created with tools/make-{binaries,installers,packages}: +/ejabberd_*.deb +/ejabberd-*.rpm +/ejabberd-*.run +/ejabberd-*.tar.gz diff --git a/.shellcheckrc b/.shellcheckrc new file mode 100644 index 000000000..0b7131a2e --- /dev/null +++ b/.shellcheckrc @@ -0,0 +1,4 @@ +disable=SC2016,SC2086,SC2089,SC2090 +external-sources=true +source=ejabberdctl.cfg.example +shell=sh diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index aa9acef82..000000000 --- a/.travis.yml +++ /dev/null @@ -1,72 +0,0 @@ -language: erlang - -otp_release: - - 17.5 - - 18.3 - - 19.1 - -services: - - riak - - redis-server - -before_install: - # - # We need MySQL 5.6 or newer in order to get support for FULLTEXT indexes - # with InnoDB. As soon as Travis ships that version, the following lines - # (except for the "apt-get update" call) can go away. - # - # See: https://github.com/travis-ci/travis-ci/issues/1986 - # - - sudo sed -i -e s/table_cache/table_open_cache/ -e /log_slow_queries/d /etc/mysql/my.cnf - - sudo apt-key adv --import .travis/mysql_repo_key.asc - - sudo add-apt-repository 'deb http://repo.mysql.com/apt/ubuntu/ precise mysql-5.6' - - sudo apt-get -qq update - - sudo apt-get -qq -o Dpkg::Options::=--force-confold install mysql-server-5.6 - # /END MYSQL 5.6 - - pip install --user coveralls-merge - -install: - - sudo apt-get -qq install libexpat1-dev libyaml-dev libpam0g-dev libsqlite3-dev - -before_script: - # Ulimit: See Travis-CI issue report: https://github.com/travis-ci/travis-ci/issues/3328 - - echo 'ulimit -n 4096' > riak - - sudo mv riak /etc/default/riak - - mkdir "$PWD/ebin" - - echo "[{riak_kv, [{add_paths, [\"$PWD/ebin/\"]}]}]." > advanced.config - - sudo mv advanced.config /etc/riak/advanced.config - - sudo service riak restart - - sudo riak-admin wait-for-service riak_kv 'riak@127.0.0.1' - - sudo riak-admin test - - mysql -u root -e "CREATE USER 'ejabberd_test'@'localhost' IDENTIFIED BY 'ejabberd_test';" - - mysql -u root -e "CREATE DATABASE ejabberd_test;" - - mysql -u root -e "GRANT ALL ON ejabberd_test.* TO 'ejabberd_test'@'localhost';" - - psql -U postgres -c "CREATE USER ejabberd_test WITH PASSWORD 'ejabberd_test';" - - psql -U postgres -c "CREATE DATABASE ejabberd_test;" - - psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE ejabberd_test TO ejabberd_test;" - -script: - - ./autogen.sh - - ./configure --prefix=/tmp/ejabberd --enable-all --disable-odbc - - make - - make install - - make xref - - sed -i -e 's/ct:pal/ct:log/' test/suite.erl - - ln -sf ../sql priv/ - - escript ./rebar skip_deps=true ct -v - - grep -q 'TEST COMPLETE, \([[:digit:]]*\) ok, .* of \1 ' logs/raw.log - -after_script: - - find logs -name suite.log -exec cat '{}' ';' - -after_failure: - - find logs -name exunit.log -exec cat '{}' ';' - # Try checking Riak database logs - - tail -n 100000 /var/log/riak/*.log - - find logs -name ejabberd.log -exec cat '{}' ';' - -after_success: - - coveralls-merge erlang.json - -notifications: - email: false diff --git a/.travis/mysql_repo_key.asc b/.travis/mysql_repo_key.asc deleted file mode 100644 index 063f5148a..000000000 --- a/.travis/mysql_repo_key.asc +++ /dev/null @@ -1,96 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v1.4.9 (SunOS) - -mQGiBD4+owwRBAC14GIfUfCyEDSIePvEW3SAFUdJBtoQHH/nJKZyQT7h9bPlUWC3 -RODjQReyCITRrdwyrKUGku2FmeVGwn2u2WmDMNABLnpprWPkBdCk96+OmSLN9brZ -fw2vOUgCmYv2hW0hyDHuvYlQA/BThQoADgj8AW6/0Lo7V1W9/8VuHP0gQwCgvzV3 -BqOxRznNCRCRxAuAuVztHRcEAJooQK1+iSiunZMYD1WufeXfshc57S/+yeJkegNW -hxwR9pRWVArNYJdDRT+rf2RUe3vpquKNQU/hnEIUHJRQqYHo8gTxvxXNQc7fJYLV -K2HtkrPbP72vwsEKMYhhr0eKCbtLGfls9krjJ6sBgACyP/Vb7hiPwxh6rDZ7ITnE -kYpXBACmWpP8NJTkamEnPCia2ZoOHODANwpUkP43I7jsDmgtobZX9qnrAXw+uNDI -QJEXM6FSbi0LLtZciNlYsafwAPEOMDKpMqAK6IyisNtPvaLd8lH0bPAnWqcyefep -rv0sxxqUEMcM3o7wwgfN83POkDasDbs3pjwPhxvhz6//62zQJ7Q2TXlTUUwgUmVs -ZWFzZSBFbmdpbmVlcmluZyA8bXlzcWwtYnVpbGRAb3NzLm9yYWNsZS5jb20+iGkE -ExECACkCGyMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAIZAQUCUwHUZgUJGmbLywAK -CRCMcY07UHLh9V+DAKCjS1gGwgVI/eut+5L+l2v3ybl+ZgCcD7ZoA341HtoroV3U -6xRD09fUgeq0O015U1FMIFBhY2thZ2Ugc2lnbmluZyBrZXkgKHd3dy5teXNxbC5j -b20pIDxidWlsZEBteXNxbC5jb20+iG8EMBECAC8FAk53Pa0oHSBidWlsZEBteXNx -bC5jb20gd2lsbCBzdG9wIHdvcmtpbmcgc29vbgAKCRCMcY07UHLh9bU9AJ9xDK0o -xJFL9vTl9OSZC4lX0K9AzwCcCrS9cnJyz79eaRjL0s2r/CcljdyIZQQTEQIAHQUC -R6yUtAUJDTBYqAULBwoDBAMVAwIDFgIBAheAABIJEIxxjTtQcuH1B2VHUEcAAQGu -kgCffz4GUEjzXkOi71VcwgCxASTgbe0An34LPr1j9fCbrXWXO14msIADfb5piEwE -ExECAAwFAj4+o9EFgwlmALsACgkQSVDhKrJykfIk4QCfWbEeKN+3TRspe+5xKj+k -QJSammIAnjUz0xFWPlVx0f8o38qNG1bq0cU9iEwEExECAAwFAj5CggMFgwliIokA -CgkQtvXNTca6JD+WkQCgiGmnoGjMojynp5ppvMXkyUkfnykAoK79E6h8rwkSDZou -iz7nMRisH8uyiEYEEBECAAYFAj+s468ACgkQr8UjSHiDdA/2lgCg21IhIMMABTYd -p/IBiUsP/JQLiEoAnRzMywEtujQz/E9ono7H1DkebDa4iEYEEBECAAYFAj+0Q3cA -CgkQhZavqzBzTmbGwwCdFqD1frViC7WRt8GKoOS7hzNN32kAnirlbwpnT7a6NOsQ -83nk11a2dePhiEYEEBECAAYFAkNbs+oACgkQi9gubzC5S1x/dACdELKoXQKkwJN0 -gZztsM7kjsIgyFMAnRRMbHQ7V39XC90OIpaPjk3a01tgiEYEExECAAYFAkTxMyYA -CgkQ9knE9GCTUwwKcQCgibak/SwhxWH1ijRhgYCo5GtM4vcAnAhtzL57wcw1Kg1X -m7nVGetUqJ7fiEwEEBECAAwFAkGBywEFgwYi2YsACgkQGFnQH2d7oexCjQCcD8sJ -NDc/mS8m8OGDUOx9VMWcnGkAnj1YWOD+Qhxo3mI/Ul9oEAhNkjcfiEwEEBECAAwF -AkGByzQFgwYi2VgACgkQgcL36+ITtpIiIwCdFVNVUB8xe8mFXoPm4d9Z54PTjpMA -niSPA/ZsfJ3oOMLKar4F0QPPrdrGiEwEEBECAAwFAkGBy2IFgwYi2SoACgkQa3Ds -2V3D9HMJqgCbBYzr5GPXOXgP88jKzmdbjweqXeEAnRss4G2G/3qD7uhTL1SPT1SH -jWUXiEwEEBECAAwFAkHQkyQFgwXUEWgACgkQfSXKCsEpp8JiVQCghvWvkPqowsw8 -w7WSseTcw1tflvkAni+vLHl/DqIly0LkZYn5jzK1dpvfiEwEEBECAAwFAkIrW7oF -gwV5SNIACgkQ5hukiRXruavzEwCgkzL5QkLSypcw9LGHcFSx1ya0VL4An35nXkum -g6cCJ1NP8r2I4NcZWIrqiEwEEhECAAwFAkAqWToFgwd6S1IACgkQPKEfNJT6+GEm -XACcD+A53A5OGM7w750W11ukq4iZ9ckAnRMvndAqn3YTOxxlLPj2UPZiSgSqiEwE -EhECAAwFAkA9+roFgwdmqdIACgkQ8tdcY+OcZZyy3wCgtDcwlaq20w0cNuXFLLNe -EUaFFTwAni6RHN80moSVAdDTRkzZacJU3M5QiEwEEhECAAwFAkEOCoQFgwaWmggA -CgkQOcor9D1qil/83QCeITZ9wIo7XAMjC6y4ZWUL4m+edZsAoMOhRIRi42fmrNFu -vNZbnMGej81viEwEEhECAAwFAkKApTQFgwUj/1gACgkQBA3AhXyDn6jjJACcD1A4 -UtXk84J13JQyoH9+dy24714Aniwlsso/9ndICJOkqs2j5dlHFq6oiEwEExECAAwF -Aj5NTYQFgwlXVwgACgkQLbt2v63UyTMFDACglT5G5NVKf5Mj65bFSlPzb92zk2QA -n1uc2h19/IwwrsbIyK/9POJ+JMP7iEwEExECAAwFAkHXgHYFgwXNJBYACgkQZu/b -yM2C/T4/vACfXe67xiSHB80wkmFZ2krb+oz/gBAAnjR2ucpbaonkQQgnC3GnBqmC -vNaJiEwEExECAAwFAkIYgQ4FgwWMI34ACgkQdsEDHKIxbqGg7gCfQi2HcrHn+yLF -uNlH1oSOh48ZM0oAn3hKV0uIRJphonHaUYiUP1ttWgdBiGUEExECAB0FCwcKAwQD -FQMCAxYCAQIXgAUCS3AvygUJEPPzpwASB2VHUEcAAQEJEIxxjTtQcuH1sNsAniYp -YBGqy/HhMnw3WE8kXahOOR5KAJ4xUmWPGYP4l3hKxyNK9OAUbpDVYIh7BDARAgA7 -BQJCdzX1NB0AT29wcy4uLiBzaG91bGQgaGF2ZSBiZWVuIGxvY2FsISBJJ20gKnNv -KiBzdHVwaWQuLi4ACgkQOcor9D1qil/vRwCdFo08f66oKLiuEAqzlf9iDlPozEEA -n2EgvCYLCCHjfGosrkrU3WK5NFVgiI8EMBECAE8FAkVvAL9IHQBTaG91bGQgaGF2 -ZSBiZWVuIGEgbG9jYWwgc2lnbmF0dXJlLCBvciBzb21ldGhpbmcgLSBXVEYgd2Fz -IEkgdGhpbmtpbmc/AAoJEDnKK/Q9aopfoPsAn3BVqKOalJeF0xPSvLR90PsRlnmG -AJ44oisY7Tl3NJbPgZal8W32fbqgbIkCIgQQAQIADAUCQYHLhQWDBiLZBwAKCRCq -4+bOZqFEaKgvEACCErnaHGyUYa0wETjj6DLEXsqeOiXad4i9aBQxnD35GUgcFofC -/nCY4XcnCMMEnmdQ9ofUuU3OBJ6BNJIbEusAabgLooebP/3KEaiCIiyhHYU5jarp -ZAh+Zopgs3Oc11mQ1tIaS69iJxrGTLodkAsAJAeEUwTPq9fHFFzC1eGBysoyFWg4 -bIjz/zClI+qyTbFA5g6tRoiXTo8ko7QhY2AA5UGEg+83Hdb6akC04Z2QRErxKAqr -phHzj8XpjVOsQAdAi/qVKQeNKROlJ+iq6+YesmcWGfzeb87dGNweVFDJIGA0qY27 -pTb2lExYjsRFN4Cb13NfodAbMTOxcAWZ7jAPCxAPlHUG++mHMrhQXEToZnBFE4nb -nC7vOBNgWdjUgXcpkUCkop4b17BFpR+k8ZtYLSS8p2LLz4uAeCcSm2/msJxT7rC/ -FvoH8428oHincqs2ICo9zO/Ud4HmmO0O+SsZdVKIIjinGyOVWb4OOzkAlnnhEZ3o -6hAHcREIsBgPwEYVTj/9ZdC0AO44Nj9cU7awaqgtrnwwfr/o4V2gl8bLSkltZU27 -/29HeuOeFGjlFe0YrDd/aRNsxbyb2O28H4sG1CVZmC5uK1iQBDiSyA7Q0bbdofCW -oQzm5twlpKWnY8Oe0ub9XP5p/sVfck4FceWFHwv+/PC9RzSl33lQ6vM2wIkCIgQT -AQIADAUCQp8KHAWDBQWacAAKCRDYwgoJWiRXzyE+D/9uc7z6fIsalfOYoLN60ajA -bQbI/uRKBFugyZ5RoaItusn9Z2rAtn61WrFhu4uCSJtFN1ny2RERg40f56pTghKr -D+YEt+Nze6+FKQ5AbGIdFsR/2bUk+ZZRSt83e14Lcb6ii/fJfzkoIox9ltkifQxq -Y7Tvk4noKu4oLSc8O1Wsfc/y0B9sYUUCmUfcnq58DEmGie9ovUslmyt5NPnveXxp -5UeaRc5Rqt9tK2B4A+7/cqENrdZJbAMSunt2+2fkYiRunAFPKPBdJBsY1sxeL/A9 -aKe0viKEXQdAWqdNZKNCi8rd/oOP99/9lMbFudAbX6nL2DSb1OG2Z7NWEqgIAzjm -pwYYPCKeVz5Q8R+if9/fe5+STY/55OaI33fJ2H3v+U435VjYqbrerWe36xJItcJe -qUzW71fQtXi1CTEl3w2ch7VF5oj/QyjabLnAlHgSlkSi6p7By5C2MnbCHlCfPnIi -nPhFoRcRGPjJe9nFwGs+QblvS/Chzc2WX3s/2SWm4gEUKRX4zsAJ5ocyfa/vkxCk -SxK/erWlCPf/J1T70+i5waXDN/E3enSet/WL7h94pQKpjz8OdGL4JSBHuAVGA+a+ -dknqnPF0KMKLhjrgV+L7O84FhbmAP7PXm3xmiMPriXf+el5fZZequQoIagf8rdRH -HhRJxQgI0HNknkaOqs8dtrkCDQQ+PqMdEAgA7+GJfxbMdY4wslPnjH9rF4N2qfWs -EN/lxaZoJYc3a6M02WCnHl6ahT2/tBK2w1QI4YFteR47gCvtgb6O1JHffOo2HfLm -RDRiRjd1DTCHqeyX7CHhcghj/dNRlW2Z0l5QFEcmV9U0Vhp3aFfWC4Ujfs3LU+hk -AWzE7zaD5cH9J7yv/6xuZVw411x0h4UqsTcWMu0iM1BzELqX1DY7LwoPEb/O9Rkb -f4fmLe11EzIaCa4PqARXQZc4dhSinMt6K3X4BrRsKTfozBu74F47D8Ilbf5vSYHb -uE5p/1oIDznkg/p8kW+3FxuWrycciqFTcNz215yyX39LXFnlLzKUb/F5GwADBQf+ -Lwqqa8CGrRfsOAJxim63CHfty5mUc5rUSnTslGYEIOCR1BeQauyPZbPDsDD9MZ1Z -aSafanFvwFG6Llx9xkU7tzq+vKLoWkm4u5xf3vn55VjnSd1aQ9eQnUcXiL4cnBGo -TbOWI39EcyzgslzBdC++MPjcQTcA7p6JUVsP6oAB3FQWg54tuUo0Ec8bsM8b3Ev4 -2LmuQT5NdKHGwHsXTPtl0klk4bQk4OajHsiy1BMahpT27jWjJlMiJc+IWJ0mghkK -Ht926s/ymfdf5HkdQ1cyvsz5tryVI3Fx78XeSYfQvuuwqp2H139pXGEkg0n6KdUO -etdZWhe70YGNPw1yjWJT1IhUBBgRAgAMBQJOdz3tBQkT+wG4ABIHZUdQRwABAQkQ -jHGNO1By4fUUmwCbBYr2+bBEn/L2BOcnw9Z/QFWuhRMAoKVgCFm5fadQ3Afi+UQl -AcOphrnJ -=443I ------END PGP PUBLIC KEY BLOCK----- diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..6d51e0b07 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "erlang-ls.erlang-ls" + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..78cdf45ae --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,66 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Relive (Vim)", + "type": "erlang", + "request": "launch", + "runinterminal": [ + "./rebar3", "shell", + "--apps", "ejabberd", + "--config", "rel/relive.config", + "--script", "rel/relive.escript", + "--name", "ejabberd@localhost", + "--setcookie", "COOKIE" + ], + "projectnode": "ejabberd@localhost", + "cookie": "COOKIE", + "timeout": 900, + "cwd": "." + }, + { + "name": "Relive (VSCode)", + "type": "erlang", + "request": "launch", + "runinterminal": [ + ".vscode/relive.sh" + ], + "projectnode": "ejabberd@localhost", + "cookie": "COOKIE", + "timeout": 300, + "cwd": "${workspaceRoot}" + }, + { + "name": "Relive (alternate)", + "type": "erlang", + "request": "launch", + "runinterminal": [ + "./rebar3", "shell", + "--apps", "ejabberd", + "--config", "rel/relive.config", + "--script", "rel/relive.escript", + "--name", "ejabberd@localhost", + "--setcookie", "COOKIE" + ], + "projectnode": "ejabberd@localhost", + "cookie": "COOKIE", + "timeout": 300, + "cwd": "${workspaceRoot}" + }, + { + "name": "Attach", + "type": "erlang", + "request": "attach", + "runinterminal": [ + "./rebar3", "shell", + "--sname", "clean@localhost", + "--setcookie", "COOKIE", + "--start-clean" + ], + "projectnode": "ejabberd@localhost", + "cookie": "COOKIE", + "timeout": 300, + "cwd": "${workspaceRoot}" + } + ] +} diff --git a/.vscode/relive.sh b/.vscode/relive.sh new file mode 100755 index 000000000..b10b83fb0 --- /dev/null +++ b/.vscode/relive.sh @@ -0,0 +1,6 @@ +[ ! -f Makefile ] \ + && ./autogen.sh \ + && ./configure --with-rebar=rebar3 \ + && make + +make relive diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..c8e01bd2a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + "editor.tabSize": 8, + "remote.portsAttributes": { + "1883": {"label": "MQTT", "onAutoForward": "silent"}, + "4369": {"label": "EPMD", "onAutoForward": "silent"}, + "5222": {"label": "XMPP C2S", "onAutoForward": "silent"}, + "5223": {"label": "XMPP C2S (legacy)", "onAutoForward": "silent"}, + "5269": {"label": "XMPP S2S", "onAutoForward": "silent"}, + "5280": {"label": "HTTP", "onAutoForward": "silent"}, + "5443": {"label": "HTTPS", "onAutoForward": "silent"}, + "7777": {"label": "XMPP SOCKS5 (proxy65)", "onAutoForward": "silent"} + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..cadfc1c74 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,1673 @@ +## Version 25.08 + +#### API Commands + +- `ban_account`: Run `sm_kick_user` event when kicking account ([#4415](https://github.com/processone/ejabberd/issues/4415)) +- `ban_account`: No need to change password ([#4415](https://github.com/processone/ejabberd/issues/4415)) +- `mnesia_change`: New command in `ejabberdctl` script that helps changing the mnesia node name + +#### Configuration + +- Rename `auth_password_types_hidden_in_scram1` option to `auth_password_types_hidden_in_sasl1` +- `econf`: If a host in configuration is encoded IDNA, decode it ([#3519](https://github.com/processone/ejabberd/issues/3519)) +- `ejabberd_config`: New predefined keyword `HOST_URL_ENCODE` +- `ejabberd.yml.example`: Use `HOST_URL_ENCODE` to handle case when vhost is non-latin1 +- `mod_conversejs`: Add option `conversejs_plugins` ([#4413](https://github.com/processone/ejabberd/issues/4413)) +- `mod_matrix_gw`: Add `leave_timeout` option ([#4386](https://github.com/processone/ejabberd/issues/4386)) + +#### Documentation and Tests + +- `COMPILE.md`: Mention dependencies and add link to Docs ([#4431](https://github.com/processone/ejabberd/issues/4431)) +- `ejabberd_doc`: Document commands tags for modules +- CI: bump XMPP-Interop-Testing/xmpp-interop-tests-action ([#4425](https://github.com/processone/ejabberd/issues/4425)) +- Runtime: Raise the minimum Erlang tested to Erlang/OTP 24 + +#### Installers and Container + +- Bump Erlang/OTP version to 27.3.4.2 +- Bump OpenSSL version to 3.5.2 +- `make-binaries`: Disable Linux-PAM's `logind` support + +#### Core and Modules + +- Bump `p1_acme` to fix `'AttributePKCS-10'` and OTP 28 ([processone/p1_acme#4](https://github.com/processone/p1_acme/issues/4)) +- Prevent loops in `xml_compress:decode` with corrupted data +- `ejabberd_auth_mnesia`: Fix issue with filtering duplicates in `get_users()` +- `ejabberd_listener`: Add secret in temporary unix domain socket path ([#4422](https://github.com/processone/ejabberd/issues/4422)) +- `ejabberd_listener`: Log error when cannot set definitive unix socket ([#4422](https://github.com/processone/ejabberd/issues/4422)) +- `ejabberd_listener`: Try to create provisional socket in final directory ([#4422](https://github.com/processone/ejabberd/issues/4422)) +- `ejabberd_logger`: Print log lines colorized in console when using rebar3 +- `mod_conversejs`: Ensure assets_path ends in `/` as required by Converse ([#4414](https://github.com/processone/ejabberd/issues/4414)) +- `mod_conversejs`: Ensure plugins URL is separated with `/` ([#4413](https://github.com/processone/ejabberd/issues/4413)) +- `mod_http_upload`: Encode URLs into IDNA when showing to XMPP client ([#3519](https://github.com/processone/ejabberd/issues/3519)) +- `mod_matrix_gw`: Add support for null values in `is_canonical_json` ([#4421](https://github.com/processone/ejabberd/issues/4421)) +- `mod_matrix_gw`: Don't send empty direct Matrix messages ([#4420](https://github.com/processone/ejabberd/issues/4420)) +- `mod_matrix_gw`: Matrix gateway updates +- `mod_muc`: Report db failures when restoring rooms +- `mod_muc`: Unsubscribe users from members-only rooms when expelled ([#4412](https://github.com/processone/ejabberd/issues/4412)) +- `mod_providers`: New module to serve easily XMPP Providers files +- `mod_register`: Don't duplicate welcome subject and message +- `mod_scram_upgrade`: Fix format of passwords updates +- `mod_scram_upgrade`: Only offer upgrades to methods that aren't already stored + +## Version 25.07 + +#### Security fix + +- `ext_mod`: Add temporary workaround for zip including absolute path + +#### Compilation + +- Raise the minimum Elixir tested version to 1.14.0 ([#4281](https://github.com/processone/ejabberd/issues/4281)) +- Raise Erlang/OTP minimum requirement to 25.0 ([#4281](https://github.com/processone/ejabberd/issues/4281)) +- `configure.ac`: Allow to specify minimal erlang version using `--with-min-erlang` +- `Makefile.in`: Add target `test-` +- `rebar3-format.sh`: Replace csplit with perl +- Container: Bump Erlang/OTP 27.3.4.1, Elixir 1.18.4 +- Installers: Bump Erlang/OTP 27.3.4.1, Elixir 1.18.4, libexpat 2.7.1, OpenSSL 3.5.1 + +#### Configuration and Tests + +- Add `rest_proxy*` options to configure proxy used by rest module +- `ejabberd_c2s`: Add `auth_password_types_hidden_in_scram1` option +- `ejabberd_http`: Remove unused `default_host` option and state element +- `ejabberd_http`: New option `hosts_alias` and function `resolve_host_alias/1` ([#4400](https://github.com/processone/ejabberd/issues/4400)) +- New predefined keywords: `CONFIG_PATH` and `LOG_PATH` +- Fix macro used in string options when defined in env var +- Use auxiliary function to get `$HOME`, use Mnesia directory when not set ([#4402](https://github.com/processone/ejabberd/issues/4402)) +- `ejabberd_config`: Better `lists:uniq` substitute +- Tests: update readme and compose to work with current sw versions +- Update Elvis to 4.1.1, fix some warnings and enable their tests + +#### Erlang/OTP 28 support + +- Add workaround in `p1_acme` for Jose 1.11.10 not supporting OTP 28 `ecPrivkeyVer1` ([#4393](https://github.com/processone/ejabberd/issues/4393)) +- Bump `fast_xml` and `xmpp` for improved Erlang/OTP 28 support +- Bump `xmpp` and `p1_acme` patched with Erlang/OTP 28 support +- Fix `make options` in Erlang/OTP 28 ([#4352](https://github.com/processone/ejabberd/issues/4352)) +- Fix crash in `rebar3 cover` with Erlang/OTP 28 ([#4353](https://github.com/processone/ejabberd/issues/4353)) +- Rebar/Rebar3: Update binaries to work with Erlang/OTP 25-28 ([#4354](https://github.com/processone/ejabberd/issues/4354)) +- CI and Runtime: Add Erlang/OTP 28 to the versions matrix + +#### SQL + +- Fix mnesia to sql exporter after changes to auth tables +- Update code for switching to new schema type to users table changes +- Add mssql specific implementation of `delete_old_mam_messages` +- Make `delete_old_mam_messages_batch` work with sqlite +- `ejabberd_sm_sql`: Use misc:encode_pid/1 +- `mysql.sql`: Fix typo in commit 7862c6a when creating users table +- `pg.sql`: Fix missing comma in postgres schema ([#4409](https://github.com/processone/ejabberd/issues/4409)) + +#### Core and Modules + +- `ejabberd_s2s_in`: Allow S2S connections to accept client certificates that have only server purpose ([#4392](https://github.com/processone/ejabberd/issues/4392)) +- `ext_mod`: Recommend to write README.md instead txt (processone/ejabberd-contrib#363) +- `ext_mod`: Support library path installed from Debian (processone/ejabberd-contrib#363) +- `ext_mod`: When upgrading module, clean also the compiled directories +- `gen_mod`: Add support to prepare module stopping before actually stopping any module +- `mod_antispam`: Imported from ejabberd-contrib and improved ([#4373](https://github.com/processone/ejabberd/issues/4373)) +- `mod_auth_fast`: Clear tokens on kick, change pass and unregister ([#4397](https://github.com/processone/ejabberd/issues/4397))([#4398](https://github.com/processone/ejabberd/issues/4398))([#4399](https://github.com/processone/ejabberd/issues/4399)) +- `mod_conversejs`: Add link in WebAdmin to local Converse if configured +- `mod_mam`: Present mam full text search in xep-431 compatible way +- `mod_mam_mnesia`: Handle objects that don't need conversion in `transform/0` +- `mod_matrix_gw`: Don't send empty messages in Matrix rooms ([#4385](https://github.com/processone/ejabberd/issues/4385)) +- `mod_matrix_gw`: Support older Matrix rooms versions starting from version 4 +- `mod_matrix_gw`: When encoding JSON, handle term that is key-value list ([#4379](https://github.com/processone/ejabberd/issues/4379)) +- `mod_matrix_gw_s2s`: Fix key validation in `check_signature` +- `mod_mix` and `mod_muc_rtbl`: Support list of IDs in `pubsub-items-retract` (processone/xmpp#100) +- `mod_pubsub_serverinfo`: Imported module from ejabberd-contrib ([#4408](https://github.com/processone/ejabberd/issues/4408)) +- `mod_register`: Normalize username when determining if user want to change pass +- `mod_register`: Strip query data when returning errors +- WebAdmin: New hooks `webadmin_menu_system` to add items to system menu + +## Version 25.04 + +#### Security fixes +- Fixes issue with handling of user provided occupant-id in messages and presences sent to muc room. Server was replacing + just first instance of occupant-id with its own version, leaving other ones untouched. That would mean that depending + on order in which clients send occupant-id, they could see value provided by sender, and that could be used to spoof + as different sender. + +#### Commands API +- `kick_users`: New command to kick all logged users for a given host + +#### Bugfixes +- Fix issue with sql schema auto upgrade when using `sqlite` database +- Fix problem with container update, that could ignore previous data stored in `mnesia` database +- Revert limit of allowed characters in shared roster group names, that will again allow using symbols like `:` + +## Version 25.03 + +#### Commands API +- `ejabberdctl`: New option `CTL_OVER_HTTP` ([#4340](https://github.com/processone/ejabberd/issues/4340)) +- `ejabberd_web_admin`: Support commands with tuple arguments +- `mod_adhoc_api`: New module to execute API Commands using Ad-Hoc Commands ([#4357](https://github.com/processone/ejabberd/issues/4357)) +- `mod_http_api`: Sort list elements in a command result +- Show warning when registering command with an existing name +- Fix commands unregistration +- `change_room_option`: Add forgotten support to set `enable_hats` room option +- `change_room_option`: Verify room option value before setting it ([#4337](https://github.com/processone/ejabberd/issues/4337)) +- `create_room_with_opts`: Recommend using `;` and `=` separators +- `list_cluster_detailed`: Fix crash when a node is down +- `mnesia_list_tables`: Allow using this internal command +- `mnesia_table_change_storage`: Allow using this internal command +- `status`: Separate command result with newline +- `update_sql`: Fix updating tables created by ejabberd internally +- `update_sql`: Fix MySQL support + +#### Configuration +- `acl`: Fix bug matching the acl `shared_group: NAME` +- `define_keyword`: New option to define keywords ([#4350](https://github.com/processone/ejabberd/issues/4350)) +- `define_macro`: Add option to `globals()` because it's useless inside `host_config` +- `ejabberd.yml.example`: Enable `mod_muc_occupantid` by default +- Add support to use keywords in toplevel, listener and modules +- Show warning also when deprecated listener option is set as disabled ([#4345](https://github.com/processone/ejabberd/issues/4345)) + +#### Container +- Bump versions to Erlang/OTP 27.3 and Elixir 1.18.3 +- Add `ERL_FLAGS` to compile elixir on qemu cross-platform +- Copy files to stable path, add ecs backwards compatibility +- Fix warning about relative workdir +- Improve entrypoint script: register account, or set random +- Link path to Mnesia spool dir for backwards compatibility +- Place `sockets/` outside `database/` +- Use again direct METHOD, qemu got fixed ([#4280](https://github.com/processone/ejabberd/issues/4280)) +- `ejabberd.yml.example`: Copy main example configuration file +- `ejabberd.yml.example`: Define and use macros in the default configuration file +- `ejabberd.yml.example`: Enable `CTL_OVER_HTTP` by default +- `ejabberd.yml.example`: Listen for webadmin in a port number lower than any other +- `ejabberdapi`: Compile during build +- `CONTAINER.md`: Include documentation for ecs container image + +#### Core and Modules +- `ejabberd_auth`: Add support for `auth_stored_password_types` +- `ejabberd_router`: Don't rewrite "self-addressed" privileged IQs as results ([#4348](https://github.com/processone/ejabberd/issues/4348)) +- `misc`: Fix json version of `json_encode_with_kv_list` for nested kv lists ([#4338](https://github.com/processone/ejabberd/issues/4338)) +- OAuth: Fix crashes when oauth is feed with invalid jid ([#4355](https://github.com/processone/ejabberd/issues/4355)) +- PubSub: Bubble up db errors in `nodetree_tree_sql:set_node` +- `mod_configure`: Add option `access` to let configure the access name +- `mod_mix_pam`: Remove `Channels` roster group of mix channels ([#4297](https://github.com/processone/ejabberd/issues/4297)) +- `mod_muc`: Document MUC room option vcard_xupdate +- `mod_privilege`: Accept non-privileged IQs from privileged components ([#4341](https://github.com/processone/ejabberd/issues/4341)) +- `mod_private`: Improve exception handling +- `mod_private`: Don't warn on conversion errors +- `mod_private`: Handle invalid PEP-native bookmarks +- `mod_private`: Don't crash on invalid bookmarks +- `mod_s2s_bidi`: Stop processing other handlers in s2s_in_handle_info ([#4344](https://github.com/processone/ejabberd/issues/4344)) +- `mod_s2s_bidi`: Fix issue with wrong namespace + +#### Dependencies +- `ex_doc`: Bump to 0.37.2 +- `stringprep`: Bump to 1.0.31 +- `provider_asn1`: Bump to 0.4.1 +- `xmpp` Bump to bring fix for ssdp hash calculation +- `xmpp` Bump to get support for webchat_url ([#3041](https://github.com/processone/ejabberd/issues/3041)) +- `xmpp` Bump to get XEP-0317 Hats namespaces version 0.2.0 +- `xmpp` Bump to bring SSDP to XEP version 0.4 +- `yconf` Bump to support macro inside string + +#### Development and Testing +- `mix.exs`: Keep debug info when building `dev` release +- `mix.exs`: The `ex_doc` dependency is only relevant for the `edoc` Mix environment +- `ext_mod`: add `$libdir/include` to include path +- `ext_mod`: fix greedy include path ([#4359](https://github.com/processone/ejabberd/issues/4359)) +- `gen_mod`: Support registering commands and `hook_subscribe` in `start/2` result +- `c2s_handle_bind`: New event in `ejabberd_c2s` ([#4356](https://github.com/processone/ejabberd/issues/4356)) +- `muc_disco_info_extras`: New event `mod_muc_room` useful for `mod_muc_webchat_url` ([#3041](https://github.com/processone/ejabberd/issues/3041)) +- VSCode: Fix compiling support +- Add tests for config features `define_macro` and `define_keyword` +- Allow test to run using `ct_run` +- Fixes to handle re-running test after `update_sql` +- Uninstall `mod_example` when the tests has finished + +#### Documentation +- Add XEPs that are indirectly supported and required by XEP-0479 +- Document that XEP-0474 0.4.0 was recently upgraded +- Don't use backtick quotes for ejabberd name +- Fix values allowed in db_type of mod_auth_fast documentation +- Reword explanation about ACL names and definitions +- Update moved or broken URLs in documentation + +#### Installers +- Bump Erlang/OTP 27.3 and Elixir 1.18.3 +- Bump OpenSSL 3.4.1 +- Bump crosstool-NG 1.27.0 +- Fix building Termcap and Linux-PAM + +#### Matrix Gateway +- Preserve XMPP message IDs in Matrix rooms +- Better Matrix room topic and room roles to MUC conversion, support room aliases in invites +- Add `muc#user` element to presences and an initial empty subject +- Fix `gen_iq_handler:remove_iq_handler` call +- Properly handle IQ requests +- Support Matrix room aliases +- Fix handling of 3PI events + +#### Unix Domain Socket +- Add support for socket relative path +- Use `/tmp` for temporary socket, as path is restricted to 107 chars +- Handle unix socket when logging remote client +- When stopping listener, delete Unix Domain Socket file +- `get_auto_url` option: Don't build auto URL if port is unix domain socket ([#4345](https://github.com/processone/ejabberd/issues/4345)) + +## Version 24.12 + +#### Miscelanea + +- Elixir: support loading Elixir modules for auth ([#4315](https://github.com/processone/ejabberd/issues/4315)) +- Environment variables `EJABBERD_MACRO` to define macros +- Fix problem starting ejabberd when first host uses SQL, other one mnesia +- HTTP Websocket: Enable `allow_unencrypted_sasl2` on websockets ([#4323](https://github.com/processone/ejabberd/issues/4323)) +- Relax checks for channels bindings for connections using external encryption +- Redis: Add support for unix domain socket ([#4318](https://github.com/processone/ejabberd/issues/4318)) +- Redis: Use eredis 1.7.1 from Nordix when using mix/rebar3 and Erlang 21+ +- `mod_auth_fast`: New module with support XEP-0484: Fast Authentication Streamlining Tokens +- `mod_http_api`: Fix crash when module not enabled (for example, in CT tests) +- `mod_http_api`: New option `default_version` +- `mod_muc`: Make rsm handling in disco items, correctly count skipped rooms +- `mod_offline`: Only delete offline msgs when user has MAM enabled ([#4287](https://github.com/processone/ejabberd/issues/4287)) +- `mod_priviled`: Handle properly roster iq +- `mod_pubsub`: Send notifications on PEP item retract +- `mod_s2s_bidi`: Catch extra case in check for s2s bidi element +- `mod_scram_upgrade`: Don't abort the upgrade +- `mod_shared_roster`: The name of a new group is lowercased +- `mod_shared_roster`: Get back support for `groupid@vhost` in `displayed` +- `mod_stun_disco`: Fix syntax of credentials response + +#### Commands API + +- Change arguments and result to consistent names (API v3) +- `create_rooms_file`: Improve to support vhosts with different config +- `evacuate_kindly`: New command to kick users and prevent login ([#4309](https://github.com/processone/ejabberd/issues/4309)) +- `join_cluster`: Explain that this returns immediately (since 5a34020, 24.06) +- `mod_muc_admin`: Rename argument `name` to `room` for consistency + +#### Documentation + +- Fix some documentation syntax, add links to toplevel, modules and API +- `CONTAINER.md`: Add kubernetes yaml examples to use with podman +- `SECURITY.md`: Add security policy and reporting guidelines +- `ejabberd.service`: Disable the systemd watchdog by default +- `ejabberd.yml.example`: Use non-standard STUN port + +#### WebAdmin + +- Shared group names are case sensitive, use original case instead of lowercase +- Use lowercase username and server authentication credentials +- Fix calculation of node's uptime days +- Fix link to displayed group when it is from another vhost + +## Version 24.10 + +#### Miscelanea + +- `ejabberd_c2s`: Optionally allow unencrypted SASL2 +- `ejabberd_system_monitor`: Handle call by `gen_event:swap_handler` ([#4233](https://github.com/processone/ejabberd/issues/4233)) +- `ejabberd_http_ws`: Remove support for old websocket connection protocol +- `ejabberd_stun`: Omit `auth_realm` log message +- `ext_mod`: Handle `info` message when contrib module transfers table ownership +- `mod_block_strangers`: Add feature announcement to disco-info ([#4039](https://github.com/processone/ejabberd/issues/4039)) +- `mod_mam`: Advertise XEP-0424 feature in server disco-info ([#3340](https://github.com/processone/ejabberd/issues/3340)) +- `mod_muc_admin`: Better handling of malformed jids in `send_direct_invitation` command +- `mod_muc_rtbl`: Fix call to `gen_server:stop` ([#4260](https://github.com/processone/ejabberd/issues/4260)) +- `mod_privilege`: Support "IQ permission" from XEP-0356 0.4.1 ([#3889](https://github.com/processone/ejabberd/issues/3889)) +- `mod_pubsub`: Don't blindly echo PEP notification +- `mod_pubsub`: Skip non-delivery errors for local pubsub generated notifications +- `mod_pubsub`: Fall back to default plugin options +- `mod_pubsub`: Fix choice of node config defaults +- `mod_pubsub`: Fix merging of default node options +- `mod_pubsub`: Fix default node config parsing +- `mod_register`: Support to block IPs in a vhost using `append_host_config` ([#4038](https://github.com/processone/ejabberd/issues/4038)) +- `mod_s2s_bidi`: Add support for S2S Bidirectional +- `mod_scram_upgrade`: Add support for SCRAM upgrade tasks +- `mod_vcard`: Return error stanza when storage doesn't support vcard update ([#4266](https://github.com/processone/ejabberd/issues/4266)) +- `mod_vcard`: Return explicit error stanza when user attempts to modify other's vcard +- Minor improvements to support `mod_tombstones` (#2456) +- Update `fast_xml` to use `use_maps` and remove obsolete elixir files +- Update `fast_tls` and `xmpp` to improve s2s fallback for invalid direct tls connections +- `make-binaries`: Bump dependency versions: Elixir 1.17.2, OpenSSL 3.3.2, ... + +#### Administration + +- `ejabberdctl`: If `ERLANG_NODE` lacks host, add hostname ([#4288](https://github.com/processone/ejabberd/issues/4288)) +- `ejabberd_app`: At server start, log Erlang and Elixir versions +- MySQL: Fix column type in the schema update of `archive` table in schema update + +#### Commands API + +- `get_mam_count`: New command to get number of archived messages for an account +- `set_presence`: Return error when session not found +- `update`: Fix command output +- Add `mam` and `offline` tags to the related purge commands + +#### Code Quality + +- Fix warnings about unused macro definitions reported by Erlang LS +- Fix Elvis report: Fix dollar space syntax +- Fix Elvis report: Remove spaces in weird places +- Fix Elvis report: Don't use ignored variables +- Fix Elvis report: Remove trailing whitespace characters +- Define the types of options that `opt_type.sh` cannot derive automatically +- `ejabberd_http_ws`: Fix dialyzer warnings +- `mod_matrix_gw`: Remove useless option `persist` +- `mod_privilege`: Replace `try...catch` with a clean alternative + +#### Development Help + +- `elvis.config`: Fix file syntax, set vim mode, disable many tests +- `erlang_ls.config`: Let it find paths, update to Erlang 26, enable crossref +- `hooks_deps`: Hide false-positive warnings about `gen_mod` +- `Makefile`: Add support for `make elvis` when using rebar3 +- `.vscode/launch.json`: Experimental support for debugging with Neovim +- CI: Add Elvis tests +- CI: Add XMPP Interop tests +- Runtime: Cache hex.pm archive from rebar3 and mix + +#### Documentation + +- Add links in top-level options documentation to their Docs website sections +- Document which SQL servers can really use `update_sql_schema` +- Improve documentation of `ldap_servers` and `ldap_backups` options ([#3977](https://github.com/processone/ejabberd/issues/3977)) +- `mod_register`: Document behavior when `access` is set to `none` ([#4078](https://github.com/processone/ejabberd/issues/4078)) + +#### Elixir + +- Handle case when elixir support is enabled but not available +- Start ExSync manually to ensure it's started if (and only if) Relive +- `mix.exs`: Fix `mix release` error: `logger` being regular and included application ([#4265](https://github.com/processone/ejabberd/issues/4265)) +- `mix.exs`: Remove from `extra_applications` the apps already defined in `deps` ([#4265](https://github.com/processone/ejabberd/issues/4265)) + +#### WebAdmin + +- Add links in user page to offline and roster pages +- Add new "MAM Archive" page to webadmin +- Improve many pages to handle when modules are disabled +- `mod_admin_extra`: Move some webadmin pages to their modules + +## Version 24.07 + +#### Core + +- `ejabberd_options`: Add trailing `@` to `@VERSION@` parsing +- `mod_http_api`: Fix problem parsing tuples when using OTP 27 json library ([#4242](https://github.com/processone/ejabberd/issues/4242)) +- `mod_http_api`: Restore args conversion of `{"k":"v"}` to tuple lists +- `mod_matrix_gw`: Add misc:json_encode_With_kv_lists and use it in matrix sign function +- `mod_muc`: Output `muc#roominfo_avatarhash` in room disco info as per updated XEP-0486 ([#4234](https://github.com/processone/ejabberd/issues/4234)) +- `mod_muc`: Improve cross version handling of muc retractions +- `node_pep`: Add missing feature `item-ids` to node_pep +- `mod_register`: Send welcome message as `chat` too ([#4246](https://github.com/processone/ejabberd/issues/4246)) +- `ejabberd_hooks`: Support for ejabberd hook subscribers, useful for [mod_prometheus](https://github.com/processone/ejabberd-contrib/tree/master/mod_prometheus) +- `ejabberd.app`: Don't add `iex` to included_applications +- `make-installers`: Fix path in scripts in regular user install ([#4258](https://github.com/processone/ejabberd/issues/4258)) +- Test: New tests for API commands + +#### Documentation + +- `mod_matrix_gw`: Fix `matrix_id_as_jid` option documentation +- `mod_register`: Add example configuration of `welcome_message` option +- `mix.exs`: Add ejabberd example config files to the hex package +- Update `CODE_OF_CONDUCT.md` + +#### ext_mod + +- Fetch dependencies from hex.pm when mix is available +- files_to_path is deprecated, use compile_to_path +- Compile all Elixir files in a library with one function call +- Improve error result when problem compiling elixir file +- Handle case when contrib module has no `*.ex` and no `*.erl` +- `mix.exs`: Include Elixir's Logger in the OTP release, useful for [mod_libcluster](https://github.com/processone/ejabberd-contrib/tree/master/mod_libcluster) + +#### Logs + +- Print message when starting ejabberd application fails +- Use error_logger when printing startup failure message +- Use proper format depending on the formatter ([#4256](https://github.com/processone/ejabberd/issues/4256)) + +#### SQL + +- Add option `update_sql_schema_timeout` to allow schema update use longer timeouts +- Add ability to specify custom timeout for sql operations +- Allow to configure number of restart in `sql_transaction()` +- Make sql query in testsuite compatible with pg9.1 +- In `mysql.sql`, fix update instructions for the `archive` table, `origin_id` column ([#4259](https://github.com/processone/ejabberd/issues/4259)) + +#### WebAdmin + +- `ejabberd.yml.example`: Add `api_permissions` group for webadmin ([#4249](https://github.com/processone/ejabberd/issues/4249)) +- Don't use host from url in webadmin, prefer host used for authentication +- Fix number of accounts shown in the online-users page +- Fix crash when viewing old shared roster groups ([#4245](https://github.com/processone/ejabberd/issues/4245)) +- Support groupid with spaces when making shared roster result ([#4245](https://github.com/processone/ejabberd/issues/4245)) + +## Version 24.06 + +#### Core + +- `econf`: Add ability to use additional custom errors when parsing options +- `ejabberd_logger`: Reloading configuration will update logger settings +- `gen_mod`: Add support to specify a hook global, not vhost-specific +- `mod_configure`: Retract `Get User Password` command to update XEP-0133 1.3.0 +- `mod_conversejs`: Simplify support for `@HOST@` in `default_domain` option ([#4167](https://github.com/processone/ejabberd/issues/4167)) +- `mod_mam`: Document that XEP-0441 is implemented as well +- `mod_mam`: Update support for XEP-0425 version 0.3.0, keep supporting 0.2.1 ([#4193](https://github.com/processone/ejabberd/issues/4193)) +- `mod_matrix_gw`: Fix support for `@HOST@` in `matrix_domain` option ([#4167](https://github.com/processone/ejabberd/issues/4167)) +- `mod_muc_log`: Hide join/leave lines, add method to show them +- `mod_muc_log`: Support `allowpm` introduced in 2bd61ab +- `mod_muc_room`: Use ejabberd hooks instead of function calls to `mod_muc_log` ([#4191](https://github.com/processone/ejabberd/issues/4191)) +- `mod_private`): Cope with bookmark decoding errors +- `mod_vcard_xupdate`: Send hash after avatar get set for first time +- `prosody2ejabberd`: Handle the `approved` attribute. As feature isn't implemented, discard it ([#4188](https://github.com/processone/ejabberd/issues/4188)) + +#### SQL + +- `update_sql_schema`: Enable this option by default +- CI: Don't load database schema files for mysql and pgsql +- Support Unix Domain Socket with updated p1_pgsql and p1_mysql ([#3716](https://github.com/processone/ejabberd/issues/3716)) +- Fix handling of `mqtt_pub` table definition from `mysql.sql` and fix `should_update_schema/1` in `ejabberd_sql_schema.erl` +- Don't start sql connection pools for unknown hosts +- Add `update_primary_key` command to sql schema updater +- Fix crash running `export2sql` when MAM enabled but MUC disabled +- Improve detection of types in odbc + +#### Commands API + +- New ban commands use private storage to keep ban information ([#4201](https://github.com/processone/ejabberd/issues/4201)) +- `join_cluster_here`: New command to join a remote node into our local cluster +- Don't name integer and string results in API examples ([#4198](https://github.com/processone/ejabberd/issues/4198)) +- `get_user_subscriptions`: Fix validation of user field in that command +- `mod_admin_extra`: Handle case when `mod_private` is not enabled ([#4201](https://github.com/processone/ejabberd/issues/4201)) +- `mod_muc_admin`: Improve validation of arguments in several commands + +#### Compile + +- `ejabberdctl`: Comment ERTS_VSN variable when not used ([#4194](https://github.com/processone/ejabberd/issues/4194)) +- `ejabberdctl`: Fix iexlive after `make prod` when using Elixir +- `ejabberdctl`: If `INET_DIST_INTERFACE` is IPv6, set required option ([#4189](https://github.com/processone/ejabberd/issues/4189)) +- `ejabberdctl`: Make native dynamic node names work when using fully qualified domain names +- `rebar.config.script`: Support relaxed dependency version ([#4192](https://github.com/processone/ejabberd/issues/4192)) +- `rebar.config`: Update deps version to rebar3's relaxed versioning +- `rebar.lock`: Track file, now that rebar3 uses loose dependency versioning +- `configure.ac`: When using rebar3, unlock dependencies that are disabled ([#4212](https://github.com/processone/ejabberd/issues/4212)) +- `configure.ac`: When using rebar3 with old Erlang, unlock some dependencies ([#4213](https://github.com/processone/ejabberd/issues/4213)) +- `mix:exs`: Move `xmpp` from `included_applications` to `applications` + +#### Dependencies + +- Base64url: Use only when using rebar2 and Erlang lower than 24 +- Idna: Bump from 6.0.0 to 6.1.1 +- Jiffy: Use Json module when Erlang/OTP 27, jiffy with older ones +- Jose: Update to the new 1.11.10 for Erlang/OTP higher than 23 +- Luerl: Update to 1.2.0 when OTP same or higher than 20, simplifies commit a09f222 +- P1_acme: Update to support Jose 1.11.10 and Ipv6 support ([#4170](https://github.com/processone/ejabberd/issues/4170)) +- P1_acme: Update to use Erlang's json library instead of jiffy when OTP 27 +- Port_compiler: Update to 1.15.0 that supports Erlang/OTP 27.0 + +#### Development Help + +- `.gitignore`: Ignore ctags/etags files +- `make dialyzer`: Add support to run Dialyzer with Mix +- `make format|indent`: New targets to format and indent source code +- `make relive`: Add Sync tool with Rebar3, ExSync with Mix +- `hook_deps`: Use precise name: hooks are added and later deleted, not removed +- `hook_deps`: Fix to handle FileNo as tuple `{FileNumber, CharacterPosition}` +- Add support to test also EUnit suite +- Fix `code:lib_dir` call to work with Erlang/OTP 27.0-rc2 +- Set process flags when Erlang/OTP 27 to help debugging +- Test retractions in mam_tests + +#### Documentation + +- Add some XEPs support that was forgotten +- Fix documentation links to new URLs generated by MkDocs +- Remove `...` in example configuration: it is assumed and reduces verbosity +- Support for version note in modules too +- Mark toplevel options, commands and modules that changed in latest version +- Now modules themselves can have version annotations in `note` + +#### Installers and Container + +- make-binaries: Bump Erlang/OTP to 26.2.5 and Elixir 1.16.3 +- make-binaries: Bump OpenSSL to 3.3.1 +- make-binaries: Bump Linux-PAM to 1.6.1 +- make-binaries: Bump Expat to 2.6.2 +- make-binaries: Revert temporarily an OTP commit that breaks MSSQL ([#4178](https://github.com/processone/ejabberd/issues/4178)) +- CONTAINER.md: Invalid `CTL_ON_CREATE` usage in docker-compose example + +#### WebAdmin + +- ejabberd_ctl: Improve parsing of commas in arguments +- ejabberd_ctl: Fix output of UTF-8-encoded binaries +- WebAdmin: Remove webadmin_view for now, as commands allow more fine-grained permissions +- WebAdmin: Unauthorized response: include some text to direct to the logs +- WebAdmin: Improve home page +- WebAdmin: Sort alphabetically the menu items, except the most used ones +- WebAdmin: New login box in the left menu bar +- WebAdmin: Add make_command functions to produce HTML command element +- Document 'any' argument and result type, useful for internal commands +- Commands with 'internal' tag: don't list and block execution by frontends +- WebAdmin: Move content to commands; new pages; hook changes; new commands + +## Version 24.02 + +#### Core: + +- Added Matrix gateway in `mod_matrix_gw` +- Support SASL2 and Bind2 +- Support tls-server-end-point channel binding and sasl2 codec +- Support tls-exporter channel binding +- Support XEP-0474: SASL SCRAM Downgrade Protection +- Fix presenting features and returning results of inline bind2 elements +- `disable_sasl_scram_downgrade_protection`: New option to disable XEP-0474 +- `negotiation_timeout`: Increase default value from 30s to 2m +- mod_carboncopy: Teach how to interact with bind2 inline requests + +#### Other: + +- ejabberdctl: Fix startup problem when having set `EJABBERD_OPTS` and logger options +- ejabberdctl: Set EJABBERD_OPTS back to `""`, and use previous flags as example +- eldap: Change logic for `eldap tls_verify=soft` and `false` +- eldap: Don't set `fail_if_no_peer_cert` for eldap ssl client connections +- Ignore hints when checking for chat states +- mod_mam: Support XEP-0424 Message Retraction +- mod_mam: Fix XEP-0425: Message Moderation with SQL storage +- mod_ping: Support XEP-0198 pings when stream management is enabled +- mod_pubsub: Normalize pubsub `max_items` node options on read +- mod_pubsub: PEP nodetree: Fix reversed logic in node fixup function +- mod_pubsub: Only care about PEP bookmarks options when creating node from scratch + +#### SQL: + +- MySQL: Support `sha256_password` auth plugin +- ejabberd_sql_schema: Use the first unique index as a primary key +- Update SQL schema files for MAM's XEP-0424 +- New option [`sql_flags`](https://docs.ejabberd.im/admin/configuration/toplevel/#sql-flags): right now only useful to enable `mysql_alternative_upsert` + +#### Installers and Container: + +- Container: Add ability to ignore failures in execution of `CTL_ON_*` commands +- Container: Update to Erlang/OTP 26.2, Elixir 1.16.1 and Alpine 3.19 +- Container: Update this custom ejabberdctl to match the main one +- make-binaries: Bump OpenSSL 3.2.1, Erlang/OTP 26.2.2, Elixir 1.16.1 +- make-binaries: Bump many dependency versions + +#### Commands API: + +- `print_sql_schema`: New command available in ejabberdctl command-line script +- ejabberdctl: Rework temporary node name generation +- ejabberdctl: Print argument description, examples and note in help +- ejabberdctl: Document exclusive ejabberdctl commands like all the others +- Commands: Add a new `muc_sub` tag to all the relevant commands +- Commands: Improve syntax of many commands documentation +- Commands: Use list arguments in many commands that used separators +- Commands: `set_presence`: switch priority argument from string to integer +- ejabberd_commands: Add the command API version as [a tag `vX`](https://docs.ejabberd.im/developer/ejabberd-api/admin-tags/#v1) +- ejabberd_ctl: Add support for list and tuple arguments +- ejabberd_xmlrpc: Fix support for restuple error response +- mod_http_api: When no specific API version is requested, use the latest + +#### Compilation with Rebar3/Elixir/Mix: +- Fix compilation with Erlang/OTP 27: don't use the reserved word 'maybe' +- configure: Fix explanation of `--enable-group` option ([#4135](https://github.com/processone/ejabberd/issues/4135)) +- Add observer and runtime_tools in releases when `--enable-tools` +- Update "make translations" to reduce build requirements +- Use Luerl 1.0 for Erlang 20, 1.1.1 for 21-26, and temporary fork for 27 +- Makefile: Add `install-rel` and `uninstall-rel` +- Makefile: Rename `make rel` to `make prod` +- Makefile: Update `make edoc` to use ExDoc, requires mix +- Makefile: No need to use `escript` to run rebar|rebar3|mix +- configure: If `--with-rebar=rebar3` but rebar3 not system-installed, use local one +- configure: Use Mix or Rebar3 by default instead of Rebar2 to compile ejabberd +- ejabberdctl: Detect problem running iex or etop and show explanation +- Rebar3: Include Elixir files when making a release +- Rebar3: Workaround to fix protocol consolidation +- Rebar3: Add support to compile Elixir dependencies +- Rebar3: Compile explicitly our Elixir files when `--enable-elixir` +- Rebar3: Provide proper path to `iex` +- Rebar/Rebar3: Update binaries to work with Erlang/OTP 24-27 +- Rebar/Rebar3: Remove Elixir as a rebar dependency +- Rebar3/Mix: If `dev` profile/environment, enable tools automatically +- Elixir: Fix compiling ejabberd as a dependency ([#4128](https://github.com/processone/ejabberd/issues/4128)) +- Elixir: Fix ejabberdctl start/live when installed +- Elixir: Fix: `FORMATTER ERROR: bad return value` ([#4087](https://github.com/processone/ejabberd/issues/4087)) +- Elixir: Fix: Couldn't find file `Elixir Hex API` +- Mix: Enable stun by default when `vars.config` not found +- Mix: New option `vars_config_path` to set path to `vars.config` ([#4128](https://github.com/processone/ejabberd/issues/4128)) +- Mix: Fix ejabberdctl iexlive problem locating iex in an OTP release + +## Version 23.10 + +#### Compilation: + +- Erlang/OTP: Raise the requirement to Erlang/OTP 20.0 as a minimum +- CI: Update tests to Erlang/OTP 26 and recent Elixir +- Move Xref and Dialyzer options from workflows to `rebar.config` +- Add sections to `rebar.config` to organize its content +- Dialyzer dirty workarounds because `re:mp()` is not an exported type +- When installing module already configured, keep config as example +- Elixir 1.15 removed support for `--app` +- Elixir: Improve support to stop external modules written in Elixir +- Elixir: Update syntax of function calls as recommended by Elixir compiler +- Elixir: When building OTP release with mix, keep `ERLANG_NODE=ejabberd@localhost` +- `ejabberdctl`: Pass `ERLANG_OPTS` when calling `erl` to parse the `INET_DIST_INTERFACE` ([#4066](https://github.com/processone/ejabberd/issues/#4066) + +#### Commands: + +- `create_room_with_opts`: Fix typo and move examples to `args_example` ([#4080](https://github.com/processone/ejabberd/issues/#4080)) +- `etop`: Let `ejabberdctl etop` work in a release (if `observer` application is available) +- `get_roster`: Command now returns groups in a list instead of newlines ([#4088](https://github.com/processone/ejabberd/issues/#4088)) +- `halt`: New command to halt ejabberd abruptly with an error status code +- `ejabberdctl`: Fix calling ejabberdctl command with wrong number of arguments with Erlang 26 +- `ejabberdctl`: Improve printing lists in results +- `ejabberdctl`: Support `policy=user` in the help and return proper arguments +- `ejabberdctl`: Document how to stop a debug shell: control+g + +#### Container: + +- Dockerfile: Add missing dependency for mssql databases +- Dockerfile: Reorder stages and steps for consistency +- Dockerfile: Use Alpine as base for `METHOD=package` +- Dockerfile: Rename packages to improve compatibility +- Dockerfile: Provide specific OTP and elixir vsn for direct compilation +- Halt ejabberd if a command in `CTL_ON_` fails during ejabberd startup + +#### Core: + +- `auth_external_user_exists_check`: New option ([#3377](https://github.com/processone/ejabberd/issues/#3377)) +- `gen_mod`: Extend `gen_mod` API to simplify hooks and IQ handlers registration +- `gen_mod`: Add shorter forms for `gen_mod` hook/`iq_handler` API +- `gen_mod`: Update modules to the new `gen_mod` API +- `install_contrib_modules`: New option to define contrib modules to install automatically +- `unix_socket`: New listener option, useful when setting unix socket files ([#4059](https://github.com/processone/ejabberd/issues/#4059)) +- `ejabberd_systemd`: Add a few debug messages +- `ejabberd_systemd`: Avoid using `gen_server` timeout ([#4054](https://github.com/processone/ejabberd/issues/#4054))([#4058](https://github.com/processone/ejabberd/issues/#4058)) +- `ejabberd_listener`: Increase default listen queue backlog value to 128, which is the default value on both Linux and FreeBSD ([#4025](https://github.com/processone/ejabberd/issues/#4025)) +- OAuth: Handle `badpass` error message +- When sending message on behalf of user, trigger `user_send_packet` ([#3990](https://github.com/processone/ejabberd/issues/#3990)) +- Web Admin: In roster page move the `AddJID` textbox to top ([#4067](https://github.com/processone/ejabberd/issues/#4067)) +- Web Admin: Show a warning when visiting webadmin with non-privileged account ([#4089](https://github.com/processone/ejabberd/issues/#4089)) + +#### Docs: + +- Example configuration: clarify 5223 tls options; specify s2s shaper +- Make sure that `policy=user` commands have `host` instead of `server` arg in docs +- Improve syntax of many command descriptions for the Docs site +- Move example Perl extauth script from ejabberd git to Docs site +- Remove obsolete example files, and add link in Docs to the archived copies + +#### Installers (`make-binaries`): +- Bump Erlang/OTP version to 26.1.1, and other dependencies +- Remove outdated workaround +- Don't build Linux-PAM examples +- Fix check for current Expat version +- Apply minor simplifications +- Don't duplicate config entries +- Don't hard-code musl version +- Omit unnecessary glibc setting +- Set kernel version for all builds +- Let curl fail on HTTP errors + +#### Modules: + +- `mod_muc_log`: Add trailing backslash to URLs shown in disco info +- `mod_muc_occupantid`: New module with support for XEP-0421 Occupant Id ([#3397](https://github.com/processone/ejabberd/issues/#3397)) +- `mod_muc_rtbl`: Better error handling in ([#4050](https://github.com/processone/ejabberd/issues/#4050)) +- `mod_private`: Add support for XEP-0402 PEP Native Bookmarks +- `mod_privilege`: Don't fail to edit roster ([#3942](https://github.com/processone/ejabberd/issues/#3942)) +- `mod_pubsub`: Fix usage of `plugins` option, which produced `default_node_config` ignore ([#4070](https://github.com/processone/ejabberd/issues/#4070)) +- `mod_pubsub`: Add `pubsub_delete_item` hook +- `mod_pubsub`: Report support of `config-node-max` in pep +- `mod_pubsub`: Relay pubsub iq queries to muc members without using bare jid ([#4093](https://github.com/processone/ejabberd/issues/#4093)) +- `mod_pubsub`: Allow pubsub node owner to overwrite items published by other persons +- `mod_push_keepalive`: Delay `wake_on_start` +- `mod_push_keepalive`: Don't let hook crash +- `mod_push`: Add `notify_on` option +- `mod_push`: Set `last-message-sender` to bare JID +- `mod_register_web`: Make redirect to page that end with `/` ([#3177](https://github.com/processone/ejabberd/issues/#3177)) +- `mod_shared_roster_ldap`: Don't crash in `get_member_jid` on empty output ([#3614](https://github.com/processone/ejabberd/issues/#3614)) + +#### MUC: + +- Add support to register nick in a room ([#3455](https://github.com/processone/ejabberd/issues/#3455)) +- Convert `allow_private_message` MUC room option to `allowpm` ([#3736](https://github.com/processone/ejabberd/issues/#3736)) +- Update xmpp version to send `roomconfig_changesubject` in disco#info ([#4085](https://github.com/processone/ejabberd/issues/#4085)) +- Fix crash when loading room from DB older than ffa07c6, 23.04 +- Fix support to retract a MUC room message +- Don't always store messages passed through `muc_filter_message` ([#4083](https://github.com/processone/ejabberd/issues/#4083)) +- Pass also MUC room retract messages over the `muc_filter_message` ([#3397](https://github.com/processone/ejabberd/issues/#3397)) +- Pass MUC room private messages over the `muc_filter_message` too ([#3397](https://github.com/processone/ejabberd/issues/#3397)) +- Store the subject author JID, and run `muc_filter_message` when sending subject ([#3397](https://github.com/processone/ejabberd/issues/#3397)) +- Remove existing role information for users that are kicked from room ([#4035](https://github.com/processone/ejabberd/issues/#4035)) +- Expand rule "mucsub subscribers are members in members only rooms" to more places + +#### SQL: + +- Add ability to force alternative upsert implementation in mysql +- Properly parse mysql version even if it doesn't have type tag +- Use prepared statement with mysql +- Add alternate version of mysql upsert +- `ejabberd_auth_sql`: Reset scram fields when setting plain password +- `mod_privacy_sql`: Fix return values from `calculate_diff` +- `mod_privacy_sql`: Optimize `set_list` +- `mod_privacy_sql`: Use more efficient way to calculate changes in `set_privacy_list` + +## Version 23.04 + +#### General: + +- New `s2s_out_bounce_packet` hook +- Re-allow anonymous connection for connection without client certificates ([#3985](https://github.com/processone/ejabberd/issues/3985)) +- Stop `ejabberd_system_monitor` before stopping node +- `captcha_url` option now accepts `auto` value, and it's the default +- `mod_mam`: Add support for XEP-0425: Message Moderation +- `mod_mam_sql`: Fix problem with results of mam queries using rsm with max and before +- `mod_muc_rtbl`: New module for Real-Time Block List for MUC rooms ([#4017](https://github.com/processone/ejabberd/issues/4017)) +- `mod_roster`: Set roster name from XEP-0172, or the stored one ([#1611](https://github.com/processone/ejabberd/issues/1611)) +- `mod_roster`: Preliminary support to store extra elements in subscription request ([#840](https://github.com/processone/ejabberd/issues/840)) +- `mod_pubsub`: Pubsub xdata fields `max_item/item_expira/children_max` use `max` not `infinity` +- `mod_vcard_xupdate`: Invalidate `vcard_xupdate` cache on all nodes when vcard is updated + +#### Admin: + +- `ext_mod`: Improve support for loading `*.so` files from `ext_mod` dependencies +- Improve output in `gen_html_doc_for_commands` command +- Fix ejabberdctl output formatting ([#3979](https://github.com/processone/ejabberd/issues/3979)) +- Log HTTP handler exceptions + +#### MUC: + +- New command `get_room_history` +- Persist `none` role for outcasts +- Try to populate room history from mam when unhibernating +- Make `mod_muc_room:set_opts` process persistent flag first +- Allow passing affiliations and subscribers to `create_room_with_opts` command +- Store state in db in `mod_muc:create_room()` +- Make subscribers members by default + +#### SQL schemas: + +- Fix a long standing bug in new schema migration +- `update_sql` command: Many improvements in new schema migration +- `update_sql` command: Add support to migrate MySQL too +- Change PostgreSQL SERIAL to BIGSERIAL columns +- Fix minor SQL schema inconsistencies +- Remove unnecessary indexes +- New SQL schema migrate fix + +#### MS SQL: + +- MS SQL schema fixes +- Add `new` schema for MS SQL +- Add MS SQL support for new schema migration +- Minor MS SQL improvements +- Fix MS SQL error caused by `ORDER BY` in subquery + +#### SQL Tests: + +- Add support for running tests on MS SQL +- Add ability to run tests on upgraded DB +- Un-deprecate `ejabberd_config:set_option/2` +- Use python3 to run `extauth.py` for tests +- Correct README for creating test docker MS SQL DB +- Fix TSQLlint warnings in MSSQL test script + +#### Testing: + +- Fix Shellcheck warnings in shell scripts +- Fix Remark-lint warnings +- Fix Prospector and Pylint warnings in test `extauth.py` +- Stop testing ejabberd with Erlang/OTP 19.3, as Github Actions no longer supports ubuntu-18.04 +- Test only with oldest OTP supported (20.0), newest stable (25.3) and bleeding edge (26.0-rc2) +- Upload Common Test logs as artifact in case of failure + +#### `ecs` container image: +- Update Alpine to 3.17 to get Erlang/OTP 25 and Elixir 1.14 +- Add `tini` as runtime init +- Set `ERLANG_NODE` fixed to `ejabberd@localhost` +- Upload images as artifacts to Github Actions +- Publish tag images automatically to ghcr.io + +#### `ejabberd` container image: +- Update Alpine to 3.17 to get Erlang/OTP 25 and Elixir 1.14 +- Add `METHOD` to build container using packages ([#3983](https://github.com/processone/ejabberd/issues/3983)) +- Add `tini` as runtime init +- Detect runtime dependencies automatically +- Remove unused Mix stuff: ejabberd script and static COOKIE +- Copy captcha scripts to `/opt/ejabberd-*/lib` like the installers +- Expose only `HOME` volume, it contains all the required subdirs +- ejabberdctl: Don't use `.../releases/COOKIE`, it's no longer included + +#### Installers: + +- make-binaries: Bump versions, e.g. erlang/otp to 25.3 +- make-binaries: Fix building with erlang/otp v25.x +- make-packages: Fix for installers workflow, which didn't find lynx + +## Version 23.01 + +#### General: + +- Add `misc:uri_parse/2` to allow declaring default ports for protocols +- CAPTCHA: Add support to define module instead of path to script +- Clustering: Handle `mnesia_system_event mnesia_up` when other node joins this ([#3842](https://github.com/processone/ejabberd/issues/3842)) +- ConverseJS: Don't set i18n option because Converse enforces it instead of browser lang ([#3951](https://github.com/processone/ejabberd/issues/3951)) +- ConverseJS: Try to redirect access to files `mod_conversejs` to CDN when there is no local copies +- ext_mod: compile C files and install them in ejabberd's `priv` +- ext_mod: Support to get module status from Elixir modules +- make-binaries: reduce log output +- make-binaries: Bump zlib version to 1.2.13 +- MUC: Don't store mucsub presence events in offline storage +- MUC: `hibernation_time` is not an option worth storing in room state ([#3946](https://github.com/processone/ejabberd/issues/3946)) +- Multicast: Jid format when `multicastc` was cached ([#3950](https://github.com/processone/ejabberd/issues/3950)) +- mysql: Pass `ssl` options to mysql driver +- pgsql: Do not set `standard_conforming_strings` to `off` ([#3944](https://github.com/processone/ejabberd/issues/3944)) +- OAuth: Accept `jid` as a HTTP URL query argument +- OAuth: Handle when client is not identified +- PubSub: Expose the `pubsub#type` field in `disco#info` query to the node ([#3914](https://github.com/processone/ejabberd/issues/3914)) +- Translations: Update German translation + +#### Admin: + +- `api_permissions`: Fix option crash when doesn't have `who:` section +- `log_modules_fully`: New option to list modules that will log everything +- `outgoing_s2s_families`: Changed option's default to IPv6, and fall back to IPv4 +- Fix bash completion when using Relive or other install methods +- Fix portability issue with some shells ([#3970](https://github.com/processone/ejabberd/issues/3970)) +- Allow admin command to subscribe new users to `members_only` rooms +- Use alternative `split/2` function that works with Erlang/OTP as old as 19.3 +- Silent warning in OTP24 about not specified `cacerts` in SQL connections +- Fix compilation warnings with Elixir 1.14 + +#### DOAP: + +- Support extended `-protocol` erlang attribute +- Add extended RFCs and XEP details to some protocol attributes +- `tools/generate-doap.sh`: New script to generate DOAP file, add `make doap` ([#3915](https://github.com/processone/ejabberd/issues/3915)) +- `ejabberd.doap`: New DOAP file describing ejabberd supported protocols + +#### MQTT: + +- Add MQTT bridge module +- Add support for certificate authentication in MQTT bridge +- Implement reload in MQTT bridge +- Add support for websockets to MQTT bridge +- Recognize ws5/wss5 urls in MQTT bridge +- `mqtt_publish`: New hook for MQTT publish event +- `mqtt_(un)subscribe`: New hooks for MQTT subscribe & unsubscribe events + +#### VSCode: + +- Improve `.devcontainer` to use use devcontainer image and `.vscode` +- Add `.vscode` files to instruct VSCode how to run ejabberd +- Add Erlang LS default configuration +- Add Elvis default configuration + +## Version 22.10 + +#### Core: + +- Add `log_burst_limit_*` options ([#3865](https://github.com/processone/ejabberd/issues/3865)) +- Support `ERL_DIST_PORT` option to work without epmd +- Auth JWT: Catch all errors from `jose_jwt:verify` and log debugging details ([#3890](https://github.com/processone/ejabberd/issues/3890)) +- CAPTCHA: Support `@VERSION@` and `@SEMVER@` in `captcha_cmd` option ([#3835](https://github.com/processone/ejabberd/issues/3835)) +- HTTP: Fix unix socket support ([#3894](https://github.com/processone/ejabberd/issues/3894)) +- HTTP: Handle invalid values in `X-Forwarded-For` header more gracefuly +- Listeners: Let module take over socket +- Listeners: Don't register listeners that failed to start in config reload +- `mod_admin_extra`: Handle empty roster group names +- `mod_conversejs`: Fix crash when mod_register not enabled ([#3824](https://github.com/processone/ejabberd/issues/3824)) +- `mod_host_meta`: Complain at start if listener is not encrypted +- `mod_ping`: Fix regression on `stop_ping` in clustering context ([#3817](https://github.com/processone/ejabberd/issues/3817)) +- `mod_pubsub`: Don't crash on command failures +- `mod_shared_roster`: Fix cache invalidation +- `mod_shared_roster_ldap`: Update roster_get hook to use `#roster_item{}` +- `prosody2ejabberd`: Fix parsing of scram password from prosody + +#### MIX: + +- Fix MIX's filter_nodes +- Return user jid on join +- `mod_mix_pam`: Add new MIX namespaces to disco features +- `mod_mix_pam`: Add handling of IQs with newer MIX namespaces +- `mod_mix_pam`: Do roster pushes on join/leave +- `mod_mix_pam`: Parse sub elements of the mix join remote result +- `mod_mix_pam`: Provide MIX channels as roster entries via hook +- `mod_mix_pam`: Display joined channels on webadmin page +- `mod_mix_pam`: Adapt to renaming of `participant-id` from mix_roster_channel record +- `mod_roster`: Change hook type from `#roster{}` to `#roster_item{}` +- `mod_roster`: Respect MIX `` setting +- `mod_roster`: Adapt to change of mix_annotate type to boolean in roster_query +- `mod_shared_roster`: Fix wrong hook type `#roster{}` (now `#roster_item{}`) + +#### MUC: + +- Store role, and use it when joining a moderated room ([#3330](https://github.com/processone/ejabberd/issues/3330)) +- Don't persist `none` role ([#3330](https://github.com/processone/ejabberd/issues/3330)) +- Allow MUC service admins to bypass max_user_conferences limitation +- Show allow_query_users room option in disco info ([#3830](https://github.com/processone/ejabberd/issues/3830)) +- mod_muc_room: Don't set affiliation to `none` if it's already `none` in `process_item_change/3` +- Fix mucsub unsubscribe notification payload to have muc_unsubcribe in it +- Allow muc_{un}subscribe hooks to modify sent packets +- Pass room state to muc_{un}subscribed hook +- The archive_msg export fun requires MUC Service for room archives +- Export `mod_muc_admin:get_room_pid/2` +- Export function for getting room diagnostics + +#### SQL: + +- Handle errors reported from begin/commit inside transaction +- Make connection close errors bubble up from inside sql transaction +- Make first sql reconnect wait shorter time +- React to sql driver process exit earlier +- Skip connection exit message when we triggered reconnection +- Add syntax_tools to applications, required when using ejabberd_sql_pt ([#3869](https://github.com/processone/ejabberd/issues/3869)) +- Fix mam delete_old_messages_batch for sql backend +- Use `INSERT ... ON DUPLICATE KEY UPDATE` for upsert on mysql +- Update mysql library +- Catch mysql connection being close earlier + +#### Build: + +- `make all`: Generate start scripts here, not in `make install` ([#3821](https://github.com/processone/ejabberd/issues/3821)) +- `make clean`: Improve this and "distclean" +- `make deps`: Ensure deps configuration is ran when getting deps ([#3823](https://github.com/processone/ejabberd/issues/3823)) +- `make help`: Update with recent changes +- `make install`: Don't leak DESTDIR in files copied by 'make install' +- `make options`: Fix error reporting on OTP24+ +- `make update`: configure also in this case, similarly to `make deps` +- Add definition to detect OTP older than 25, used by ejabberd_auth_http +- Configure eimp with mix to detect image convert properly ([#3823](https://github.com/processone/ejabberd/issues/3823)) +- Remove unused macro definitions detected by rebar3_hank +- Remove unused header files which content is already in xmpp library + +#### Container: + +- Get ejabberd-contrib sources to include them +- Copy `.ejabberd-modules` directory if available +- Do not clone repo inside container build +- Use `make deps`, which performs additional steps ([#3823](https://github.com/processone/ejabberd/issues/3823)) +- Support `ERL_DIST_PORT` option to work without epmd +- Copy `ejabberd-docker-install.bat` from docker-ejabberd git and rename it +- Set a less frequent healthcheck to reduce CPU usage ([#3826](https://github.com/processone/ejabberd/issues/3826)) +- Fix build instructions, add more podman examples + +#### Installers: + +- make-binaries: Include CAPTCHA script with release +- make-binaries: Edit rebar.config more carefully +- make-binaries: Fix linking of EIMP dependencies +- make-binaries: Fix GitHub release version checks +- make-binaries: Adjust Mnesia spool directory path +- make-binaries: Bump Erlang/OTP version to 24.3.4.5 +- make-binaries: Bump Expat and libpng versions +- make-packages: Include systemd unit with RPM +- make-packages: Fix permissions on RPM systems +- make-installers: Support non-root installation +- make-installers: Override code on upgrade +- make-installers: Apply cosmetic changes + +#### External modules: + +- ext_mod: Support managing remote nodes in the cluster +- ext_mod: Handle correctly when COMMIT.json not found +- Don't bother with COMMIT.json user-friendly feature in automated user case +- Handle not found COMMIT.json, for example in GH Actions +- Add WebAdmin page for managing external modules + +#### Workflows Actions: + +- Update workflows to Erlang 25 +- Update workflows: Ubuntu 18 is deprecated and 22 is added +- CI: Remove syntax_tools from applications, as fast_xml fails Dialyzer +- Runtime: Add Xref options to be as strict as CI + +## Version 22.05 + +#### Core +- C2S: Don't expect that socket will be available in `c2s_terminated` hook +- Event handling process hook tracing +- Guard against `erlang:system_info(logical_processors)` not always returning a number +- `domain_balancing`: Allow for specifying `type` only, without specifying `component_number` + +#### MQTT +- Add TLS certificate authentication for MQTT connections +- Fix login when generating client id, keep connection record (#3593) +- Pass property name as expected in mqtt_codec (fixes login using MQTT 5) +- Support MQTT subscriptions spread over the cluster (#3750) + +#### MUC +- Attach meta field with real jid to mucsub subscription events +- Handle user removal +- Stop empty MUC rooms 30 seconds after creation +- `default_room_options`: Update options configurable +- `subscribe_room_many_max_users`: New option in `mod_muc_admin` + +#### mod_conversejs +- Improved options to support `@HOST@` and `auto` values +- Set `auth` and `register` options based on ejabberd configuration +- `conversejs_options`: New option +- `conversejs_resources`: New option + +#### PubSub +- `mod_pubsub`: Allow for limiting `item_expire` value +- `mod_pubsub`: Unsubscribe JID on whitelist removal +- `node_pep`: Add config-node and multi-items features (#3714) + +#### SQL +- Improve compatibility with various db engine versions +- Sync old-to-new schema script with reality (#3790) +- Slight improvement in MSSQL testing support, but not yet complete + +#### Other Modules +- `auth_jwt`: Checking if an user is active in SM for a JWT authenticated user (#3795) +- `mod_configure`: Implement Get List of Registered/Online Users from XEP-0133 +- `mod_host_meta`: New module to serve host-meta files, see XEP-0156 +- `mod_mam`: Store all mucsub notifications not only message notifications +- `mod_ping`: Delete ping timer if resource is gone after the ping has been sent +- `mod_ping`: Don't send ping if resource is gone +- `mod_push`: Fix notifications for pending sessions (XEP-0198) +- `mod_push`: Keep push session ID on session resume +- `mod_shared_roster`: Adjust special group cache size +- `mod_shared_roster`: Normalize JID on unset_presence (#3752) +- `mod_stun_disco`: Fix parsing of IPv6 listeners + +#### Dependencies +- autoconf: Supported from 2.59 to the new 2.71 +- fast_tls: Update to 1.1.14 to support OpenSSL 3 +- jiffy: Update to 1.1.1 to support Erlang/OTP 25.0-rc1 +- luerl: Update to 1.0.0, now available in hex.pm +- lager: This dependency is used only when Erlang is older than 22 +- rebar2: Updated binary to work from Erlang/OTP 22 to 25 +- rebar3: Updated binary to work from Erlang/OTP 22 to 25 +- `make update`: Fix when used with rebar 3.18 + +#### Compile +- `mix release`: Copy `include/` files for ejabberd, deps and otp, in `mix.exs` +- `rebar3 release`: Fix ERTS path in `ejabberdctl` +- `configure.ac`: Set default ejabberd version number when not using git +- `mix.exs`: Move some dependencies as optional +- `mix.exs`: No need to use Distillery, Elixir has built-in support for OTP releases (#3788) +- `tools/make-binaries`: New script for building Linux binaries +- `tools/make-installers`: New script for building command line installers + +#### Start +- New `make relive` similar to `ejabberdctl live` without installing +- `ejabberdctl`: Fix some warnings detected by ShellCheck +- `ejabberdctl`: Mention in the help: `etop`, `ping` and `started`/`stopped` +- `make rel`: Switch to paths: `conf/`, `database/`, `logs/` +- `mix.exs`: Add `-boot` and `-boot_var` in `ejabberdctl` instead of adding `vm.args` +- `tools/captcha.sh`: Fix some warnings detected by ShellCheck + +#### Commands +- Accept more types of ejabberdctl commands arguments as JSON-encoded +- `delete_old_mam_messages_batch`: New command with rate limit +- `delete_old_messages_batch`: New command with rate limit +- `get_room_occupants_number`: Don't request the whole MUC room state (#3684, #1964) +- `get_vcard`: Add support for MUC room vCard +- `oauth_revoke_token`: Add support to work with all backends +- `room_unused_*`: Optimize commands in SQL by reusing `created_at` +- `rooms_unused_...`: Let `get_all_rooms` handle `global` argument (#3726) +- `stop|restart`: Terminate ejabberd_sm before everything else to ensure sessions closing (#3641) +- `subscribe_room_many`: New command + +#### Translations +- Updated Catalan +- Updated French +- Updated German +- Updated Portuguese +- Updated Portuguese (Brazil) +- Updated Spanish + +#### Workflows +- CI: Publish CT logs and Cover on failure to an external GH Pages repo +- CI: Test shell scripts using ShellCheck (#3738) +- Container: New workflow to build and publish containers +- Installers: Add job to create draft release +- Installers: New workflow to build binary packages +- Runtime: New workflow to test compilation, rel, starting and ejabberdctl + +## Version 21.12 + +#### Commands +- `create_room_with_opts`: Fixed when using SQL storage +- `change_room_option`: Add missing fields from config inside `mod_muc_admin:change_options` +- piefxis: Fixed arguments of all commands + +#### Modules +- mod_caps: Don't forget caps on XEP-0198 resumption +- mod_conversejs: New module to serve a simple page for Converse.js +- mod_http_upload_quota: Avoid `max_days` race +- mod_muc: Support MUC hats (XEP-0317, conversejs/prosody compatible) +- mod_muc: Optimize MucSub processing +- mod_muc: Fix exception in mucsub {un}subscription events multicast handler +- mod_multicast: Improve and optimize multicast routing code +- mod_offline: Allow storing non-composing x:events in offline +- mod_ping: Send ping from server, not bare user JID +- mod_push: Fix handling of MUC/Sub messages +- mod_register: New allow_modules option to restrict registration modules +- mod_register_web: Handle unknown host gracefully +- mod_register_web: Use mod_register configured restrictions + +#### PubSub +- Add `delete_expired_pubsub_items` command +- Add `delete_old_pubsub_items` command +- Optimize publishing on large nodes (SQL) +- Support unlimited number of items +- Support `max_items=max` node configuration +- Bump default value for `max_items` limit from 10 to 1000 +- Use configured `max_items` by default +- node_flat: Avoid catch-all clauses for RSM +- node_flat_sql: Avoid catch-all clauses for RSM + +#### SQL +- Use `INSERT ... ON CONFLICT` in SQL_UPSERT for PostgreSQL >= 9.5 +- mod_mam export: assign MUC entries to the MUC service +- MySQL: Fix typo when creating index +- PgSQL: Add SASL auth support, PostgreSQL 14 +- PgSQL: Add missing SQL migration for table `push_session` +- PgSQL: Fix `vcard_search` definition in pgsql new schema + +#### Other +- `captcha-ng.sh`: "sort -R" command not POSIX, added "shuf" and "cat" as fallback +- Make s2s connection table cleanup more robust +- Update export/import of scram password to XEP-0227 1.1 +- Update Jose to 1.11.1 (the last in hex.pm correctly versioned) + +## Version 21.07 + +#### Compilation +- Add rebar3 3.15.2 binary +- Add support for mix to: `./configure --enable-rebar=mix` +- Improved `make rel` to work with rebar3 and mix +- Add `make dev` to build a development release with rebar3 or mix +- Hex: Add `sql/` and `vars.config` to Hex package files +- Hex: Update mix applications list to fix error `p1_utils is listed as both...` +- There are so many targets in Makefile... add `make help` +- Fix extauth.py failure in test suite with Python 3 +- Added experimental support for GitHub Codespaces +- Switch test service from TravisCI to GitHub Actions + +#### Commands: + +- Display extended error message in ejabberdctl +- Remove SMP option from ejabberdctl.cfg, `-smp` was removed in OTP 21 +- `create_room`: After creating room, store in DB if it's persistent +- `help`: Major changes in its usage and output +- `srg_create`: Update to use `label` parameter instead of `name` + +#### Modules: + +- ejabberd_listener: New `send_timeout` option +- mod_mix: Improvements to update to 0.14.1 +- mod_muc_room: Don't leak owner JIDs +- mod_multicast: Routing for more MUC packets +- mod_multicast: Correctly strip only other bcc addresses +- mod_mqtt: Allow shared roster group placeholder in mqtt topic +- mod_pubsub: Several fixes when using PubSub with RSM +- mod_push: Handle MUC/Sub events correctly +- mod_shared_roster: Delete cache after performing change to be sure that in cache will be up to date data +- mod_shared_roster: Improve database and caching +- mod_shared_roster: Reconfigure cache when options change +- mod_vcard: Fix invalid_encoding error when using extended plane characters in vcard +- mod_vcard: Update econf:vcard() to generate correct vcard_temp record +- WebAdmin: New simple pages to view mnesia tables information and content +- WebSocket: Fix typos + +#### SQL: + +- MySQL Backend Patch for scram-sha512 +- SQLite: When exporting for SQLite, use its specific escape options +- SQLite: Minor fixes for new_sql_schema support +- mod_privacy: Cast as boolean when exporting privacy_list_data to PostgreSQL +- mod_mqtt: Add mqtt_pub table definition for MSSQL +- mod_shared_roster: Add missing indexes to `sr_group` tables in all SQL databases + +## Version 21.04 + +#### API Commands: + +- `add_rosteritem/...`: Add argument guards to roster commands +- `get_user_subscriptions`: New command for MUC/Sub +- `remove_mam_for_user_with_peer`: Fix when removing room archive +- `send_message`: Fix bug introduced in ejabberd 21.01 +- `set_vcard`: Return modules errors + +#### Build and setup: + +- Allow ejabberd to be compatible as a dependency for an Erlang project using rebar3 +- CAPTCHA: New question/answer-based CAPTCHA script +- `--enable-lua`: new configure option for luerl instead of --enable-tools +- Remove support for HiPE, it was experimental and Erlang/OTP 24 removes it +- Update `sql_query` record to handle the Erlang/OTP 24 compiler reports +- Updated dependencies to fix Dialyzer warnings + +#### Miscellaneous: + +- CAPTCHA: Update `FORM_TYPE` from captcha to register +- LDAP: fix eldap certificate verification +- MySQL: Fix for "specified key was too long" +- Translations: updated the Esperanto, Greek, and Japanese translations +- Websocket: Fix PONG responses + +#### Modules: + +- `mod_block_strangers`: If stanza is type error, allow it passing +- `mod_caps`: Don't request roster when not needed +- `mod_caps`: Skip reading roster in one more case +- `mod_mam`: Remove `queryid` from MAM fin element +- `mod_mqtt`: When deregistering XMPP account, close its MQTT sessions +- `mod_muc`: Take in account subscriber's affiliation when checking access to moderated room +- `mod_muc`: Use monitors to track online and hard-killed rooms +- `mod_muc`: When occupant is banned, remove his subscriptions too +- `mod_privacy`: Make fetching roster lazy +- `mod_pubsub`: Don't fail on PEP unsubscribe +- `mod_pubsub`: Fix `gen_pubsub_node:get_state` return value +- `mod_vcard`: Obtain and provide photo type in vCard LDAP + +## Version 21.01 + +#### Miscellaneous changes: + +- `log_rotate_size` option: Fix handling of ‘infinity’ value +- `mod_time`: Fix invalid timezone +- Auth JWT: New `check_decoded_jwt` hook runs the default JWT verifier +- MUC: Allow non-occupant non-subscribed service admin send private MUC message +- MUC: New `max_password` and `max_captcha_whitelist` options +- OAuth: New `oauth_cache_rest_failure_life_time` option +- PEP: Skip reading pep nodes that we know won’t be requested due to caps +- SQL: Add sql script to migrate mysql from old schema to new +- SQL: Don’t use REPLACE for upsert when there are “-” fields. +- Shared Rosters LDAP: Add multi-domain support (and flexibility) +- Sqlite3: Fix dependency version +- Stun: Block loopback addresses by default +- Several documentation fixes and clarifications + +#### Commands: + +- `decide_room`: Use better fallback value for room activity time when skipping room +- `delete_old_message`: Fix when using sqlite spool table +- `module_install`: Make ext_mod compile module with debug_info flags +- `room_unused_*`: Don’t fetch subscribers list +- `send_message`: Don’t include empty in messages +- `set_room_affiliation`: Validate affiliations + +#### Running: + +- Docker: New `Dockerfile` and `devcontainer.json` +- New `ejabberdctl foreground-quiet` +- Systemd: Allow for listening on privileged ports +- Systemd: Integrate nicely with systemd + +#### Translations: + +- Moved gettext PO files to a new `ejabberd-po` repository +- Improved several translations: Catalan, Chinese, German, Greek, Indonesian, Norwegian, Portuguese (Brazil), Spanish. + +## Version 20.12 + +- Add support for `SCRAM-SHA-{256,512}-{PLUS}` authentication +- Don't use same value in cache for user don't exist and wrong password +- `outgoing_s2s_ipv*_address`: New options to set ipv4/ipv6 outbound s2s out interface +- s2s_send_packet: this hook now filters outgoing s2s stanzas +- start_room: new hook runs when a room process is started +- check_decoded_jwt: new hook to check decoded JWT after success authentication + +#### Admin +- Docker: Fix DB initialization +- New sql_odbc_driver option: choose the mssql ODBC driver +- Rebar3: Fully supported. Enable with `./configure --with-rebar=/path/to/rebar3` +- systemd: start ejabberd in foreground + +#### Modules: +- MAM: Make sure that jid used as base in mam xml_compress is bare +- MAM: Support for MAM Flipped Pages +- MUC: Always show MucSub subscribers nicks +- MUC: Don't forget not-persistent rooms in load_permanent_rooms +- MUC Admin: Better error reporting +- MUC Admin: Fix commands with hibernated rooms +- MUC Admin: Many improvements in rooms_unused_list/destroy +- MUC Admin: create_room_with_opts Store options only if room starts +- Pubsub: Remove 'dag' node plugin documentation +- Push: Fix API call return type on error +- Push: Support cache config changes on reload +- Register: Allow for account-removal-only setup again +- Roster: Make roster subscriptions work better with invalid roster state in db +- Vcard: Fix vCard search by User when using Mnesia +- WebAdmin: Allow vhost admins to view WebAdmin menus +- WebAdmin: Don't do double utf-8 conversion on translated strings +- WebAdmin: Mark dangerous buttons with CSS +- WebSocket: Make websocket send put back pressure on c2s process + +## Version 20.07 + +#### Changes in this version +- Add support for using unix sockets in listeners. +- Make this version compatible with erlang R23 +- Make room permissions checks more strict for subscribers +- Fix problem with muc rooms crashing when using muc logger + with some locales +- Limit stat calls that logger module issues +- Don't throw errors when using user_regexp acl rule and + having non-matching host +- Fix problem with leaving old data when updating shared rosters +- Fix edge case that caused failure of resuming old sessions with + stream management. +- Fix crash when room that was started with logging enabled was later + changed to logging disabled +- Increase default shaper limits (this should help with delays for + clients that are using jingle) +- Fix couple compatibility problems which prevented working on + erlang R19 +- Fix sending presence unavailable when session terminates for + clients that only send directed presences (helps with sometimes + not leaving muc rooms on disconnect). +- Prevent supervisor errors for sockets that were closed before + they were passed to handler modules +- Make stun module work better with ipv6 addresses + +## Version 20.03 + +#### Changes in this version +- Add support of ssl connection when connection to mysql + database (configured with `sql_ssl: true` option) +- Experimental support for cockroachdb when configured + with postgres connector +- Add cache and optimize queries issued by `mod_shared_roster`, + this should greatly improve performance of this module when + used with `sql` backend +- Fix problem with accessing webadmin +- Make webadmin work even when url is missing trailing slash +- When compiling external modules with ext_mod, use flags + that were detected during compilation of ejabberd +- Make config changed to ldap options be updated when issued + `reload_config` command +- Fix `room_empty_destory` command +- Fix reporting errors in `send_stanza` command when xml + passed to it couldn't be passed correctly + +## Version 20.02 + +#### Changes in this version +- Fix problems when trying to use string format with unicode + values directly in xmpp nodes +- Add missing oauth_client table declaration in lite.new.sql +- Improve compatibility with CocroachDB +- Fix importing of piefxis files that did use scram passwords +- Fix importing of piefxis files that had multiple includes + in them +- Update jiffy dependency +- Allow storage of emojis when using mssql database (Thanks + to Christoph Scholz) +- Make ejabberd_auth_http be able to use auth_opts +- Make custom_headers options in http modules correctly + override built-in values +- Fix return value of reload_config and dump_config commands + +## Version 20.01 + +#### New features +- Implement OAUTH authentication in mqtt +- Make logging infrastructure use new logger introduced + in Erlang (requires OTP22) +- New configuration parser/validator +- Initial work on being able to use CockroachDB as database backend +- Add gc command +- Add option to disable using prepared statements on Postgresql +- Implement routine for converting password to SCRAM format + for all backends not only SQL +- Add infrastructure for having module documentation directly + in individual module source code +- Generate man page automatically +- Implement copy feature in mod_carboncopy + +#### Fixes +- Make webadmin work with configurable paths +- Fix handling of result in xmlrpc module +- Make webadmin work even when accessed through not declared domain +- Better error reporting in xmlrpc +- Limit amount of results returned by disco queries to pubsub nodes +- Improve validation of configured JWT keys +- Fix race condition in Redis/SQL startup +- Fix loading order of third party modules +- Fix reloading of ACL rules +- Make account removal requests properly route response +- Improve handling of malformed inputs in send_message command +- Omit push notification if storing message in offline storage + failed +- Fix crash in stream management when timeout was not set + +## Version 19.09 + +#### Admin +- The minimum required Erlang/OTP version is now 19.3 +- Fix API call using OAuth (#2982) +- Rename MUC command arguments from Host to Service (#2976) + +#### Webadmin +- Don't treat 'Host' header as a virtual XMPP host (#2989) +- Fix some links to Guide in WebAdmin and add new ones (#3003) +- Use select fields to input host in WebAdmin Backup (#3000) +- Check account auth provided in WebAdmin is a local host (#3000) + +#### ACME +- Improve ACME implementation +- Fix IDA support in ACME requests +- Fix unicode formatting in ACME module +- Log an error message on IDNA failure +- Support IDN hostnames in ACME requests +- Don't attempt to create ACME directory on ejabberd startup +- Don't allow requesting certificates for localhost or IP-like domains +- Don't auto request certificate for localhost and IP-like domains +- Add listener for ACME challenge in example config + +#### Authentication +- JWT-only authentication for some users (#3012) + +#### MUC +- Apply default role after revoking admin affiliation (#3023) +- Custom exit message is not broadcast (#3004) +- Revert "Affiliations other than admin and owner cannot invite to members_only rooms" (#2987) +- When join new room with password, set pass and password_protected (#2668) +- Improve rooms_* commands to accept 'global' as MUC service argument (#2976) +- Rename MUC command arguments from Host to Service (#2976) + +#### SQL +- Fix transactions for Microsoft SQL Server (#2978) +- Spawn SQL connections on demand only + +#### Misc +- Add support for XEP-0328: JID Prep +- Added gsfonts for captcha +- Log Mnesia table type on creation +- Replicate Mnesia 'bosh' table when nodes are joined +- Fix certificate selection for s2s (#3015) +- Provide meaningful error when adding non-local users to shared roster (#3000) +- Websocket: don't treat 'Host' header as a virtual XMPP host (#2989) +- Fix sm ack related c2s error (#2984) +- Don't hide the reason why c2s connection has failed +- Unicode support +- Correctly handle unicode in log messages +- Fix unicode processing in ejabberd.yml + +## Version 19.08 + +#### Administration +- Improve ejabberd halting procedure +- Process unexpected erlang messages uniformly: logging a warning +- mod_configure: Remove modules management + +#### Configuration +- Use new configuration validator +- ejabberd_http: Use correct virtual host when consulting trusted_proxies +- Fix Elixir modules detection in the configuration file +- Make option 'validate_stream' global +- Allow multiple definitions of host_config and append_host_config +- Introduce option 'captcha_url' +- mod_stream_mgmt: Allow flexible timeout format +- mod_mqtt: Allow flexible timeout format in session_expiry option + +#### Misc +- Fix SQL connections leakage +- New authentication method using JWT tokens +- extauth: Add 'certauth' command +- Improve SQL pool logic +- Add and improve type specs +- Improve extraction of translated strings +- Improve error handling/reporting when loading language translations +- Improve hooks validator and fix bugs related to hooks registration +- Gracefully close inbound s2s connections +- mod_mqtt: Fix usage of TLS +- mod_offline: Make count_offline_messages cache work when using mam for storage +- mod_privacy: Don't attempt to query 'undefined' active list +- mod_privacy: Fix race condition + +#### MUC +- Add code for hibernating inactive muc_room processes +- Improve handling of unexpected iq in mod_muc_room +- Attach mod_muc_room processes to a supervisor +- Restore room when receiving message or generic iq for not started room +- Distribute routing of MUC messages across all CPU cores + +#### PubSub +- Fix pending nodes retrieval for SQL backend +- Check access_model when publishing PEP +- Remove deprecated pubsub plugins +- Expose access_model and publish_model in pubsub#metadata + +## Version 19.05 + +#### Admin +- The minimum required Erlang/OTP version is now 19.1 +- Provide a suggestion when unknown command, module, option or request handler is detected +- Deprecate some listening options: captcha, register, web_admin, http_bind and xmlrpc +- Add commands to get Mnesia info: mnesia_info and mnesia_table_info +- Fix Register command to respect mod_register's Access option +- Fixes in Prosody import: privacy and rooms +- Remove TLS options from the example config +- Improve request_handlers validator +- Fix syntax in example Elixir config file + +#### Auth +- Correctly support cache tags in ejabberd_auth +- Don't process failed EXTERNAL authentication by mod_fail2ban +- Don't call to mod_register when it's not loaded +- Make anonymous auth don't {de}register user when there are other resources + +#### Developer +- Rename listening callback from start/2 to start/3 +- New hook called when room gets destroyed: room_destroyed +- New hooks for tracking mucsub subscriptions changes: muc_subscribed, muc_unsubscribed +- Make static hooks analyzer working again + +#### MUC +- Service admins are allowed to recreate room even if archive is nonempty +- New option user_mucsub_from_muc_archive +- Avoid late arrival of get_disco_item response +- Handle get_subscribed_rooms call from mod_muc_room pid +- Fix room state cleanup from db on change of persistent option change +- Make get_subscribed_rooms work even for non-persistant rooms +- Allow non-moderator subscribers to get list of room subscribers + +#### Offline +- New option bounce_groupchat: make it not bounce mucsub/groupchat messages +- New option use_mam_for_storage: fetch data from mam instead of spool table +- When applying limit of max msgs in spool check only spool size +- Do not store mucsub wrapped messages with no-store hint in offline storage +- Always store ActivityMarker messages +- Don't issue count/message fetch queries for offline from mam when not needed +- Properly handle infinity as max number of message in mam offline storage +- Sort messages by stanza_id when using mam storage in mod_offline +- Return correct value from count_offline_messages with mam storage option +- Make mod_offline put msg ignored by mam in spool when mam storage is on + +#### SQL: +- Add SQL schemas for MQTT tables +- Report better errors on SQL terms decode failure +- Fix PostgreSQL compatibility in mod_offline_sql:remove_old_messages +- Fix handling of list arguments on pgsql +- Preliminary support for SQL in process_rosteritems command + +#### Tests +- Add tests for user mucsub mam from muc mam +- Add tests for offline with mam storage +- Add tests for offline use_mam_for_storage +- Initial Docker environment to run ejabberd test suite +- Test offline:use_mam_for_storage, mam:user_mucsub_from_muc_archive used together + +#### Websocket +- Add WebSockets support to mod_mqtt +- Return "Bad request" error when origin in websocket connection doesn't match +- Fix RFC6454 violation on websocket connection when validating Origin header +- Origin header validation on websocket connection + +#### Other modules +- mod_adhoc: Use xml:lang from stanza when it's missing in element +- mod_announce: Add 'sessionid' attribute when required +- mod_bosh: Don't put duplicate polling attribute in bosh payload +- mod_http_api: Improve argument error messages and log messages +- mod_http_upload: Feed whole image to eimp:identify/1 +- mod_http_upload: Log nicer warning on unknown host +- mod_http_upload: Case-insensitive host comparison +- mod_mqtt: Support other socket modules +- mod_push: Check for payload in encrypted messages + +## Version 19.02 + +#### Admin +- Fix in configure.ac the Erlang/OTP version: from 17.5 to 19.0 +- reload_config command: Fix crash when sql_pool_size option is used +- reload_config command: Fix crash when SQL is not configured +- rooms_empty_destroy command: Several fixes to behave more conservative +- Fix serverhost->host parameter name for muc_(un)register_nick API + +#### Configuration +- Allow specifying tag for listener for api_permission purposes +- Change default ciphers to intermediate +- Define default ciphers/protocol_option in example config +- Don't crash on malformed 'modules' section +- mod_mam: New option clear_archive_on_room_destroy to prevent archive removal on room destroy +- mod_mam: New option access_preferences to restrict who can modify the MAM preferences +- mod_muc: New option access_mam to restrict who can modify that room option +- mod_offline: New option store_groupchat to allow storing group chat messages + +#### Core +- Add MQTT protocol support +- Fix (un)setting of priority +- Use OTP application startup infrastructure for starting dependencies +- Improve starting order of several dependencies + +#### MAM +- mod_mam_mnesia/sql: Improve check for empty archive +- disallow room creation if archive not empty and clear_archive_on_room_destroy is false +- allow check if archive is empty for or user or room +- Additional checks for database failures + +#### MUC +- Make sure that room_destroyed is called even when some code throws in terminate +- Update muc room state after adding extra access field to it +- MUC/Sub: Send mucsub subscriber notification events with from set to room jid + +#### Shared Roster +- Don't perform roster push for non-local contacts +- Handle versioning result when shared roster group has remote account +- Fix SQL queries + +#### Miscelanea +- CAPTCHA: Add no-store hint to CAPTCHA challenge stanzas +- HTTP: Reject http_api request with malformed Authentication header +- mod_carboncopy: Don't lose carbons on presence change or session resumption +- mod_mix: Fix submission-id and channel resource +- mod_ping: Fix ping IQ reply/timeout processing (17.x regression) +- mod_private: Hardcode item ID for PEP bookmarks +- mod_push: Improve notification error handling +- PIEFXIS: Fix user export when password is scrammed +- Prosody: Improve import of roster items, rooms and attributes +- Translations: fixed "make translations" +- WebAdmin: Fix support to restart module with new options + +## Version 18.12 + +- MAM data store compression +- Proxy protocol support +- MUC Self-Ping optimization (XEP-0410) +- Bookmarks conversion (XEP-0411) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..e8855889e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,61 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Guidelines for Respectful and Efficient Communication on Issues, Discussions, and PRs + +To ensure that our maintainers can efficiently manage issues and provide timely updates, we kindly ask that all comments on GitHub tickets remain relevant to the topic of the issue. Please avoid posting comments solely to ping maintainers or ask for updates. If you need information on the status of an issue, consider the following: + +- **Check the Issue Timeline:** Review the existing comments and updates on the issue before posting. +- **Use Reactions:** If you want to show that you are interested in an issue, use GitHub's reaction feature (e.g., thumbs up) instead of commenting. +- **Be Patient:** Understand that maintainers may be working on multiple tasks and will provide updates as soon as possible. + +Additionally, please be aware that: + +- **User Responses:** Users who report issues may no longer be using the software, may have switched to other projects, or may simply be busy. It is their right not to respond to follow-up questions or comments. +- **Maintainer Priorities:** Maintainers have the right to define their own priorities and schedule. They will address issues based on their availability and the project's needs. + +By following these guidelines, you help us maintain a productive and respectful environment for everyone involved. + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at the email address: conduct AT process-one.net. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://contributor-covenant.org/version/1/4][version] + +[homepage]: https://www.contributor-covenant.org/ +[version]: https://www.contributor-covenant.org/version/1/4/ diff --git a/COMPILE.md b/COMPILE.md new file mode 100644 index 000000000..2eb8a1739 --- /dev/null +++ b/COMPILE.md @@ -0,0 +1,126 @@ +Compile and Install ejabberd +============================ + +This document explains how to compile and install ejabberd +from source code. + +For a more detailed explanation, please check the +ejabberd Docs: [Source Code Installation][docs-source]. + +[docs-source]: https://docs.ejabberd.im/admin/install/source/ + + +Requirements +------------ + +To compile ejabberd you need: + +- GNU Make +- GCC +- Libexpat ≥ 1.95 +- Libyaml ≥ 0.1.4 +- Erlang/OTP ≥ 25.0 +- OpenSSL ≥ 1.0.0 + +Other optional libraries are: + +- Zlib ≥ 1.2.3, for Stream Compression support (XEP-0138) +- PAM library, for Pluggable Authentication Modules (PAM) +- ImageMagick's Convert program and Ghostscript fonts, for CAPTCHA + challenges +- Elixir ≥ 1.10.3, for Elixir support. It is recommended Elixir 1.14.0 or higher + +If your system splits packages in libraries and development headers, +install the development packages too. + + +Download Source Code +-------------------- + +There are several ways to obtain the ejabberd source code: + +- Source code archive from [ProcessOne Downloads][p1dl] +- Source code package from [ejabberd GitHub Releases][ghr] +- Latest development code from [ejabberd Git repository][gitrepo] + +[p1dl]: https://www.process-one.net/download/ejabberd/ +[ghr]: https://github.com/processone/ejabberd/releases +[gitrepo]: https://github.com/processone/ejabberd + + +Compile +------- + +The general instructions to compile ejabberd are: + + ./configure + make + +If the source code doesn't contain a `configure` script, +first of all install `autoconf` and run this to generate it: + + ./autogen.sh + +To configure the compilation, features, install paths... + + ./configure --help + +The build tool automatically downloads and compiles the +erlang libraries that [ejabberd depends on][docs-repo]. + +[docs-repo]: https://docs.ejabberd.im/developer/repositories/ + + +Install in the System +--------------------- + +To install ejabberd in the system, run this with system administrator rights (root user): + + sudo make install + +This will: + +- Install the configuration files in `/etc/ejabberd/` +- Install ejabberd binary, header and runtime files in `/lib/ejabberd/` +- Install the administration script: `/sbin/ejabberdctl` +- Install ejabberd documentation in `/share/doc/ejabberd/` +- Create a spool directory: `/var/lib/ejabberd/` +- Create a directory for log files: `/var/log/ejabberd/` + + +Build an OTP Release +-------------------- + +Instead of installing ejabberd in the system, you can build an OTP release +that includes all necessary to run ejabberd in a subdirectory: + + ./configure + make prod + +Check the full list of targets: + + make help + + +Start ejabberd +-------------- + +You can use the `ejabberdctl` command line administration script to +start and stop ejabberd. Some examples, depending on your installation method: + +- When installed in the system: + ``` + ejabberdctl start + /sbin/ejabberdctl start + ``` + +- When built an OTP production release: + ``` + _build/prod/rel/ejabberd/bin/ejabberdctl start + _build/prod/rel/ejabberd/bin/ejabberdctl live + ``` + +- Start interactively without installing or building OTP release: + ``` + make relive + ``` diff --git a/CONTAINER.md b/CONTAINER.md new file mode 100644 index 000000000..e96081112 --- /dev/null +++ b/CONTAINER.md @@ -0,0 +1,1095 @@ + +[![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/processone/ejabberd?sort=semver&logo=embarcadero&label=&color=49c0c4)](https://github.com/processone/ejabberd/tags) +[![ejabberd Container on GitHub](https://img.shields.io/github/v/tag/processone/ejabberd?label=ejabberd&sort=semver&logo=opencontainersinitiative&logoColor=2094f3)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) +[![ecs Container on Docker](https://img.shields.io/docker/v/ejabberd/ecs?label=ecs&sort=semver&logo=docker)](https://hub.docker.com/r/ejabberd/ecs/) + +ejabberd Container Images +========================= + +[ejabberd][home] is an open-source, +robust, scalable and extensible realtime platform built using [Erlang/OTP][erlang], +that includes [XMPP][xmpp] Server, [MQTT][mqtt] Broker and [SIP][sip] Service. + +[home]: https://www.ejabberd.im/ +[erlang]: https://www.erlang.org/ +[xmpp]: https://xmpp.org/ +[mqtt]: https://mqtt.org/ +[sip]: https://en.wikipedia.org/wiki/Session_Initiation_Protocol + +This page documents those container images ([images comparison](#images-comparison)): + +- [![ejabberd Container](https://img.shields.io/badge/ejabberd-grey?logo=opencontainersinitiative&logoColor=2094f3)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) + published in [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd), + built using [ejabberd](https://github.com/processone/ejabberd/tree/master/.github/container) repository, + both for stable ejabberd releases and the `master` branch, in x64 and arm64 architectures. + +- [![ecs Container](https://img.shields.io/badge/ecs-grey?logo=docker&logoColor=2094f3)](https://hub.docker.com/r/ejabberd/ecs/) + published in [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/), + built using [docker-ejabberd/ecs](https://github.com/processone/docker-ejabberd/tree/master/ecs) repository + for ejabberd stable releases in x64 architectures. + +For Microsoft Windows, see +[Docker Desktop for Windows 10](https://www.process-one.net/blog/install-ejabberd-on-windows-10-using-docker-desktop/), +and [Docker Toolbox for Windows 7](https://www.process-one.net/blog/install-ejabberd-on-windows-7-using-docker-toolbox/). + +For Kubernetes Helm, see [help-ejabberd](https://github.com/sando38/helm-ejabberd). + + +Start ejabberd +-------------- + +### daemon + +Start ejabberd in a new container: + +```bash +docker run --name ejabberd -d -p 5222:5222 ghcr.io/processone/ejabberd +``` + +That runs the container as a daemon, +using ejabberd default configuration file and XMPP domain `localhost`. + +Restart the stopped ejabberd container: + +```bash +docker restart ejabberd +``` + +Stop the running container: + +```bash +docker stop ejabberd +``` + +Remove the ejabberd container: + +```bash +docker rm ejabberd +``` + + +### with Erlang console + +Start ejabberd with an interactive Erlang console attached using the `live` command: + +```bash +docker run --name ejabberd -it -p 5222:5222 ghcr.io/processone/ejabberd live +``` + +That uses the default configuration file and XMPP domain `localhost`. + + +### with your data + +Pass a configuration file as a volume +and share the local directory to store database: + +```bash +mkdir conf && cp ejabberd.yml.example conf/ejabberd.yml + +mkdir database && chown ejabberd database + +docker run --name ejabberd -it \ + -v $(pwd)/conf/ejabberd.yml:/opt/ejabberd/conf/ejabberd.yml \ + -v $(pwd)/database:/opt/ejabberd/database \ + -p 5222:5222 ghcr.io/processone/ejabberd live +``` + +Notice that ejabberd runs in the container with an account named `ejabberd` +with UID 9000 and group `ejabberd` with GID 9000, +and the volumes you mount must grant proper rights to that account. + + +Next steps +---------- + +### Register admin account + +#### [![ejabberd Container](https://img.shields.io/badge/ejabberd-grey?logo=opencontainersinitiative&logoColor=2094f3)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) [:orange_circle:](#images-comparison) + +If you set the `REGISTER_ADMIN_PASSWORD` environment variable, +an account is automatically registered with that password, +and admin privileges are granted to it. +The account created depends on what variables you have set: + +- `EJABBERD_MACRO_ADMIN=juliet@example.org` -> `juliet@example.org` +- `EJABBERD_MACRO_HOST=example.org` -> `admin@example.org` +- None of those variables are set -> `admin@localhost` + +The account registration is shown in the container log: + +``` +:> ejabberdctl register admin example.org somePassw0rd +User admin@example.org successfully registered +``` + +Alternatively, you can register the account manually yourself +and edit `conf/ejabberd.yml` and add the ACL as explained in +[ejabberd Docs: Administration Account](https://docs.ejabberd.im/admin/install/next-steps/#administration-account). + +--- + +#### [![ecs Container](https://img.shields.io/badge/ecs-grey?logo=docker&logoColor=2094f3)](https://hub.docker.com/r/ejabberd/ecs/) + +The default ejabberd configuration has already granted admin privilege +to an account that would be called `admin@localhost`, +so you just need to register it, for example: + +```bash +docker exec -it ejabberd ejabberdctl register admin localhost passw0rd +``` + +### Check ejabberd log + +Check the content of the log files inside the container, +even if you do not put it on a shared persistent drive: + +```bash +docker exec -it ejabberd tail -f logs/ejabberd.log +``` + + +### Inspect container files + +The container uses Alpine Linux. Start a shell inside the container: + +```bash +docker exec -it ejabberd sh +``` + + +### Open debug console + +Open an interactive debug Erlang console attached to a running ejabberd in a running container: + +```bash +docker exec -it ejabberd ejabberdctl debug +``` + + +### CAPTCHA + +ejabberd includes two example CAPTCHA scripts. +If you want to use any of them, first install some additional required libraries: + +```bash +docker exec --user root ejabberd apk add imagemagick ghostscript-fonts bash +``` + +Now update your ejabberd configuration file, for example: +```bash +docker exec -it ejabberd vi conf/ejabberd.yml +``` + +and add this option: +```yaml +captcha_cmd: "$HOME/bin/captcha.sh" +``` + +Finally, reload the configuration file or restart the container: +```bash +docker exec ejabberd ejabberdctl reload_config +``` + +If the CAPTCHA image is not visible, there may be a problem generating it +(the ejabberd log file may show some error message); +or the image URL may not be correctly detected by ejabberd, +in that case you can set the correct URL manually, for example: +```yaml +captcha_url: https://localhost:5443/captcha +``` + +For more details about CAPTCHA options, please check the +[CAPTCHA](https://docs.ejabberd.im/admin/configuration/basic/#captcha) +documentation section. + + +Advanced +-------- + +### Ports + +The container image exposes several ports +(check also [Docs: Firewall Settings](https://docs.ejabberd.im/admin/guide/security/#firewall-settings)): + +- `5222`: The default port for XMPP clients. +- `5269`: For XMPP federation. Only needed if you want to communicate with users on other servers. +- `5280`: For admin interface (URL is `admin/`). +- `1880`: For admin interface (URL is `/`, useful for [podman-desktop](https://podman-desktop.io/) and [docker-desktop](https://www.docker.com/products/docker-desktop/)) [:orange_circle:](#images-comparison) +- `5443`: With encryption, used for admin interface, API, CAPTCHA, OAuth, Websockets and XMPP BOSH. +- `1883`: Used for MQTT +- `4369-4399`: EPMD and Erlang connectivity, used for `ejabberdctl` and clustering +- `5210`: Erlang connectivity when `ERL_DIST_PORT` is set, alternative to EPMD [:orange_circle:](#images-comparison) + + +### Volumes + +ejabberd produces two types of data: log files and database spool files (Mnesia). +This is the kind of data you probably want to store on a persistent or local drive (at least the database). + +The volumes you may want to map: + +- `/opt/ejabberd/conf/`: Directory containing configuration and certificates +- `/opt/ejabberd/database/`: Directory containing Mnesia database. +You should back up or export the content of the directory to persistent storage +(host storage, local storage, any storage plugin) +- `/opt/ejabberd/logs/`: Directory containing log files +- `/opt/ejabberd/upload/`: Directory containing uploaded files. This should also be backed up. + +All these files are owned by an account named `ejabberd` with group `ejabberd` in the container. +Its corresponding `UID:GID` is `9000:9000`. +If you prefer bind mounts instead of volumes, then +you need to map this to valid `UID:GID` on your host to get read/write access on +mounted directories. + +If using Docker, try: +```bash +mkdir database +sudo chown 9000:9000 database +``` + +If using Podman, try: +```bash +mkdir database +podman unshare chown 9000:9000 database +``` + +It's possible to install additional ejabberd modules using volumes, check +[this Docs tutorial](https://docs.ejabberd.im/developer/extending-ejabberd/modules/#your-module-in-ejabberd-modules-with-ejabberd-container). + + +### Commands on start + +The ejabberdctl script reads the `CTL_ON_CREATE` environment variable +the first time the container is started, +and reads `CTL_ON_START` every time the container is started. +Those variables can contain one ejabberdctl command, +or several commands separated with the blankspace and `;` characters. + +If any of those commands returns a failure, the container starting gets aborted. +If there is a command with a result that can be ignored, +prefix that command with `!` + +This example, registers an `admin@localhost` account when the container is first created. +Everytime the container starts, it shows the list of registered accounts, +checks that the admin account exists and password is valid, +changes the password of an account if it exists (ignoring any failure), +and shows the ejabberd starts (check also the [full example](#customized-example)): +```yaml + environment: + - CTL_ON_CREATE=register admin localhost asd + - CTL_ON_START=stats registeredusers ; + check_password admin localhost asd ; + ! change_password bot123 localhost qqq ; + status +``` + + +### Macros in environment [:high_brightness:](#images-comparison) + +ejabberd reads `EJABBERD_MACRO_*` environment variables +and uses them to define the corresponding +[macros](https://docs.ejabberd.im/admin/configuration/file-format/#macros-in-configuration-file), +overwriting the corresponding macro definition if it was set in the configuration file. +This is supported since ejabberd 24.12. + +For example, if you configure this in `ejabberd.yml`: + +```yaml +acl: + admin: + user: ADMIN +``` + +now you can define the admin account JID using an environment variable: +```yaml + environment: + - EJABBERD_MACRO_ADMIN=admin@localhost +``` + +Check the [full example](#customized-example) for other example. + + +### ejabberd-contrib + +This section addresses those topics related to +[ejabberd-contrib](https://docs.ejabberd.im/admin/guide/modules/#ejabberd-contrib): + +- [Download source code](#download-source-code) +- [Install a module](#install-a-module) +- [Install git for dependencies](#install-git-for-dependencies) +- [Install your module](#install-your-module) + +--- + +#### Download source code + +The `ejabberd` container image includes the ejabberd-contrib git repository source code, +but `ecs` does not, so first download it: +```bash +$ docker exec ejabberd ejabberdctl modules_update_specs +``` + +#### Install a module + +Compile and install any of the contributed modules, for example: +```bash +docker exec ejabberd ejabberdctl module_install mod_statsdx + +Module mod_statsdx has been installed and started. +It's configured in the file: + /opt/ejabberd/.ejabberd-modules/mod_statsdx/conf/mod_statsdx.yml +Configure the module in that file, or remove it +and configure in your main ejabberd.yml +``` + +#### Install git for dependencies + +Some modules depend on erlang libraries, +but the container images do not include `git` or `mix` to download them. +Consequently, when you attempt to install such a module, +there will be error messages like: + +```bash +docker exec ejabberd ejabberdctl module_install ejabberd_observer_cli + +I'll download "recon" using git because I can't use Mix to fetch from hex.pm: + /bin/sh: mix: not found +Fetching dependency observer_cli: + /bin/sh: git: not found +... +``` + +the solution is to install `git` in the container image: + +```bash +docker exec --user root ejabberd apk add git + +fetch https://dl-cdn.alpinelinux.org/alpine/v3.21/main/x86_64/APKINDEX.tar.gz +fetch https://dl-cdn.alpinelinux.org/alpine/v3.21/community/x86_64/APKINDEX.tar.gz +(1/3) Installing pcre2 (10.43-r0) +(2/3) Installing git (2.47.2-r0) +(3/3) Installing git-init-template (2.47.2-r0) +Executing busybox-1.37.0-r12.trigger +OK: 27 MiB in 42 packages +``` + +and now you can upgrade the module: + +```bash +docker exec ejabberd ejabberdctl module_upgrade ejabberd_observer_cli + +I'll download "recon" using git because I can't use Mix to fetch from hex.pm: +/bin/sh: mix: not found +Fetching dependency observer_cli: Cloning into 'observer_cli'... +Fetching dependency os_stats: Cloning into 'os_stats'... +Fetching dependency recon: Cloning into 'recon'... +Inlining: inline_size=24 inline_effort=150 +Old inliner: threshold=0 functions=[{insert,2},{merge,2}] +Module ejabberd_observer_cli has been installed. +Now you can configure it in your ejabberd.yml +I'll download "recon" using git because I can't use Mix to fetch from hex.pm: +/bin/sh: mix: not found +``` + +#### Install your module + +If you [developed an ejabberd module](https://docs.ejabberd.im/developer/extending-ejabberd/modules/), +you can install it in your container image: + +1. Create a local directory for `ejabberd-modules`: + + ``` sh + mkdir docker-modules + ``` + +2. Then create the directory structure for your custom module: + + ``` sh + cd docker-modules + + mkdir -p sources/mod_hello_world/ + touch sources/mod_hello_world/mod_hello_world.spec + + mkdir sources/mod_hello_world/src/ + mv mod_hello_world.erl sources/mod_hello_world/src/ + + mkdir sources/mod_hello_world/conf/ + echo -e "modules:\n mod_hello_world: {}" > sources/mod_hello_world/conf/mod_hello_world.yml + + cd .. + ``` + +3. Grant ownership of that directory to the UID that ejabberd will use inside the Docker image: + + ``` sh + sudo chown 9000 -R docker-modules/ + ``` + +4. Start ejabberd in the container: + + ``` sh + sudo docker run \ + --name hellotest \ + -d \ + --volume "$(pwd)/docker-modules:/home/ejabberd/.ejabberd-modules/" \ + -p 5222:5222 \ + -p 5280:5280 \ + ejabberd/ecs + ``` + +5. Check the module is available for installing, and then install it: + + ``` sh + sudo docker exec -it hellotest ejabberdctl modules_available + mod_hello_world [] + + sudo docker exec -it hellotest ejabberdctl module_install mod_hello_world + ``` + +6. If the module works correctly, you will see `Hello` in the ejabberd logs when it starts: + + ``` sh + sudo docker exec -it hellotest grep Hello logs/ejabberd.log + 2020-10-06 13:40:13.154335+00:00 [info] + <0.492.0>@mod_hello_world:start/2:15 Hello, ejabberd world! + ``` + + +### ejabberdapi + +When the container is running (and thus ejabberd), you can exec commands inside the container +using `ejabberdctl` or any other of the available interfaces, see +[Understanding ejabberd "commands"](https://docs.ejabberd.im/developer/ejabberd-api/#understanding-ejabberd-commands) + +Additionally, the container image includes the `ejabberdapi` executable. +Please check the [ejabberd-api homepage](https://github.com/processone/ejabberd-api) +for configuration and usage details. + +For example, if you configure ejabberd like this: +```yaml +listen: + - + port: 5282 + module: ejabberd_http + request_handlers: + "/api": mod_http_api + +acl: + loopback: + ip: + - 127.0.0.0/8 + - ::1/128 + - ::FFFF:127.0.0.1/128 + +api_permissions: + "admin access": + who: + access: + allow: + acl: loopback + what: + - "register" +``` + +Then you could register new accounts with this query: + +```bash +docker exec -it ejabberd ejabberdapi register --endpoint=http://127.0.0.1:5282/ --jid=admin@localhost --password=passw0rd +``` + + +### Clustering + +When setting several containers to form a +[cluster of ejabberd nodes](https://docs.ejabberd.im/admin/guide/clustering/), +each one must have a different +[Erlang Node Name](https://docs.ejabberd.im/admin/guide/security/#erlang-node-name) +and the same +[Erlang Cookie](https://docs.ejabberd.im/admin/guide/security/#erlang-cookie). + +For this you can either: + +- edit `conf/ejabberdctl.cfg` and set variables `ERLANG_NODE` and `ERLANG_COOKIE` +- set the environment variables `ERLANG_NODE_ARG` and `ERLANG_COOKIE` + +--- + +Example to connect a local `ejabberdctl` to a containerized ejabberd: + +1. When creating the container, export port 5210, and set `ERLANG_COOKIE`: + ```sh + docker run --name ejabberd -it \ + -e ERLANG_COOKIE=`cat $HOME/.erlang.cookie` \ + -p 5210:5210 -p 5222:5222 \ + ghcr.io/processone/ejabberd + ``` +2. Set `ERL_DIST_PORT=5210` in `ejabberdctl.cfg` of container and local ejabberd +3. Restart the container +4. Now use `ejabberdctl` in your local ejabberd deployment + +To connect using a local `ejabberd` script: +```sh +ERL_DIST_PORT=5210 _build/dev/rel/ejabberd/bin/ejabberd ping +``` + +Example using environment variables (see full example [docker-compose.yml](https://github.com/processone/docker-ejabberd/issues/64#issuecomment-887741332)): +```yaml + environment: + - ERLANG_NODE_ARG=ejabberd@node7 + - ERLANG_COOKIE=dummycookie123 +``` + +--- + +Once you have the ejabberd nodes properly set and running, +you can tell the secondary nodes to join the master node using the +[`join_cluster`](https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#join-cluster) +API call. + +Example using environment variables (see the full +[`docker-compose.yml` clustering example](#clustering-example)): +```yaml +environment: + - ERLANG_NODE_ARG=ejabberd@replica + - ERLANG_COOKIE=dummycookie123 + - CTL_ON_CREATE=join_cluster ejabberd@main +``` + +### Change Mnesia Node Name + +To use the same Mnesia database in a container with a different hostname, +it is necessary to change the old hostname stored in Mnesia. + +This section is equivalent to the ejabberd Documentation +[Change Computer Hostname](https://docs.ejabberd.im/admin/guide/managing/#change-computer-hostname), +but particularized to containers that use this +`ecs` container image from ejabberd 23.01 or older. + +#### Setup Old Container + +Let's assume a container running ejabberd 23.01 (or older) from +this `ecs` container image, with the database directory binded +and one registered account. +This can be produced with: +```bash +OLDCONTAINER=ejaold +NEWCONTAINER=ejanew + +mkdir database +sudo chown 9000:9000 database +docker run -d --name $OLDCONTAINER -p 5222:5222 \ + -v $(pwd)/database:/opt/ejabberd/database \ + ghcr.io/processone/ejabberd:23.01 +docker exec -it $OLDCONTAINER ejabberdctl started +docker exec -it $OLDCONTAINER ejabberdctl register user1 localhost somepass +docker exec -it $OLDCONTAINER ejabberdctl registered_users localhost +``` + +Methods to know the Erlang node name: +```bash +ls database/ | grep ejabberd@ +docker exec -it $OLDCONTAINER ejabberdctl status +docker exec -it $OLDCONTAINER grep "started in the node" logs/ejabberd.log +``` + +#### Change Mnesia Node + +First of all let's store the Erlang node names and paths in variables. +In this example they would be: +```bash +OLDCONTAINER=ejaold +NEWCONTAINER=ejanew +OLDNODE=ejabberd@95145ddee27c +NEWNODE=ejabberd@localhost +OLDFILE=/opt/ejabberd/database/old.backup +NEWFILE=/opt/ejabberd/database/new.backup +``` + +1. Start your old container that can still read the Mnesia database correctly. +If you have the Mnesia spool files, +but don't have access to the old container anymore, go to +[Create Temporary Container](#create-temporary-container) +and later come back here. + +2. Generate a backup file and check it was created: +```bash +docker exec -it $OLDCONTAINER ejabberdctl backup $OLDFILE +ls -l database/*.backup +``` + +3. Stop ejabberd: +```bash +docker stop $OLDCONTAINER +``` + +4. Create the new container. For example: +```bash +docker run \ + --name $NEWCONTAINER \ + -d \ + -p 5222:5222 \ + -v $(pwd)/database:/opt/ejabberd/database \ + ghcr.io/processone/ejabberd:latest +``` + +5. Convert the backup file to new node name: +```bash +docker exec -it $NEWCONTAINER ejabberdctl mnesia_change_nodename $OLDNODE $NEWNODE $OLDFILE $NEWFILE +``` + +6. Install the backup file as a fallback: +```bash +docker exec -it $NEWCONTAINER ejabberdctl install_fallback $NEWFILE +``` + +7. Restart the container: +```bash +docker restart $NEWCONTAINER +``` + +8. Check that the information of the old database is available. +In this example, it should show that the account `user1` is registered: +```bash +docker exec -it $NEWCONTAINER ejabberdctl registered_users localhost +``` + +9. When the new container is working perfectly with the converted Mnesia database, +you may want to remove the unneeded files: +the old container, the old Mnesia spool files, and the backup files. + +#### Create Temporary Container + +In case the old container that used the Mnesia database is not available anymore, +a temporary container can be created just to read the Mnesia database +and make a backup of it, as explained in the previous section. + +This method uses `--hostname` command line argument for docker, +and `ERLANG_NODE_ARG` environment variable for ejabberd. +Their values must be the hostname of your old container +and the Erlang node name of your old ejabberd node. +To know the Erlang node name please check +[Setup Old Container](#setup-old-container). + +Command line example: +```bash +OLDHOST=${OLDNODE#*@} +docker run \ + -d \ + --name $OLDCONTAINER \ + --hostname $OLDHOST \ + -p 5222:5222 \ + -v $(pwd)/database:/opt/ejabberd/database \ + -e ERLANG_NODE_ARG=$OLDNODE \ + ghcr.io/processone/ejabberd:latest +``` + +Check the old database content is available: +```bash +docker exec -it $OLDCONTAINER ejabberdctl registered_users localhost +``` + +Now that you have ejabberd running with access to the Mnesia database, +you can continue with step 2 of previous section +[Change Mnesia Node](#change-mnesia-node). + + +Build Container Image +---------------- + +The container image includes ejabberd as a standalone OTP release built using Elixir. + +### Build `ejabberd` [![ejabberd Container](https://img.shields.io/badge/ejabberd-grey?logo=opencontainersinitiative&logoColor=2094f3)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) + +The ejabberd Erlang/OTP release is configured with: + +- `mix.exs`: Customize ejabberd release +- `vars.config`: ejabberd compilation configuration options +- `config/runtime.exs`: Customize ejabberd paths +- `ejabberd.yml.template`: ejabberd default config file + +#### Direct build + +Build ejabberd Community Server container image from ejabberd master git repository: + +```bash +docker buildx build \ + -t personal/ejabberd \ + -f .github/container/Dockerfile \ + . +``` + +#### Podman build + +To build the image using Podman, please notice: + +- `EXPOSE 4369-4399` port range is not supported, remove that in Dockerfile +- It mentions that `healthcheck` is not supported by the Open Container Initiative image format +- to start with command `live`, you may want to add environment variable `EJABBERD_BYPASS_WARNINGS=true` + +```bash +podman build \ + -t ejabberd \ + -f .github/container/Dockerfile \ + . + +podman run --name eja1 -d -p 5222:5222 localhost/ejabberd + +podman exec eja1 ejabberdctl status + +podman exec -it eja1 sh + +podman stop eja1 + +podman run --name eja1 -it -e EJABBERD_BYPASS_WARNINGS=true -p 5222:5222 localhost/ejabberd live +``` + +### Build `ecs` [![ecs Container](https://img.shields.io/badge/ecs-grey?logo=docker&logoColor=2094f3)](https://hub.docker.com/r/ejabberd/ecs/) + +The ejabberd Erlang/OTP release is configured with: + +- `rel/config.exs`: Customize ejabberd release +- `rel/dev.exs`: ejabberd environment configuration for development release +- `rel/prod.exs`: ejabberd environment configuration for production release +- `vars.config`: ejabberd compilation configuration options +- `conf/ejabberd.yml`: ejabberd default config file + +Build ejabberd Community Server base image from ejabberd master on Github: + +```bash +docker build -t personal/ejabberd . +``` + +Build ejabberd Community Server base image for a given ejabberd version: + +```bash +./build.sh 18.03 +``` + +Composer Examples +----------------- + +### Minimal Example + +This is the barely minimal file to get a usable ejabberd. + +If using Docker, write this `docker-compose.yml` file +and start it with `docker-compose up`: + +```yaml +services: + main: + image: ghcr.io/processone/ejabberd + container_name: ejabberd + ports: + - "5222:5222" + - "5269:5269" + - "5280:5280" + - "5443:5443" +``` + +If using Podman, write this `minimal.yml` file +and start it with `podman kube play minimal.yml`: + +```yaml +apiVersion: v1 + +kind: Pod + +metadata: + name: ejabberd + +spec: + containers: + + - name: ejabberd + image: ghcr.io/processone/ejabberd + ports: + - containerPort: 5222 + hostPort: 5222 + - containerPort: 5269 + hostPort: 5269 + - containerPort: 5280 + hostPort: 5280 + - containerPort: 5443 + hostPort: 5443 +``` + + +### Customized Example + +This example shows the usage of several customizations: +it uses a local configuration file, +defines a configuration macro using an environment variable, +stores the mnesia database in a local path, +registers an account when it's created, +and checks the number of registered accounts every time it's started. + +Prepare an ejabberd configuration file: +```bash +mkdir conf && cp ejabberd.yml.example conf/ejabberd.yml +``` + +Create the database directory and allow the container access to it: + +- Docker: + ```bash + mkdir database && sudo chown 9000:9000 database + ``` +- Podman: + ```bash + mkdir database && podman unshare chown 9000:9000 database + ``` + +If using Docker, write this `docker-compose.yml` file +and start it with `docker-compose up`: + +```yaml +version: '3.7' + +services: + + main: + image: ghcr.io/processone/ejabberd + container_name: ejabberd + environment: + - EJABBERD_MACRO_HOST=example.com + - EJABBERD_MACRO_ADMIN=admin@example.com + - REGISTER_ADMIN_PASSWORD=somePassw0rd + - CTL_ON_START=registered_users example.com ; + status + ports: + - "5222:5222" + - "5269:5269" + - "5280:5280" + - "5443:5443" + volumes: + - ./conf/ejabberd.yml:/opt/ejabberd/conf/ejabberd.yml:ro + - ./database:/opt/ejabberd/database +``` + +If using Podman, write this `custom.yml` file +and start it with `podman kube play custom.yml`: + +```yaml +apiVersion: v1 + +kind: Pod + +metadata: + name: ejabberd + +spec: + containers: + + - name: ejabberd + image: ghcr.io/processone/ejabberd + env: + - name: EJABBERD_MACRO_HOST + value: example.com + - name: EJABBERD_MACRO_ADMIN + value: admin@example.com + - name: REGISTER_ADMIN_PASSWORD + value: somePassw0rd + - name: CTL_ON_START + value: registered_users example.com ; + status + ports: + - containerPort: 5222 + hostPort: 5222 + - containerPort: 5269 + hostPort: 5269 + - containerPort: 5280 + hostPort: 5280 + - containerPort: 5443 + hostPort: 5443 + volumeMounts: + - mountPath: /opt/ejabberd/conf/ejabberd.yml + name: config + readOnly: true + - mountPath: /opt/ejabberd/database + name: db + + volumes: + - name: config + hostPath: + path: ./conf/ejabberd.yml + type: File + - name: db + hostPath: + path: ./database + type: DirectoryOrCreate +``` + + +### Clustering Example + +In this example, the main container is created first. +Once it is fully started and healthy, a second container is created, +and once ejabberd is started in it, it joins the first one. + +An account is registered in the first node when created (and +we ignore errors that can happen when doing that - for example +when account already exists), +and it should exist in the second node after join. + +Notice that in this example the main container does not have access +to the exterior; the replica exports the ports and can be accessed. + +If using Docker, write this `docker-compose.yml` file +and start it with `docker-compose up`: + +```yaml +version: '3.7' + +services: + + main: + image: ghcr.io/processone/ejabberd + container_name: main + environment: + - ERLANG_NODE_ARG=ejabberd@main + - ERLANG_COOKIE=dummycookie123 + - CTL_ON_CREATE=! register admin localhost asd + healthcheck: + test: netstat -nl | grep -q 5222 + start_period: 5s + interval: 5s + timeout: 5s + retries: 120 + + replica: + image: ghcr.io/processone/ejabberd + container_name: replica + depends_on: + main: + condition: service_healthy + environment: + - ERLANG_NODE_ARG=ejabberd@replica + - ERLANG_COOKIE=dummycookie123 + - CTL_ON_CREATE=join_cluster ejabberd@main + - CTL_ON_START=registered_users localhost ; + status + ports: + - "5222:5222" + - "5269:5269" + - "5280:5280" + - "5443:5443" +``` + +If using Podman, write this `cluster.yml` file +and start it with `podman kube play cluster.yml`: + +```yaml +apiVersion: v1 + +kind: Pod + +metadata: + name: cluster + +spec: + containers: + + - name: first + image: ghcr.io/processone/ejabberd + env: + - name: ERLANG_NODE_ARG + value: main@cluster + - name: ERLANG_COOKIE + value: dummycookie123 + - name: CTL_ON_CREATE + value: register admin localhost asd + - name: CTL_ON_START + value: stats registeredusers ; + status + - name: EJABBERD_MACRO_PORT_C2S + value: 6222 + - name: EJABBERD_MACRO_PORT_C2S_TLS + value: 6223 + - name: EJABBERD_MACRO_PORT_S2S + value: 6269 + - name: EJABBERD_MACRO_PORT_HTTP_TLS + value: 6443 + - name: EJABBERD_MACRO_PORT_HTTP + value: 6280 + - name: EJABBERD_MACRO_PORT_MQTT + value: 6883 + - name: EJABBERD_MACRO_PORT_PROXY65 + value: 6777 + volumeMounts: + - mountPath: /opt/ejabberd/conf/ejabberd.yml + name: config + readOnly: true + + - name: second + image: ghcr.io/processone/ejabberd + env: + - name: ERLANG_NODE_ARG + value: replica@cluster + - name: ERLANG_COOKIE + value: dummycookie123 + - name: CTL_ON_CREATE + value: join_cluster main@cluster ; + started ; + list_cluster + - name: CTL_ON_START + value: stats registeredusers ; + check_password admin localhost asd ; + status + ports: + - containerPort: 5222 + hostPort: 5222 + - containerPort: 5280 + hostPort: 5280 + volumeMounts: + - mountPath: /opt/ejabberd/conf/ejabberd.yml + name: config + readOnly: true + + volumes: + - name: config + hostPath: + path: ./conf/ejabberd.yml + type: File + +``` + + +Images Comparison +----------------- + +Let's summarize the differences between both container images. Legend: + +- :sparkle: is the recommended alternative +- :orange_circle: added in the latest release (ejabberd 25.03) +- :high_brightness: added in the previous release (ejabberd 24.12) +- :low_brightness: added in the pre-previous release (ejabberd 24.10) + +| | [![ejabberd Container](https://img.shields.io/badge/ejabberd-grey?logo=opencontainersinitiative&logoColor=2094f3)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) | [![ecs Container](https://img.shields.io/badge/ecs-grey?logo=docker&logoColor=2094f3)](https://hub.docker.com/r/ejabberd/ecs/) | +|:----------------------|:------------------|:-----------------------| +| Source code | [ejabberd/.github/container](https://github.com/processone/ejabberd/tree/master/.github/container) | [docker-ejabberd/ecs](https://github.com/processone/docker-ejabberd/tree/master/ecs) | +| Generated by | [container.yml](https://github.com/processone/ejabberd/blob/master/.github/workflows/container.yml) | [tests.yml](https://github.com/processone/docker-ejabberd/blob/master/.github/workflows/tests.yml) | +| Built for | stable releases
`master` branch | stable releases
[`master` branch zip](https://github.com/processone/docker-ejabberd/actions/workflows/tests.yml) | +| Architectures | `linux/amd64`
`linux/arm64` | `linux/amd64` | +| Software | Erlang/OTP 27.3.4.3-alpine
Elixir 1.18.4 | Alpine 3.22
Erlang/OTP 26.2
Elixir 1.18.3 | +| Published in | [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd) | [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/)
[ghcr.io/processone/ecs](https://github.com/processone/docker-ejabberd/pkgs/container/ecs) | +| :black_square_button: **Additional content** | +| [ejabberd-contrib](#ejabberd-contrib) | included | not included | +| [ejabberdapi](#ejabberdapi) | included :orange_circle: | included | +| :black_square_button: **Ports** | +| [1880](#ports) for WebAdmin | yes :orange_circle: | yes :orange_circle: | +| [5210](#ports) for `ERL_DIST_PORT` | supported | supported :orange_circle: | +| :black_square_button: **Paths** | +| `$HOME` | `/opt/ejabberd/` | `/home/ejabberd/` | +| User data | `$HOME` :sparkle:
`/home/ejabberd/` :orange_circle: | `$HOME`
`/opt/ejabberd/` :sparkle: :low_brightness: | +| `ejabberdctl` | `ejabberdctl` :sparkle:
`bin/ejabberdctl` :orange_circle: | `bin/ejabberdctl`
`ejabberdctl` :sparkle: :low_brightness: | +| [`captcha.sh`](#captcha) | `$HOME/bin/captcha.sh` :orange_circle: | `$HOME/bin/captcha.sh` :orange_circle: | +| `*.sql` files | `$HOME/sql/*.sql` :sparkle: :orange_circle:
`$HOME/database/*.sql` :orange_circle: | `$HOME/database/*.sql`
`$HOME/sql/*.sql` :sparkle: :orange_circle: | +| Mnesia spool files | `$HOME/database/` :sparkle:
`$HOME/database/NODENAME/` :orange_circle: | `$HOME/database/NODENAME/`
`$HOME/database/` :sparkle: :orange_circle: | +| :black_square_button: **Variables** | +| [`EJABBERD_MACRO_*`](#macros-in-environment) | supported :high_brightness: | supported :high_brightness: | +| Macros used in `ejabberd.yml` | yes :orange_circle: | yes :orange_circle: | +| [`EJABBERD_MACRO_ADMIN`](#register-admin-account) | Grant admin rights :orange_circle:
(default `admin@localhost`)
| Hardcoded `admin@localhost` | +| [`REGISTER_ADMIN_PASSWORD`](#register-admin-account) | Register admin account :orange_circle: | unsupported | +| `CTL_OVER_HTTP` | enabled :orange_circle: | unsupported | diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..819921ee5 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,149 @@ +# Contributing to ejabberd + +We'd love for you to contribute to our source code and to make ejabberd even better than it is +today! Here are the guidelines we'd like you to follow: + +* [Code of Conduct](#code-of-conduct) +* [Questions and Problems](#questions-bugs-features) +* [Issues and Bugs](#found-an-issue-or-bug) +* [Feature Requests](#missing-a-feature) +* [Issue Submission Guidelines](#issue-submission-guidelines) +* [Pull Request Submission Guidelines](#pull-request-submission-guidelines) +* [Signing the CLA](#signing-the-contributor-license-agreement-cla) + +## Code of Conduct + +Help us keep ejabberd community open-minded and inclusive. Please read and follow our [Code of Conduct][coc]. + +## Questions, Bugs, Features + +### Got a Question or Problem? + +Do not open issues for general support questions as we want to keep GitHub issues for bug reports +and feature requests. You've got much better chances of getting your question answered on dedicated +support platforms, the best being [Stack Overflow][stackoverflow]. + +Stack Overflow is a much better place to ask questions since: + +* there are thousands of people willing to help on Stack Overflow +* questions and answers stay available for public viewing so your question / answer might help + someone else +* Stack Overflow's voting system assures that the best answers are prominently visible. + +To save your and our time, we will systematically close all issues that are requests for general +support and redirect people to the section you are reading right now. + +Other channels for support are: + +* ejabberd XMPP room: [ejabberd@conference.process-one.net][muc] +* [ejabberd Mailing List][list] + +### Found an Issue or Bug? + +If you find a bug in the source code, you can help us by submitting an issue to our +[GitHub Repository][github]. Even better, you can submit a Pull Request with a fix. + +### Missing a Feature? + +You can request a new feature by submitting an issue to our [GitHub Repository][github-issues]. + +If you would like to implement a new feature then consider what kind of change it is: + +* **Major Changes** that you wish to contribute to the project should be discussed first in an + [GitHub issue][github-issues] that clearly outlines the changes and benefits of the feature. +* **Small Changes** can directly be crafted and submitted to the [GitHub Repository][github] + as a Pull Request. See the section about [Pull Request Submission Guidelines](#pull-request-submission-guidelines). + +## Issue Submission Guidelines + +Before you submit your issue search the archive, maybe your question was already answered. + +If your issue appears to be a bug, and hasn't been reported, open a new issue. Help us to maximize +the effort we can spend fixing issues and adding new features, by not reporting duplicate issues. + +The "[new issue][github-new-issue]" form contains a number of prompts that you should fill out to +make it easier to understand and categorize the issue. + +## Pull Request Submission Guidelines + +By submitting a pull request for a code or doc contribution, you need to have the right +to grant your contribution's copyright license to ProcessOne. Please check [ProcessOne CLA][cla] +for details. + +Before you submit your pull request consider the following guidelines: + +* Search [GitHub][github-pr] for an open or closed Pull Request + that relates to your submission. You don't want to duplicate effort. +* Create the [development environment][developer-setup] +* Make your changes in a new git branch: + + ```shell + git checkout -b my-fix-branch master + ``` + +* Test your changes and, if relevant, expand the automated test suite. +* Create your patch commit, including appropriate test cases. +* If the changes affect public APIs, change or add relevant [documentation][doc-repo]. +* Commit your changes using a descriptive commit message. + + ```shell + git commit -a + ``` + + Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files. + +* Push your branch to GitHub: + + ```shell + git push origin my-fix-branch + ``` + +* In GitHub, send a pull request to `ejabberd:master`. This will trigger the automated testing. +We will also notify you if you have not yet signed the [contribution agreement][cla]. + +* If you find that the tests have failed, look into the logs to find out +if your changes caused test failures, the commit message was malformed etc. If you find that the +tests failed or times out for unrelated reasons, you can ping a team member so that the build can be +restarted. + +* If we suggest changes, then: + + * Make the required updates. + * Test your changes and test cases. + * Commit your changes to your branch (e.g. `my-fix-branch`). + * Push the changes to your GitHub repository (this will update your Pull Request). + + You can also amend the initial commits and force push them to the branch. + + ```shell + git rebase master -i + git push origin my-fix-branch -f + ``` + + This is generally easier to follow, but separate commits are useful if the Pull Request contains + iterations that might be interesting to see side-by-side. + +That's it! Thank you for your contribution! + +## Signing the Contributor License Agreement (CLA) + +Upon submitting a Pull Request, we will ask you to sign our CLA if you haven't done +so before. It's a quick process, we promise, and you will be able to do it all online + +Here's a link to the [ProcessOne Contribution License Agreement][cla]. + +This is part of the legal framework of the open-source ecosystem that adds some red tape, +but protects both the contributor and the company / foundation behind the project. It also +gives us the option to relicense the code with a more permissive license in the future. + +[coc]: https://github.com/processone/ejabberd/blob/master/CODE_OF_CONDUCT.md +[stackoverflow]: https://stackoverflow.com/questions/tagged/ejabberd?sort=newest +[list]: https://web.archive.org/web/20230319174915/http://lists.jabber.ru/mailman/listinfo/ejabberd +[muc]: xmpp:ejabberd@conference.process-one.net +[github]: https://github.com/processone/ejabberd +[github-issues]: https://github.com/processone/ejabberd/issues +[github-new-issue]: https://github.com/processone/ejabberd/issues/new +[github-pr]: https://github.com/processone/ejabberd/pulls +[doc-repo]: https://github.com/processone/docs.ejabberd.im +[developer-setup]: https://docs.ejabberd.im/developer/ +[cla]: https://cla.process-one.net/ diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 000000000..50c7d614a --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,37 @@ +# Contributors + +We would like to thanks official ejabberd source code contributors: + +- Sergey Abramyan +- Badlop +- Ludovic Bocquet +- Emilio Bustos +- Thiago Camargo +- Juan Pablo Carlino +- Paweł Chmielowski +- Gabriel Gatu +- Tsukasa Hamano +- Konstantinos Kallas +- Evgeny Khramtsov +- Ben Langfeld +- Peter Lemenkov +- Anna Mukharram +- Johan Oudinet +- Pablo Polvorin +- Mickaël Rémond +- Matthias Rieber +- Rafael Roemhild +- Christophe Romain +- Jérôme Sautret +- Sonny Scroggin +- Alexey Shchepin +- Shelley Shyan +- Radoslaw Szymczyszyn +- Stu Tomlinson +- Christian Ulrich +- Holger Weiß + +Please, if you think we are missing your contribution, do not hesitate to contact us at ProcessOne. +In case you do not want to appear in this list, please, let us know as well. + +Thanks ! diff --git a/Makefile.in b/Makefile.in index 5ff3efe0b..cf7480702 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,7 +1,23 @@ -REBAR = @ESCRIPT@ rebar +#. +#' definitions +# + +ESCRIPT = @ESCRIPT@ +REBAR = @rebar@ # rebar|rebar3|mix binary (or path to binary) +REBAR3 = @REBAR3@ # path to rebar3 binary +MIX = @rebar@ +AWK = @AWK@ INSTALL = @INSTALL@ +MKDIR_P = @MKDIR_P@ SED = @SED@ ERL = @ERL@ +EPMD = @EPMD@ +IEX = @IEX@ + +INSTALLUSER=@INSTALLUSER@ +INSTALLGROUP=@INSTALLGROUP@ + +REBAR_ENABLE_ELIXIR = @elixir@ prefix = @prefix@ exec_prefix = @exec_prefix@ @@ -9,24 +25,27 @@ exec_prefix = @exec_prefix@ DESTDIR = # /etc/ejabberd/ -ETCDIR = $(DESTDIR)@sysconfdir@/ejabberd +ETCDIR = @sysconfdir@/ejabberd # /bin/ -BINDIR = $(DESTDIR)@bindir@ +BINDIR = @bindir@ # /sbin/ -SBINDIR = $(DESTDIR)@sbindir@ +SBINDIR = @sbindir@ # /lib/ -LIBDIR = $(DESTDIR)@libdir@ +LIBDIR = @libdir@ # /lib/ejabberd/ -EJABBERDDIR = $(DESTDIR)@libdir@/ejabberd +EJABBERDDIR = @libdir@/ejabberd # /share/doc/ejabberd PACKAGE_TARNAME = @PACKAGE_TARNAME@ datarootdir = @datarootdir@ -DOCDIR = $(DESTDIR)@docdir@ +DOCDIR = @docdir@ + +# /share/doc/man/man5 +MANDIR = @mandir@/man5 # /usr/lib/ejabberd/ebin/ BEAMDIR = $(EJABBERDDIR)/ebin @@ -46,22 +65,31 @@ SODIR = $(PRIVDIR)/lib # /usr/lib/ejabberd/priv/msgs MSGSDIR = $(PRIVDIR)/msgs +# /usr/lib/ejabberd/priv/css +CSSDIR = $(PRIVDIR)/css + +# /usr/lib/ejabberd/priv/img +IMGDIR = $(PRIVDIR)/img + +# /usr/lib/ejabberd/priv/js +JSDIR = $(PRIVDIR)/js + # /usr/lib/ejabberd/priv/sql SQLDIR = $(PRIVDIR)/sql +# /usr/lib/ejabberd/priv/lua +LUADIR = $(PRIVDIR)/lua + # /var/lib/ejabberd/ -SPOOLDIR = $(DESTDIR)@localstatedir@/lib/ejabberd - -# /var/lock/ejabberdctl -CTLLOCKDIR = $(DESTDIR)@localstatedir@/lock/ejabberdctl - -# /var/lib/ejabberd/.erlang.cookie -COOKIEFILE = $(SPOOLDIR)/.erlang.cookie +SPOOLDIR = @localstatedir@/lib/ejabberd # /var/log/ejabberd/ -LOGDIR = $(DESTDIR)@localstatedir@/log/ejabberd +LOGDIR = @localstatedir@/log/ejabberd + +#. +#' install user +# -INSTALLUSER=@INSTALLUSER@ # if no user was enabled, don't set privileges or ownership ifeq ($(INSTALLUSER),) O_USER= @@ -77,87 +105,254 @@ else INIT_USER=$(INSTALLUSER) endif -all: deps src +# if no group was enabled, don't set privileges or ownership +ifneq ($(INSTALLGROUP),) + G_USER=-g $(INSTALLGROUP) +endif -deps: deps/.got +#. +#' rebar / rebar3 / mix +# -deps/.got: - rm -rf deps/.got - rm -rf deps/.built - $(REBAR) get-deps && :> deps/.got +ifeq "$(notdir $(MIX))" "mix" +REBAR_VER:=6 +REBAR_VER_318:=0 +else +REBAR_VER:=$(shell $(REBAR) --version | $(AWK) -F '[ .]' '/rebar / {print $$2}') +REBAR_VER_318:=$(shell $(REBAR) --version | $(AWK) -F '[ .]' '/rebar / {print ($$2 == 3 && $$3 >= 18 ? 1 : 0)}') +endif -deps/.built: deps/.got - $(REBAR) compile && :> deps/.built +ifeq "$(REBAR_VER)" "6" + REBAR=$(MIX) + SKIPDEPS= + LISTDEPS=deps.tree + UPDATEDEPS=deps.update + DEPSPATTERN="s/.*─ \([a-z0-9_]*\) .*/\1/p;" + DEPSBASE=_build + DEPSDIR=$(DEPSBASE)/dev/lib + GET_DEPS= deps.get + CONFIGURE_DEPS=(cd deps/eimp; ./configure) + EBINDIR=$(DEPSDIR)/ejabberd/ebin + XREFOPTIONS=graph + EDOCPRE=MIX_ENV=edoc + EDOCTASK=docs --proglang erlang + CLEANARG=--deps + ELIXIR_LIBDIR_RAW=$(shell elixir -e "IO.puts(:filename.dirname(:code.lib_dir(:elixir)))" -e ":erlang.halt") + ELIXIR_LIBDIR=":$(ELIXIR_LIBDIR_RAW)" + REBARREL=MIX_ENV=prod $(REBAR) release --overwrite + REBARDEV=MIX_ENV=dev $(REBAR) release --overwrite + RELIVECMD=$(ESCRIPT) rel/relive.escript && MIX_ENV=dev RELIVE=true $(IEX) --name ejabberd@localhost -S mix run + REL_LIB_DIR = _build/dev/rel/ejabberd/lib + COPY_REL_TARGET = dev + GET_DEPS_TRANSLATIONS=MIX_ENV=translations $(REBAR) $(GET_DEPS) + DEPSDIR_TRANSLATIONS=deps +else +ifeq ($(REBAR_ENABLE_ELIXIR),true) + ELIXIR_LIBDIR_RAW=$(shell elixir -e "IO.puts(:filename.dirname(:code.lib_dir(:elixir)))" -e ":erlang.halt") + ELIXIR_LIBDIR=":$(ELIXIR_LIBDIR_RAW)" + EXPLICIT_ELIXIR_COMPILE=MIX_ENV=default mix compile.elixir + EXPLICIT_ELIXIR_COMPILE_DEV=MIX_ENV=dev mix compile.elixir + PREPARE_ELIXIR_SCRIPTS=$(MKDIR_P) rel/overlays; cp $(ELIXIR_LIBDIR_RAW)/../bin/iex rel/overlays/; cp $(ELIXIR_LIBDIR_RAW)/../bin/elixir rel/overlays/; sed -i 's|ERTS_BIN=$$|ERTS_BIN=$$SCRIPT_PATH/../../erts-{{erts_vsn}}/bin/|' rel/overlays/elixir +endif +ifeq "$(REBAR_VER)" "3" + SKIPDEPS= + LISTDEPS=tree +ifeq "$(REBAR_VER_318)" "1" + UPDATEDEPS=upgrade --all +else + UPDATEDEPS=upgrade +endif + DEPSPATTERN="s/ (.*//; /^ / s/.* \([a-z0-9_]*\).*/\1/p;" + DEPSBASE=_build + DEPSDIR=$(DEPSBASE)/default/lib + GET_DEPS= get-deps + CONFIGURE_DEPS=$(REBAR) configure-deps + EBINDIR=$(DEPSDIR)/ejabberd/ebin + XREFOPTIONS= + CLEANARG=--all + REBARREL=$(REBAR) as prod tar + REBARDEV=$(REBAR) as dev release + RELIVECMD=$(REBAR) as dev relive + REL_LIB_DIR = _build/dev/rel/ejabberd/lib + COPY_REL_TARGET = dev + GET_DEPS_TRANSLATIONS=$(REBAR) as translations $(GET_DEPS) + DEPSDIR_TRANSLATIONS=_build/translations/lib +else + SKIPDEPS=skip_deps=true + LISTDEPS=-q list-deps + UPDATEDEPS=update-deps + DEPSPATTERN="/ TAG / s/ .*// p; / REV / s/ .*// p; / BRANCH / s/ .*// p;" + DEPSBASE=deps + DEPSDIR=$(DEPSBASE) + GET_DEPS= get-deps + CONFIGURE_DEPS=$(REBAR) configure-deps + EBINDIR=ebin + XREFOPTIONS= + CLEANARG= + REBARREL=$(REBAR) generate + REBARDEV= + RELIVECMD=@echo "Rebar2 detected... relive not supported.\ + \nTry: ./configure --with-rebar=rebar3 ; make relive" + REL_LIB_DIR = rel/ejabberd/lib + COPY_REL_TARGET = rel +endif +endif -src: deps/.built - $(REBAR) skip_deps=true compile +#. +#' main targets +# + +all: scripts deps src + +deps: $(DEPSDIR)/.got + +$(DEPSDIR)/.got: + rm -rf $(DEPSDIR)/.got + rm -rf $(DEPSDIR)/.built + $(MKDIR_P) $(DEPSDIR) + $(REBAR) $(GET_DEPS) && :> $(DEPSDIR)/.got + $(CONFIGURE_DEPS) + +$(DEPSDIR)/.built: $(DEPSDIR)/.got + $(REBAR) compile && :> $(DEPSDIR)/.built + +src: $(DEPSDIR)/.built + $(REBAR) $(SKIPDEPS) compile + $(EXPLICIT_ELIXIR_COMPILE) update: - rm -rf deps/.got - rm -rf deps/.built - $(REBAR) update-deps && :> deps/.got + rm -rf $(DEPSDIR)/.got + rm -rf $(DEPSDIR)/.built + $(REBAR) $(UPDATEDEPS) && :> $(DEPSDIR)/.got + $(CONFIGURE_DEPS) xref: all - $(REBAR) skip_deps=true xref + $(REBAR) $(SKIPDEPS) xref $(XREFOPTIONS) +hooks: all + tools/hook_deps.sh $(EBINDIR) + +options: all + tools/opt_types.sh ejabberd_option $(EBINDIR) translations: - contrib/extract_translations/prepare-translation.sh -updateall + $(GET_DEPS_TRANSLATIONS) + tools/prepare-tr.sh $(DEPSDIR_TRANSLATIONS) -edoc: - $(ERL) -noinput +B -eval \ - 'case edoc:application(ejabberd, ".", []) of ok -> halt(0); error -> halt(1) end.' +doap: + tools/generate-doap.sh + +#. +#' edoc +# + +edoc: edoc_files edoc_compile + $(EDOCPRE) $(REBAR) $(EDOCTASK) + +edoc_compile: deps + $(EDOCPRE) $(REBAR) compile + +edoc_files: _build/edoc/docs.md _build/edoc/logo.png + +_build/edoc/docs.md: edoc_compile + echo "For much more detailed and complete ejabberd documentation, " \ + "go to the [ejabberd Docs](https://docs.ejabberd.im/) site." \ + > _build/edoc/docs.md + +_build/edoc/logo.png: edoc_compile + wget https://docs.ejabberd.im/assets/img/footer_logo_e.png -O _build/edoc/logo.png + +#. +#' format / indent +# + +format: + tools/rebar3-format.sh $(REBAR3) + +indent: + tools/emacs-indent.sh + +#. +#' copy-files +# JOIN_PATHS=$(if $(wordlist 2,1000,$(1)),$(firstword $(1))/$(call JOIN_PATHS,$(wordlist 2,1000,$(1))),$(1)) VERSIONED_DEP=$(if $(DEP_$(1)_VERSION),$(DEP_$(1)_VERSION),$(1)) +DEPIX:=$(words $(subst /, ,$(DEPSDIR))) +LIBIX:=$(shell expr "$(DEPIX)" + 2) + ELIXIR_TO_DEST=$(LIBDIR) $(call VERSIONED_DEP,$(word 2,$(1))) $(wordlist 5,1000,$(1)) DEPS_TO_DEST=$(LIBDIR) $(call VERSIONED_DEP,$(word 2,$(1))) $(wordlist 3,1000,$(1)) MAIN_TO_DEST=$(LIBDIR) $(call VERSIONED_DEP,ejabberd) $(1) -TO_DEST_SINGLE=$(if $(subst XdepsX,,X$(word 1,$(1))X),$(call MAIN_TO_DEST,$(1)),$(if $(subst XlibX,,X$(word 3,$(1))X),$(call DEPS_TO_DEST,$(1)),$(call ELIXIR_TO_DEST,$(1)))) -TO_DEST=$(foreach path,$(1),$(call JOIN_PATHS,$(call TO_DEST_SINGLE,$(subst /, ,$(path))))) +TO_DEST_SINGLE=$(if $(subst X$(DEPSBASE)X,,X$(word 1,$(1))X),$(call MAIN_TO_DEST,$(1)),$(if $(subst XlibX,,X$(word $(LIBIX),$(1))X),$(call DEPS_TO_DEST,$(wordlist $(DEPIX),1000,$(1))),$(call ELIXIR_TO_DEST,$(wordlist $(DEPIX),1000,$(1))))) +TO_DEST=$(foreach path,$(1),$(call JOIN_PATHS,$(DESTDIR)$(call TO_DEST_SINGLE,$(subst /, ,$(path))))) FILTER_DIRS=$(foreach path,$(1),$(if $(wildcard $(path)/*),,$(path))) FILES_WILDCARD=$(call FILTER_DIRS,$(foreach w,$(1),$(wildcard $(w)))) ifeq ($(MAKECMDGOALS),copy-files-sub) -DEPS:=$(sort $(shell $(REBAR) list-deps|$(SED) -e '/^=/d;s/ .*//')) +DEPS:=$(sort $(shell QUIET=1 $(REBAR) $(LISTDEPS) | $(SED) -ne $(DEPSPATTERN) )) -DEPS_FILES=$(call FILES_WILDCARD,$(foreach DEP,$(DEPS),deps/$(DEP)/ebin/*.beam deps/$(DEP)/ebin/*.app deps/$(DEP)/priv/* deps/$(DEP)/priv/lib/* deps/$(DEP)/priv/bin/* deps/$(DEP)/include/*.hrl deps/$(DEP)/lib/*/ebin/*.beam deps/$(DEP)/lib/*/ebin/*.app)) -DEPS_FILES_FILTERED=$(filter-out %/epam deps/elixir/ebin/elixir.app,$(DEPS_FILES)) -DEPS_DIRS=$(sort deps/ $(foreach DEP,$(DEPS),deps/$(DEP)/) $(dir $(DEPS_FILES))) +DEPS_FILES=$(call FILES_WILDCARD,$(foreach DEP,$(DEPS),$(DEPSDIR)/$(DEP)/ebin/*.beam $(DEPSDIR)/$(DEP)/ebin/*.app $(DEPSDIR)/$(DEP)/priv/* $(DEPSDIR)/$(DEP)/priv/lib/* $(DEPSDIR)/$(DEP)/priv/bin/* $(DEPSDIR)/$(DEP)/include/*.hrl $(DEPSDIR)/$(DEP)/COPY* $(DEPSDIR)/$(DEP)/LICENSE* $(DEPSDIR)/$(DEP)/lib/*/ebin/*.beam $(DEPSDIR)/$(DEP)/lib/*/ebin/*.app)) -MAIN_FILES=$(filter-out %/configure.beam,$(call FILES_WILDCARD,ebin/*.beam ebin/*.app priv/msgs/*.msg priv/lib/* include/*.hrl)) -MAIN_DIRS=$(sort $(dir $(MAIN_FILES)) priv/bin priv/sql) +BINARIES=$(DEPSDIR)/epam/priv/bin/epam $(DEPSDIR)/eimp/priv/bin/eimp $(DEPSDIR)/fs/priv/mac_listener + +DEPS_FILES_FILTERED=$(filter-out $(BINARIES) $(DEPSDIR)/elixir/ebin/elixir.app,$(DEPS_FILES)) +DEPS_DIRS=$(sort $(DEPSDIR)/ $(foreach DEP,$(DEPS),$(DEPSDIR)/$(DEP)/) $(dir $(DEPS_FILES))) + +MAIN_FILES=$(filter-out %/configure.beam,$(call FILES_WILDCARD,$(EBINDIR)/*.beam $(EBINDIR)/*.app priv/msgs/*.msg priv/css/*.css priv/img/*.png priv/js/*.js priv/lib/* include/*.hrl COPYING)) +MAIN_DIRS=$(sort $(dir $(MAIN_FILES)) priv/bin priv/sql priv/lua) define DEP_VERSION_template DEP_$(1)_VERSION:=$(shell $(SED) -e '/vsn/!d;s/.*, *"/$(1)-/;s/".*//' $(2) 2>/dev/null) endef -$(foreach DEP,$(DEPS),$(eval $(call DEP_VERSION_template,$(DEP),deps/$(DEP)/ebin/$(DEP).app))) -$(eval $(call DEP_VERSION_template,ejabberd,ebin/ejabberd.app)) +DELETE_TARGET_SO=$(if $(subst X.soX,,X$(suffix $(1))X),,rm -f $(call TO_DEST,$(1));) + +$(foreach DEP,$(DEPS),$(eval $(call DEP_VERSION_template,$(DEP),$(DEPSDIR)/$(DEP)/ebin/$(DEP).app))) +$(eval $(call DEP_VERSION_template,ejabberd,$(EBINDIR)/ejabberd.app)) define COPY_template -$(call TO_DEST,$(1)): $(1) $(call TO_DEST,$(dir $(1))) ; $$(INSTALL) -m 644 $(1) $(call TO_DEST,$(1)) +$(call TO_DEST,$(1)): $(1) $(call TO_DEST,$(dir $(1))) ; $(call DELETE_TARGET_SO, $(1)) $$(INSTALL) -m 644 $(1) $(call TO_DEST,$(1)) +endef + +define COPY_BINARY_template +$(call TO_DEST,$(1)): $(1) $(call TO_DEST,$(dir $(1))) ; rm -f $(call TO_DEST,$(1)); $$(INSTALL) -m 755 $$(O_USER) $(1) $(call TO_DEST,$(1)) endef $(foreach file,$(DEPS_FILES_FILTERED) $(MAIN_FILES),$(eval $(call COPY_template,$(file)))) -$(sort $(call TO_DEST,$(MAIN_DIRS) $(DEPS_DIRS))): - $(INSTALL) -d $@ +$(foreach file,$(BINARIES),$(eval $(call COPY_BINARY_template,$(file)))) -$(call TO_DEST,deps/p1_pam/priv/bin/epam): $(LIBDIR)/%: deps/p1_pam/priv/bin/epam $(call TO_DEST,deps/p1_pam/priv/bin/) - $(INSTALL) -m 750 $(O_USER) $< $@ +$(sort $(call TO_DEST,$(MAIN_DIRS) $(DEPS_DIRS))): + $(INSTALL) -d $@ $(call TO_DEST,priv/sql/lite.sql): sql/lite.sql $(call TO_DEST,priv/sql) $(INSTALL) -m 644 $< $@ -$(call TO_DEST,priv/bin/captcha.sh): tools/captcha.sh $(call TO_DEST,priv/bin) - $(INSTALL) -m 750 $(O_USER) $< $@ +$(call TO_DEST,priv/sql/lite.new.sql): sql/lite.new.sql $(call TO_DEST,priv/sql) + $(INSTALL) -m 644 $< $@ -copy-files-sub2: $(call TO_DEST,$(DEPS_FILES) $(MAIN_FILES) priv/bin/captcha.sh priv/sql/lite.sql) +$(call TO_DEST,priv/bin/captcha.sh): tools/captcha.sh $(call TO_DEST,priv/bin) + $(INSTALL) -m 755 $(O_USER) $< $@ + +$(call TO_DEST,priv/lua/redis_sm.lua): priv/lua/redis_sm.lua $(call TO_DEST,priv/lua) + $(INSTALL) -m 644 $< $@ + +ifeq (@sqlite@,true) +SQLITE_FILES = priv/sql/lite.sql priv/sql/lite.new.sql +endif + +ifeq (@redis@,true) +REDIS_FILES = priv/lua/redis_sm.lua +endif + +copy-files-sub2: $(call TO_DEST,$(DEPS_FILES) $(MAIN_FILES) priv/bin/captcha.sh $(SQLITE_FILES) $(REDIS_FILES)) + +.PHONY: $(call TO_DEST,$(DEPS_FILES) $(MAIN_DIRS) $(DEPS_DIRS)) endif @@ -166,147 +361,265 @@ copy-files: copy-files-sub: copy-files-sub2 -install: all copy-files +#. +#' copy-files-rel +# + +copy-files-rel: $(COPY_REL_TARGET) # - # Configuration files - $(INSTALL) -d -m 750 $(G_USER) $(ETCDIR) - [ -f $(ETCDIR)/ejabberd.yml ] \ - && $(INSTALL) -b -m 640 $(G_USER) ejabberd.yml.example $(ETCDIR)/ejabberd.yml-new \ - || $(INSTALL) -b -m 640 $(G_USER) ejabberd.yml.example $(ETCDIR)/ejabberd.yml - $(SED) -e "s*{{rootdir}}*@prefix@*g" \ - -e "s*{{installuser}}*@INSTALLUSER@*g" \ - -e "s*{{bindir}}*@bindir@*g" \ - -e "s*{{libdir}}*@libdir@*g" \ - -e "s*{{sysconfdir}}*@sysconfdir@*g" \ - -e "s*{{localstatedir}}*@localstatedir@*g" \ - -e "s*{{docdir}}*@docdir@*g" \ - -e "s*{{erl}}*@ERL@*g" \ - -e "s*{{epmd}}*@EPMD@*g" ejabberdctl.template \ - > ejabberdctl.example - [ -f $(ETCDIR)/ejabberdctl.cfg ] \ - && $(INSTALL) -b -m 640 $(G_USER) ejabberdctl.cfg.example $(ETCDIR)/ejabberdctl.cfg-new \ - || $(INSTALL) -b -m 640 $(G_USER) ejabberdctl.cfg.example $(ETCDIR)/ejabberdctl.cfg - $(INSTALL) -b -m 644 $(G_USER) inetrc $(ETCDIR)/inetrc + # Libraries + (cd $(REL_LIB_DIR) && find . -follow -type f ! -executable -exec $(INSTALL) -vDm 640 $(G_USER) {} $(DESTDIR)$(LIBDIR)/{} \;) # - # Administration script - [ -d $(SBINDIR) ] || $(INSTALL) -d -m 755 $(SBINDIR) - $(INSTALL) -m 550 $(G_USER) ejabberdctl.example $(SBINDIR)/ejabberdctl - # Elixir binaries - [ -d $(BINDIR) ] || $(INSTALL) -d -m 755 $(BINDIR) - [ -f deps/elixir/bin/iex ] && $(INSTALL) -m 550 $(G_USER) deps/elixir/bin/iex $(BINDIR)/iex || true - [ -f deps/elixir/bin/elixir ] && $(INSTALL) -m 550 $(G_USER) deps/elixir/bin/elixir $(BINDIR)/elixir || true - [ -f deps/elixir/bin/mix ] && $(INSTALL) -m 550 $(G_USER) deps/elixir/bin/mix $(BINDIR)/mix || true + # *.so: + (cd $(REL_LIB_DIR) && find . -follow -type f -executable -name *.so -exec $(INSTALL) -vDm 640 $(G_USER) {} $(DESTDIR)$(LIBDIR)/{} \;) # - # Init script + # Executable files + (cd $(REL_LIB_DIR) && find . -follow -type f -executable ! -name *.so -exec $(INSTALL) -vDm 550 $(G_USER) {} $(DESTDIR)$(LIBDIR)/{} \;) + +#. +#' uninstall-librel +# + +uninstall-librel: + (cd $(REL_LIB_DIR) && find . -follow -type f -exec rm -fv -v $(DESTDIR)$(LIBDIR)/{} \;) + (cd $(REL_LIB_DIR) && find . -follow -depth -type d -exec rm -dv -v $(DESTDIR)$(LIBDIR)/{} \;) + +#. +#' relive +# + +relive: + $(EXPLICIT_ELIXIR_COMPILE_DEV) + $(RELIVECMD) + +relivelibdir=$(shell pwd)/$(DEPSDIR) +relivedir=$(shell pwd)/_build/relive +CONFIG_DIR = ${relivedir}/conf +SPOOL_DIR = ${relivedir}/database +LOGS_DIR = ${relivedir}/logs + +#. +#' scripts +# + +ejabberdctl.relive: + $(SED) -e "s*{{installuser}}*${INSTALLUSER}*g" \ + -e "s*{{config_dir}}*${CONFIG_DIR}*g" \ + -e "s*{{logs_dir}}*${LOGS_DIR}*g" \ + -e "s*{{spool_dir}}*${SPOOL_DIR}*g" \ + -e "s*{{bindir}}*${BINDIR}*g" \ + -e "s*{{libdir}}*${relivelibdir}${ELIXIR_LIBDIR}*g" \ + -e "s*ERTS_VSN*# ERTS_VSN*g" \ + -e "s*{{iexpath}}*${IEX}*g" \ + -e "s*{{erl}}*${ERL}*g" \ + -e "s*{{epmd}}*${EPMD}*g" ejabberdctl.template \ + > ejabberdctl.relive + +ejabberd.init: $(SED) -e "s*@ctlscriptpath@*$(SBINDIR)*g" \ -e "s*@installuser@*$(INIT_USER)*g" ejabberd.init.template \ > ejabberd.init chmod 755 ejabberd.init - # - # Service script - $(SED) -e "s*@ctlscriptpath@*$(SBINDIR)*g" ejabberd.service.template \ + +ejabberd.service: + $(SED) -e "s*@ctlscriptpath@*$(SBINDIR)*g" \ + -e "s*@installuser@*$(INIT_USER)*g" ejabberd.service.template \ > ejabberd.service chmod 644 ejabberd.service + +ejabberdctl.example: vars.config + $(SED) -e "s*{{installuser}}*${INSTALLUSER}*g" \ + -e "s*{{config_dir}}*${ETCDIR}*g" \ + -e "s*{{logs_dir}}*${LOGDIR}*g" \ + -e "s*{{spool_dir}}*${SPOOLDIR}*g" \ + -e "s*{{bindir}}*${BINDIR}*g" \ + -e "s*{{libdir}}*${LIBDIR}${ELIXIR_LIBDIR}*g" \ + -e "s*ERTS_VSN*# ERTS_VSN*g" \ + -e "s*{{iexpath}}*${IEX}*g" \ + -e "s*{{erl}}*${ERL}*g" \ + -e "s*{{epmd}}*${EPMD}*g" ejabberdctl.template \ + > ejabberdctl.example + +scripts: ejabberd.init ejabberd.service ejabberdctl.example + +#. +#' install +# + +install: copy-files install-main + +install-rel: copy-files-rel install-main + +install-main: + # + # Configuration files + $(INSTALL) -d -m 750 $(G_USER) $(DESTDIR)$(ETCDIR) + [ -f $(DESTDIR)$(ETCDIR)/ejabberd.yml ] \ + && $(INSTALL) -b -m 640 $(G_USER) ejabberd.yml.example $(DESTDIR)$(ETCDIR)/ejabberd.yml-new \ + || $(INSTALL) -b -m 640 $(G_USER) ejabberd.yml.example $(DESTDIR)$(ETCDIR)/ejabberd.yml + [ -f $(DESTDIR)$(ETCDIR)/ejabberdctl.cfg ] \ + && $(INSTALL) -b -m 640 $(G_USER) ejabberdctl.cfg.example $(DESTDIR)$(ETCDIR)/ejabberdctl.cfg-new \ + || $(INSTALL) -b -m 640 $(G_USER) ejabberdctl.cfg.example $(DESTDIR)$(ETCDIR)/ejabberdctl.cfg + $(INSTALL) -b -m 644 $(G_USER) inetrc $(DESTDIR)$(ETCDIR)/inetrc + # + # Administration script + [ -d $(DESTDIR)$(SBINDIR) ] || $(INSTALL) -d -m 755 $(DESTDIR)$(SBINDIR) + $(INSTALL) -m 550 $(G_USER) ejabberdctl.example $(DESTDIR)$(SBINDIR)/ejabberdctl + # Elixir binaries + [ -d $(DESTDIR)$(BINDIR) ] || $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) + [ -f $(DEPSDIR)/elixir/bin/iex ] && $(INSTALL) -m 550 $(G_USER) $(DEPSDIR)/elixir/bin/iex $(DESTDIR)$(BINDIR)/iex || true + [ -f $(DEPSDIR)/elixir/bin/elixir ] && $(INSTALL) -m 550 $(G_USER) $(DEPSDIR)/elixir/bin/elixir $(DESTDIR)$(BINDIR)/elixir || true + [ -f $(DEPSDIR)/elixir/bin/mix ] && $(INSTALL) -m 550 $(G_USER) $(DEPSDIR)/elixir/bin/mix $(DESTDIR)$(BINDIR)/mix || true # # Spool directory - $(INSTALL) -d -m 750 $(O_USER) $(SPOOLDIR) - $(CHOWN_COMMAND) -R @INSTALLUSER@ $(SPOOLDIR) >$(CHOWN_OUTPUT) - chmod -R 750 $(SPOOLDIR) - [ ! -f $(COOKIEFILE) ] || { $(CHOWN_COMMAND) @INSTALLUSER@ $(COOKIEFILE) >$(CHOWN_OUTPUT) ; chmod 400 $(COOKIEFILE) ; } - # - # ejabberdctl lock directory - $(INSTALL) -d -m 750 $(O_USER) $(CTLLOCKDIR) - $(CHOWN_COMMAND) -R @INSTALLUSER@ $(CTLLOCKDIR) >$(CHOWN_OUTPUT) - chmod -R 750 $(CTLLOCKDIR) + $(INSTALL) -d -m 750 $(O_USER) $(DESTDIR)$(SPOOLDIR) + $(CHOWN_COMMAND) -R $(INSTALLUSER) $(DESTDIR)$(SPOOLDIR) >$(CHOWN_OUTPUT) + chmod -R 750 $(DESTDIR)$(SPOOLDIR) # # Log directory - $(INSTALL) -d -m 750 $(O_USER) $(LOGDIR) - $(CHOWN_COMMAND) -R @INSTALLUSER@ $(LOGDIR) >$(CHOWN_OUTPUT) - chmod -R 750 $(LOGDIR) + $(INSTALL) -d -m 750 $(O_USER) $(DESTDIR)$(LOGDIR) + $(CHOWN_COMMAND) -R $(INSTALLUSER) $(DESTDIR)$(LOGDIR) >$(CHOWN_OUTPUT) + chmod -R 750 $(DESTDIR)$(LOGDIR) # # Documentation - $(INSTALL) -d $(DOCDIR) - [ -f doc/guide.html ] \ - && $(INSTALL) -m 644 doc/guide.html $(DOCDIR) \ - || echo "Documentation not included in sources" - $(INSTALL) -m 644 COPYING $(DOCDIR) + $(INSTALL) -d $(DESTDIR)$(MANDIR) + $(INSTALL) -d $(DESTDIR)$(DOCDIR) + [ -f man/ejabberd.yml.5 ] \ + && $(INSTALL) -m 644 man/ejabberd.yml.5 $(DESTDIR)$(MANDIR) \ + || echo "Man page not included in sources" + $(INSTALL) -m 644 COPYING $(DESTDIR)$(DOCDIR) + +#. +#' uninstall +# uninstall: uninstall-binary +uninstall-rel: uninstall-binary uninstall-librel + uninstall-binary: - rm -f $(SBINDIR)/ejabberdctl - rm -f $(BINDIR)/iex - rm -f $(BINDIR)/elixir - rm -f $(BINDIR)/mix - rm -fr $(DOCDIR) - rm -f $(BEAMDIR)/*.beam - rm -f $(BEAMDIR)/*.app - rm -fr $(BEAMDIR) - rm -f $(INCLUDEDIR)/*.hrl - rm -fr $(INCLUDEDIR) - rm -fr $(PBINDIR) - rm -f $(SODIR)/*.so - rm -fr $(SODIR) - rm -f $(MSGSDIR)/*.msgs - rm -fr $(MSGSDIR) - rm -f $(SQLDIR)/*.sql - rm -fr $(SQLDIR) - rm -fr $(PRIVDIR) - rm -fr $(EJABBERDDIR) + rm -f $(DESTDIR)$(SBINDIR)/ejabberdctl + rm -f $(DESTDIR)$(BINDIR)/iex + rm -f $(DESTDIR)$(BINDIR)/elixir + rm -f $(DESTDIR)$(BINDIR)/mix + rm -fr $(DESTDIR)$(DOCDIR) + rm -f $(DESTDIR)$(BEAMDIR)/*.beam + rm -f $(DESTDIR)$(BEAMDIR)/*.app + rm -fr $(DESTDIR)$(BEAMDIR) + rm -f $(DESTDIR)$(INCLUDEDIR)/*.hrl + rm -fr $(DESTDIR)$(INCLUDEDIR) + rm -fr $(DESTDIR)$(PBINDIR) + rm -f $(DESTDIR)$(SODIR)/*.so + rm -fr $(DESTDIR)$(SODIR) + rm -f $(DESTDIR)$(MSGSDIR)/*.msg + rm -fr $(DESTDIR)$(MSGSDIR) + rm -f $(DESTDIR)$(CSSDIR)/*.css + rm -fr $(DESTDIR)$(CSSDIR) + rm -f $(DESTDIR)$(IMGDIR)/*.png + rm -fr $(DESTDIR)$(IMGDIR) + rm -f $(DESTDIR)$(JSDIR)/*.js + rm -fr $(DESTDIR)$(JSDIR) + rm -f $(DESTDIR)$(SQLDIR)/*.sql + rm -fr $(DESTDIR)$(SQLDIR) + rm -fr $(DESTDIR)$(LUADIR)/*.lua + rm -fr $(DESTDIR)$(LUADIR) + rm -fr $(DESTDIR)$(PRIVDIR) + rm -fr $(DESTDIR)$(EJABBERDDIR) + rm -f $(DESTDIR)$(MANDIR)/ejabberd.yml.5 uninstall-all: uninstall-binary - rm -rf $(ETCDIR) - rm -rf $(EJABBERDDIR) - rm -rf $(SPOOLDIR) - rm -rf $(CTLLOCKDIR) - rm -rf $(LOGDIR) + rm -rf $(DESTDIR)$(ETCDIR) + rm -rf $(DESTDIR)$(EJABBERDDIR) + rm -rf $(DESTDIR)$(SPOOLDIR) + rm -rf $(DESTDIR)$(LOGDIR) + +#. +#' clean +# clean: - rm -rf deps/.got - rm -rf deps/.built + rm -rf $(DEPSDIR)/.got + rm -rf $(DEPSDIR)/.built rm -rf test/*.beam - $(REBAR) clean + rm -f rebar.lock + rm -f ejabberdctl.example ejabberd.init ejabberd.service + $(REBAR) clean $(CLEANARG) clean-rel: rm -rf rel/ejabberd distclean: clean clean-rel + rm -f aclocal.m4 rm -f config.status rm -f config.log rm -rf autom4te.cache + rm -rf $(EBINDIR) + rm -rf $(DEPSBASE) rm -rf deps - rm -rf ebin rm -f Makefile rm -f vars.config - rm -f src/ejabberd.app.src - [ ! -f ../ChangeLog ] || rm -f ../ChangeLog -rel: all - $(REBAR) generate +#. +#' releases +# + +rel: prod + +prod: + $(PREPARE_ELIXIR_SCRIPTS) + $(REBARREL) + +DEV_CONFIG = _build/dev/rel/ejabberd/conf/ejabberd.yml + +dev $(DEV_CONFIG): + $(PREPARE_ELIXIR_SCRIPTS) + $(REBARDEV) + +#. +#' tags +# TAGS: - etags *.erl + etags src/*.erl + +#. +#' makefile +# Makefile: Makefile.in -deps := $(wildcard deps/*/ebin) +#. +#' dialyzer +# + +ifeq "$(REBAR_VER)" "6" # Mix +dialyzer: + MIX_ENV=test $(REBAR) dialyzer + +else +ifeq "$(REBAR_VER)" "3" # Rebar3 +dialyzer: + $(REBAR) dialyzer + +else # Rebar2 +deps := $(wildcard $(DEPSDIR)/*/ebin) dialyzer/erlang.plt: - @mkdir -p dialyzer + @$(MKDIR_P) dialyzer @dialyzer --build_plt --output_plt dialyzer/erlang.plt \ -o dialyzer/erlang.log --apps kernel stdlib sasl crypto \ - public_key ssl mnesia inets odbc tools compiler erts webtool \ - runtime_tools asn1 observer xmerl et gs wx syntax_tools; \ + public_key ssl mnesia inets odbc compiler erts \ + os_mon asn1 syntax_tools; \ status=$$? ; if [ $$status -ne 2 ]; then exit $$status; else exit 0; fi dialyzer/deps.plt: - @mkdir -p dialyzer + @$(MKDIR_P) dialyzer @dialyzer --build_plt --output_plt dialyzer/deps.plt \ -o dialyzer/deps.log $(deps); \ status=$$? ; if [ $$status -ne 2 ]; then exit $$status; else exit 0; fi dialyzer/ejabberd.plt: - @mkdir -p dialyzer + @$(MKDIR_P) dialyzer @dialyzer --build_plt --output_plt dialyzer/ejabberd.plt \ -o dialyzer/ejabberd.log ebin; \ status=$$? ; if [ $$status -ne 2 ]; then exit $$status; else exit 0; fi @@ -327,17 +640,91 @@ dialyzer: erlang_plt deps_plt ejabberd_plt @dialyzer --plts dialyzer/*.plt --no_check_plt \ --get_warnings -o dialyzer/error.log ebin; \ status=$$? ; if [ $$status -ne 2 ]; then exit $$status; else exit 0; fi +endif +endif + +#. +#' elvis +# + +elvis: + $(REBAR) lint + +#. +#' test +# test: @echo "************************** NOTICE ***************************************" @cat test/README @echo "*************************************************************************" @cd priv && ln -sf ../sql - $(REBAR) skip_deps=true ct + $(REBAR) $(SKIPDEPS) ct -quicktest: - $(REBAR) skip_deps=true ct suites=elixir +.PHONY: test-% +define test-group-target +test-$1: + $(REBAR) $(SKIPDEPS) ct --suite=test/ejabberd_SUITE --group=$1 +endef -.PHONY: src edoc dialyzer Makefile TAGS clean clean-rel distclean rel \ - install uninstall uninstall-binary uninstall-all translations deps test \ - quicktest erlang_plt deps_plt ejabberd_plt +ifneq ($(filter test-%,$(MAKECMDGOALS)),) +group_to_test := $(patsubst test-%,%,$(filter test-%,$(MAKECMDGOALS))) +$(eval $(call test-group-target,$(group_to_test))) +endif + +test-eunit: + $(REBAR) $(SKIPDEPS) eunit --verbose + +#. +#' phony +# + +.PHONY: src edoc dialyzer Makefile TAGS clean clean-rel distclean prod rel \ + install uninstall uninstall-binary uninstall-all translations deps test test-eunit \ + all dev doap help install-rel relive scripts uninstall-rel update \ + erlang_plt deps_plt ejabberd_plt xref hooks options format indent + +#. +#' help +# + +help: + @echo "" + @echo " [all] " + @echo " scripts Prepare ejabberd start scripts" + @echo " deps Get and configure dependencies" + @echo " src Compile dependencies and ejabberd" + @echo " update Update dependencies source code" + @echo " clean Clean binary files" + @echo " distclean Clean completely the development files" + @echo "" + @echo " install Install ejabberd to /usr/local" + @echo " install-rel Install ejabberd to /usr/local (using release)" + @echo " uninstall Uninstall ejabberd (buggy)" + @echo " uninstall-rel Uninstall ejabberd (using release)" + @echo " uninstall-all Uninstall also configuration, logs, mnesia... (buggy)" + @echo "" + @echo " prod Build a production release" + @echo " dev Build a development release" + @echo " relive Start a live ejabberd in _build/relive/" + @echo "" + @echo " doap Generate DOAP file" + @echo " edoc Generate EDoc documentation [mix]" + @echo " options Generate ejabberd_option.erl" + @echo " translations Extract translation files" + @echo " TAGS Generate tags file for text editors" + @echo "" + @echo " format Format source code using rebar3_format" + @echo " indent Indent source code using erlang-mode [emacs]" + @echo "" + @echo " dialyzer Run Dialyzer static analyzer" + @echo " elvis Run Elvis source code style reviewer [rebar3]" + @echo " hooks Run hooks validator" + @echo " test Run Common Tests suite [rebar3]" + @echo " test-eunit Run EUnit suite [rebar3]" + @echo " test- Run Common Test suite for specific group only [rebar3]" + @echo " xref Run cross reference analysis [rebar3]" + +#. +#' +# vim: foldmarker=#',#. foldmethod=marker: diff --git a/README b/README deleted file mode 100644 index b0dff0720..000000000 --- a/README +++ /dev/null @@ -1,174 +0,0 @@ -ejabberd Community Edition -========================== - -[![Build Status](https://travis-ci.org/processone/ejabberd.svg?branch=master)](https://travis-ci.org/processone/ejabberd) [![Hex version](https://img.shields.io/hexpm/v/ejabberd.svg "Hex version")](https://hex.pm/packages/ejabberd) - -ejabberd is a distributed, fault-tolerant technology that allows the creation -of large-scale instant messaging applications. The server can reliably support -thousands of simultaneous users on a single node and has been designed to -provide exceptional standards of fault tolerance. As an open source -technology, based on industry-standards, ejabberd can be used to build bespoke -solutions very cost effectively. - - -Key Features ------------- - -- **Cross-platform** - ejabberd runs under Microsoft Windows and Unix-derived systems such as - Linux, FreeBSD and NetBSD. - -- **Distributed** - You can run ejabberd on a cluster of machines and all of them will serve the - same XMPP domain(s). When you need more capacity you can simply add a new - cheap node to your cluster. Accordingly, you do not need to buy an expensive - high-end machine to support tens of thousands concurrent users. - -- **Fault-tolerant** - You can deploy an ejabberd cluster so that all the information required for - a properly working service will be replicated permanently on all nodes. This - means that if one of the nodes crashes, the others will continue working - without disruption. In addition, nodes also can be added or replaced ‘on - the fly’. - -- **Administrator-friendly** - ejabberd is built on top of the Open Source Erlang. As a result you do not - need to install an external database, an external web server, amongst others - because everything is already included, and ready to run out of the box. - Other administrator benefits include: - - Comprehensive documentation. - - Straightforward installers for Linux and Mac OS X. - - Web administration. - - Shared roster groups. - - Command line administration tool. - - Can integrate with existing authentication mechanisms. - - Capability to send announce messages. - -- **Internationalized** - ejabberd leads in internationalization. Hence it is very well suited in a - globalized world. Related features are: - - Translated to 25 languages. - - Support for IDNA. - -- **Open Standards** - ejabberd is the first Open Source Jabber server claiming to fully comply to - the XMPP standard. - - Fully XMPP-compliant. - - XML-based protocol. - - Many protocols supported. - - -Additional Features -------------------- - -Moreover, ejabberd comes with a wide range of other state-of-the-art features: - -- **Modularity** - - Load only the modules you want. - - Extend ejabberd with your own custom modules. - -- **Security** - - SASL and STARTTLS for c2s and s2s connections. - - STARTTLS and Dialback s2s connections. - - Web Admin accessible via HTTPS secure access. - -- **Databases** - - Internal database for fast deployment (Mnesia). - - Native MySQL support. - - Native PostgreSQL support. - - ODBC data storage support. - - Microsoft SQL Server support. - -- **Authentication** - - Internal authentication. - - PAM, LDAP and ODBC. - - External authentication script. - -- **Others** - - Support for virtual hosting. - - Compressing XML streams with Stream Compression (XEP-0138). - - Statistics via Statistics Gathering (XEP-0039). - - IPv6 support both for c2s and s2s connections. - - Multi-User Chat module with support for clustering and HTML logging. - - Users Directory based on users vCards. - - Publish-Subscribe component with support for Personal Eventing. - - Support for web clients: HTTP Polling and HTTP Binding (BOSH). - - IRC transport. - - Component support: interface with networks such as AIM, ICQ and MSN. - - -Quickstart guide ----------------- - -### 0. Requirements - -To compile ejabberd you need: - - - GNU Make. - - GCC. - - Libexpat 1.95 or higher. - - Libyaml 0.1.4 or higher. - - Erlang/OTP 17.1 or higher. - - OpenSSL 1.0.0 or higher, for STARTTLS, SASL and SSL encryption. - - Zlib 1.2.3 or higher, for Stream Compression support (XEP-0138). Optional. - - PAM library. Optional. For Pluggable Authentication Modules (PAM). - - GNU Iconv 1.8 or higher, for the IRC Transport (mod_irc). Optional. Not - needed on systems with GNU Libc. - - ImageMagick's Convert program. Optional. For CAPTCHA challenges. - - -### 1. Compile and install on *nix systems - -To compile ejabberd, execute the following commands. The first one is only -necessary if your source tree didn't come with a `configure` script. - - ./autogen.sh - ./configure - make - -To install ejabberd, run this command with system administrator rights (root -user): - - sudo make install - -These commands will: - -- Install the configuration files in `/etc/ejabberd/` -- Install ejabberd binary, header and runtime files in `/lib/ejabberd/` -- Install the administration script: `/sbin/ejabberdctl` -- Install ejabberd documentation in `/share/doc/ejabberd/` -- Create a spool directory: `/var/lib/ejabberd/` -- Create a directory for log files: `/var/log/ejabberd/` - - -### 2. Start ejabberd - -You can use the `ejabberdctl` command line administration script to -start and stop ejabberd. For example: - - ejabberdctl start - - -For detailed information please refer to the ejabberd Installation and -Operation Guide available online and in the `doc` directory of the source -tarball. - - -Development ------------ - -In order to assist in the development of ejabberd, and particularly the -execution of the test suite, a Vagrant environment is available at -https://github.com/processone/ejabberd-vagrant-dev. - -To start ejabberd in development mode from the repository directory, you can -type a command like: - - EJABBERD_CONFIG_PATH=ejabberd.yml erl -pa ebin -pa deps/*/ebin -pa test -pa deps/elixir/lib/*/ebin/ -s ejabberd - -Links ------ - -- Documentation: http://docs.ejabberd.im -- Community site: https://www.ejabberd.im -- ejabberd commercial offering and support: https://www.process-one.net/en/ejabberd diff --git a/README.md b/README.md deleted file mode 120000 index 100b93820..000000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -README \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000..646cc4a17 --- /dev/null +++ b/README.md @@ -0,0 +1,136 @@ + +

+ +

+

+ + + + + + + + + + +
+ + + + + + + + +

+ +[ejabberd][im] is an open-source, +robust, scalable and extensible realtime platform built using [Erlang/OTP][erlang], +that includes [XMPP][xmpp] Server, [MQTT][mqtt] Broker and [SIP][sip] Service. + +Check the features in [ejabberd.im][im], [ejabberd Docs][features], +[ejabberd at ProcessOne][p1home], and the list of [supported protocols in ProcessOne][xeps] +and [XMPP.org][xmppej]. + +Installation +------------ + +There are several ways to install ejabberd: + +- Source code: compile yourself, see [COMPILE](COMPILE.md) +- Installers: + - [ProcessOne Download Page][p1download] or [GitHub Releases][releases] for releases. + - [GitHub Actions](https://github.com/processone/ejabberd/actions/workflows/installers.yml) for master branch (`run`/`deb`/`rpm` for `x64` and `arm64`) +- Docker Containers: + - `ecs` container image: [Docker Hub][hubecs] and [Github Packages][packagesecs], see [ecs README][docker-ecs-readme] (for `x64`) + - `ejabberd` container image: [Github Packages][packages] for releases and master branch, see [CONTAINER](CONTAINER.md) (for `x64` and `arm64`) +- Using your [Operating System package][osp] +- Using the [Homebrew][homebrew] package manager + +More info can be found in the `Installation` part of [ejabberd Docs](https://docs.ejabberd.im/admin/install/). + +Documentation +------------- + +Please check the [ejabberd Docs][docs] website. + +When compiling from source code, you can get some help with: + + ./configure --help + make help + +Once ejabberd is installed, try: + + ejabberdctl help + man ejabberd.yml + +Development +----------- + +Bug reports and features are tracked using [GitHub Issues][issues], +please check [CONTRIBUTING](CONTRIBUTING.md) for details. + +Translations can be improved online [using Weblate][weblate] +or in your local machine as explained in [Localization][localization]. + +Documentation for developers is available in [ejabberd docs: Developers][docs-dev]. + +There are nightly builds of ejabberd, both for `master` branch and for Pull Requests: + +- Installers: go to [GitHub Actions: Installers](https://github.com/processone/ejabberd/actions/workflows/installers.yml), open the most recent commit, on the bottom of that commit page, download the `ejabberd-packages.zip` artifact. +- `ejabberd` container image: go to [ejabberd Github Packages][packages] + +Security reports or concerns should preferably be reported privately, +please send an email to the address: contact at process-one dot net +or some other method from [ProcessOne Contact][p1contact]. + +For commercial offering and support, including [ejabberd Business Edition][p1home] +and [Fluux (ejabberd in the Cloud)][fluux], please check [ProcessOne ejabberd page][p1home]. + +Security +-------- + +For information on how to report security vulnerabilities, please refer to the [SECURITY.md](SECURITY.md) file. It contains guidelines on how to report vulnerabilities privately and securely, ensuring that any issues are addressed in a timely and confidential manner. + +Community +--------- + +There are several places to get in touch with other ejabberd developers and administrators: + +- ejabberd XMPP chatroom: [ejabberd@conference.process-one.net][muc] +- [GitHub Discussions][discussions] +- [Stack Overflow][stackoverflow] + +License +------- + +- ejabberd is released under the __GNU General Public License v2__ (see [COPYING](COPYING)) +- [ejabberd translations](https://github.com/processone/ejabberd-po/) under __MIT License__. + +[discussions]: https://github.com/processone/ejabberd/discussions +[docker-ecs-readme]: https://github.com/processone/docker-ejabberd/tree/master/ecs#readme +[docs-dev]: https://docs.ejabberd.im/developer/ +[docs]: https://docs.ejabberd.im +[erlang]: https://www.erlang.org/ +[features]: https://docs.ejabberd.im/admin/introduction/ +[fluux]: https://fluux.io/ +[homebrew]: https://docs.ejabberd.im/admin/install/homebrew/ +[hubecs]: https://hub.docker.com/r/ejabberd/ecs/ +[im]: https://www.ejabberd.im/ +[issues]: https://github.com/processone/ejabberd/issues +[localization]: https://docs.ejabberd.im/developer/extending-ejabberd/localization/ +[mqtt]: https://mqtt.org/ +[muc]: xmpp:ejabberd@conference.process-one.net +[osp]: https://docs.ejabberd.im/admin/install/os-package/ +[p1contact]: https://www.process-one.net/contact/ +[p1download]: https://www.process-one.net/download/ejabberd/ +[p1home]: https://www.process-one.net/ejabberd/ +[packages]: https://github.com/processone/ejabberd/pkgs/container/ejabberd +[packagesecs]: https://github.com/processone/docker-ejabberd/pkgs/container/ecs +[releases]: https://github.com/processone/ejabberd/releases +[sip]: https://en.wikipedia.org/wiki/Session_Initiation_Protocol +[stackoverflow]: https://stackoverflow.com/questions/tagged/ejabberd?sort=newest +[weblate]: https://hosted.weblate.org/projects/ejabberd/ejabberd-po/ +[xeps]: https://www.process-one.net/ejabberd-features/ +[xmpp]: https://xmpp.org/ +[xmppej]: https://xmpp.org/software/servers/ejabberd/ diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..bb2292826 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,45 @@ +# Security Policy + +## Supported Versions + +We recommend that all users always use the latest version of ejabberd. + +To ensure the best experience and security, upgrade to the latest version available on [this repo](https://github.com/processone/ejabberd). + +## Reporting a Vulnerability + +### Private Reporting + +**Preferred Method**: Use GitHub's private vulnerability reporting system by clicking the "Report a Vulnerability" button in the [Security tab of this repository](https://github.com/processone/ejabberd/security). This ensures your report is securely transmitted and tracked. + +**Alternative**: If you cannot use the GitHub system, send an email to **`contact@process-one.net`** with the following details: + +- A clear description of the vulnerability. +- Steps to reproduce the issue. +- Any potential impact or exploitation scenarios. + +### Response Time + +We aim to acknowledge receipt of your report within 72 hours. You can expect regular updates on the status of your report. + +### Resolution + +If the vulnerability is confirmed, we will work on a patch or mitigation strategy. +We will notify you once the issue is resolved and coordinate a public disclosure if needed. + +### Acknowledgements + +We value and appreciate the contributions of security researchers and community members. +If you wish, we are happy to acknowledge your efforts publicly by listing your name (or alias) below in this document. +Please let us know if you would like to be recognized when reporting the vulnerability. + +## Public Discussion + +For general inquiries or discussions about the project’s security, feel free to chat with us here: + +- XMPP room: `ejabberd@conference.process-one.net` +- [GitHub Discussions](https://github.com/processone/ejabberd/discussions) + +However, please note that if the issue is **critical** or potentially exploitable, **do not share it publicly**. Instead, we strongly recommend you contact the maintainers directly via the private reporting methods outlined above to ensure a secure and timely response. + +Thank you for helping us improve the security of ejabberd! diff --git a/_checkouts/configure_deps/rebar.config b/_checkouts/configure_deps/rebar.config new file mode 100644 index 000000000..f618f3e40 --- /dev/null +++ b/_checkouts/configure_deps/rebar.config @@ -0,0 +1,2 @@ +{erl_opts, [debug_info]}. +{deps, []}. \ No newline at end of file diff --git a/_checkouts/configure_deps/src/configure_deps.app.src b/_checkouts/configure_deps/src/configure_deps.app.src new file mode 100644 index 000000000..6ef9e0763 --- /dev/null +++ b/_checkouts/configure_deps/src/configure_deps.app.src @@ -0,0 +1,9 @@ +{application, configure_deps, + [{description, "A rebar3 plugin to explicitly run configure on dependencies"}, + {vsn, "0.0.1"}, + {registered, []}, + {applications, [kernel, stdlib]}, + {env,[]}, + {modules, []}, + {links, []} + ]}. diff --git a/_checkouts/configure_deps/src/configure_deps.erl b/_checkouts/configure_deps/src/configure_deps.erl new file mode 100644 index 000000000..5ec5beb45 --- /dev/null +++ b/_checkouts/configure_deps/src/configure_deps.erl @@ -0,0 +1,8 @@ +-module(configure_deps). + +-export([init/1]). + +-spec init(rebar_state:t()) -> {ok, rebar_state:t()}. +init(State) -> + {ok, State1} = configure_deps_prv:init(State), + {ok, State1}. diff --git a/_checkouts/configure_deps/src/configure_deps_prv.erl b/_checkouts/configure_deps/src/configure_deps_prv.erl new file mode 100644 index 000000000..91f2a3adc --- /dev/null +++ b/_checkouts/configure_deps/src/configure_deps_prv.erl @@ -0,0 +1,54 @@ +-module(configure_deps_prv). + +-export([init/1, do/1, format_error/1]). + +-define(PROVIDER, 'configure-deps'). +-define(DEPS, [install_deps]). + +%% =================================================================== +%% Public API +%% =================================================================== +-spec init(rebar_state:t()) -> {ok, rebar_state:t()}. +init(State) -> + Provider = providers:create([ + {namespace, default}, + {name, ?PROVIDER}, % The 'user friendly' name of the task + {module, ?MODULE}, % The module implementation of the task + {bare, true}, % The task can be run by the user, always true + {deps, ?DEPS}, % The list of dependencies + {example, "rebar3 configure-deps"}, % How to use the plugin + {opts, []}, % list of options understood by the plugin + {short_desc, "Explicitly run ./configure for dependencies"}, + {desc, "A rebar plugin to allow explicitly running ./configure on dependencies. Useful if dependencies might change prior to compilation when configure is run."} + ]), + {ok, rebar_state:add_provider(State, Provider)}. + + +-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. +do(State) -> + Apps = rebar_state:project_apps(State) ++ lists:usort(rebar_state:all_deps(State)), + lists:foreach(fun do_app/1, Apps), + {ok, State}. + +exec_configure({'configure-deps', Cmd}, Dir) -> + rebar_utils:sh(Cmd, [{cd, Dir}, {use_stdout, true}]); +exec_configure(_, Acc) -> Acc. + +parse_pre_hooks({pre_hooks, PreHooks}, Acc) -> + lists:foldl(fun exec_configure/2, Acc, PreHooks); +parse_pre_hooks(_, Acc) -> Acc. + +parse_additions({add, App, Additions}, {MyApp, Dir}) when App == MyApp -> + lists:foldl(fun parse_pre_hooks/2, Dir, Additions), + {MyApp, Dir}; +parse_additions(_, Acc) -> Acc. + +do_app(App) -> + Dir = rebar_app_info:dir(App), + Opts = rebar_app_info:opts(App), + Overrides = rebar_opts:get(Opts, overrides), + lists:foldl(fun parse_additions/2, {binary_to_atom(rebar_app_info:name(App), utf8), Dir}, Overrides). + +-spec format_error(any()) -> iolist(). +format_error(Reason) -> + io_lib:format("~p", [Reason]). diff --git a/asn1/XmppAddr.asn1 b/asn1/XmppAddr.asn1 deleted file mode 100644 index 14f350d3d..000000000 --- a/asn1/XmppAddr.asn1 +++ /dev/null @@ -1,14 +0,0 @@ -XmppAddr { iso(1) identified-organization(3) - dod(6) internet(1) security(5) mechanisms(5) pkix(7) - id-on(8) id-on-xmppAddr(5) } - -DEFINITIONS EXPLICIT TAGS ::= -BEGIN - -id-on-xmppAddr OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) - dod(6) internet(1) security(5) mechanisms(5) pkix(7) - id-on(8) 5 } - -XmppAddr ::= UTF8String - -END diff --git a/config/config.exs b/config/config.exs deleted file mode 100644 index 0d1a3c720..000000000 --- a/config/config.exs +++ /dev/null @@ -1,10 +0,0 @@ -use Mix.Config - -# This is standard path in the context of ejabberd release -config :ejabberd, - file: "config/ejabberd.yml", - log_path: 'log/ejabberd.log' - -# Customize Mnesia directory: -config :mnesia, - dir: 'mnesiadb/' diff --git a/config/ejabberd.exs b/config/ejabberd.exs index 05c2b5d83..296567f1c 100644 --- a/config/ejabberd.exs +++ b/config/ejabberd.exs @@ -4,17 +4,15 @@ defmodule Ejabberd.ConfigFile do def start do [loglevel: 4, log_rotate_size: 10485760, - log_rotate_date: "", log_rotate_count: 1, - log_rate_limit: 100, auth_method: :internal, max_fsm_queue: 1000, language: "en", allow_contrib_modules: true, hosts: ["localhost"], - shaper: shaper, - acl: acl, - access: access] + shaper: shaper(), + acl: acl(), + access: access()] end defp shaper do @@ -61,7 +59,6 @@ defmodule Ejabberd.ConfigFile do @opts [ port: 5280, web_admin: true, - http_poll: true, http_bind: true, captcha: true] end @@ -84,7 +81,7 @@ defmodule Ejabberd.ConfigFile do module :mod_client_state do @opts [ - drop_chat_states: true, + queue_chat_states: true, queue_presence: false] end @@ -94,9 +91,6 @@ defmodule Ejabberd.ConfigFile do module :mod_disco do end - module :mod_irc do - end - module :mod_http_bind do end @@ -135,9 +129,10 @@ defmodule Ejabberd.ConfigFile do module :mod_register do @opts [welcome_message: [ subject: "Welcome!", - body: "Hi.\nWelcome to this XMPP Server", + body: "Hi.\nWelcome to this XMPP server" + ], ip_access: :trusted_network, - access: :register]] + access: :register] end module :mod_roster do diff --git a/config/ejabberd.yml b/config/ejabberd.yml deleted file mode 100644 index 80fc3c622..000000000 --- a/config/ejabberd.yml +++ /dev/null @@ -1,667 +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_poll: 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: - drop_chat_states: true - queue_presence: false - mod_configure: {} # requires mod_adhoc - mod_disco: {} - ## mod_echo: {} - mod_irc: {} - 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 comsumption, but XEP incompliant - ignore_pep_from_offline: true - ## XEP compliant, but increases resource comsumption - ## 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/config/runtime.exs b/config/runtime.exs new file mode 100644 index 000000000..adfc18c06 --- /dev/null +++ b/config/runtime.exs @@ -0,0 +1,15 @@ +import Config + +rootdefault = case System.get_env("RELIVE", "false") do + "true" -> "_build/relive" + "false" -> "" +end + +rootpath = System.get_env("RELEASE_ROOT", rootdefault) +config :ejabberd, + file: Path.join(rootpath, "conf/ejabberd.yml"), + log_path: Path.join(rootpath, "logs/ejabberd.log") +config :mnesia, + dir: Path.join(rootpath, "database/") +config :exsync, + reload_callback: {:ejabberd_admin, :update, []} diff --git a/configure.ac b/configure.ac index f3ff9ae54..b91595dc5 100644 --- a/configure.ac +++ b/configure.ac @@ -1,16 +1,27 @@ # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. -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.1 (Erlang/OTP 17.1)" -REQUIRE_ERLANG_MAX="9.0.0 (No Max)" +AC_PREREQ(2.59) +AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 25.08` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) + +AC_ARG_WITH(min-erlang, + AS_HELP_STRING([--with-min-erlang=version],[set minimal required erlang version, default to OTP25]), +[if test "X$withval" = "X"; then + REQUIRE_ERLANG_MIN="13.0 (Erlang/OTP 25.0)" +else + REQUIRE_ERLANG_MIN="$withval" +fi +], [REQUIRE_ERLANG_MIN="13.0 (Erlang/OTP 25.0)"]) + +REQUIRE_ERLANG_MAX="100.0.0 (No Max)" AC_CONFIG_MACRO_DIR([m4]) # Checks for programs. AC_PROG_MAKE_SET +AC_PROG_AWK AC_PROG_INSTALL +AC_PROG_MKDIR_P AC_PROG_SED if test "x$GCC" = "xyes"; then @@ -19,8 +30,7 @@ fi # Checks Erlang runtime and compiler AC_ARG_WITH(erlang, - AC_HELP_STRING([--with-erlang=dir], - [search for erlang in dir]), + AS_HELP_STRING([--with-erlang=dir],[search for erlang in dir]), [if test "$withval" = "yes" -o "$withval" = "no" -o "X$with_erlang" = "X"; then extra_erl_path="" else @@ -28,25 +38,46 @@ else fi ]) +AC_ARG_WITH(rebar, + AS_HELP_STRING([--with-rebar=bin],[use as build tool the rebar/rebar3/mix binary specified]), +[if test "$withval" = "yes" -o "$withval" = "no" -o "X$with_rebar" = "X"; then + rebar="rebar3" +else + rebar="$with_rebar" +fi +], [rebar="unconfigured"]) + AC_PATH_TOOL(ERL, erl, , [${extra_erl_path}$PATH]) AC_PATH_TOOL(ERLC, erlc, , [${extra_erl_path}$PATH]) AC_PATH_TOOL(EPMD, epmd, , [${extra_erl_path}$PATH]) +AC_PATH_TOOL(REBAR, rebar, , [${extra_erl_path}$PATH]) +AC_PATH_TOOL(REBAR3, rebar3, , [${extra_erl_path}$PATH]) +AC_PATH_TOOL(ELIXIR, elixir, , [${extra_erl_path}$PATH]) +AC_PATH_TOOL(IEX, iex, , [${extra_erl_path}$PATH]) +AC_PATH_TOOL(MIX, mix, , [${extra_erl_path}$PATH]) + +if test "$rebar" = unconfigured; then + if test "x$ELIXIR" = "x" -o "x$IEX" = "x" -o "x$MIX" = "x"; then + if test "x$REBAR3" = "x"; then + rebar="rebar3" + else + rebar=$REBAR3 + fi + else + rebar=$MIX + fi +fi +if test "x$rebar" = "xrebar" -a "x$REBAR" = "x" ; then + rebar="./rebar" +fi +if test "x$rebar" = "xrebar3" -a "x$REBAR3" = "x" ; then + rebar="./rebar3" +fi + AC_ERLANG_NEED_ERL AC_ERLANG_NEED_ERLC -AC_ARG_ENABLE(erlang-version-check, -[AC_HELP_STRING([--enable-erlang-version-check], - [Check Erlang/OTP version @<:@default=yes@:>@])]) -case "$enable_erlang_version_check" in - yes|'') - ERLANG_VERSION_CHECK([$REQUIRE_ERLANG_MIN],[$REQUIRE_ERLANG_MAX]) - ;; - no) - ERLANG_VERSION_CHECK([$REQUIRE_ERLANG_MIN],[$REQUIRE_ERLANG_MAX],[warn]) - ;; -esac - # Checks and sets ERLANG_ROOT_DIR and ERLANG_LIB_DIR variable AC_ERLANG_SUBST_ROOT_DIR # AC_ERLANG_SUBST_LIB_DIR @@ -66,159 +97,191 @@ if test "x$MAKE" = "x"; then fi # Change default prefix -AC_PREFIX_DEFAULT(/) +AC_PREFIX_DEFAULT(/usr/local) -AC_ARG_ENABLE(hipe, -[AC_HELP_STRING([--enable-hipe], [compile natively with HiPE, not recommended (default: no)])], -[case "${enableval}" in - yes) hipe=true ;; - no) hipe=false ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-hipe) ;; -esac],[hipe=false]) - -AC_ARG_ENABLE(roster_gateway_workaround, -[AC_HELP_STRING([--enable-roster-gateway-workaround], [turn on workaround for processing gateway subscriptions (default: no)])], -[case "${enableval}" in - yes) roster_gateway_workaround=true ;; - no) roster_gateway_workaround=false ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-roster-gateway-workaround) ;; -esac],[roster_gateway_workaround=false]) - -AC_ARG_ENABLE(full_xml, -[AC_HELP_STRING([--enable-full-xml], [use XML features in XMPP stream (ex: CDATA) (default: no, requires XML compliant clients)])], -[case "${enableval}" in - yes) full_xml=true ;; - no) full_xml=false ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-full-xml) ;; -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 ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-mssql) ;; -esac],[db_type=generic]) +AC_CONFIG_FILES([Makefile + vars.config]) AC_ARG_ENABLE(all, -[AC_HELP_STRING([--enable-all], [same as --enable-odbc --enable-mysql --enable-pgsql --enable-sqlite --enable-pam --enable-zlib --enable-riak --enable-redis --enable-elixir --enable-iconv --enable-debug --enable-tools (useful for Dialyzer checks, default: no)])], +[AS_HELP_STRING([--enable-all],[same as --enable-odbc --enable-mssql --enable-mysql --enable-pgsql --enable-sqlite --enable-pam --enable-zlib --enable-redis --enable-elixir --enable-stun --enable-sip --enable-debug --enable-lua --enable-tools (useful for Dialyzer checks, default: no)])], [case "${enableval}" in - yes) odbc=true mysql=true pgsql=true sqlite=true pam=true zlib=true riak=true redis=true elixir=true iconv=true debug=true tools=true ;; - no) odbc=false mysql=false pgsql=false sqlite=false pam=false zlib=false riak=false redis=false elixir=false iconv=false debug=false tools=false ;; + yes) odbc=true mssql=true mysql=true pgsql=true sqlite=true pam=true zlib=true redis=true elixir=true stun=true sip=true debug=true lua=true tools=true ;; + no) odbc=false mssql=false mysql=false pgsql=false sqlite=false pam=false zlib=false redis=false elixir=false stun=false sip=false debug=false lua=false tools=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-all) ;; esac],[]) -AC_ARG_ENABLE(tools, -[AC_HELP_STRING([--enable-tools], [build development tools (default: no)])], -[case "${enableval}" in - yes) tools=true ;; - no) tools=false ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-tools) ;; -esac],[if test "x$tools" = "x"; then tools=false; fi]) - -AC_ARG_ENABLE(odbc, -[AC_HELP_STRING([--enable-odbc], [enable pure ODBC support (default: no)])], -[case "${enableval}" in - yes) odbc=true ;; - no) odbc=false ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-odbc) ;; -esac],[if test "x$odbc" = "x"; then odbc=false; fi]) - -AC_ARG_ENABLE(mysql, -[AC_HELP_STRING([--enable-mysql], [enable MySQL support (default: no)])], -[case "${enableval}" in - yes) mysql=true ;; - no) mysql=false ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-mysql) ;; -esac],[if test "x$mysql" = "x"; then mysql=false; fi]) - -AC_ARG_ENABLE(pgsql, -[AC_HELP_STRING([--enable-pgsql], [enable PostgreSQL support (default: no)])], -[case "${enableval}" in - yes) pgsql=true ;; - no) pgsql=false ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-pgsql) ;; -esac],[if test "x$pgsql" = "x"; then pgsql=false; fi]) - -AC_ARG_ENABLE(sqlite, -[AC_HELP_STRING([--enable-sqlite], [enable SQLite support (default: no)])], -[case "${enableval}" in - yes) sqlite=true ;; - no) sqlite=false ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-sqlite) ;; -esac],[if test "x$sqlite" = "x"; then sqlite=false; fi]) - -AC_ARG_ENABLE(pam, -[AC_HELP_STRING([--enable-pam], [enable PAM support (default: no)])], -[case "${enableval}" in - yes) pam=true ;; - no) pam=false ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-pam) ;; -esac],[if test "x$pam" = "x"; then pam=false; fi]) - -AC_ARG_ENABLE(zlib, -[AC_HELP_STRING([--enable-zlib], [enable Stream Compression (XEP-0138) using zlib (default: yes)])], -[case "${enableval}" in - yes) zlib=true ;; - no) zlib=false ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-zlib) ;; -esac],[if test "x$zlib" = "x"; then zlib=true; fi]) - -AC_ARG_ENABLE(riak, -[AC_HELP_STRING([--enable-riak], [enable Riak support (default: no)])], -[case "${enableval}" in - yes) riak=true ;; - no) riak=false ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-riak) ;; -esac],[if test "x$riak" = "x"; then riak=false; fi]) - -AC_ARG_ENABLE(redis, -[AC_HELP_STRING([--enable-redis], [enable Redis support (default: no)])], -[case "${enableval}" in - yes) redis=true ;; - no) redis=false ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-redis) ;; -esac],[if test "x$redis" = "x"; then redis=false; fi]) - -AC_ARG_ENABLE(elixir, -[AC_HELP_STRING([--enable-elixir], [enable Elixir support (default: no)])], -[case "${enableval}" in - yes) elixir=true ;; - no) elixir=false ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-elixir) ;; -esac],[if test "x$elixir" = "x"; then elixir=false; fi]) - -AC_ARG_ENABLE(iconv, -[AC_HELP_STRING([--enable-iconv], [enable iconv support (default: yes)])], -[case "${enableval}" in - yes) iconv=true ;; - no) iconv=false ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-iconv) ;; -esac],[if test "x$iconv" = "x"; then iconv=true; fi]) - AC_ARG_ENABLE(debug, -[AC_HELP_STRING([--enable-debug], [enable debug information (default: yes)])], +[AS_HELP_STRING([--enable-debug],[enable debug information (default: yes)])], [case "${enableval}" in yes) debug=true ;; no) debug=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-debug) ;; esac],[if test "x$debug" = "x"; then debug=true; fi]) +AC_ARG_ENABLE(elixir, +[AS_HELP_STRING([--enable-elixir],[enable Elixir support in Rebar3 (default: no)])], +[case "${enableval}" in + yes) elixir=true ;; + no) elixir=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-elixir) ;; +esac],[if test "x$elixir" = "x"; then elixir=false; fi]) + +AC_ARG_ENABLE(erlang-version-check, +[AS_HELP_STRING([--enable-erlang-version-check],[Check Erlang/OTP version (default: yes)])]) +case "$enable_erlang_version_check" in + yes|'') + ERLANG_VERSION_CHECK([$REQUIRE_ERLANG_MIN],[$REQUIRE_ERLANG_MAX]) + ;; + no) + ERLANG_VERSION_CHECK([$REQUIRE_ERLANG_MIN],[$REQUIRE_ERLANG_MAX],[warn]) + ;; +esac + +AC_ARG_ENABLE(full_xml, +[AS_HELP_STRING([--enable-full-xml],[use XML features in XMPP stream (ex: CDATA) (default: no, requires XML compliant clients)])], +[case "${enableval}" in + yes) full_xml=true ;; + no) full_xml=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-full-xml) ;; +esac],[full_xml=false]) + +ENABLEGROUP="" +AC_ARG_ENABLE(group, + [AS_HELP_STRING([--enable-group[[=GROUP]]], [specify the group of the account defined in --enable-user (default: no)])], + [case "${enableval}" in + yes) ENABLEGROUP=`groups |head -n 1` ;; + no) ENABLEGROUP="" ;; + *) ENABLEGROUP=$enableval + esac], + []) +if test "$ENABLEGROUP" != ""; then + echo "allow this system group to start ejabberd: $ENABLEGROUP" + AC_SUBST([INSTALLGROUP], [$ENABLEGROUP]) +fi + AC_ARG_ENABLE(latest_deps, -[AC_HELP_STRING([--enable-latest-deps], [makes rebar use latest commits for dependences instead of tagged versions (default: no)])], +[AS_HELP_STRING([--enable-latest-deps],[makes rebar use latest commits for dependencies instead of tagged versions (default: no)])], [case "${enableval}" in yes) latest_deps=true ;; no) latest_deps=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-latest-deps) ;; esac],[if test "x$latest_deps" = "x"; then latest_deps=false; fi]) -AC_CONFIG_FILES([Makefile - vars.config - src/ejabberd.app.src]) +AC_ARG_ENABLE(lua, +[AS_HELP_STRING([--enable-lua],[enable Lua support, to import from Prosody (default: no)])], +[case "${enableval}" in + yes) lua=true ;; + no) lua=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-lua) ;; +esac],[if test "x$lua" = "x"; then lua=false; fi]) + +AC_ARG_ENABLE(mssql, +[AS_HELP_STRING([--enable-mssql],[use Microsoft SQL Server database (default: no, requires --enable-odbc)])], +[case "${enableval}" in + yes) mssql=true ;; + no) mssql=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-mssql) ;; +esac],[if test "x$mssql" = "x"; then mssql=false; fi]) + +AC_ARG_ENABLE(mysql, +[AS_HELP_STRING([--enable-mysql],[enable MySQL support (default: no)])], +[case "${enableval}" in + yes) mysql=true ;; + no) mysql=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-mysql) ;; +esac],[if test "x$mysql" = "x"; then mysql=false; fi]) + +AC_ARG_ENABLE(new_sql_schema, +[AS_HELP_STRING([--enable-new-sql-schema],[use new SQL schema by default (default: no)])], +[case "${enableval}" in + yes) new_sql_schema=true ;; + no) new_sql_schema=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-new-sql-schema) ;; +esac],[new_sql_schema=false]) + +AC_ARG_ENABLE(odbc, +[AS_HELP_STRING([--enable-odbc],[enable pure ODBC support (default: no)])], +[case "${enableval}" in + yes) odbc=true ;; + no) odbc=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-odbc) ;; +esac],[if test "x$odbc" = "x"; then odbc=false; fi]) + +AC_ARG_ENABLE(pam, +[AS_HELP_STRING([--enable-pam],[enable PAM support (default: no)])], +[case "${enableval}" in + yes) pam=true ;; + no) pam=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-pam) ;; +esac],[if test "x$pam" = "x"; then pam=false; fi]) + +AC_ARG_ENABLE(pgsql, +[AS_HELP_STRING([--enable-pgsql],[enable PostgreSQL support (default: no)])], +[case "${enableval}" in + yes) pgsql=true ;; + no) pgsql=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-pgsql) ;; +esac],[if test "x$pgsql" = "x"; then pgsql=false; fi]) + +AC_ARG_ENABLE(redis, +[AS_HELP_STRING([--enable-redis],[enable Redis support (default: no)])], +[case "${enableval}" in + yes) redis=true ;; + no) redis=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-redis) ;; +esac],[if test "x$redis" = "x"; then redis=false; fi]) + +AC_ARG_ENABLE(roster_gateway_workaround, +[AS_HELP_STRING([--enable-roster-gateway-workaround],[turn on workaround for processing gateway subscriptions (default: no)])], +[case "${enableval}" in + yes) roster_gateway_workaround=true ;; + no) roster_gateway_workaround=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-roster-gateway-workaround) ;; +esac],[roster_gateway_workaround=false]) + +AC_ARG_ENABLE(sip, +[AS_HELP_STRING([--enable-sip],[enable SIP support (default: no)])], +[case "${enableval}" in + yes) sip=true ;; + no) sip=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-sip) ;; +esac],[if test "x$sip" = "x"; then sip=false; fi]) + +AC_ARG_ENABLE(sqlite, +[AS_HELP_STRING([--enable-sqlite],[enable SQLite support (default: no)])], +[case "${enableval}" in + yes) sqlite=true ;; + no) sqlite=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-sqlite) ;; +esac],[if test "x$sqlite" = "x"; then sqlite=false; fi]) + +AC_ARG_ENABLE(stun, +[AS_HELP_STRING([--enable-stun],[enable STUN/TURN support (default: yes)])], +[case "${enableval}" in + yes) stun=true ;; + no) stun=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-stun) ;; +esac],[if test "x$stun" = "x"; then stun=true; fi]) + +AC_ARG_ENABLE(system_deps, +[AS_HELP_STRING([--enable-system-deps],[makes rebar use locally installed dependencies instead of downloading them (default: no)])], +[case "${enableval}" in + yes) system_deps=true ;; + no) system_deps=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-system-deps) ;; +esac],[if test "x$system_deps" = "x"; then system_deps=false; fi]) + +AC_ARG_ENABLE(tools, +[AS_HELP_STRING([--enable-tools],[include debugging/development tools (default: no)])], +[case "${enableval}" in + yes) tools=true ;; + no) tools=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-tools) ;; +esac],[if test "x$tools" = "x"; then tools=false; fi]) ENABLEUSER="" AC_ARG_ENABLE(user, - [AS_HELP_STRING([--enable-user[[[[=USER]]]]], [allow this system user to start ejabberd (default: no)])], + [AS_HELP_STRING([--enable-user[[=USER]]], [allow this system user to start ejabberd (default: no)])], [case "${enableval}" in yes) ENABLEUSER=`whoami` ;; no) ENABLEUSER="" ;; @@ -230,34 +293,77 @@ if test "$ENABLEUSER" != ""; then AC_SUBST([INSTALLUSER], [$ENABLEUSER]) fi -ERLANG_DEPRECATED_TYPES_CHECK +AC_ARG_ENABLE(zlib, +[AS_HELP_STRING([--enable-zlib],[enable Stream Compression (XEP-0138) using zlib (default: yes)])], +[case "${enableval}" in + yes) zlib=true ;; + no) zlib=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-zlib) ;; +esac],[if test "x$zlib" = "x"; then zlib=true; fi]) -if test "$sqlite" = "true"; then - AX_LIB_SQLITE3([3.6.19]) - if test "x$SQLITE3_VERSION" = "x"; then - AC_MSG_ERROR(SQLite3 library >= 3.6.19 was not found) - fi -fi +case "`uname`" in + "Darwin") + # Darwin (macos) erlang-sqlite is built using amalgamated lib, so no external dependency + ;; + *) + if test "$sqlite" = "true"; then + AX_LIB_SQLITE3([3.6.19]) + if test "x$SQLITE3_VERSION" = "x"; then + AC_MSG_ERROR(SQLite3 library >= 3.6.19 was not found) + fi + fi + ;; +esac + +AC_MSG_RESULT([build tool to use (change using --with-rebar): $rebar]) -AC_SUBST(hipe) AC_SUBST(roster_gateway_workaround) +AC_SUBST(new_sql_schema) AC_SUBST(full_xml) -AC_SUBST(db_type) AC_SUBST(odbc) +AC_SUBST(mssql) AC_SUBST(mysql) AC_SUBST(pgsql) AC_SUBST(sqlite) AC_SUBST(pam) AC_SUBST(zlib) -AC_SUBST(riak) +AC_SUBST(rebar) AC_SUBST(redis) AC_SUBST(elixir) -AC_SUBST(iconv) +AC_SUBST(stun) +AC_SUBST(sip) AC_SUBST(debug) +AC_SUBST(lua) AC_SUBST(tools) AC_SUBST(latest_deps) +AC_SUBST(system_deps) AC_SUBST(CFLAGS) AC_SUBST(CPPFLAGS) AC_SUBST(LDFLAGS) AC_OUTPUT + +AS_CASE([$rebar], + [*rebar3], [ + deps="" + AS_IF([test "x$stun" = "xfalse"], [deps="stun,$deps"]) + AS_IF([test "x$sqlite" = "xfalse"], [deps="sqlite3,$deps"]) + AS_IF([test "x$pgsql" = "xfalse"], [deps="p1_pgsql,$deps"]) + AS_IF([test "x$mysql" = "xfalse"], [deps="p1_mysql,$deps"]) + AS_IF([test "x$zlib" = "xfalse"], [deps="ezlib,$deps"]) + AS_IF([test "x$sip" = "xfalse"], [deps="esip,$deps"]) + AS_IF([test "x$redis" = "xfalse"], [deps="eredis,$deps"]) + AS_IF([test "x$pam" = "xfalse"], [deps="epam,$deps"]) + AS_IF([test "x$deps" = "x"], [], + [AC_MSG_NOTICE([unlocking disabled rebar3 dependencies: $deps]) + $rebar unlock "$deps"]) + deps="" + ERLANG_VERSION=m4_esyscmd([erl -noinput -noshell -eval 'erlang:display(list_to_integer(erlang:system_info(otp_release))), halt().']) + AS_IF([test "$ERLANG_VERSION" -lt "21"], [deps="luerl,$deps"]) + AS_IF([test "$ERLANG_VERSION" -lt "22"], [deps="lager,$deps"]) + AS_IF([test "$ERLANG_VERSION" -le "23"], [deps="jose,$deps"]) + AS_IF([test "$ERLANG_VERSION" -ge "27"], [deps="jiffy,$deps"]) + AS_IF([test "x$deps" = "x"], [], + [AC_MSG_NOTICE([unlocking rebar3 dependencies for old Erlang/OTP: $deps]) + $rebar unlock "$deps"]) + ]) diff --git a/contrib/extract_translations/README b/contrib/extract_translations/README deleted file mode 100644 index 9278dd106..000000000 --- a/contrib/extract_translations/README +++ /dev/null @@ -1,21 +0,0 @@ -extract_translations - auxiliary tool that extracts lines to be translated -from ejabberd source tree. - -Building: - erlc extract_translations.erl - -Invoking 1: - erl -noinput -s extract_translations -extra dirname message_file - - where dirname is the directory "src" in ejabberd's source tree root, - message_file is a file with translated messages (src/msgs/*.msg). - - Result is a list of messages from source files which aren't contained in - message file. - -Invoking 2: - erl -noinput -s extract_translations -extra -unused dirname message_file - - Result is a list of messages from message file which aren't in source - files anymore. - diff --git a/contrib/extract_translations/extract_translations.erl b/contrib/extract_translations/extract_translations.erl deleted file mode 100644 index 70304761a..000000000 --- a/contrib/extract_translations/extract_translations.erl +++ /dev/null @@ -1,307 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : extract_translations.erl -%%% Author : Sergei Golovan -%%% Purpose : Auxiliary tool for interface/messages translators -%%% Created : 23 Apr 2005 by Sergei Golovan -%%% Id : $Id$ -%%%---------------------------------------------------------------------- - --module(extract_translations). --author('sgolovan@nes.ru'). - --export([start/0]). - --define(STATUS_SUCCESS, 0). --define(STATUS_ERROR, 1). --define(STATUS_USAGE, 2). - --include_lib("kernel/include/file.hrl"). - - -start() -> - ets:new(translations, [named_table, public]), - ets:new(translations_obsolete, [named_table, public]), - ets:new(files, [named_table, public]), - ets:new(vars, [named_table, public]), - case init:get_plain_arguments() of - ["-srcmsg2po", Dir, File] -> - print_po_header(File), - Status = process(Dir, File, srcmsg2po), - halt(Status); - ["-unused", Dir, File] -> - Status = process(Dir, File, unused), - halt(Status); - [Dir, File] -> - Status = process(Dir, File, used), - halt(Status); - _ -> - print_usage(), - halt(?STATUS_USAGE) - end. - - -process(Dir, File, Used) -> - case load_file(File) of - {error, Reason} -> - io:format("~s: ~s~n", [File, file:format_error(Reason)]), - ?STATUS_ERROR; - _ -> - FileList = find_src_files(Dir), - lists:foreach( - fun(F) -> - parse_file(Dir, F, Used) - end, FileList), - case Used of - unused -> - ets:foldl(fun({Key, _}, _) -> - io:format("~p~n", [Key]) - end, ok, translations); - srcmsg2po -> - ets:foldl(fun({Key, Trans}, _) -> - print_translation_obsolete(Key, Trans) - end, ok, translations_obsolete); - _ -> - ok - end, - ?STATUS_SUCCESS - end. - -parse_file(Dir, File, Used) -> - ets:delete_all_objects(vars), - case epp:parse_file(File, [Dir, filename:dirname(File) | code:get_path()], []) of - {ok, Forms} -> - lists:foreach( - fun(F) -> - parse_form(Dir, File, F, Used) - end, Forms); - _ -> - ok - end. - -parse_form(Dir, File, Form, Used) -> - case Form of - %%{undefined, Something} -> - %% io:format("Undefined: ~p~n", [Something]); - {call, - _, - {remote, _, {atom, _, translate}, {atom, _, translate}}, - [_, {string, Line, Str}] - } -> - process_string(Dir, File, Line, Str, Used); - {call, - _, - {remote, _, {atom, _, translate}, {atom, _, translate}}, - [_, - {bin,_, - [{bin_element,_, - {string,Line,Str}, - default,default}]}] - } -> - process_string(Dir, File, Line, Str, Used); - {call, - _, - {remote, _, {atom, _, translate}, {atom, _, translate}}, - [_, {var, _, Name}] - } -> - case ets:lookup(vars, Name) of - [{_Name, Value, Line}] -> - process_string(Dir, File, Line, Value, Used); - _ -> - ok - end; - {match, - _, - {var, _, Name}, - {string, Line, Value} - } -> - ets:insert(vars, {Name, Value, Line}); - {match, - _, - {var, _, Name}, - {bin,Line,[{bin_element,_,{string,_,Value},_,_}]} - } -> - ets:insert(vars, {Name, Value, Line}); - L when is_list(L) -> - lists:foreach( - fun(F) -> - parse_form(Dir, File, F, Used) - end, L); - T when is_tuple(T) -> - lists:foreach( - fun(F) -> - parse_form(Dir, File, F, Used) - end, tuple_to_list(T)); - _ -> - ok - end. - -process_string(_Dir, _File, _Line, "", _Used) -> - ok; - -process_string(_Dir, File, Line, Str, Used) -> - case {ets:lookup(translations, Str), Used} of - {[{_Key, _Trans}], unused} -> - ets:delete(translations, Str); - {[{_Key, _Trans}], used} -> - ok; - {[{_Key, Trans}], srcmsg2po} -> - ets:delete(translations_obsolete, Str), - print_translation(File, Line, Str, Trans); - {_, used} -> - case ets:lookup(files, File) of - [{_}] -> - ok; - _ -> - io:format("~n% ~s~n", [File]), - ets:insert(files, {File}) - end, - case Str of - [] -> ok; - _ -> io:format("{~p, \"\"}.~n", [Str]) - end, - ets:insert(translations, {Str, ""}); - {_, srcmsg2po} -> - case ets:lookup(files, File) of - [{_}] -> - ok; - _ -> - ets:insert(files, {File}) - end, - ets:insert(translations, {Str, ""}), - print_translation(File, Line, Str, ""); - _ -> - ok - end. - -load_file(File) -> - case file:consult(File) of - {ok, Terms} -> - lists:foreach( - fun({Orig, Trans}) -> - case Trans of - "" -> - ok; - _ -> - ets:insert(translations, {Orig, Trans}), - ets:insert(translations_obsolete, {Orig, Trans}) - end - end, Terms); - Err -> - Err - end. - -find_src_files(Dir) -> - case file:list_dir(Dir) of - {ok, FileList} -> - recurse_filelist( - lists:map( - fun(F) -> - filename:join(Dir, F) - end, FileList)); - _ -> - [] - end. - -recurse_filelist(FileList) -> - recurse_filelist(FileList, []). - -recurse_filelist([], Acc) -> - lists:reverse(Acc); - -recurse_filelist([H | T], Acc) -> - case file:read_file_info(H) of - {ok, #file_info{type = directory}} -> - recurse_filelist(T, lists:reverse(find_src_files(H)) ++ Acc); - {ok, #file_info{type = regular}} -> - case string:substr(H, string:len(H) - 3) of - ".erl" -> - recurse_filelist(T, [H | Acc]); - ".hrl" -> - recurse_filelist(T, [H | Acc]); - _ -> - recurse_filelist(T, Acc) - end; - _ -> - recurse_filelist(T, Acc) - end. - - -print_usage() -> - io:format( - "Usage: extract_translations [-unused] dir file~n" - "~n" - "Example:~n" - " extract_translations . ./msgs/ru.msg~n" - ). - - -%%% -%%% Gettext -%%% - -print_po_header(File) -> - MsgProps = get_msg_header_props(File), - {Language, [LastT | AddT]} = prepare_props(MsgProps), - print_po_header(Language, LastT, AddT). - -get_msg_header_props(File) -> - {ok, F} = file:open(File, [read]), - Lines = get_msg_header_props(F, []), - file:close(F), - Lines. - -get_msg_header_props(F, Lines) -> - String = io:get_line(F, ""), - case io_lib:fread("% ", String) of - {ok, [], RemString} -> - case io_lib:fread("~s", RemString) of - {ok, [Key], Value} when Value /= "\n" -> - %% The first character in Value is a blankspace: - %% And the last characters are 'slash n' - ValueClean = string:substr(Value, 2, string:len(Value)-2), - get_msg_header_props(F, Lines ++ [{Key, ValueClean}]); - _ -> - get_msg_header_props(F, Lines) - end; - _ -> - Lines - end. - -prepare_props(MsgProps) -> - Language = proplists:get_value("Language:", MsgProps), - Authors = proplists:get_all_values("Author:", MsgProps), - {Language, Authors}. - -print_po_header(Language, LastTranslator, AdditionalTranslatorsList) -> - AdditionalTranslatorsString = build_additional_translators(AdditionalTranslatorsList), - HeaderString = - "msgid \"\"\n" - "msgstr \"\"\n" - ++ "\"X-Language: " ++ Language ++ "\\n\"\n" - "\"Last-Translator: " ++ LastTranslator ++ "\\n\"\n" - ++ AdditionalTranslatorsString ++ - "\"MIME-Version: 1.0\\n\"\n" - "\"Content-Type: text/plain; charset=UTF-8\\n\"\n" - "\"Content-Transfer-Encoding: 8bit\\n\"\n", - io:format("~s~n", [HeaderString]). - -build_additional_translators(List) -> - lists:foldl( - fun(T, Str) -> - Str ++ "\"X-Additional-Translator: " ++ T ++ "\\n\"\n" - end, - "", - List). - -print_translation(File, Line, Str, StrT) -> - StrQ = ejabberd_regexp:greplace(list_to_binary(Str), <<"\\\"">>, <<"\\\\\"">>), - StrTQ = ejabberd_regexp:greplace(list_to_binary(StrT), <<"\\\"">>, <<"\\\\\"">>), - io:format("#: ~s:~p~nmsgid \"~s\"~nmsgstr \"~s\"~n~n", [File, Line, StrQ, StrTQ]). - -print_translation_obsolete(Str, StrT) -> - File = "unknown.erl", - Line = 1, - StrQ = ejabberd_regexp:greplace(Str, "\\\"", "\\\\\""), - StrTQ = ejabberd_regexp:greplace(StrT, "\\\"", "\\\\\""), - io:format("#: ~s:~p~n#~~ msgid \"~s\"~n#~~ msgstr \"~s\"~n~n", [File, Line, StrQ, StrTQ]). - diff --git a/contrib/extract_translations/prepare-translation.sh b/contrib/extract_translations/prepare-translation.sh deleted file mode 100755 index 98c282cc1..000000000 --- a/contrib/extract_translations/prepare-translation.sh +++ /dev/null @@ -1,366 +0,0 @@ -#!/bin/bash - -# Frontend for ejabberd's extract_translations.erl -# by Badlop - -# How to create template files for a new language: -# NEWLANG=zh -# cp msgs/ejabberd.pot msgs/$NEWLANG.po -# echo \{\"\",\"\"\}. > msgs/$NEWLANG.msg -# ../../extract_translations/prepare-translation.sh -updateall - -prepare_dirs () -{ - # Where is Erlang binary - ERL=`which erl` - - EJA_SRC_DIR=$EJA_DIR/src/ - EJA_MSGS_DIR=$EJA_DIR/priv/msgs/ - EXTRACT_DIR=$EJA_DIR/contrib/extract_translations/ - EXTRACT_ERL=$EXTRACT_DIR/extract_translations.erl - EXTRACT_BEAM=$EXTRACT_DIR/extract_translations.beam - - SRC_DIR=$RUN_DIR/src - EBIN_DIR=$RUN_DIR/ebin - MSGS_DIR=$EJA_DIR/priv/msgs - - if !([[ -n $EJA_DIR ]]) - then - echo "ejabberd dir does not exist: $EJA_DIR" - fi - - if !([[ -x $EXTRACT_BEAM ]]) - then - sh -c "cd $EXTRACT_DIR; $ERL -compile $EXTRACT_ERL" - fi -} - -extract_lang () -{ - MSGS_FILE=$1 - MSGS_FILE2=$MSGS_FILE.translate - MSGS_PATH=$MSGS_DIR/$MSGS_FILE - MSGS_PATH2=$MSGS_DIR/$MSGS_FILE2 - - echo -n "Extracting language strings for '$MSGS_FILE':" - - echo -n " new..." - cd $SRC_DIR - $ERL -pa $EXTRACT_DIR -noinput -noshell -s extract_translations -s init stop -extra . $MSGS_PATH >$MSGS_PATH.new - sed -e 's/^% \.\//% /g;' $MSGS_PATH.new > $MSGS_PATH.new2 - mv $MSGS_PATH.new2 $MSGS_PATH.new - - echo -n " old..." - $ERL -pa $EXTRACT_DIR -noinput -noshell -s extract_translations -s init stop -extra -unused . $MSGS_PATH >$MSGS_PATH.unused - find_unused_full $MSGS_FILE $MSGS_FILE.unused - - echo "" >$MSGS_PATH2 - echo " ***** Translation file for ejabberd ***** " >>$MSGS_PATH2 - echo "" >>$MSGS_PATH2 - - echo "" >>$MSGS_PATH2 - echo " *** New strings: Can you please translate them? *** " >>$MSGS_PATH2 - cat $MSGS_PATH.new >>$MSGS_PATH2 - - echo "" >>$MSGS_PATH2 - echo "" >>$MSGS_PATH2 - echo " *** Unused strings: They will be removed automatically *** " >>$MSGS_PATH2 - cat $MSGS_PATH.unused.full >>$MSGS_PATH2 - - echo "" >>$MSGS_PATH2 - echo "" >>$MSGS_PATH2 - echo " *** Already translated strings: you can also modify any of them if you want *** " >>$MSGS_PATH2 - echo "" >>$MSGS_PATH2 - cat $MSGS_PATH.old_cleaned >>$MSGS_PATH2 - - echo " ok" - - rm $MSGS_PATH.new - rm $MSGS_PATH.old_cleaned - rm $MSGS_PATH.unused.full -} - -extract_lang_all () -{ - cd $MSGS_DIR - for i in $( ls *.msg ) ; do - extract_lang $i; - done - - echo -e "File\tMissing\tLanguage\t\tLast translator" - echo -e "----\t-------\t--------\t\t---------------" - cd $MSGS_DIR - for i in $( ls *.msg ) ; do - MISSING=`cat $i.translate | grep "\", \"\"}." | wc -l` - LANGUAGE=`grep "X-Language:" $i.translate | sed 's/% Language: //g'` - LASTAUTH=`grep "Author:" $i.translate | head -n 1 | sed 's/% Author: //g'` - echo -e "$i\t$MISSING\t$LANGUAGE\t$LASTAUTH" - done - - cd $MSGS_DIR - REVISION=`git describe --always` - zip $HOME/ejabberd-langs-$REVISION.zip *.translate; - - rm *.translate -} - -find_unused_full () -{ - DATFILE=$1 - DATFILEI=$1.old_cleaned - DELFILE=$2 - cd msgs - - DATFILE1=$DATFILE.t1 - DATFILE2=$DATFILE.t2 - - DELFILE1=$DELFILE.t1 - DELFILE2=$DELFILE.t2 - DELFILEF=$DATFILE.unused.full - echo "" >$DELFILEF - - grep -v "\\\\" $DELFILE >$DELFILE2 - echo ENDFILEMARK >>$DELFILE2 - cp $DATFILE $DATFILEI - cp $DATFILE $DATFILE2 - - cp $DELFILE2 $DELFILE1 - STRING=`head -1 $DELFILE1` - until [[ $STRING == ENDFILEMARK ]]; do - cp $DELFILE2 $DELFILE1 - cp $DATFILE2 $DATFILE1 - - STRING=`head -1 $DELFILE1` - - cat $DATFILE1 | grep "$STRING" >>$DELFILEF - cat $DATFILE1 | grep -v "$STRING" >$DATFILE2 - cat $DELFILE1 | grep -v "$STRING" >$DELFILE2 - done - - mv $DATFILE2 $DATFILEI - - rm -f $MSGS_PATH.t1 - rm $MSGS_PATH.unused - rm -f $MSGS_PATH.unused.t1 - rm $MSGS_PATH.unused.t2 - - cd .. -} - -extract_lang_srcmsg2po () -{ - LANG=$1 - LANG_CODE=$LANG.$PROJECT - MSGS_PATH=$MSGS_DIR/$LANG_CODE.msg - PO_PATH=$MSGS_DIR/$LANG_CODE.po - - echo $MSGS_PATH - - cd $SRC_DIR - $ERL -pa $EXTRACT_DIR -pa $EBIN_DIR -pa $EJA_SRC_DIR -pa ../include -noinput -noshell -s extract_translations -s init stop -extra -srcmsg2po . $MSGS_PATH >$PO_PATH.1 - sed -e 's/ \[\]$/ \"\"/g;' $PO_PATH.1 > $PO_PATH.2 - msguniq --sort-by-file $PO_PATH.2 --output-file=$PO_PATH - - rm $PO_PATH.* -} - -extract_lang_src2pot () -{ - LANG_CODE=$PROJECT - MSGS_PATH=$MSGS_DIR/$LANG_CODE.msg - POT_PATH=$MSGS_DIR/$LANG_CODE.pot - - echo -n "" >$MSGS_PATH - echo "% Language: Language Name" >>$MSGS_PATH - echo "% Author: Translator name and contact method" >>$MSGS_PATH - echo "" >>$MSGS_PATH - - cd $SRC_DIR - $ERL -pa $EXTRACT_DIR -pa $EBIN_DIR -pa $EJA_SRC_DIR -pa ../include -noinput -noshell -s extract_translations -s init stop -extra -srcmsg2po . $MSGS_PATH >$POT_PATH.1 - sed -e 's/ \[\]$/ \"\"/g;' $POT_PATH.1 > $POT_PATH.2 - - #msguniq --sort-by-file $POT_PATH.2 $EJA_MSGS_DIR --output-file=$POT_PATH - msguniq --sort-by-file $POT_PATH.2 --output-file=$POT_PATH - - rm $POT_PATH.* - rm $MSGS_PATH - - # If the project is a specific module, not the main ejabberd - if [[ $PROJECT != ejabberd ]] ; then - # Remove from project.pot the strings that are already present in the general ejabberd - EJABBERD_MSG_FILE=$EJA_MSGS_DIR/es.po # This is just some file with translated strings - POT_PATH_TEMP=$POT_PATH.temp - msgattrib --set-obsolete --only-file=$EJABBERD_MSG_FILE -o $POT_PATH_TEMP $POT_PATH - mv $POT_PATH_TEMP $POT_PATH - fi -} - -extract_lang_popot2po () -{ - LANG_CODE=$1 - PO_PATH=$MSGS_DIR/$LANG_CODE.po - POT_PATH=$MSGS_DIR/$PROJECT.pot - - msgmerge $PO_PATH $POT_PATH >$PO_PATH.translate 2>/dev/null - mv $PO_PATH.translate $PO_PATH -} - -extract_lang_po2msg () -{ - LANG_CODE=$1 - PO_PATH=$LANG_CODE.po - MS_PATH=$PO_PATH.ms - MSGID_PATH=$PO_PATH.msgid - MSGSTR_PATH=$PO_PATH.msgstr - MSGS_PATH=$LANG_CODE.msg - - cd $MSGS_DIR - - # Check PO has correct ~ - # Let's convert to C format so we can use msgfmt - PO_TEMP=$LANG_CODE.po.temp - cat $PO_PATH | sed 's/%/perc/g' | sed 's/~/%/g' | sed 's/#:.*/#, c-format/g' >$PO_TEMP - msgfmt $PO_TEMP --check-format - result=$? - rm $PO_TEMP - if [ $result -ne 0 ] ; then - exit 1 - fi - - msgattrib $PO_PATH --translated --no-fuzzy --no-obsolete --no-location --no-wrap | grep "^msg" | tail --lines=+3 >$MS_PATH - grep "^msgid" $PO_PATH.ms | sed 's/^msgid //g' >$MSGID_PATH - grep "^msgstr" $PO_PATH.ms | sed 's/^msgstr //g' >$MSGSTR_PATH - echo "%% -*- coding: latin-1 -*-" >$MSGS_PATH - paste $MSGID_PATH $MSGSTR_PATH --delimiter=, | awk '{print "{" $0 "}."}' | sort -g >>$MSGS_PATH - - rm $MS_PATH - rm $MSGID_PATH - rm $MSGSTR_PATH -} - -extract_lang_updateall () -{ - echo "Generating POT" - extract_lang_src2pot - - cd $MSGS_DIR - echo "" - echo -e "File Missing Language Last translator" - echo -e "---- ------- -------- ---------------" - for i in $( ls *.msg ) ; do - LANG_CODE=${i%.msg} - echo -n $LANG_CODE | awk '{printf "%-6s", $1 }' - - # Convert old MSG file to PO - PO=$LANG_CODE.po - [ -f $PO ] || extract_lang_srcmsg2po $LANG_CODE - - extract_lang_popot2po $LANG_CODE - extract_lang_po2msg $LANG_CODE - - MISSING=`msgfmt --statistics $PO 2>&1 | awk '{printf "%5s", $4 }'` - echo -n " $MISSING" - - LANGUAGE=`grep "X-Language:" $PO | sed 's/\"X-Language: //g' | sed 's/\\\\n\"//g' | awk '{printf "%-12s", $1}'` - echo -n " $LANGUAGE" - - LASTAUTH=`grep "Last-Translator" $PO | sed 's/\"Last-Translator: //g' | sed 's/\\\\n\"//g'` - echo " $LASTAUTH" - done - echo "" - rm messages.mo - - cd .. -} - -translation_instructions () -{ - echo "" - echo " A new file has been created for you, with the current, the new and the deprecated strings:" - echo " $MSGS_PATH2" - echo "" - echo " At the end of that file you will find the strings you must update:" - echo " - Untranslated strings are like this: {"March", ""}." - echo " To translate the string, add the text inside the commas. Example:" - echo " {"March", "Marzo"}." - echo " - Old strings that are not used: "Woowoa"" - echo " Search the entire file for those strings and remove them" - echo "" - echo " Once you have translated all the strings and removed all the old ones," - echo " rename the file to overwrite the previous one:" - echo " $MSGS_PATH" -} - -EJA_DIR=`pwd` -RUN_DIR=`pwd` -PROJECT=ejabberd - -while [ $# -ne 0 ] ; do - PARAM=$1 - shift - case $PARAM in - --) break ;; - -project) - PROJECT=$1 - shift - ;; - -ejadir) - EJA_DIR=$1 - shift - ;; - -rundir) - RUN_DIR=$1 - shift - ;; - -lang) - LANGU=$1 - prepare_dirs - extract_lang $LANGU - shift - ;; - -langall) - prepare_dirs - extract_lang_all - ;; - -srcmsg2po) - LANG_CODE=$1 - prepare_dirs - extract_lang_srcmsg2po $LANG_CODE - shift - ;; - -popot2po) - LANG_CODE=$1 - prepare_dirs - extract_lang_popot2po $LANG_CODE - shift - ;; - -src2pot) - prepare_dirs - extract_lang_src2pot - ;; - -po2msg) - LANG_CODE=$1 - prepare_dirs - extract_lang_po2msg $LANG_CODE - shift - ;; - -updateall) - prepare_dirs - extract_lang_updateall - ;; - *) - echo "Options:" - echo " -langall" - echo " -lang LANGUAGE_FILE" - echo " -srcmsg2po LANGUAGE Construct .msg file using source code to PO file" - echo " -src2pot Generate template POT file from source code" - echo " -popot2po LANGUAGE Update PO file with template POT file" - echo " -po2msg LANGUAGE Export PO file to MSG file" - echo " -updateall Generate POT and update all PO" - echo "" - echo "Example:" - echo " ./prepare-translation.sh -lang es.msg" - exit 0 - ;; - esac -done diff --git a/ejabberd.doap b/ejabberd.doap new file mode 100644 index 000000000..bfd3f5636 --- /dev/null +++ b/ejabberd.doap @@ -0,0 +1,849 @@ + + + + ejabberd + XMPP Server with MQTT Broker and SIP Service + Robust, Ubiquitous and Massively Scalable Messaging Platform (XMPP Server, MQTT Broker, SIP Service) + 2002-11-16 + BSD + Linux + macOS + Windows + Erlang + C + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2.9 + 0.5.0 + complete + + + + + + + 2.0 + 0.5.0 + complete + mod_last + + + + + + 1.2 + 16.02 + complete + mod_offline + + + + + + 1.6 + 0.5.0 + complete + mod_privacy + + + + + + 1.4 + 0.1.0 + complete + mod_offline + + + + + + 1.3 + 0.7.5 + complete + mod_offline + + + + + + 2.4 + 0.1.0 + complete + mod_disco + + + + + + 1.1 + 15.04 + complete + mod_multicast + + + + + + 0.6.0 + 0.1.0 + complete + mod_stats + + + + + + 1.25 + 0.5.0 + complete + mod_muc + + + + + + 1.2 + 0.5.0 + complete + mod_pubsub + + + + + + 1.2 + 0.1.0 + complete + mod_private + + + + + + 1.2 + 1.1.0 + complete + mod_adhoc + + + + + + 1.2 + 0.1.0 + complete + mod_vcard + + + + + + 1.3 + 0.1.0 + complete + mod_vcard + + + + + + 1.0 + 2.1.0 + complete + + + + + + + 1.14 + 0.5.0 + partial + mod_pubsub + + + + + + 1.8 + 2.0.0 + complete + mod_proxy65 + + + + + + 2.4 + 0.1.0 + complete + mod_register + + + + + + 2.5 + 17.03 + complete + mod_legacy_auth + + + + + + 1.1.1 + 2.1.0 + complete + + + + + + + 2.1 + 2.1.0 + complete + mod_client_state + + + + + + 1.0 + 0.5.0 + complete + + + + + + + 1.1 + 0.1.0 + complete + mod_version + + + + + + 1.1 + 0.5.0 + complete + + + + + + + 1.6 + 0.1.0 + complete + ejabberd_service + + + + + + 1.5 + 2.1.4 + complete + mod_caps + + + + + + 1.11 + 16.12 + complete + ejabberd_bosh + + + + + + 1.3.0 + 13.10 + partial + mod_configure + + + + + + 2.1 + 1.1.0 + complete + ejabberd_c2s + + + + + + 1.1 + 17.09 + complete + mod_vcard + + + + + + 1.4.0 + 22.05 + complete + mod_host_meta + + + + + + 1.0 + 2.1.0 + complete + mod_disco + + + + + + 1.0 + 2.1.0 + complete + ejabberd_captcha + + + + + + 1.0 + 16.01 + complete + mod_offline + + + + + + 1.2 + 2.0.0 + complete + mod_pubsub + + + + + + 1.0 + 17.12 + complete + + + + + + + 1.2 + 1.1.0 + complete + ejabberd_auth_anonymous + + + + + + 1.1 + 17.03 + complete + + + + + + + 1.0 + 17.03 + complete + mod_s2s_dialback + + + + + + 1.2 + 2.1.7 + complete + mod_blocking + + + + + + 1.5.2 + 14.05 + complete + mod_stream_mgmt + + + + + + 2.0 + 2.1.0 + complete + mod_ping + + + + + + 2.0 + 2.1.0 + complete + mod_time + + + + + + 2.0 + 2.1.0 + complete + mod_offline + + + + + + 1.0 + 1.1.2 + complete + + + + + + + 1.4 + 16.12 + complete + ejabberd_bosh + + + + + + 0.7 + 20.04 + complete + mod_stun_disco + + + + + + 1.1.1 + 17.03 + complete + mod_s2s_dialback + + + + + + 1.1.1 + 2.0.0 + complete + mod_pubsub + + + + + + 1.1 + 2.1.0 + partial + ejabberd_piefxis + + + + + + 1.0 + 2.1.0 + complete + ejabberd_captcha + + + + + + 1.3 + 2.1.0 + complete + mod_roster + + + + + + 0.2 + 2.1.0 + complete + mod_pubsub + + + + + + 1.2 + 0.5.0 + complete + mod_muc + + + + + + 0.2 + 2.1.3 + complete + mod_sic + + + + + + 1.0.1 + 13.06 + complete + mod_carboncopy + + + + + + 1.0.1 + 24.10 + complete + mod_s2s_bidi + + + + + + 0.6.1 + 15.06 + complete + mod_mam + + + + + + 0.3.1 + 21.12 + complete + mod_muc_room, 0.3.1 since 25.xx + + + + + + 0.1 + 19.09 + complete + mod_jidprep + + + + + + 0.2 + 16.01 + complete + mod_mam, mod_muc_log, mod_offline + + + + + + 0.1 + 14.12 + complete + mod_client_state + + + + + + 0.4.1 + 16.09 + complete + mod_delegation + + + + + + 0.4.1 + 24.10 + complete + mod_privilege + + + + + + 0.2 + 17.08 + complete + mod_push + + + + + + 0.5.0 + 15.09 + complete + mod_mam + + + + + + 0.3.0 + 15.10 + complete + mod_http_upload + + + + + + 1.1.0 + 17.09 + complete + + + + + + + 0.14.1 + 16.03 + complete + mod_mix + + + + + + 0.8.3 + 21.12 + complete + node_pep + + + + + + 0.3.0 + 24.02 + complete + + + + + + + 0.4.0 + 24.02 + complete + + + + + + + 0.2.0 + 18.03 + complete + mod_avatar + + + + + + 1.1.3 + 23.10 + complete + mod_private + + + + + + 0.3.0 + 19.02 + complete + mod_mix_pam + + + + + + 1.1.0 + 18.12 + complete + mod_muc_room + + + + + + 0.2.0 + 18.12 + complete + mod_private + + + + + + 0.1.0 + 23.10 + complete + mod_muc_occupantid + + + + + + 0.4.2 + 24.02 + partial + mod_mam, Tombstones not implemented + + + + + + 0.3.0 + 24.06 + complete + mod_mam + + + + + + 0.2.0 + 24.12 + complete + mod_mam + + + + + + 0.4.0 + 24.02 + complete + + + + + + + 0.2.0 + 15.06 + complete + mod_mam + + + + + + 0.4.0 + 24.02 + complete + , 0.4.0 since 25.03 + + + + + + 0.2.0 + 24.10 + complete + mod_scram_upgrade + + + + + + 0.2.0 + 24.12 + complete + mod_auth_fast + + + + + + 0.1.1 + 25.07 + complete + mod_pubsub_serverinfo + + + + + + 0.1.0 + 24.07 + complete + mod_muc + + + + diff --git a/ejabberd.init.template b/ejabberd.init.template index 90d75404f..060d64317 100644 --- a/ejabberd.init.template +++ b/ejabberd.init.template @@ -6,8 +6,8 @@ # Required-Stop: $remote_fs $network $named $time # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 -# Short-Description: Starts ejabberd jabber server -# Description: Starts ejabberd jabber server, an XMPP +# Short-Description: Starts ejabberd XMPP server +# Description: Starts ejabberd XMPP server, an XMPP # compliant server written in Erlang. ### END INIT INFO diff --git a/ejabberd.service.template b/ejabberd.service.template index a70d2254d..902a81cb2 100644 --- a/ejabberd.service.template +++ b/ejabberd.service.template @@ -3,16 +3,19 @@ Description=XMPP Server After=network.target [Service] -Type=forking -User=ejabberd -Group=ejabberd +Type=notify +User=@installuser@ +Group=@installuser@ LimitNOFILE=65536 Restart=on-failure RestartSec=5 -ExecStart=/bin/sh -c '@ctlscriptpath@/ejabberdctl start && @ctlscriptpath@/ejabberdctl started' +ExecStart=@ctlscriptpath@/ejabberdctl foreground ExecStop=/bin/sh -c '@ctlscriptpath@/ejabberdctl stop && @ctlscriptpath@/ejabberdctl stopped' +ExecReload=@ctlscriptpath@/ejabberdctl reload_config +NotifyAccess=all PrivateDevices=true -ProtectSystem=full +AmbientCapabilities=CAP_NET_BIND_SERVICE +TimeoutSec=300 [Install] WantedBy=multi-user.target diff --git a/ejabberd.yml.example b/ejabberd.yml.example index dae839fdc..b9df7799b 100644 --- a/ejabberd.yml.example +++ b/ejabberd.yml.example @@ -1,694 +1,251 @@ ### -###' ejabberd configuration file +### ejabberd configuration file ### +### The parameters used in this configuration file are explained at +### +### https://docs.ejabberd.im/admin/configuration ### - -### 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. +### ******************************************************* +### ******* !!! WARNING !!! ******* +### ******* YAML IS INDENTATION SENSITIVE ******* +### ******* MAKE SURE YOU INDENT SECTIONS CORRECTLY ******* +### ******************************************************* ### 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" - -###. =============== -###' NODE PARAMETERS - -## -## net_ticktime: Specifies net_kernel tick time in seconds. This options must have -## identical value on all nodes, and in most cases shouldn't be changed at all from -## default value. -## -## net_ticktime: 60 - -###. ================ -###' SERVED HOSTNAMES - -## -## hosts: Domains served by ejabberd. -## You can define one or several, for example: -## hosts: -## - "example.net" -## - "example.com" -## - "example.org" -## hosts: - - "localhost" + - 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 +loglevel: info -###. =============== -###' LISTENING PORTS +## If you already have certificates, list them here +# certfiles: +# - /etc/letsencrypt/live/domain.tld/fullchain.pem +# - /etc/letsencrypt/live/domain.tld/privkey.pem -## -## listen: The ports ejabberd will listen on, which service each is handled -## by and what options to start it with. -## -listen: - - +listen: + - port: 5222 + ip: "::" 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 + max_stanza_size: 262144 shaper: c2s_shaper access: c2s - - + starttls_required: true + - + port: 5223 + ip: "::" + module: ejabberd_c2s + max_stanza_size: 262144 + shaper: c2s_shaper + access: c2s + tls: true + - port: 5269 + ip: "::" module: ejabberd_s2s_in - ## - ## ejabberd_service: Interact with external components (transports, ...) - ## - ## - - ## port: 8888 - ## module: ejabberd_service - ## access: all - ## shaper_rule: fast - ## ip: "127.0.0.1" - ## privilege_access: - ## roster: "both" - ## message: "outgoing" - ## presence: "roster" - ## delegations: - ## "urn:xmpp:mam:1": - ## filtering: ["node"] - ## "http://jabber.org/protocol/pubsub": - ## filtering: [] - ## 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 - ## access_commands: {} - - + max_stanza_size: 524288 + shaper: s2s_shaper + - + port: 5443 + ip: "::" + module: ejabberd_http + tls: true + request_handlers: + /admin: ejabberd_web_admin + /api: mod_http_api + /bosh: mod_bosh + /captcha: ejabberd_captcha + /upload: mod_http_upload + /ws: ejabberd_http_ws + - port: 5280 + ip: "::" module: ejabberd_http request_handlers: - "/websocket": ejabberd_http_ws - ## "/pub/archive": mod_http_fileserver - web_admin: true - http_bind: true - ## register: true - captcha: true + /admin: ejabberd_web_admin + /.well-known/acme-challenge: ejabberd_acme + - + port: 5478 + ip: "::" + transport: udp + module: ejabberd_stun + use_turn: true + ## The server's public IPv4 address: + # turn_ipv4_address: "203.0.113.3" + ## The server's public IPv6 address: + # turn_ipv6_address: "2001:db8::3" + - + port: 1883 + ip: "::" + module: mod_mqtt + backlog: 1000 -###. ================== -###' S2S GLOBAL OPTIONS +s2s_use_starttls: optional -## -## 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 SQL -## Remember to setup a database in the next section. -## -## auth_method: sql - -## -## 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: -## -## sql_type: mysql -## sql_server: "server" -## sql_database: "database" -## sql_username: "username" -## sql_password: "password" -## -## If you want to specify the port: -## sql_port: 1234 - -## -## PostgreSQL server: -## -## sql_type: pgsql -## sql_server: "server" -## sql_database: "database" -## sql_username: "username" -## sql_password: "password" -## -## If you want to specify the port: -## sql_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 - -## -## SQLite: -## -## sql_type: sqlite -## sql_database: "/path/to/database.db" - -## -## ODBC compatible or MSSQL server: -## -## sql_type: odbc -## sql_server: "DSN=ejabberd;UID=ejabberd;PWD=ejabberd" - -## -## Number of connections to open to the database for each virtual host -## -## sql_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 -## -## sql_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: + 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" + - 127.0.0.0/8 + - ::1/128 - ## - ## Bad XMPP servers - ## - ## bad_servers: - ## server: - ## - "xmpp.zombie.org" - ## - "xmpp.spam.com" +access_rules: + local: + allow: local + c2s: + deny: blocked + allow: all + announce: + allow: admin + configure: + allow: admin + muc_create: + allow: local + pubsub_createnode: + allow: local + trusted_network: + allow: loopback -## -## Define specific ACLs in a virtual host. -## -## host_config: -## "localhost": -## acl: -## admin: -## user: -## - "bob-local@localhost" +api_permissions: + "console commands": + from: ejabberd_ctl + who: all + what: "*" + "webadmin commands": + from: ejabberd_web_admin + who: admin + what: "*" + "adhoc commands": + from: mod_adhoc_api + who: admin + what: "*" + "http access": + from: mod_http_api + who: + access: + allow: + - acl: loopback + - acl: admin + oauth: + scope: "ejabberd:admin" + access: + allow: + - acl: loopback + - acl: admin + what: + - "*" + - "!stop" + - "!start" + "public commands": + who: + ip: 127.0.0.1/8 + what: + - status + - connected_users_number -###. ============ -###' SHAPER RULES +shaper: + normal: + rate: 3000 + burst_size: 20000 + fast: 100000 shaper_rules: - ## Maximum number of simultaneous sessions allowed for a single user: max_user_sessions: 10 - ## Maximum number of offline messages that users can have: max_user_offline_messages: - - 5000: admin - - 100 - ## For C2S connections, all users except admins use the "normal" shaper + 5000: admin + 100: all c2s_shaper: - - none: admin - - normal - ## All S2S connections use the "fast" shaper + none: admin + normal: all s2s_shaper: fast -###. ============ -###' ACCESS RULES -access_rules: - ## This rule allows access only for local users: - local: - - allow: local - ## Only non-blocked users can use c2s connections: - c2s: - - deny: blocked - - allow - ## Only admins can send announcement messages: - announce: - - allow: admin - ## Only admins can use the configuration interface: - configure: - - allow: admin - ## Only accounts of the local ejabberd server can create rooms: - muc_create: - - allow: local - ## Only accounts on the local ejabberd server can create Pubsub nodes: - pubsub_createnode: - - allow: local - ## In-band registration allows registration of any possible username. - ## To disable in-band registration, replace 'allow' with 'deny'. - register: - - allow - ## Only allow to register from localhost - trusted_network: - - allow: loopback - ## Do not establish S2S connections with bad servers - ## s2s: - ## - deny: - ## - ip: "XXX.XXX.XXX.XXX/32" - ## - deny: - ## - ip: "XXX.XXX.XXX.XXX/32" - ## - 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: -## - allow: admin -## - deny -## register: -## - 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: +modules: mod_adhoc: {} - ## mod_admin_extra: {} - mod_announce: # recommends mod_adhoc + mod_adhoc_api: {} + mod_admin_extra: {} + mod_announce: access: announce - mod_blocking: {} # requires mod_privacy + mod_avatar: {} + mod_blocking: {} + mod_bosh: {} mod_caps: {} mod_carboncopy: {} mod_client_state: {} - mod_configure: {} # requires mod_adhoc - ##mod_delegation: {} # for xep0356 + mod_configure: {} mod_disco: {} - ## mod_echo: {} - mod_irc: {} - mod_http_bind: {} - ## mod_http_fileserver: - ## docroot: "/var/www" - ## accesslog: "/var/log/ejabberd/access.log" + mod_fail2ban: {} + mod_http_api: {} + mod_http_upload: + put_url: https://@HOST_URL_ENCODE@:5443/upload + custom_headers: + "Access-Control-Allow-Origin": "https://@HOST@" + "Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS" + "Access-Control-Allow-Headers": "Content-Type" mod_last: {} - mod_muc: - ## host: "conference.@HOST@" + mod_mam: + ## Mnesia is limited to 2GB, better to use an SQL backend + ## For small servers SQLite is a good fit and is very easy + ## to configure. Uncomment this when you have SQL configured: + ## db_type: sql + assume_mam_usage: true + default: always + mod_mqtt: {} + mod_muc: access: - allow access_admin: - allow: admin access_create: muc_create access_persistent: muc_create - ## mod_muc_log: {} - ## mod_multicast: {} - mod_offline: + access_mam: + - allow + default_room_options: + mam: true + mod_muc_admin: {} + mod_muc_occupantid: {} + 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: + mod_proxy65: + access: local + max_connections: 5 + mod_pubsub: access_createnode: pubsub_createnode - ## reduces resource comsumption, but XEP incompliant - ignore_pep_from_offline: true - ## XEP compliant, but increases resource comsumption - ## 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: {} + plugins: + - flat + - pep + force_node_config: + ## Avoid buggy clients to make their bookmarks public + storage:bookmarks: + access_model: whitelist + mod_push: {} + mod_push_keepalive: {} + mod_register: + ## Only accept registration requests from the "trusted" + ## network (see access_rules section above). + ## Think twice before enabling registration from any + ## address. See the Jabber SPAM Manifesto for details: + ## https://github.com/ge0rg/jabber-spam-fighting-manifesto + ip_access: trusted_network + mod_roster: + versioning: true + mod_s2s_bidi: {} + mod_s2s_dialback: {} mod_shared_roster: {} - mod_stats: {} - mod_time: {} - mod_vcard: - search: false - mod_version: {} + mod_stream_mgmt: + resend_on_timeout: if_offline + mod_stun_disco: {} + mod_vcard: {} + mod_vcard_xupdate: {} + mod_version: + show_os: false -## -## 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 foldmarker=###',###. foldmethod=marker: +### vim: set filetype=yaml tabstop=8 diff --git a/ejabberdctl.cfg.example b/ejabberdctl.cfg.example index d121aa824..88f99cd78 100644 --- a/ejabberdctl.cfg.example +++ b/ejabberdctl.cfg.example @@ -12,36 +12,22 @@ # #POLL=true -#. -#' SMP: SMP support ([enable|auto|disable]) -# -# Explanation in Erlang/OTP documentation: -# enable: starts the Erlang runtime system with SMP support enabled. -# This may fail if no runtime system with SMP support is available. -# auto: starts the Erlang runtime system with SMP support enabled if it -# is available and more than one logical processor are detected. -# disable: starts a runtime system without SMP support. -# -# Default: auto -# -#SMP=auto - #. #' ERL_MAX_PORTS: Maximum number of simultaneously open Erlang ports # # ejabberd consumes two or three ports for every connection, either -# from a client or from another Jabber server. So take this into +# from a client or from another XMPP server. So take this into # account when setting this limit. # -# Default: 32000 +# Default: 65536 (or 8196 on Windows) # Maximum: 268435456 # -#ERL_MAX_PORTS=32000 +#ERL_MAX_PORTS=65536 #. #' FIREWALL_WINDOW: Range of allowed ports to pass through a firewall # -# If Ejabberd is configured to run in cluster, and a firewall is blocking ports, +# If ejabberd is configured to run in cluster, and a firewall is blocking ports, # it's possible to make Erlang use a defined range of port (instead of dynamic # ports) for node communication. # @@ -61,12 +47,28 @@ #INET_DIST_INTERFACE=127.0.0.1 #. -#' ERL_EPMD_ADDRESS: IP addresses where epmd listens for connections +#' ERL_DIST_PORT: Port number for Erlang distribution # -# IMPORTANT: This option works only in Erlang/OTP R14B03 and newer. +# For Erlang distribution, clustering and ejabberdctl usage, the +# Erlang VM listens in a random TCP port number, and the Erlang Port +# Mapper Daemon (EPMD) is spawned and used to determine this port +# number. +# +# ERL_DIST_PORT can define this port number. In that case, EPMD is +# not spawned during ejabberd startup, and ERL_EPMD_ADDRESS is +# ignored. ERL_DIST_PORT must be set to the same port number during +# ejabberd startup and when calling ejabberdctl. This feature +# requires at least Erlang/OTP 23.1. +# +# Default: not defined +# +#ERL_DIST_PORT=5210 + +#. +#' ERL_EPMD_ADDRESS: IP addresses where EPMD listens for connections # # This environment variable may be set to a comma-separated -# list of IP addresses, in which case the epmd daemon +# list of IP addresses, in which case the EPMD daemon # will listen only on the specified address(es) and on the # loopback address (which is implicitly added to the list if it # has not been specified). The default behaviour is to listen on @@ -85,10 +87,10 @@ # Erlang, and therefore not related to the operating system processes, you do # not have to worry about allowing a huge number of them. # -# Default: 250000 +# Default: 262144 # Maximum: 268435456 # -#ERL_PROCESSES=250000 +#ERL_PROCESSES=262144 #. #' ERL_MAX_ETS_TABLES: Maximum number of ETS and Mnesia tables @@ -99,17 +101,18 @@ # You can safely increase this limit when starting ejabberd. It impacts memory # consumption but the difference will be quite small. # -# Default: 1400 +# Default: 2053 # -#ERL_MAX_ETS_TABLES=1400 +#ERL_MAX_ETS_TABLES=2053 #. #' ERL_OPTIONS: Additional Erlang options # -# The next variable allows to specify additional options passed to erlang while -# starting ejabberd. Some useful options are -noshell, -detached, -heart. When -# ejabberd is started from an init.d script options -noshell and -detached are -# added implicitly. See erl(1) for more info. +# The next variable allows to specify additional options passed to +# all commands using erlang interpreter. This applies to starting +# ejabberd server itself but also auxiliary commands like for example +# starting debug shell. See erl(1) for list of commands that can be +# used here. # # It might be useful to add "-pa /usr/local/lib/ejabberd/ebin" if you # want to add local modules in this path. @@ -118,6 +121,20 @@ # #ERL_OPTIONS="" +#. +#' EJABBERD_OPTS: Additional Erlang options to start ejabberd +# +# The next variable allows to specify additional options passed to erlang while +# starting ejabberd. Some useful options are -noshell, -detached, -heart. When +# ejabberd is started from an init.d script options -noshell and -detached are +# added implicitly. See erl(1) for more info. +# +# For example you can use value "-heart -env HEART_BEAT_TIMEOUT 120 -env ERL_CRASH_DUMP_SECONDS 60" +# +# Default: "" +# +#EJABBERD_OPTS="" + #. #' ERLANG_NODE: Erlang node name # @@ -181,6 +198,17 @@ # #CONTRIB_MODULES_CONF_DIR=/etc/ejabberd/modules +#. +#' CTL_OVER_HTTP: Path to ejabberdctl HTTP listener socket +# +# To speedup ejabberdctl execution time for ejabberd commands, +# you can setup an ejabberd_http listener with ejabberd_ctl handling requests, +# listening in a unix domain socket. +# +# Default: disabled +# +#CTL_OVER_HTTP=sockets/ctl_over_http.sock + #. #' # vim: foldmarker=#',#. foldmethod=marker: diff --git a/ejabberdctl.template b/ejabberdctl.template index 5b34ebee4..0d124bead 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -1,265 +1,151 @@ -#!/bin/bash +#!/bin/sh # define default configuration POLL=true -SMP=auto ERL_MAX_PORTS=32000 ERL_PROCESSES=250000 ERL_MAX_ETS_TABLES=1400 FIREWALL_WINDOW="" +INET_DIST_INTERFACE="" ERLANG_NODE=ejabberd@localhost # define default environment variables -SCRIPT_DIR=`cd ${0%/*} && pwd` -ERL={{erl}} -IEX={{bindir}}/iex -EPMD={{epmd}} -INSTALLUSER={{installuser}} -ERL_LIBS={{libdir}} +[ -z "$SCRIPT" ] && SCRIPT=$0 +SCRIPT_DIR="$(cd "$(dirname "$SCRIPT")" && pwd -P)" +# shellcheck disable=SC2034 +ERTS_VSN="{{erts_vsn}}" +ERL="{{erl}}" +EPMD="{{epmd}}" +IEX="{{iexpath}}" +INSTALLUSER="{{installuser}}" -# check the proper system user is used if defined -if [ "$INSTALLUSER" != "" ] ; then - EXEC_CMD="false" - for GID in `id -G`; do - if [ $GID -eq 0 ] ; then - INSTALLUSER_HOME=$(getent passwd "$INSTALLUSER" | cut -d: -f6) - if [ -n "$INSTALLUSER_HOME" ] && [ ! -d "$INSTALLUSER_HOME" ] ; then - mkdir -p "$INSTALLUSER_HOME" - chown "$INSTALLUSER" "$INSTALLUSER_HOME" - fi - EXEC_CMD="su $INSTALLUSER -c" +# check the proper system user is used +case $(id -un) in + "$INSTALLUSER") + EXEC_CMD="as_current_user" + ;; + root) + if [ -n "$INSTALLUSER" ] ; then + EXEC_CMD="as_install_user" + else + EXEC_CMD="as_current_user" + echo "WARNING: It is not recommended to run ejabberd as root" >&2 fi - done - if [ `id -g` -eq `id -g $INSTALLUSER` ] ; then - EXEC_CMD="bash -c" - fi - if [ "$EXEC_CMD" = "false" ] ; then - echo "This command can only be run by root or the user $INSTALLUSER" >&2 - exit 4 - fi -else - EXEC_CMD="bash -c" -fi + ;; + *) + if [ -n "$INSTALLUSER" ] ; then + echo "ERROR: This command can only be run by root or the user $INSTALLUSER" >&2 + exit 7 + else + EXEC_CMD="as_current_user" + fi + ;; +esac # parse command line parameters -declare -a ARGS=() -while [ $# -ne 0 ] ; do - PARAM="$1" - shift - case $PARAM in - --) break ;; - --no-timeout) EJABBERD_NO_TIMEOUT="--no-timeout" ;; - --node) ERLANG_NODE_ARG=$1 ; shift ;; - --config-dir) ETC_DIR="$1" ; shift ;; - --config) EJABBERD_CONFIG_PATH="$1" ; shift ;; - --ctl-config) EJABBERDCTL_CONFIG_PATH="$1" ; shift ;; - --logs) LOGS_DIR="$1" ; shift ;; - --spool) SPOOL_DIR="$1" ; shift ;; - *) ARGS=("${ARGS[@]}" "$PARAM") ;; +while [ $# -gt 0 ]; do + case $1 in + -n|--node) ERLANG_NODE_ARG=$2; shift 2;; + -s|--spool) SPOOL_DIR=$2; shift 2;; + -l|--logs) LOGS_DIR=$2; shift 2;; + -f|--config) EJABBERD_CONFIG_PATH=$2; shift 2;; + -c|--ctl-config) EJABBERDCTL_CONFIG_PATH=$2; shift 2;; + -d|--config-dir) CONFIG_DIR=$2; shift 2;; + -t|--no-timeout) NO_TIMEOUT="--no-timeout"; shift;; + *) break;; esac done -# Define ejabberd variable if they have not been defined from the command line -if [ "$ETC_DIR" = "" ] ; then - ETC_DIR={{sysconfdir}}/ejabberd -fi -if [ "$EJABBERDCTL_CONFIG_PATH" = "" ] ; then - EJABBERDCTL_CONFIG_PATH=$ETC_DIR/ejabberdctl.cfg -fi -if [ -f "$EJABBERDCTL_CONFIG_PATH" ] ; then - . "$EJABBERDCTL_CONFIG_PATH" -fi -if [ "$EJABBERD_CONFIG_PATH" = "" ] ; then - EJABBERD_CONFIG_PATH=$ETC_DIR/ejabberd.yml -fi -if [ "$LOGS_DIR" = "" ] ; then - LOGS_DIR={{localstatedir}}/log/ejabberd -fi -if [ "$SPOOL_DIR" = "" ] ; then - SPOOL_DIR={{localstatedir}}/lib/ejabberd -fi -if [ "$EJABBERD_DOC_PATH" = "" ] ; then - EJABBERD_DOC_PATH={{docdir}} -fi -if [ "$ERLANG_NODE_ARG" != "" ] ; then - ERLANG_NODE=$ERLANG_NODE_ARG -fi -if [ "{{release}}" != "true" -a "$EJABBERD_BIN_PATH" = "" ] ; then - EJABBERD_BIN_PATH={{libdir}}/ejabberd/priv/bin -fi -EJABBERD_LOG_PATH=$LOGS_DIR/ejabberd.log -DATETIME=`date "+%Y%m%d-%H%M%S"` -ERL_CRASH_DUMP=$LOGS_DIR/erl_crash_$DATETIME.dump -ERL_INETRC=$ETC_DIR/inetrc +# define ejabberd variables if not already defined from the command line +: "${CONFIG_DIR:="{{config_dir}}"}" +: "${LOGS_DIR:="{{logs_dir}}"}" +: "${EJABBERD_CONFIG_PATH:="$CONFIG_DIR/ejabberd.yml"}" +: "${EJABBERDCTL_CONFIG_PATH:="$CONFIG_DIR/ejabberdctl.cfg"}" +# Allows passing extra Erlang command-line arguments in vm.args file +: "${VMARGS:="$CONFIG_DIR/vm.args"}" +# shellcheck source=ejabberdctl.cfg.example +[ -f "$EJABBERDCTL_CONFIG_PATH" ] && . "$EJABBERDCTL_CONFIG_PATH" +[ -n "$ERLANG_NODE_ARG" ] && ERLANG_NODE="$ERLANG_NODE_ARG" +[ "$ERLANG_NODE" = "${ERLANG_NODE%@*}" ] && ERLANG_NODE="$ERLANG_NODE@$(hostname -s)" +[ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && S="-s" +: "${SPOOL_DIR:="{{spool_dir}}"}" +: "${EJABBERD_LOG_PATH:="$LOGS_DIR/ejabberd.log"}" + +# backward support for old mnesia spool dir path +: "${SPOOL_DIR_OLD:="$SPOOL_DIR/$ERLANG_NODE"}" +[ -r "$SPOOL_DIR_OLD/schema.DAT" ] && [ ! -r "$SPOOL_DIR/schema.DAT" ] && SPOOL_DIR="$SPOOL_DIR_OLD" -# define mnesia options -MNESIA_OPTS="-mnesia dir \"\\\"$SPOOL_DIR\\\"\" $MNESIA_OPTIONS" # define erl parameters -ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS" -KERNEL_OPTS="" -if [ "$FIREWALL_WINDOW" != "" ] ; then - KERNEL_OPTS="${KERNEL_OPTS} -kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}" +ERLANG_OPTS="+K $POLL +P $ERL_PROCESSES $ERL_OPTIONS" +if [ -n "$FIREWALL_WINDOW" ] ; then + ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}" fi -if [ "$INET_DIST_INTERFACE" != "" ] ; then - INET_DIST_INTERFACE2="$(echo $INET_DIST_INTERFACE | sed 's/\./,/g')" - if [ "$INET_DIST_INTERFACE" != "$INET_DIST_INTERFACE2" ] ; then - INET_DIST_INTERFACE2="{$INET_DIST_INTERFACE2}" +if [ -n "$INET_DIST_INTERFACE" ] ; then + INET_DIST_INTERFACE2=$("$ERL" $ERLANG_OPTS -noshell -eval 'case inet:parse_address("'$INET_DIST_INTERFACE'") of {ok,IP} -> io:format("~p",[IP]); _ -> ok end.' -s erlang halt) + if [ -n "$INET_DIST_INTERFACE2" ] ; then + if [ "$(echo "$INET_DIST_INTERFACE2" | grep -o "," | wc -l)" -eq 7 ] ; then + INET_DIST_INTERFACE2="$INET_DIST_INTERFACE2 -proto_dist inet6_tcp" + fi + ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_use_interface $INET_DIST_INTERFACE2" fi - KERNEL_OPTS="${KERNEL_OPTS} -kernel inet_dist_use_interface \"${INET_DIST_INTERFACE2}\"" fi -if [ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] ; then - NAME="-sname" -else - NAME="-name" -fi -IEXNAME="-$NAME" +[ -n "$ERL_DIST_PORT" ] && ERLANG_OPTS="$ERLANG_OPTS -erl_epmd_port $ERL_DIST_PORT -start_epmd false" +# if vm.args file exists in config directory, pass it to Erlang VM +[ -f "$VMARGS" ] && ERLANG_OPTS="$ERLANG_OPTS -args_file $VMARGS" +ERL_LIBS='{{libdir}}' +ERL_CRASH_DUMP="$LOGS_DIR"/erl_crash_$(date "+%Y%m%d-%H%M%S").dump +ERL_INETRC="$CONFIG_DIR"/inetrc -# define ejabberd environment parameters -if [ "$EJABBERD_CONFIG_PATH" != "${EJABBERD_CONFIG_PATH%.yml}" ] ; then - rate=$(sed '/^[ ]*log_rate_limit/!d;s/.*://;s/ *//' $EJABBERD_CONFIG_PATH) - rotate=$(sed '/^[ ]*log_rotate_size/!d;s/.*://;s/ *//' $EJABBERD_CONFIG_PATH) - count=$(sed '/^[ ]*log_rotate_count/!d;s/.*://;s/ *//' $EJABBERD_CONFIG_PATH) - date=$(sed '/^[ ]*log_rotate_date/!d;s/.*://;s/ *//' $EJABBERD_CONFIG_PATH) -else - rate=$(sed '/^[ ]*log_rate_limit/!d;s/.*,//;s/ *//;s/}\.//' $EJABBERD_CONFIG_PATH) - rotate=$(sed '/^[ ]*log_rotate_size/!d;s/.*,//;s/ *//;s/}\.//' $EJABBERD_CONFIG_PATH) - count=$(sed '/^[ ]*log_rotate_count/!d;s/.*,//;s/ *//;s/}\.//' $EJABBERD_CONFIG_PATH) - date=$(sed '/^[ ]*log_rotate_date/!d;s/.*,//;s/ *//;s/}\.//' $EJABBERD_CONFIG_PATH) -fi -[ -z "$rate" ] || EJABBERD_OPTS="log_rate_limit $rate" -[ -z "$rotate" ] || EJABBERD_OPTS="${EJABBERD_OPTS} log_rotate_size $rotate" -[ -z "$count" ] || EJABBERD_OPTS="${EJABBERD_OPTS} log_rotate_count $count" -[ -z "$date" ] || EJABBERD_OPTS="${EJABBERD_OPTS} log_rotate_date '$date'" -[ -z "$EJABBERD_OPTS" ] || EJABBERD_OPTS="-ejabberd ${EJABBERD_OPTS}" - -[ -d "$SPOOL_DIR" ] || $EXEC_CMD "mkdir -p $SPOOL_DIR" -cd "$SPOOL_DIR" +# define ejabberd parameters +EJABBERD_OPTS="\ +$(sed '/^log_rotate_size/!d;s/:[ \t]*\([0-9]\{1,\}\).*/ \1/;s/:[ \t]*\(infinity\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ +$(sed '/^log_rotate_count/!d;s/:[ \t]*\([0-9]*\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ +$(sed '/^log_burst_limit_count/!d;s/:[ \t]*\([0-9]*\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ +$(sed '/^log_burst_limit_window_time/!d;s/:[ \t]*\([0-9]*[a-z]*\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ +$EJABBERD_OPTS" +[ -n "$EJABBERD_OPTS" ] && EJABBERD_OPTS="-ejabberd $EJABBERD_OPTS" +EJABBERD_OPTS="-mnesia dir \"$SPOOL_DIR\" $MNESIA_OPTIONS $EJABBERD_OPTS -s ejabberd" # export global variables export EJABBERD_CONFIG_PATH export EJABBERD_LOG_PATH -export EJABBERD_BIN_PATH -export EJABBERD_DOC_PATH export EJABBERD_PID_PATH export ERL_CRASH_DUMP export ERL_EPMD_ADDRESS +export ERL_DIST_PORT export ERL_INETRC export ERL_MAX_PORTS export ERL_MAX_ETS_TABLES export CONTRIB_MODULES_PATH export CONTRIB_MODULES_CONF_DIR export ERL_LIBS +export SCRIPT_DIR -shell_escape_str() +set_dist_client() { - if test $# -eq 0; then - printf '"" ' - else - shell_escape "$@" - fi + [ -n "$ERL_DIST_PORT" ] && ERLANG_OPTS="$ERLANG_OPTS -dist_listen false" } -shell_escape() +# run command either directly or via su $INSTALLUSER +exec_cmd() { - local RES=() - for i in "$@"; do - if test -z "$i"; then - printf '"" ' - else - printf '%q ' "$i" - fi - done -} - -# start server -start() -{ - check_start - CMD="`shell_escape \"$ERL\" \"$NAME\" \"$ERLANG_NODE\"` \ - -noinput -detached \ - $MNESIA_OPTS \ - $KERNEL_OPTS \ - $EJABBERD_OPTS \ - -s ejabberd \ - $ERLANG_OPTS \ - `shell_escape \"${ARGS[@]}\" \"$@\"`" - $EXEC_CMD "$CMD" -} - -# attach to server -debug() -{ - debugwarning - NID=$(uid debug) - CMD="`shell_escape \"$ERL\" \"$NAME\" \"$NID\"` \ - -remsh $ERLANG_NODE \ - -hidden \ - $KERNEL_OPTS \ - $ERLANG_OPTS \ - `shell_escape \"${ARGS[@]}\" \"$@\"`" - $EXEC_CMD "$CMD" -} - -# attach to server using Elixir -iexdebug() -{ - debugwarning - # Elixir shell is hidden as default - NID=$(uid debug) - CMD="`shell_escape \"$IEX\" \"$IEXNAME\" \"$NID\"` \ - -remsh $ERLANG_NODE \ - --erl `shell_escape \"$KERNEL_OPTS\"` \ - --erl `shell_escape \"$ERLANG_OPTS\"` \ - --erl `shell_escape \"${ARGS[@]}\"` \ - --erl `shell_escape_str \"$@\"`" - $EXEC_CMD "ERL_PATH=\"$ERL\" $CMD" -} - -# start interactive server -live() -{ - livewarning - CMD="`shell_escape \"$ERL\" \"$NAME\" \"${ERLANG_NODE}\"` \ - $MNESIA_OPTS \ - $KERNEL_OPTS \ - $EJABBERD_OPTS \ - -s ejabberd \ - $ERLANG_OPTS \ - `shell_escape \"${ARGS[@]}\" \"$@\"`" - $EXEC_CMD "$CMD" -} - -# start interactive server with Elixir -iexlive() -{ - livewarning - echo $@ - CMD="`shell_escape \"$IEX\" \"$IEXNAME\" \"${ERLANG_NODE}\"` \ - --erl \"-mnesia dir \\\"$SPOOL_DIR\\\"\" \ - --erl \"`shell_escape \"$KERNEL_OPTS\"`\" \ - --erl \"`shell_escape \"$EJABBERD_OPTS\"`\" \ - --app ejabberd \ - --erl `shell_escape \"$ERLANG_OPTS\"` \ - --erl `shell_escape \"${ARGS[@]}\"` \ - --erl `shell_escape_str \"$@\"`" - $EXEC_CMD "ERL_PATH=\"$ERL\" $CMD" -} - -# start server in the foreground -foreground() -{ - check_start - CMD="`shell_escape \"$ERL\" \"$NAME\" \"$ERLANG_NODE\"` \ - -noinput \ - $MNESIA_OPTS \ - $KERNEL_OPTS \ - $EJABBERD_OPTS \ - -s ejabberd \ - $ERLANG_OPTS \ - `shell_escape \"${ARGS[@]}\" \"$@\"`" - $EXEC_CMD "$CMD" + case $EXEC_CMD in + as_install_user) su -s /bin/sh -c 'exec "$0" "$@"' "$INSTALLUSER" -- "$@" ;; + as_current_user) "$@" ;; + esac +} +exec_erl() +{ + NODE=$1; shift + exec_cmd "$ERL" ${S:--}name "$NODE" $ERLANG_OPTS "$@" +} +exec_iex() +{ + NODE=$1; shift + exec_cmd "$IEX" -${S:--}name "$NODE" --erl "$ERLANG_OPTS" "$@" } +# usage debugwarning() { if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then @@ -272,21 +158,22 @@ debugwarning() echo "Please be extremely cautious with your actions," echo "and exit immediately if you are not completely sure." echo "" - echo "To detach this shell from ejabberd, press:" - echo " control+c, control+c" + echo "To exit and detach this shell from ejabberd, press:" + echo " control+g and then q" echo "" + #vt100 echo "Please do NOT use control+c in this debug shell !" + #vt100 echo "" echo "--------------------------------------------------------------------" echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:" echo " EJABBERD_BYPASS_WARNINGS=true" echo "Press return to continue" - read foo + read -r _ echo "" fi } livewarning() { - check_start if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then echo "--------------------------------------------------------------------" echo "" @@ -296,57 +183,72 @@ livewarning() echo "Please be extremely cautious with your actions," echo "and exit immediately if you are not completely sure." echo "" - echo "To exit this LIVE mode and stop ejabberd, press:" - echo " q(). and press the Enter key" + echo "To stop ejabberd gracefully:" + echo " ejabberd:stop()." + echo "To quit erlang immediately, press:" + echo " control+g and then q" echo "" echo "--------------------------------------------------------------------" echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:" echo " EJABBERD_BYPASS_WARNINGS=true" echo "Press return to continue" - read foo + read -r _ echo "" fi } -etop() +check_etop_result() { - NID=$(uid top) - $EXEC_CMD "$ERL \ - $NAME $NID \ - -hidden -s etop -s erlang halt -output text -node $ERLANG_NODE" + result=$? + if [ $result -eq 1 ] ; then + echo "" + echo "It seems there was some problem running 'ejabberdctl etop'." + echo "Is the error message something like this?" + echo " Failed to load module 'etop' because it cannot be found..." + echo "Then probably ejabberd was compiled with development tools disabled." + echo "To use 'etop', recompile ejabberd with: ./configure --enable-tools" + echo "" + exit $result + fi } -ping() +check_iex_result() { - [ -z "$1" ] && PEER=${ERLANG_NODE} || PEER=$1 - if [ "$PEER" = "${PEER%.*}" ] ; then - PING_NAME="-sname" - PING_NODE=$(hostname -s) - else - PING_NAME="-name" - PING_NODE=$(hostname) + result=$? + if [ $result -eq 127 ] ; then + echo "" + echo "It seems there was some problem finding 'iex' binary from Elixir." + echo "Probably ejabberd was compiled with Rebar3 and Elixir disabled, like:" + echo " ./configure" + echo "which is equivalent to:" + echo " ./configure --with-rebar=rebar3 --disable-elixir" + echo "To use 'iex', recompile ejabberd enabling Elixir or using Mix:" + echo " ./configure --enable-elixir" + echo " ./configure --with-rebar=mix" + echo "" + exit $result fi - NID=$(uid ping ${PING_NODE}) - $EXEC_CMD "$ERL \ - $PING_NAME $NID \ - -hidden $KERNEL_OPTS $ERLANG_OPTS \ - -eval 'io:format(\"~p~n\",[net_adm:ping('\"'\"'$PEER'\"'\"')])' \ - -s erlang halt -output text -noinput" } help() { echo "" echo "Commands to start an ejabberd node:" - echo " start Start an ejabberd node in server mode" - echo " debug Attach an interactive Erlang shell to a running ejabberd node" - echo " iexdebug Attach an interactive Elixir shell to a running ejabberd node" - echo " live Start an ejabberd node in live (interactive) mode" - echo " iexlive Start an ejabberd node in live (interactive) mode, within an Elixir shell" - echo " foreground Start an ejabberd node in server mode (attached)" + echo " start Start in server mode" + echo " foreground Start in server mode (attached)" + echo " foreground-quiet Start in server mode (attached), show only critical messages" + echo " live Start in interactive mode, with Erlang shell" + echo " iexlive Start in interactive mode, with Elixir shell" + echo "" + echo "Commands to interact with a running ejabberd node:" + echo " debug Attach an interactive Erlang shell to a running node" + echo " iexdebug Attach an interactive Elixir shell to a running node" + echo " etop Attach to a running node and start Erlang Top" + echo " ping Send ping to the node, returns pong or pang" + echo " started|stopped Wait for the node to fully start|stop" echo "" echo "Optional parameters when starting an ejabberd node:" - echo " --config-dir dir Config ejabberd: $ETC_DIR" + echo " --config-dir dir Config ejabberd: $CONFIG_DIR" echo " --config file Config ejabberd: $EJABBERD_CONFIG_PATH" echo " --ctl-config file Config ejabberdctl: $EJABBERDCTL_CONFIG_PATH" echo " --logs dir Directory for logs: $LOGS_DIR" @@ -355,92 +257,318 @@ help() echo "" } -# common control function -ctl() -{ - NID=$(uid ctl) - CMD="`shell_escape \"$ERL\" \"$NAME\" \"$NID\"` \ - -noinput -hidden $KERNEL_OPTS -s ejabberd_ctl \ - -extra `shell_escape \"$ERLANG_NODE\"` $EJABBERD_NO_TIMEOUT \ - `shell_escape \"$@\"`" - $EXEC_CMD "$CMD" - result=$? - case $result in - 2) help;; - 3) help;; - *) :;; - esac - return $result -} - -uid() -{ - uuid=$(uuidgen 2>/dev/null) - [ -z "$uuid" -a -f /proc/sys/kernel/random/uuid ] && uuid=$(&1 | sed 's|.* \([0-9]*[0-9]\).*|\1|g')" + if [ $ERTSVERSION -lt 11 ] ; then # otp 23.0 includes erts 11.0 + # Erlang/OTP lower than 23, which doesn's support dynamic node code + N=1 + PF=$(( $$ % 97 )) + while + case $# in + 0) NN="${PF}-${N}-${ERLANG_NODE}" + ;; + 1) NN="${PF}-${N}-${1}-${ERLANG_NODE}" + ;; + 2) NN="${PF}-${N}-${1}@${2}" + ;; + esac + N=$(( N + 1 + ( $$ % 5 ) )) + "$EPMD" -names 2>/dev/null | grep -q " ${NN%@*} " + do :; done + echo $NN + else + # Erlang/OTP 23 or higher: use native dynamic node code + # https://www.erlang.org/patches/otp-23.0#OTP-13812 + if [ "$ERLANG_NODE" != "${ERLANG_NODE%.*}" ]; then + echo "undefined@${ERLANG_NODE#*@}" + else + echo "undefined" + fi + fi } # stop epmd if there is no other running node stop_epmd() { + [ -n "$ERL_DIST_PORT" ] && return "$EPMD" -names 2>/dev/null | grep -q name || "$EPMD" -kill >/dev/null } # make sure node not already running and node name unregistered +# if all ok, ensure runtime directory exists and make it current directory check_start() { + [ -n "$ERL_DIST_PORT" ] && return "$EPMD" -names 2>/dev/null | grep -q " ${ERLANG_NODE%@*} " && { - ps ux | grep -v grep | grep -q " $ERLANG_NODE " && { + pgrep -f "$ERLANG_NODE" >/dev/null && { echo "ERROR: The ejabberd node '$ERLANG_NODE' is already running." exit 4 - } || { - ps ux | grep -v grep | grep -q beam && { - echo "ERROR: The ejabberd node '$ERLANG_NODE' is registered," - echo " but no related beam process has been found." - echo "Shutdown all other erlang nodes, and call 'epmd -kill'." - exit 5 - } || { - "$EPMD" -kill >/dev/null - } } + pgrep beam >/dev/null && { + echo "ERROR: The ejabberd node '$ERLANG_NODE' is registered," + echo " but no related beam process has been found." + echo "Shutdown all other erlang nodes, and call 'epmd -kill'." + exit 5 + } + "$EPMD" -kill >/dev/null } } # allow sync calls -wait_for_status() +wait_status() { - # args: status try delay - # return: 0 OK, 1 KO - timeout=$2 - status=4 - while [ $status -ne $1 ] ; do - sleep $3 - timeout=`expr $timeout - 1` - [ $timeout -eq 0 ] && { - status=$1 - } || { - ctl status > /dev/null - status=$? - } - done - [ $timeout -eq 0 ] && return 1 || return 0 + wait_status_node "$ERLANG_NODE" $1 $2 $3 } -# main handler -case "${ARGS[0]}" in - 'start') start;; - 'debug') debug;; - 'iexdebug') iexdebug;; - 'live') live;; - 'iexlive') iexlive;; - 'foreground') foreground;; - 'ping'*) ping ${ARGS[1]};; - 'etop') etop;; - 'started') wait_for_status 0 30 2;; # wait 30x2s before timeout - 'stopped') wait_for_status 3 30 2 && stop_epmd;; # wait 30x2s before timeout - *) ctl "${ARGS[@]}";; +wait_status_node() +{ + CONNECT_NODE=$1 + shift + # args: status try delay + # return: 0 OK, 1 KO + timeout="$2" + status=4 + while [ "$status" -ne "$1" ] ; do + sleep "$3" + timeout=$((timeout - 1)) + if [ $timeout -eq 0 ] ; then + status="$1" + else + exec_erl "$(uid ctl)" -hidden -noinput \ + -eval 'net_kernel:connect_node('"'$CONNECT_NODE'"')' \ + -s ejabberd_ctl \ + -extra "$CONNECT_NODE" $NO_TIMEOUT status > /dev/null + status="$?" + fi + done + [ $timeout -gt 0 ] +} + +exec_other_command() +{ + exec_other_command_node $ERLANG_NODE "$@" +} + +exec_other_command_node() +{ + CONNECT_NODE=$1 + shift + if [ -z "$CTL_OVER_HTTP" ] || [ ! -S "$CTL_OVER_HTTP" ] \ + || [ ! -x "$(command -v curl)" ] || [ -z "$1" ] || [ "$1" = "help" ] \ + || [ "$1" = "mnesia_info_ctl" ]|| [ "$1" = "print_sql_schema" ] ; then + exec_erl "$(uid ctl)" -hidden -noinput \ + -eval 'net_kernel:connect_node('"'$CONNECT_NODE'"')' \ + -s ejabberd_ctl \ + -extra "$CONNECT_NODE" $NO_TIMEOUT "$@" + result=$? + case $result in + 3) help;; + *) :;; + esac + return $result + else + exec_ctl_over_http_socket "$@" + fi +} + +exec_ctl_over_http_socket() +{ + COMMAND=${1} + CARGS="" + while [ $# -gt 0 ]; do + [ -z "$CARGS" ] && CARGS="[" || CARGS="${CARGS}, " + CARGS="${CARGS}\"$1\"" + shift + done + CARGS="${CARGS}]" + TEMPHEADERS=temp-headers.log + curl \ + --unix-socket ${CTL_OVER_HTTP} \ + --header "Content-Type: application/json" \ + --header "Accept: application/json" \ + --data "${CARGS}" \ + --dump-header ${TEMPHEADERS} \ + --no-progress-meter \ + "http://localhost/ctl/${COMMAND}" + result=$(sed -n 's/.*status-code: \([0-9]*\).*/\1/p' < $TEMPHEADERS) + rm ${TEMPHEADERS} + case $result in + 2|3) exec_other_command help ${COMMAND};; + *) :;; + esac + exit $result +} + +# ensure we can change current directory to SPOOL_DIR +[ -d "$SPOOL_DIR" ] || exec_cmd mkdir -p "$SPOOL_DIR" +cd "$SPOOL_DIR" || { + echo "ERROR: can not access directory $SPOOL_DIR" + exit 6 +} + +printe() +{ + printf "\n" + printf "\e[1;40;32m==> %s\e[0m\n" "$1" +} + +## Function copied from tools/make-installers +user_agrees() +{ + question="$*" + + if [ -t 0 ] + then + printe "$question (y/n) [n]" + read -r response + case "$response" in + [Yy]|[Yy][Ee][Ss]) + return 0 + ;; + [Nn]|[Nn][Oo]|'') + return 1 + ;; + *) + echo 'Please respond with "yes" or "no".' + user_agrees "$question" + ;; + esac + else # Assume 'yes' if not running interactively. + return 0 + fi +} + +mnesia_change() +{ + ERLANG_NODE_OLD="$1" + [ "$ERLANG_NODE_OLD" = "" ] \ + && echo "Error: Please provide the old erlang node name, for example:" \ + && echo " ejabberdctl mnesia_change ejabberd@oldmachine" \ + && exit 1 + + SPOOL_DIR_BACKUP=$SPOOL_DIR/$ERLANG_NODE_OLD-backup/ + OLDFILE=$SPOOL_DIR_BACKUP/$ERLANG_NODE_OLD.backup + NEWFILE=$SPOOL_DIR_BACKUP/$ERLANG_NODE.backup + + printe "This changes your mnesia database from node name '$ERLANG_NODE_OLD' to '$ERLANG_NODE'" + + [ -d "$SPOOL_DIR_BACKUP" ] && printe "WARNING! A backup of old node already exists in $SPOOL_DIR_BACKUP" + + if ! user_agrees "Do you want to proceed?" + then + echo 'Operation aborted.' + exit 1 + fi + + printe "Starting ejabberd with old node name $ERLANG_NODE_OLD ..." + exec_erl "$ERLANG_NODE_OLD" $EJABBERD_OPTS -detached + wait_status_node $ERLANG_NODE_OLD 0 30 2 + result=$? + case $result in + 1) echo "There was a problem starting ejabberd with the old erlang node name. " \ + && echo "Check for log errors in $EJABBERD_LOG_PATH" \ + && exit $result;; + *) :;; + esac + exec_other_command_node $ERLANG_NODE_OLD "status" + + printe "Making backup of old database to file $OLDFILE ..." + mkdir $SPOOL_DIR_BACKUP + exec_other_command_node $ERLANG_NODE_OLD backup "$OLDFILE" + + printe "Changing node name in new backup file $NEWFILE ..." + exec_other_command_node $ERLANG_NODE_OLD mnesia_change_nodename "$ERLANG_NODE_OLD" "$ERLANG_NODE" "$OLDFILE" "$NEWFILE" + + printe "Stopping old ejabberd..." + exec_other_command_node $ERLANG_NODE_OLD "stop" + wait_status_node $ERLANG_NODE_OLD 3 30 2 && stop_epmd + + printe "Moving old mnesia spool files to backup subdirectory $SPOOL_DIR_BACKUP ..." + mv $SPOOL_DIR/*.DAT $SPOOL_DIR_BACKUP + mv $SPOOL_DIR/*.DCD $SPOOL_DIR_BACKUP + mv $SPOOL_DIR/*.LOG $SPOOL_DIR_BACKUP + + printe "Starting ejabberd with new node name $ERLANG_NODE ..." + exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -detached + wait_status 0 30 2 + exec_other_command "status" + + printe "Installing fallback of new mnesia..." + exec_other_command install_fallback "$NEWFILE" + + printe "Stopping new ejabberd..." + exec_other_command "stop" + wait_status 3 30 2 && stop_epmd + + printe "Finished, now you can start ejabberd normally" +} + +# main +case $1 in + start) + check_start + exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -detached + ;; + foreground) + check_start + exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -noinput + ;; + foreground-quiet) + check_start + exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -noinput -ejabberd quiet true + ;; + live) + livewarning + check_start + exec_erl "$ERLANG_NODE" $EJABBERD_OPTS + ;; + debug) + debugwarning + set_dist_client + exec_erl "$(uid debug)" -hidden -remsh "$ERLANG_NODE" + ;; + etop) + set_dist_client + exec_erl "$(uid top)" -hidden -remsh "$ERLANG_NODE" -s etop \ + -output text + check_etop_result + ;; + iexdebug) + debugwarning + set_dist_client + exec_iex "$(uid debug)" --remsh "$ERLANG_NODE" + check_iex_result + ;; + iexlive) + livewarning + exec_iex "$ERLANG_NODE" --erl "$EJABBERD_OPTS" + check_iex_result + ;; + ping) + PEER=${2:-$ERLANG_NODE} + [ "$PEER" = "${PEER%.*}" ] && PS="-s" + set_dist_client + exec_cmd "$ERL" ${PS:--}name "$(uid ping "$(hostname $PS)")" $ERLANG_OPTS \ + -noinput -hidden \ + -eval 'net_kernel:connect_node('"'$PEER'"')' \ + -eval 'io:format("~p~n",[net_adm:ping('"'$PEER'"')])' \ + -eval 'halt(case net_adm:ping('"'$PEER'"') of pong -> 0; pang -> 1 end).' \ + -output text + ;; + started) + set_dist_client + wait_status 0 30 2 # wait 30x2s before timeout + ;; + stopped) + set_dist_client + wait_status 3 30 2 && stop_epmd # wait 30x2s before timeout + ;; + mnesia_change) + mnesia_change $2 + ;; + *) + set_dist_client + exec_other_command "$@" + ;; esac diff --git a/elvis.config b/elvis.config new file mode 100644 index 000000000..4ac4df9e1 --- /dev/null +++ b/elvis.config @@ -0,0 +1,59 @@ +[ + { + elvis, + [ + {config, + [#{dirs => ["src"], + filter => "*.erl", + ignore => ['ELDAPv3', eldap_filter_yecc], + ruleset => erl_files, + rules => [{elvis_text_style, line_length, #{limit => 1000, skip_comments => false}}, + {elvis_text_style, no_tabs, disable}, + {elvis_style, atom_naming_convention, disable}, + {elvis_style, consistent_variable_casing, disable}, + {elvis_style, dont_repeat_yourself, #{min_complexity => 70}}, + {elvis_style, export_used_types, disable}, + {elvis_style, function_naming_convention, disable}, + {elvis_style, god_modules, #{limit => 300}}, + {elvis_style, invalid_dynamic_call, disable}, + {elvis_style, macro_names, disable}, + {elvis_style, max_function_arity, disable}, % #{max_arity => 15}}, + {elvis_style, nesting_level, disable}, + {elvis_style, no_author, disable}, + {elvis_style, no_boolean_in_comparison, disable}, + {elvis_style, no_catch_expressions, disable}, + {elvis_style, no_debug_call, disable}, + {elvis_style, no_if_expression, disable}, + {elvis_style, no_import, disable}, + {elvis_style, no_nested_try_catch, disable}, + {elvis_style, no_operation_on_same_value, disable}, + {elvis_style, no_receive_without_timeout, disable}, + {elvis_style, no_single_clause_case, disable}, + {elvis_style, no_spec_with_records, disable}, + {elvis_style, no_throw, disable}, + {elvis_style, operator_spaces, disable}, + {elvis_style, param_pattern_matching, disable}, + {elvis_style, private_data_types, disable}, + {elvis_style, variable_naming_convention, disable} + ] + }, + + %#{dirs => ["include"], + % filter => "*.hrl", + % ruleset => hrl_files}, + + #{dirs => ["."], + filter => "Makefile.in", + ruleset => makefiles, + rules => [{elvis_text_style, line_length, #{limit => 400, + skip_comments => false}}, + {elvis_style, dont_repeat_yourself, #{min_complexity => 20}} + ] + } + ] + } + ] + } +]. + +%% vim: set filetype=erlang tabstop=8: diff --git a/erlang_ls.config b/erlang_ls.config new file mode 100644 index 000000000..83e3e52a5 --- /dev/null +++ b/erlang_ls.config @@ -0,0 +1,32 @@ +#otp_path: "/usr/lib/erlang" +#plt_path: "_build/default/rebar3_24.3.3_plt" +#code_reload: +# node: ejabberd@localhost +apps_dirs: + - "_build/default/lib/*" +deps_dirs: + - "_build/default/lib/*" +include_dirs: + - "_build/default/lib" + - "_build/default/lib/*/include" + - "include" +macros: + - name: DEPRECATED_GET_STACKTRACE + - name: HAVE_ERL_ERROR + - name: HAVE_URI_STRING + - name: OTP_BELOW_27 + - name: SIP + - name: STUN +diagnostics: + enabled: + - crossref + disabled: +# - dialyzer + - unused_includes # Otherwise it complains about unused logger.hrl +lenses: + disabled: + - ct-run-test + - function-references + - server-info + - show-behaviour-usages + - suggest-spec diff --git a/examples/extauth/check_pass_null.pl b/examples/extauth/check_pass_null.pl deleted file mode 100755 index cbf179202..000000000 --- a/examples/extauth/check_pass_null.pl +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/perl - -use Unix::Syslog qw(:macros :subs); - -my $domain = $ARGV[0] || "example.com"; - -while(1) - { - # my $rin = '',$rout; - # vec($rin,fileno(STDIN),1) = 1; - # $ein = $rin; - # my $nfound = select($rout=$rin,undef,undef,undef); - - my $buf = ""; - syslog LOG_INFO,"waiting for packet"; - my $nread = sysread STDIN,$buf,2; - do { syslog LOG_INFO,"port closed"; exit; } unless $nread == 2; - my $len = unpack "n",$buf; - my $nread = sysread STDIN,$buf,$len; - - my ($op,$user,$host,$password) = split /:/,$buf; - #$user =~ s/\./\//og; - my $jid = "$user\@$domain"; - my $result; - - syslog(LOG_INFO,"request (%s)", $op); - - SWITCH: - { - $op eq 'auth' and do - { - $result = 1; - },last SWITCH; - - $op eq 'setpass' and do - { - $result = 1; - },last SWITCH; - - $op eq 'isuser' and do - { - # password is null. Return 1 if the user $user\@$domain exitst. - $result = 1; - },last SWITCH; - - $op eq 'tryregister' and do - { - $result = 1; - },last SWITCH; - - $op eq 'removeuser' and do - { - # password is null. Return 1 if the user $user\@$domain exitst. - $result = 1; - },last SWITCH; - - $op eq 'removeuser3' and do - { - $result = 1; - },last SWITCH; - }; - my $out = pack "nn",2,$result ? 1 : 0; - syswrite STDOUT,$out; - } - -closelog; diff --git a/examples/mtr/ejabberd b/examples/mtr/ejabberd deleted file mode 100644 index 4328b0697..000000000 --- a/examples/mtr/ejabberd +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/sh -# -# PROVIDE: ejabberd -# REQUIRE: DAEMON -# KEYWORD: shutdown -# - -HOME=/usr/pkg/jabber D=/usr/pkg/jabber/ejabberd export HOME - -name="ejabberd" -rcvar=$name - -if [ -r /etc/rc.conf ] -then - . /etc/rc.conf -else - eval ${rcvar}=YES -fi - -# $flags from environment overrides ${rcvar}_flags -if [ -n "${flags}" ] -then - eval ${rcvar}_flags="${flags}" -fi - -checkyesno() -{ - eval _value=\$${1} - case $_value in - [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) return 0 ;; - [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) return 1 ;; - *) - echo "\$${1} is not set properly." - return 1 - ;; - esac -} - -cmd=${1:-start} -case ${cmd} in -force*) - cmd=${cmd#force} - eval ${rcvar}=YES - ;; -esac - -if checkyesno ${rcvar} -then -else - exit 0 -fi - -case ${cmd} in -start) - if [ -x $D/src ]; then - echo "Starting ${name}." - cd $D/src - ERL_MAX_PORTS=32000 export ERL_MAX_PORTS - ulimit -n $ERL_MAX_PORTS - su jabber -c "/usr/pkg/bin/erl -sname ejabberd -s ejabberd -heart -detached -sasl sasl_error_logger '{file, \"ejabberd-sasl.log\"}' &" \ - 1>/dev/null 2>&1 - fi - ;; -stop) - echo "rpc:call('ejabberd@`hostname -s`', init, stop, [])." | \ - su jabber -c "/usr/pkg/bin/erl -sname ejabberdstop" - ;; -restart) - echo "rpc:call('ejabberd@`hostname -s`', init, restart, [])." | \ - su jabber -c "/usr/pkg/bin/erl -sname ejabberdrestart" - ;; -*) - echo "Usage: $0 {start|stop|restart}" - exit 1 -esac diff --git a/examples/mtr/ejabberd-netbsd.sh b/examples/mtr/ejabberd-netbsd.sh deleted file mode 100644 index 9896c9bc4..000000000 --- a/examples/mtr/ejabberd-netbsd.sh +++ /dev/null @@ -1,81 +0,0 @@ -#!/bin/sh - -echo '1. fetch, compile, and install erlang' - -if [ ! pkg_info erlang 1>/dev/null 2>&1 ]; then - cd /usr/pkgsrc/lang/erlang - make fetch-list|sh - make - make install -fi -if pkg_info erlang | grep -q erlang-9.1nb1; then -else - echo "erlang-9.1nb1 not installed" 1>&2 - exit 1 -fi - - -echo '2. install crypt_drv.so' - -if [ ! -d /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib ] ; then - mkdir -p /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib -fi -if [ ! -f /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib/crypto_drv.so ]; then - cp work/otp*/lib/crypto/priv/*/*/crypto_drv.so \ - /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib -fi - - -echo '3. compile and install elibcrypto.so' - -if [ ! -f /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib/elibcrypto.so ]; then -cd /usr/pkgsrc/lang/erlang/work/otp_src_R9B-1/lib/crypto/c_src -ld -r -u CRYPTO_set_mem_functions -u MD5 -u MD5_Init -u MD5_Update \ - -u MD5_Final -u SHA1 -u SHA1_Init -u SHA1_Update -u SHA1_Final \ - -u des_set_key -u des_ncbc_encrypt -u des_ede3_cbc_encrypt \ - -L/usr/lib -lcrypto -o ../priv/obj/i386--netbsdelf/elibcrypto.o -cc -shared \ - -L/usr/pkgsrc/lang/erlang/work/otp_src_R9B-1/lib/erl_interface/obj/i386--netbsdelf \ - -o ../priv/obj/i386--netbsdelf/elibcrypto.so \ - ../priv/obj/i386--netbsdelf/elibcrypto.o -L/usr/lib -lcrypto -cp ../priv/obj/i386--netbsdelf/elibcrypto.so \ - /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib -fi - - -echo '4. compile and install ssl_esock' - -if [ ! -f /usr/pkg/lib/erlang/lib/ssl-2.3.5/priv/bin/ssl_esock ]; then - cd /usr/pkg/lib/erlang/lib/ssl-2.3.5/priv/obj/ - make -fi - - -echo '5. initial ejabberd configuration' - -cd /usr/pkg/jabber/ejabberd/src -./configure - - -echo '6. edit ejabberd Makefiles' - -for M in Makefile mod_*/Makefile; do - if [ ! -f $M.orig ]; then - mv $M $M.orig - sed -e s%/usr/local%/usr/pkg%g < $M.orig > $M - fi -done - - -echo '7. compile ejabberd' - -gmake -for A in mod_irc mod_muc mod_pubsub; do - (cd $A; gmake) -done - - -echo '' -echo 'now edit ejabberd.cfg' -echo '' -echo 'to start ejabberd: erl -sname ejabberd -s ejabberd' diff --git a/examples/mtr/ejabberd.cfg b/examples/mtr/ejabberd.cfg deleted file mode 100644 index 6f4f4b284..000000000 --- a/examples/mtr/ejabberd.cfg +++ /dev/null @@ -1,66 +0,0 @@ -% jabber.dbc.mtview.ca.us - -override_acls. - -{acl, admin, {user, "mrose", "jabber.dbc.mtview.ca.us"}}. - - -{access, announce, [{allow, admin}, - {deny, all}]}. -{access, c2s, [{deny, blocked}, - {allow, all}]}. -{access, c2s_shaper, [{none, admin}, - {normal, all}]}. -{access, configure, [{allow, admin}, - {deny, all}]}. -{access, disco_admin, [{allow, admin}, - {deny, all}]}. -{access, muc_admin, [{allow, admin}, - {deny, all}]}. -{access, register, [{deny, all}]}. -{access, s2s_shaper, [{fast, all}]}. - - -{auth_method, internal}. -{host, "jabber.dbc.mtview.ca.us"}. -{outgoing_s2s_port, 5269}. -{shaper, normal, {maxrate, 1000}}. -{shaper, fast, {maxrate, 50000}}. -{welcome_message, none}. - - -{listen, [{5222, ejabberd_c2s, - [{access, c2s}, - {shaper, c2s_shaper}]}, - {5223, ejabberd_c2s, - [{access, c2s}, - {shaper, c2s_shaper}, - {ssl, [{certfile, "/etc/openssl/certs/ejabberd.pem"}]}]}, - {5269, ejabberd_s2s_in, - [{shaper, s2s_shaper}]}]}. - - -{modules, [ - {mod_register, []}, - {mod_roster, []}, - {mod_privacy, []}, - {mod_configure, []}, - {mod_disco, []}, - {mod_stats, []}, - {mod_vcard, []}, - {mod_offline, []}, - {mod_echo, [{host, "echo.jabber.dbc.mtview.ca.us"}]}, - {mod_private, []}, -% {mod_irc, []}, - {mod_muc, []}, - {mod_pubsub, []}, - {mod_time, []}, - {mod_last, []}, - {mod_version, []} - ]}. - - - -% Local Variables: -% mode: erlang -% End: diff --git a/examples/transport-configs/configs/aim-transport.xml b/examples/transport-configs/configs/aim-transport.xml deleted file mode 100644 index 41804c69d..000000000 --- a/examples/transport-configs/configs/aim-transport.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - %d: [%t] (%h): %s - /var/log/jabber/aim-transport-error.log - - - - - record - %d %h %s - /var/log/jabber/aim-transport-record.log - - - - - - - - /usr/local/lib/jabber/libjabberdxdbfile.so - - - /var/spool/jabber - - - - - - - - - - AIM/ICQ Transport - This is the AIM/ICQ Transport. - EMAIL@ADDRESS.COM - http://aim-transport.jabberstudio.org/ - - cp1252 - - - - /usr/local/lib/jabber/aim-transport.so - - - - - - - - - 127.0.0.1 - 5233 - SECRET - - - - /var/run/jabber/aim-transport.pid - - diff --git a/examples/transport-configs/configs/ile.xml b/examples/transport-configs/configs/ile.xml deleted file mode 100644 index 5999f0fbd..000000000 --- a/examples/transport-configs/configs/ile.xml +++ /dev/null @@ -1,136 +0,0 @@ - - - - - 127.0.0.1 - 5238 - SECRET - ile.SERVER.COM - 7 - en - - I Love Email - With this service you can receive email notifications. - -Security warning: Be careful when using this. Your password will travel in clear from your client to your jabber server if you don't use SSL and it will probably travel in clear from the jabber server to your email server. Use with care. This shouldn't be an issue in your Intranet, but it is if you use an ILE installed in a foreign jabber server. - EMAIL@ADDRESS.COM - http://ile.jabberstudio.org/ - - - - - /var/log/jabber/ile.log - 1 - - - - 10 - 20 - - - - /var/spool/jabber/ile.SERVER.COM/users.db - /var/spool/jabber/ile.SERVER.COM/passwords.db - /var/spool/jabber/ile.SERVER.COM/hosts.db - /var/spool/jabber/ile.SERVER.COM/types.db - /var/spool/jabber/ile.SERVER.COM/notifyxa.db - /var/spool/jabber/ile.SERVER.COM/notifydnd.db - /var/spool/jabber/ile.SERVER.COM/urls.db - - -
- - Please fill in the fields,according to your email account settings and notification preferences - ILE: Email notification service - Email account settings - Username - Password - Hostname - Type - You have received NUM email messages since last time I checked, which was CHECKINTERVAL minutes ago. - There was an error while trying to check mail for ACCOUNT. - Notification Options - Notify even when Xtended Away (XA) - Notify even when Do Not Disturb (DND) - Webmail URL - Login to ACCOUNT - ILE: an email notifier component: http://ile.jabberstudio.org - - - - Por favor, rellene los campos del formulario. - ILE: Servicio de notificación de correo - Configuración de la cuenta de correo - Usuario - Clave - Host - Tipo - Ha recibido NUM email(s) desde la última comprobación que fue hace CHECKINTERVAL minutos - Ha habido un error en la comprobación del correo para la cuenta ACCOUNT. - Opciones de notificación - Notificar incluso si muy ausente (XA) - Notificar incluso si no molestar (DND) - Webmail URL - Leer correo de ACCOUNT - ILE: un notificador de nuevo email - http://ile.jabberstudio.org - - - - Ompli els camps del formulari. - ILE: Servei de notificació de nou email - Dades del compte de mail - Usuari - Clau - Host - Tipus - Ha rebut NUM email(s) des de la última comprobació que va ser fa CHECKINTERVAL minuts. - S'ha produit un error en la comprobació del correu per al compte ACCOUNT. - Opcions de notificació - Notificar si molt absent (XA) - Notificar si no molestar (DND) - Webmail URL - Llegir correu de ACCOUNT - ILE: un notificador de nou email - http://ile.jabberstudio.org - - - - - Va rog completati urmatoarele campuri - I Love Email: new email notification service - Email account settings - Nume utilizator - Parola - Nume gazda - Tip - Ati primit NUM mesaj(e) de la ultima verificare, care a fost acum CHECKINTERVAL minute. - A fost eroare in timp ce incercam sa verific posta pentru ACCOUNT. - Notification Options - Notify even when Xtended Away (XA) - Notify even when Do Not Disturb (DND) - Webmail URL - Login to ACCOUNT - ILE: an email notifier component: http://ile.jabberstudio.org - - - - - Vul volgende velden in. - ILE: Dienst voor e-mailnotificaties - Instellingen van e-mailaccount - Gebruikersnaam - Wachtwoord - Inkomende mailserver - Type verbinding - U hebt NUM berichten ontvangen sinds CHECKINTERVAL minuten geleden. - Fout tijdens controle op nieuwe e-mails bij ACCOUNT. ILE zal deze account niet meer opnieuw controleren tot u uw registratiegegevens wijzigt of opnieuw aanmeldt. - Notificatie-instellingen - Notificeer ook in de status Niet Beschikbaar (XA) - Notificeer ook in de status Niet Storen (DND) - URL van webmail - Aanmelden op ACCOUNT - ILE: een dienst om e-mailnotificaties te ontvangen: http://ile.jabberstudio.org - - -
- -
\ No newline at end of file diff --git a/examples/transport-configs/configs/jabber-gg-transport.xml b/examples/transport-configs/configs/jabber-gg-transport.xml deleted file mode 100644 index 39d8c0b64..000000000 --- a/examples/transport-configs/configs/jabber-gg-transport.xml +++ /dev/null @@ -1,149 +0,0 @@ - - - - - - - 127.0.0.1 - 5237 - SECRET - - - - - - Fill in your GG number (after "username") - and password to register on the transport. -

To change your information in the GaduGadu directory you need to fill in the other fields. -

To remove registration you need to leave the form blank. - - - - - - - To search people:
- First fill in surname or family name, nickname, city, birthyear or range of birthyears (eg. 1950-1960) - and gender (you may fill in more fields at once).
- or
- Fill in phone number
- or
- Fill in the GG number of the person you are searching. -
-
- - - - Please fill in the GaduGadu number of the person you want to add. - - GG Nummer - - - - Gadu-Gadu Transport - This is the Gadu-Gadu Transport. - EMAIL@ADDRESS.COM - http://www.jabberstudio.org/projects/jabber-gg-transport/ - - - - - - - - /var/log/jabber/jabber-gg-transport.log - - - - - - 60 - 10 - - - 315360000 - - - 300 - - - 60 - - - 5 - - - - - - - /var/spool/jabber/gg.SERVER.COM/ - - - /var/run/jabber/jabber-gg-transport.pid - - - GG_TRANSPORT_ADMIN@SERVER.COM - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/transport-configs/configs/jit.xml b/examples/transport-configs/configs/jit.xml deleted file mode 100644 index c0d78c40a..000000000 --- a/examples/transport-configs/configs/jit.xml +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - - - - - /var/log/jabber/jit-error - - - - - record - /var/log/jabber/jit-record - - - - - - - - /usr/local/lib/jabber/xdb_file.so - - - /var/spool/jabber - - - - - - - - sms.icq.SERVER.COM - - - - sms.icq.SERVER.COM - - away - - - Fill in your UIN and password. - Search ICQ users. - - ICQ Transport (JIT) - This is the Jabber ICQ Transport. - EMAIL@ADDRESS.COM - http://jit.jabberstudio.org/ - - - 3907 - - - - - - - - - - - - /var/spool/jabber/jit-count - - 5 - - 5 - - 18000 - windows-1252 - - login.icq.com - - - - - /usr/local/lib/jabber/jit.so - - - - - - - SERVER.COM - - - 127.0.0.1 - 5234 - SECRET - - - - /var/run/jabber/jit.pid - - diff --git a/examples/transport-configs/configs/msn-transport.xml b/examples/transport-configs/configs/msn-transport.xml deleted file mode 100644 index a6f1391db..000000000 --- a/examples/transport-configs/configs/msn-transport.xml +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - - - - %d: [%t] (%h): %s - /var/log/jabber/msn-transport-error.log - - - - - record - %d %h %s - /var/log/jabber/msn-transport-record.log - - - - - - - - /usr/local/lib/jabber/libjabberdxdbfile.so - - - /var/spool/jabber - - - - - - - - - Fill in your MSN account and password (eg: user1@hotmail.com). A nickname is optional. - - MSN Transport - This is the MSN Transport. - EMAIL@ADDRESS.COM - http://msn-transport.jabberstudio.org/ - - - - - More than one user entered this chat session. Enter this room to switch to groupchat modus. - - is available - has leaved the room - - - - - - - - - - /usr/local/lib/jabber/msn-transport.so - - - - - - - - - 127.0.0.1 - 5235 - SECRET - - - - /var/run/jabber/msn-transport.pid - - diff --git a/examples/transport-configs/configs/yahoo-transport-2.xml b/examples/transport-configs/configs/yahoo-transport-2.xml deleted file mode 100644 index 2d077aa7a..000000000 --- a/examples/transport-configs/configs/yahoo-transport-2.xml +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - - - - - %d: [%t] (%h): %s - /var/log/jabber/yahoo-transport-2-error.log - - - - - - - - - /usr/local/lib/jabber/libjabberdxdbfile.so - - - /var/spool/jabber - - - - - - - - - - Yahoo! Transport - vCard not implemented in current version - This is the Yahoo! transport. - EMAIL@ADDRESS.COM - http://yahoo-transport-2.jabberstudio.org/ - - Fill in your YAHOO! Messenger username and password to register on this transport. - scs.msg.yahoo.com - 5050 - - CP1252 - - - - - - /usr/local/lib/jabber/yahoo-transport-2.so - - - - - - - - - 127.0.0.1 - 5236 - SECRET - - - - /var/run/jabber/yahoo-transport-2.pid - - diff --git a/examples/transport-configs/init-scripts/aim-transport b/examples/transport-configs/init-scripts/aim-transport deleted file mode 100755 index e13d6572f..000000000 --- a/examples/transport-configs/init-scripts/aim-transport +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/sh -######################################################### -# -# aim-transport -- script to start aim-transport. -# -######################################################### - -DAEMON=/usr/local/sbin/jabberd-aim-transport -CONF=/etc/jabber/aim-transport.xml -NAME=jabberd-aim-transport -HOME=/etc/jabber/ -USER=ejabberd - -######################################################### - -if [ "`/usr/bin/whoami`" != "$USER" ]; then - - echo "You need to be" $USER "user to run this script." - exit 1 -fi - -case "$1" in - debug) - test -f $DAEMON -a -f $CONF || exit 0 - echo "Starting $NAME in debugging mode." - $DAEMON -D -H $HOME -c $CONF & - ;; - start) - test -f $DAEMON -a -f $CONF || exit 0 - echo "Starting $NAME." - $DAEMON -H $HOME -c $CONF & - ;; - stop) - echo "Stopping $NAME." - killall $NAME & - ;; - restart|reload) - $0 stop - sleep 3 - $0 start - ;; - *) - echo "Usage: $0 {debug|start|stop|restart}" - exit 1 -esac diff --git a/examples/transport-configs/init-scripts/ile b/examples/transport-configs/init-scripts/ile deleted file mode 100755 index a1e072f2c..000000000 --- a/examples/transport-configs/init-scripts/ile +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/sh -######################################################### -# -# ile -- script to start ILE. -# -######################################################### - -DAEMON=/usr/local/sbin/ile.pl -NAME=ile.pl -CONF=/etc/jabber/ile.xml -USER=ejabberd - -######################################################### - -if [ "`/usr/bin/whoami`" != "$USER" ]; then - - echo "You need to be" $USER "user to run this script." - exit 1 -fi - -case "$1" in - debug) - echo "Not implemented yet. Starting in normal mode" - $0 start - ;; - start) - test -f $DAEMON || exit 0 - echo "Starting $NAME." - $DAEMON $CONF & - ;; - stop) - echo "Stopping $NAME." - killall $NAME & - ;; - restart|reload) - $0 stop - sleep 3 - $0 start - ;; - *) - echo "Usage: $0 {debug|start|stop|status|restart}" - exit 1 -esac diff --git a/examples/transport-configs/init-scripts/jabber-gg-transport b/examples/transport-configs/init-scripts/jabber-gg-transport deleted file mode 100755 index 269685d0d..000000000 --- a/examples/transport-configs/init-scripts/jabber-gg-transport +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/sh -######################################################### -# -# jabber-gg-transport -- script to start jabber-gg-transport. -# -######################################################### - -DAEMON=/usr/local/sbin/jggtrans -CONF=/etc/jabber/jabber-gg-transport.xml -NAME=jggtrans -HOME=/etc/jabber/ -USER=ejabberd - -######################################################### - -if [ "`/usr/bin/whoami`" != "$USER" ]; then - - echo "You need to be" $USER "user to run this script." - exit 1 -fi - -case "$1" in - debug) - test -f $DAEMON -a -f $CONF || exit 0 - echo "Starting $NAME in debugging mode." - $DAEMON -D -H $HOME -c $CONF & - ;; - start) - test -f $DAEMON -a -f $CONF || exit 0 - echo "Starting $NAME." - $DAEMON $CONF & - ;; - stop) - echo "Stopping $NAME." - killall $NAME & - rm /var/run/jabber/jabber-gg-transport.pid - ;; - - restart|reload) - $0 stop - sleep 3 - $0 start - ;; - *) - echo "Usage: $0 {debug|start|stop|restart}" - exit 1 -esac diff --git a/examples/transport-configs/init-scripts/jit b/examples/transport-configs/init-scripts/jit deleted file mode 100755 index 55e000ee8..000000000 --- a/examples/transport-configs/init-scripts/jit +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/sh -######################################################### -# -# jit -- script to start JIT. -# -######################################################### - -DAEMON=/usr/local/sbin/wpjabber-jit -CONF=/etc/jabber/jit.xml -NAME=wpjabber-jit -HOME=/etc/jabber/ -USER=ejabberd - -######################################################### - -if [ "`/usr/bin/whoami`" != "$USER" ]; then - - echo "You need to be" $USER "user to run this script." - exit 1 -fi - -case "$1" in - debug) - test -f $DAEMON -a -f $CONF || exit 0 - echo "Starting $NAME in debugging mode." - $DAEMON -D -H $HOME -c $CONF & - ;; - start) - test -f $DAEMON -a -f $CONF || exit 0 - echo "Starting $NAME." - $DAEMON -H $HOME -c $CONF & - ;; - stop) - echo "Stopping $NAME." - killall $NAME & - ;; - restart|reload) - $0 stop - sleep 3 - $0 start - ;; - *) - echo "Usage: $0 {debug|start|stop|restart}" - exit 1 -esac diff --git a/examples/transport-configs/init-scripts/msn-transport b/examples/transport-configs/init-scripts/msn-transport deleted file mode 100755 index 555ba2b0f..000000000 --- a/examples/transport-configs/init-scripts/msn-transport +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/sh -######################################################### -# -# msn-transport -- script to start MSN Transport. -# -######################################################### - -DAEMON=/usr/local/sbin/jabberd-msn-transport -CONF=/etc/jabber/msn-transport.xml -NAME=jabberd-msn-transport -HOME=/etc/jabber/ -USER=ejabberd - -######################################################### - -if [ "`/usr/bin/whoami`" != "$USER" ]; then - - echo "You need to be" $USER "user to run this script." - exit 1 -fi - -case "$1" in - strace) - test -f $DAEMON -a -f $CONF || exit 0 - echo "Starting $NAME in strace mode." - strace -o /opt/ejabberd/var/log/jabber/strace.log $DAEMON -H $HOME -c $CONF & - ;; - debug) - test -f $DAEMON -a -f $CONF || exit 0 - echo "Starting $NAME in debugging mode." - $DAEMON -D -H $HOME -c $CONF & - ;; - start) - test -f $DAEMON -a -f $CONF || exit 0 - echo "Starting $NAME." - $DAEMON -H $HOME -c $CONF & - ;; - stop) - echo "Stopping $NAME." - killall $NAME & - ;; - restart|reload) - $0 stop - sleep 3 - $0 start - ;; - *) - echo "Usage: $0 {debug|start|stop|restart}" - exit 1 -esac diff --git a/examples/transport-configs/init-scripts/yahoo-transport-2 b/examples/transport-configs/init-scripts/yahoo-transport-2 deleted file mode 100755 index fde78a913..000000000 --- a/examples/transport-configs/init-scripts/yahoo-transport-2 +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/sh -############################################################## -# -# yahoo-transport-2 -- script to start Yahoo-transport-2. -# -############################################################# - -DAEMON=/usr/local/sbin/jabberd-yahoo-transport-2 -CONF=/etc/jabber/yahoo-transport-2.xml -NAME=jabberd-yahoo-transport-2 -HOME=/etc/jabber/ -USER=ejabberd - -############################################################# - -if [ "`/usr/bin/whoami`" != "$USER" ]; then - - echo "You need to be" $USER "user to run this script." - exit 1 -fi - -case "$1" in - debug) - test -f $DAEMON -a -f $CONF || exit 0 - echo "Starting $NAME in debugging mode." - $DAEMON -D -H $HOME -c $CONF & - ;; - start) - test -f $DAEMON -a -f $CONF || exit 0 - echo "Starting $NAME." - $DAEMON -H $HOME -c $CONF & - ;; - stop) - echo "Stopping $NAME." - killall $NAME & - ;; - restart|reload) - $0 stop - sleep 3 - $0 start - ;; - *) - echo "Usage: $0 {debug|start|stop|restart}" - exit 1 -esac diff --git a/include/bosh.hrl b/include/bosh.hrl index 9cdfd98d8..dd9f1b6a1 100644 --- a/include/bosh.hrl +++ b/include/bosh.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -48,4 +48,4 @@ -define(HEADER(CType), [CType, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_HEADERS]). --define(PROCNAME, ejabberd_mod_bosh). +-define(BOSH_CACHE, bosh_cache). diff --git a/include/ejabberd.hrl b/include/ejabberd.hrl deleted file mode 100644 index 391089a0e..000000000 --- a/include/ejabberd.hrl +++ /dev/null @@ -1,74 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% -%%% ejabberd, Copyright (C) 2002-2016 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. -%%% -%%%---------------------------------------------------------------------- - --ifndef(EJABBERD_HRL). --define(EJABBERD_HRL, true). - --define(VERSION, ejabberd_config:get_version()). - --define(MYHOSTS, ejabberd_config:get_myhosts()). - --define(MYNAME, hd(ejabberd_config:get_myhosts())). - --define(MYLANG, ejabberd_config:get_mylang()). - --define(MSGS_DIR, filename:join(["priv", "msgs"])). - --define(SQL_DIR, filename:join(["priv", "sql"])). - --define(CONFIG_PATH, <<"ejabberd.cfg">>). - --define(LOG_PATH, <<"ejabberd.log">>). - --define(EJABBERD_URI, <<"http://www.process-one.net/en/ejabberd/">>). - --define(COPYRIGHT, "Copyright (c) 2002-2016 ProcessOne"). - --define(S2STIMEOUT, timer:minutes(10)). - -%%-define(DBGFSM, true). - --record(scram, - {storedkey = <<"">>, - serverkey = <<"">>, - salt = <<"">>, - iterationcount = 0 :: integer()}). - --type scram() :: #scram{}. - --define(SCRAM_DEFAULT_ITERATION_COUNT, 4096). - --ifdef(ERL_DEPRECATED_TYPES). - --define(TDICT, dict()). --define(TGB_TREE, gb_tree()). --define(TGB_SET, gb_set()). --define(TQUEUE, queue()). - --else. - --define(TDICT, dict:dict()). --define(TGB_TREE, gb_trees:tree()). --define(TGB_SET, gb_sets:set()). --define(TQUEUE, queue:queue()). - --endif. - --endif. diff --git a/include/ejabberd_config.hrl b/include/ejabberd_auth.hrl similarity index 68% rename from include/ejabberd_config.hrl rename to include/ejabberd_auth.hrl index 2d6889313..bf7660d3f 100644 --- a/include/ejabberd_config.hrl +++ b/include/ejabberd_auth.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -18,13 +18,5 @@ %%% %%%---------------------------------------------------------------------- --record(local_config, {key :: any(), value :: any()}). - --type local_config() :: #local_config{}. - --record(state, - {opts = [] :: [acl:acl() | local_config()], - hosts = [] :: [binary()], - override_local = false :: boolean(), - override_global = false :: boolean(), - override_acls = false :: boolean()}). +-record(passwd, {us = {<<"">>, <<"">>} :: {binary(), binary()} | {binary(), binary(), atom()} | '$1', + password = <<"">> :: binary() | scram() | '_'}). diff --git a/include/ejabberd_commands.hrl b/include/ejabberd_commands.hrl index 199be890e..14d19d2e1 100644 --- a/include/ejabberd_commands.hrl +++ b/include/ejabberd_commands.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -19,13 +19,30 @@ %%%---------------------------------------------------------------------- -type aterm() :: {atom(), atype()}. --type atype() :: integer | string | binary | +-type atype() :: integer | string | binary | any | atom | {tuple, [aterm()]} | {list, aterm()}. -type rterm() :: {atom(), rtype()}. --type rtype() :: integer | string | atom | +-type rtype() :: integer | string | atom | any | {tuple, [rterm()]} | {list, rterm()} | rescode | restuple. +%% The 'any' and 'atom' argument types and 'any' result type +%% should only be used %% by commands with tag 'internal', +%% which are meant to be used only internally in ejabberd, +%% and not called using external frontends. + +%% The purpose of a command can either be: +%% - informative: its purpose is to obtain information +%% - modifier: its purpose is to produce some change in the server +%% +%% A modifier command should be designed just to produce its desired side-effect, +%% and its result term should just be success or failure: rescode or restuple. +%% +%% ejabberd_web_admin:make_command/2 considers that commands +%% with result type different than rescode or restuple +%% are commands that can be safely executed automatically +%% to get information and build the web page. + -type oauth_scope() :: atom(). %% ejabberd_commands OAuth ReST ACL definition: @@ -51,6 +68,7 @@ desc = "" :: string() | '_' | '$3', longdesc = "" :: string() | '_', version = 0 :: integer(), + note = "" :: string(), weight = 1 :: integer(), module :: atom() | '_', function :: atom() | '_', @@ -58,48 +76,32 @@ policy = restricted :: open | restricted | admin | user, %% access is: [accessRuleName] or [{Module, AccessOption, DefaultAccessRuleName}] access = [] :: [{atom(),atom(),atom()}|atom()], + definer = unknown :: atom(), result = {res, rescode} :: rterm() | '_' | '$2', + args_rename = [] :: [{atom(),atom()}], args_desc = none :: none | [string()] | '_', result_desc = none :: none | string() | '_', args_example = none :: none | [any()] | '_', result_example = none :: any()}). -%% TODO Fix me: Type is not up to date --type ejabberd_commands() :: #ejabberd_commands{name :: atom(), - tags :: [atom()], - desc :: string(), - longdesc :: string(), - version :: integer(), - module :: atom(), - function :: atom(), - args :: [aterm()], - policy :: open | restricted | admin | user, - access :: [{atom(),atom(),atom()}|atom()], - result :: rterm()}. +-type ejabberd_commands() :: #ejabberd_commands{name :: atom(), + tags :: [atom()], + desc :: string(), + longdesc :: string(), + version :: integer(), + note :: string(), + weight :: integer(), + module :: atom(), + function :: atom(), + args :: [aterm()], + policy :: open | restricted | admin | user, + access :: [{atom(),atom(),atom()}|atom()], + definer :: atom(), + result :: rterm(), + args_rename :: [{atom(),atom()}], + args_desc :: none | [string()] | '_', + result_desc :: none | string() | '_', + args_example :: none | [any()] | '_', + result_example :: any() + }. -%% @type ejabberd_commands() = #ejabberd_commands{ -%% name = atom(), -%% tags = [atom()], -%% desc = string(), -%% longdesc = string(), -%% module = atom(), -%% function = atom(), -%% args = [aterm()], -%% result = rterm() -%% }. -%% desc: Description of the command -%% args: Describe the accepted arguments. -%% This way the function that calls the command can format the -%% arguments before calling. - -%% @type atype() = integer | string | {tuple, [aterm()]} | {list, aterm()}. -%% Allowed types for arguments are integer, string, tuple and list. - -%% @type rtype() = integer | string | atom | {tuple, [rterm()]} | {list, rterm()} | rescode | restuple. -%% A rtype is either an atom or a tuple with two elements. - -%% @type aterm() = {Name::atom(), Type::atype()}. -%% An argument term is a tuple with the term name and the term type. - -%% @type rterm() = {Name::atom(), Type::rtype()}. -%% A result term is a tuple with the term name and the term type. diff --git a/include/ejabberd_ctl.hrl b/include/ejabberd_ctl.hrl index 3951129e7..cad82da89 100644 --- a/include/ejabberd_ctl.hrl +++ b/include/ejabberd_ctl.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_http.hrl b/include/ejabberd_http.hrl index 25209f76c..9e1373ce6 100644 --- a/include/ejabberd_http.hrl +++ b/include/ejabberd_http.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -21,9 +21,10 @@ -record(request, {method :: method(), path = [] :: [binary()], + raw_path :: binary(), q = [] :: [{binary() | nokey, binary()}], us = {<<>>, <<>>} :: {binary(), binary()}, - auth :: {binary(), binary()} | {oauth, binary(), []} | undefined, + auth :: {binary(), binary()} | {oauth, binary(), []} | undefined | invalid, lang = <<"">> :: binary(), data = <<"">> :: binary(), ip :: {inet:ip_address(), inet:port_number()}, @@ -31,7 +32,10 @@ port = 5280 :: inet:port_number(), opts = [] :: list(), tp = http :: protocol(), - headers = [] :: [{atom() | binary(), binary()}]}). + headers = [] :: [{atom() | binary(), binary()}], + length = 0 :: non_neg_integer(), + sockmod :: gen_tcp | fast_tls, + socket :: inet:socket() | fast_tls:tls_socket()}). -record(ws, {socket :: inet:socket() | fast_tls:tls_socket(), @@ -46,6 +50,6 @@ buf :: binary(), http_opts = [] :: list()}). --type method() :: 'GET' | 'HEAD' | 'DELETE' | 'OPTIONS' | 'PUT' | 'POST' | 'TRACE'. +-type method() :: 'GET' | 'HEAD' | 'DELETE' | 'OPTIONS' | 'PUT' | 'POST' | 'TRACE' | 'PATCH'. -type protocol() :: http | https. -type http_request() :: #request{}. diff --git a/include/ejabberd_oauth.hrl b/include/ejabberd_oauth.hrl index 6b5a9bcc8..4798d9070 100644 --- a/include/ejabberd_oauth.hrl +++ b/include/ejabberd_oauth.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -22,5 +22,12 @@ token = <<"">> :: binary() | '_', us = {<<"">>, <<"">>} :: {binary(), binary()} | '_', scope = [] :: [binary()] | '_', - expire :: integer() | '$1' + expire :: integer() | '$1' | '_' + }). + +-record(oauth_client, { + client_id = <<"">> :: binary() | '_', + client_name = <<"">> :: binary() | '_', + grant_type :: password | implicit | '_', + options :: [any()] | '_' }). diff --git a/include/ejabberd_router.hrl b/include/ejabberd_router.hrl new file mode 100644 index 000000000..060ab79a1 --- /dev/null +++ b/include/ejabberd_router.hrl @@ -0,0 +1,8 @@ +-define(ROUTES_CACHE, routes_cache). + +-type local_hint() :: integer() | {apply, atom(), atom()}. + +-record(route, {domain :: binary(), + server_host :: binary(), + pid :: undefined | pid(), + local_hint :: local_hint() | undefined}). diff --git a/include/ejabberd_sm.hrl b/include/ejabberd_sm.hrl index f86ab1c15..54a828e1a 100644 --- a/include/ejabberd_sm.hrl +++ b/include/ejabberd_sm.hrl @@ -1,13 +1,36 @@ +%%%---------------------------------------------------------------------- +%%% +%%% ejabberd, Copyright (C) 2002-2025 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. +%%% +%%%---------------------------------------------------------------------- + -ifndef(EJABBERD_SM_HRL). -define(EJABBERD_SM_HRL, true). +-define(SM_CACHE, sm_cache). + -record(session, {sid, usr, us, priority, info = []}). -record(session_counter, {vhost, count}). -type sid() :: {erlang:timestamp(), pid()}. -type ip() :: {inet:ip_address(), inet:port_number()} | undefined. -type info() :: [{conn, atom()} | {ip, ip()} | {node, atom()} | {oor, boolean()} | {auth_module, atom()} - | {num_stanzas_in, non_neg_integer()}]. + | {num_stanzas_in, non_neg_integer()} + | {atom(), term()}]. -type prio() :: undefined | integer(). -endif. diff --git a/include/ejabberd_sql.hrl b/include/ejabberd_sql.hrl new file mode 100644 index 000000000..d0ab55cba --- /dev/null +++ b/include/ejabberd_sql.hrl @@ -0,0 +1,75 @@ +%%%---------------------------------------------------------------------- +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- +-define(SQL_MARK, sql__mark_). +-define(SQL(SQL), ?SQL_MARK(SQL)). + +-define(SQL_UPSERT_MARK, sql_upsert__mark_). +-define(SQL_UPSERT(Host, Table, Fields), + ejabberd_sql:sql_query(Host, ?SQL_UPSERT_MARK(Table, Fields))). +-define(SQL_UPSERT_T(Table, Fields), + ejabberd_sql:sql_query_t(?SQL_UPSERT_MARK(Table, Fields))). + +-define(SQL_INSERT_MARK, sql_insert__mark_). +-define(SQL_INSERT(Table, Fields), ?SQL_INSERT_MARK(Table, Fields)). + +-ifdef(COMPILER_REPORTS_ONLY_LINES). +-record(sql_query, {hash :: binary(), + format_query :: fun(), + format_res :: fun(), + args :: fun(), + flags :: non_neg_integer(), + loc :: {module(), pos_integer()}}). +-else. +-record(sql_query, {hash :: binary(), + format_query :: fun(), + format_res :: fun(), + args :: fun(), + flags :: non_neg_integer(), + loc :: {module(), {pos_integer(), pos_integer()}}}). +-endif. + +-record(sql_escape, {string :: fun((binary()) -> binary()), + integer :: fun((integer()) -> binary()), + boolean :: fun((boolean()) -> binary()), + in_array_string :: fun((binary()) -> binary()), + like_escape :: fun(() -> binary())}). + + +-record(sql_index, {columns, + unique = false :: boolean(), + meta = #{}}). +-record(sql_column, {name :: binary(), + type, + default = false, + opts = []}). +-record(sql_table, {name :: binary(), + columns :: [#sql_column{}], + indices = [] :: [#sql_index{}], + post_create}). +-record(sql_schema, {version :: integer(), + tables :: [#sql_table{}], + update = []}). +-record(sql_references, {table :: binary(), + column :: binary()}). + +-record(sql_schema_info, + {db_type :: pgsql | mysql | sqlite, + db_version :: any(), + new_schema = true :: boolean()}). diff --git a/include/ejabberd_sql_pt.hrl b/include/ejabberd_sql_pt.hrl index f1a3dba3f..f89f5c969 100644 --- a/include/ejabberd_sql_pt.hrl +++ b/include/ejabberd_sql_pt.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -17,17 +17,5 @@ %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- - --define(SQL_MARK, sql__mark_). --define(SQL(SQL), ?SQL_MARK(SQL)). - --define(SQL_UPSERT_MARK, sql_upsert__mark_). --define(SQL_UPSERT(Host, Table, Fields), - ejabberd_sql:sql_query(Host, ?SQL_UPSERT_MARK(Table, Fields))). --define(SQL_UPSERT_T(Table, Fields), - ejabberd_sql:sql_query_t(?SQL_UPSERT_MARK(Table, Fields))). - --record(sql_query, {hash, format_query, format_res, args, loc}). - --record(sql_escape, {string, integer, boolean}). - +-compile([{parse_transform, ejabberd_sql_pt}]). +-include("ejabberd_sql.hrl"). diff --git a/include/ejabberd_web_admin.hrl b/include/ejabberd_web_admin.hrl index 33ff3a451..45e4beada 100644 --- a/include/ejabberd_web_admin.hrl +++ b/include/ejabberd_web_admin.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -37,14 +37,12 @@ -define(XAC(Name, Attrs, Text), ?XAE(Name, Attrs, [?C(Text)])). --define(T(Text), translate:translate(Lang, Text)). +-define(CT(Text), ?C((translate:translate(Lang, Text)))). --define(CT(Text), ?C((?T(Text)))). - --define(XCT(Name, Text), ?XC(Name, (?T(Text)))). +-define(XCT(Name, Text), ?XC(Name, (translate:translate(Lang, Text)))). -define(XACT(Name, Attrs, Text), - ?XAC(Name, Attrs, (?T(Text)))). + ?XAC(Name, Attrs, (translate:translate(Lang, Text)))). -define(LI(Els), ?XE(<<"li">>, Els)). @@ -53,7 +51,7 @@ -define(AC(URL, Text), ?A(URL, [?C(Text)])). --define(ACT(URL, Text), ?AC(URL, (?T(Text)))). +-define(ACT(URL, Text), ?AC(URL, (translate:translate(Lang, Text)))). -define(P, ?X(<<"p">>)). @@ -64,8 +62,21 @@ [{<<"type">>, Type}, {<<"name">>, Name}, {<<"value">>, Value}])). +-define(INPUTPH(Type, Name, Value, PlaceHolder), + ?XA(<<"input">>, + [{<<"type">>, Type}, {<<"name">>, Name}, + {<<"value">>, Value}, {<<"placeholder">>, PlaceHolder}])). + -define(INPUTT(Type, Name, Value), - ?INPUT(Type, Name, (?T(Value)))). + ?INPUT(Type, Name, (translate:translate(Lang, Value)))). + +-define(INPUTD(Type, Name, Value), + ?XA(<<"input">>, + [{<<"type">>, Type}, {<<"name">>, Name}, + {<<"class">>, <<"btn-danger">>}, {<<"value">>, Value}])). + +-define(INPUTTD(Type, Name, Value), + ?INPUTD(Type, Name, (translate:translate(Lang, Value)))). -define(INPUTS(Type, Name, Value, Size), ?XA(<<"input">>, @@ -73,7 +84,7 @@ {<<"value">>, Value}, {<<"size">>, Size}])). -define(INPUTST(Type, Name, Value, Size), - ?INPUT(Type, Name, (?T(Value)), Size)). + ?INPUT(Type, Name, (translate:translate(Lang, Value)), Size)). -define(ACLINPUT(Text), ?XE(<<"td">>, @@ -89,16 +100,27 @@ -define(XRES(Text), ?XAC(<<"p">>, [{<<"class">>, <<"result">>}], Text)). +-define(DIVRES(Elements), + ?XAE(<<"div">>, [{<<"class">>, <<"result">>}], Elements)). + %% Guide Link --define(XREST(Text), ?XRES((?T(Text)))). +-define(XREST(Text), ?XRES((translate:translate(Lang, Text)))). -define(GL(Ref, Title), ?XAE(<<"div">>, [{<<"class">>, <<"guidelink">>}], [?XAE(<<"a">>, - [{<<"href">>, <<"/admin/doc/guide.html#", Ref/binary>>}, + [{<<"href">>, <<"https://docs.ejabberd.im/", Ref/binary>>}, {<<"target">>, <<"_blank">>}], - [?C(<<"[Guide: ", Title/binary, "]">>)])])). + [?C(<<"docs: ", Title/binary>>)])])). %% h1 with a Guide Link --define(H1GL(Name, Ref, Title), - [?XC(<<"h1">>, Name), ?GL(Ref, Title)]). +-define(H1GLraw(Name, Ref, Title), + [?XC(<<"h1">>, Name), ?GL(Ref, Title), ?BR, ?BR]). +-define(H1GL(Name, RefConf, Title), + ?H1GLraw(Name, <<"admin/configuration/", RefConf/binary>>, Title)). + +-define(ANCHORL(Ref), + ?XAE(<<"div">>, [{<<"class">>, <<"anchorlink">>}], + [?XAE(<<"a">>, + [{<<"href">>, <<"#", Ref/binary>>}], + [?C(unicode:characters_to_binary("¶"))])])). diff --git a/include/eldap.hrl b/include/eldap.hrl index 6dfc894a8..0b6dc97e5 100644 --- a/include/eldap.hrl +++ b/include/eldap.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -27,7 +27,7 @@ -record(eldap_search, {scope = wholeSubtree :: scope(), base = <<"">> :: binary(), - filter :: eldap:filter(), + filter :: eldap:filter() | undefined, limit = 0 :: non_neg_integer(), attributes = [] :: [binary()], types_only = false :: boolean(), @@ -44,6 +44,7 @@ attributes = [] :: [{binary(), [binary()]}]}). -type tlsopts() :: [{encrypt, tls | starttls | none} | + {tls_certfile, binary() | undefined} | {tls_cacertfile, binary() | undefined} | {tls_depth, non_neg_integer() | undefined} | {tls_verify, hard | soft | false}]. @@ -61,3 +62,18 @@ -type eldap_config() :: #eldap_config{}. -type eldap_search() :: #eldap_search{}. -type eldap_entry() :: #eldap_entry{}. + +-define(eldap_config(M, H), + #eldap_config{ + servers = M:ldap_servers(H), + backups = M:ldap_backups(H), + tls_options = [{encrypt, M:ldap_encrypt(H)}, + {tls_verify, M:ldap_tls_verify(H)}, + {tls_certfile, M:ldap_tls_certfile(H)}, + {tls_cacertfile, M:ldap_tls_cacertfile(H)}, + {tls_depth, M:ldap_tls_depth(H)}], + port = M:ldap_port(H), + dn = M:ldap_rootdn(H), + password = M:ldap_password(H), + base = M:ldap_base(H), + deref_aliases = M:ldap_deref_aliases(H)}). diff --git a/include/http_bind.hrl b/include/http_bind.hrl index cd8512f0e..ab1294e7d 100644 --- a/include/http_bind.hrl +++ b/include/http_bind.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/jlib.hrl b/include/jlib.hrl deleted file mode 100644 index 5a3c1634e..000000000 --- a/include/jlib.hrl +++ /dev/null @@ -1,501 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% -%%% ejabberd, Copyright (C) 2002-2016 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. -%%% -%%%---------------------------------------------------------------------- - --include("ns.hrl"). --include("fxml.hrl"). - --define(STANZA_ERROR(Code, Type, Condition), - #xmlel{name = <<"error">>, - attrs = [{<<"code">>, Code}, {<<"type">>, Type}], - children = - [#xmlel{name = Condition, - attrs = [{<<"xmlns">>, ?NS_STANZAS}], - children = []}]}). - --define(ERR_BAD_FORMAT, - ?STANZA_ERROR(<<"406">>, <<"modify">>, - <<"bad-format">>)). - --define(ERR_BAD_REQUEST, - ?STANZA_ERROR(<<"400">>, <<"modify">>, - <<"bad-request">>)). - --define(ERR_CONFLICT, - ?STANZA_ERROR(<<"409">>, <<"cancel">>, <<"conflict">>)). - --define(ERR_FEATURE_NOT_IMPLEMENTED, - ?STANZA_ERROR(<<"501">>, <<"cancel">>, - <<"feature-not-implemented">>)). - --define(ERR_FORBIDDEN, - ?STANZA_ERROR(<<"403">>, <<"auth">>, <<"forbidden">>)). - --define(ERR_GONE, - ?STANZA_ERROR(<<"302">>, <<"modify">>, <<"gone">>)). - --define(ERR_INTERNAL_SERVER_ERROR, - ?STANZA_ERROR(<<"500">>, <<"wait">>, - <<"internal-server-error">>)). - --define(ERR_ITEM_NOT_FOUND, - ?STANZA_ERROR(<<"404">>, <<"cancel">>, - <<"item-not-found">>)). - --define(ERR_JID_MALFORMED, - ?STANZA_ERROR(<<"400">>, <<"modify">>, - <<"jid-malformed">>)). - --define(ERR_NOT_ACCEPTABLE, - ?STANZA_ERROR(<<"406">>, <<"modify">>, - <<"not-acceptable">>)). - --define(ERR_NOT_ALLOWED, - ?STANZA_ERROR(<<"405">>, <<"cancel">>, - <<"not-allowed">>)). - --define(ERR_NOT_AUTHORIZED, - ?STANZA_ERROR(<<"401">>, <<"auth">>, - <<"not-authorized">>)). - --define(ERR_PAYMENT_REQUIRED, - ?STANZA_ERROR(<<"402">>, <<"auth">>, - <<"payment-required">>)). - --define(ERR_RECIPIENT_UNAVAILABLE, - ?STANZA_ERROR(<<"404">>, <<"wait">>, - <<"recipient-unavailable">>)). - --define(ERR_REDIRECT, - ?STANZA_ERROR(<<"302">>, <<"modify">>, <<"redirect">>)). - --define(ERR_REGISTRATION_REQUIRED, - ?STANZA_ERROR(<<"407">>, <<"auth">>, - <<"registration-required">>)). - --define(ERR_REMOTE_SERVER_NOT_FOUND, - ?STANZA_ERROR(<<"404">>, <<"cancel">>, - <<"remote-server-not-found">>)). - --define(ERR_REMOTE_SERVER_TIMEOUT, - ?STANZA_ERROR(<<"504">>, <<"wait">>, - <<"remote-server-timeout">>)). - --define(ERR_RESOURCE_CONSTRAINT, - ?STANZA_ERROR(<<"500">>, <<"wait">>, - <<"resource-constraint">>)). - --define(ERR_SERVICE_UNAVAILABLE, - ?STANZA_ERROR(<<"503">>, <<"cancel">>, - <<"service-unavailable">>)). - --define(ERR_SUBSCRIPTION_REQUIRED, - ?STANZA_ERROR(<<"407">>, <<"auth">>, - <<"subscription-required">>)). - --define(ERR_UNEXPECTED_REQUEST, - ?STANZA_ERROR(<<"400">>, <<"wait">>, - <<"unexpected-request">>)). - --define(ERR_UNEXPECTED_REQUEST_CANCEL, - ?STANZA_ERROR(<<"401">>, <<"cancel">>, - <<"unexpected-request">>)). - -%-define(ERR_, -% ?STANZA_ERROR("", "", "")). - --define(STANZA_ERRORT(Code, Type, Condition, Lang, - Text), - #xmlel{name = <<"error">>, - attrs = [{<<"code">>, Code}, {<<"type">>, Type}], - children = - [#xmlel{name = Condition, - attrs = [{<<"xmlns">>, ?NS_STANZAS}], children = []}, - #xmlel{name = <<"text">>, - attrs = [{<<"xmlns">>, ?NS_STANZAS}], - children = - [{xmlcdata, - translate:translate(Lang, Text)}]}]}). - --define(ERRT_BAD_FORMAT(Lang, Text), - ?STANZA_ERRORT(<<"406">>, <<"modify">>, - <<"bad-format">>, Lang, Text)). - --define(ERRT_BAD_REQUEST(Lang, Text), - ?STANZA_ERRORT(<<"400">>, <<"modify">>, - <<"bad-request">>, Lang, Text)). - --define(ERRT_CONFLICT(Lang, Text), - ?STANZA_ERRORT(<<"409">>, <<"cancel">>, <<"conflict">>, - Lang, Text)). - --define(ERRT_FEATURE_NOT_IMPLEMENTED(Lang, Text), - ?STANZA_ERRORT(<<"501">>, <<"cancel">>, - <<"feature-not-implemented">>, Lang, Text)). - --define(ERRT_FORBIDDEN(Lang, Text), - ?STANZA_ERRORT(<<"403">>, <<"auth">>, <<"forbidden">>, - Lang, Text)). - --define(ERRT_GONE(Lang, Text), - ?STANZA_ERRORT(<<"302">>, <<"modify">>, <<"gone">>, - Lang, Text)). - --define(ERRT_INTERNAL_SERVER_ERROR(Lang, Text), - ?STANZA_ERRORT(<<"500">>, <<"wait">>, - <<"internal-server-error">>, Lang, Text)). - --define(ERRT_ITEM_NOT_FOUND(Lang, Text), - ?STANZA_ERRORT(<<"404">>, <<"cancel">>, - <<"item-not-found">>, Lang, Text)). - --define(ERRT_JID_MALFORMED(Lang, Text), - ?STANZA_ERRORT(<<"400">>, <<"modify">>, - <<"jid-malformed">>, Lang, Text)). - --define(ERRT_NOT_ACCEPTABLE(Lang, Text), - ?STANZA_ERRORT(<<"406">>, <<"modify">>, - <<"not-acceptable">>, Lang, Text)). - --define(ERRT_NOT_ALLOWED(Lang, Text), - ?STANZA_ERRORT(<<"405">>, <<"cancel">>, - <<"not-allowed">>, Lang, Text)). - --define(ERRT_NOT_AUTHORIZED(Lang, Text), - ?STANZA_ERRORT(<<"401">>, <<"auth">>, - <<"not-authorized">>, Lang, Text)). - --define(ERRT_PAYMENT_REQUIRED(Lang, Text), - ?STANZA_ERRORT(<<"402">>, <<"auth">>, - <<"payment-required">>, Lang, Text)). - --define(ERRT_RECIPIENT_UNAVAILABLE(Lang, Text), - ?STANZA_ERRORT(<<"404">>, <<"wait">>, - <<"recipient-unavailable">>, Lang, Text)). - --define(ERRT_REDIRECT(Lang, Text), - ?STANZA_ERRORT(<<"302">>, <<"modify">>, <<"redirect">>, - Lang, Text)). - --define(ERRT_REGISTRATION_REQUIRED(Lang, Text), - ?STANZA_ERRORT(<<"407">>, <<"auth">>, - <<"registration-required">>, Lang, Text)). - --define(ERRT_REMOTE_SERVER_NOT_FOUND(Lang, Text), - ?STANZA_ERRORT(<<"404">>, <<"cancel">>, - <<"remote-server-not-found">>, Lang, Text)). - --define(ERRT_REMOTE_SERVER_TIMEOUT(Lang, Text), - ?STANZA_ERRORT(<<"504">>, <<"wait">>, - <<"remote-server-timeout">>, Lang, Text)). - --define(ERRT_RESOURCE_CONSTRAINT(Lang, Text), - ?STANZA_ERRORT(<<"500">>, <<"wait">>, - <<"resource-constraint">>, Lang, Text)). - --define(ERRT_SERVICE_UNAVAILABLE(Lang, Text), - ?STANZA_ERRORT(<<"503">>, <<"cancel">>, - <<"service-unavailable">>, Lang, Text)). - --define(ERRT_SUBSCRIPTION_REQUIRED(Lang, Text), - ?STANZA_ERRORT(<<"407">>, <<"auth">>, - <<"subscription-required">>, Lang, Text)). - --define(ERRT_UNEXPECTED_REQUEST(Lang, Text), - ?STANZA_ERRORT(<<"400">>, <<"wait">>, - <<"unexpected-request">>, Lang, Text)). - --define(ERR_AUTH_NO_RESOURCE_PROVIDED(Lang), - ?ERRT_NOT_ACCEPTABLE(Lang, <<"No resource provided">>)). - --define(ERR_AUTH_BAD_RESOURCE_FORMAT(Lang), - ?ERRT_NOT_ACCEPTABLE(Lang, - <<"Illegal resource format">>)). - --define(ERR_AUTH_RESOURCE_CONFLICT(Lang), - ?ERRT_CONFLICT(Lang, <<"Resource conflict">>)). - --define(STREAM_ERROR(Condition, Cdata), - #xmlel{name = <<"stream:error">>, attrs = [], - children = - [#xmlel{name = Condition, - attrs = [{<<"xmlns">>, ?NS_STREAMS}], - children = [{xmlcdata, Cdata}]}]}). - --define(SERR_BAD_FORMAT, - ?STREAM_ERROR(<<"bad-format">>, <<"">>)). - --define(SERR_BAD_NAMESPACE_PREFIX, - ?STREAM_ERROR(<<"bad-namespace-prefix">>, <<"">>)). - --define(SERR_CONFLICT, - ?STREAM_ERROR(<<"conflict">>, <<"">>)). - --define(SERR_CONNECTION_TIMEOUT, - ?STREAM_ERROR(<<"connection-timeout">>, <<"">>)). - --define(SERR_HOST_GONE, - ?STREAM_ERROR(<<"host-gone">>, <<"">>)). - --define(SERR_HOST_UNKNOWN, - ?STREAM_ERROR(<<"host-unknown">>, <<"">>)). - --define(SERR_IMPROPER_ADDRESSING, - ?STREAM_ERROR(<<"improper-addressing">>, <<"">>)). - --define(SERR_INTERNAL_SERVER_ERROR, - ?STREAM_ERROR(<<"internal-server-error">>, <<"">>)). - --define(SERR_INVALID_FROM, - ?STREAM_ERROR(<<"invalid-from">>, <<"">>)). - --define(SERR_INVALID_ID, - ?STREAM_ERROR(<<"invalid-id">>, <<"">>)). - --define(SERR_INVALID_NAMESPACE, - ?STREAM_ERROR(<<"invalid-namespace">>, <<"">>)). - --define(SERR_INVALID_XML, - ?STREAM_ERROR(<<"invalid-xml">>, <<"">>)). - --define(SERR_NOT_AUTHORIZED, - ?STREAM_ERROR(<<"not-authorized">>, <<"">>)). - --define(SERR_POLICY_VIOLATION, - ?STREAM_ERROR(<<"policy-violation">>, <<"">>)). - --define(SERR_REMOTE_CONNECTION_FAILED, - ?STREAM_ERROR(<<"remote-connection-failed">>, <<"">>)). - --define(SERR_RESOURSE_CONSTRAINT, - ?STREAM_ERROR(<<"resource-constraint">>, <<"">>)). - --define(SERR_RESTRICTED_XML, - ?STREAM_ERROR(<<"restricted-xml">>, <<"">>)). - --define(SERR_SEE_OTHER_HOST(Host), - ?STREAM_ERROR(<<"see-other-host">>, Host)). - --define(SERR_SYSTEM_SHUTDOWN, - ?STREAM_ERROR(<<"system-shutdown">>, <<"">>)). - --define(SERR_UNSUPPORTED_ENCODING, - ?STREAM_ERROR(<<"unsupported-encoding">>, <<"">>)). - --define(SERR_UNSUPPORTED_STANZA_TYPE, - ?STREAM_ERROR(<<"unsupported-stanza-type">>, <<"">>)). - --define(SERR_UNSUPPORTED_VERSION, - ?STREAM_ERROR(<<"unsupported-version">>, <<"">>)). - --define(SERR_XML_NOT_WELL_FORMED, - ?STREAM_ERROR(<<"xml-not-well-formed">>, <<"">>)). - -%-define(SERR_, -% ?STREAM_ERROR("", "")). - --define(STREAM_ERRORT(Condition, Cdata, Lang, Text), - #xmlel{name = <<"stream:error">>, attrs = [], - children = - [#xmlel{name = Condition, - attrs = [{<<"xmlns">>, ?NS_STREAMS}], - children = [{xmlcdata, Cdata}]}, - #xmlel{name = <<"text">>, - attrs = - [{<<"xml:lang">>, Lang}, - {<<"xmlns">>, ?NS_STREAMS}], - children = - [{xmlcdata, - translate:translate(Lang, Text)}]}]}). - --define(SERRT_BAD_FORMAT(Lang, Text), - ?STREAM_ERRORT(<<"bad-format">>, <<"">>, Lang, Text)). - --define(SERRT_BAD_NAMESPACE_PREFIX(Lang, Text), - ?STREAM_ERRORT(<<"bad-namespace-prefix">>, <<"">>, Lang, - Text)). - --define(SERRT_CONFLICT(Lang, Text), - ?STREAM_ERRORT(<<"conflict">>, <<"">>, Lang, Text)). - --define(SERRT_CONNECTION_TIMEOUT(Lang, Text), - ?STREAM_ERRORT(<<"connection-timeout">>, <<"">>, Lang, - Text)). - --define(SERRT_HOST_GONE(Lang, Text), - ?STREAM_ERRORT(<<"host-gone">>, <<"">>, Lang, Text)). - --define(SERRT_HOST_UNKNOWN(Lang, Text), - ?STREAM_ERRORT(<<"host-unknown">>, <<"">>, Lang, Text)). - --define(SERRT_IMPROPER_ADDRESSING(Lang, Text), - ?STREAM_ERRORT(<<"improper-addressing">>, <<"">>, Lang, - Text)). - --define(SERRT_INTERNAL_SERVER_ERROR(Lang, Text), - ?STREAM_ERRORT(<<"internal-server-error">>, <<"">>, - Lang, Text)). - --define(SERRT_INVALID_FROM(Lang, Text), - ?STREAM_ERRORT(<<"invalid-from">>, <<"">>, Lang, Text)). - --define(SERRT_INVALID_ID(Lang, Text), - ?STREAM_ERRORT(<<"invalid-id">>, <<"">>, Lang, Text)). - --define(SERRT_INVALID_NAMESPACE(Lang, Text), - ?STREAM_ERRORT(<<"invalid-namespace">>, <<"">>, Lang, - Text)). - --define(SERRT_INVALID_XML(Lang, Text), - ?STREAM_ERRORT(<<"invalid-xml">>, <<"">>, Lang, Text)). - --define(SERRT_NOT_AUTHORIZED(Lang, Text), - ?STREAM_ERRORT(<<"not-authorized">>, <<"">>, Lang, - Text)). - --define(SERRT_POLICY_VIOLATION(Lang, Text), - ?STREAM_ERRORT(<<"policy-violation">>, <<"">>, Lang, - Text)). - --define(SERRT_REMOTE_CONNECTION_FAILED(Lang, Text), - ?STREAM_ERRORT(<<"remote-connection-failed">>, <<"">>, - Lang, Text)). - --define(SERRT_RESOURSE_CONSTRAINT(Lang, Text), - ?STREAM_ERRORT(<<"resource-constraint">>, <<"">>, Lang, - Text)). - --define(SERRT_RESTRICTED_XML(Lang, Text), - ?STREAM_ERRORT(<<"restricted-xml">>, <<"">>, Lang, - Text)). - --define(SERRT_SEE_OTHER_HOST(Host, Lang, Text), - ?STREAM_ERRORT(<<"see-other-host">>, Host, Lang, Text)). - --define(SERRT_SYSTEM_SHUTDOWN(Lang, Text), - ?STREAM_ERRORT(<<"system-shutdown">>, <<"">>, Lang, - Text)). - --define(SERRT_UNSUPPORTED_ENCODING(Lang, Text), - ?STREAM_ERRORT(<<"unsupported-encoding">>, <<"">>, Lang, - Text)). - --define(SERRT_UNSUPPORTED_STANZA_TYPE(Lang, Text), - ?STREAM_ERRORT(<<"unsupported-stanza-type">>, <<"">>, - Lang, Text)). - --define(SERRT_UNSUPPORTED_VERSION(Lang, Text), - ?STREAM_ERRORT(<<"unsupported-version">>, <<"">>, Lang, - Text)). - --define(SERRT_XML_NOT_WELL_FORMED(Lang, Text), - ?STREAM_ERRORT(<<"xml-not-well-formed">>, <<"">>, Lang, - Text)). - --record(jid, {user = <<"">> :: binary(), - server = <<"">> :: binary(), - resource = <<"">> :: binary(), - luser = <<"">> :: binary(), - lserver = <<"">> :: binary(), - lresource = <<"">> :: binary()}). - --type(jid() :: #jid{}). - --type(ljid() :: {binary(), binary(), binary()}). - --record(iq, {id = <<"">> :: binary(), - type = get :: get | set | result | error, - xmlns = <<"">> :: binary(), - lang = <<"">> :: binary(), - sub_el = #xmlel{} :: xmlel() | [xmlel()]}). - --type(iq_get() - :: #iq{ - id :: binary(), - type :: get, - xmlns :: binary(), - lang :: binary(), - sub_el :: xmlel() - } -). - --type(iq_set() - :: #iq{ - id :: binary(), - type :: set, - xmlns :: binary(), - lang :: binary(), - sub_el :: xmlel() - } -). - --type iq_request() :: iq_get() | iq_set(). - --type(iq_result() - :: #iq{ - id :: binary(), - type :: result, - xmlns :: binary(), - lang :: binary(), - sub_el :: [xmlel()] - } -). - --type(iq_error() - :: #iq{ - id :: binary(), - type :: error, - xmlns :: binary(), - lang :: binary(), - sub_el :: [xmlel()] - } -). - --type iq_reply() :: iq_result() | iq_error() . - --type(iq() :: iq_request() | iq_reply()). - --record(rsm_in, {max :: integer() | error, - direction :: before | aft, - id :: binary(), - index :: integer() | error}). - --record(rsm_out, {count :: integer(), - index :: integer(), - first :: binary(), - last :: binary()}). - --type(rsm_in() :: #rsm_in{}). - --type(rsm_out() :: #rsm_out{}). - --type broadcast() :: {broadcast, broadcast_data()}. - --type broadcast_data() :: - {rebind, pid(), binary()} | %% ejabberd_c2s - {item, ljid(), mod_roster:subscription()} | %% mod_roster/mod_shared_roster - {exit, binary()} | %% mod_roster/mod_shared_roster - {privacy_list, mod_privacy:userlist(), binary()} | %% mod_privacy - {blocking, unblock_all | {block | unblock, [ljid()]}}. %% mod_blocking - --record(xmlelement, {name = "" :: string(), - attrs = [] :: [{string(), string()}], - children = [] :: [{xmlcdata, iodata()} | xmlelement()]}). - --type xmlelement() :: #xmlelement{}. diff --git a/include/logger.hrl b/include/logger.hrl index df0776863..e41ab73dd 100644 --- a/include/logger.hrl +++ b/include/logger.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -18,22 +18,68 @@ %%% %%%---------------------------------------------------------------------- -define(PRINT(Format, Args), io:format(Format, Args)). + +-ifdef(LAGER). -compile([{parse_transform, lager_transform}]). -define(DEBUG(Format, Args), - lager:debug(Format, Args)). + begin lager:debug(Format, Args), ok end). -define(INFO_MSG(Format, Args), - lager:info(Format, Args)). + begin lager:info(Format, Args), ok end). -define(WARNING_MSG(Format, Args), - lager:warning(Format, Args)). + begin lager:warning(Format, Args), ok end). -define(ERROR_MSG(Format, Args), - lager:error(Format, Args)). + begin lager:error(Format, Args), ok end). -define(CRITICAL_MSG(Format, Args), - lager:critical(Format, Args)). + begin lager:critical(Format, Args), ok end). +-else. +-include_lib("kernel/include/logger.hrl"). + +-define(CLEAD, "\e[1"). % bold +-define(CMID, "\e[0"). % normal +-define(CCLEAN, "\e[0m"). % clean + +-define(CDEFAULT, ";49;95m"). % light magenta +-define(CDEBUG, ";49;90m"). % dark gray +-define(CINFO, ";49;92m"). % green +-define(CWARNING, ";49;93m"). % light yellow +-define(CERROR, ";49;91m"). % light magenta +-define(CCRITICAL,";49;31m"). % light red + +-define(DEBUG(Format, Args), + begin ?LOG_DEBUG(Format, Args, + #{clevel => ?CLEAD ++ ?CDEBUG, + ctext => ?CMID ++ ?CDEBUG}), + ok end). + +-define(INFO_MSG(Format, Args), + begin ?LOG_INFO(Format, Args, + #{clevel => ?CLEAD ++ ?CINFO, + ctext => ?CCLEAN}), + ok end). + +-define(WARNING_MSG(Format, Args), + begin ?LOG_WARNING(Format, Args, + #{clevel => ?CLEAD ++ ?CWARNING, + ctext => ?CMID ++ ?CWARNING}), + ok end). + +-define(ERROR_MSG(Format, Args), + begin ?LOG_ERROR(Format, Args, + #{clevel => ?CLEAD ++ ?CERROR, + ctext => ?CMID ++ ?CERROR}), + ok end). + +-define(CRITICAL_MSG(Format, Args), + begin ?LOG_CRITICAL(Format, Args, + #{clevel => ?CLEAD++ ?CCRITICAL, + ctext => ?CMID ++ ?CCRITICAL}), + ok end). +-endif. %% Use only when trying to troubleshoot test problem with ExUnit -define(EXUNIT_LOG(Format, Args), @@ -41,3 +87,6 @@ false -> ok; _ -> 'Elixir.Logger':bare_log(error, io_lib:format(Format, Args), [?MODULE]) end). + +%% Uncomment if you want to debug p1_fsm/gen_fsm +%%-define(DBGFSM, true). diff --git a/include/mod_announce.hrl b/include/mod_announce.hrl index 83d72aaf1..77badf90e 100644 --- a/include/mod_announce.hrl +++ b/include/mod_announce.hrl @@ -1,3 +1,23 @@ +%%%---------------------------------------------------------------------- +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + -record(motd, {server = <<"">> :: binary(), packet = #xmlel{} :: xmlel()}). diff --git a/include/adhoc.hrl b/include/mod_antispam.hrl similarity index 54% rename from include/adhoc.hrl rename to include/mod_antispam.hrl index bd1bfaa6f..c30f24620 100644 --- a/include/adhoc.hrl +++ b/include/mod_antispam.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -18,27 +18,19 @@ %%% %%%---------------------------------------------------------------------- --record(adhoc_request, -{ - lang = <<"">> :: binary(), - node = <<"">> :: binary(), - sessionid = <<"">> :: binary(), - action = <<"">> :: binary(), - xdata = false :: false | xmlel(), - others = [] :: [xmlel()] -}). +-define(MODULE_ANTISPAM, mod_antispam). --record(adhoc_response, -{ - lang = <<"">> :: binary(), - node = <<"">> :: binary(), - sessionid = <<"">> :: binary(), - status :: atom(), - defaultaction = <<"">> :: binary(), - actions = [] :: [binary()], - notes = [] :: [{binary(), binary()}], - elements = [] :: [xmlel()] -}). +-type url() :: binary(). +-type filename() :: binary() | none | false. +-type jid_set() :: sets:set(ljid()). +-type url_set() :: sets:set(url()). --type adhoc_request() :: #adhoc_request{}. --type adhoc_response() :: #adhoc_response{}. +-define(DEFAULT_RTBL_DOMAINS_NODE, <<"spam_source_domains">>). + +-record(rtbl_service, + {host = none :: binary() | none, + node = ?DEFAULT_RTBL_DOMAINS_NODE :: binary(), + subscribed = false :: boolean(), + retry_timer = undefined :: reference() | undefined}). + +-type rtbl_service() :: #rtbl_service{}. diff --git a/include/mod_caps.hrl b/include/mod_caps.hrl index 067df9490..ee1bbe44e 100644 --- a/include/mod_caps.hrl +++ b/include/mod_caps.hrl @@ -1,3 +1,23 @@ +%%%---------------------------------------------------------------------- +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + -record(caps_features, {node_pair = {<<"">>, <<"">>} :: {binary(), binary()}, features = [] :: [binary()] | pos_integer() diff --git a/include/mod_carboncopy.hrl b/include/mod_carboncopy.hrl deleted file mode 100644 index df69938e8..000000000 --- a/include/mod_carboncopy.hrl +++ /dev/null @@ -1,4 +0,0 @@ --type matchspec_atom() :: '_' | '$1' | '$2' | '$3'. --record(carboncopy, {us :: {binary(), binary()} | matchspec_atom(), - resource :: binary() | matchspec_atom(), - version :: binary() | matchspec_atom()}). diff --git a/include/mod_irc.hrl b/include/mod_irc.hrl deleted file mode 100644 index b9696a88b..000000000 --- a/include/mod_irc.hrl +++ /dev/null @@ -1,15 +0,0 @@ --type conn_param() :: {binary(), binary(), inet:port_number(), binary()} | - {binary(), binary(), inet:port_number()} | - {binary(), binary()} | - {binary()}. - --type irc_data() :: [{username, binary()} | {connections_params, [conn_param()]}]. - --record(irc_connection, - {jid_server_host = {#jid{}, <<"">>, <<"">>} :: {jid(), binary(), binary()}, - pid = self() :: pid()}). - --record(irc_custom, - {us_host = {{<<"">>, <<"">>}, <<"">>} :: {{binary(), binary()}, - binary()}, - data = [] :: irc_data()}). diff --git a/include/mod_last.hrl b/include/mod_last.hrl index 494bf7b0c..b1c13621a 100644 --- a/include/mod_last.hrl +++ b/include/mod_last.hrl @@ -1,3 +1,23 @@ +%%%---------------------------------------------------------------------- +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + -record(last_activity, {us = {<<"">>, <<"">>} :: {binary(), binary()}, timestamp = 0 :: non_neg_integer(), status = <<"">> :: binary()}). diff --git a/include/mod_mam.hrl b/include/mod_mam.hrl index a2b92fca5..77ea54a5e 100644 --- a/include/mod_mam.hrl +++ b/include/mod_mam.hrl @@ -1,12 +1,33 @@ +%%%---------------------------------------------------------------------- +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + -record(archive_msg, - {us = {<<"">>, <<"">>} :: {binary(), binary()} | '$2', - id = <<>> :: binary() | '_', - timestamp = p1_time_compat:timestamp() :: erlang:timestamp() | '_' | '$1', - peer = {<<"">>, <<"">>, <<"">>} :: ljid() | '_' | '$3' | undefined, - bare_peer = {<<"">>, <<"">>, <<"">>} :: ljid() | '_' | '$3', - packet = #xmlel{} :: xmlel() | message() | '_', + {us = {<<"">>, <<"">>} :: {binary(), binary()}, + id = <<>> :: binary(), + timestamp = erlang:timestamp() :: erlang:timestamp(), + peer = {<<"">>, <<"">>, <<"">>} :: ljid() | undefined, + bare_peer = {<<"">>, <<"">>, <<"">>} :: ljid(), + packet = #xmlel{} :: xmlel() | message(), nick = <<"">> :: binary(), - type = chat :: chat | groupchat}). + type = chat :: chat | groupchat, + origin_id = <<"">> :: binary()}). -record(archive_prefs, {us = {<<"">>, <<"">>} :: {binary(), binary()}, diff --git a/src/randoms.erl b/include/mod_matrix_gw.hrl similarity index 53% rename from src/randoms.erl rename to include/mod_matrix_gw.hrl index ae477d27d..cdb272e8e 100644 --- a/src/randoms.erl +++ b/include/mod_matrix_gw.hrl @@ -1,11 +1,6 @@ %%%---------------------------------------------------------------------- -%%% File : randoms.erl -%%% Author : Alexey Shchepin -%%% Purpose : Random generation number wrapper -%%% Created : 13 Dec 2002 by Alexey Shchepin %%% -%%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -23,33 +18,19 @@ %%% %%%---------------------------------------------------------------------- --module(randoms). - --author('alexey@process-one.net'). - --export([get_string/0, uniform/0, uniform/1, bytes/1]). - --export([start/0]). - --define(THRESHOLD, 16#10000000000000000). - -start() -> - ok. - -get_string() -> - R = crypto:rand_uniform(0, ?THRESHOLD), - integer_to_binary(R). - -uniform() -> - crypto:rand_uniform(0, ?THRESHOLD)/?THRESHOLD. - -uniform(N) -> - crypto:rand_uniform(1, N+1). - --ifdef(STRONG_RAND_BYTES). -bytes(N) -> - crypto:strong_rand_bytes(N). --else. -bytes(N) -> - crypto:rand_bytes(N). --endif. +-record(room_version, + {id :: binary(), + %% use the same field names as in Synapse + enforce_key_validity :: boolean(), + special_case_aliases_auth :: boolean(), + strict_canonicaljson :: boolean(), + limit_notifications_power_levels :: boolean(), + knock_join_rule :: boolean(), + restricted_join_rule :: boolean(), + restricted_join_rule_fix :: boolean(), + knock_restricted_join_rule :: boolean(), + enforce_int_power_levels :: boolean(), + implicit_room_creator :: boolean(), + updated_redaction_rules :: boolean(), + hydra :: boolean() + }). diff --git a/include/mod_muc.hrl b/include/mod_muc.hrl index 8d0c1b09d..f801b29e1 100644 --- a/include/mod_muc.hrl +++ b/include/mod_muc.hrl @@ -1,7 +1,6 @@ %%%---------------------------------------------------------------------- -%%% File : mod_muc.hrl %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -23,11 +22,15 @@ {'_', binary()}, opts = [] :: list() | '_'}). --record(muc_online_room, - {name_host = {<<"">>, <<"">>} :: {binary(), binary()} | '$1' | - {'_', binary()} | '_', - pid = self() :: pid() | '$2' | '_' | '$1'}). - -record(muc_registered, {us_host = {{<<"">>, <<"">>}, <<"">>} :: {{binary(), binary()}, binary()} | '$1', nick = <<"">> :: binary()}). + +-record(muc_online_room, + {name_host :: {binary(), binary()} | '$1' | {'_', binary()} | '_', + pid :: pid() | '$2' | '_' | '$1'}). + +-record(muc_online_users, {us :: {binary(), binary()}, + resource :: binary() | '_', + room :: binary() | '_' | '$1', + host :: binary() | '_' | '$2'}). diff --git a/include/mod_muc_room.hrl b/include/mod_muc_room.hrl index dd414a8d8..5f81fe026 100644 --- a/include/mod_muc_room.hrl +++ b/include/mod_muc_room.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -18,22 +18,19 @@ %%% %%%---------------------------------------------------------------------- --include("ejabberd.hrl"). - -define(MAX_USERS_DEFAULT, 200). -define(SETS, gb_sets). --define(DICT, dict). - -record(lqueue, { - queue :: ?TQUEUE, - len :: integer(), - max :: integer() + queue = p1_queue:new() :: p1_queue:queue(lqueue_elem()), + max = 0 :: integer() }). -type lqueue() :: #lqueue{}. +-type lqueue_elem() :: {binary(), message(), boolean(), + erlang:timestamp(), non_neg_integer()}. -record(config, { @@ -41,13 +38,13 @@ description = <<"">> :: binary(), allow_change_subj = true :: boolean(), allow_query_users = true :: boolean(), - allow_private_messages = true :: boolean(), + allowpm = anyone :: anyone | participants | moderators | none, allow_private_messages_from_visitors = anyone :: anyone | moderators | nobody , allow_visitor_status = true :: boolean(), allow_visitor_nickchange = true :: boolean(), public = true :: boolean(), public_list = true :: boolean(), - persistent = false :: boolean(), + persistent = false :: boolean() | {destroying, boolean()}, moderated = true :: boolean(), captcha_protected = false :: boolean(), members_by_default = true :: boolean(), @@ -64,8 +61,12 @@ max_users = ?MAX_USERS_DEFAULT :: non_neg_integer() | none, logging = false :: boolean(), vcard = <<"">> :: binary(), - captcha_whitelist = (?SETS):empty() :: ?TGB_SET, - mam = false :: boolean() + vcard_xupdate = undefined :: undefined | external | binary(), + captcha_whitelist = (?SETS):empty() :: gb_sets:set(), + mam = false :: boolean(), + pubsub = <<"">> :: binary(), + enable_hats = false :: boolean(), + lang = ejabberd_option:language() :: binary() }). -type config() :: #config{}. @@ -80,21 +81,31 @@ role :: role(), %%is_subscriber = false :: boolean(), %%subscriptions = [] :: [binary()], - last_presence :: xmlel() + last_presence :: presence() | undefined }). -record(subscriber, {jid :: jid(), nick = <<>> :: binary(), nodes = [] :: [binary()]}). +-record(muc_subscribers, + {subscribers = #{} :: subscribers(), + subscriber_nicks = #{} :: subscriber_nicks(), + subscriber_nodes = #{} :: subscriber_nodes() + }). + +-type subscribers() :: #{ljid() => #subscriber{}}. +-type subscriber_nicks() :: #{binary() => [ljid()]}. +-type subscriber_nodes() :: #{binary() => subscribers()}. + -record(activity, { message_time = 0 :: integer(), presence_time = 0 :: integer(), - message_shaper :: shaper:shaper(), - presence_shaper :: shaper:shaper(), - message :: xmlel(), - presence :: {binary(), xmlel()} + message_shaper = none :: ejabberd_shaper:shaper(), + presence_shaper = none :: ejabberd_shaper:shaper(), + message :: message() | undefined, + presence :: {binary(), presence()} | undefined }). -record(state, @@ -102,28 +113,30 @@ room = <<"">> :: binary(), host = <<"">> :: binary(), server_host = <<"">> :: binary(), - access = {none,none,none,none} :: {atom(), atom(), atom(), atom()}, + access = {none,none,none,none,none} :: {atom(), atom(), atom(), atom(), atom()}, jid = #jid{} :: jid(), config = #config{} :: config(), - users = (?DICT):new() :: ?TDICT, - subscribers = (?DICT):new() :: ?TDICT, - subscriber_nicks = (?DICT):new() :: ?TDICT, + users = #{} :: users(), + muc_subscribers = #muc_subscribers{} :: #muc_subscribers{}, last_voice_request_time = treap:empty() :: treap:treap(), - robots = (?DICT):new() :: ?TDICT, - nicks = (?DICT):new() :: ?TDICT, - affiliations = (?DICT):new() :: ?TDICT, - history :: lqueue(), - subject = <<"">> :: binary(), - subject_author = <<"">> :: binary(), - just_created = false :: boolean(), + robots = #{} :: robots(), + nicks = #{} :: nicks(), + affiliations = #{} :: affiliations(), + roles = #{} :: roles(), + history = #lqueue{} :: lqueue(), + subject = [] :: [text()], + subject_author = {<<"">>, #jid{}} :: {binary(), jid()}, + hats_defs = #{} :: #{binary() => {binary(), binary()}}, + hats_users = #{} :: #{ljid() => [binary()]}, + just_created = erlang:system_time(microsecond) :: true | integer(), activity = treap:empty() :: treap:treap(), - room_shaper = none :: shaper:shaper(), - room_queue = queue:new() :: ?TQUEUE + room_shaper = none :: ejabberd_shaper:shaper(), + room_queue :: p1_queue:queue({message | presence, jid()}) | undefined, + hibernate_timer = none :: reference() | none | hibernating }). --record(muc_online_users, {us = {<<>>, <<>>} :: {binary(), binary()}, - resource = <<>> :: binary() | '_', - room = <<>> :: binary() | '_' | '$1', - host = <<>> :: binary() | '_' | '$2'}). - --type muc_online_users() :: #muc_online_users{}. +-type users() :: #{ljid() => #user{}}. +-type robots() :: #{jid() => {binary(), stanza()}}. +-type nicks() :: #{binary() => [ljid()]}. +-type affiliations() :: #{ljid() => affiliation() | {affiliation(), binary()}}. +-type roles() :: #{ljid() => role() | {role(), binary()}}. diff --git a/include/mod_offline.hrl b/include/mod_offline.hrl index cc644c4c2..e1bb236f6 100644 --- a/include/mod_offline.hrl +++ b/include/mod_offline.hrl @@ -1,10 +1,30 @@ +%%%---------------------------------------------------------------------- +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + -record(offline_msg, {us = {<<"">>, <<"">>} :: {binary(), binary()}, - timestamp = p1_time_compat:timestamp() :: erlang:timestamp() | '_', - expire = p1_time_compat:timestamp() :: erlang:timestamp() | never | '_', + timestamp :: erlang:timestamp() | '_' | undefined, + expire :: erlang:timestamp() | never | undefined | '_', from = #jid{} :: jid() | '_', to = #jid{} :: jid() | '_', - packet = #xmlel{} :: xmlel() | '_'}). + packet = #xmlel{} :: xmlel() | message() | '_'}). -record(state, {host = <<"">> :: binary(), diff --git a/include/mod_privacy.hrl b/include/mod_privacy.hrl index dbd19a081..8118a6de6 100644 --- a/include/mod_privacy.hrl +++ b/include/mod_privacy.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -38,11 +38,3 @@ -type listitem_type() :: none | jid | group | subscription. -type listitem_value() :: none | both | from | to | jid:ljid() | binary(). -type listitem_action() :: allow | deny. - --record(userlist, {name = none :: none | binary(), - list = [] :: [listitem()], - needdb = false :: boolean()}). - --type userlist() :: #userlist{}. - --export_type([userlist/0]). diff --git a/include/mod_private.hrl b/include/mod_private.hrl index d833af35f..05adc7d8b 100644 --- a/include/mod_private.hrl +++ b/include/mod_private.hrl @@ -1,3 +1,23 @@ +%%%---------------------------------------------------------------------- +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + -record(private_storage, {usns = {<<"">>, <<"">>, <<"">>} :: {binary(), binary(), binary() | '$1' | '_'}, diff --git a/include/mod_proxy65.hrl b/include/mod_proxy65.hrl index 1d8ceb3c6..4f017124a 100644 --- a/include/mod_proxy65.hrl +++ b/include/mod_proxy65.hrl @@ -2,7 +2,7 @@ %%% RFC 1928 constants. %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_push.hrl b/include/mod_push.hrl new file mode 100644 index 000000000..8a9de102b --- /dev/null +++ b/include/mod_push.hrl @@ -0,0 +1,24 @@ +%%%---------------------------------------------------------------------- +%%% ejabberd, Copyright (C) 2017-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- +-record(push_session, + {us = {<<"">>, <<"">>} :: {binary(), binary()}, + timestamp = erlang:timestamp() :: erlang:timestamp(), + service = {<<"">>, <<"">>, <<"">>} :: ljid(), + node = <<"">> :: binary(), + xml :: undefined | xmlel()}). diff --git a/include/mod_roster.hrl b/include/mod_roster.hrl index 818508703..a056dd22c 100644 --- a/include/mod_roster.hrl +++ b/include/mod_roster.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_shared_roster.hrl b/include/mod_shared_roster.hrl index 1f96b3034..4c35878e8 100644 --- a/include/mod_shared_roster.hrl +++ b/include/mod_shared_roster.hrl @@ -1,3 +1,23 @@ +%%%---------------------------------------------------------------------- +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + -record(sr_group, {group_host = {<<"">>, <<"">>} :: {'$1' | binary(), '$2' | binary()}, opts = [] :: list() | '_' | '$2'}). diff --git a/include/mod_vcard.hrl b/include/mod_vcard.hrl index 3bd62b2eb..d97e5c900 100644 --- a/include/mod_vcard.hrl +++ b/include/mod_vcard.hrl @@ -1,3 +1,23 @@ +%%%---------------------------------------------------------------------- +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + -record(vcard_search, {us, user, luser, fn, lfn, family, lfamily, given, lgiven, middle, lmiddle, nickname, lnickname, bday, diff --git a/include/mod_vcard_xupdate.hrl b/include/mod_vcard_xupdate.hrl deleted file mode 100644 index 8634597aa..000000000 --- a/include/mod_vcard_xupdate.hrl +++ /dev/null @@ -1,2 +0,0 @@ --record(vcard_xupdate, {us = {<<>>, <<>>} :: {binary(), binary()}, - hash = <<>> :: binary()}). diff --git a/include/mqtt.hrl b/include/mqtt.hrl new file mode 100644 index 000000000..bf910368f --- /dev/null +++ b/include/mqtt.hrl @@ -0,0 +1,209 @@ +%%%------------------------------------------------------------------- +%%% @author Evgeny Khramtsov +%%% @copyright (C) 2002-2025 ProcessOne, SARL. All Rights Reserved. +%%% +%%% Licensed under the Apache License, Version 2.0 (the "License"); +%%% you may not use this file except in compliance with the License. +%%% You may obtain a copy of the License at +%%% +%%% http://www.apache.org/licenses/LICENSE-2.0 +%%% +%%% Unless required by applicable law or agreed to in writing, software +%%% distributed under the License is distributed on an "AS IS" BASIS, +%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%%% See the License for the specific language governing permissions and +%%% limitations under the License. +%%% +%%%------------------------------------------------------------------- +-define(MQTT_VERSION_4, 4). +-define(MQTT_VERSION_5, 5). + +-record(connect, {proto_level = 4 :: non_neg_integer(), + will :: undefined | publish(), + clean_start = true :: boolean(), + keep_alive = 0 :: non_neg_integer(), + client_id = <<>> :: binary(), + username = <<>> :: binary(), + password = <<>> :: binary(), + will_properties = #{} :: properties(), + properties = #{} :: properties()}). +-record(connack, {session_present = false :: boolean(), + code = success :: reason_code(), + properties = #{} :: properties()}). + +-record(publish, {id :: undefined | non_neg_integer(), + dup = false :: boolean(), + qos = 0 :: qos(), + retain = false :: boolean(), + topic :: binary(), + payload :: binary(), + properties = #{} :: properties(), + meta = #{} :: map()}). +-record(puback, {id :: non_neg_integer(), + code = success :: reason_code(), + properties = #{} :: properties()}). +-record(pubrec, {id :: non_neg_integer(), + code = success :: reason_code(), + properties = #{} :: properties()}). +-record(pubrel, {id :: non_neg_integer(), + code = success :: reason_code(), + properties = #{} :: properties(), + meta = #{} :: map()}). +-record(pubcomp, {id :: non_neg_integer(), + code = success :: reason_code(), + properties = #{} :: properties()}). + +-record(subscribe, {id :: non_neg_integer(), + filters :: [{binary(), sub_opts()}], + properties = #{} :: properties(), + meta = #{} :: map()}). +-record(suback, {id :: non_neg_integer(), + codes = [] :: [char() | reason_code()], + properties = #{} :: properties()}). + +-record(unsubscribe, {id :: non_neg_integer(), + filters :: [binary()], + properties = #{} :: properties(), + meta = #{} :: map()}). +-record(unsuback, {id :: non_neg_integer(), + codes = [] :: [reason_code()], + properties = #{} :: properties()}). + +-record(pingreq, {meta = #{} :: map()}). +-record(pingresp, {}). + +-record(disconnect, {code = 'normal-disconnection' :: reason_code(), + properties = #{} :: properties()}). + +-record(auth, {code = success :: reason_code(), + properties = #{} :: properties()}). + +-record(sub_opts, {qos = 0 :: qos(), + no_local = false :: boolean(), + retain_as_published = false :: boolean(), + retain_handling = 0 :: 0..2}). + +-type qos() :: 0|1|2. +-type sub_opts() :: #sub_opts{}. +-type utf8_pair() :: {binary(), binary()}. +-type properties() :: #{assigned_client_identifier => binary(), + authentication_data => binary(), + authentication_method => binary(), + content_type => binary(), + correlation_data => binary(), + maximum_packet_size => pos_integer(), + maximum_qos => 0|1, + message_expiry_interval => non_neg_integer(), + payload_format_indicator => binary | utf8, + reason_string => binary(), + receive_maximum => pos_integer(), + request_problem_information => boolean(), + request_response_information => boolean(), + response_information => binary(), + response_topic => binary(), + retain_available => boolean(), + server_keep_alive => non_neg_integer(), + server_reference => binary(), + session_expiry_interval => non_neg_integer(), + shared_subscription_available => boolean(), + subscription_identifier => [non_neg_integer()] | non_neg_integer(), + subscription_identifiers_available => boolean(), + topic_alias => pos_integer(), + topic_alias_maximum => non_neg_integer(), + user_property => [utf8_pair()], + wildcard_subscription_available => boolean(), + will_delay_interval => non_neg_integer()}. +-type property() :: assigned_client_identifier | + authentication_data | + authentication_method | + content_type | + correlation_data | + maximum_packet_size | + maximum_qos | + message_expiry_interval | + payload_format_indicator | + reason_string | + receive_maximum | + request_problem_information | + request_response_information | + response_information | + response_topic | + retain_available | + server_keep_alive | + server_reference | + session_expiry_interval | + shared_subscription_available | + subscription_identifier | + subscription_identifiers_available | + topic_alias | + topic_alias_maximum | + user_property | + wildcard_subscription_available | + will_delay_interval. +-type reason_code() :: 'success' | + 'normal-disconnection' | + 'granted-qos-0' | + 'granted-qos-1' | + 'granted-qos-2' | + 'disconnect-with-will-message' | + 'no-matching-subscribers' | + 'no-subscription-existed' | + 'continue-authentication' | + 're-authenticate' | + 'unspecified-error' | + 'malformed-packet' | + 'protocol-error' | + 'implementation-specific-error' | + 'unsupported-protocol-version' | + 'client-identifier-not-valid' | + 'bad-user-name-or-password' | + 'not-authorized' | + 'server-unavailable' | + 'server-busy' | + 'banned' | + 'server-shutting-down' | + 'bad-authentication-method' | + 'keep-alive-timeout' | + 'session-taken-over' | + 'topic-filter-invalid' | + 'topic-name-invalid' | + 'packet-identifier-in-use' | + 'packet-identifier-not-found' | + 'receive-maximum-exceeded' | + 'topic-alias-invalid' | + 'packet-too-large' | + 'message-rate-too-high' | + 'quota-exceeded' | + 'administrative-action' | + 'payload-format-invalid' | + 'retain-not-supported' | + 'qos-not-supported' | + 'use-another-server' | + 'server-moved' | + 'shared-subscriptions-not-supported' | + 'connection-rate-exceeded' | + 'maximum-connect-time' | + 'subscription-identifiers-not-supported' | + 'wildcard-subscriptions-not-supported'. + +-type connect() :: #connect{}. +-type connack() :: #connack{}. +-type publish() :: #publish{}. +-type puback() :: #puback{}. +-type pubrel() :: #pubrel{}. +-type pubrec() :: #pubrec{}. +-type pubcomp() :: #pubcomp{}. +-type subscribe() :: #subscribe{}. +-type suback() :: #suback{}. +-type unsubscribe() :: #unsubscribe{}. +-type unsuback() :: #unsuback{}. +-type pingreq() :: #pingreq{}. +-type pingresp() :: #pingresp{}. +-type disconnect() :: #disconnect{}. +-type auth() :: #auth{}. + +-type mqtt_packet() :: connect() | connack() | publish() | puback() | + pubrel() | pubrec() | pubcomp() | subscribe() | + suback() | unsubscribe() | unsuback() | pingreq() | + pingresp() | disconnect() | auth(). +-type mqtt_version() :: ?MQTT_VERSION_4 | ?MQTT_VERSION_5. diff --git a/include/pubsub.hrl b/include/pubsub.hrl index a05807247..316be342a 100644 --- a/include/pubsub.hrl +++ b/include/pubsub.hrl @@ -1,40 +1,33 @@ -%%% ==================================================================== -%%% ``The contents of this file are subject to the Erlang Public License, -%%% Version 1.1, (the "License"); you may not use this file except in -%%% compliance with the License. You should have received a copy of the -%%% Erlang Public License along with this software. If not, it can be -%%% retrieved via the world wide web at http://www.erlang.org/. -%%% +%%%---------------------------------------------------------------------- %%% -%%% Software distributed under the License is distributed on an "AS IS" -%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%%% the License for the specific language governing rights and limitations -%%% under the License. -%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% -%%% The Initial Developer of the Original Code is ProcessOne. -%%% Portions created by ProcessOne are Copyright 2006-2016, ProcessOne -%%% All Rights Reserved.'' -%%% This software is copyright 2006-2016, 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. %%% -%%% copyright 2006-2016 ProcessOne +%%% 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. %%% -%%% This file contains pubsub types definition. -%%% ==================================================================== - --include("ejabberd.hrl"). +%%%---------------------------------------------------------------------- %% ------------------------------- %% Pubsub constants -define(ERR_EXTENDED(E, C), mod_pubsub:extended_error(E, C)). %% The actual limit can be configured with mod_pubsub's option max_items_node --define(MAXITEMS, 10). +-define(MAXITEMS, 1000). %% this is currently a hard limit. -%% Would be nice to have it configurable. --define(MAX_PAYLOAD_SIZE, 60000). +%% Would be nice to have it configurable. +-define(MAX_PAYLOAD_SIZE, 250000). %% ------------------------------- %% Pubsub types @@ -65,7 +58,7 @@ %% note: pos_integer() should always be used, but we allow anything else coded %% as binary, so one can have a custom implementation of nodetree with custom %% indexing (see nodetree_virtual). this also allows to use any kind of key for -%% indexing nodes, as this can be usefull with external backends such as sql. +%% indexing nodes, as this can be useful with external backends such as sql. -type(itemId() :: binary()). %% @type itemId() = string(). @@ -91,7 +84,7 @@ Value::binary() | [binary()] | boolean() }). --type(subOptions() :: [mod_pubsub:subOption(),...]). +-type(subOptions() :: [mod_pubsub:subOption()]). -type(pubOption() :: {Option::binary(), @@ -109,13 +102,6 @@ ). %% @type affiliation() = 'none' | 'owner' | 'publisher' | 'publish-only' | 'member' | 'outcast'. --type(subscription() :: 'none' - | 'pending' - | 'unconfigured' - | 'subscribed' -). -%% @type subscription() = 'none' | 'pending' | 'unconfigured' | 'subscribed'. - -type(accessModel() :: 'open' | 'presence' | 'roster' @@ -143,13 +129,14 @@ id ,% :: mod_pubsub:nodeIdx(), parents = [] ,% :: [mod_pubsub:nodeId(),...], type = <<"flat">>,% :: binary(), - owners = [] ,% :: [jlib:ljid(),...], + owners = [] ,% :: [jid:ljid(),...], options = [] % :: mod_pubsub:nodeOptions() }). -record(pubsub_state, { - stateid ,% :: {jlib:ljid(), mod_pubsub:nodeIdx()}, + stateid ,% :: {jid:ljid(), mod_pubsub:nodeIdx()}, + nodeidx ,% :: mod_pubsub:nodeIdx(), items = [] ,% :: [mod_pubsub:itemId(),...], affiliation = 'none',% :: mod_pubsub:affiliation(), subscriptions = [] % :: [{mod_pubsub:subscription(), mod_pubsub:subId()}] @@ -158,8 +145,9 @@ -record(pubsub_item, { itemid ,% :: {mod_pubsub:itemId(), mod_pubsub:nodeIdx()}, - creation = {unknown, unknown},% :: {erlang:timestamp(), jlib:ljid()}, - modification = {unknown, unknown},% :: {erlang:timestamp(), jlib:ljid()}, + nodeidx ,% :: mod_pubsub:nodeIdx(), + creation = {unknown, unknown},% :: {erlang:timestamp(), jid:ljid()}, + modification = {unknown, unknown},% :: {erlang:timestamp(), jid:ljid()}, payload = [] % :: mod_pubsub:payload() }). @@ -171,8 +159,14 @@ -record(pubsub_last_item, { - nodeid ,% :: mod_pubsub:nodeIdx(), + nodeid ,% :: {binary(), mod_pubsub:nodeIdx()}, itemid ,% :: mod_pubsub:itemId(), - creation ,% :: {erlang:timestamp(), jlib:ljid()}, + creation ,% :: {erlang:timestamp(), jid:ljid()}, payload % :: mod_pubsub:payload() }). + +-record(pubsub_orphan, +{ + nodeid ,% :: mod_pubsub:nodeIdx(), + items = [] % :: list() +}). diff --git a/include/translate.hrl b/include/translate.hrl new file mode 100644 index 000000000..b0e50e7d6 --- /dev/null +++ b/include/translate.hrl @@ -0,0 +1 @@ +-define(T(S), <>). diff --git a/install-sh b/install-sh old mode 100644 new mode 100755 diff --git a/lib/ct_formatter.ex b/lib/ct_formatter.ex deleted file mode 100644 index 0c301353b..000000000 --- a/lib/ct_formatter.ex +++ /dev/null @@ -1,130 +0,0 @@ -defmodule ExUnit.CTFormatter do - @moduledoc false - - use GenEvent - - import ExUnit.Formatter, only: [format_time: 2, format_test_failure: 5, - format_test_case_failure: 5] - - def init(opts) do - file = File.open! "exunit.log", [:append] - # We do not print filter in log file as exclusion of test with tag - # pending: true is always done - config = %{ - file: file, - seed: opts[:seed], - trace: opts[:trace], - colors: Keyword.put_new(opts[:colors], :enabled, false), - width: 80, - tests_counter: 0, - failures_counter: 0, - skipped_counter: 0, - invalids_counter: 0 - } - {:ok, config} - end - - def handle_event({:suite_started, _opts}, config) do - {:ok, config} - end - - def handle_event({:suite_finished, run_us, load_us}, config) do - print_suite(config, run_us, load_us) - File.close config[:file] - :remove_handler - end - - def handle_event({:test_started, %ExUnit.Test{} = test}, config) do - if config.tests_counter == 0, do: IO.binwrite config[:file], "== Running #{test.case} ==\n\n" - {:ok, config} - end - - def handle_event({:test_finished, %ExUnit.Test{state: nil} = _test}, config) do - IO.binwrite config[:file], "." - {:ok, %{config | tests_counter: config.tests_counter + 1}} - end - - def handle_event({:test_finished, %ExUnit.Test{state: {:skip, _}} = _test}, config) do - {:ok, %{config | tests_counter: config.tests_counter + 1, - skipped_counter: config.skipped_counter + 1}} - end - - def handle_event({:test_finished, %ExUnit.Test{state: {:invalid, _}} = _test}, config) do - IO.binwrite config[:file], "?" - {:ok, %{config | tests_counter: config.tests_counter + 1, - invalids_counter: config.invalids_counter + 1}} - end - - def handle_event({:test_finished, %ExUnit.Test{state: {:failed, failures}} = test}, config) do - formatted = format_test_failure(test, failures, config.failures_counter + 1, - config.width, &formatter(&1, &2, config)) - print_failure(formatted, config) - print_logs(test.logs) - - {:ok, %{config | tests_counter: config.tests_counter + 1, - failures_counter: config.failures_counter + 1}} - end - - def handle_event({:case_started, %ExUnit.TestCase{}}, config) do - {:ok, config} - end - - def handle_event({:case_finished, %ExUnit.TestCase{state: nil}}, config) do - {:ok, config} - end - - def handle_event({:case_finished, %ExUnit.TestCase{state: {:failed, failures}} = test_case}, config) do - formatted = format_test_case_failure(test_case, failures, config.failures_counter + 1, - config.width, &formatter(&1, &2, config)) - print_failure(formatted, config) - {:ok, %{config | failures_counter: config.failures_counter + 1}} - end - - ## Printing - - defp print_suite(config, run_us, load_us) do - IO.binwrite config[:file], "\n\n" - IO.binwrite config[:file], format_time(run_us, load_us) - IO.binwrite config[:file], "\n\n" - - # singular/plural - test_pl = pluralize(config.tests_counter, "test", "tests") - failure_pl = pluralize(config.failures_counter, "failure", "failures") - - message = - "#{config.tests_counter} #{test_pl}, #{config.failures_counter} #{failure_pl}" - |> if_true(config.skipped_counter > 0, & &1 <> ", #{config.skipped_counter} skipped") - |> if_true(config.invalids_counter > 0, & &1 <> ", #{config.invalids_counter} invalid") - - cond do - config.failures_counter > 0 -> IO.binwrite config[:file], message - config.invalids_counter > 0 -> IO.binwrite config[:file], message - true -> IO.binwrite config[:file], message - end - - IO.binwrite config[:file], "\nRandomized with seed #{config.seed}\n\n\n\n" - end - - defp if_true(value, false, _fun), do: value - defp if_true(value, true, fun), do: fun.(value) - - defp print_failure(formatted, config) do - IO.binwrite config[:file], "\n" - IO.binwrite config[:file], formatted - IO.binwrite config[:file], "\n" - end - - defp formatter(_, msg, _config), - do: msg - - defp pluralize(1, singular, _plural), do: singular - defp pluralize(_, _singular, plural), do: plural - - defp print_logs(""), do: nil - - defp print_logs(output) do - indent = "\n " - output = String.replace(output, "\n", indent) - IO.puts([" The following output was logged:", indent | output]) - end -end diff --git a/lib/ejabberd/config/attr.ex b/lib/ejabberd/config/attr.ex index 9d17b157d..85d19191b 100644 --- a/lib/ejabberd/config/attr.ex +++ b/lib/ejabberd/config/attr.ex @@ -41,7 +41,7 @@ defmodule Ejabberd.Config.Attr do """ @spec validate([attr]) :: [{:ok, attr}] | [{:error, attr, atom()}] def validate(attrs) when is_list(attrs), do: Enum.map(attrs, &valid_attr?/1) - def validate(attr), do: validate([attr]) |> List.first + def validate(attr), do: validate([attr]) @doc """ Returns the type of an attribute, given its name. diff --git a/lib/ejabberd/config/config.ex b/lib/ejabberd/config/config.ex index 4d1270bc1..a8805e612 100644 --- a/lib/ejabberd/config/config.ex +++ b/lib/ejabberd/config/config.ex @@ -23,7 +23,7 @@ defmodule Ejabberd.Config do # Could be also possible to interrupt the compilation&execution by throwing # an exception if necessary. def __before_compile__(_env) do - get_modules_parsed_in_order + get_modules_parsed_in_order() |> EjabberdModule.validate |> EjabberdLogger.log_errors end @@ -36,8 +36,8 @@ defmodule Ejabberd.Config do case force do true -> - Ejabberd.Config.Store.stop - Ejabberd.Config.Store.start_link + Ejabberd.Config.Store.stop() + Ejabberd.Config.Store.start_link() do_init(file_path) false -> if not init_already_executed, do: do_init(file_path) @@ -48,9 +48,9 @@ defmodule Ejabberd.Config do Returns a list with all the opts, formatted for ejabberd. """ def get_ejabberd_opts do - get_general_opts - |> Dict.put(:modules, get_modules_parsed_in_order()) - |> Dict.put(:listeners, get_listeners_parsed_in_order()) + get_general_opts() + |> Map.put(:modules, get_modules_parsed_in_order()) + |> Map.put(:listeners, get_listeners_parsed_in_order()) |> Ejabberd.Config.OptsFormatter.format_opts_for_ejabberd end @@ -105,11 +105,8 @@ defmodule Ejabberd.Config do Code.eval_file(file_path) |> extract_and_store_module_name() # Getting start/0 config - Ejabberd.Config.Store.get(:module_name) - |> case do - nil -> IO.puts "[ ERR ] Configuration module not found." - [module] -> call_start_func_and_store_data(module) - end + [module] = Ejabberd.Config.Store.get(:module_name) + call_start_func_and_store_data(module) # Fetching git modules and install them get_modules_parsed_in_order() diff --git a/lib/ejabberd/config/ejabberd_hook.ex b/lib/ejabberd/config/ejabberd_hook.ex index 8b7858d23..5f9de4aa0 100644 --- a/lib/ejabberd/config/ejabberd_hook.ex +++ b/lib/ejabberd/config/ejabberd_hook.ex @@ -13,7 +13,6 @@ defmodule Ejabberd.Config.EjabberdHook do @doc """ Register a hook to ejabberd. """ - @spec start(EjabberdHook.t) :: none def start(%EjabberdHook{hook: hook, opts: opts, fun: fun}) do host = Keyword.get(opts, :host, :global) priority = Keyword.get(opts, :priority, 50) diff --git a/lib/ejabberd/config/ejabberd_module.ex b/lib/ejabberd/config/ejabberd_module.ex index 4de9a302e..6d5b1e467 100644 --- a/lib/ejabberd/config/ejabberd_module.ex +++ b/lib/ejabberd/config/ejabberd_module.ex @@ -7,14 +7,14 @@ defmodule Ejabberd.Config.EjabberdModule do the already existing Elixir.Module. """ - @type t :: %{module: atom, attrs: [Attr.t]} + alias Ejabberd.Config.EjabberdModule + alias Ejabberd.Config.Validation + alias Ejabberd.Config.Attr + + @type t :: %{module: atom, attrs: [Attr.attr]} defstruct [:module, :attrs] - alias Ejabberd.Config.EjabberdModule - alias Ejabberd.Config.Attr - alias Ejabberd.Config.Validation - @doc """ Given a list of modules / single module it runs different validators on them. @@ -30,7 +30,6 @@ defmodule Ejabberd.Config.EjabberdModule do a git attribute and tries to fetch the repo, then, it install them through :ext_mod.install/1 """ - @spec fetch_git_repos([EjabberdModule.t]) :: none() def fetch_git_repos(modules) do modules |> Enum.filter(&is_git_module?/1) @@ -61,7 +60,7 @@ defmodule Ejabberd.Config.EjabberdModule do defp fetch_and_store_repo_source_if_not_exists(path, repo) do unless File.exists?(path) do IO.puts "[info] Fetching: #{repo}" - :os.cmd('git clone #{repo} #{path}') + :os.cmd(~c"git clone #{repo} #{path}") end end diff --git a/lib/ejabberd/config/logger/ejabberd_logger.ex b/lib/ejabberd/config/logger/ejabberd_logger.ex index 270fbfaa6..822571916 100644 --- a/lib/ejabberd/config/logger/ejabberd_logger.ex +++ b/lib/ejabberd/config/logger/ejabberd_logger.ex @@ -17,11 +17,11 @@ defmodule Ejabberd.Config.EjabberdLogger do end defp do_log_errors({:ok, _mod}), do: nil - defp do_log_errors({:error, _mod, errors}), do: Enum.each errors, &do_log_errors/1 - defp do_log_errors({:attribute, errors}), do: Enum.each errors, &log_attribute_error/1 - defp do_log_errors({:dependency, errors}), do: Enum.each errors, &log_dependency_error/1 + defp do_log_errors({:error, _mod, errors}), do: (Enum.each errors, &do_log_errors/1) + defp do_log_errors({:attribute, errors}), do: (Enum.each errors, &log_attribute_error/1) + defp do_log_errors({:dependency, errors}), do: (Enum.each errors, &log_dependency_error/1) - defp log_attribute_error({{attr_name, val}, :attr_not_supported}), do: + defp log_attribute_error({{attr_name, _val}, :attr_not_supported}), do: IO.puts "[ WARN ] Annotation @#{attr_name} is not supported." defp log_attribute_error({{attr_name, val}, :type_not_supported}), do: diff --git a/lib/ejabberd/config/opts_formatter.ex b/lib/ejabberd/config/opts_formatter.ex index b7010ddfe..3d3db926f 100644 --- a/lib/ejabberd/config/opts_formatter.ex +++ b/lib/ejabberd/config/opts_formatter.ex @@ -14,15 +14,12 @@ defmodule Ejabberd.Config.OptsFormatter do Look at how Config.get_ejabberd_opts/0 is constructed for more informations. """ - @spec format_opts_for_ejabberd([{atom(), any()}]) :: list() + @spec format_opts_for_ejabberd(map) :: list() def format_opts_for_ejabberd(opts) do opts |> format_attrs_for_ejabberd end - defp format_attrs_for_ejabberd(opts) when is_list(opts), - do: Enum.map opts, &format_attrs_for_ejabberd/1 - defp format_attrs_for_ejabberd({:listeners, mods}), do: {:listen, format_listeners_for_ejabberd(mods)} @@ -32,6 +29,9 @@ defmodule Ejabberd.Config.OptsFormatter do defp format_attrs_for_ejabberd({key, opts}) when is_atom(key), do: {key, opts} + defp format_attrs_for_ejabberd(opts), + do: (Enum.map opts, &format_attrs_for_ejabberd/1) + defp format_mods_for_ejabberd(mods) do Enum.map mods, fn %EjabberdModule{module: mod, attrs: attrs} -> {mod, attrs[:opts]} diff --git a/lib/ejabberd/config/validator/validation.ex b/lib/ejabberd/config/validator/validation.ex index 2fe00361a..227a3545f 100644 --- a/lib/ejabberd/config/validator/validation.ex +++ b/lib/ejabberd/config/validator/validation.ex @@ -3,14 +3,12 @@ defmodule Ejabberd.Config.Validation do Module used to validate a list of modules. """ + alias Ejabberd.Config.EjabberdModule + alias Ejabberd.Config.Validator + @type mod_validation :: {[EjabberdModule.t], EjabberdModule.t, map} @type mod_validation_result :: {:ok, EjabberdModule.t} | {:error, EjabberdModule.t, map} - alias Ejabberd.Config.EjabberdModule - alias Ejabberd.Config.Attr - alias Ejabberd.Config.Validator - alias Ejabberd.Config.ValidatorUtility - @doc """ Given a module or a list of modules it runs validators on them and returns {:ok, mod} or {:error, mod, errors}, for each diff --git a/lib/ejabberd/config/validator/validator_attrs.ex b/lib/ejabberd/config/validator/validator_attrs.ex index 94117ab21..e0e133b61 100644 --- a/lib/ejabberd/config/validator/validator_attrs.ex +++ b/lib/ejabberd/config/validator/validator_attrs.ex @@ -3,11 +3,12 @@ defmodule Ejabberd.Config.Validator.Attrs do Validator module used to validate attributes. """ - # TODO: Duplicated from validator.ex !!! - @type mod_validation :: {[EjabberdModule.t], EjabberdModule.t, map} - import Ejabberd.Config.ValidatorUtility alias Ejabberd.Config.Attr + alias Ejabberd.Config.EjabberdModule + + # TODO: Duplicated from validator.ex !!! + @type mod_validation :: {[EjabberdModule.t], EjabberdModule.t, map} @doc """ Given a module (with the form used for validation) @@ -17,9 +18,9 @@ defmodule Ejabberd.Config.Validator.Attrs do @spec validate(mod_validation) :: mod_validation def validate({modules, mod, errors}) do errors = Enum.reduce mod.attrs, errors, fn(attr, err) -> - case Attr.validate(attr) do - {:ok, attr} -> err - {:error, attr, cause} -> put_error(err, :attribute, {attr, cause}) + case Attr.validate([attr]) do + [{:ok, _attr}] -> err + [{:error, attr, cause}] -> put_error(err, :attribute, {attr, cause}) end end diff --git a/lib/ejabberd/config/validator/validator_dependencies.ex b/lib/ejabberd/config/validator/validator_dependencies.ex index d44c8a136..4eb466663 100644 --- a/lib/ejabberd/config/validator/validator_dependencies.ex +++ b/lib/ejabberd/config/validator/validator_dependencies.ex @@ -4,6 +4,8 @@ defmodule Ejabberd.Config.Validator.Dependencies do with the @dependency annotation. """ + alias Ejabberd.Config.EjabberdModule + # TODO: Duplicated from validator.ex !!! @type mod_validation :: {[EjabberdModule.t], EjabberdModule.t, map} import Ejabberd.Config.ValidatorUtility diff --git a/lib/ejabberd/config/validator/validator_utility.ex b/lib/ejabberd/config/validator/validator_utility.ex index 17805f748..6047618b6 100644 --- a/lib/ejabberd/config/validator/validator_utility.ex +++ b/lib/ejabberd/config/validator/validator_utility.ex @@ -4,8 +4,6 @@ defmodule Ejabberd.Config.ValidatorUtility do Imports utility functions for working with validation structures. """ - alias Ejabberd.Config.EjabberdModule - @doc """ Inserts an error inside the errors collection, for the given key. If the key doesn't exists then it creates an empty collection @@ -22,7 +20,6 @@ defmodule Ejabberd.Config.ValidatorUtility do Given a list of modules it extracts and returns a list of the module names (which are Elixir.Module). """ - @spec extract_module_names(EjabberdModule.t) :: [atom] def extract_module_names(modules) when is_list(modules) do modules |> Enum.map(&Map.get(&1, :module)) diff --git a/lib/ejabberd/config_util.ex b/lib/ejabberd/config_util.ex index 6592104a2..71d854f15 100644 --- a/lib/ejabberd/config_util.ex +++ b/lib/ejabberd/config_util.ex @@ -7,7 +7,7 @@ defmodule Ejabberd.ConfigUtil do @doc """ Returns true when the config file is based on elixir. """ - @spec is_elixir_config(list) :: boolean + @spec is_elixir_config(binary) :: boolean def is_elixir_config(filename) when is_list(filename) do is_elixir_config(to_string(filename)) end diff --git a/lib/ejabberd/logger.ex b/lib/ejabberd/logger.ex index bef1cb3aa..3f97b4ccc 100644 --- a/lib/ejabberd/logger.ex +++ b/lib/ejabberd/logger.ex @@ -1,9 +1,9 @@ defmodule Ejabberd.Logger do - def critical(message, args \\ []), do: :lager.log(:critical, [], message, args) - def error(message, args \\ []), do: :lager.log(:error, [], message, args) - def warning(message, args \\ []), do: :lager.log(:warning, [], message, args) - def info(message, args \\ []), do: :lager.log(:info, [], message, args) - def debug(message, args \\ []), do: :lager.log(:debug, [], message, args) + def critical(message, args \\ []), do: :logger.critical(message, args) + def error(message, args \\ []), do: :logger.error(message, args) + def warning(message, args \\ []), do: :logger.warning(message, args) + def info(message, args \\ []), do: :logger.info(message, args) + def debug(message, args \\ []), do: :logger.debug( message, args) end diff --git a/lib/ejabberd/module.ex b/lib/ejabberd/module.ex deleted file mode 100644 index 9fb3f040c..000000000 --- a/lib/ejabberd/module.ex +++ /dev/null @@ -1,19 +0,0 @@ -defmodule Ejabberd.Module do - - defmacro __using__(opts) do - logger_enabled = Keyword.get(opts, :logger, true) - - quote do - @behaviour :gen_mod - import Ejabberd.Module - - unquote(if logger_enabled do - quote do: import Ejabberd.Logger - end) - end - end - - # gen_mod callbacks - def depends(_host, _opts), do: [] - def mod_opt_type(_), do: [] -end diff --git a/lib/ejabberd_auth_example.ex b/lib/ejabberd_auth_example.ex new file mode 100644 index 000000000..5bc37e093 --- /dev/null +++ b/lib/ejabberd_auth_example.ex @@ -0,0 +1,44 @@ +defmodule Ejabberd.Auth.Example do + + @moduledoc """ + Example ejabberd auth method written in Elixir. + + This is an example to demonstrate the usage of Elixir to + create ejabberd auth methods. + + Example configuration: + auth_method: 'Ejabberd.Auth.Example' + """ + + @behaviour :ejabberd_auth + import Ejabberd.Logger + + @impl true + def start(host) do + info("Starting Ejabberd.Auth.Example to authenticate '#{host}' users") + nil + end + + @impl true + def stop(host) do + info("Stopping Ejabberd.Auth.Example to authenticate '#{host}' users") + nil + end + + @impl true + def check_password("alice", _authz_id, _host, "secret"), do: {:nocache, true} + def check_password(_username, _authz_id, _host, _secret), do: {:nocache, false} + + @impl true + def user_exists("alice", _host), do: {:nocache, true} + def user_exists(_username, _host), do: {:nocache, false} + + @impl true + def plain_password_required(_binary), do: true + + @impl true + def store_type(_host), do: :external + + @impl true + def use_cache(_host), do: false +end diff --git a/lib/mix/tasks/deps.tree.ex b/lib/mix/tasks/deps.tree.ex index 94cb85a50..e93b4aa48 100644 --- a/lib/mix/tasks/deps.tree.ex +++ b/lib/mix/tasks/deps.tree.ex @@ -14,15 +14,15 @@ defmodule Mix.Tasks.Ejabberd.Deps.Tree do def run(_argv) do # First we need to start manually the store to be available # during the compilation of the config file. - Ejabberd.Config.Store.start_link - Ejabberd.Config.init(:ejabberd_config.get_ejabberd_config_path()) + Ejabberd.Config.Store.start_link() + Ejabberd.Config.init(:ejabberd_config.path()) - Mix.shell.info "ejabberd modules" + Mix.shell().info "ejabberd modules" Ejabberd.Config.Store.get(:modules) |> Enum.reverse # Because of how mods are stored inside the store |> format_mods - |> Mix.shell.info + |> Mix.shell().info end defp format_mods(mods) when is_list(mods) do @@ -40,7 +40,7 @@ defmodule Mix.Tasks.Ejabberd.Deps.Tree do end end - defp build_dependency_tree(mods, mod, []), do: %{module: mod, dependency: []} + defp build_dependency_tree(_mods, mod, []), do: %{module: mod, dependency: []} defp build_dependency_tree(mods, mod, deps) when is_list(deps) do dependencies = Enum.map deps, fn dep -> dep_deps = get_dependencies_of_mod(mods, dep) @@ -65,7 +65,7 @@ defmodule Mix.Tasks.Ejabberd.Deps.Tree do defp keep_only_mods_not_used_as_dep(mods, mods_used_as_dep) do Enum.filter mods, fn %{module: mod} -> - not mod in mods_used_as_dep + not (mod in mods_used_as_dep) end end diff --git a/lib/mod_example.ex b/lib/mod_example.ex new file mode 100644 index 000000000..12166810a --- /dev/null +++ b/lib/mod_example.ex @@ -0,0 +1,46 @@ +defmodule Ejabberd.Module.Example do + + @moduledoc """ + Example ejabberd module written in Elixir. + + This is an example to demonstrate the usage of Elixir to + create ejabberd modules. + + Example configuration: + modules: + 'Ejabberd.Module.Example': {} + """ + + @behaviour :gen_mod + import Ejabberd.Logger + + def start(host, _opts) do + info("Starting Ejabberd.Module.Example for host '#{host}'") + Ejabberd.Hooks.add(:set_presence_hook, host, __MODULE__, :on_presence, 50) + :ok + end + + def stop(host) do + info("Stopping Ejabberd.Module.Example for host '#{host}'") + Ejabberd.Hooks.delete(:set_presence_hook, host, __MODULE__, :on_presence, 50) + :ok + end + + def on_presence(user, _server, _resource, _packet) do + info("Receive presence for #{user}") + :none + end + + def depends(_host, _opts) do + [] + end + + def mod_options(_host) do + [] + end + + def mod_doc() do + %{:desc => "This is just a demonstration."} + end + +end diff --git a/lib/mod_presence_demo.ex b/lib/mod_presence_demo.ex deleted file mode 100644 index 09bf58405..000000000 --- a/lib/mod_presence_demo.ex +++ /dev/null @@ -1,20 +0,0 @@ -defmodule ModPresenceDemo do - use Ejabberd.Module - - def start(host, _opts) do - info('Starting ejabberd module Presence Demo') - Ejabberd.Hooks.add(:set_presence_hook, host, __MODULE__, :on_presence, 50) - :ok - end - - def stop(host) do - info('Stopping ejabberd module Presence Demo') - Ejabberd.Hooks.delete(:set_presence_hook, host, __MODULE__, :on_presence, 50) - :ok - end - - def on_presence(user, _server, _resource, _packet) do - info('Receive presence for #{user}') - :none - end -end diff --git a/m4/erlang-extra.m4 b/m4/erlang-extra.m4 index f658057b5..95c4b138d 100644 --- a/m4/erlang-extra.m4 +++ b/m4/erlang-extra.m4 @@ -75,7 +75,7 @@ EOF if test "x`cat conftest.out`" != "xok"; then AC_MSG_RESULT([failed]) X="`cat conftest.out`" - if test "[$3]" == "warn"; then + if test "[$3]" = "warn"; then AC_MSG_WARN([$X]) else AC_MSG_FAILURE([$X]) @@ -84,20 +84,3 @@ EOF AC_MSG_RESULT([ok]) fi ]) dnl ERLANG_VERSION_CHECK - -AC_DEFUN([ERLANG_DEPRECATED_TYPES_CHECK], -[ AC_MSG_CHECKING([whether Erlang is using deprecated types]) - cat > conftest.erl < /dev/null 2>&1; then - AC_MSG_RESULT([no]) - AC_SUBST(erlang_deprecated_types, false) - else - AC_MSG_RESULT([yes]) - AC_SUBST(erlang_deprecated_types, true) - fi -]) diff --git a/man/ejabberd.yml.5 b/man/ejabberd.yml.5 new file mode 100644 index 000000000..aa42e20b2 --- /dev/null +++ b/man/ejabberd.yml.5 @@ -0,0 +1,8890 @@ +'\" t +.\" Title: ejabberd.yml +.\" Author: [see the "AUTHOR" section] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 08/22/2025 +.\" Manual: \ \& +.\" Source: \ \& +.\" Language: English +.\" +.TH "EJABBERD\&.YML" "5" "08/22/2025" "\ \&" "\ \&" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +ejabberd.yml \- main configuration file for ejabberd\&. +.SH "SYNOPSIS" +.sp +ejabberd\&.yml +.SH "DESCRIPTION" +.sp +The configuration file is written in YAML language\&. +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBWarning\fR +.ps -1 +.br +.sp +YAML is indentation sensitive, so make sure you respect indentation, or otherwise you will get pretty cryptic configuration errors\&. +.sp .5v +.RE +.sp +Logically, configuration options are split into 3 main categories: \fIModules\fR, \fIListeners\fR and everything else called \fITop Level\fR options\&. Thus this document is split into 3 main chapters describing each category separately\&. So, the contents of ejabberd\&.yml will typically look like this: +.sp +.if n \{\ +.RS 4 +.\} +.nf +hosts: + \- example\&.com + \- domain\&.tld +loglevel: info +\&.\&.\&. +listen: + \- + port: 5222 + module: ejabberd_c2s + \&.\&.\&. +modules: + mod_roster: {} + \&.\&.\&. +.fi +.if n \{\ +.RE +.\} +.sp +Any configuration error (such as syntax error, unknown option or invalid option value) is fatal in the sense that ejabberd will refuse to load the whole configuration file and will not start or will abort configuration reload\&. +.sp +All options can be changed in runtime by running \fIejabberdctl reload\-config\fR command\&. Configuration reload is atomic: either all options are accepted and applied simultaneously or the new configuration is refused without any impact on currently running configuration\&. +.sp +Some options can be specified for particular virtual host(s) only using \fIhost_config\fR or \fIappend_host_config\fR options\&. Such options are called \fIlocal\fR\&. Examples are \fImodules\fR, \fIauth_method\fR and \fIdefault_db\fR\&. The options that cannot be defined per virtual host are called \fIglobal\fR\&. Examples are \fIloglevel\fR, \fIcertfiles\fR and \fIlisten\fR\&. It is a configuration mistake to put \fIglobal\fR options under \fIhost_config\fR or \fIappend_host_config\fR section \- ejabberd will refuse to load such configuration\&. +.sp +It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/25\&.08/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. +.sp +Note that this document is intended to provide comprehensive description of all configuration options that can be consulted to understand the meaning of a particular option, its format and possible values\&. It will be quite hard to understand how to configure ejabberd by reading this document only \- for this purpose the reader is recommended to read online Configuration Guide available at https://docs\&.ejabberd\&.im/admin/configuration\&. +.SH "TOP LEVEL OPTIONS" +.sp +This section describes top level options of ejabberd 25\&.08\&. The options that changed in this version are marked with 🟤\&. +.PP +\fBaccess_rules\fR: \fI{AccessName: {allow|deny: ACLName|ACLDefinition}}\fR +.RS 4 +This option defines +\fIbasic\&.md#access\-rules|Access Rules\fR\&. Each access rule is assigned a name that can be referenced from other parts of the configuration file (mostly from +\fIaccess\fR +options of ejabberd modules)\&. Each rule definition may contain arbitrary number of +\fIallow\fR +or +\fIdeny\fR +sections, and each section may contain any number of ACL rules (see +\fIacl\fR +option)\&. There are no access rules defined by default\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +access_rules: + configure: + allow: admin + something: + deny: someone + allow: all + s2s_banned: + deny: problematic_hosts + deny: banned_forever + deny: + ip: 222\&.111\&.222\&.111/32 + deny: + ip: 111\&.222\&.111\&.222/32 + allow: all + xmlrpc_access: + allow: + user: peter@example\&.com + allow: + user: ivone@example\&.com + allow: + user: bot@example\&.com + ip: 10\&.0\&.0\&.0/24 +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBacl\fR: \fI{ACLName: {ACLType: ACLValue}}\fR +.RS 4 +This option defines +\fI\&.\&./configuration/basic\&.md#acl|access control lists\fR: named sets of rules which are used to match against different targets (such as a JID or an IP address)\&. Every set of rules has name +\fIACLName\fR: it can be any string except +\fIall\fR +or +\fInone\fR +(those are predefined names for the rules that match all or nothing respectively)\&. The name +\fIACLName\fR +can be referenced from other parts of the configuration file, for example in +\fIaccess_rules\fR +option\&. The rules of +\fIACLName\fR +are represented by mapping +\fI{ACLType: ACLValue}\fR\&. These can be one of the following: +.PP +\fBip\fR: \fINetwork\fR +.RS 4 +The rule matches any IP address from the +\fINetwork\fR\&. +.RE +.PP +\fBnode_glob\fR: \fIPattern\fR +.RS 4 +Same as +\fInode_regexp\fR, but matching is performed on a specified +\fIPattern\fR +according to the rules used by the Unix shell\&. +.RE +.PP +\fBnode_regexp\fR: \fIuser_regexp@server_regexp\fR +.RS 4 +The rule matches any JID with node part matching regular expression +\fIuser_regexp\fR +and server part matching regular expression +\fIserver_regexp\fR\&. +.RE +.PP +\fBresource\fR: \fIResource\fR +.RS 4 +The rule matches any JID with a resource +\fIResource\fR\&. +.RE +.PP +\fBresource_glob\fR: \fIPattern\fR +.RS 4 +Same as +\fIresource_regexp\fR, but matching is performed on a specified +\fIPattern\fR +according to the rules used by the Unix shell\&. +.RE +.PP +\fBresource_regexp\fR: \fIRegexp\fR +.RS 4 +The rule matches any JID with a resource that matches regular expression +\fIRegexp\fR\&. +.RE +.PP +\fBserver\fR: \fIServer\fR +.RS 4 +The rule matches any JID from server +\fIServer\fR\&. The value of +\fIServer\fR +must be a valid hostname or an IP address\&. +.RE +.PP +\fBserver_glob\fR: \fIPattern\fR +.RS 4 +Same as +\fIserver_regexp\fR, but matching is performed on a specified +\fIPattern\fR +according to the rules used by the Unix shell\&. +.RE +.PP +\fBserver_regexp\fR: \fIRegexp\fR +.RS 4 +The rule matches any JID from the server that matches regular expression +\fIRegexp\fR\&. +.RE +.PP +\fBuser\fR: \fIUsername\fR +.RS 4 +If +\fIUsername\fR +is in the form of "user@server", the rule matches a JID against this value\&. Otherwise, if +\fIUsername\fR +is in the form of "user", the rule matches any JID that has +\fIUsername\fR +in the node part as long as the server part of this JID is any virtual host served by ejabberd\&. +.RE +.PP +\fBuser_glob\fR: \fIPattern\fR +.RS 4 +Same as +\fIuser_regexp\fR, but matching is performed on a specified +\fIPattern\fR +according to the rules used by the Unix shell\&. +.RE +.PP +\fBuser_regexp\fR: \fIRegexp\fR +.RS 4 +If +\fIRegexp\fR +is in the form of "regexp@server", the rule matches any JID with node part matching regular expression "regexp" as long as the server part of this JID is equal to "server"\&. If +\fIRegexp\fR +is in the form of "regexp", the rule matches any JID with node part matching regular expression "regexp" as long as the server part of this JID is any virtual host served by ejabberd\&. +.RE +.RE +.PP +\fBacme\fR: \fIOptions\fR +.RS 4 +\fIbasic\&.md#acme|ACME\fR +configuration, to automatically obtain SSL certificates for the domains served by ejabberd, which means that certificate requests and renewals are performed to some CA server (aka "ACME server") in a fully automated mode\&. The +\fIOptions\fR +are: +.PP +\fBauto\fR: \fItrue | false\fR +.RS 4 +Whether to automatically request certificates for all configured domains (that yet have no a certificate) on server start or configuration reload\&. The default is +\fItrue\fR\&. +.RE +.PP +\fBca_url\fR: \fIURL\fR +.RS 4 +The ACME directory URL used as an entry point for the ACME server\&. The default value is +https://acme\-v02\&.api\&.letsencrypt\&.org/directory +\- the directory URL of Let\(cqs Encrypt authority\&. +.RE +.PP +\fBcert_type\fR: \fIrsa | ec\fR +.RS 4 +A type of a certificate key\&. Available values are +\fIec\fR +and +\fIrsa\fR +for EC and RSA certificates respectively\&. It\(cqs better to have RSA certificates for the purpose of backward compatibility with legacy clients and servers, thus the default is +\fIrsa\fR\&. +.RE +.PP +\fBcontact\fR: \fI[Contact, \&.\&.\&.]\fR +.RS 4 +A list of contact addresses (typically emails) where an ACME server will send notifications when problems occur\&. The value of +\fIContact\fR +must be in the form of "scheme:address" (e\&.g\&. "mailto:user@domain\&.tld")\&. The default is an empty list which means an ACME server will send no notices\&. +.RE +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +acme: + ca_url: https://acme\-v02\&.api\&.letsencrypt\&.org/directory + contact: + \- mailto:admin@domain\&.tld + \- mailto:bot@domain\&.tld + auto: true + cert_type: rsa +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBallow_contrib_modules\fR: \fItrue | false\fR +.RS 4 +Whether to allow installation of third\-party modules or not\&. See +\fI\&.\&./\&.\&./admin/guide/modules\&.md#ejabberd\-contrib|ejabberd\-contrib\fR +documentation section\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBallow_multiple_connections\fR: \fItrue | false\fR +.RS 4 +This option is only used when the anonymous mode is enabled\&. Setting it to +\fItrue\fR +means that the same username can be taken multiple times in anonymous login mode if different resource are used to connect\&. This option is only useful in very special occasions\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBanonymous_protocol\fR: \fIlogin_anon | sasl_anon | both\fR +.RS 4 +Define what +\fIauthentication\&.md#anonymous\-login\-and\-sasl\-anonymous|anonymous\fR +protocol will be used: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIlogin_anon\fR +means that the anonymous login method will be used\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIsasl_anon\fR +means that the SASL Anonymous method will be used\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIboth\fR +means that SASL Anonymous and login anonymous are both enabled\&. +.RE +.RE +.sp +The default value is \fIsasl_anon\fR\&. +.PP +\fBapi_permissions\fR: \fI[Permission, \&.\&.\&.]\fR +.RS 4 +Define the permissions for API access\&. Please consult the ejabberd Docs web → For Developers → ejabberd ReST API → +\fI\&.\&./\&.\&./developer/ejabberd\-api/permissions\&.md|API Permissions\fR\&. +.RE +.PP +\fBappend_host_config\fR: \fI{Host: Options}\fR +.RS 4 +Add a few specific options to a certain +\fI\&.\&./configuration/basic\&.md#virtual\-hosting|virtual host\fR\&. +.RE +.PP +\fBauth_cache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as +\fIcache_life_time\fR, but applied to authentication cache only\&. If not set, the value from +\fIcache_life_time\fR +will be used\&. +.RE +.PP +\fBauth_cache_missed\fR: \fItrue | false\fR +.RS 4 +Same as +\fIcache_missed\fR, but applied to authentication cache only\&. If not set, the value from +\fIcache_missed\fR +will be used\&. +.RE +.PP +\fBauth_cache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as +\fIcache_size\fR, but applied to authentication cache only\&. If not set, the value from +\fIcache_size\fR +will be used\&. +.RE +.PP +\fBauth_external_user_exists_check\fR: \fItrue | false\fR +.RS 4 +\fINote\fR +about this option: added in 23\&.10\&. Supplement check for user existence based on +\fImod_last\fR +data, for authentication methods that don\(cqt have a way to reliably tell if a user exists (like is the case for +\fIjwt\fR +and certificate based authentication)\&. This helps with processing offline message for those users\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBauth_method\fR: \fI[mnesia | sql | anonymous | external | jwt | ldap | pam, \&.\&.\&.]\fR +.RS 4 +A list of +\fIauthentication\&.md|authentication\fR +methods to use\&. If several methods are defined, authentication is considered successful as long as authentication of at least one of the methods succeeds\&. The default value is +\fI[mnesia]\fR\&. +.RE +.PP +\fBauth_opts\fR: \fI[Option, \&.\&.\&.]\fR +.RS 4 +This is used by the contributed module +\fIejabberd_auth_http\fR +that can be installed from the +\fI\&.\&./\&.\&./admin/guide/modules\&.md#ejabberd\-contrib|ejabberd\-contrib\fR +Git repository\&. Please refer to that module\(cqs README file for details\&. +.RE +.PP +\fBauth_password_format\fR: \fIplain | scram\fR +.RS 4 +\fINote\fR +about this option: improved in 20\&.01\&. The option defines in what format the users passwords are stored, plain text or in +\fIauthentication\&.md#scram|SCRAM\fR +format: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIplain\fR: The password is stored as plain text in the database\&. This is risky because the passwords can be read if your database gets compromised\&. This is the default value\&. This format allows clients to authenticate using: the old Jabber Non\-SASL (XEP\-0078), SASL PLAIN, SASL DIGEST\-MD5, and SASL SCRAM\-SHA\-1/256/512(\-PLUS)\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIscram\fR: The password is not stored, only some information required to verify the hash provided by the client\&. It is impossible to obtain the original plain password from the stored information; for this reason, when this value is configured it cannot be changed to plain anymore\&. This format allows clients to authenticate using: SASL PLAIN and SASL SCRAM\-SHA\-1/256/512(\-PLUS)\&. The SCRAM variant depends on the +\fIauth_scram_hash\fR +option\&. +.RE +.RE +.sp +The default value is \fIplain\fR\&. +.PP +\fBauth_password_types_hidden_in_sasl1\fR: \fI[plain | scram_sha1 | scram_sha256 | scram_sha512]\fR +.RS 4 +\fINote\fR +about this option: added in 25\&.07\&. List of password types that should not be offered in SASL1 authenticatication\&. Because SASL1, unlike SASL2, can\(cqt have list of available mechanisms tailored to individual user, it\(cqs possible that offered mechanisms will not be compatible with stored password, especially if new password type was added recently\&. This option allows disabling offering some mechanisms in SASL1, to a time until new password type will be available for all users\&. +.RE +.PP +\fBauth_scram_hash\fR: \fIsha | sha256 | sha512\fR +.RS 4 +Hash algorithm that should be used to store password in +\fIauthentication\&.md#scram|SCRAM\fR +format\&. You shouldn\(cqt change this if you already have passwords generated with a different algorithm \- users that have such passwords will not be able to authenticate\&. The default value is +\fIsha\fR\&. +.RE +.PP +\fBauth_stored_password_types\fR: \fI[plain | scram_sha1 | scram_sha256 | scram_sha512]\fR +.RS 4 +\fINote\fR +about this option: added in 25\&.03\&. List of password types that should be stored simultaneously for each user in database\&. When the user sets the account password, database will be updated to store the password in formats compatible with each type listed here\&. This can be used to migrate user passwords to a more secure format\&. If this option if set, it will override values set in +\fIauth_scram_hash\fR +and +\fIauth_password_format\fR +options\&. The default value is +[]\&. +.RE +.PP +\fBauth_use_cache\fR: \fItrue | false\fR +.RS 4 +Same as +\fIuse_cache\fR, but applied to authentication cache only\&. If not set, the value from +\fIuse_cache\fR +will be used\&. +.RE +.PP +\fBc2s_cafile\fR: \fIPath\fR +.RS 4 +Full path to a file containing one or more CA certificates in PEM format\&. All client certificates should be signed by one of these root CA certificates and should contain the corresponding JID(s) in +\fIsubjectAltName\fR +field\&. There is no default value\&. +.RE +.sp +You can use \fIhost_config\fR to specify this option per\-vhost\&. +.sp +To set a specific file per listener, use the listener\(cqs \fIlisten\-options\&.md#cafile|cafile\fR option\&. Please notice that \fIc2s_cafile\fR overrides the listener\(cqs \fIcafile\fR option\&. +.PP +\fBc2s_ciphers\fR: \fI[Cipher, \&.\&.\&.]\fR +.RS 4 +A list of OpenSSL ciphers to use for c2s connections\&. The default value is shown in the example below: +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +c2s_ciphers: + \- HIGH + \- "!aNULL" + \- "!eNULL" + \- "!3DES" + \- "@STRENGTH" +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBc2s_dhfile\fR: \fIPath\fR +.RS 4 +Full path to a file containing custom DH parameters to use for c2s connections\&. Such a file could be created with the command +\fI"openssl dhparam \-out dh\&.pem 2048"\fR\&. If this option is not specified, 2048\-bit MODP Group with 256\-bit Prime Order Subgroup will be used as defined in RFC5114 Section 2\&.3\&. +.RE +.PP +\fBc2s_protocol_options\fR: \fI[Option, \&.\&.\&.]\fR +.RS 4 +List of general SSL options to use for c2s connections\&. These map to OpenSSL\(cqs +\fIset_options()\fR\&. The default value is shown in the example below: +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +c2s_protocol_options: + \- no_sslv3 + \- cipher_server_preference + \- no_compression +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBc2s_tls_compression\fR: \fItrue | false\fR +.RS 4 +Whether to enable or disable TLS compression for c2s connections\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBca_file\fR: \fIPath\fR +.RS 4 +Path to a file of CA root certificates\&. The default is to use system defined file if possible\&. +.RE +.sp +For server connections, this \fIca_file\fR option is overridden by the \fIs2s_cafile\fR option\&. +.PP +\fBcache_life_time\fR: \fItimeout()\fR +.RS 4 +The time of a cached item to keep in cache\&. Once it\(cqs expired, the corresponding item is erased from cache\&. The default value is +\fI1 hour\fR\&. Several modules have a similar option; and some core ejabberd parts support similar options too, see +\fIauth_cache_life_time\fR, +\fIoauth_cache_life_time\fR, +\fIrouter_cache_life_time\fR, and +\fIsm_cache_life_time\fR\&. +.RE +.PP +\fBcache_missed\fR: \fItrue | false\fR +.RS 4 +Whether or not to cache missed lookups\&. When there is an attempt to lookup for a value in a database and this value is not found and the option is set to +\fItrue\fR, this attempt will be cached and no attempts will be performed until the cache expires (see +\fIcache_life_time\fR)\&. Usually you don\(cqt want to change it\&. Default is +\fItrue\fR\&. Several modules have a similar option; and some core ejabberd parts support similar options too, see +\fIauth_cache_missed\fR, +\fIoauth_cache_missed\fR, +\fIrouter_cache_missed\fR, and +\fIsm_cache_missed\fR\&. +.RE +.PP +\fBcache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +A maximum number of items (not memory!) in cache\&. The rule of thumb, for all tables except rosters, you should set it to the number of maximum online users you expect\&. For roster multiply this number by 20 or so\&. If the cache size reaches this threshold, it\(cqs fully cleared, i\&.e\&. all items are deleted, and the corresponding warning is logged\&. You should avoid frequent cache clearance, because this degrades performance\&. The default value is +\fI1000\fR\&. Several modules have a similar option; and some core ejabberd parts support similar options too, see +\fIauth_cache_size\fR, +\fIoauth_cache_size\fR, +\fIrouter_cache_size\fR, and +\fIsm_cache_size\fR\&. +.RE +.PP +\fBcaptcha_cmd\fR: \fIPath | ModuleName\fR +.RS 4 +\fINote\fR +about this option: improved in 23\&.01\&. Full path to a script that generates +\fIbasic\&.md#captcha|CAPTCHA\fR +images\&. The keyword +\fI@VERSION@\fR +is replaced with ejabberd version number in +\fIXX\&.YY\fR +format\&. The keyword +\fI@SEMVER@\fR +is replaced with ejabberd version number in semver format when compiled with Elixir\(cqs mix, or XX\&.YY format otherwise\&. Alternatively, it can be the name of a module that implements ejabberd CAPTCHA support\&. There is no default value: when this option is not set, CAPTCHA functionality is completely disabled\&. +.sp +\fBExamples\fR: +.sp +When using the ejabberd installers or container image, the example captcha scripts can be used like this: +.sp +.if n \{\ +.RS 4 +.\} +.nf +captcha_cmd: /opt/ejabberd\-@VERSION@/lib/ejabberd\-@SEMVER@/priv/bin/captcha\&.sh +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBcaptcha_host\fR: \fIString\fR +.RS 4 +Deprecated\&. Use +\fIcaptcha_url\fR +instead\&. +.RE +.PP +\fBcaptcha_limit\fR: \fIpos_integer() | infinity\fR +.RS 4 +Maximum number of +\fIbasic\&.md#captcha|CAPTCHA\fR +generated images per minute for any given JID\&. The option is intended to protect the server from CAPTCHA DoS\&. The default value is +\fIinfinity\fR\&. +.RE +.PP +\fBcaptcha_url\fR: \fIURL | auto | undefined\fR +.RS 4 +\fINote\fR +about this option: improved in 23\&.04\&. An URL where +\fIbasic\&.md#captcha|CAPTCHA\fR +requests should be sent\&. NOTE: you need to configure +\fIrequest_handlers\fR +for +\fIejabberd_http\fR +listener as well\&. If set to +\fIauto\fR, it builds the URL using a +\fIrequest_handler\fR +already enabled, with encryption if available\&. If set to +\fIundefined\fR, it builds the URL using the deprecated +\fIcaptcha_host\fR +\fI+ /captcha\fR\&. The default value is +\fIauto\fR\&. +.RE +.PP +\fBcertfiles\fR: \fI[Path, \&.\&.\&.]\fR +.RS 4 +The option accepts a list of file paths (optionally with wildcards) containing either PEM certificates or PEM private keys\&. At startup or configuration reload, ejabberd reads all certificates from these files, sorts them, removes duplicates, finds matching private keys and then rebuilds full certificate chains for the use in TLS connections\&. Use this option when TLS is enabled in either of ejabberd listeners: +\fIejabberd_c2s\fR, +\fIejabberd_http\fR +and so on\&. NOTE: if you modify the certificate files or change the value of the option, run +\fIejabberdctl reload\-config\fR +in order to rebuild and reload the certificate chains\&. +.sp +\fBExamples\fR: +.sp +If you use +Let\(cqs Encrypt +certificates for your domain "domain\&.tld", the configuration will look like this: +.sp +.if n \{\ +.RS 4 +.\} +.nf +certfiles: + \- /etc/letsencrypt/live/domain\&.tld/fullchain\&.pem + \- /etc/letsencrypt/live/domain\&.tld/privkey\&.pem +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBcluster_backend\fR: \fIBackend\fR +.RS 4 +A database backend to use for storing information about cluster\&. The only available value so far is +\fImnesia\fR\&. +.RE +.PP +\fBcluster_nodes\fR: \fI[Node, \&.\&.\&.]\fR +.RS 4 +A list of Erlang nodes to connect on ejabberd startup\&. This option is mostly intended for ejabberd customization and sophisticated setups\&. The default value is an empty list\&. +.RE +.PP +\fBdefault_db\fR: \fImnesia | sql\fR +.RS 4 +\fIdatabase\&.md#default\-database|Default database\fR +to store persistent data in ejabberd\&. Some components can be configured with specific toplevel options like +\fIoauth_db_type\fR\&. Many modules can be configured with specific module options, usually named +db_type\&. The default value is +\fImnesia\fR\&. +.RE +.PP +\fBdefault_ram_db\fR: \fImnesia | redis | sql\fR +.RS 4 +Default volatile (in\-memory) storage for ejabberd\&. Some components can be configured with specific toplevel options like +\fIrouter_db_type\fR +and +\fIsm_db_type\fR\&. Some modules can be configured with specific module options, usually named +ram_db_type\&. The default value is +\fImnesia\fR\&. +.RE +.PP +\fBdefine_keyword\fR: \fI{NAME: Value}\fR +.RS 4 +\fINote\fR +about this option: added in 25\&.03\&. Allows to define configuration +\fI\&.\&./configuration/file\-format\&.md#macros\-and\-keywords|keywords\fR\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +define_keyword: + SQL_USERNAME: "eja\&.global" + +host_config: + localhost: + define_keyword: + SQL_USERNAME: "eja\&.localhost" + +sql_username: "prefix\&.@SQL_USERNAME@" +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBdefine_macro\fR: \fI{NAME: Value}\fR +.RS 4 +\fINote\fR +about this option: improved in 25\&.03\&. Allows to define configuration +\fI\&.\&./configuration/file\-format\&.md#macros\-and\-keywords|macros\fR\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +define_macro: + DEBUG: debug + LOG_LEVEL: DEBUG + USERBOB: + user: bob@localhost + +loglevel: LOG_LEVEL + +acl: + admin: USERBOB +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBdisable_sasl_mechanisms\fR: \fI[Mechanism, \&.\&.\&.]\fR +.RS 4 +Specify a list of SASL mechanisms (such as +\fIDIGEST\-MD5\fR +or +\fISCRAM\-SHA1\fR) that should not be offered to the client\&. For convenience, the value of +\fIMechanism\fR +is case\-insensitive\&. The default value is an empty list, i\&.e\&. no mechanisms are disabled by default\&. +.RE +.PP +\fBdisable_sasl_scram_downgrade_protection\fR: \fItrue | false\fR +.RS 4 +Allows to disable sending data required by +\fIXEP\-0474: SASL SCRAM Downgrade Protection\fR\&. There are known buggy clients (like those that use strophejs 1\&.6\&.2) which will not be able to authenticatate when servers sends data from that specification\&. This options allows server to disable it to allow even buggy clients connects, but in exchange decrease MITM protection\&. The default value of this option is +\fIfalse\fR +which enables this extension\&. +.RE +.PP +\fBdomain_balancing\fR: \fI{Domain: Options}\fR +.RS 4 +An algorithm to +\fI\&.\&./guide/clustering\&.md#service\-load\-balancing|load\-balance\fR +the components that are plugged on an ejabberd cluster\&. It means that you can plug one or several instances of the same component on each ejabberd node and that the traffic will be automatically distributed\&. The algorithm to deliver messages to the component(s) can be specified by this option\&. For any component connected as +\fIDomain\fR, available +\fIOptions\fR +are: +.PP +\fBcomponent_number\fR: \fI2\&.\&.1000\fR +.RS 4 +The number of components to balance\&. +.RE +.PP +\fBtype\fR: \fIValue\fR +.RS 4 +How to deliver stanzas to connected components\&. The default value is +\fIrandom\fR\&. Possible values: +.RE +.PP +\fB\- bare_destination\fR +.RS 4 +by the bare JID (without resource) of the packet\(cqs +\fIto\fR +attribute +.RE +.PP +\fB\- bare_source\fR +.RS 4 +by the bare JID (without resource) of the packet\(cqs +\fIfrom\fR +attribute is used +.RE +.PP +\fB\- destination\fR +.RS 4 +an instance is chosen by the full JID of the packet\(cqs +\fIto\fR +attribute +.RE +.PP +\fB\- random\fR +.RS 4 +an instance is chosen at random +.RE +.PP +\fB\- source\fR +.RS 4 +by the full JID of the packet\(cqs +\fIfrom\fR +attribute +.RE +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +domain_balancing: + component\&.domain\&.tld: + type: destination + component_number: 5 + transport\&.example\&.org: + type: bare_source +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBext_api_headers\fR: \fIHeaders\fR +.RS 4 +String of headers (separated with commas +\fI,\fR) that will be provided by ejabberd when sending ReST requests\&. The default value is an empty string of headers: +\fI""\fR\&. +.RE +.PP +\fBext_api_http_pool_size\fR: \fIpos_integer()\fR +.RS 4 +Define the size of the HTTP pool, that is, the maximum number of sessions that the ejabberd ReST service will handle simultaneously\&. The default value is: +\fI100\fR\&. +.RE +.PP +\fBext_api_path_oauth\fR: \fIPath\fR +.RS 4 +Define the base URI path when performing OAUTH ReST requests\&. The default value is: +\fI"/oauth"\fR\&. +.RE +.PP +\fBext_api_url\fR: \fIURL\fR +.RS 4 +Define the base URI when performing ReST requests\&. The default value is: +\fI"http://localhost/api"\fR\&. +.RE +.PP +\fBextauth_pool_name\fR: \fIName\fR +.RS 4 +Define the pool name appendix in +\fIauthentication\&.md#external\-script|external auth\fR, so the full pool name will be +\fIextauth_pool_Name\fR\&. The default value is the hostname\&. +.RE +.PP +\fBextauth_pool_size\fR: \fISize\fR +.RS 4 +The option defines the number of instances of the same +\fIauthentication\&.md#external\-script|external auth\fR +program to start for better load balancing\&. The default is the number of available CPU cores\&. +.RE +.PP +\fBextauth_program\fR: \fIPath\fR +.RS 4 +Indicate in this option the full path to the +\fIauthentication\&.md#external\-script|external authentication script\fR\&. The script must be executable by ejabberd\&. +.RE +.PP +\fBfqdn\fR: \fIDomain\fR +.RS 4 +A fully qualified domain name that will be used in SASL DIGEST\-MD5 authentication\&. The default is detected automatically\&. +.RE +.PP +\fBhide_sensitive_log_data\fR: \fItrue | false\fR +.RS 4 +A privacy option to not log sensitive data (mostly IP addresses)\&. The default value is +\fIfalse\fR +for backward compatibility\&. +.RE +.PP +\fBhost_config\fR: \fI{Host: Options}\fR +.RS 4 +The option is used to redefine +\fIOptions\fR +for +\fI\&.\&./configuration/basic\&.md#virtual\-hosting|virtual host\fR +\fIHost\fR\&. In the example below LDAP authentication method will be used on virtual host +\fIdomain\&.tld\fR +and SQL method will be used on virtual host +\fIexample\&.org\fR\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +hosts: + \- domain\&.tld + \- example\&.org + +auth_method: + \- sql + +host_config: + domain\&.tld: + auth_method: + \- ldap +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBhosts\fR: \fI[Domain1, Domain2, \&.\&.\&.]\fR +.RS 4 +List of one or more +\fI\&.\&./configuration/basic\&.md#host\-names|host names\fR +(or domains) that ejabberd will serve\&. This is a +\fBmandatory\fR +option\&. +.RE +.PP +\fBhosts_alias\fR: \fI{Alias: Host}\fR +.RS 4 +\fINote\fR +about this option: added in 25\&.07\&. Define aliases for existing vhosts managed by ejabberd\&. An alias may be a regexp expression\&. This option is only consulted by the +\fIejabberd_http\fR +listener\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +hosts: + \- domain\&.tld + \- example\&.org + +hosts_alias: + xmpp\&.domain\&.tld: domain\&.tld + jabber\&.domain\&.tld: domain\&.tld + mytest\&.net: example\&.org + "exa*": example\&.org +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBinclude_config_file\fR: \fI[Filename, \&.\&.\&.] | {Filename: Options}\fR +.RS 4 +Read and +\fI\&.\&./configuration/file\-format\&.md#include\-additional\-files|include additional file\fR +from +\fIFilename\fR\&. If the value is provided in +\fI{Filename: Options}\fR +format, the +\fIOptions\fR +must be one of the following: +.PP +\fBallow_only\fR: \fI[OptionName, \&.\&.\&.]\fR +.RS 4 +Allows only the usage of those options in the included file +\fIFilename\fR\&. The options that do not match this criteria are not accepted\&. The default value is to include all options\&. +.RE +.PP +\fBdisallow\fR: \fI[OptionName, \&.\&.\&.]\fR +.RS 4 +Disallows the usage of those options in the included file +\fIFilename\fR\&. The options that match this criteria are not accepted\&. The default value is an empty list\&. +.RE +.RE +.PP +\fBinstall_contrib_modules\fR: \fI[Module, \&.\&.\&.]\fR +.RS 4 +\fINote\fR +about this option: added in 23\&.10\&. Modules to install from +\fI\&.\&./\&.\&./admin/guide/modules\&.md#ejabberd\-contrib|ejabberd\-contrib\fR +at start time\&. The default value is an empty list of modules: +\fI[]\fR\&. +.RE +.PP +\fBjwt_auth_only_rule\fR: \fIAccessName\fR +.RS 4 +This ACL rule defines accounts that can use only the +\fIauthentication\&.md#jwt\-authentication|JWT\fR +auth method, even if others are also defined in the ejabberd configuration file\&. In other words: if there are several auth methods enabled for this host (JWT, SQL, \&...), users that match this rule can only use JWT\&. The default value is +\fInone\fR\&. +.RE +.PP +\fBjwt_jid_field\fR: \fIFieldName\fR +.RS 4 +By default, the JID is defined in the +\fI"jid"\fR +JWT field\&. In this option you can specify other +\fIauthentication\&.md#jwt\-authentication|JWT\fR +field name where the JID is defined\&. +.RE +.PP +\fBjwt_key\fR: \fIFilePath\fR +.RS 4 +Path to the file that contains the +\fIauthentication\&.md#jwt\-authentication|JWT\fR +key\&. The default value is +\fIundefined\fR\&. +.RE +.PP +\fBlanguage\fR: \fILanguage\fR +.RS 4 +Define the +\fI\&.\&./configuration/basic\&.md#default\-language|default language\fR +of server strings that can be seen by XMPP clients\&. If an XMPP client does not possess +\fIxml:lang\fR +attribute, the specified language is used\&. The default value is +\fI"en"\fR\&. +.RE +.PP +\fBldap_backups\fR: \fI[Host, \&.\&.\&.]\fR +.RS 4 +A list of IP addresses or DNS names of LDAP backup servers (see +\fI\&.\&./configuration/ldap\&.md#ldap\-connection|LDAP connection\fR)\&. When no servers listed in +\fIldap_servers\fR +option are reachable, ejabberd connects to these backup servers\&. The default is an empty list, i\&.e\&. no backup servers specified\&. Please notice that ejabberd only connects to the next server when the existing connection is lost; it doesn\(cqt detect when a previously\-attempted server becomes available again\&. +.RE +.PP +\fBldap_base\fR: \fIBase\fR +.RS 4 +LDAP base directory which stores users accounts\&. There is no default value: you must set the option in order for LDAP connections to work properly\&. +.RE +.PP +\fBldap_deref_aliases\fR: \fInever | always | finding | searching\fR +.RS 4 +Whether to dereference aliases or not\&. The default value is +\fInever\fR\&. +.RE +.PP +\fBldap_dn_filter\fR: \fI{Filter: FilterAttrs}\fR +.RS 4 +This filter is applied on the results returned by the main filter\&. The filter performs an additional LDAP lookup to make the complete result\&. This is useful when you are unable to define all filter rules in +\fIldap_filter\fR\&. You can define +\fI"%u"\fR, +\fI"%d"\fR, +\fI"%s"\fR +and +\fI"%D"\fR +pattern variables in +\fIFilter: "%u"\fR +is replaced by a user\(cqs part of the JID, +\fI"%d"\fR +is replaced by the corresponding domain (virtual host), all +\fI"%s"\fR +variables are consecutively replaced by values from the attributes in +\fIFilterAttrs\fR +and +\fI"%D"\fR +is replaced by Distinguished Name from the result set\&. There is no default value, which means the result is not filtered\&. WARNING: Since this filter makes additional LDAP lookups, use it only as the last resort: try to define all filter rules in +\fIldap_filter\fR +option if possible\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +ldap_dn_filter: + "(&(name=%s)(owner=%D)(user=%u@%d))": [sn] +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBldap_encrypt\fR: \fItls | none\fR +.RS 4 +Whether to encrypt LDAP connection using TLS or not\&. The default value is +\fInone\fR\&. NOTE: STARTTLS encryption is not supported\&. +.RE +.PP +\fBldap_filter\fR: \fIFilter\fR +.RS 4 +An LDAP filter as defined in +RFC4515\&. There is no default value\&. Example: +\fI"(&(objectClass=shadowAccount)(memberOf=XMPP Users))"\fR\&. NOTE: don\(cqt forget to close brackets and don\(cqt use superfluous whitespaces\&. Also you must not use +\fI"uid"\fR +attribute in the filter because this attribute will be appended to the filter automatically\&. +.RE +.PP +\fBldap_password\fR: \fIPassword\fR +.RS 4 +Bind password\&. The default value is an empty string\&. +.RE +.PP +\fBldap_port\fR: \fI1\&.\&.65535\fR +.RS 4 +Port to connect to your LDAP server\&. The default port is +\fI389\fR +if encryption is disabled and +\fI636\fR +if encryption is enabled\&. +.RE +.PP +\fBldap_rootdn\fR: \fIRootDN\fR +.RS 4 +Bind Distinguished Name\&. The default value is an empty string, which means "anonymous connection"\&. +.RE +.PP +\fBldap_servers\fR: \fI[Host, \&.\&.\&.]\fR +.RS 4 +A list of IP addresses or DNS names of your LDAP servers (see +\fI\&.\&./configuration/ldap\&.md#ldap\-connection|LDAP connection\fR)\&. ejabberd connects immediately to all of them, and reconnects infinitely if connection is lost\&. The default value is +\fI[localhost]\fR\&. +.RE +.PP +\fBldap_tls_cacertfile\fR: \fIPath\fR +.RS 4 +A path to a file containing PEM encoded CA certificates\&. This option is required when TLS verification is enabled\&. +.RE +.PP +\fBldap_tls_certfile\fR: \fIPath\fR +.RS 4 +A path to a file containing PEM encoded certificate along with PEM encoded private key\&. This certificate will be provided by ejabberd when TLS enabled for LDAP connections\&. There is no default value, which means no client certificate will be sent\&. +.RE +.PP +\fBldap_tls_depth\fR: \fINumber\fR +.RS 4 +Specifies the maximum verification depth when TLS verification is enabled, i\&.e\&. how far in a chain of certificates the verification process can proceed before the verification is considered to be failed\&. Peer certificate = 0, CA certificate = 1, higher level CA certificate = 2, etc\&. The value +\fI2\fR +thus means that a chain can at most contain peer cert, CA cert, next CA cert, and an additional CA cert\&. The default value is +\fI1\fR\&. +.RE +.PP +\fBldap_tls_verify\fR: \fIfalse | soft | hard\fR +.RS 4 +This option specifies whether to verify LDAP server certificate or not when TLS is enabled\&. When +\fIhard\fR +is set, ejabberd doesn\(cqt proceed if the certificate is invalid\&. When +\fIsoft\fR +is set, ejabberd proceeds even if the check has failed\&. The default is +\fIfalse\fR, which means no checks are performed\&. +.RE +.PP +\fBldap_uids\fR: \fI[Attr] | {Attr: AttrFormat}\fR +.RS 4 +LDAP attributes which hold a list of attributes to use as alternatives for getting the JID, where +\fIAttr\fR +is an LDAP attribute which holds the user\(cqs part of the JID and +\fIAttrFormat\fR +must contain one and only one pattern variable +\fI"%u"\fR +which will be replaced by the user\(cqs part of the JID\&. For example, +\fI"%\fR\fIu@example\fR\fI\&.org"\fR\&. If the value is in the form of +\fI[Attr]\fR +then +\fIAttrFormat\fR +is assumed to be +\fI"%u"\fR\&. +.RE +.PP +\fBlisten\fR: \fI[Options, \&.\&.\&.]\fR +.RS 4 +The option for listeners configuration\&. See the +\fIlisten\&.md|Listen Modules\fR +section for details\&. +.RE +.PP +\fBlog_burst_limit_count\fR: \fINumber\fR +.RS 4 +\fINote\fR +about this option: added in 22\&.10\&. The number of messages to accept in +log_burst_limit_window_time +period before starting to drop them\&. Default +500 +.RE +.PP +\fBlog_burst_limit_window_time\fR: \fINumber\fR +.RS 4 +\fINote\fR +about this option: added in 22\&.10\&. The time period to rate\-limit log messages by\&. Defaults to +1 +second\&. +.RE +.PP +\fBlog_modules_fully\fR: \fI[Module, \&.\&.\&.]\fR +.RS 4 +\fINote\fR +about this option: added in 23\&.01\&. List of modules that will log everything independently from the general loglevel option\&. +.RE +.PP +\fBlog_rotate_count\fR: \fINumber\fR +.RS 4 +The number of rotated log files to keep\&. The default value is +\fI1\fR, which means that only keeps +ejabberd\&.log\&.0, +error\&.log\&.0 +and +crash\&.log\&.0\&. +.RE +.PP +\fBlog_rotate_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +The size (in bytes) of a log file to trigger rotation\&. If set to +\fIinfinity\fR, log rotation is disabled\&. The default value is 10 Mb expressed in bytes: +\fI10485760\fR\&. +.RE +.PP +\fBloglevel\fR: \fInone | emergency | alert | critical | error | warning | notice | info | debug\fR +.RS 4 +Verbosity of ejabberd +\fI\&.\&./configuration/basic\&.md#logging|logging\fR\&. The default value is +\fIinfo\fR\&. NOTE: previous versions of ejabberd had log levels defined in numeric format (\fI0\&.\&.5\fR)\&. The numeric values are still accepted for backward compatibility, but are not recommended\&. +.RE +.PP +\fBmax_fsm_queue\fR: \fISize\fR +.RS 4 +This option specifies the maximum number of elements in the queue of the FSM (Finite State Machine)\&. Roughly speaking, each message in such queues represents one XML stanza queued to be sent into its relevant outgoing stream\&. If queue size reaches the limit (because, for example, the receiver of stanzas is too slow), the FSM and the corresponding connection (if any) will be terminated and error message will be logged\&. The reasonable value for this option depends on your hardware configuration\&. The allowed values are positive integers\&. The default value is +\fI10000\fR\&. +.RE +.PP +\fBmodules\fR: \fI{Module: Options}\fR +.RS 4 +Set all the +\fImodules\&.md|modules\fR +configuration options\&. +.RE +.PP +\fBnegotiation_timeout\fR: \fItimeout()\fR +.RS 4 +Time to wait for an XMPP stream negotiation to complete\&. When timeout occurs, the corresponding XMPP stream is closed\&. The default value is +\fI120\fR +seconds\&. +.RE +.PP +\fBnet_ticktime\fR: \fItimeout()\fR +.RS 4 +This option can be used to tune tick time parameter of +\fInet_kernel\fR\&. It tells Erlang VM how often nodes should check if intra\-node communication was not interrupted\&. This option must have identical value on all nodes, or it will lead to subtle bugs\&. Usually leaving default value of this is option is best, tweak it only if you know what you are doing\&. The default value is +\fI1 minute\fR\&. +.RE +.PP +\fBnew_sql_schema\fR: \fItrue | false\fR +.RS 4 +Whether to use the +\fIdatabase\&.md#default\-and\-new\-schemas|new SQL schema\fR\&. All schemas are located at +https://github\&.com/processone/ejabberd/tree/25\&.08/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The +\fInew\fR +schema can handle several XMPP domains in a single ejabberd database\&. Using this +\fInew\fR +schema is best when serving several XMPP domains and/or changing domains from time to time\&. This avoid need to manage several databases and handle complex configuration changes\&. The default depends on configuration flag +\fI\-\-enable\-new\-sql\-schema\fR +which is set at compile time\&. +.RE +.PP +\fBoauth_access\fR: \fIAccessName\fR +.RS 4 +By default creating OAuth tokens is not allowed\&. To define which users can create OAuth tokens, you can refer to an ejabberd access rule in the +\fIoauth_access\fR +option\&. Use +\fIall\fR +to allow everyone to create tokens\&. +.RE +.PP +\fBoauth_cache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as +\fIcache_life_time\fR, but applied to OAuth cache only\&. If not set, the value from +\fIcache_life_time\fR +will be used\&. +.RE +.PP +\fBoauth_cache_missed\fR: \fItrue | false\fR +.RS 4 +Same as +\fIcache_missed\fR, but applied to OAuth cache only\&. If not set, the value from +\fIcache_missed\fR +will be used\&. +.RE +.PP +\fBoauth_cache_rest_failure_life_time\fR: \fItimeout()\fR +.RS 4 +\fINote\fR +about this option: added in 21\&.01\&. The time that a failure in OAuth ReST is cached\&. The default value is +\fIinfinity\fR\&. +.RE +.PP +\fBoauth_cache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as +\fIcache_size\fR, but applied to OAuth cache only\&. If not set, the value from +\fIcache_size\fR +will be used\&. +.RE +.PP +\fBoauth_client_id_check\fR: \fIallow | db | deny\fR +.RS 4 +Define whether the client authentication is always allowed, denied, or it will depend if the client ID is present in the database\&. The default value is +\fIallow\fR\&. +.RE +.PP +\fBoauth_db_type\fR: \fImnesia | sql\fR +.RS 4 +Database backend to use for OAuth authentication\&. The default value is picked from +\fIdefault_db\fR +option, or if it\(cqs not set, +\fImnesia\fR +will be used\&. +.RE +.PP +\fBoauth_expire\fR: \fItimeout()\fR +.RS 4 +Time during which the OAuth token is valid, in seconds\&. After that amount of time, the token expires and the delegated credential cannot be used and is removed from the database\&. The default is +\fI4294967\fR +seconds\&. +.RE +.PP +\fBoauth_use_cache\fR: \fItrue | false\fR +.RS 4 +Same as +\fIuse_cache\fR, but applied to OAuth cache only\&. If not set, the value from +\fIuse_cache\fR +will be used\&. +.RE +.PP +\fBoom_killer\fR: \fItrue | false\fR +.RS 4 +Enable or disable OOM (out\-of\-memory) killer\&. When system memory raises above the limit defined in +\fIoom_watermark\fR +option, ejabberd triggers OOM killer to terminate most memory consuming Erlang processes\&. Note that in order to maintain functionality, ejabberd only attempts to kill transient processes, such as those managing client sessions, s2s or database connections\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBoom_queue\fR: \fISize\fR +.RS 4 +Trigger OOM killer when some of the running Erlang processes have messages queue above this +\fISize\fR\&. Note that such processes won\(cqt be killed if +\fIoom_killer\fR +option is set to +\fIfalse\fR +or if +\fIoom_watermark\fR +is not reached yet\&. +.RE +.PP +\fBoom_watermark\fR: \fIPercent\fR +.RS 4 +A percent of total system memory consumed at which OOM killer should be activated with some of the processes possibly be killed (see +\fIoom_killer\fR +option)\&. Later, when memory drops below this +\fIPercent\fR, OOM killer is deactivated\&. The default value is +\fI80\fR +percents\&. +.RE +.PP +\fBoutgoing_s2s_families\fR: \fI[ipv6 | ipv4, \&.\&.\&.]\fR +.RS 4 +\fINote\fR +about this option: changed in 23\&.01\&. Specify which address families to try, in what order\&. The default is +\fI[ipv6, ipv4]\fR +which means it first tries connecting with IPv6, if that fails it tries using IPv4\&. This option is obsolete and irrelevant when using ejabberd 23\&.01 and Erlang/OTP 22, or newer versions of them\&. +.RE +.PP +\fBoutgoing_s2s_ipv4_address\fR: \fIAddress\fR +.RS 4 +\fINote\fR +about this option: added in 20\&.12\&. Specify the IPv4 address that will be used when establishing an outgoing S2S IPv4 connection, for example +\fI"127\&.0\&.0\&.1"\fR\&. The default value is +\fIundefined\fR\&. +.RE +.PP +\fBoutgoing_s2s_ipv6_address\fR: \fIAddress\fR +.RS 4 +\fINote\fR +about this option: added in 20\&.12\&. Specify the IPv6 address that will be used when establishing an outgoing S2S IPv6 connection, for example +\fI"::FFFF:127\&.0\&.0\&.1"\fR\&. The default value is +\fIundefined\fR\&. +.RE +.PP +\fBoutgoing_s2s_port\fR: \fI1\&.\&.65535\fR +.RS 4 +A port number to use for outgoing s2s connections when the target server doesn\(cqt have an SRV record\&. The default value is +\fI5269\fR\&. +.RE +.PP +\fBoutgoing_s2s_timeout\fR: \fItimeout()\fR +.RS 4 +The timeout in seconds for outgoing S2S connection attempts\&. The default value is +\fI10\fR +seconds\&. +.RE +.PP +\fBpam_service\fR: \fIName\fR +.RS 4 +This option defines the +\fIauthentication\&.md#pam\-authentication|PAM\fR +service name\&. Refer to the PAM documentation of your operation system for more information\&. The default value is +\fIejabberd\fR\&. +.RE +.PP +\fBpam_userinfotype\fR: \fIusername | jid\fR +.RS 4 +This option defines what type of information about the user ejabberd provides to the +\fIauthentication\&.md#pam\-authentication|PAM\fR +service: only the username, or the user\(cqs JID\&. Default is +\fIusername\fR\&. +.RE +.PP +\fBpgsql_users_number_estimate\fR: \fItrue | false\fR +.RS 4 +Whether to use PostgreSQL estimation when counting registered users\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBqueue_dir\fR: \fIDirectory\fR +.RS 4 +If +\fIqueue_type\fR +option is set to +\fIfile\fR, use this +\fIDirectory\fR +to store file queues\&. The default is to keep queues inside Mnesia directory\&. +.RE +.PP +\fBqueue_type\fR: \fIram | file\fR +.RS 4 +Default type of queues in ejabberd\&. Modules may have its own value of the option\&. The value of +\fIram\fR +means that queues will be kept in memory\&. If value +\fIfile\fR +is set, you may also specify directory in +\fIqueue_dir\fR +option where file queues will be placed\&. The default value is +\fIram\fR\&. +.RE +.PP +\fBredis_connect_timeout\fR: \fItimeout()\fR +.RS 4 +A timeout to wait for the connection to be re\-established to the +\fIdatabase\&.md#redis|Redis\fR +server\&. The default is +\fI1 second\fR\&. +.RE +.PP +\fBredis_db\fR: \fINumber\fR +.RS 4 +\fIdatabase\&.md#redis|Redis\fR +database number\&. The default is +\fI0\fR\&. +.RE +.PP +\fBredis_password\fR: \fIPassword\fR +.RS 4 +The password to the +\fIdatabase\&.md#redis|Redis\fR +server\&. The default is an empty string, i\&.e\&. no password\&. +.RE +.PP +\fBredis_pool_size\fR: \fINumber\fR +.RS 4 +The number of simultaneous connections to the +\fIdatabase\&.md#redis|Redis\fR +server\&. The default value is +\fI10\fR\&. +.RE +.PP +\fBredis_port\fR: \fI1\&.\&.65535\fR +.RS 4 +The port where the +\fIdatabase\&.md#redis|Redis\fR +server is accepting connections\&. The default is +\fI6379\fR\&. +.RE +.PP +\fBredis_queue_type\fR: \fIram | file\fR +.RS 4 +The type of request queue for the +\fIdatabase\&.md#redis|Redis\fR +server\&. See description of +\fIqueue_type\fR +option for the explanation\&. The default value is the value defined in +\fIqueue_type\fR +or +\fIram\fR +if the latter is not set\&. +.RE +.PP +\fBredis_server\fR: \fIHost | IP Address | Unix Socket Path\fR +.RS 4 +\fINote\fR +about this option: improved in 24\&.12\&. A hostname, IP address or unix domain socket file of the +\fIdatabase\&.md#redis|Redis\fR +server\&. Setup the path to unix domain socket like: +\fI"unix:/path/to/socket"\fR\&. The default value is +\fIlocalhost\fR\&. +.RE +.PP +\fBregistration_timeout\fR: \fItimeout()\fR +.RS 4 +This is a global option for module +\fImod_register\fR\&. It limits the frequency of registrations from a given IP or username\&. So, a user that tries to register a new account from the same IP address or JID during this time after their previous registration will receive an error with the corresponding explanation\&. To disable this limitation, set the value to +\fIinfinity\fR\&. The default value is +\fI600 seconds\fR\&. +.RE +.PP +\fBresource_conflict\fR: \fIsetresource | closeold | closenew\fR +.RS 4 +NOTE: this option is deprecated and may be removed anytime in the future versions\&. The possible values match exactly the three possibilities described in +XMPP Core: section 7\&.7\&.2\&.2\&. The default value is +\fIcloseold\fR\&. If the client uses old Jabber Non\-SASL authentication (XEP\-0078), then this option is not respected, and the action performed is +\fIcloseold\fR\&. +.RE +.PP +\fBrest_proxy\fR: \fIHost\fR +.RS 4 +\fINote\fR +about this option: added in 25\&.07\&. Address of a HTTP Connect proxy used by modules issuing rest calls (like ejabberd_oauth_rest) +.RE +.PP +\fBrest_proxy_password\fR: \fIstring()\fR +.RS 4 +\fINote\fR +about this option: added in 25\&.07\&. Password used to authenticate to HTTP Connect proxy used by modules issuing rest calls (like ejabberd_oauth_rest) +.RE +.PP +\fBrest_proxy_port\fR: \fI1\&.\&.65535\fR +.RS 4 +\fINote\fR +about this option: added in 25\&.07\&. Port of a HTTP Connect proxy used by modules issuing rest calls (like ejabberd_oauth_rest) +.RE +.PP +\fBrest_proxy_username\fR: \fIstring()\fR +.RS 4 +\fINote\fR +about this option: added in 25\&.07\&. Username used to authenticate to HTTP Connect proxy used by modules issuing rest calls (like ejabberd_oauth_rest) +.RE +.PP +\fBrouter_cache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as +\fIcache_life_time\fR, but applied to routing table cache only\&. If not set, the value from +\fIcache_life_time\fR +will be used\&. +.RE +.PP +\fBrouter_cache_missed\fR: \fItrue | false\fR +.RS 4 +Same as +\fIcache_missed\fR, but applied to routing table cache only\&. If not set, the value from +\fIcache_missed\fR +will be used\&. +.RE +.PP +\fBrouter_cache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as +\fIcache_size\fR, but applied to routing table cache only\&. If not set, the value from +\fIcache_size\fR +will be used\&. +.RE +.PP +\fBrouter_db_type\fR: \fImnesia | redis | sql\fR +.RS 4 +Database backend to use for routing information\&. The default value is picked from +\fIdefault_ram_db\fR +option, or if it\(cqs not set, +\fImnesia\fR +will be used\&. +.RE +.PP +\fBrouter_use_cache\fR: \fItrue | false\fR +.RS 4 +Same as +\fIuse_cache\fR, but applied to routing table cache only\&. If not set, the value from +\fIuse_cache\fR +will be used\&. +.RE +.PP +\fBrpc_timeout\fR: \fItimeout()\fR +.RS 4 +A timeout for remote function calls between nodes in an ejabberd cluster\&. You should probably never change this value since those calls are used for internal needs only\&. The default value is +\fI5\fR +seconds\&. +.RE +.PP +\fBs2s_access\fR: \fIAccess\fR +.RS 4 +This +\fIbasic\&.md#access\-rules|Access Rule\fR +defines to what remote servers can s2s connections be established\&. The default value is +\fIall\fR; no restrictions are applied, it is allowed to connect s2s to/from all remote servers\&. +.RE +.PP +\fBs2s_cafile\fR: \fIPath\fR +.RS 4 +A path to a file with CA root certificates that will be used to authenticate s2s connections\&. If not set, the value of +\fIca_file\fR +will be used\&. +.RE +.sp +You can use \fIhost_config\fR to specify this option per\-vhost\&. +.PP +\fBs2s_ciphers\fR: \fI[Cipher, \&.\&.\&.]\fR +.RS 4 +A list of OpenSSL ciphers to use for s2s connections\&. The default value is shown in the example below: +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +s2s_ciphers: + \- HIGH + \- "!aNULL" + \- "!eNULL" + \- "!3DES" + \- "@STRENGTH" +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBs2s_dhfile\fR: \fIPath\fR +.RS 4 +Full path to a file containing custom DH parameters to use for s2s connections\&. Such a file could be created with the command +\fI"openssl dhparam \-out dh\&.pem 2048"\fR\&. If this option is not specified, 2048\-bit MODP Group with 256\-bit Prime Order Subgroup will be used as defined in RFC5114 Section 2\&.3\&. +.RE +.PP +\fBs2s_dns_retries\fR: \fINumber\fR +.RS 4 +DNS resolving retries\&. The default value is +\fI2\fR\&. +.RE +.PP +\fBs2s_dns_timeout\fR: \fItimeout()\fR +.RS 4 +The timeout for DNS resolving\&. The default value is +\fI10\fR +seconds\&. +.RE +.PP +\fBs2s_max_retry_delay\fR: \fItimeout()\fR +.RS 4 +The maximum allowed delay for s2s connection retry to connect after a failed connection attempt\&. The default value is +\fI300\fR +seconds (5 minutes)\&. +.RE +.PP +\fBs2s_protocol_options\fR: \fI[Option, \&.\&.\&.]\fR +.RS 4 +List of general SSL options to use for s2s connections\&. These map to OpenSSL\(cqs +\fIset_options()\fR\&. The default value is shown in the example below: +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +s2s_protocol_options: + \- no_sslv3 + \- cipher_server_preference + \- no_compression +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBs2s_queue_type\fR: \fIram | file\fR +.RS 4 +The type of a queue for s2s packets\&. See description of +\fIqueue_type\fR +option for the explanation\&. The default value is the value defined in +\fIqueue_type\fR +or +\fIram\fR +if the latter is not set\&. +.RE +.PP +\fBs2s_timeout\fR: \fItimeout()\fR +.RS 4 +A time to wait before closing an idle s2s connection\&. The default value is +\fI1\fR +hour\&. +.RE +.PP +\fBs2s_tls_compression\fR: \fItrue | false\fR +.RS 4 +Whether to enable or disable TLS compression for s2s connections\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBs2s_use_starttls\fR: \fItrue | false | optional | required\fR +.RS 4 +Whether to use STARTTLS for s2s connections\&. The value of +\fIfalse\fR +means STARTTLS is prohibited\&. The value of +\fItrue\fR +or +\fIoptional\fR +means STARTTLS is enabled but plain connections are still allowed\&. And the value of +\fIrequired\fR +means that only STARTTLS connections are allowed\&. The default value is +\fIfalse\fR +(for historical reasons)\&. +.RE +.PP +\fBs2s_zlib\fR: \fItrue | false\fR +.RS 4 +Whether to use +\fIzlib\fR +compression (as defined in +XEP\-0138) or not\&. The default value is +\fIfalse\fR\&. WARNING: this type of compression is nowadays considered insecure\&. +.RE +.PP +\fBshaper\fR: \fI{ShaperName: Rate}\fR +.RS 4 +The option defines a set of +\fI\&.\&./configuration/basic\&.md#shapers|shapers\fR\&. Every shaper is assigned a name +\fIShaperName\fR +that can be used in other parts of the configuration file, such as +\fIshaper_rules\fR +option\&. The shaper itself is defined by its +\fIRate\fR, where +\fIRate\fR +stands for the maximum allowed incoming rate in +\fBbytes\fR +per second\&. When a connection exceeds this limit, ejabberd stops reading from the socket until the average rate is again below the allowed maximum\&. In the example below shaper +\fInormal\fR +limits the traffic speed to 1,000 bytes/sec and shaper +\fIfast\fR +limits the traffic speed to 50,000 bytes/sec: +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +shaper: + normal: 1000 + fast: 50000 +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBshaper_rules\fR: \fI{ShaperRuleName: {Number|ShaperName: ACLName|ACLDefinition}}\fR +.RS 4 +This option defines +\fI\&.\&./configuration/basic\&.md#shaper\-rules|shaper rules\fR +to use for matching user/hosts\&. Semantics is similar to +\fIaccess_rules\fR +option, the only difference is that instead using +\fIallow\fR +or +\fIdeny\fR, a name of a shaper (defined in +\fIshaper\fR +option) or a positive number should be used\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +shaper_rules: + connections_limit: + 10: + user: peter@example\&.com + 100: admin + 5: all + download_speed: + fast: admin + slow: anonymous_users + normal: all + log_days: 30 +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBsm_cache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as +\fIcache_life_time\fR, but applied to client sessions table cache only\&. If not set, the value from +\fIcache_life_time\fR +will be used\&. +.RE +.PP +\fBsm_cache_missed\fR: \fItrue | false\fR +.RS 4 +Same as +\fIcache_missed\fR, but applied to client sessions table cache only\&. If not set, the value from +\fIcache_missed\fR +will be used\&. +.RE +.PP +\fBsm_cache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as +\fIcache_size\fR, but applied to client sessions table cache only\&. If not set, the value from +\fIcache_size\fR +will be used\&. +.RE +.PP +\fBsm_db_type\fR: \fImnesia | redis | sql\fR +.RS 4 +Database backend to use for client sessions information\&. The default value is picked from +\fIdefault_ram_db\fR +option, or if it\(cqs not set, +\fImnesia\fR +will be used\&. +.RE +.PP +\fBsm_use_cache\fR: \fItrue | false\fR +.RS 4 +Same as +\fIuse_cache\fR, but applied to client sessions table cache only\&. If not set, the value from +\fIuse_cache\fR +will be used\&. +.RE +.PP +\fBsql_connect_timeout\fR: \fItimeout()\fR +.RS 4 +A time to wait for connection to an SQL server to be established\&. The default value is +\fI5\fR +seconds\&. +.RE +.PP +\fBsql_database\fR: \fIDatabase\fR +.RS 4 +An SQL database name\&. For SQLite this must be a full path to a database file\&. The default value is +\fIejabberd\fR\&. +.RE +.PP +\fBsql_flags\fR: \fI[mysql_alternative_upsert]\fR +.RS 4 +\fINote\fR +about this option: added in 24\&.02\&. This option accepts a list of SQL flags, and is empty by default\&. +\fImysql_alternative_upsert\fR +forces the alternative upsert implementation in MySQL\&. +.RE +.PP +\fBsql_keepalive_interval\fR: \fItimeout()\fR +.RS 4 +An interval to make a dummy SQL request to keep alive the connections to the database\&. There is no default value, so no keepalive requests are made\&. +.RE +.PP +\fBsql_odbc_driver\fR: \fIPath\fR +.RS 4 +\fINote\fR +about this option: added in 20\&.12\&. Path to the ODBC driver to use to connect to a Microsoft SQL Server database\&. This option only applies if the +\fIsql_type\fR +option is set to +\fImssql\fR +and +\fIsql_server\fR +is not an ODBC connection string\&. The default value is: +\fIlibtdsodbc\&.so\fR +.RE +.PP +\fBsql_password\fR: \fIPassword\fR +.RS 4 +The password for SQL authentication\&. The default is empty string\&. +.RE +.PP +\fBsql_pool_size\fR: \fISize\fR +.RS 4 +Number of connections to the SQL server that ejabberd will open for each virtual host\&. The default value is +\fI10\fR\&. WARNING: for SQLite this value is +\fI1\fR +by default and it\(cqs not recommended to change it due to potential race conditions\&. +.RE +.PP +\fBsql_port\fR: \fI1\&.\&.65535\fR +.RS 4 +The port where the SQL server is accepting connections\&. The default is +\fI3306\fR +for MySQL, +\fI5432\fR +for PostgreSQL and +\fI1433\fR +for MS SQL\&. The option has no effect for SQLite\&. +.RE +.PP +\fBsql_prepared_statements\fR: \fItrue | false\fR +.RS 4 +\fINote\fR +about this option: added in 20\&.01\&. This option is +\fItrue\fR +by default, and is useful to disable prepared statements\&. The option is valid for PostgreSQL and MySQL\&. +.RE +.PP +\fBsql_query_timeout\fR: \fItimeout()\fR +.RS 4 +A time to wait for an SQL query response\&. The default value is +\fI60\fR +seconds\&. +.RE +.PP +\fBsql_queue_type\fR: \fIram | file\fR +.RS 4 +The type of a request queue for the SQL server\&. See description of +\fIqueue_type\fR +option for the explanation\&. The default value is the value defined in +\fIqueue_type\fR +or +\fIram\fR +if the latter is not set\&. +.RE +.PP +\fBsql_server\fR: \fIHost | IP Address | ODBC Connection String | Unix Socket Path\fR +.RS 4 +\fINote\fR +about this option: improved in 24\&.06\&. The hostname or IP address of the SQL server\&. For +\fIsql_type\fR +\fImssql\fR +or +\fIodbc\fR +this can also be an ODBC connection string\&. When +\fIsql_type\fR +is +\fImysql\fR +or +\fIpgsql\fR, this can be the path to a unix domain socket expressed like: +\fI"unix:/path/to/socket"\fR\&.The default value is +\fIlocalhost\fR\&. +.RE +.PP +\fBsql_ssl\fR: \fItrue | false\fR +.RS 4 +\fINote\fR +about this option: improved in 20\&.03\&. Whether to use SSL encrypted connections to the SQL server\&. The option is only available for MySQL, MS SQL and PostgreSQL\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBsql_ssl_cafile\fR: \fIPath\fR +.RS 4 +A path to a file with CA root certificates that will be used to verify SQL connections\&. Implies +\fIsql_ssl\fR +and +\fIsql_ssl_verify\fR +options are set to +\fItrue\fR\&. There is no default which means certificate verification is disabled\&. This option has no effect for MS SQL\&. +.RE +.PP +\fBsql_ssl_certfile\fR: \fIPath\fR +.RS 4 +A path to a certificate file that will be used for SSL connections to the SQL server\&. Implies +\fIsql_ssl\fR +option is set to +\fItrue\fR\&. There is no default which means ejabberd won\(cqt provide a client certificate to the SQL server\&. This option has no effect for MS SQL\&. +.RE +.PP +\fBsql_ssl_verify\fR: \fItrue | false\fR +.RS 4 +Whether to verify SSL connection to the SQL server against CA root certificates defined in +\fIsql_ssl_cafile\fR +option\&. Implies +\fIsql_ssl\fR +option is set to +\fItrue\fR\&. This option has no effect for MS SQL\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBsql_start_interval\fR: \fItimeout()\fR +.RS 4 +A time to wait before retrying to restore failed SQL connection\&. The default value is +\fI30\fR +seconds\&. +.RE +.PP +\fBsql_type\fR: \fImssql | mysql | odbc | pgsql | sqlite\fR +.RS 4 +The type of an SQL connection\&. The default is +\fIodbc\fR\&. +.RE +.PP +\fBsql_username\fR: \fIUsername\fR +.RS 4 +A user name for SQL authentication\&. The default value is +\fIejabberd\fR\&. +.RE +.PP +\fBtrusted_proxies\fR: \fIall | [Network1, Network2, \&.\&.\&.]\fR +.RS 4 +Specify what proxies are trusted when an HTTP request contains the header +\fIX\-Forwarded\-For\fR\&. You can specify +\fIall\fR +to allow all proxies, or specify a list of IPs, possibly with masks\&. The default value is an empty list\&. Using this option you can know the real IP of the request, for admin purpose, or security configuration (for example using +\fImod_fail2ban\fR)\&. IMPORTANT: The proxy MUST be configured to set the +\fIX\-Forwarded\-For\fR +header if you enable this option as, otherwise, the client can set it itself and as a result the IP value cannot be trusted for security rules in ejabberd\&. +.RE +.PP +\fBupdate_sql_schema\fR: \fItrue | false\fR +.RS 4 +\fINote\fR +about this option: updated in 24\&.06\&. Allow ejabberd to update SQL schema in MySQL, PostgreSQL and SQLite databases\&. This option was added in ejabberd 23\&.10, and enabled by default since 24\&.06\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBupdate_sql_schema_timeout\fR: \fItimeout()\fR +.RS 4 +\fINote\fR +about this option: added in 24\&.07\&. Time allocated to SQL schema update queries\&. The default value is set to 5 minutes\&. +.RE +.PP +\fBuse_cache\fR: \fItrue | false\fR +.RS 4 +Enable or disable cache\&. The default is +\fItrue\fR\&. Several modules have a similar option; and some core ejabberd parts support similar options too, see +\fIauth_use_cache\fR, +\fIoauth_use_cache\fR, +\fIrouter_use_cache\fR, and +\fIsm_use_cache\fR\&. +.RE +.PP +\fBvalidate_stream\fR: \fItrue | false\fR +.RS 4 +Whether to validate any incoming XML packet according to the schemas of +supported XMPP extensions\&. WARNING: the validation is only intended for the use by client developers \- don\(cqt enable it in production environment\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBversion\fR: \fIstring()\fR +.RS 4 +The option can be used to set custom ejabberd version, that will be used by different parts of ejabberd, for example by +\fImod_version\fR +module\&. The default value is obtained at compile time from the underlying version control system\&. +.RE +.PP +\fBwebsocket_origin\fR: \fIignore | URL\fR +.RS 4 +This option enables validation for +\fIOrigin\fR +header to protect against connections from other domains than given in the configuration file\&. In this way, the lower layer load balancer can be chosen for a specific ejabberd implementation while still providing a secure WebSocket connection\&. The default value is +\fIignore\fR\&. An example value of the +\fIURL\fR +is +\fI"https://test\&.example\&.org:8081"\fR\&. +.RE +.PP +\fBwebsocket_ping_interval\fR: \fItimeout()\fR +.RS 4 +Defines time between pings sent by the server to a client (WebSocket level protocol pings are used for this) to keep a connection active\&. If the client doesn\(cqt respond to two consecutive pings, the connection will be assumed as closed\&. The value of +\fI0\fR +can be used to disable the feature\&. This option makes the server sending pings only for connections using the RFC compliant protocol\&. For older style connections the server expects that whitespace pings would be used for this purpose\&. The default value is +\fI60\fR +seconds\&. +.RE +.PP +\fBwebsocket_timeout\fR: \fItimeout()\fR +.RS 4 +Amount of time without any communication after which the connection would be closed\&. The default value is +\fI300\fR +seconds\&. +.RE +.SH "MODULES" +.sp +This section describes modules options of ejabberd 25\&.08\&. The modules that changed in this version are marked with 🟤\&. +.SS "mod_adhoc" +.sp +def:ad\-hoc command +.sp +: Command that can be executed by an XMPP client using XEP\-0050\&. +.sp +This module implements XEP\-0050: Ad\-Hoc Commands\&. It\(cqs an auxiliary module and is only needed by some of the other modules\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBreport_commands_node\fR: \fItrue | false\fR +.RS 4 +Provide the Commands item in the Service Discovery\&. Default value: +\fIfalse\fR\&. +.RE +.RE +.SS "mod_adhoc_api" +.sp +\fINote\fR about this option: added in 25\&.03\&. +.sp +Execute (def:API commands) in a XMPP client using XEP\-0050: Ad\-Hoc Commands\&. This module requires \fImod_adhoc\fR (to execute the commands), and recommends \fImod_disco\fR (to discover the commands)\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBdefault_version\fR: \fIinteger() | string()\fR +.RS 4 +What API version to use\&. If setting an ejabberd version, it will use the latest API version that was available in that (def:c2s) ejabberd version\&. For example, setting +\fI"24\&.06"\fR +in this option implies +\fI2\fR\&. The default value is the latest version\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +acl: + admin: + user: jan@localhost + +api_permissions: + "adhoc commands": + from: mod_adhoc_api + who: admin + what: + \- "[tag:roster]" + \- "[tag:session]" + \- stats + \- status + +modules: + mod_adhoc_api: + default_version: 2 +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_admin_extra" +.sp +This module provides additional administrative commands\&. +.sp +Details for some commands: +.sp +\fIban_account\fR API: This command kicks all the connected sessions of the account from the server\&. It also changes their password to a randomly generated one, so they can\(cqt login anymore unless a server administrator changes their password again\&. It is possible to define the reason of the ban\&. The new password also includes the reason and the date and time of the ban\&. See an example below\&. +.sp +\fIpush_roster\fR API (and \fIpush_roster_all\fR API): The roster file must be placed, if using Windows, on the directory where you installed ejabberd: C:/Program Files/ejabberd or similar\&. If you use other Operating System, place the file on the same directory where the \&.beam files are installed\&. See below an example roster file\&. +.sp +\fIsrg_create\fR API: If you want to put a group Name with blank spaces, use the characters \fI"\fR\*(Aq and \fI\*(Aq"\fR to define when the Name starts and ends\&. See an example below\&. +.sp +The module has no options\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExamples:\fR +.RS 4 +.sp +With this configuration, vCards can only be modified with mod_admin_extra commands: +.sp +.if n \{\ +.RS 4 +.\} +.nf +acl: + adminextraresource: + \- resource: "modadminextraf8x,31ad" +access_rules: + vcard_set: + \- allow: adminextraresource +modules: + mod_admin_extra: {} + mod_vcard: + access_set: vcard_set +.fi +.if n \{\ +.RE +.\} +.sp +Content of roster file for \fIpush_roster\fR API: +.sp +.if n \{\ +.RS 4 +.\} +.nf +[{<<"bob">>, <<"example\&.org">>, <<"workers">>, <<"Bob">>}, +{<<"mart">>, <<"example\&.org">>, <<"workers">>, <<"Mart">>}, +{<<"Rich">>, <<"example\&.org">>, <<"bosses">>, <<"Rich">>}]\&. +.fi +.if n \{\ +.RE +.\} +.sp +With this call, the sessions of the local account which JID is \fIboby@example\&.org\fR will be kicked, and its password will be set to something like \fIBANNED_ACCOUNT\(em20080425T21:45:07\(em2176635\(emSpammed_rooms\fR +.sp +.if n \{\ +.RS 4 +.\} +.nf +ejabberdctl vhost example\&.org ban_account boby "Spammed rooms" +.fi +.if n \{\ +.RE +.\} +.sp +Call to \fIsrg_create\fR API using double\-quotes and single\-quotes: +.sp +.if n \{\ +.RS 4 +.\} +.nf +ejabberdctl srg_create g1 example\&.org "\*(AqGroup number 1\*(Aq" this_is_g1 g1 +.fi +.if n \{\ +.RE +.\} +.sp +\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#accounts|accounts\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#erlang|erlang\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#last|last\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#private|private\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#purge|purge\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#roster|roster\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#session|session\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#shared_roster_group|shared_roster_group\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#stanza|stanza\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#statistics|statistics\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#vcard|vcard\fR +.RE +.SS "mod_admin_update_sql" +.sp +This module can be used to update existing SQL database from the default to the new schema\&. Check the section \fIdatabase\&.md#default\-and\-new\-schemas|Default and New Schemas\fR for details\&. Please note that only MS SQL, MySQL, and PostgreSQL are supported\&. When the module is loaded use \fIupdate_sql\fR API\&. +.sp +The module has no options\&. +.SS "mod_announce" +.sp +This module enables configured users to broadcast announcements and to set the message of the day (MOTD)\&. Configured users can perform these actions with an XMPP client either using Ad\-Hoc Commands or sending messages to specific JIDs\&. +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBNote\fR +.ps -1 +.br +.sp +This module can be resource intensive on large deployments as it may broadcast a lot of messages\&. This module should be disabled for instances of ejabberd with hundreds of thousands users\&. +.sp .5v +.RE +.sp +To send announcements using XEP\-0050: Ad\-Hoc Commands, this module requires \fImod_adhoc\fR (to execute the commands), and recommends \fImod_disco\fR (to discover the commands)\&. +.sp +To send announcements by sending messages to specific JIDs, these are the destination JIDs: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIexample\&.org/announce/all\fR: Send the message to all registered users in that vhost\&. If the user is online and connected to several resources, only the resource with the highest priority will receive the message\&. If the registered user is not connected, the message is stored offline in assumption that offline storage (see +\fImod_offline\fR) is enabled\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIexample\&.org/announce/online\fR: Send the message to all connected users\&. If the user is online and connected to several resources, all resources will receive the message\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIexample\&.org/announce/motd\fR: Set the message of the day (MOTD) that is sent to users when they login\&. Also sends the message to all connected users (similar to +\fIannounce/online\fR)\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIexample\&.org/announce/motd/update\fR: Set the message of the day (MOTD) that is sent to users when they login\&. This does not send the message to any currently connected user\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIexample\&.org/announce/motd/delete\fR: Remove the existing message of the day (MOTD) by sending a message to this JID\&. +.RE +.sp +There are similar destination JIDs to apply to all virtual hosts in ejabberd: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIexample\&.org/announce/all\-hosts/all\fR: send to all registered accounts +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIexample\&.org/announce/all\-hosts/online\fR: send to online sessions +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIexample\&.org/announce/all\-hosts/motd\fR: set MOTD and send to online +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIexample\&.org/announce/all\-hosts/motd/update\fR: update MOTD +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIexample\&.org/announce/all\-hosts/motd/delete\fR: delete MOTD +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess\fR: \fIAccessName\fR +.RS 4 +This option specifies who is allowed to send announcements and to set the message of the day\&. The default value is +\fInone\fR +(i\&.e\&. nobody is able to send such messages)\&. +.RE +.PP +\fBcache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as top\-level +\fIcache_life_time\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_missed\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIcache_missed\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as top\-level +\fIcache_size\fR +option, but applied to this module only\&. +.RE +.PP +\fBdb_type\fR: \fImnesia | sql\fR +.RS 4 +Same as top\-level +\fIdefault_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBuse_cache\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIuse_cache\fR +option, but applied to this module only\&. +.RE +.RE +.SS "mod_antispam" +.sp +\fINote\fR about this option: added in 25\&.07\&. +.sp +Filter spam messages and subscription requests received from remote servers based on Real\-Time Block Lists (RTBL), lists of known spammer JIDs and/or URLs mentioned in spam messages\&. Traffic classified as spam is rejected with an error (and an \fI[info]\fR message is logged) unless the sender is subscribed to the recipient\(cqs presence\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess_spam\fR: \fIAccess\fR +.RS 4 +Access rule that controls what accounts may receive spam messages\&. If the rule returns +\fIallow\fR +for a given recipient, spam messages aren\(cqt rejected for that recipient\&. The default value is +\fInone\fR, which means that all recipients are subject to spam filtering verification\&. +.RE +.PP +\fBcache_size\fR: \fIpos_integer()\fR +.RS 4 +Maximum number of JIDs that will be cached due to sending spam URLs\&. If that limit is exceeded, the least recently used entries are removed from the cache\&. Setting this option to +\fI0\fR +disables the caching feature\&. Note that separate caches are used for each virtual host, and that the caches aren\(cqt distributed across cluster nodes\&. The default value is +\fI10000\fR\&. +.RE +.PP +\fBrtbl_services\fR: \fI[Service]\fR +.RS 4 +Query a RTBL service to get domains to block, as provided by +xmppbl\&.org\&. Please note right now this option only supports one service in that list\&. For blocking spam and abuse on MUC channels, please use +\fImod_muc_rtbl\fR +for now\&. If only the host is provided, the default node names will be assumed\&. If the node name is different than +\fIspam_source_domains\fR, you can setup the custom node name with the option +\fIspam_source_domains_node\fR\&. The default value is an empty list of services\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +rtbl_services: + \- pubsub\&.server1\&.localhost: + spam_source_domains_node: actual_custom_pubsub_node +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBspam_domains_file\fR: \fInone | Path\fR +.RS 4 +Path to a plain text file containing a list of known spam domains, one domain per line\&. Messages and subscription requests sent from one of the listed domains are classified as spam if sender is not in recipient\(cqs roster\&. This list of domains gets merged with the one retrieved by an RTBL host if any given\&. Use an absolute path, or the +\fI@CONFIG_PATH@\fR +predefined keyword +if the file is available in the configuration directory\&. The default value is +\fInone\fR\&. +.RE +.PP +\fBspam_dump_file\fR: \fIfalse | true | Path\fR +.RS 4 +Path to the file to store blocked messages\&. Use an absolute path, or the +\fI@LOG_PATH@\fR +predefined keyword +to store logs in the same place that the other ejabberd log files\&. If set to +\fIfalse\fR, it doesn\(cqt dump stanzas, which is the default\&. If set to +\fItrue\fR, it stores in +\fI"@LOG_PATH@/spam_dump_@HOST@\&.log"\fR\&. +.RE +.PP +\fBspam_jids_file\fR: \fInone | Path\fR +.RS 4 +Path to a plain text file containing a list of known spammer JIDs, one JID per line\&. Messages and subscription requests sent from one of the listed JIDs are classified as spam\&. Messages containing at least one of the listed JIDsare classified as spam as well\&. Furthermore, the sender\(cqs JID will be cached, so that future traffic originating from that JID will also be classified as spam\&. Use an absolute path, or the +\fI@CONFIG_PATH@\fR +predefined keyword +if the file is available in the configuration directory\&. The default value is +\fInone\fR\&. +.RE +.PP +\fBspam_urls_file\fR: \fInone | Path\fR +.RS 4 +Path to a plain text file containing a list of URLs known to be mentioned in spam message bodies\&. Messages containing at least one of the listed URLs are classified as spam\&. Furthermore, the sender\(cqs JID will be cached, so that future traffic originating from that JID will be classified as spam as well\&. Use an absolute path, or the +\fI@CONFIG_PATH@\fR +predefined keyword +if the file is available in the configuration directory\&. The default value is +\fInone\fR\&. +.RE +.PP +\fBwhitelist_domains_file\fR: \fInone | Path\fR +.RS 4 +Path to a file containing a list of domains to whitelist from being blocked, one per line\&. If either it is in +\fIspam_domains_file\fR +or more realistically in a domain sent by a RTBL host (see option +\fIrtbl_services\fR) then this domain will be ignored and stanzas from there won\(cqt be blocked\&. Use an absolute path, or the +\fI@CONFIG_PATH@\fR +predefined keyword +if the file is available in the configuration directory\&. The default value is +\fInone\fR\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_antispam: + rtbl_services: + \- xmppbl\&.org + spam_jids_file: "@CONFIG_PATH@/spam_jids\&.txt" + spam_dump_file: "@LOG_PATH@/spam/host\-@HOST@\&.log" +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_auth_fast" +.sp +\fINote\fR about this option: added in 24\&.12\&. +.sp +The module adds support for XEP\-0484: Fast Authentication Streamlining Tokens that allows users to authenticate using self\-managed tokens\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBdb_type\fR: \fImnesia\fR +.RS 4 +Same as top\-level +\fIdefault_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBtoken_lifetime\fR: \fItimeout()\fR +.RS 4 +Time that tokens will be kept, measured from it\(cqs creation time\&. Default value set to 30 days +.RE +.PP +\fBtoken_refresh_age\fR: \fItimeout()\fR +.RS 4 +This time determines age of token, that qualifies for automatic refresh\&. Default value set to 1 day +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_auth_fast: + token_lifetime: 14days +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_avatar" +.sp +The purpose of the module is to cope with legacy and modern XMPP clients posting avatars\&. The process is described in XEP\-0398: User Avatar to vCard\-Based Avatars Conversion\&. +.sp +Also, the module supports conversion between avatar image formats on the fly\&. +.sp +The module depends on \fImod_vcard\fR, \fImod_vcard_xupdate\fR and \fImod_pubsub\fR\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBconvert\fR: \fI{From: To}\fR +.RS 4 +Defines image conversion rules: the format in +\fIFrom\fR +will be converted to format in +\fITo\fR\&. The value of +\fIFrom\fR +can also be +\fIdefault\fR, which is match\-all rule\&. NOTE: the list of supported formats is detected at compile time depending on the image libraries installed in the system\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +convert: + webp: jpg + default: png +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBrate_limit\fR: \fINumber\fR +.RS 4 +Limit any given JID by the number of avatars it is able to convert per minute\&. This is to protect the server from image conversion DoS\&. The default value is +\fI10\fR\&. +.RE +.RE +.SS "mod_block_strangers" +.sp +This module blocks and logs any messages coming from an unknown entity\&. If a writing entity is not in your roster, you can let this module drop and/or log the message\&. By default you\(cqll just not receive message from that entity\&. Enable this module if you want to drop SPAM messages\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess\fR: \fIAccessName\fR +.RS 4 +The option is supposed to be used when +\fIallow_local_users\fR +and +\fIallow_transports\fR +are not enough\&. It\(cqs an ACL where +\fIdeny\fR +means the message will be rejected (or a CAPTCHA would be generated for a presence, if configured), and +\fIallow\fR +means the sender is whitelisted and the stanza will pass through\&. The default value is +\fInone\fR, which means nothing is whitelisted\&. +.RE +.PP +\fBallow_local_users\fR: \fItrue | false\fR +.RS 4 +This option specifies if strangers from the same local host should be accepted or not\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBallow_transports\fR: \fItrue | false\fR +.RS 4 +If set to +\fItrue\fR +and some server\(cqs JID is in user\(cqs roster, then messages from any user of this server are accepted even if no subscription present\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBcaptcha\fR: \fItrue | false\fR +.RS 4 +Whether to generate CAPTCHA or not in response to messages from strangers\&. See also section +\fIbasic\&.md#captcha|CAPTCHA\fR +of the Configuration Guide\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBdrop\fR: \fItrue | false\fR +.RS 4 +This option specifies if strangers messages should be dropped or not\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBlog\fR: \fItrue | false\fR +.RS 4 +This option specifies if strangers\*(Aq messages should be logged (as info message) in ejabberd\&.log\&. The default value is +\fIfalse\fR\&. +.RE +.RE +.SS "mod_blocking" +.sp +The module implements XEP\-0191: Blocking Command\&. +.sp +This module depends on \fImod_privacy\fR where all the configuration is performed\&. +.sp +The module has no options\&. +.SS "mod_bosh" +.sp +This module implements XMPP over BOSH as defined in XEP\-0124 and XEP\-0206\&. BOSH stands for Bidirectional\-streams Over Synchronous HTTP\&. It makes it possible to simulate long lived connections required by XMPP over the HTTP protocol\&. In practice, this module makes it possible to use XMPP in a browser without WebSocket support and more generally to have a way to use XMPP while having to get through an HTTP proxy\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBcache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as top\-level +\fIcache_life_time\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_missed\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIcache_missed\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as top\-level +\fIcache_size\fR +option, but applied to this module only\&. +.RE +.PP +\fBjson\fR: \fItrue | false\fR +.RS 4 +This option has no effect\&. +.RE +.PP +\fBmax_concat\fR: \fIpos_integer() | infinity\fR +.RS 4 +This option limits the number of stanzas that the server will send in a single bosh request\&. The default value is +\fIunlimited\fR\&. +.RE +.PP +\fBmax_inactivity\fR: \fItimeout()\fR +.RS 4 +The option defines the maximum inactivity period\&. The default value is +\fI30\fR +seconds\&. +.RE +.PP +\fBmax_pause\fR: \fIpos_integer()\fR +.RS 4 +Indicate the maximum length of a temporary session pause (in seconds) that a client can request\&. The default value is +\fI120\fR\&. +.RE +.PP +\fBprebind\fR: \fItrue | false\fR +.RS 4 +If enabled, the client can create the session without going through authentication\&. Basically, it creates a new session with anonymous authentication\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBqueue_type\fR: \fIram | file\fR +.RS 4 +Same as top\-level +\fIqueue_type\fR +option, but applied to this module only\&. +.RE +.PP +\fBram_db_type\fR: \fImnesia | sql | redis\fR +.RS 4 +Same as top\-level +\fIdefault_ram_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBuse_cache\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIuse_cache\fR +option, but applied to this module only\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +listen: + \- + port: 5222 + module: ejabberd_c2s + \- + port: 5443 + module: ejabberd_http + request_handlers: + /bosh: mod_bosh + +modules: + mod_bosh: {} +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_caps" +.sp +This module implements XEP\-0115: Entity Capabilities\&. The main purpose of the module is to provide PEP functionality (see \fImod_pubsub\fR)\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBcache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as top\-level +\fIcache_life_time\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_missed\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIcache_missed\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as top\-level +\fIcache_size\fR +option, but applied to this module only\&. +.RE +.PP +\fBdb_type\fR: \fImnesia | sql\fR +.RS 4 +Same as top\-level +\fIdefault_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBuse_cache\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIuse_cache\fR +option, but applied to this module only\&. +.RE +.RE +.SS "mod_carboncopy" +.sp +The module implements XEP\-0280: Message Carbons\&. The module broadcasts messages on all connected user resources (devices)\&. +.sp +The module has no options\&. +.SS "mod_client_state" +.sp +This module allows for queueing certain types of stanzas when a client indicates that the user is not actively using the client right now (see XEP\-0352: Client State Indication)\&. This can save bandwidth and resources\&. +.sp +A stanza is dropped from the queue if it\(cqs effectively obsoleted by a new one (e\&.g\&., a new presence stanza would replace an old one from the same client)\&. The queue is flushed if a stanza arrives that won\(cqt be queued, or if the queue size reaches a certain limit (currently 100 stanzas), or if the client becomes active again\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBqueue_chat_states\fR: \fItrue | false\fR +.RS 4 +Queue "standalone" chat state notifications (as defined in +XEP\-0085: Chat State Notifications) while a client indicates inactivity\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBqueue_pep\fR: \fItrue | false\fR +.RS 4 +Queue PEP notifications while a client is inactive\&. When the queue is flushed, only the most recent notification of a given PEP node is delivered\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBqueue_presence\fR: \fItrue | false\fR +.RS 4 +While a client is inactive, queue presence stanzas that indicate (un)availability\&. The default value is +\fItrue\fR\&. +.RE +.RE +.SS "mod_configure" +.sp +The module provides server configuration functionalities using XEP\-0030: Service Discovery and XEP\-0050: Ad\-Hoc Commands: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +List and discover outgoing s2s, online client sessions and all registered accounts +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Most of the ad\-hoc commands defined in +XEP\-0133: Service Administration +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Additional custom ad\-hoc commands specific to ejabberd +.RE +.sp +This module requires \fImod_adhoc\fR (to execute the commands), and recommends \fImod_disco\fR (to discover the commands)\&. +.sp +Please notice that all the ad\-hoc commands implemented by this module have an equivalent API Command that you can execute using \fImod_adhoc_api\fR or any other API frontend\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess\fR: \fIAccessName\fR +.RS 4 +\fINote\fR +about this option: added in 25\&.03\&. This option defines which access rule will be used to control who is allowed to access the features provided by this module\&. The default value is +\fIconfigure\fR\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +acl: + admin: + user: sun@localhost + +access_rules: + configure: + allow: admin + +modules: + mod_configure: + access: configure +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_conversejs" +.sp +\fINote\fR about this option: improved in 25\&.07\&. +.sp +This module serves a simple page for the Converse XMPP web browser client\&. +.sp +To use this module, in addition to adding it to the \fImodules\fR section, you must also enable it in \fIlisten\fR → \fIejabberd_http\fR → \fIlisten\-options\&.md#request_handlers|request_handlers\fR\&. +.sp +Make sure either \fImod_bosh\fR or \fIlisten\&.md#ejabberd_http_ws|ejabberd_http_ws\fR are enabled in at least one \fIrequest_handlers\fR\&. +.sp +When \fIconversejs_css\fR and \fIconversejs_script\fR are \fIauto\fR, by default they point to the public Converse client\&. +.sp +This module is available since ejabberd 21\&.12\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBbosh_service_url\fR: \fIauto | BoshURL\fR +.RS 4 +BOSH service URL to which Converse can connect to\&. The keyword +\fI@HOST@\fR +is replaced with the real virtual host name\&. If set to +\fIauto\fR, it will build the URL of the first configured BOSH request handler\&. The default value is +\fIauto\fR\&. +.RE +.PP +\fBconversejs_css\fR: \fIauto | URL\fR +.RS 4 +Converse CSS URL\&. The keyword +\fI@HOST@\fR +is replaced with the hostname\&. The default value is +\fIauto\fR\&. +.RE +.PP +\fBconversejs_options\fR: \fI{Name: Value}\fR +.RS 4 +\fINote\fR +about this option: added in 22\&.05\&. Specify additional options to be passed to Converse\&. See +Converse configuration\&. Only boolean, integer and string values are supported; lists are not supported\&. +.RE +.PP +\fBconversejs_plugins\fR: \fI[Filename]\fR +.RS 4 +List of additional local files to include as scripts in the homepage\&. Please make sure those files are available in the path specified in +\fIconversejs_resources\fR +option, in subdirectory +\fIplugins/\fR\&. If using the public Converse client, then +\fI"libsignal"\fR +gets replaced with the URL of the public library\&. The default value is +\fI[]\fR\&. +.RE +.PP +\fBconversejs_resources\fR: \fIPath\fR +.RS 4 +\fINote\fR +about this option: added in 22\&.05\&. Local path to the Converse files\&. If not set, the public Converse client will be used instead\&. +.RE +.PP +\fBconversejs_script\fR: \fIauto | URL\fR +.RS 4 +Converse main script URL\&. The keyword +\fI@HOST@\fR +is replaced with the hostname\&. The default value is +\fIauto\fR\&. +.RE +.PP +\fBdefault_domain\fR: \fIDomain\fR +.RS 4 +Specify a domain to act as the default for user JIDs\&. The keyword +\fI@HOST@\fR +is replaced with the hostname\&. The default value is +\fI@HOST@\fR\&. +.RE +.PP +\fBwebsocket_url\fR: \fIauto | WebSocketURL\fR +.RS 4 +A WebSocket URL to which Converse can connect to\&. The +\fI@HOST@\fR +keyword is replaced with the real virtual host name\&. If set to +\fIauto\fR, it will build the URL of the first configured WebSocket request handler\&. The default value is +\fIauto\fR\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExamples:\fR +.RS 4 +.sp +Manually setup WebSocket url, and use the public Converse client: +.sp +.if n \{\ +.RS 4 +.\} +.nf +listen: + \- + port: 5280 + module: ejabberd_http + request_handlers: + /bosh: mod_bosh + /websocket: ejabberd_http_ws + /conversejs: mod_conversejs + +modules: + mod_bosh: {} + mod_conversejs: + conversejs_plugins: ["libsignal"] + websocket_url: "ws://@HOST@:5280/websocket" +.fi +.if n \{\ +.RE +.\} +.sp +Host Converse locally and let auto detection of WebSocket and Converse URLs: +.sp +.if n \{\ +.RS 4 +.\} +.nf +listen: + \- + port: 443 + module: ejabberd_http + tls: true + request_handlers: + /websocket: ejabberd_http_ws + /conversejs: mod_conversejs + +modules: + mod_conversejs: + conversejs_resources: "/home/ejabberd/conversejs\-x\&.y\&.z/package/dist" + conversejs_plugins: ["libsignal\-protocol\&.min\&.js"] + # File path is: /home/ejabberd/conversejs\-x\&.y\&.z/package/dist/plugins/libsignal\-protocol\&.min\&.js +.fi +.if n \{\ +.RE +.\} +.sp +Configure some additional options for Converse +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_conversejs: + websocket_url: auto + conversejs_options: + auto_away: 30 + clear_cache_on_logout: true + i18n: "pt" + locked_domain: "@HOST@" + message_archiving: always + theme: dracula +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_delegation" +.sp +This module is an implementation of XEP\-0355: Namespace Delegation\&. Only admin mode has been implemented by now\&. Namespace delegation allows external services to handle IQ using specific namespace\&. This may be applied for external PEP service\&. +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBWarning\fR +.ps -1 +.br +.sp +Security issue: Namespace delegation gives components access to sensitive data, so permission should be granted carefully, only if you trust the component\&. +.sp .5v +.RE +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBNote\fR +.ps -1 +.br +.sp +This module is complementary to \fImod_privilege\fR but can also be used separately\&. +.sp .5v +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBnamespaces\fR: \fI{Namespace: Options}\fR +.RS 4 +If you want to delegate namespaces to a component, specify them in this option, and associate them to an access rule\&. The +\fIOptions\fR +are: +.PP +\fBaccess\fR: \fIAccessName\fR +.RS 4 +The option defines which components are allowed for namespace delegation\&. The default value is +\fInone\fR\&. +.RE +.PP +\fBfiltering\fR: \fIAttributes\fR +.RS 4 +The list of attributes\&. Currently not used\&. +.RE +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExamples:\fR +.RS 4 +.sp +Make sure you do not delegate the same namespace to several services at the same time\&. As in the example provided later, to have the \fIsat\-pubsub\&.example\&.org\fR component perform correctly disable the \fImod_pubsub\fR module\&. +.sp +.if n \{\ +.RS 4 +.\} +.nf +access_rules: + external_pubsub: + allow: external_component + external_mam: + allow: external_component + +acl: + external_component: + server: sat\-pubsub\&.example\&.org + +modules: + mod_delegation: + namespaces: + urn:xmpp:mam:1: + access: external_mam + http://jabber\&.org/protocol/pubsub: + access: external_pubsub +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_disco" +.sp +This module adds support for XEP\-0030: Service Discovery\&. With this module enabled, services on your server can be discovered by XMPP clients\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBextra_domains\fR: \fI[Domain, \&.\&.\&.]\fR +.RS 4 +With this option, you can specify a list of extra domains that are added to the Service Discovery item list\&. The default value is an empty list\&. +.RE +.PP +\fBname\fR: \fIName\fR +.RS 4 +A name of the server in the Service Discovery\&. This will only be displayed by special XMPP clients\&. The default value is +\fIejabberd\fR\&. +.RE +.PP +\fBserver_info\fR: \fI[Info, \&.\&.\&.]\fR +.RS 4 +Specify additional information about the server, as described in +XEP\-0157: Contact Addresses for XMPP Services\&. Every +\fIInfo\fR +element in the list is constructed from the following options: +.PP +\fBmodules\fR: \fIall | [Module, \&.\&.\&.]\fR +.RS 4 +The value can be the keyword +\fIall\fR, in which case the information is reported in all the services, or a list of ejabberd modules, in which case the information is only specified for the services provided by those modules\&. +.RE +.PP +\fBname\fR: \fIName\fR +.RS 4 +The field +\fIvar\fR +name that will be defined\&. See XEP\-0157 for some standardized names\&. +.RE +.PP +\fBurls\fR: \fI[URI, \&.\&.\&.]\fR +.RS 4 +A list of contact URIs, such as HTTP URLs, XMPP URIs and so on\&. +.RE +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +server_info: + \- + modules: all + name: abuse\-addresses + urls: ["mailto:abuse@shakespeare\&.lit"] + \- + modules: [mod_muc] + name: "Web chatroom logs" + urls: ["http://www\&.example\&.org/muc\-logs"] + \- + modules: [mod_disco] + name: feedback\-addresses + urls: + \- http://shakespeare\&.lit/feedback\&.php + \- mailto:feedback@shakespeare\&.lit + \- xmpp:feedback@shakespeare\&.lit + \- + modules: + \- mod_disco + \- mod_vcard + name: admin\-addresses + urls: + \- mailto:xmpp@shakespeare\&.lit + \- xmpp:admins@shakespeare\&.lit +.fi +.if n \{\ +.RE +.\} +.RE +.RE +.SS "mod_fail2ban" +.sp +The module bans IPs that show the malicious signs\&. Currently only C2S authentication failures are detected\&. +.sp +Unlike the standalone program, \fImod_fail2ban\fR clears the record of authentication failures after some time since the first failure or on a successful authentication\&. It also does not simply block network traffic, but provides the client with a descriptive error message\&. +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBWarning\fR +.ps -1 +.br +.sp +You should not use this module behind a proxy or load balancer\&. ejabberd will see the failures as coming from the load balancer and, when the threshold of auth failures is reached, will reject all connections coming from the load balancer\&. You can lock all your user base out of ejabberd when using this module behind a proxy\&. +.sp .5v +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess\fR: \fIAccessName\fR +.RS 4 +Specify an access rule for whitelisting IP addresses or networks\&. If the rule returns +\fIallow\fR +for a given IP address, that address will never be banned\&. The +\fIAccessName\fR +should be of type +\fIip\fR\&. The default value is +\fInone\fR\&. +.RE +.PP +\fBc2s_auth_ban_lifetime\fR: \fItimeout()\fR +.RS 4 +The lifetime of the IP ban caused by too many C2S authentication failures\&. The default value is +\fI1\fR +hour\&. +.RE +.PP +\fBc2s_max_auth_failures\fR: \fINumber\fR +.RS 4 +The number of C2S authentication failures to trigger the IP ban\&. The default value is +\fI20\fR\&. +.RE +.sp +\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#accounts|accounts\fR +.RE +.SS "mod_host_meta" +.sp +\fINote\fR about this option: added in 22\&.05\&. +.sp +This module serves small \fIhost\-meta\fR files as described in XEP\-0156: Discovering Alternative XMPP Connection Methods\&. +.sp +To use this module, in addition to adding it to the \fImodules\fR section, you must also enable it in \fIlisten\fR → \fIejabberd_http\fR → \fIlisten\-options\&.md#request_handlers|request_handlers\fR\&. +.sp +Notice it only works if \fIlisten\&.md#ejabberd_http|ejabberd_http\fR has \fIlisten\-options\&.md#tls|tls\fR enabled\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBbosh_service_url\fR: \fIundefined | auto | BoshURL\fR +.RS 4 +BOSH service URL to announce\&. The keyword +\fI@HOST@\fR +is replaced with the real virtual host name\&. If set to +\fIauto\fR, it will build the URL of the first configured BOSH request handler\&. The default value is +\fIauto\fR\&. +.RE +.PP +\fBwebsocket_url\fR: \fIundefined | auto | WebSocketURL\fR +.RS 4 +WebSocket URL to announce\&. The keyword +\fI@HOST@\fR +is replaced with the real virtual host name\&. If set to +\fIauto\fR, it will build the URL of the first configured WebSocket request handler\&. The default value is +\fIauto\fR\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +listen: + \- + port: 443 + module: ejabberd_http + tls: true + request_handlers: + /bosh: mod_bosh + /ws: ejabberd_http_ws + /\&.well\-known/host\-meta: mod_host_meta + /\&.well\-known/host\-meta\&.json: mod_host_meta + +modules: + mod_bosh: {} + mod_host_meta: + bosh_service_url: "https://@HOST@:5443/bosh" + websocket_url: "wss://@HOST@:5443/ws" +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_http_api" +.sp +This module provides a ReST interface to call \fI\&.\&./\&.\&./developer/ejabberd\-api/index\&.md|ejabberd API\fR commands using JSON data\&. +.sp +To use this module, in addition to adding it to the \fImodules\fR section, you must also enable it in \fIlisten\fR → \fIejabberd_http\fR → \fIlisten\-options\&.md#request_handlers|request_handlers\fR\&. +.sp +To use a specific API version N, when defining the URL path in the request_handlers, add a vN\&. For example: \fI/api/v2: mod_http_api\fR\&. +.sp +To run a command, send a POST request to the corresponding URL: \fIhttp://localhost:5280/api/COMMAND\-NAME\fR +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBdefault_version\fR: \fIinteger() | string()\fR +.RS 4 +\fINote\fR +about this option: added in 24\&.12\&. What API version to use when none is specified in the URL path\&. If setting an ejabberd version, it will use the latest API version that was available in that ejabberd version\&. For example, setting +\fI"24\&.06"\fR +in this option implies +\fI2\fR\&. The default value is the latest version\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +listen: + \- + port: 5280 + module: ejabberd_http + request_handlers: + /api: mod_http_api + +modules: + mod_http_api: + default_version: 2 +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_http_fileserver" +.sp +This simple module serves files from the local disk over HTTP\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccesslog\fR: \fIPath\fR +.RS 4 +File to log accesses using an Apache\-like format\&. No log will be recorded if this option is not specified\&. +.RE +.PP +\fBcontent_types\fR: \fI{Extension: Type}\fR +.RS 4 +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\&. The default values are: +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +content_types: + \&.css: text/css + \&.gif: image/gif + \&.html: text/html + \&.jar: application/java\-archive + \&.jpeg: image/jpeg + \&.jpg: image/jpeg + \&.js: text/javascript + \&.png: image/png + \&.svg: image/svg+xml + \&.txt: text/plain + \&.xml: application/xml + \&.xpi: application/x\-xpinstall + \&.xul: application/vnd\&.mozilla\&.xul+xml +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBcustom_headers\fR: \fI{Name: Value}\fR +.RS 4 +Indicate custom HTTP headers to be included in all responses\&. There are no custom headers by default\&. +.RE +.PP +\fBdefault_content_type\fR: \fIType\fR +.RS 4 +Specify the content type to use for unknown extensions\&. The default value is +\fIapplication/octet\-stream\fR\&. +.RE +.PP +\fBdirectory_indices\fR: \fI[Index, \&.\&.\&.]\fR +.RS 4 +Indicate one or more directory index files, similarly to Apache\(cqs +\fIDirectoryIndex\fR +variable\&. When an HTTP request hits a directory instead of a regular file, those directory indices are looked in order, and the first one found is returned\&. The default value is an empty list\&. +.RE +.PP +\fBdocroot\fR: \fIPath\fR +.RS 4 +Directory to serve the files from\&. This is a mandatory option\&. +.RE +.PP +\fBmust_authenticate_with\fR: \fI[{Username, Hostname}, \&.\&.\&.]\fR +.RS 4 +List of accounts that are allowed to use this service\&. Default value: +\fI[]\fR\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExamples:\fR +.RS 4 +.sp +This example configuration will serve the files from the local directory \fI/var/www\fR in the address \fIhttp://example\&.org:5280/pub/content/\fR\&. In this example a new content type \fIogg\fR is defined, \fIpng\fR is redefined, and \fIjpg\fR definition is deleted: +.sp +.if n \{\ +.RS 4 +.\} +.nf +listen: + \- + port: 5280 + module: ejabberd_http + request_handlers: + /pub/content: mod_http_fileserver + +modules: + mod_http_fileserver: + docroot: /var/www + accesslog: /var/log/ejabberd/access\&.log + directory_indices: + \- index\&.html + \- main\&.htm + custom_headers: + X\-Powered\-By: Erlang/OTP + X\-Fry: "It\*(Aqs a widely\-believed fact!" + content_types: + \&.ogg: audio/ogg + \&.png: image/png + default_content_type: text/html +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_http_upload" +.sp +This module allows for requesting permissions to upload a file via HTTP as described in XEP\-0363: HTTP File Upload\&. If the request is accepted, the client receives a URL for uploading the file and another URL from which that file can later be downloaded\&. +.sp +In order to use this module, it must be enabled in \fIlisten\fR → \fIejabberd_http\fR → \fIlisten\-options\&.md#request_handlers|request_handlers\fR\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess\fR: \fIAccessName\fR +.RS 4 +This option defines the access rule to limit who is permitted to use the HTTP upload service\&. The default value is +\fIlocal\fR\&. If no access rule of that name exists, no user will be allowed to use the service\&. +.RE +.PP +\fBcustom_headers\fR: \fI{Name: Value}\fR +.RS 4 +This option specifies additional header fields to be included in all HTTP responses\&. By default no custom headers are included\&. +.RE +.PP +\fBdir_mode\fR: \fIPermission\fR +.RS 4 +This option defines the permission bits of the +\fIdocroot\fR +directory and any directories created during file uploads\&. The bits are specified as an octal number (see the +\fIchmod(1)\fR +manual page) within double quotes\&. For example: +\fI"0755"\fR\&. The default is undefined, which means no explicit permissions will be set\&. +.RE +.PP +\fBdocroot\fR: \fIPath\fR +.RS 4 +Uploaded files are stored below the directory specified (as an absolute path) with this option\&. The keyword +\fI@HOME@\fR +is replaced with the home directory of the user running ejabberd, and the keyword +\fI@HOST@\fR +with the virtual host name\&. The default value is +\fI"@HOME@/upload"\fR\&. +.RE +.PP +\fBexternal_secret\fR: \fIText\fR +.RS 4 +This option makes it possible to offload all HTTP Upload processing to a separate HTTP server\&. Both ejabberd and the HTTP server should share this secret and behave exactly as described at +Prosody\(cqs mod_http_upload_external: Implementation\&. There is no default value\&. +.RE +.PP +\fBfile_mode\fR: \fIPermission\fR +.RS 4 +This option defines the permission bits of uploaded files\&. The bits are specified as an octal number (see the +\fIchmod(1)\fR +manual page) within double quotes\&. For example: +\fI"0644"\fR\&. The default is undefined, which means no explicit permissions will be set\&. +.RE +.PP +\fBget_url\fR: \fIURL\fR +.RS 4 +This option specifies the initial part of the GET URLs used for downloading the files\&. The default value is +\fIundefined\fR\&. When this option is +\fIundefined\fR, this option is set to the same value as +\fIput_url\fR\&. The keyword +\fI@HOST@\fR +is replaced with the virtual host name\&. NOTE: if GET requests are handled by this module, the +\fIget_url\fR +must match the +\fIput_url\fR\&. Setting it to a different value only makes sense if an external web server or +\fImod_http_fileserver\fR +is used to serve the uploaded files\&. +.RE +.PP +\fBhost\fR +.RS 4 +Deprecated\&. Use +\fIhosts\fR +instead\&. +.RE +.PP +\fBhosts\fR: \fI[Host, \&.\&.\&.]\fR +.RS 4 +This option defines the Jabber IDs of the service\&. If the +\fIhosts\fR +option is not specified, the only Jabber ID will be the hostname of the virtual host with the prefix +\fI"upload\&."\fR\&. The keyword +\fI@HOST@\fR +is replaced with the real virtual host name\&. +.RE +.PP +\fBjid_in_url\fR: \fInode | sha1\fR +.RS 4 +When this option is set to +\fInode\fR, the node identifier of the user\(cqs JID (i\&.e\&., the user name) is included in the GET and PUT URLs generated by +\fImod_http_upload\fR\&. Otherwise, a SHA\-1 hash of the user\(cqs bare JID is included instead\&. The default value is +\fIsha1\fR\&. +.RE +.PP +\fBmax_size\fR: \fISize\fR +.RS 4 +This option limits the acceptable file size\&. Either a number of bytes (larger than zero) or +\fIinfinity\fR +must be specified\&. The default value is +\fI104857600\fR\&. +.RE +.PP +\fBname\fR: \fIName\fR +.RS 4 +A name of the service in the Service Discovery\&. The default value is +\fI"HTTP File Upload"\fR\&. Please note this will only be displayed by some XMPP clients\&. +.RE +.PP +\fBput_url\fR: \fIURL\fR +.RS 4 +This option specifies the initial part of the PUT URLs used for file uploads\&. The keyword +\fI@HOST@\fR +is replaced with the virtual host name\&. And +\fI@HOST_URL_ENCODE@\fR +is replaced with the host name encoded for URL, useful when your virtual hosts contain non\-latin characters\&. NOTE: different virtual hosts cannot use the same PUT URL\&. The default value is +\fI"https://@HOST@:5443/upload"\fR\&. +.RE +.PP +\fBrm_on_unregister\fR: \fItrue | false\fR +.RS 4 +This option specifies whether files uploaded by a user should be removed when that user is unregistered\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBsecret_length\fR: \fILength\fR +.RS 4 +This option defines the length of the random string included in the GET and PUT URLs generated by +\fImod_http_upload\fR\&. The minimum length is +\fI8\fR +characters, but it is recommended to choose a larger value\&. The default value is +\fI40\fR\&. +.RE +.PP +\fBservice_url\fR +.RS 4 +Deprecated\&. +.RE +.PP +\fBthumbnail\fR: \fItrue | false\fR +.RS 4 +This option specifies whether ejabberd should create thumbnails of uploaded images\&. If a thumbnail is created, a element that contains the download and some metadata is returned with the PUT response\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBvcard\fR: \fIvCard\fR +.RS 4 +A custom vCard of the service that will be displayed by some XMPP clients in Service Discovery\&. The value of +\fIvCard\fR +is a YAML map constructed from an XML representation of vCard\&. Since the representation has no attributes, the mapping is straightforward\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# This XML representation of vCard: +# +# Conferences +# +# +# Elm Street +# +# +# +# is translated to: +vcard: + fn: Conferences + adr: + \- + work: true + street: Elm Street +.fi +.if n \{\ +.RE +.\} +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +listen: + \- + port: 5443 + module: ejabberd_http + tls: true + request_handlers: + /upload: mod_http_upload + +modules: + mod_http_upload: + docroot: /ejabberd/upload + put_url: "https://@HOST@:5443/upload" +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_http_upload_quota" +.sp +This module adds quota support for mod_http_upload\&. +.sp +This module depends on \fImod_http_upload\fR\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess_hard_quota\fR: \fIAccessName\fR +.RS 4 +This option defines which access rule is used to specify the "hard quota" for the matching JIDs\&. That rule must yield a positive number for any JID that is supposed to have a quota limit\&. This is the number of megabytes a corresponding user may upload\&. When this threshold is exceeded, ejabberd deletes the oldest files uploaded by that user until their disk usage equals or falls below the specified soft quota (see also option +\fIaccess_soft_quota\fR)\&. The default value is +\fIhard_upload_quota\fR\&. +.RE +.PP +\fBaccess_soft_quota\fR: \fIAccessName\fR +.RS 4 +This option defines which access rule is used to specify the "soft quota" for the matching JIDs\&. That rule must yield a positive number of megabytes for any JID that is supposed to have a quota limit\&. See the description of the +\fIaccess_hard_quota\fR +option for details\&. The default value is +\fIsoft_upload_quota\fR\&. +.RE +.PP +\fBmax_days\fR: \fIDays\fR +.RS 4 +If a number larger than zero is specified, any files (and directories) older than this number of days are removed from the subdirectories of the +\fIdocroot\fR +directory, once per day\&. The default value is +\fIinfinity\fR\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExamples:\fR +.RS 4 +.sp +Notice it\(cqs not necessary to specify the \fIaccess_hard_quota\fR and \fIaccess_soft_quota\fR options in order to use the quota feature\&. You can stick to the default names and just specify access rules such as those in this example: +.sp +.if n \{\ +.RS 4 +.\} +.nf +shaper_rules: + soft_upload_quota: + 1000: all # MiB + hard_upload_quota: + 1100: all # MiB + +modules: + mod_http_upload: {} + mod_http_upload_quota: + max_days: 100 +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_jidprep" +.sp +This module allows XMPP clients to ask the server to normalize a JID as per the rules specified in RFC 6122: XMPP Address Format\&. This might be useful for clients in certain constrained environments, or for testing purposes\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess\fR: \fIAccessName\fR +.RS 4 +This option defines which access rule will be used to control who is allowed to use this service\&. The default value is +\fIlocal\fR\&. +.RE +.RE +.SS "mod_last" +.sp +This module adds support for XEP\-0012: Last Activity\&. It can be used to discover when a disconnected user last accessed the server, to know when a connected user was last active on the server, or to query the uptime of the ejabberd server\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBcache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as top\-level +\fIcache_life_time\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_missed\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIcache_missed\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as top\-level +\fIcache_size\fR +option, but applied to this module only\&. +.RE +.PP +\fBdb_type\fR: \fImnesia | sql\fR +.RS 4 +Same as top\-level +\fIdefault_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBuse_cache\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIuse_cache\fR +option, but applied to this module only\&. +.RE +.RE +.SS "mod_legacy_auth" +.sp +The module implements XEP\-0078: Non\-SASL Authentication\&. +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBNote\fR +.ps -1 +.br +.sp +This type of authentication was obsoleted in 2008 and you unlikely need this module unless you have something like outdated Jabber bots\&. +.sp .5v +.RE +.sp +The module has no options\&. +.SS "mod_mam" +.sp +This module implements XEP\-0313: Message Archive Management and XEP\-0441: Message Archive Management Preferences\&. Compatible XMPP clients can use it to store their chat history on the server\&. +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBNote\fR +.ps -1 +.br +.sp +Mnesia backend for mod_mam is not recommended: it\(cqs limited to 2GB and often gets corrupted when reaching this limit\&. SQL backend is recommended\&. Namely, for small servers SQLite is a preferred choice because it\(cqs very easy to configure\&. +.sp .5v +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess_preferences\fR: \fIAccessName\fR +.RS 4 +This access rule defines who is allowed to modify the MAM preferences\&. The default value is +\fIall\fR\&. +.RE +.PP +\fBassume_mam_usage\fR: \fItrue | false\fR +.RS 4 +This option determines how ejabberd\(cqs stream management code (see +\fImod_stream_mgmt\fR) handles unacknowledged messages when the connection is lost\&. Usually, such messages are either bounced or resent\&. However, neither is done for messages that were stored in the user\(cqs MAM archive if this option is set to +\fItrue\fR\&. In this case, ejabberd assumes those messages will be retrieved from the archive\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBcache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as top\-level +\fIcache_life_time\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_missed\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIcache_missed\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as top\-level +\fIcache_size\fR +option, but applied to this module only\&. +.RE +.PP +\fBclear_archive_on_room_destroy\fR: \fItrue | false\fR +.RS 4 +Whether to destroy message archive of a room (see +\fImod_muc\fR) when it gets destroyed\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBcompress_xml\fR: \fItrue | false\fR +.RS 4 +When enabled, new messages added to archives are compressed using a custom compression algorithm\&. This feature works only with SQL backends\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBdb_type\fR: \fImnesia | sql\fR +.RS 4 +Same as top\-level +\fIdefault_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBdefault\fR: \fIalways | never | roster\fR +.RS 4 +The option defines default policy for chat history\&. When +\fIalways\fR +is set every chat message is stored\&. With +\fIroster\fR +only chat history with contacts from user\(cqs roster is stored\&. And +\fInever\fR +fully disables chat history\&. Note that a client can change its policy via protocol commands\&. The default value is +\fInever\fR\&. +.RE +.PP +\fBrequest_activates_archiving\fR: \fItrue | false\fR +.RS 4 +If the value is +\fItrue\fR, no messages are stored for a user until their client issue a MAM request, regardless of the value of the +\fIdefault\fR +option\&. Once the server received a request, that user\(cqs messages are archived as usual\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBuse_cache\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIuse_cache\fR +option, but applied to this module only\&. +.RE +.PP +\fBuser_mucsub_from_muc_archive\fR: \fItrue | false\fR +.RS 4 +When this option is disabled, for each individual subscriber a separate mucsub message is stored\&. With this option enabled, when a user fetches archive virtual mucsub, messages are generated from muc archives\&. The default value is +\fIfalse\fR\&. +.RE +.sp +\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#mam|mam\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#purge|purge\fR +.RE +.SS "mod_matrix_gw 🟤" +.sp +\fINote\fR about this option: improved in 25\&.08\&. +.sp +Matrix gateway\&. Supports room versions 9, 10 and 11 since ejabberd 25\&.03; room versions 4 and higher since ejabberd 25\&.07; room version 12 (hydra rooms) since ejabberd 25\&.08\&. Erlang/OTP 25 or higher is required to use this module\&. This module is available since ejabberd 24\&.02\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBhost\fR: \fIHost\fR +.RS 4 +This option defines the Jabber IDs of the service\&. If the +\fIhost\fR +option is not specified, the Jabber ID will be the hostname of the virtual host with the prefix +\fI"matrix\&."\fR\&. The keyword +\fI@HOST@\fR +is replaced with the real virtual host name\&. +.RE +.PP +\fBkey\fR: \fIstring()\fR +.RS 4 +Value of the matrix signing key, in base64\&. +.RE +.PP +\fBkey_name\fR: \fIstring()\fR +.RS 4 +Name of the matrix signing key\&. +.RE +.PP +\fBleave_timeout\fR: \fIinteger()\fR +.RS 4 +Delay in seconds between a user leaving a MUC room and sending +\fIleave\fR +Matrix event\&. +.RE +.PP +\fBmatrix_domain\fR: \fIDomain\fR +.RS 4 +Specify a domain in the Matrix federation\&. The keyword +\fI@HOST@\fR +is replaced with the hostname\&. The default value is +\fI@HOST@\fR\&. +.RE +.PP +\fBmatrix_id_as_jid\fR: \fItrue | false\fR +.RS 4 +If set to +\fItrue\fR, all packets failing to be delivered via an XMPP server\-to\-server connection will then be routed to the Matrix gateway by translating a Jabber ID +\fIuser@matrixdomain\&.tld\fR +to a Matrix user identifier +\fI@user:matrixdomain\&.tld\fR\&. When set to +\fIfalse\fR, messages must be explicitly sent to the matrix gateway service Jabber ID to be routed to a remote Matrix server\&. In this case, to send a message to Matrix user +\fI@user:matrixdomain\&.tld\fR, the client must send a message to the JID +\fIuser%\fR\fImatrixdomain\&.tld@matrix\&.myxmppdomain\fR\fI\&.tld\fR, where +\fImatrix\&.myxmppdomain\&.tld\fR +is the JID of the gateway service as set by the +\fIhost\fR +option\&. The default is +\fIfalse\fR\&. +.RE +.PP +\fBnotary_servers\fR: \fI[Server, \&.\&.\&.]\fR +.RS 4 +A list of notary servers\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +listen: + \- + port: 8448 + module: ejabberd_http + tls: true + request_handlers: + "/_matrix": mod_matrix_gw + +modules: + mod_matrix_gw: + key_name: "key1" + key: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + matrix_id_as_jid: true +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_metrics" +.sp +This module sends events to external backend (by now only grapherl is supported)\&. Supported events are: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +sm_register_connection +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +sm_remove_connection +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +user_send_packet +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +user_receive_packet +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +s2s_send_packet +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +s2s_receive_packet +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +register_user +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +remove_user +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +offline_message +.RE +.sp +When enabled, every call to these hooks triggers a counter event to be sent to the external backend\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBip\fR: \fIIPv4Address\fR +.RS 4 +IPv4 address where the backend is located\&. The default value is +\fI127\&.0\&.0\&.1\fR\&. +.RE +.PP +\fBport\fR: \fIPort\fR +.RS 4 +An internet port number at which the backend is listening for incoming connections/packets\&. The default value is +\fI11111\fR\&. +.RE +.RE +.SS "mod_mix" +.sp +\fINote\fR about this option: added in 16\&.03 and improved in 19\&.02\&. +.sp +This module is an experimental implementation of XEP\-0369: Mediated Information eXchange (MIX)\&. It\(cqs asserted that the MIX protocol is going to replace the MUC protocol in the future (see \fImod_muc\fR)\&. +.sp +To learn more about how to use that feature, you can refer to our tutorial: \fI\&.\&./\&.\&./tutorials/mix\-010\&.md|Getting started with MIX\fR +.sp +The module depends on \fImod_mam\fR\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess_create\fR: \fIAccessName\fR +.RS 4 +An access rule to control MIX channels creations\&. The default value is +\fIall\fR\&. +.RE +.PP +\fBdb_type\fR: \fImnesia | sql\fR +.RS 4 +Same as top\-level +\fIdefault_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBhost\fR +.RS 4 +Deprecated\&. Use +\fIhosts\fR +instead\&. +.RE +.PP +\fBhosts\fR: \fI[Host, \&.\&.\&.]\fR +.RS 4 +This option defines the Jabber IDs of the service\&. If the +\fIhosts\fR +option is not specified, the only Jabber ID will be the hostname of the virtual host with the prefix +\fI"mix\&."\fR\&. The keyword +\fI@HOST@\fR +is replaced with the real virtual host name\&. +.RE +.PP +\fBname\fR: \fIName\fR +.RS 4 +A name of the service in the Service Discovery\&. This will only be displayed by special XMPP clients\&. The default value is +\fIChannels\fR\&. +.RE +.RE +.SS "mod_mix_pam" +.sp +This module implements XEP\-0405: Mediated Information eXchange (MIX): Participant Server Requirements\&. The module is needed if MIX compatible clients on your server are going to join MIX channels (either on your server or on any remote servers)\&. +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBNote\fR +.ps -1 +.br +.sp +\fImod_mix\fR is not required for this module to work, however, without \fImod_mix_pam\fR the MIX functionality of your local XMPP clients will be impaired\&. +.sp .5v +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBcache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as top\-level +\fIcache_life_time\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_missed\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIcache_missed\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as top\-level +\fIcache_size\fR +option, but applied to this module only\&. +.RE +.PP +\fBdb_type\fR: \fImnesia | sql\fR +.RS 4 +Same as top\-level +\fIdefault_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBuse_cache\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIuse_cache\fR +option, but applied to this module only\&. +.RE +.RE +.SS "mod_mqtt" +.sp +This module adds \fI\&.\&./guide/mqtt/index\&.md|support for the MQTT\fR protocol version \fI3\&.1\&.1\fR and \fI5\&.0\fR\&. Remember to configure \fImod_mqtt\fR in \fImodules\fR and \fIlisten\fR sections\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess_publish\fR: \fI{TopicFilter: AccessName}\fR +.RS 4 +Access rules to restrict access to topics for publishers\&. By default there are no restrictions\&. +.RE +.PP +\fBaccess_subscribe\fR: \fI{TopicFilter: AccessName}\fR +.RS 4 +Access rules to restrict access to topics for subscribers\&. By default there are no restrictions\&. +.RE +.PP +\fBcache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as top\-level +\fIcache_life_time\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_missed\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIcache_missed\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as top\-level +\fIcache_size\fR +option, but applied to this module only\&. +.RE +.PP +\fBdb_type\fR: \fImnesia | sql\fR +.RS 4 +Same as top\-level +\fIdefault_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBmatch_retained_limit\fR: \fIpos_integer() | infinity\fR +.RS 4 +The option limits the number of retained messages returned to a client when it subscribes to some topic filter\&. The default value is +\fI1000\fR\&. +.RE +.PP +\fBmax_queue\fR: \fISize\fR +.RS 4 +Maximum queue size for outgoing packets\&. The default value is +\fI5000\fR\&. +.RE +.PP +\fBmax_topic_aliases\fR: \fI0\&.\&.65535\fR +.RS 4 +The maximum number of aliases a client is able to associate with the topics\&. The default value is +\fI100\fR\&. +.RE +.PP +\fBmax_topic_depth\fR: \fIDepth\fR +.RS 4 +The maximum topic depth, i\&.e\&. the number of slashes (\fI/\fR) in the topic\&. The default value is +\fI8\fR\&. +.RE +.PP +\fBqueue_type\fR: \fIram | file\fR +.RS 4 +Same as top\-level +\fIqueue_type\fR +option, but applied to this module only\&. +.RE +.PP +\fBram_db_type\fR: \fImnesia\fR +.RS 4 +Same as top\-level +\fIdefault_ram_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBsession_expiry\fR: \fItimeout()\fR +.RS 4 +The option specifies how long to wait for an MQTT session resumption\&. When +\fI0\fR +is set, the session gets destroyed when the underlying client connection is closed\&. The default value is +\fI5\fR +minutes\&. +.RE +.PP +\fBuse_cache\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIuse_cache\fR +option, but applied to this module only\&. +.RE +.RE +.SS "mod_mqtt_bridge" +.sp +This module adds ability to synchronize local MQTT topics with data on remote servers It can update topics on remote servers when local user updates local topic, or can subscribe for changes on remote server, and update local copy when remote data is updated\&. It is available since ejabberd 23\&.01\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBreplication_user\fR: \fIJID\fR +.RS 4 +Identifier of a user that will be assigned as owner of local changes\&. +.RE +.PP +\fBservers\fR: \fI{ServerUrl: {Key: Value}}\fR +.RS 4 +Declaration of data to share for each ServerUrl\&. Server URLs can use schemas: +\fImqtt\fR, +\fImqtts\fR +(mqtt with tls), +\fImqtt5\fR, +\fImqtt5s\fR +(both to trigger v5 protocol), +\fIws\fR, +\fIwss\fR, +\fIws5\fR, +\fIwss5\fR\&. Keys must be: +.PP +\fBauthentication\fR: \fI{AuthKey: AuthValue}\fR +.RS 4 +List of authentication information, where AuthKey can be: +\fIusername\fR +and +\fIpassword\fR +fields, or +\fIcertfile\fR +pointing to client certificate\&. Certificate authentication can be used only with mqtts, mqtt5s, wss, wss5\&. +.RE +.PP +\fBpublish\fR: \fI{LocalTopic: RemoteTopic}\fR +.RS 4 +Either publish or subscribe must be set, or both\&. +.RE +.PP +\fBsubscribe\fR: \fI{RemoteTopic: LocalTopic}\fR +.RS 4 +Either publish or subscribe must be set, or both\&. +.RE +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_mqtt_bridge: + replication_user: "mqtt@xmpp\&.server\&.com" + servers: + "mqtt://server\&.com": + authentication: + certfile: "/etc/ejabberd/mqtt_server\&.pem" + publish: + "localA": "remoteA" # local changes to \*(AqlocalA\*(Aq will be replicated on remote server as \*(AqremoteA\*(Aq + "topicB": "topicB" + subscribe: + "remoteB": "localB" # changes to \*(AqremoteB\*(Aq on remote server will be stored as \*(AqlocalB\*(Aq on local server +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_muc" +.sp +This module provides support for XEP\-0045: Multi\-User Chat\&. Users can discover existing rooms, join or create them\&. Occupants of a room can chat in public or have private chats\&. +.sp +The MUC service allows any Jabber ID to register a nickname, so nobody else can use that nickname in any room in the MUC service\&. To register a nickname, open the Service Discovery in your XMPP client and register in the MUC service\&. +.sp +It is also possible to register a nickname in a room, so nobody else can use that nickname in that room\&. If a nick is registered in the MUC service, that nick cannot be registered in any room, and vice versa: a nick that is registered in a room cannot be registered at the MUC service\&. +.sp +This module supports clustering and load balancing\&. One module can be started per cluster node\&. Rooms are distributed at creation time on all available MUC module instances\&. The multi\-user chat module is clustered but the rooms themselves are not clustered nor fault\-tolerant: if the node managing a set of rooms goes down, the rooms disappear and they will be recreated on an available node on first connection attempt\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess\fR: \fIAccessName\fR +.RS 4 +You can specify who is allowed to use the Multi\-User Chat service\&. By default everyone is allowed to use it\&. +.RE +.PP +\fBaccess_admin\fR: \fIAccessName\fR +.RS 4 +This option specifies who is allowed to administrate the Multi\-User Chat service\&. The default value is +\fInone\fR, which means that only the room creator can administer their room\&. The administrators can send a normal message to the service JID, and it will be shown in all active rooms as a service message\&. The administrators can send a groupchat message to the JID of an active room, and the message will be shown in the room as a service message\&. +.RE +.PP +\fBaccess_create\fR: \fIAccessName\fR +.RS 4 +To configure who is allowed to create new rooms at the Multi\-User Chat service, this option can be used\&. The default value is +\fIall\fR, which means everyone is allowed to create rooms\&. +.RE +.PP +\fBaccess_mam\fR: \fIAccessName\fR +.RS 4 +To configure who is allowed to modify the +\fImam\fR +room option\&. The default value is +\fIall\fR, which means everyone is allowed to modify that option\&. +.RE +.PP +\fBaccess_persistent\fR: \fIAccessName\fR +.RS 4 +To configure who is allowed to modify the +\fIpersistent\fR +room option\&. The default value is +\fIall\fR, which means everyone is allowed to modify that option\&. +.RE +.PP +\fBaccess_register\fR: \fIAccessName\fR +.RS 4 +\fINote\fR +about this option: improved in 23\&.10\&. This option specifies who is allowed to register nickname within the Multi\-User Chat service and rooms\&. The default is +\fIall\fR +for backward compatibility, which means that any user is allowed to register any free nick in the MUC service and in the rooms\&. +.RE +.PP +\fBcleanup_affiliations_on_start\fR: \fItrue | false\fR +.RS 4 +\fINote\fR +about this option: added in 22\&.05\&. Remove affiliations for non\-existing local users on startup\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBdb_type\fR: \fImnesia | sql\fR +.RS 4 +Same as top\-level +\fIdefault_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBdefault_room_options\fR: \fIOptions\fR +.RS 4 +Define the default room options\&. Note that the creator of a room can modify the options of his room at any time using an XMPP client with MUC capability\&. The +\fIOptions\fR +are: +.PP +\fBallow_change_subj\fR: \fItrue | false\fR +.RS 4 +Allow occupants to change the subject\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBallow_private_messages_from_visitors\fR: \fIanyone | moderators | nobody\fR +.RS 4 +Visitors can send private messages to other occupants\&. The default value is +\fIanyone\fR +which means visitors can send private messages to any occupant\&. +.RE +.PP +\fBallow_query_users\fR: \fItrue | false\fR +.RS 4 +Occupants can send IQ queries to other occupants\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBallow_subscription\fR: \fItrue | false\fR +.RS 4 +Allow users to subscribe to room events as described in +\fI\&.\&./\&.\&./developer/xmpp\-clients\-bots/extensions/muc\-sub\&.md|Multi\-User Chat Subscriptions\fR\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBallow_user_invites\fR: \fItrue | false\fR +.RS 4 +Allow occupants to send invitations\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBallow_visitor_nickchange\fR: \fItrue | false\fR +.RS 4 +Allow visitors to change nickname\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBallow_visitor_status\fR: \fItrue | false\fR +.RS 4 +Allow visitors to send status text in presence updates\&. If disallowed, the status text is stripped before broadcasting the presence update to all the room occupants\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBallow_voice_requests\fR: \fItrue | false\fR +.RS 4 +Allow visitors in a moderated room to request voice\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBallowpm\fR: \fIanyone | participants | moderators | none\fR +.RS 4 +Who can send private messages\&. The default value is +\fIanyone\fR\&. +.RE +.PP +\fBanonymous\fR: \fItrue | false\fR +.RS 4 +The room is anonymous: occupants don\(cqt see the real JIDs of other occupants\&. Note that the room moderators can always see the real JIDs of the occupants\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBcaptcha_protected\fR: \fItrue | false\fR +.RS 4 +When a user tries to join a room where they have no affiliation (not owner, admin or member), the room requires them to fill a CAPTCHA challenge (see section +\fIbasic\&.md#captcha|CAPTCHA\fR +in order to accept their join in the room\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBdescription\fR: \fIRoom Description\fR +.RS 4 +Short description of the room\&. The default value is an empty string\&. +.RE +.PP +\fBenable_hats\fR: \fItrue | false\fR +.RS 4 +\fINote\fR +about this option: improved in 25\&.03\&. Allow extended roles as defined in XEP\-0317 Hats\&. Check the +\fI\&.\&./\&.\&./tutorials/muc\-hats\&.md|MUC Hats\fR +tutorial\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBlang\fR: \fILanguage\fR +.RS 4 +Preferred language for the discussions in the room\&. The language format should conform to RFC 5646\&. There is no value by default\&. +.RE +.PP +\fBlogging\fR: \fItrue | false\fR +.RS 4 +The public messages are logged using +\fImod_muc_log\fR\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBmam\fR: \fItrue | false\fR +.RS 4 +Enable message archiving\&. Implies mod_mam is enabled\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBmax_users\fR: \fINumber\fR +.RS 4 +Maximum number of occupants in the room\&. The default value is +\fI200\fR\&. +.RE +.PP +\fBmembers_by_default\fR: \fItrue | false\fR +.RS 4 +The occupants that enter the room are participants by default, so they have "voice"\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBmembers_only\fR: \fItrue | false\fR +.RS 4 +Only members of the room can enter\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBmoderated\fR: \fItrue | false\fR +.RS 4 +Only occupants with "voice" can send public messages\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBpassword\fR: \fIPassword\fR +.RS 4 +Password of the room\&. Implies option +\fIpassword_protected\fR +set to +\fItrue\fR\&. There is no default value\&. +.RE +.PP +\fBpassword_protected\fR: \fItrue | false\fR +.RS 4 +The password is required to enter the room\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBpersistent\fR: \fItrue | false\fR +.RS 4 +The room persists even if the last participant leaves\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBpresence_broadcast\fR: \fI[Role]\fR +.RS 4 +List of roles for which presence is broadcasted\&. The list can contain one or several of: +\fImoderator\fR, +\fIparticipant\fR, +\fIvisitor\fR\&. The default value is shown in the example below: +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +presence_broadcast: + \- moderator + \- participant + \- visitor +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBpublic\fR: \fItrue | false\fR +.RS 4 +The room is public in the list of the MUC service, so it can be discovered\&. MUC admins and room participants will see private rooms in Service Discovery if their XMPP client supports this feature\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBpublic_list\fR: \fItrue | false\fR +.RS 4 +The list of participants is public, without requiring to enter the room\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBpubsub\fR: \fIPubSub Node\fR +.RS 4 +XMPP URI of associated Publish/Subscribe node\&. The default value is an empty string\&. +.RE +.PP +\fBtitle\fR: \fIRoom Title\fR +.RS 4 +A human\-readable title of the room\&. There is no default value +.RE +.PP +\fBvcard\fR: \fIvCard\fR +.RS 4 +A custom vCard for the room\&. See the equivalent mod_muc option\&.The default value is an empty string\&. +.RE +.PP +\fBvcard_xupdate\fR: \fIundefined | external | AvatarHash\fR +.RS 4 +Set the hash of the avatar image\&. The default value is +\fIundefined\fR\&. +.RE +.PP +\fBvoice_request_min_interval\fR: \fINumber\fR +.RS 4 +Minimum interval between voice requests, in seconds\&. The default value is +\fI1800\fR\&. +.RE +.RE +.PP +\fBhibernation_timeout\fR: \fIinfinity | Seconds\fR +.RS 4 +Timeout before hibernating the room process, expressed in seconds\&. The default value is +\fIinfinity\fR\&. +.RE +.PP +\fBhistory_size\fR: \fISize\fR +.RS 4 +A small history of the current discussion is sent to users when they enter the room\&. With this option you can define the number of history messages to keep and send to users joining the room\&. The value is a non\-negative integer\&. Setting the value to +\fI0\fR +disables the history feature and, as a result, nothing is kept in memory\&. The default value is +\fI20\fR\&. This value affects all rooms on the service\&. NOTE: modern XMPP clients rely on Message Archives (XEP\-0313), so feel free to disable the history feature if you\(cqre only using modern clients and have +\fImod_mam\fR +module loaded\&. +.RE +.PP +\fBhost\fR +.RS 4 +Deprecated\&. Use +\fIhosts\fR +instead\&. +.RE +.PP +\fBhosts\fR: \fI[Host, \&.\&.\&.]\fR +.RS 4 +This option defines the Jabber IDs of the service\&. If the +\fIhosts\fR +option is not specified, the only Jabber ID will be the hostname of the virtual host with the prefix "conference\&."\&. The keyword +\fI@HOST@\fR +is replaced with the real virtual host name\&. +.RE +.PP +\fBmax_captcha_whitelist\fR: \fINumber\fR +.RS 4 +\fINote\fR +about this option: added in 21\&.01\&. This option defines the maximum number of characters that Captcha Whitelist can have when configuring the room\&. The default value is +\fIinfinity\fR\&. +.RE +.PP +\fBmax_password\fR: \fINumber\fR +.RS 4 +\fINote\fR +about this option: added in 21\&.01\&. This option defines the maximum number of characters that Password can have when configuring the room\&. The default value is +\fIinfinity\fR\&. +.RE +.PP +\fBmax_room_desc\fR: \fINumber\fR +.RS 4 +This option defines the maximum number of characters that Room Description can have when configuring the room\&. The default value is +\fIinfinity\fR\&. +.RE +.PP +\fBmax_room_id\fR: \fINumber\fR +.RS 4 +This option defines the maximum number of characters that Room ID can have when creating a new room\&. The default value is +\fIinfinity\fR\&. +.RE +.PP +\fBmax_room_name\fR: \fINumber\fR +.RS 4 +This option defines the maximum number of characters that Room Name can have when configuring the room\&. The default value is +\fIinfinity\fR\&. +.RE +.PP +\fBmax_rooms_discoitems\fR: \fINumber\fR +.RS 4 +When there are more rooms than this +\fINumber\fR, only the non\-empty ones are returned in a Service Discovery query\&. The default value is +\fI100\fR\&. +.RE +.PP +\fBmax_user_conferences\fR: \fINumber\fR +.RS 4 +This option defines the maximum number of rooms that any given user can join\&. The default value is +\fI100\fR\&. This option is used to prevent possible abuses\&. Note that this is a soft limit: some users can sometimes join more conferences in cluster configurations\&. +.RE +.PP +\fBmax_users\fR: \fINumber\fR +.RS 4 +This option defines at the service level, the maximum number of users allowed per room\&. It can be lowered in each room configuration but cannot be increased in individual room configuration\&. The default value is +\fI200\fR\&. +.RE +.PP +\fBmax_users_admin_threshold\fR: \fINumber\fR +.RS 4 +This option defines the number of service admins or room owners allowed to enter the room when the maximum number of allowed occupants was reached\&. The default limit is +\fI5\fR\&. +.RE +.PP +\fBmax_users_presence\fR: \fINumber\fR +.RS 4 +This option defines after how many users in the room, it is considered overcrowded\&. When a MUC room is considered overcrowded, presence broadcasts are limited to reduce load, traffic and excessive presence "storm" received by participants\&. The default value is +\fI1000\fR\&. +.RE +.PP +\fBmin_message_interval\fR: \fINumber\fR +.RS 4 +This option defines the minimum interval between two messages send by an occupant in seconds\&. This option is global and valid for all rooms\&. A decimal value can be used\&. When this option is not defined, message rate is not limited\&. This feature can be used to protect a MUC service from occupant abuses and limit number of messages that will be broadcasted by the service\&. A good value for this minimum message interval is +\fI0\&.4\fR +second\&. If an occupant tries to send messages faster, an error is send back explaining that the message has been discarded and describing the reason why the message is not acceptable\&. +.RE +.PP +\fBmin_presence_interval\fR: \fINumber\fR +.RS 4 +This option defines the minimum of time between presence changes coming from a given occupant in seconds\&. This option is global and valid for all rooms\&. A decimal value can be used\&. When this option is not defined, no restriction is applied\&. This option can be used to protect a MUC service for occupants abuses\&. If an occupant tries to change its presence more often than the specified interval, the presence is cached by ejabberd and only the last presence is broadcasted to all occupants in the room after expiration of the interval delay\&. Intermediate presence packets are silently discarded\&. A good value for this option is +\fI4\fR +seconds\&. +.RE +.PP +\fBname\fR: \fIstring()\fR +.RS 4 +The value of the service name\&. This name is only visible in some clients that support +XEP\-0030: Service Discovery\&. The default is +\fIChatrooms\fR\&. +.RE +.PP +\fBpreload_rooms\fR: \fItrue | false\fR +.RS 4 +Whether to load all persistent rooms in memory on startup\&. If disabled, the room is only loaded on first participant join\&. The default is +\fItrue\fR\&. It makes sense to disable room preloading when the number of rooms is high: this will improve server startup time and memory consumption\&. +.RE +.PP +\fBqueue_type\fR: \fIram | file\fR +.RS 4 +Same as top\-level +\fIqueue_type\fR +option, but applied to this module only\&. +.RE +.PP +\fBram_db_type\fR: \fImnesia | sql\fR +.RS 4 +Same as top\-level +\fIdefault_ram_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBregexp_room_id\fR: \fIstring()\fR +.RS 4 +This option defines the regular expression that a Room ID must satisfy to allow the room creation\&. The default value is the empty string\&. +.RE +.PP +\fBroom_shaper\fR: \fInone | ShaperName\fR +.RS 4 +This option defines shaper for the MUC rooms\&. The default value is +\fInone\fR\&. +.RE +.PP +\fBuser_message_shaper\fR: \fInone | ShaperName\fR +.RS 4 +This option defines shaper for the users messages\&. The default value is +\fInone\fR\&. +.RE +.PP +\fBuser_presence_shaper\fR: \fInone | ShaperName\fR +.RS 4 +This option defines shaper for the users presences\&. The default value is +\fInone\fR\&. +.RE +.PP +\fBvcard\fR: \fIvCard\fR +.RS 4 +A custom vCard of the service that will be displayed by some XMPP clients in Service Discovery\&. The value of +\fIvCard\fR +is a YAML map constructed from an XML representation of vCard\&. Since the representation has no attributes, the mapping is straightforward\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# This XML representation of vCard: +# +# Conferences +# +# +# Elm Street +# +# +# +# is translated to: +vcard: + fn: Conferences + adr: + \- + work: true + street: Elm Street +.fi +.if n \{\ +.RE +.\} +.RE +.RE +.SS "mod_muc_admin" +.sp +This module provides commands to administer local MUC services and their MUC rooms\&. It also provides simple WebAdmin pages to view the existing rooms\&. +.sp +This module depends on \fImod_muc\fR\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBsubscribe_room_many_max_users\fR: \fINumber\fR +.RS 4 +\fINote\fR +about this option: added in 22\&.05\&. How many users can be subscribed to a room at once using the +\fIsubscribe_room_many\fR +API\&. The default value is +\fI50\fR\&. +.RE +.sp +\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#muc|muc\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#muc_room|muc_room\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#muc_sub|muc_sub\fR +.RE +.SS "mod_muc_log" +.sp +This module enables optional logging of Multi\-User Chat (MUC) public conversations to HTML\&. Once you enable this module, users can join a room using a MUC capable XMPP client, and if they have enough privileges, they can request the configuration form in which they can set the option to enable room logging\&. +.sp +Features: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Room details are added on top of each page: room title, JID, author, subject and configuration\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +The room JID in the generated HTML is a link to join the room (using XMPP URI)\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Subject and room configuration changes are tracked and displayed\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Joins, leaves, nick changes, kicks, bans and +\fI/me\fR +are tracked and displayed, including the reason if available\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Generated HTML files are XHTML 1\&.0 Transitional and CSS compliant\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Timestamps are self\-referencing links\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Links on top for quicker navigation: Previous day, Next day, Up\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +CSS is used for style definition, and a custom CSS file can be used\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +URLs on messages and subjects are converted to hyperlinks\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Timezone used on timestamps is shown on the log files\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +A custom link can be added on top of each page\&. +.RE +.sp +The module depends on \fImod_muc\fR\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess_log\fR: \fIAccessName\fR +.RS 4 +This option restricts which occupants are allowed to enable or disable room logging\&. The default value is +\fImuc_admin\fR\&. NOTE: for this default setting you need to have an access rule for +\fImuc_admin\fR +in order to take effect\&. +.RE +.PP +\fBcssfile\fR: \fIPath | URL\fR +.RS 4 +With this option you can set whether the HTML files should have a custom CSS file or if they need to use the embedded CSS\&. Allowed values are either +\fIPath\fR +to local file or an +\fIURL\fR +to a remote file\&. By default a predefined CSS will be embedded into the HTML page\&. +.RE +.PP +\fBdirname\fR: \fIroom_jid | room_name\fR +.RS 4 +Configure the name of the room directory\&. If set to +\fIroom_jid\fR, the room directory name will be the full room JID\&. Otherwise, the room directory name will be only the room name, not including the MUC service name\&. The default value is +\fIroom_jid\fR\&. +.RE +.PP +\fBdirtype\fR: \fIsubdirs | plain\fR +.RS 4 +The type of the created directories can be specified with this option\&. If set to +\fIsubdirs\fR, subdirectories are created for each year and month\&. Otherwise, the names of the log files contain the full date, and there are no subdirectories\&. The default value is +\fIsubdirs\fR\&. +.RE +.PP +\fBfile_format\fR: \fIhtml | plaintext\fR +.RS 4 +Define the format of the log files: +\fIhtml\fR +stores in HTML format, +\fIplaintext\fR +stores in plain text\&. The default value is +\fIhtml\fR\&. +.RE +.PP +\fBfile_permissions\fR: \fI{mode: Mode, group: Group}\fR +.RS 4 +Define the permissions that must be used when creating the log files: the number of the mode, and the numeric id of the group that will own the files\&. The default value is shown in the example below: +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +file_permissions: + mode: 644 + group: 33 +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBoutdir\fR: \fIPath\fR +.RS 4 +This option sets the full path to the directory in which the HTML files should be stored\&. Make sure the ejabberd daemon user has write access on that directory\&. The default value is +\fIwww/muc\fR\&. +.RE +.PP +\fBspam_prevention\fR: \fItrue | false\fR +.RS 4 +If set to +\fItrue\fR, a special attribute is added to links that prevent their indexation by search engines\&. The default value is +\fItrue\fR, which mean that +\fInofollow\fR +attributes will be added to user submitted links\&. +.RE +.PP +\fBtimezone\fR: \fIlocal | universal\fR +.RS 4 +The time zone for the logs is configurable with this option\&. If set to +\fIlocal\fR, the local time, as reported to Erlang emulator by the operating system, will be used\&. Otherwise, UTC time will be used\&. The default value is +\fIlocal\fR\&. +.RE +.PP +\fBtop_link\fR: \fI{URL: Text}\fR +.RS 4 +With this option you can customize the link on the top right corner of each log file\&. The default value is shown in the example below: +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +top_link: + /: Home +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBurl\fR: \fIURL\fR +.RS 4 +A top level +\fIURL\fR +where a client can access logs of a particular conference\&. The conference name is appended to the URL if +\fIdirname\fR +option is set to +\fIroom_name\fR +or a conference JID is appended to the +\fIURL\fR +otherwise\&. There is no default value\&. +.RE +.RE +.SS "mod_muc_occupantid" +.sp +\fINote\fR about this option: added in 23\&.10\&. +.sp +This module implements XEP\-0421: Anonymous unique occupant identifiers for MUCs\&. +.sp +When the module is enabled, the feature is enabled in all semi\-anonymous rooms\&. +.sp +The module has no options\&. +.SS "mod_muc_rtbl" +.sp +\fINote\fR about this option: added in 23\&.04\&. +.sp +This module implement Real\-time blocklists for MUC rooms\&. +.sp +It works by observing remote pubsub node conforming with specification described in https://xmppbl\&.org/\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBrtbl_node\fR: \fIPubsubNodeName\fR +.RS 4 +Name of pubsub node that should be used to track blocked users\&. The default value is +\fImuc_bans_sha256\fR\&. +.RE +.PP +\fBrtbl_server\fR: \fIDomain\fR +.RS 4 +Domain of xmpp server that serves block list\&. The default value is +\fIxmppbl\&.org\fR +.RE +.RE +.SS "mod_multicast" +.sp +This module implements a service for XEP\-0033: Extended Stanza Addressing\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess\fR: \fIAccess\fR +.RS 4 +The access rule to restrict who can send packets to the multicast service\&. Default value: +\fIall\fR\&. +.RE +.PP +\fBhost\fR +.RS 4 +Deprecated\&. Use +\fIhosts\fR +instead\&. +.RE +.PP +\fBhosts\fR: \fI[Host, \&.\&.\&.]\fR +.RS 4 +This option defines the Jabber IDs of the service\&. If the +\fIhosts\fR +option is not specified, the only Jabber ID will be the hostname of the virtual host with the prefix "multicast\&."\&. The keyword +\fI@HOST@\fR +is replaced with the real virtual host name\&. The default value is +\fImulticast\&.@HOST@\fR\&. +.RE +.PP +\fBlimits\fR: \fISender: Stanza: Number\fR +.RS 4 +Specify a list of custom limits which override the default ones defined in XEP\-0033\&. Limits are defined per sender type and stanza type, where: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIsender\fR +can be: +\fIlocal\fR +or +\fIremote\fR\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIstanza\fR +can be: +\fImessage\fR +or +\fIpresence\fR\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fInumber\fR +can be a positive integer or +\fIinfinite\fR\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# Default values: +local: + message: 100 + presence: 100 +remote: + message: 20 + presence: 20 +.fi +.if n \{\ +.RE +.\} +.RE +.RE +.PP +\fBname\fR +.RS 4 +Service name to provide in the Info query to the Service Discovery\&. Default is +\fI"Multicast"\fR\&. +.RE +.PP +\fBvcard\fR +.RS 4 +vCard element to return when queried\&. Default value is +\fIundefined\fR\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +# Only admins can send packets to multicast service +access_rules: + multicast: + \- allow: admin + +# If you want to allow all your users: +access_rules: + multicast: + \- allow + +# This allows both admins and remote users to send packets, +# but does not allow local users +acl: + allservers: + server_glob: "*" +access_rules: + multicast: + \- allow: admin + \- deny: local + \- allow: allservers + +modules: + mod_multicast: + host: multicast\&.example\&.org + access: multicast + limits: + local: + message: 40 + presence: infinite + remote: + message: 150 +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_offline" +.sp +This module implements XEP\-0160: Best Practices for Handling Offline Messages and XEP\-0013: Flexible Offline Message Retrieval\&. This means that all messages sent to an offline user will be stored on the server until that user comes online again\&. Thus it is very similar to how email works\&. A user is considered offline if no session presence priority > 0 are currently open\&. +.sp +The \fIdelete_expired_messages\fR API allows to delete expired messages, and \fIdelete_old_messages\fR API deletes older ones\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess_max_user_messages\fR: \fIAccessName\fR +.RS 4 +This option defines which access rule will be enforced to limit the maximum number of offline messages that a user can have (quota)\&. When a user has too many offline messages, any new messages that they receive are discarded, and a +\fI\fR +error is returned to the sender\&. The default value is +\fImax_user_offline_messages\fR\&. +.RE +.PP +\fBbounce_groupchat\fR: \fItrue | false\fR +.RS 4 +This option is use the disable an optimization that avoids bouncing error messages when groupchat messages could not be stored as offline\&. It will reduce chat room load, without any drawback in standard use cases\&. You may change default value only if you have a custom module which uses offline hook after +\fImod_offline\fR\&. This option can be useful for both standard MUC and MucSub, but the bounce is much more likely to happen in the context of MucSub, so it is even more important to have it on large MucSub services\&. The default value is +\fIfalse\fR, meaning the optimization is enabled\&. +.RE +.PP +\fBcache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as top\-level +\fIcache_life_time\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as top\-level +\fIcache_size\fR +option, but applied to this module only\&. +.RE +.PP +\fBdb_type\fR: \fImnesia | sql\fR +.RS 4 +Same as top\-level +\fIdefault_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBstore_empty_body\fR: \fItrue | false | unless_chat_state\fR +.RS 4 +Whether or not to store messages that lack a +\fI\fR +element\&. The default value is +\fIunless_chat_state\fR, which tells ejabberd to store messages even if they lack the +\fI\fR +element, unless they only contain a chat state notification (as defined in +XEP\-0085: Chat State Notifications\&. +.RE +.PP +\fBstore_groupchat\fR: \fItrue | false\fR +.RS 4 +Whether or not to store groupchat messages\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBuse_cache\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIuse_cache\fR +option, but applied to this module only\&. +.RE +.PP +\fBuse_mam_for_storage\fR: \fItrue | false\fR +.RS 4 +This is an experimental option\&. By enabling the option, this module uses the +\fIarchive\fR +table from +\fImod_mam\fR +instead of its own spool table to retrieve the messages received when the user was offline\&. This allows client developers to slowly drop XEP\-0160 and rely on XEP\-0313 instead\&. It also further reduces the storage required when you enable MucSub\&. Enabling this option has a known drawback for the moment: most of flexible message retrieval queries don\(cqt work (those that allow retrieval/deletion of messages by id), but this specification is not widely used\&. The default value is +\fIfalse\fR +to keep former behaviour as default\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExamples:\fR +.RS 4 +.sp +This example allows power users to have as much as 5000 offline messages, administrators up to 2000, and all the other users up to 100: +.sp +.if n \{\ +.RS 4 +.\} +.nf +acl: + admin: + user: + \- admin1@localhost + \- admin2@example\&.org + poweruser: + user: + \- bob@example\&.org + \- jane@example\&.org + +shaper_rules: + max_user_offline_messages: + \- 5000: poweruser + \- 2000: admin + \- 100 + +modules: + \&.\&.\&. + mod_offline: + access_max_user_messages: max_user_offline_messages + \&.\&.\&. +.fi +.if n \{\ +.RE +.\} +.sp +\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#offline|offline\fR +.RE +.SS "mod_ping" +.sp +This module implements support for XEP\-0199: XMPP Ping and periodic keepalives\&. When this module is enabled ejabberd responds correctly to ping requests, as defined by the protocol\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBping_ack_timeout\fR: \fItimeout()\fR +.RS 4 +How long to wait before deeming that a client has not answered a given server ping request\&. NOTE: when +\fImod_stream_mgmt\fR +is loaded and stream management is enabled by a client, this value is ignored, and the +ack_timeout +applies instead\&. The default value is +\fIundefined\fR\&. +.RE +.PP +\fBping_interval\fR: \fItimeout()\fR +.RS 4 +How often to send pings to connected clients, if option +\fIsend_pings\fR +is set to +\fItrue\fR\&. If a client connection does not send or receive any stanza within this interval, a ping request is sent to the client\&. The default value is +\fI1\fR +minute\&. +.RE +.PP +\fBsend_pings\fR: \fItrue | false\fR +.RS 4 +If this option is set to +\fItrue\fR, the server sends pings to connected clients that are not active in a given interval defined in +\fIping_interval\fR +option\&. This is useful to keep client connections alive or checking availability\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBtimeout_action\fR: \fInone | kill\fR +.RS 4 +What to do when a client does not answer to a server ping request in less than period defined in +\fIping_ack_timeout\fR +option: +\fIkill\fR +means destroying the underlying connection, +\fInone\fR +means to do nothing\&. NOTE: when +\fImod_stream_mgmt\fR +is loaded and stream management is enabled by a client, killing the client connection doesn\(cqt mean killing the client session \- the session will be kept alive in order to give the client a chance to resume it\&. The default value is +\fInone\fR\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_ping: + send_pings: true + ping_interval: 4 min + timeout_action: kill +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_pres_counter" +.sp +This module detects flood/spam in presence subscriptions traffic\&. If a user sends or receives more of those stanzas in a given time interval, the exceeding stanzas are silently dropped, and a warning is logged\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBcount\fR: \fINumber\fR +.RS 4 +The number of subscription presence stanzas (subscribe, unsubscribe, subscribed, unsubscribed) allowed for any direction (input or output) per time defined in +\fIinterval\fR +option\&. Please note that two users subscribing to each other usually generate 4 stanzas, so the recommended value is +\fI4\fR +or more\&. The default value is +\fI5\fR\&. +.RE +.PP +\fBinterval\fR: \fItimeout()\fR +.RS 4 +The time interval\&. The default value is +\fI1\fR +minute\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_pres_counter: + count: 5 + interval: 30 secs +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_privacy" +.sp +This module implements XEP\-0016: Privacy Lists\&. +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBNote\fR +.ps -1 +.br +.sp +Nowadays modern XMPP clients rely on XEP\-0191: Blocking Command which is implemented by \fImod_blocking\fR\&. However, you still need \fImod_privacy\fR loaded in order for \fImod_blocking\fR to work\&. +.sp .5v +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBcache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as top\-level +\fIcache_life_time\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_missed\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIcache_missed\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as top\-level +\fIcache_size\fR +option, but applied to this module only\&. +.RE +.PP +\fBdb_type\fR: \fImnesia | sql\fR +.RS 4 +Same as top\-level +\fIdefault_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBuse_cache\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIuse_cache\fR +option, but applied to this module only\&. +.RE +.RE +.SS "mod_private" +.sp +This module adds support for XEP\-0049: Private XML Storage\&. +.sp +Using this method, XMPP entities can store private data on the server, retrieve it whenever necessary and share it between multiple connected clients of the same user\&. The data stored might be anything, as long as it is a valid XML\&. One typical usage is storing a bookmark of all user\(cqs conferences (XEP\-0048: Bookmarks)\&. +.sp +It also implements the bookmark conversion described in XEP\-0402: PEP Native Bookmarks, see \fIbookmarks_to_pep\fR API\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBcache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as top\-level +\fIcache_life_time\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_missed\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIcache_missed\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as top\-level +\fIcache_size\fR +option, but applied to this module only\&. +.RE +.PP +\fBdb_type\fR: \fImnesia | sql\fR +.RS 4 +Same as top\-level +\fIdefault_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBuse_cache\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIuse_cache\fR +option, but applied to this module only\&. +.RE +.sp +\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#private|private\fR +.RE +.SS "mod_privilege" +.sp +\fINote\fR about this option: improved in 24\&.10\&. +.sp +This module is an implementation of XEP\-0356: Privileged Entity\&. This extension allows components to have privileged access to other entity data (send messages on behalf of the server or on behalf of a user, get/set user roster, access presence information, etc\&.)\&. This may be used to write powerful external components, for example implementing an external PEP or MAM service\&. +.sp +By default a component does not have any privileged access\&. It is worth noting that the permissions grant access to the component to a specific data type for all users of the virtual host on which \fImod_privilege\fR is loaded\&. +.sp +Make sure you have a listener configured to connect your component\&. Check the section about listening ports for more information\&. +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBWarning\fR +.ps -1 +.br +.sp +Security issue: Privileged access gives components access to sensitive data, so permission should be granted carefully, only if you trust a component\&. +.sp .5v +.RE +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBNote\fR +.ps -1 +.br +.sp +This module is complementary to \fImod_delegation\fR, but can also be used separately\&. +.sp .5v +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBiq\fR: \fI{Namespace: Options}\fR +.RS 4 +This option defines namespaces and their IQ permissions\&. By default no permissions are given\&. The +\fIOptions\fR +are: +.PP +\fBboth\fR: \fIAccessName\fR +.RS 4 +Allows sending IQ stanzas of type +\fIget\fR +and +\fIset\fR\&. The default value is +\fInone\fR\&. +.RE +.PP +\fBget\fR: \fIAccessName\fR +.RS 4 +Allows sending IQ stanzas of type +\fIget\fR\&. The default value is +\fInone\fR\&. +.RE +.PP +\fBset\fR: \fIAccessName\fR +.RS 4 +Allows sending IQ stanzas of type +\fIset\fR\&. The default value is +\fInone\fR\&. +.RE +.RE +.PP +\fBmessage\fR: \fIOptions\fR +.RS 4 +This option defines permissions for messages\&. By default no permissions are given\&. The +\fIOptions\fR +are: +.PP +\fBoutgoing\fR: \fIAccessName\fR +.RS 4 +The option defines an access rule for sending outgoing messages by the component\&. The default value is +\fInone\fR\&. +.RE +.RE +.PP +\fBpresence\fR: \fIOptions\fR +.RS 4 +This option defines permissions for presences\&. By default no permissions are given\&. The +\fIOptions\fR +are: +.PP +\fBmanaged_entity\fR: \fIAccessName\fR +.RS 4 +An access rule that gives permissions to the component to receive server presences\&. The default value is +\fInone\fR\&. +.RE +.PP +\fBroster\fR: \fIAccessName\fR +.RS 4 +An access rule that gives permissions to the component to receive the presence of both the users and the contacts in their roster\&. The default value is +\fInone\fR\&. +.RE +.RE +.PP +\fBroster\fR: \fIOptions\fR +.RS 4 +This option defines roster permissions\&. By default no permissions are given\&. The +\fIOptions\fR +are: +.PP +\fBboth\fR: \fIAccessName\fR +.RS 4 +Sets read/write access to a user\(cqs roster\&. The default value is +\fInone\fR\&. +.RE +.PP +\fBget\fR: \fIAccessName\fR +.RS 4 +Sets read access to a user\(cqs roster\&. The default value is +\fInone\fR\&. +.RE +.PP +\fBset\fR: \fIAccessName\fR +.RS 4 +Sets write access to a user\(cqs roster\&. The default value is +\fInone\fR\&. +.RE +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_privilege: + iq: + http://jabber\&.org/protocol/pubsub: + get: all + roster: + get: all + presence: + managed_entity: all + message: + outgoing: all +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_providers 🟤" +.sp +\fINote\fR about this option: added in 25\&.08\&. +.sp +This module serves JSON provider files API v2 as described by XMPP Providers\&. +.sp +It attempts to fill some properties gathering values automatically from your existing ejabberd configuration\&. Try enabling the module, check what values are displayed, and then customize using the options\&. +.sp +To use this module, in addition to adding it to the \fImodules\fR section, you must also enable it in \fIlisten\fR → \fIejabberd_http\fR → \fIlisten\-options\&.md#request_handlers|request_handlers\fR\&. Notice you should set in \fIlisten\&.md#ejabberd_http|ejabberd_http\fR the option \fIlisten\-options\&.md#tls|tls\fR enabled\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBalternativeJids\fR: \fI[string()]\fR +.RS 4 +List of JIDs (XMPP server domains) a provider offers for registration other than its main JID\&. The default value is +\fI[]\fR\&. +.RE +.PP +\fBbusFactor\fR: \fIinteger()\fR +.RS 4 +Bus factor of the XMPP service (i\&.e\&., the minimum number of team members that the service could not survive losing) or +\fI\-1\fR +for n/a\&. The default value is +\fI\-1\fR\&. +.RE +.PP +\fBfreeOfCharge\fR: \fItrue | false\fR +.RS 4 +Whether the XMPP service can be used for free\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBlanguages\fR: \fI[string()]\fR +.RS 4 +List of language codes that your pages are available\&. Some options define URL where the keyword +\fI@LANGUAGE_URL@\fR +will be replaced with each of those language codes\&. The default value is a list with the language set in the option +\fIlanguage\fR, for example: +\fI[en]\fR\&. +.RE +.PP +\fBlegalNotice\fR: \fIstring()\fR +.RS 4 +Legal notice web page (per language)\&. The keyword +\fI@LANGUAGE_URL@\fR +is replaced with each language\&. The default value is +\fI""\fR\&. +.RE +.PP +\fBmaximumHttpFileUploadStorageTime\fR: \fIinteger()\fR +.RS 4 +Maximum storage duration of each shared file (number in days, +\fI0\fR +for no limit or +\fI\-1\fR +for less than 1 day)\&. The default value is the same as option +\fImax_days\fR +from module +\fImod_http_upload_quota\fR, or +\fI0\fR +otherwise\&. +.RE +.PP +\fBmaximumHttpFileUploadTotalSize\fR: \fIinteger()\fR +.RS 4 +Maximum size of all shared files in total per user (number in megabytes (MB), +\fI0\fR +for no limit or +\fI\-1\fR +for less than 1 MB)\&. Attention: MB is used instead of MiB (e\&.g\&., 104,857,600 bytes = 100 MiB H 104 MB)\&. This property is not about the maximum size of each shared file, which is already retrieved via XMPP\&. The default value is the value of the shaper value of option +\fIaccess_hard_quota\fR +from module +\fImod_http_upload_quota\fR, or +\fI0\fR +otherwise\&. +.RE +.PP +\fBmaximumMessageArchiveManagementStorageTime\fR: \fIinteger()\fR +.RS 4 +Maximum storage duration of each exchanged message (number in days, +\fI0\fR +for no limit or +\fI\-1\fR +for less than 1 day)\&. The default value is +\fI0\fR\&. +.RE +.PP +\fBorganization\fR: \fIstring()\fR +.RS 4 +Type of organization providing the XMPP service\&. Allowed values are: +\fIcompany\fR, +\fI"commercial person"\fR, +\fI"private person"\fR, +\fIgovernmental\fR, +\fI"non\-governmental"\fR +or +\fI""\fR\&. The default value is +\fI""\fR\&. +.RE +.PP +\fBpasswordReset\fR: \fIstring()\fR +.RS 4 +Password reset web page (per language) used for an automatic password reset (e\&.g\&., via email) or describing how to manually reset a password (e\&.g\&., by contacting the provider)\&. The keyword +\fI@LANGUAGE_URL@\fR +is replaced with each language\&. The default value is an URL built automatically if +\fImod_register_web\fR +is configured as a +\fIrequest_handler\fR, or +\fI""\fR +otherwise\&. +.RE +.PP +\fBprofessionalHosting\fR: \fItrue | false\fR +.RS 4 +Whether the XMPP server is hosted with good internet connection speed, uninterruptible power supply, access protection and regular backups\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBserverLocations\fR: \fI[string()]\fR +.RS 4 +List of language codes of Server/Backup locations\&. The default value is an empty list: +\fI[]\fR\&. +.RE +.PP +\fBserverTesting\fR: \fItrue | false\fR +.RS 4 +Whether tests against the provider\(cqs server are allowed (e\&.g\&., certificate checks and uptime monitoring)\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBsince\fR: \fIstring()\fR +.RS 4 +Date since the XMPP service is available\&. The default value is an empty string: +\fI""\fR\&. +.RE +.PP +\fBwebsite\fR: \fIstring()\fR +.RS 4 +Provider website\&. The keyword +\fI@LANGUAGE_URL@\fR +is replaced with each language\&. The default value is +\fI""\fR\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +listen: + \- + port: 443 + module: ejabberd_http + tls: true + request_handlers: + /\&.well\-known/xmpp\-provider\-v2\&.json: mod_providers + +modules: + mod_providers: + alternativeJids: ["example1\&.com", "example2\&.com"] + busFactor: 1 + freeOfCharge: true + languages: [ag, ao, bg, en] + legalNotice: "http://@HOST@/legal/@LANGUAGE_URL@/" + maximumHttpFileUploadStorageTime: 0 + maximumHttpFileUploadTotalSize: 0 + maximumMessageArchiveManagementStorageTime: 0 + organization: "non\-governmental" + passwordReset: "http://@HOST@/reset/@LANGUAGE_URL@/" + professionalHosting: true + serverLocations: [ao, bg] + serverTesting: true + since: "2025\-12\-31" + website: "http://@HOST@/website/@LANGUAGE_URL@/" +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_proxy65" +.sp +This module implements XEP\-0065: SOCKS5 Bytestreams\&. It allows ejabberd to act as a file transfer proxy between two XMPP clients\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess\fR: \fIAccessName\fR +.RS 4 +Defines an access rule for file transfer initiators\&. The default value is +\fIall\fR\&. You may want to restrict access to the users of your server only, in order to avoid abusing your proxy by the users of remote servers\&. +.RE +.PP +\fBauth_type\fR: \fIanonymous | plain\fR +.RS 4 +SOCKS5 authentication type\&. The default value is +\fIanonymous\fR\&. If set to +\fIplain\fR, ejabberd will use authentication backend as it would for SASL PLAIN\&. +.RE +.PP +\fBhost\fR +.RS 4 +Deprecated\&. Use +\fIhosts\fR +instead\&. +.RE +.PP +\fBhostname\fR: \fIHost\fR +.RS 4 +Defines a hostname offered by the proxy when establishing a session with clients\&. This is useful when you run the proxy behind a NAT\&. The keyword +\fI@HOST@\fR +is replaced with the virtual host name\&. The default is to use the value of +\fIip\fR +option\&. Examples: +\fIproxy\&.mydomain\&.org\fR, +\fI200\&.150\&.100\&.50\fR\&. +.RE +.PP +\fBhosts\fR: \fI[Host, \&.\&.\&.]\fR +.RS 4 +This option defines the Jabber IDs of the service\&. If the +\fIhosts\fR +option is not specified, the only Jabber ID will be the hostname of the virtual host with the prefix "proxy\&."\&. The keyword +\fI@HOST@\fR +is replaced with the real virtual host name\&. +.RE +.PP +\fBip\fR: \fIIPAddress\fR +.RS 4 +This option specifies which network interface to listen for\&. The default value is an IP address of the service\(cqs DNS name, or, if fails, +\fI127\&.0\&.0\&.1\fR\&. +.RE +.PP +\fBmax_connections\fR: \fIpos_integer() | infinity\fR +.RS 4 +Maximum number of active connections per file transfer initiator\&. The default value is +\fIinfinity\fR\&. +.RE +.PP +\fBname\fR: \fIName\fR +.RS 4 +The value of the service name\&. This name is only visible in some clients that support +XEP\-0030: Service Discovery\&. The default is "SOCKS5 Bytestreams"\&. +.RE +.PP +\fBport\fR: \fI1\&.\&.65535\fR +.RS 4 +A port number to listen for incoming connections\&. The default value is +\fI7777\fR\&. +.RE +.PP +\fBram_db_type\fR: \fImnesia | redis | sql\fR +.RS 4 +Same as top\-level +\fIdefault_ram_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBrecbuf\fR: \fISize\fR +.RS 4 +A size of the buffer for incoming packets\&. If you define a shaper, set the value of this option to the size of the shaper in order to avoid traffic spikes in file transfers\&. The default value is +\fI65536\fR +bytes\&. +.RE +.PP +\fBshaper\fR: \fIShaper\fR +.RS 4 +This option defines a shaper for the file transfer peers\&. A shaper with the maximum bandwidth will be selected\&. The default is +\fInone\fR, i\&.e\&. no shaper\&. +.RE +.PP +\fBsndbuf\fR: \fISize\fR +.RS 4 +A size of the buffer for outgoing packets\&. If you define a shaper, set the value of this option to the size of the shaper in order to avoid traffic spikes in file transfers\&. The default value is +\fI65536\fR +bytes\&. +.RE +.PP +\fBvcard\fR: \fIvCard\fR +.RS 4 +A custom vCard of the service that will be displayed by some XMPP clients in Service Discovery\&. The value of +\fIvCard\fR +is a YAML map constructed from an XML representation of vCard\&. Since the representation has no attributes, the mapping is straightforward\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +acl: + admin: + user: admin@example\&.org + proxy_users: + server: example\&.org + +access_rules: + proxy65_access: + allow: proxy_users + +shaper_rules: + proxy65_shaper: + none: admin + proxyrate: proxy_users + +shaper: + proxyrate: 10240 + +modules: + mod_proxy65: + host: proxy1\&.example\&.org + name: "File Transfer Proxy" + ip: 200\&.150\&.100\&.1 + port: 7778 + max_connections: 5 + access: proxy65_access + shaper: proxy65_shaper + recbuf: 10240 + sndbuf: 10240 +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_pubsub" +.sp +This module offers a service for XEP\-0060: Publish\-Subscribe\&. The functionality in \fImod_pubsub\fR can be extended using plugins\&. The plugin that implements PEP (XEP\-0163: Personal Eventing via Pubsub) is enabled in the default ejabberd configuration file, and it requires \fImod_caps\fR\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess_createnode\fR: \fIAccessName\fR +.RS 4 +This option restricts which users are allowed to create pubsub nodes using +\fIacl\fR +and +\fIaccess\fR\&. By default any account in the local ejabberd server is allowed to create pubsub nodes\&. The default value is: +\fIall\fR\&. +.RE +.PP +\fBdb_type\fR: \fImnesia | sql\fR +.RS 4 +Same as top\-level +\fIdefault_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBdefault_node_config\fR: \fIList of Key:Value\fR +.RS 4 +To override default node configuration, regardless of node plugin\&. Value is a list of key\-value definition\&. Node configuration still uses default configuration defined by node plugin, and overrides any items by value defined in this configurable list\&. +.RE +.PP +\fBforce_node_config\fR: \fIList of Node and the list of its Key:Value\fR +.RS 4 +Define the configuration for given nodes\&. The default value is: +\fI[]\fR\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +force_node_config: + ## Avoid buggy clients to make their bookmarks public + storage:bookmarks: + access_model: whitelist +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBhost\fR +.RS 4 +Deprecated\&. Use +\fIhosts\fR +instead\&. +.RE +.PP +\fBhosts\fR: \fI[Host, \&.\&.\&.]\fR +.RS 4 +This option defines the Jabber IDs of the service\&. If the +\fIhosts\fR +option is not specified, the only Jabber ID will be the hostname of the virtual host with the prefix "pubsub\&."\&. The keyword +\fI@HOST@\fR +is replaced with the real virtual host name\&. +.RE +.PP +\fBignore_pep_from_offline\fR: \fIfalse | true\fR +.RS 4 +To specify whether or not we should get last published PEP items from users in our roster which are offline when we connect\&. Value is +\fItrue\fR +or +\fIfalse\fR\&. If not defined, pubsub assumes true so we only get last items of online contacts\&. +.RE +.PP +\fBlast_item_cache\fR: \fIfalse | true\fR +.RS 4 +To specify whether or not pubsub should cache last items\&. Value is +\fItrue\fR +or +\fIfalse\fR\&. If not defined, pubsub does not cache last items\&. On systems with not so many nodes, caching last items speeds up pubsub and allows you to raise the user connection rate\&. The cost is memory usage, as every item is stored in memory\&. +.RE +.PP +\fBmax_item_expire_node\fR: \fItimeout() | infinity\fR +.RS 4 +\fINote\fR +about this option: added in 21\&.12\&. Specify the maximum item epiry time\&. Default value is: +\fIinfinity\fR\&. +.RE +.PP +\fBmax_items_node\fR: \fInon_neg_integer() | infinity\fR +.RS 4 +Define the maximum number of items that can be stored in a node\&. Default value is: +\fI1000\fR\&. +.RE +.PP +\fBmax_nodes_discoitems\fR: \fIpos_integer() | infinity\fR +.RS 4 +The maximum number of nodes to return in a discoitem response\&. The default value is: +\fI100\fR\&. +.RE +.PP +\fBmax_subscriptions_node\fR: \fIMaxSubs\fR +.RS 4 +Define the maximum number of subscriptions managed by a node\&. Default value is no limitation: +\fIundefined\fR\&. +.RE +.PP +\fBname\fR: \fIName\fR +.RS 4 +The value of the service name\&. This name is only visible in some clients that support +XEP\-0030: Service Discovery\&. The default is +\fIvCard User Search\fR\&. +.RE +.PP +\fBnodetree\fR: \fINodetree\fR +.RS 4 +To specify which nodetree to use\&. If not defined, the default pubsub nodetree is used: +\fItree\fR\&. Only one nodetree can be used per host, and is shared by all node plugins\&. +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fItree\fR +nodetree store node configuration and relations on the database\&. +\fIflat\fR +nodes are stored without any relationship, and +\fIhometree\fR +nodes can have child nodes\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIvirtual\fR +nodetree does not store nodes on database\&. This saves resources on systems with tons of nodes\&. If using the +\fIvirtual\fR +nodetree, you can only enable those node plugins: +\fI[flat, pep]\fR +or +\fI[flat]\fR; any other plugins configuration will not work\&. Also, all nodes will have the default configuration, and this can not be changed\&. Using +\fIvirtual\fR +nodetree requires to start from a clean database, it will not work if you used the default +\fItree\fR +nodetree before\&. +.RE +.RE +.PP +\fBpep_mapping\fR: \fIList of Key:Value\fR +.RS 4 +In this option you can provide a list of key\-value to choose defined node plugins on given PEP namespace\&. The following example will use +\fInode_tune\fR +instead of +\fInode_pep\fR +for every PEP node with the tune namespace: +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + \&.\&.\&. + mod_pubsub: + pep_mapping: + http://jabber\&.org/protocol/tune: tune + \&.\&.\&. +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBplugins\fR: \fI[Plugin, \&.\&.\&.]\fR +.RS 4 +To specify which pubsub node plugins to use\&. The first one in the list is used by default\&. If this option is not defined, the default plugins list is: +\fI[flat]\fR\&. PubSub clients can define which plugin to use when creating a node: add +\fItype=\*(Aqplugin\-name\fR\*(Aq attribute to the +\fIcreate\fR +stanza element\&. +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIflat\fR +plugin handles the default behaviour and follows standard XEP\-0060 implementation\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIpep\fR +plugin adds extension to handle Personal Eventing Protocol (XEP\-0163) to the PubSub engine\&. When enabled, PEP is handled automatically\&. +.RE +.RE +.PP +\fBvcard\fR: \fIvCard\fR +.RS 4 +A custom vCard of the server that will be displayed by some XMPP clients in Service Discovery\&. The value of +\fIvCard\fR +is a YAML map constructed from an XML representation of vCard\&. Since the representation has no attributes, the mapping is straightforward\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# This XML representation of vCard: +# +# Conferences +# +# +# Elm Street +# +# +# +# is translated to: +vcard: + fn: Conferences + adr: + \- + work: true + street: Elm Street +.fi +.if n \{\ +.RE +.\} +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExamples:\fR +.RS 4 +.sp +Example of configuration that uses flat nodes as default, and allows use of flat, hometree and pep nodes: +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_pubsub: + access_createnode: pubsub_createnode + max_subscriptions_node: 100 + default_node_config: + notification_type: normal + notify_retract: false + max_items: 4 + plugins: + \- flat + \- pep +.fi +.if n \{\ +.RE +.\} +.sp +Using relational database requires using mod_pubsub with db_type \fIsql\fR\&. Only flat, hometree and pep plugins supports SQL\&. The following example shows previous configuration with SQL usage: +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_pubsub: + db_type: sql + access_createnode: pubsub_createnode + ignore_pep_from_offline: true + last_item_cache: false + plugins: + \- flat + \- pep +.fi +.if n \{\ +.RE +.\} +.sp +\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#purge|purge\fR +.RE +.SS "mod_pubsub_serverinfo" +.sp +\fINote\fR about this option: added in 25\&.07\&. +.sp +This module adds support for XEP\-0485: PubSub Server Information to expose S2S information over the Pub/Sub service\&. +.sp +Active S2S connections are published to a local PubSub node\&. Currently the node name is hardcoded as \fI"serverinfo"\fR\&. +.sp +Connections that support this feature are exposed with their domain names, otherwise they are shown as anonymous nodes\&. At startup a list of well known public servers is fetched\&. Those are not shown as anonymous even if they don\(cqt support this feature\&. +.sp +Please note that the module only shows S2S connections established while the module is running\&. If you install the module at runtime, run \fIstop_s2s_connections\fR API or restart ejabberd to force S2S reconnections that the module will detect and publish\&. +.sp +This module depends on \fImod_pubsub\fR and \fImod_disco\fR\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBpubsub_host\fR: \fIundefined | string()\fR +.RS 4 +Use this local PubSub host to advertise S2S connections\&. This must be a host local to this service handled by +\fImod_pubsub\fR\&. This option is only needed if your configuration has more than one host in mod_pubsub\(cqs +\fIhosts\fR +option\&. The default value is the first host defined in mod_pubsub +\fIhosts\fR +option\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_pubsub_serverinfo: + pubsub_host: custom\&.pubsub\&.domain\&.local +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_push" +.sp +This module implements the XMPP server\(cqs part of the push notification solution specified in XEP\-0357: Push Notifications\&. It does not generate, for example, APNS or FCM notifications directly\&. Instead, it\(cqs designed to work with so\-called "app servers" operated by third\-party vendors of mobile apps\&. Those app servers will usually trigger notification delivery to the user\(cqs mobile device using platform\-dependent backend services such as FCM or APNS\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBcache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as top\-level +\fIcache_life_time\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_missed\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIcache_missed\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as top\-level +\fIcache_size\fR +option, but applied to this module only\&. +.RE +.PP +\fBdb_type\fR: \fImnesia | sql\fR +.RS 4 +Same as top\-level +\fIdefault_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBinclude_body\fR: \fItrue | false | Text\fR +.RS 4 +If this option is set to +\fItrue\fR, the message text is included with push notifications generated for incoming messages with a body\&. The option can instead be set to a static +\fIText\fR, in which case the specified text will be included in place of the actual message body\&. This can be useful to signal the app server whether the notification was triggered by a message with body (as opposed to other types of traffic) without leaking actual message contents\&. The default value is "New message"\&. +.RE +.PP +\fBinclude_sender\fR: \fItrue | false\fR +.RS 4 +If this option is set to +\fItrue\fR, the sender\(cqs JID is included with push notifications generated for incoming messages with a body\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBnotify_on\fR: \fImessages | all\fR +.RS 4 +\fINote\fR +about this option: added in 23\&.10\&. If this option is set to +\fImessages\fR, notifications are generated only for actual chat messages with a body text (or some encrypted payload)\&. If it\(cqs set to +\fIall\fR, any kind of XMPP stanza will trigger a notification\&. If unsure, it\(cqs strongly recommended to stick to +\fIall\fR, which is the default value\&. +.RE +.PP +\fBuse_cache\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIuse_cache\fR +option, but applied to this module only\&. +.RE +.sp +\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#purge|purge\fR +.RE +.SS "mod_push_keepalive" +.sp +This module tries to keep the stream management session (see \fImod_stream_mgmt\fR) of a disconnected mobile client alive if the client enabled push notifications for that session\&. However, the normal session resumption timeout is restored once a push notification is issued, so the session will be closed if the client doesn\(cqt respond to push notifications\&. +.sp +The module depends on \fImod_push\fR\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBresume_timeout\fR: \fItimeout()\fR +.RS 4 +This option specifies the period of time until the session of a disconnected push client times out\&. This timeout is only in effect as long as no push notification is issued\&. Once that happened, the resumption timeout configured for +\fImod_stream_mgmt\fR +is restored\&. The default value is +\fI72\fR +hours\&. +.RE +.PP +\fBwake_on_start\fR: \fItrue | false\fR +.RS 4 +If this option is set to +\fItrue\fR, notifications are generated for +\fBall\fR +registered push clients during server startup\&. This option should not be enabled on servers with many push clients as it can generate significant load on the involved push services and the server itself\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBwake_on_timeout\fR: \fItrue | false\fR +.RS 4 +If this option is set to +\fItrue\fR, a notification is generated shortly before the session would time out as per the +\fIresume_timeout\fR +option\&. The default value is +\fItrue\fR\&. +.RE +.RE +.SS "mod_register" +.sp +This module adds support for XEP\-0077: In\-Band Registration\&. This protocol enables end users to use an XMPP client to: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Register a new account on the server\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Change the password from an existing account on the server\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Delete an existing account on the server\&. +.RE +.sp +This module reads also the top\-level \fIregistration_timeout\fR option defined globally for the server, so please check that option documentation too\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess\fR: \fIAccessName\fR +.RS 4 +Specify rules to restrict what usernames can be registered\&. If a rule returns +\fIdeny\fR +on the requested username, registration of that user name is denied\&. There are no restrictions by default\&. If +\fIAccessName\fR +is +\fInone\fR, then registering new accounts using In\-Band Registration is disabled and the corresponding stream feature is not announced to clients\&. +.RE +.PP +\fBaccess_from\fR: \fIAccessName\fR +.RS 4 +By default, ejabberd doesn\(cqt allow the client to register new accounts from s2s or existing c2s sessions\&. You can change it by defining access rule in this option\&. Use with care: allowing registration from s2s leads to uncontrolled massive accounts creation by rogue users\&. +.RE +.PP +\fBaccess_remove\fR: \fIAccessName\fR +.RS 4 +Specify rules to restrict access for user unregistration\&. By default any user is able to unregister their account\&. +.RE +.PP +\fBallow_modules\fR: \fIall | [Module, \&.\&.\&.]\fR +.RS 4 +\fINote\fR +about this option: added in 21\&.12\&. List of modules that can register accounts, or +\fIall\fR\&. The default value is +\fIall\fR, which is equivalent to something like +\fI[mod_register, mod_register_web]\fR\&. +.RE +.PP +\fBcaptcha_protected\fR: \fItrue | false\fR +.RS 4 +Protect registrations with +\fIbasic\&.md#captcha|CAPTCHA\fR\&. The default is +\fIfalse\fR\&. +.RE +.PP +\fBip_access\fR: \fIAccessName\fR +.RS 4 +Define rules to allow or deny account registration depending on the IP address of the XMPP client\&. The +\fIAccessName\fR +should be of type +\fIip\fR\&. The default value is +\fIall\fR\&. +.RE +.PP +\fBpassword_strength\fR: \fIEntropy\fR +.RS 4 +This option sets the minimum +Shannon entropy +for passwords\&. The value +\fIEntropy\fR +is a number of bits of entropy\&. The recommended minimum is 32 bits\&. The default is +\fI0\fR, i\&.e\&. no checks are performed\&. +.RE +.PP +\fBredirect_url\fR: \fIURL\fR +.RS 4 +This option enables registration redirection as described in +XEP\-0077: In\-Band Registration: Redirection\&. +.RE +.PP +\fBregistration_watchers\fR: \fI[JID, \&.\&.\&.]\fR +.RS 4 +This option defines a list of JIDs which will be notified each time a new account is registered\&. +.RE +.PP +\fBwelcome_message\fR: \fI{subject: Subject, body: Body}\fR +.RS 4 +Set a welcome message that is sent to each newly registered account\&. The message will have subject +\fISubject\fR +and text +\fIBody\fR\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_register: + welcome_message: + subject: "Welcome!" + body: |\- + Hi! + Welcome to this XMPP server +.fi +.if n \{\ +.RE +.\} +.RE +.RE +.SS "mod_register_web" +.sp +This module provides a web page where users can: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Register a new account on the server\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Change the password from an existing account on the server\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Unregister an existing account on the server\&. +.RE +.sp +This module supports \fIbasic\&.md#captcha|CAPTCHA\fR to register a new account\&. To enable this feature, configure the top\-level \fIcaptcha_cmd\fR and top\-level \fIcaptcha_url\fR options\&. +.sp +As an example usage, the users of the host \fIlocalhost\fR can visit the page: \fIhttps://localhost:5280/register/\fR It is important to include the last / character in the URL, otherwise the subpages URL will be incorrect\&. +.sp +This module is enabled in \fIlisten\fR → \fIejabberd_http\fR → \fIlisten\-options\&.md#request_handlers|request_handlers\fR, no need to enable in \fImodules\fR\&. The module depends on \fImod_register\fR where all the configuration is performed\&. +.sp +The module has no options\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +listen: + \- + port: 5280 + module: ejabberd_http + request_handlers: + /register: mod_register_web + +modules: + mod_register: {} +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_roster" +.sp +This module implements roster management as defined in RFC6121 Section 2\&. The module also adds support for XEP\-0237: Roster Versioning\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess\fR: \fIAccessName\fR +.RS 4 +This option can be configured to specify rules to restrict roster management\&. If the rule returns +\fIdeny\fR +on the requested user name, that user cannot modify their personal roster, i\&.e\&. they cannot add/remove/modify contacts or send presence subscriptions\&. The default value is +\fIall\fR, i\&.e\&. no restrictions\&. +.RE +.PP +\fBcache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as top\-level +\fIcache_life_time\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_missed\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIcache_missed\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as top\-level +\fIcache_size\fR +option, but applied to this module only\&. +.RE +.PP +\fBdb_type\fR: \fImnesia | sql\fR +.RS 4 +Same as top\-level +\fIdefault_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBstore_current_id\fR: \fItrue | false\fR +.RS 4 +If this option is set to +\fItrue\fR, the current roster version number is stored on the database\&. If set to +\fIfalse\fR, the roster version number is calculated on the fly each time\&. Enabling this option reduces the load for both ejabberd and the database\&. This option does not affect the client in any way\&. This option is only useful if option +\fIversioning\fR +is set to +\fItrue\fR\&. The default value is +\fIfalse\fR\&. IMPORTANT: if you use +\fImod_shared_roster\fR +or +\fImod_shared_roster_ldap\fR, you must set the value of the option to +\fIfalse\fR\&. +.RE +.PP +\fBuse_cache\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIuse_cache\fR +option, but applied to this module only\&. +.RE +.PP +\fBversioning\fR: \fItrue | false\fR +.RS 4 +Enables/disables Roster Versioning\&. The default value is +\fIfalse\fR\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_roster: + versioning: true + store_current_id: false +.fi +.if n \{\ +.RE +.\} +.sp +\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#roster|roster\fR +.RE +.SS "mod_s2s_bidi" +.sp +\fINote\fR about this option: added in 24\&.10\&. +.sp +The module adds support for XEP\-0288: Bidirectional Server\-to\-Server Connections that allows using single s2s connection to communicate in both directions\&. +.sp +The module has no options\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_s2s_bidi: {} +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_s2s_dialback" +.sp +The module adds support for XEP\-0220: Server Dialback to provide server identity verification based on DNS\&. +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBWarning\fR +.ps -1 +.br +.sp +DNS\-based verification is vulnerable to DNS cache poisoning, so modern servers rely on verification based on PKIX certificates\&. Thus this module is only recommended for backward compatibility with servers running outdated software or non\-TLS servers, or those with invalid certificates (as long as you accept the risks, e\&.g\&. you assume that the remote server has an invalid certificate due to poor administration and not because it\(cqs compromised)\&. +.sp .5v +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess\fR: \fIAccessName\fR +.RS 4 +An access rule that can be used to restrict dialback for some servers\&. The default value is +\fIall\fR\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_s2s_dialback: + access: + allow: + server: legacy\&.domain\&.tld + server: invalid\-cert\&.example\&.org + deny: all +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_scram_upgrade" +.sp +\fINote\fR about this option: added in 24\&.10\&. +.sp +The module adds support for XEP\-0480: SASL Upgrade Tasks that allows users to upgrade passwords to more secure representation\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBoffered_upgrades\fR: \fIlist(sha256, sha512)\fR +.RS 4 +List with upgrade types that should be offered +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_scram_upgrade: + offered_upgrades: + \- sha256 + \- sha512 +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_service_log" +.sp +This module forwards copies of all stanzas to remote XMPP servers or components\&. Every stanza is encapsulated into element as described in XEP\-0297: Stanza Forwarding\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBloggers\fR: \fI[Domain, \&.\&.\&.]\fR +.RS 4 +A list of servers or connected components to which stanzas will be forwarded\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_service_log: + loggers: + \- xmpp\-server\&.tld + \- component\&.domain\&.tld +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_shared_roster" +.sp +This module enables you to create shared roster groups: groups of accounts that can see members from (other) groups in their rosters\&. +.sp +The big advantages of this feature are that end users do not need to manually add all users to their rosters, and that they cannot permanently delete users from the shared roster groups\&. A shared roster group can have members from any XMPP server, but the presence will only be available from and to members of the same virtual host where the group is created\&. It still allows the users to have / add their own contacts, as it does not replace the standard roster\&. Instead, the shared roster contacts are merged to the relevant users at retrieval time\&. The standard user rosters thus stay unmodified\&. +.sp +Shared roster groups can be edited via the Web Admin, and some API commands called \fIsrg_\fR, for example \fIsrg_add\fR API\&. Each group has a unique name and those parameters: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Label: Used in the rosters where this group is displayed\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Description: of the group, which has no effect\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Members: A list of JIDs of group members, entered one per line in the Web Admin\&. The special member directive +\fI@all@\fR +represents all the registered users in the virtual host; which is only recommended for a small server with just a few hundred users\&. The special member directive +\fI@online@\fR +represents the online users in the virtual host\&. With those two directives, the actual list of members in those shared rosters is generated dynamically at retrieval time\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Displayed: A list of groups that will be in the rosters of this group\(cqs members\&. A group of other vhost can be identified with +\fIgroupid@vhost\fR\&. +.RE +.sp +This module depends on \fImod_roster\fR\&. If not enabled, roster queries will return 503 errors\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBcache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as top\-level +\fIcache_life_time\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_missed\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIcache_missed\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as top\-level +\fIcache_size\fR +option, but applied to this module only\&. +.RE +.PP +\fBdb_type\fR: \fImnesia | sql\fR +.RS 4 +Same as top\-level +\fIdefault_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBuse_cache\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIuse_cache\fR +option, but applied to this module only\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExamples:\fR +.RS 4 +.sp +Take the case of a computer club that wants all its members seeing each other in their rosters\&. To achieve this, they need to create a shared roster group similar to this one: +.sp +.if n \{\ +.RS 4 +.\} +.nf +Name: club_members +Label: Club Members +Description: Members from the computer club +Members: member1@example\&.org, member2@example\&.org, member3@example\&.org +Displayed Groups: club_members +.fi +.if n \{\ +.RE +.\} +.sp +In another case we have a company which has three divisions: Management, Marketing and Sales\&. All group members should see all other members in their rosters\&. Additionally, all managers should have all marketing and sales people in their roster\&. Simultaneously, all marketeers and the whole sales team should see all managers\&. This scenario can be achieved by creating shared roster groups as shown in the following lists: +.sp +.if n \{\ +.RS 4 +.\} +.nf +First list: +Name: management +Label: Management +Description: Management +Members: manager1@example\&.org, manager2@example\&.org +Displayed: management, marketing, sales + +Second list: +Name: marketing +Label: Marketing +Description: Marketing +Members: marketeer1@example\&.org, marketeer2@example\&.org, marketeer3@example\&.org +Displayed: management, marketing + +Third list: +Name: sales +Label: Sales +Description: Sales +Members: salesman1@example\&.org, salesman2@example\&.org, salesman3@example\&.org +Displayed: management, sales +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_shared_roster_ldap" +.sp +This module lets the server administrator automatically populate users\*(Aq rosters (contact lists) with entries based on users and groups defined in an LDAP\-based directory\&. +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBNote\fR +.ps -1 +.br +.sp +\fImod_shared_roster_ldap\fR depends on \fImod_roster\fR being enabled\&. Roster queries will return \fI503\fR errors if \fImod_roster\fR is not enabled\&. +.sp .5v +.RE +.sp +The module accepts many configuration options\&. Some of them, if unspecified, default to the values specified for the top level of configuration\&. This lets you avoid specifying, for example, the bind password in multiple places\&. +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Filters: +\fIldap_rfilter\fR, +\fIldap_ufilter\fR, +\fIldap_gfilter\fR, +\fIldap_filter\fR\&. These options specify LDAP filters used to query for shared roster information\&. All of them are run against the ldap_base\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Attributes: +\fIldap_groupattr\fR, +\fIldap_groupdesc\fR, +\fIldap_memberattr\fR, +\fIldap_userdesc\fR, +\fIldap_useruid\fR\&. These options specify the names of the attributes which hold interesting data in the entries returned by running filters specified with the filter options\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Control parameters: +\fIldap_auth_check\fR, +\fIldap_group_cache_validity\fR, +\fIldap_memberattr_format\fR, +\fIldap_memberattr_format_re\fR, +\fIldap_user_cache_validity\fR\&. These parameters control the behaviour of the module\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Connection parameters: The module also accepts the connection parameters, all of which default to the top\-level parameter of the same name, if unspecified\&. See +\fIldap\&.md#ldap\-connection|LDAP Connection\fR +section for more information about them\&. +.RE +.sp +Check also the \fIldap\&.md#ldap\-examples|Configuration examples\fR section to get details about retrieving the roster, and configuration examples including Flat DIT and Deep DIT\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBcache_life_time\fR +.RS 4 +Same as top\-level +\fIcache_life_time\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_missed\fR +.RS 4 +Same as top\-level +\fIcache_missed\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_size\fR +.RS 4 +Same as top\-level +\fIcache_size\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_auth_check\fR: \fItrue | false\fR +.RS 4 +Whether the module should check (via the ejabberd authentication subsystem) for existence of each user in the shared LDAP roster\&. Set to +\fIfalse\fR +if you want to disable the check\&. Default value is +\fItrue\fR\&. +.RE +.PP +\fBldap_backups\fR +.RS 4 +Same as top\-level +\fIldap_backups\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_base\fR +.RS 4 +Same as top\-level +\fIldap_base\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_deref_aliases\fR +.RS 4 +Same as top\-level +\fIldap_deref_aliases\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_encrypt\fR +.RS 4 +Same as top\-level +\fIldap_encrypt\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_filter\fR +.RS 4 +Additional filter which is AND\-ed together with "User Filter" and "Group Filter"\&. For more information check the LDAP +\fIldap\&.md#filters|Filters\fR +section\&. +.RE +.PP +\fBldap_gfilter\fR +.RS 4 +"Group Filter", used when retrieving human\-readable name (a\&.k\&.a\&. "Display Name") and the members of a group\&. See also the parameters +\fIldap_groupattr\fR, +\fIldap_groupdesc\fR +and +\fIldap_memberattr\fR\&. If unspecified, defaults to the top\-level parameter of the same name\&. If that one also is unspecified, then the filter is constructed exactly like "User Filter"\&. +.RE +.PP +\fBldap_groupattr\fR +.RS 4 +The name of the attribute that holds the group name, and that is used to differentiate between them\&. Retrieved from results of the "Roster Filter" and "Group Filter"\&. Defaults to +\fIcn\fR\&. +.RE +.PP +\fBldap_groupdesc\fR +.RS 4 +The name of the attribute which holds the human\-readable group name in the objects you use to represent groups\&. Retrieved from results of the "Group Filter"\&. Defaults to whatever +\fIldap_groupattr\fR +is set\&. +.RE +.PP +\fBldap_memberattr\fR +.RS 4 +The name of the attribute which holds the IDs of the members of a group\&. Retrieved from results of the "Group Filter"\&. Defaults to +\fImemberUid\fR\&. The name of the attribute differs depending on the objectClass you use for your group objects, for example: +\fIposixGroup\fR +→ +\fImemberUid\fR; +\fIgroupOfNames\fR +→ +\fImember\fR; +\fIgroupOfUniqueNames\fR +→ +\fIuniqueMember\fR\&. +.RE +.PP +\fBldap_memberattr_format\fR +.RS 4 +A globbing format for extracting user ID from the value of the attribute named by +\fIldap_memberattr\fR\&. Defaults to +\fI%u\fR, which means that the whole value is the member ID\&. If you change it to something different, you may also need to specify the User and Group Filters manually; see section Filters\&. +.RE +.PP +\fBldap_memberattr_format_re\fR +.RS 4 +A regex for extracting user ID from the value of the attribute named by +\fIldap_memberattr\fR\&. Check the LDAP +\fIldap\&.md#control\-parameters|Control Parameters\fR +section\&. +.RE +.PP +\fBldap_password\fR +.RS 4 +Same as top\-level +\fIldap_password\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_port\fR +.RS 4 +Same as top\-level +\fIldap_port\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_rfilter\fR +.RS 4 +So called "Roster Filter"\&. Used to find names of all "shared roster" groups\&. See also the +\fIldap_groupattr\fR +parameter\&. If unspecified, defaults to the top\-level parameter of the same name\&. You must specify it in some place in the configuration, there is no default\&. +.RE +.PP +\fBldap_rootdn\fR +.RS 4 +Same as top\-level +\fIldap_rootdn\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_servers\fR +.RS 4 +Same as top\-level +\fIldap_servers\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_tls_cacertfile\fR +.RS 4 +Same as top\-level +\fIldap_tls_cacertfile\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_tls_certfile\fR +.RS 4 +Same as top\-level +\fIldap_tls_certfile\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_tls_depth\fR +.RS 4 +Same as top\-level +\fIldap_tls_depth\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_tls_verify\fR +.RS 4 +Same as top\-level +\fIldap_tls_verify\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_ufilter\fR +.RS 4 +"User Filter", used for retrieving the human\-readable name of roster entries (usually full names of people in the roster)\&. See also the parameters +\fIldap_userdesc\fR +and +\fIldap_useruid\fR\&. For more information check the LDAP +\fIldap\&.md#filters|Filters\fR +section\&. +.RE +.PP +\fBldap_uids\fR +.RS 4 +Same as top\-level +\fIldap_uids\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_userdesc\fR +.RS 4 +The name of the attribute which holds the human\-readable user name\&. Retrieved from results of the "User Filter"\&. Defaults to +\fIcn\fR\&. +.RE +.PP +\fBldap_userjidattr\fR +.RS 4 +The name of the attribute which is used to map user id to XMPP jid\&. If not specified (and that is default value of this option), user jid will be created from user id and this module host\&. +.RE +.PP +\fBldap_useruid\fR +.RS 4 +The name of the attribute which holds the ID of a roster item\&. Value of this attribute in the roster item objects needs to match the ID retrieved from the +\fIldap_memberattr\fR +attribute of a group object\&. Retrieved from results of the "User Filter"\&. Defaults to +\fIcn\fR\&. +.RE +.PP +\fBuse_cache\fR +.RS 4 +Same as top\-level +\fIuse_cache\fR +option, but applied to this module only\&. +.RE +.RE +.SS "mod_sic" +.sp +This module adds support for XEP\-0279: Server IP Check\&. This protocol enables a client to discover its external IP address\&. +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBWarning\fR +.ps -1 +.br +.sp +The protocol extension is deferred and seems like there are no clients supporting it, so using this module is not recommended and, furthermore, the module might be removed in the future\&. +.sp .5v +.RE +.sp +The module has no options\&. +.SS "mod_sip" +.sp +This module adds SIP proxy/registrar support for the corresponding virtual host\&. +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBNote\fR +.ps -1 +.br +.sp +It is not enough to just load this module\&. You should also configure listeners and DNS records properly\&. For details see the section about the \fIlisten\&.md#ejabberd_sip|ejabberd_sip\fR listen module in the ejabberd Documentation\&. +.sp .5v +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBalways_record_route\fR: \fItrue | false\fR +.RS 4 +Always insert "Record\-Route" header into SIP messages\&. With this approach it is possible to bypass NATs/firewalls a bit more easily\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBflow_timeout_tcp\fR: \fItimeout()\fR +.RS 4 +The option sets a keep\-alive timer for +SIP outbound +TCP connections\&. The default value is +\fI2\fR +minutes\&. +.RE +.PP +\fBflow_timeout_udp\fR: \fItimeout()\fR +.RS 4 +The options sets a keep\-alive timer for +SIP outbound +UDP connections\&. The default value is +\fI29\fR +seconds\&. +.RE +.PP +\fBrecord_route\fR: \fIURI\fR +.RS 4 +When the option +\fIalways_record_route\fR +is set to +\fItrue\fR +or when +SIP outbound +is utilized, ejabberd inserts "Record\-Route" header field with this +\fIURI\fR +into a SIP message\&. The default is a SIP URI constructed from the virtual host on which the module is loaded\&. +.RE +.PP +\fBroutes\fR: \fI[URI, \&.\&.\&.]\fR +.RS 4 +You can set a list of SIP URIs of routes pointing to this SIP proxy server\&. The default is a list containing a single SIP URI constructed from the virtual host on which the module is loaded\&. +.RE +.PP +\fBvia\fR: \fI[URI, \&.\&.\&.]\fR +.RS 4 +A list to construct "Via" headers for inserting them into outgoing SIP messages\&. This is useful if you\(cqre running your SIP proxy in a non\-standard network topology\&. Every +\fIURI\fR +element in the list must be in the form of "scheme://host:port", where "transport" must be +\fItls\fR, +\fItcp\fR, or +\fIudp\fR, "host" must be a domain name or an IP address and "port" must be an internet port number\&. Note that all parts of the +\fIURI\fR +are mandatory (e\&.g\&. you cannot omit "port" or "scheme")\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_sip: + always_record_route: false + record_route: "sip:example\&.com;lr" + routes: + \- "sip:example\&.com;lr" + \- "sip:sip\&.example\&.com;lr" + flow_timeout_udp: 30 sec + flow_timeout_tcp: 1 min + via: + \- tls://sip\-tls\&.example\&.com:5061 + \- tcp://sip\-tcp\&.example\&.com:5060 + \- udp://sip\-udp\&.example\&.com:5060 +.fi +.if n \{\ +.RE +.\} +.RE +.SS "mod_stats" +.sp +This module adds support for XEP\-0039: Statistics Gathering\&. This protocol allows you to retrieve the following statistics from your ejabberd server: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Total number of registered users on the current virtual host (users/total)\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Total number of registered users on all virtual hosts (users/all\-hosts/total)\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Total number of online users on the current virtual host (users/online)\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Total number of online users on all virtual hosts (users/all\-hosts/online)\&. +.RE +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBNote\fR +.ps -1 +.br +.sp +The protocol extension is deferred and seems like even a few clients that were supporting it are now abandoned\&. So using this module makes very little sense\&. +.sp .5v +.RE +.sp +The module has no options\&. +.SS "mod_stream_mgmt" +.sp +This module adds support for XEP\-0198: Stream Management\&. This protocol allows active management of an XML stream between two XMPP entities, including features for stanza acknowledgments and stream resumption\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBack_timeout\fR: \fItimeout()\fR +.RS 4 +A time to wait for stanza acknowledgments\&. Setting it to +\fIinfinity\fR +effectively disables the timeout\&. The default value is +\fI1\fR +minute\&. +.RE +.PP +\fBcache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as top\-level +\fIcache_life_time\fR +option, but applied to this module only\&. The default value is +\fI48 hours\fR\&. +.RE +.PP +\fBcache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as top\-level +\fIcache_size\fR +option, but applied to this module only\&. +.RE +.PP +\fBmax_ack_queue\fR: \fISize\fR +.RS 4 +This option specifies the maximum number of unacknowledged stanzas queued for possible retransmission\&. When the limit is exceeded, the client session is terminated\&. The allowed values are positive integers and +\fIinfinity\fR\&. You should be careful when setting this value as it should not be set too low, otherwise, you could kill sessions in a loop, before they get the chance to finish proper session initiation\&. It should definitely be set higher that the size of the offline queue (for example at least 3 times the value of the max offline queue and never lower than +\fI1000\fR)\&. The default value is +\fI5000\fR\&. +.RE +.PP +\fBmax_resume_timeout\fR: \fItimeout()\fR +.RS 4 +A client may specify the period of time until a session times out if the connection is lost\&. During this period of time, the client may resume its session\&. This option limits the period of time a client is permitted to request\&. It must be set to a timeout equal to or larger than the default +\fIresume_timeout\fR\&. By default, it is set to the same value as the +\fIresume_timeout\fR +option\&. +.RE +.PP +\fBqueue_type\fR: \fIram | file\fR +.RS 4 +Same as top\-level +\fIqueue_type\fR +option, but applied to this module only\&. +.RE +.PP +\fBresend_on_timeout\fR: \fItrue | false | if_offline\fR +.RS 4 +If this option is set to +\fItrue\fR, any message stanzas that weren\(cqt acknowledged by the client will be resent on session timeout\&. This behavior might often be desired, but could have unexpected results under certain circumstances\&. For example, a message that was sent to two resources might get resent to one of them if the other one timed out\&. Therefore, the default value for this option is +\fIfalse\fR, which tells ejabberd to generate an error message instead\&. As an alternative, the option may be set to +\fIif_offline\fR\&. In this case, unacknowledged messages are resent only if no other resource is online when the session times out\&. Otherwise, error messages are generated\&. +.RE +.PP +\fBresume_timeout\fR: \fItimeout()\fR +.RS 4 +This option configures the (default) period of time until a session times out if the connection is lost\&. During this period of time, a client may resume its session\&. Note that the client may request a different timeout value, see the +\fImax_resume_timeout\fR +option\&. Setting it to +\fI0\fR +effectively disables session resumption\&. The default value is +\fI5\fR +minutes\&. +.RE +.RE +.SS "mod_stun_disco" +.sp +\fINote\fR about this option: added in 20\&.04\&. +.sp +This module allows XMPP clients to discover STUN/TURN services and to obtain temporary credentials for using them as per XEP\-0215: External Service Discovery\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess\fR: \fIAccessName\fR +.RS 4 +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 +\fIlocal\fR\&. +.RE +.PP +\fBcredentials_lifetime\fR: \fItimeout()\fR +.RS 4 +The lifetime of temporary credentials offered to clients\&. If ejabberd\(cqs built\-in TURN service is used, TURN relays allocated using temporary credentials will be terminated shortly after the credentials expired\&. The default value is +\fI12 hours\fR\&. Note that restarting the ejabberd node invalidates any temporary credentials offered before the restart unless a +\fIsecret\fR +is specified (see below)\&. +.RE +.PP +\fBoffer_local_services\fR: \fItrue | false\fR +.RS 4 +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 +\fIservices\fR +option (see below)\&. For non\-anonymous TURN services, temporary credentials will be offered to the client\&. The default value is +\fItrue\fR\&. +.RE +.PP +\fBsecret\fR: \fIText\fR +.RS 4 +The secret used for generating temporary credentials\&. If this option isn\(cqt specified, a secret will be auto\-generated\&. However, a secret must be specified explicitly if non\-anonymous TURN services running on other ejabberd nodes and/or external TURN +\fIservices\fR +are configured\&. Also note that auto\-generated secrets are lost when the node is restarted, which invalidates any credentials offered before the restart\&. Therefore, it\(cqs recommended to explicitly specify a secret if clients cache retrieved credentials (for later use) across service restarts\&. +.RE +.PP +\fBservices\fR: \fI[Service, \&.\&.\&.]\fR +.RS 4 +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 +\fIsecret\fR +must be specified explicitly, and must be shared with that service\&. This will only work with ejabberd\(cqs built\-in STUN/TURN server and with external servers that support the same +REST API For Access To TURN Services\&. Unless the +\fIoffer_local_services\fR +is set to +\fIfalse\fR, the explicitly listed services will be offered in addition to those announced automatically\&. +.PP +\fBhost\fR: \fIHost\fR +.RS 4 +The hostname or IP address the STUN/TURN service is listening on\&. For non\-TLS services, it\(cqs recommended to specify an IP address (to avoid additional DNS lookup latency on the client side)\&. For TLS services, the hostname (or IP address) should match the certificate\&. Specifying the +\fIhost\fR +option is mandatory\&. +.RE +.PP +\fBport\fR: \fI1\&.\&.65535\fR +.RS 4 +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\&. +.RE +.PP +\fBrestricted\fR: \fItrue | false\fR +.RS 4 +This option determines whether temporary credentials for accessing the service are offered\&. The default is +\fIfalse\fR +for STUN/STUNS services and +\fItrue\fR +for TURN/TURNS services\&. +.RE +.PP +\fBtransport\fR: \fItcp | udp\fR +.RS 4 +The transport protocol supported by the service\&. The default is +\fIudp\fR +for non\-TLS services and +\fItcp\fR +for TLS services\&. +.RE +.PP +\fBtype\fR: \fIstun | turn | stuns | turns\fR +.RS 4 +The type of service\&. Must be +\fIstun\fR +or +\fIturn\fR +for non\-TLS services, +\fIstuns\fR +or +\fIturns\fR +for TLS services\&. The default type is +\fIstun\fR\&. +.RE +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +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: 2001:db8::3 + port: 3478 + type: stun + transport: udp + restricted: false + \- + host: 2001:db8::3 + port: 3478 + type: turn + transport: udp + restricted: true + \- + host: server\&.example\&.com + port: 5349 + type: turns + transport: tcp + restricted: true +.fi +.if n \{\ +.RE +.\} +.RE +.RE +.SS "mod_time" +.sp +This module adds support for XEP\-0202: Entity Time\&. In other words, the module reports server\(cqs system time\&. +.sp +The module has no options\&. +.SS "mod_vcard" +.sp +This module allows end users to store and retrieve their vCard, and to retrieve other users vCards, as defined in XEP\-0054: vcard\-temp\&. The module also implements an uncomplicated Jabber User Directory based on the vCards of these users\&. Moreover, it enables the server to send its vCard when queried\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBallow_return_all\fR: \fItrue | false\fR +.RS 4 +This option enables you to specify if search operations with empty input fields should return all users who added some information to their vCard\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBcache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as top\-level +\fIcache_life_time\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_missed\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIcache_missed\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as top\-level +\fIcache_size\fR +option, but applied to this module only\&. +.RE +.PP +\fBdb_type\fR: \fImnesia | sql | ldap\fR +.RS 4 +Same as top\-level +\fIdefault_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBhost\fR +.RS 4 +Deprecated\&. Use +\fIhosts\fR +instead\&. +.RE +.PP +\fBhosts\fR: \fI[Host, \&.\&.\&.]\fR +.RS 4 +This option defines the Jabber IDs of the service\&. If the +\fIhosts\fR +option is not specified, the only Jabber ID will be the hostname of the virtual host with the prefix "vjud\&."\&. The keyword +\fI@HOST@\fR +is replaced with the real virtual host name\&. +.RE +.PP +\fBmatches\fR: \fIpos_integer() | infinity\fR +.RS 4 +With this option, the number of reported search results can be limited\&. If the option\(cqs value is set to +\fIinfinity\fR, all search results are reported\&. The default value is +\fI30\fR\&. +.RE +.PP +\fBname\fR: \fIName\fR +.RS 4 +The value of the service name\&. This name is only visible in some clients that support +XEP\-0030: Service Discovery\&. The default is +\fIvCard User Search\fR\&. +.RE +.PP +\fBsearch\fR: \fItrue | false\fR +.RS 4 +This option specifies whether the search functionality is enabled or not\&. If disabled, the options +\fIhosts\fR, +\fIname\fR +and +\fIvcard\fR +will be ignored and the Jabber User Directory service will not appear in the Service Discovery item list\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBuse_cache\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIuse_cache\fR +option, but applied to this module only\&. +.RE +.PP +\fBvcard\fR: \fIvCard\fR +.RS 4 +A custom vCard of the server that will be displayed by some XMPP clients in Service Discovery\&. The value of +\fIvCard\fR +is a YAML map constructed from an XML representation of vCard\&. Since the representation has no attributes, the mapping is straightforward\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# This XML representation of vCard: +# +# +# Conferences +# +# +# Elm Street +# +# +# +# is translated to: +# +vcard: + fn: Conferences + adr: + \- + work: true + street: Elm Street +.fi +.if n \{\ +.RE +.\} +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options for ldap backend:\fR +.RS 4 +.PP +\fBldap_backups\fR +.RS 4 +Same as top\-level +\fIldap_backups\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_base\fR +.RS 4 +Same as top\-level +\fIldap_base\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_deref_aliases\fR +.RS 4 +Same as top\-level +\fIldap_deref_aliases\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_encrypt\fR +.RS 4 +Same as top\-level +\fIldap_encrypt\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_filter\fR +.RS 4 +Same as top\-level +\fIldap_filter\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_password\fR +.RS 4 +Same as top\-level +\fIldap_password\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_port\fR +.RS 4 +Same as top\-level +\fIldap_port\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_rootdn\fR +.RS 4 +Same as top\-level +\fIldap_rootdn\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_search_fields\fR: \fI{Name: Attribute, \&.\&.\&.}\fR +.RS 4 +This option defines the search form and the LDAP attributes to search within\&. +\fIName\fR +is the name of a search form field which will be automatically translated by using the translation files (see +\fImsgs/*\&.msg\fR +for available words)\&. +\fIAttribute\fR +is the LDAP attribute or the pattern +\fI%u\fR\&. +.sp +\fBExamples\fR: +.sp +The default is: +.sp +.if n \{\ +.RS 4 +.\} +.nf +User: "%u" +"Full Name": displayName +"Given Name": givenName +"Middle Name": initials +"Family Name": sn +Nickname: "%u" +Birthday: birthDay +Country: c +City: l +Email: mail +"Organization Name": o +"Organization Unit": ou +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBldap_search_reported\fR: \fI{SearchField: VcardField}, \&.\&.\&.}\fR +.RS 4 +This option defines which search fields should be reported\&. +\fISearchField\fR +is the name of a search form field which will be automatically translated by using the translation files (see +\fImsgs/*\&.msg\fR +for available words)\&. +\fIVcardField\fR +is the vCard field name defined in the +\fIldap_vcard_map\fR +option\&. +.sp +\fBExamples\fR: +.sp +The default is: +.sp +.if n \{\ +.RS 4 +.\} +.nf +"Full Name": FN +"Given Name": FIRST +"Middle Name": MIDDLE +"Family Name": LAST +"Nickname": NICKNAME +"Birthday": BDAY +"Country": CTRY +"City": LOCALITY +"Email": EMAIL +"Organization Name": ORGNAME +"Organization Unit": ORGUNIT +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBldap_servers\fR +.RS 4 +Same as top\-level +\fIldap_servers\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_tls_cacertfile\fR +.RS 4 +Same as top\-level +\fIldap_tls_cacertfile\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_tls_certfile\fR +.RS 4 +Same as top\-level +\fIldap_tls_certfile\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_tls_depth\fR +.RS 4 +Same as top\-level +\fIldap_tls_depth\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_tls_verify\fR +.RS 4 +Same as top\-level +\fIldap_tls_verify\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_uids\fR +.RS 4 +Same as top\-level +\fIldap_uids\fR +option, but applied to this module only\&. +.RE +.PP +\fBldap_vcard_map\fR: \fI{Name: {Pattern, LDAPattributes}, \&.\&.\&.}\fR +.RS 4 +With this option you can set the table that maps LDAP attributes to vCard fields\&. +\fIName\fR +is the type name of the vCard as defined in +RFC 2426\&. +\fIPattern\fR +is a string which contains pattern variables +\fI%u\fR, +\fI%d\fR +or +\fI%s\fR\&. +\fILDAPattributes\fR +is the list containing LDAP attributes\&. The pattern variables +\fI%s\fR +will be sequentially replaced with the values of LDAP attributes from +\fIList_of_LDAP_attributes\fR, +\fI%u\fR +will be replaced with the user part of a JID, and +\fI%d\fR +will be replaced with the domain part of a JID\&. +.sp +\fBExamples\fR: +.sp +The default is: +.sp +.if n \{\ +.RS 4 +.\} +.nf +NICKNAME: {"%u": []} +FN: {"%s": [displayName]} +LAST: {"%s": [sn]} +FIRST: {"%s": [givenName]} +MIDDLE: {"%s": [initials]} +ORGNAME: {"%s": [o]} +ORGUNIT: {"%s": [ou]} +CTRY: {"%s": [c]} +LOCALITY: {"%s": [l]} +STREET: {"%s": [street]} +REGION: {"%s": [st]} +PCODE: {"%s": [postalCode]} +TITLE: {"%s": [title]} +URL: {"%s": [labeleduri]} +DESC: {"%s": [description]} +TEL: {"%s": [telephoneNumber]} +EMAIL: {"%s": [mail]} +BDAY: {"%s": [birthDay]} +ROLE: {"%s": [employeeType]} +PHOTO: {"%s": [jpegPhoto]} +.fi +.if n \{\ +.RE +.\} +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options for mnesia backend:\fR +.RS 4 +.PP +\fBsearch_all_hosts\fR: \fItrue | false\fR +.RS 4 +Whether to perform search on all virtual hosts or not\&. The default value is +\fItrue\fR\&. +.RE +.RE +.SS "mod_vcard_xupdate" +.sp +The user\(cqs client can store an avatar in the user vCard\&. The vCard\-Based Avatars protocol (XEP\-0153) provides a method for clients to inform the contacts what is the avatar hash value\&. However, simple or small clients may not implement that protocol\&. +.sp +If this module is enabled, all the outgoing client presence stanzas get automatically the avatar hash on behalf of the client\&. So, the contacts receive the presence stanzas with the \fIUpdate Data\fR described in XEP\-0153 as if the client would had inserted it itself\&. If the client had already included such element in the presence stanza, it is replaced with the element generated by ejabberd\&. +.sp +By enabling this module, each vCard modification produces a hash recalculation, and each presence sent by a client produces hash retrieval and a presence stanza rewrite\&. For this reason, enabling this module will introduce a computational overhead in servers with clients that change frequently their presence\&. However, the overhead is significantly reduced by the use of caching, so you probably don\(cqt want to set \fIuse_cache\fR to \fIfalse\fR\&. +.sp +The module depends on \fImod_vcard\fR\&. +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBNote\fR +.ps -1 +.br +.sp +Nowadays XEP\-0153 is used mostly as "read\-only", i\&.e\&. modern clients don\(cqt publish their avatars inside vCards\&. Thus in the majority of cases the module is only used along with \fImod_avatar\fR for providing backward compatibility\&. +.sp .5v +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBcache_life_time\fR: \fItimeout()\fR +.RS 4 +Same as top\-level +\fIcache_life_time\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_missed\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIcache_missed\fR +option, but applied to this module only\&. +.RE +.PP +\fBcache_size\fR: \fIpos_integer() | infinity\fR +.RS 4 +Same as top\-level +\fIcache_size\fR +option, but applied to this module only\&. +.RE +.PP +\fBuse_cache\fR: \fItrue | false\fR +.RS 4 +Same as top\-level +\fIuse_cache\fR +option, but applied to this module only\&. +.RE +.RE +.SS "mod_version" +.sp +This module implements XEP\-0092: Software Version\&. Consequently, it answers ejabberd\(cqs version when queried\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBshow_os\fR: \fItrue | false\fR +.RS 4 +Should the operating system be revealed or not\&. The default value is +\fItrue\fR\&. +.RE +.RE +.SH "LISTENERS" +.sp +This section describes listeners options of ejabberd 25\&.08\&. +.sp +TODO +.SH "AUTHOR" +.sp +ProcessOne\&. +.SH "VERSION" +.sp +This document describes the configuration file of ejabberd 25\&.08\&. Configuration options of other ejabberd versions may differ significantly\&. +.SH "REPORTING BUGS" +.sp +Report bugs to https://github\&.com/processone/ejabberd/issues +.SH "SEE ALSO" +.sp +Default configuration file: https://github\&.com/processone/ejabberd/blob/25\&.08/ejabberd\&.yml\&.example +.sp +Main site: https://ejabberd\&.im +.sp +Documentation: https://docs\&.ejabberd\&.im +.sp +Configuration Guide: https://docs\&.ejabberd\&.im/admin/configuration +.sp +Source code: https://github\&.com/processone/ejabberd +.SH "COPYING" +.sp +Copyright (c) 2002\-2025 ProcessOne\&. diff --git a/mix.exs b/mix.exs index b9ff8794e..fcb3ac39e 100644 --- a/mix.exs +++ b/mix.exs @@ -1,114 +1,402 @@ -defmodule Ejabberd.Mixfile do +defmodule Ejabberd.MixProject do use Mix.Project def project do [app: :ejabberd, - version: "16.11.0", - description: description, - elixir: "~> 1.2", + source_url: "https://github.com/processone/ejabberd", + version: version(), + description: description(), + elixir: elixir_required_version(), elixirc_paths: ["lib"], compile_path: ".", - compilers: [:asn1] ++ Mix.compilers, - erlc_options: erlc_options, + compilers: [:asn1, :yecc] ++ Mix.compilers(), + erlc_options: erlc_options(), erlc_paths: ["asn1", "src"], # Elixir tests are starting the part of ejabberd they need aliases: [test: "test --no-start"], - package: package, - deps: deps] + start_permanent: Mix.env() == :prod, + language: :erlang, + dialyzer: dialyzer(), + releases: releases(), + package: package(), + docs: docs(), + deps: deps()] + end + + def version do + case config(:vsn) do + :false -> "0.0.0" # ./configure wasn't run: vars.config not created + ~c"0.0" -> "0.0.0" # the full git repository wasn't downloaded + ~c"latest.0" -> "0.0.0" # running 'docker-ejabberd/ecs/build.sh latest' + [_, _, ?., _, _] = x -> + head = String.replace(:erlang.list_to_binary(x), ~r/\.0+([0-9])/, ".\\1") + "#{head}.0" + vsn -> String.replace(:erlang.list_to_binary(vsn), ~r/\.0+([0-9])/, ".\\1") + end end def description do """ - Robust, ubiquitous and massively scalable Jabber / XMPP Instant Messaging platform. + Robust, Ubiquitous and Massively Scalable Messaging Platform (XMPP, MQTT, SIP Server) """ end def application do [mod: {:ejabberd_app, []}, - applications: [:ssl], - included_applications: [:lager, :mnesia, :p1_utils, :cache_tab, - :fast_tls, :stringprep, :fast_xml, :xmpp, - :stun, :fast_yaml, :esip, :jiffy, :p1_oauth2] - ++ cond_apps] + extra_applications: [:inets, :kernel, :sasl, :ssl, :stdlib, :syntax_tools, + :logger, :mix] + ++ cond_apps(), + included_applications: [:mnesia, :os_mon, + :cache_tab, :eimp, :mqtree, :p1_acme, + :p1_oauth2, :pkix] + ++ cond_included_apps()] + end + + defp dialyzer do + [ + plt_add_apps: [ + :mnesia, :odbc, :os_mon, :stdlib, + :eredis, :luerl, + :cache_tab, :eimp, :epam, :esip, :ezlib, :mqtree, + :p1_acme, :p1_mysql, :p1_oauth2, :p1_pgsql, :pkix, + :sqlite3, :stun, :xmpp], + ] + end + + defp if_version_above(ver, okResult) do + if :erlang.system_info(:otp_release) > ver do + okResult + else + [] + end + end + + defp if_version_below(ver, okResult) do + if :erlang.system_info(:otp_release) < ver do + okResult + else + [] + end end defp erlc_options do # Use our own includes + includes from all dependencies - includes = ["include"] ++ Path.wildcard("deps/*/include") - [:debug_info, {:d, :ELIXIR_ENABLED}] ++ Enum.map(includes, fn(path) -> {:i, path} end) + includes = ["include", deps_include()] + result = [{:d, :ELIXIR_ENABLED}] ++ + cond_options() ++ + Enum.map(includes, fn (path) -> {:i, path} end) ++ + if_version_above(~c"20", [{:d, :HAVE_URI_STRING}]) ++ + if_version_above(~c"20", [{:d, :HAVE_ERL_ERROR}]) ++ + if_version_below(~c"21", [{:d, :USE_OLD_HTTP_URI}]) ++ + if_version_below(~c"22", [{:d, :LAGER}]) ++ + if_version_below(~c"21", [{:d, :NO_CUSTOMIZE_HOSTNAME_CHECK}]) ++ + if_version_below(~c"23", [{:d, :USE_OLD_CRYPTO_HMAC}]) ++ + if_version_below(~c"23", [{:d, :USE_OLD_PG2}]) ++ + if_version_below(~c"24", [{:d, :COMPILER_REPORTS_ONLY_LINES}]) ++ + if_version_below(~c"24", [{:d, :SYSTOOLS_APP_DEF_WITHOUT_OPTIONAL}]) ++ + if_version_below(~c"24", [{:d, :OTP_BELOW_24}]) ++ + if_version_below(~c"25", [{:d, :OTP_BELOW_25}]) ++ + if_version_below(~c"26", [{:d, :OTP_BELOW_26}]) ++ + if_version_below(~c"27", [{:d, :OTP_BELOW_27}]) ++ + if_version_below(~c"28", [{:d, :OTP_BELOW_28}]) + defines = for {:d, value} <- result, do: {:d, value} + result ++ [{:d, :ALL_DEFS, defines}] + end + + defp cond_options do + for {:true, option} <- [{config(:sip), {:d, :SIP}}, + {config(:stun), {:d, :STUN}}, + {config(:debug), :debug_info}, + {not config(:debug), {:debug_info, false}}, + {config(:roster_gateway_workaround), {:d, :ROSTER_GATEWAY_WORKAROUND}}, + {config(:new_sql_schema), {:d, :NEW_SQL_SCHEMA}} + ], do: + option end defp deps do - [{:lager, "~> 3.2"}, - {:p1_utils, "~> 1.0"}, - {:cache_tab, "~> 1.0"}, - {:stringprep, "~> 1.0", override: true}, # override cause of :xmpp + [{:cache_tab, "~> 1.0"}, + {:dialyxir, "~> 1.2", only: [:test], runtime: false}, + {:eimp, "~> 1.0"}, + {:ex_doc, "~> 0.31", only: [:edoc], runtime: false}, + {:fast_tls, "~> 1.1.24"}, + {:fast_xml, "~> 1.1.56"}, {:fast_yaml, "~> 1.0"}, - {:fast_tls, "~> 1.0"}, - {:fast_xml, "~> 1.1", override: true}, # override cause of :xmpp - {:xmpp, github: "processone/xmpp", tag: "1.1.1"}, - {:stun, "~> 1.0"}, - {:esip, "~> 1.0"}, - {:jiffy, "~> 0.14.7"}, - {:p1_oauth2, "~> 0.6.1"}, - {:exrm, "~> 1.0.0", only: :dev}, - # relx is used by exrm. Lock version as for now, ejabberd doesn not compile fine with - # version 3.20: - {:relx, "~> 3.21", only: :dev}, - {:ex_doc, ">= 0.0.0", only: :dev}] - ++ cond_deps + {:idna, "~> 6.0"}, + {:mqtree, "~> 1.0"}, + {:p1_acme, ">= 1.0.28"}, + {:p1_oauth2, "~> 0.6"}, + {:p1_utils, "~> 1.0"}, + {:pkix, "~> 1.0"}, + {:stringprep, ">= 1.0.26"}, + {:xmpp, git: "https://github.com/processone/xmpp", ref: "e9d901ea84fd3910ad32b715853397eb1155b41c", override: true}, + {:yconf, git: "https://github.com/processone/yconf", ref: "95692795a8a8d950ba560e5b07e6b80660557259", override: true}] + ++ cond_deps() + end + + defp deps_include() do + if Mix.Project.umbrella?() do + "../../deps" + else + case Mix.Project.deps_paths()[:ejabberd] do + nil -> "deps" + _ -> ".." + end + end end defp cond_deps do - for {:true, dep} <- [{config(:mysql), {:p1_mysql, "~> 1.0"}}, - {config(:pgsql), {:p1_pgsql, "~> 1.1"}}, - {config(:sqlite), {:sqlite3, "~> 1.1"}}, - {config(:riak), {:riakc, "~> 2.4"}}, - {config(:redis), {:eredis, "~> 1.0"}}, + for {:true, dep} <- [{config(:pam), {:epam, "~> 1.0"}}, + {Mix.env() == :translations, + {:ejabberd_po, git: "https://github.com/processone/ejabberd-po.git"}}, + {Mix.env() == :dev, + {:exsync, "~> 0.2", optional: true, runtime: false}}, + {config(:redis), {:eredis, "~> 1.7.1"}}, + {config(:sip), {:esip, "~> 1.0"}}, {config(:zlib), {:ezlib, "~> 1.0"}}, - {config(:iconv), {:iconv, "~> 1.0"}}, - {config(:pam), {:p1_pam, "~> 1.0"}}, - {config(:tools), {:luerl, github: "rvirding/luerl", tag: "v0.2"}}, - {config(:tools), {:meck, "~> 0.8.4"}}, - {config(:tools), {:moka, github: "processone/moka", tag: "1.0.5c"}}], do: + {if_version_above(~c"23", true), {:jose, "~> 1.11.10"}}, + {if_version_below(~c"24", true), {:jose, "1.11.1", override: true}}, + {if_version_below(~c"27", true), {:jiffy, "~> 1.1.1"}}, + {if_version_below(~c"22", true), {:lager, "~> 3.9.1"}}, + {config(:lua), {:luerl, "~> 1.2.0"}}, + {config(:mysql), {:p1_mysql, ">= 1.0.24"}}, + {config(:pgsql), {:p1_pgsql, ">= 1.1.32"}}, + {config(:sqlite), {:sqlite3, "~> 1.1"}}, + {config(:stun), {:stun, "~> 1.0"}}], do: dep end defp cond_apps do - for {:true, app} <- [{config(:redis), :eredis}, - {config(:mysql), :p1_mysql}, - {config(:pgsql), :p1_pgsql}, - {config(:sqlite), :sqlite3}, - {config(:zlib), :ezlib}, - {config(:iconv), :iconv}], do: + for {:true, app} <- [{config(:stun), :stun}, + {if_version_below(~c"27", true), :jiffy}, + {config(:tools), :debugger}, + {config(:tools), :observer}, + {config(:tools), :wx}], do: app end - def package do - [# These are the default files included in the package - files: ["lib", "src", "priv", "mix.exs", "include", "README.md", "COPYING"], - maintainers: ["ProcessOne"], - licenses: ["GPLv2"], - links: %{"Site" => "https://www.ejabberd.im", - "Documentation" => "http://docs.ejabberd.im", - "Source" => "https://github.com/processone/ejabberd", - "ProcessOne" => "http://www.process-one.net/"}] + defp cond_included_apps do + for {:true, app} <- [{config(:pam), :epam}, + {config(:lua), :luerl}, + {config(:redis), :eredis}, + {Mix.env() == :edoc, :ex_doc}, + {Mix.env() == :test, :dialyxir}, + {if_version_below(~c"22", true), :lager}, + {config(:mysql), :p1_mysql}, + {config(:sip), :esip}, + {config(:odbc), :odbc}, + {config(:pgsql), :p1_pgsql}, + {config(:sqlite), :sqlite3}], do: + app end - def vars do - case :file.consult("vars.config") do + defp package do + [# These are the default files included in the package + files: ["include", "lib", "priv", "sql", "src", + "COPYING", "README.md", + "ejabberd.yml.example", "config/runtime.exs", + "mix.exs", "rebar.config", "rebar.config.script", "vars.config"], + maintainers: ["ProcessOne"], + licenses: ["GPL-2.0-or-later"], + links: %{"ejabberd.im" => "https://www.ejabberd.im", + "ejabberd Docs" => "https://docs.ejabberd.im", + "GitHub" => "https://github.com/processone/ejabberd", + "ProcessOne" => "https://www.process-one.net/"}] + end + + defp vars do + filepath = case Application.fetch_env(:ejabberd, :vars_config_path) do + :error -> + "vars.config" + {:ok, path} -> + path + end + config2 = case :file.consult(filepath) do {:ok,config} -> config - _ -> [zlib: true, iconv: true] + _ -> [stun: true, zlib: true] + end + case Mix.env() do + :dev -> List.keystore(config2, :tools, 0, {:tools, true}) + _ -> config2 end end defp config(key) do - case vars[key] do + case vars()[key] do nil -> false value -> value end end + defp elixir_required_version do + case {Map.get(System.get_env(), "RELIVE", "false"), + MapSet.member?(MapSet.new(System.argv()), "release")} + do + {"true", _} -> + case Version.match?(System.version(), "~> 1.11") do + false -> + IO.puts("ERROR: To use 'make relive', Elixir 1.11.0 or higher is required.") + _ -> :ok + end + "~> 1.11" + {_, true} -> + case Version.match?(System.version(), "~> 1.10") do + false -> + IO.puts("ERROR: To build releases, Elixir 1.10.0 or higher is required.") + _ -> :ok + end + case Version.match?(System.version(), "< 1.11.4") + and :erlang.system_info(:otp_release) > ~c"23" do + true -> + IO.puts("ERROR: To build releases with Elixir lower than 1.11.4, Erlang/OTP lower than 24 is required.") + _ -> :ok + end + "~> 1.10" + _ -> + "~> 1.4" + end + end + + defp releases do + maybe_tar = case Mix.env() do + :prod -> [:tar] + _ -> [] + end + [ + ejabberd: [ + include_executables_for: [:unix], + # applications: [runtime_tools: :permanent] + strip_beams: Mix.env() != :dev, + steps: [©_extra_files/1, :assemble | maybe_tar] + ] + ] + end + + defp copy_extra_files(release) do + assigns = [ + version: version(), + rootdir: config(:rootdir), + installuser: config(:installuser), + libdir: config(:libdir), + sysconfdir: config(:sysconfdir), + localstatedir: config(:localstatedir), + config_dir: config(:config_dir), + logs_dir: config(:logs_dir), + spool_dir: config(:spool_dir), + vsn: version(), + iexpath: config(:iexpath), + erl: config(:erl), + epmd: config(:epmd), + bindir: Path.join([config(:release_dir), "releases", version()]), + release_dir: config(:release_dir), + erts_dir: config(:erts_dir), + erts_vsn: "erts-#{release.erts_version}" + ] + ro = "rel/overlays" + File.rm_rf(ro) + + # Elixir lower than 1.12.0 don't have System.shell + execute = fn(command) -> + case function_exported?(System, :shell, 1) do + true -> + System.shell(command, into: IO.stream()) + false -> + :os.cmd(to_charlist(command)) + end + end + + # Mix/Elixir lower than 1.11.0 use config/releases.exs instead of runtime.exs + case Version.match?(System.version(), "~> 1.11") do + true -> + :ok + false -> + execute.("cp config/runtime.exs config/releases.exs") + end + + execute.("sed -e 's|{{\\(\[_a-z\]*\\)}}|<%= @\\1 %>|g' ejabberdctl.template > ejabberdctl.example1") + Mix.Generator.copy_template("ejabberdctl.example1", "ejabberdctl.example2", assigns) + execute.("sed -e 's|{{\\(\[_a-z\]*\\)}}|<%= @\\1 %>|g' ejabberdctl.example2> ejabberdctl.example2a") + Mix.Generator.copy_template("ejabberdctl.example2a", "ejabberdctl.example2b", assigns) + execute.("sed -e 's|{{\\(\[_a-z\]*\\)}}|<%= @\\1 %>|g' ejabberdctl.example2b > ejabberdctl.example4") + execute.("sed -e 's|^ERLANG_OPTS=\"|ERLANG_OPTS=\"-boot ../releases/#{release.version}/start_clean -boot_var RELEASE_LIB ../lib |' ejabberdctl.example4 > ejabberdctl.example5") + execute.("sed -e 's|^INSTALLUSER=|ERL_OPTIONS=\"-setcookie \\$\\(cat \"\\${SCRIPT_DIR%/*}/releases/COOKIE\")\"\\nINSTALLUSER=|g' ejabberdctl.example5 > ejabberdctl.example6") + Mix.Generator.copy_template("ejabberdctl.example6", "#{ro}/bin/ejabberdctl", assigns) + File.chmod("#{ro}/bin/ejabberdctl", 0o755) + + File.rm("ejabberdctl.example1") + File.rm("ejabberdctl.example2") + File.rm("ejabberdctl.example2a") + File.rm("ejabberdctl.example2b") + File.rm("ejabberdctl.example3") + File.rm("ejabberdctl.example4") + File.rm("ejabberdctl.example5") + File.rm("ejabberdctl.example6") + + suffix = case Mix.env() do + :dev -> + Mix.Generator.copy_file("test/ejabberd_SUITE_data/ca.pem", "#{ro}/conf/ca.pem") + Mix.Generator.copy_file("test/ejabberd_SUITE_data/cert.pem", "#{ro}/conf/cert.pem") + ".example" + _ -> "" + end + + Mix.Generator.copy_file("ejabberd.yml.example", "#{ro}/conf/ejabberd.yml#{suffix}") + Mix.Generator.copy_file("ejabberdctl.cfg.example", "#{ro}/conf/ejabberdctl.cfg#{suffix}") + Mix.Generator.copy_file("inetrc", "#{ro}/conf/inetrc") + + Enum.each(File.ls!("sql"), + fn x -> + Mix.Generator.copy_file("sql/#{x}", "#{ro}/lib/ejabberd-#{release.version}/priv/sql/#{x}") + end) + + File.cp_r!("include", "#{ro}/lib/ejabberd-#{release.version}/include") + for {name, details} <- Map.to_list(release.applications) do + {_, is_otp_app} = List.keyfind(details, :otp_app?, 0) + {_, vsn} = List.keyfind(details, :vsn, 0) + {_, path} = List.keyfind(details, :path, 0) + source_dir = case is_otp_app do + :true -> "#{path}/include" + :false -> "deps/#{name}/include" + end + target_dir = "#{ro}/lib/#{name}-#{vsn}/include" + File.exists?(source_dir) + && File.mkdir_p(target_dir) + && File.cp_r!(source_dir, target_dir) + end + + case Mix.env() do + :dev -> execute.("REL_DIR_TEMP=$PWD/rel/overlays/ rel/setup-dev.sh mix") + _ -> :ok + end + + release + end + + defp docs do + [ + main: "readme", + logo: "_build/edoc/logo.png", + source_ref: "master", + extra_section: "", # No need for Pages section name, it's the only one + api_reference: false, # API section has just Elixir, hide it + filter_modules: "aaaaa", # Module section has just Elixir modules, hide them + extras: [ + "README.md": [title: "Readme"], + "COMPILE.md": [title: "Compile and Install"], + "CONTAINER.md": [title: "Container Image"], + "CONTRIBUTING.md": [title: "Contributing"], + "CONTRIBUTORS.md": [title: "Contributors"], + "CODE_OF_CONDUCT.md": [title: "Code of Conduct"], + "CHANGELOG.md": [title: "ChangeLog"], + "COPYING": [title: "Copying License"], + "_build/edoc/docs.md": [title: "⟹ ejabberd Docs"] + ], + groups_for_extras: [ + "": Path.wildcard("*.md") ++ ["COPYING"], + "For more documentation": "_build/edoc/docs.md" + ] + ] + end end defmodule Mix.Tasks.Compile.Asn1 do @@ -121,13 +409,18 @@ defmodule Mix.Tasks.Compile.Asn1 do def run(args) do {opts, _, _} = OptionParser.parse(args, switches: [force: :boolean]) - project = Mix.Project.config + project = Mix.Project.config() source_paths = project[:asn1_paths] || ["asn1"] dest_paths = project[:asn1_target] || ["src"] mappings = Enum.zip(source_paths, dest_paths) options = project[:asn1_options] || [] - Erlang.compile(manifest(), mappings, :asn1, :erl, opts[:force], fn + force = case opts[:force] do + :true -> [force: true] + _ -> [force: false] + end + + Erlang.compile(manifest(), mappings, :asn1, :erl, force, fn input, output -> options = options ++ [:noobj, outdir: Erlang.to_erl_file(Path.dirname(output))] case :asn1ct.compile(Erlang.to_erl_file(input), options) do @@ -137,8 +430,8 @@ defmodule Mix.Tasks.Compile.Asn1 do end) end - def manifests, do: [manifest] - defp manifest, do: Path.join(Mix.Project.manifest_path, @manifest) + def manifests, do: [manifest()] + defp manifest, do: Path.join(Mix.Project.manifest_path(), @manifest) def clean, do: Erlang.clean(manifest()) end diff --git a/mix.lock b/mix.lock index 695a514de..3eb53bce9 100644 --- a/mix.lock +++ b/mix.lock @@ -1,24 +1,39 @@ -%{"bbmustache": {:hex, :bbmustache, "1.0.4", "7ba94f971c5afd7b6617918a4bb74705e36cab36eb84b19b6a1b7ee06427aa38", [:rebar], []}, - "cache_tab": {:hex, :cache_tab, "1.0.4", "3fd2b1ab40c36e7830a4e09e836c6b0fa89191cd4e5fd471873e4eb42f5cd37c", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]}, - "cf": {:hex, :cf, "0.2.1", "69d0b1349fd4d7d4dc55b7f407d29d7a840bf9a1ef5af529f1ebe0ce153fc2ab", [:rebar3], []}, - "earmark": {:hex, :earmark, "1.0.3", "89bdbaf2aca8bbb5c97d8b3b55c5dd0cff517ecc78d417e87f1d0982e514557b", [:mix], []}, - "erlware_commons": {:hex, :erlware_commons, "0.21.0", "a04433071ad7d112edefc75ac77719dd3e6753e697ac09428fc83d7564b80b15", [:rebar3], [{:cf, "0.2.1", [hex: :cf, optional: false]}]}, - "esip": {:hex, :esip, "1.0.8", "69885a6c07964aabc6c077fe1372aa810a848bd3d9a415b160dabdce9c7a79b5", [:rebar3], [{:fast_tls, "1.0.7", [hex: :fast_tls, optional: false]}, {:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}, {:stun, "1.0.7", [hex: :stun, optional: false]}]}, - "ex_doc": {:hex, :ex_doc, "0.14.3", "e61cec6cf9731d7d23d254266ab06ac1decbb7651c3d1568402ec535d387b6f7", [:mix], [{:earmark, "~> 1.0", [hex: :earmark, optional: false]}]}, - "exrm": {:hex, :exrm, "1.0.8", "5aa8990cdfe300282828b02cefdc339e235f7916388ce99f9a1f926a9271a45d", [:mix], [{:relx, "~> 3.5", [hex: :relx, optional: false]}]}, - "ezlib": {:hex, :ezlib, "1.0.1", "add8b2770a1a70c174aaea082b4a8668c0c7fdb03ee6cc81c6c68d3a6c3d767d", [:rebar3], []}, - "fast_tls": {:hex, :fast_tls, "1.0.7", "9b72ecfcdcad195ab072c196fab8334f49d8fea76bf1a51f536d69e7527d902a", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]}, - "fast_xml": {:hex, :fast_xml, "1.1.15", "6d23eb7f874e1357cf80a48d75a7bd0c8f6318029dc4b70122e9f54911f57f83", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]}, - "fast_yaml": {:hex, :fast_yaml, "1.0.6", "3fe6feb7935ae8028b337e53e1db29e73ad3bca8041108f6a8f73b7175ece75c", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]}, - "getopt": {:hex, :getopt, "0.8.2", "b17556db683000ba50370b16c0619df1337e7af7ecbf7d64fbf8d1d6bce3109b", [:rebar], []}, - "goldrush": {:hex, :goldrush, "0.1.8", "2024ba375ceea47e27ea70e14d2c483b2d8610101b4e852ef7f89163cdb6e649", [:rebar3], []}, - "iconv": {:hex, :iconv, "1.0.2", "a0792f06ab4b5ea1b5bb49789405739f1281a91c44cf3879cb70e4d777666217", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]}, - "jiffy": {:hex, :jiffy, "0.14.7", "9f33b893edd6041ceae03bc1e50b412e858cc80b46f3d7535a7a9940a79a1c37", [:make, :rebar], []}, - "lager": {:hex, :lager, "3.2.1", "eef4e18b39e4195d37606d9088ea05bf1b745986cf8ec84f01d332456fe88d17", [:rebar3], [{:goldrush, "0.1.8", [hex: :goldrush, optional: false]}]}, - "p1_oauth2": {:hex, :p1_oauth2, "0.6.1", "4e021250cc198c538b097393671a41e7cebf463c248980320e038fe0316eb56b", [:rebar3], []}, - "p1_utils": {:hex, :p1_utils, "1.0.5", "3e698354fdc1fea5491d991457b0cb986c0a00a47d224feb841dc3ec82b9f721", [:rebar3], []}, - "providers": {:hex, :providers, "1.6.0", "db0e2f9043ae60c0155205fcd238d68516331d0e5146155e33d1e79dc452964a", [:rebar3], [{:getopt, "0.8.2", [hex: :getopt, optional: false]}]}, - "relx": {:hex, :relx, "3.21.1", "f989dc520730efd9075e9f4debcb8ba1d7d1e86b018b0bcf45a2eb80270b4ad6", [:rebar3], [{:bbmustache, "1.0.4", [hex: :bbmustache, optional: false]}, {:cf, "0.2.1", [hex: :cf, optional: false]}, {:erlware_commons, "0.21.0", [hex: :erlware_commons, optional: false]}, {:getopt, "0.8.2", [hex: :getopt, optional: false]}, {:providers, "1.6.0", [hex: :providers, optional: false]}]}, - "stringprep": {:hex, :stringprep, "1.0.6", "1cf1c439eb038aa590da5456e019f86afbfbfeb5a2d37b6e5f873041624c6701", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]}, - "stun": {:hex, :stun, "1.0.7", "904dc6f26a3c30c54881c4c3003699f2a4968067ee6b3aecdf9895aad02df75e", [:rebar3], [{:fast_tls, "1.0.7", [hex: :fast_tls, optional: false]}, {:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]}, - "xmpp": {:git, "https://github.com/processone/xmpp.git", "758c3a865563e019e46f8f6e96857a4161a833dd", [tag: "1.1.1"]}} +%{ + "base64url": {:hex, :base64url, "1.0.1", "f8c7f2da04ca9a5d0f5f50258f055e1d699f0e8bf4cfdb30b750865368403cf6", [:rebar3], [], "hexpm", "f9b3add4731a02a9b0410398b475b33e7566a695365237a6bdee1bb447719f5c"}, + "cache_tab": {:hex, :cache_tab, "1.0.33", "e2542afb34f17ee3ca19d2b0f546a074922c2b99fb6b2acfb38160d7d0336ec3", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "4258009eb050b22aabe0c848e230bba58401a6895c58c2ff74dfb635e3c35900"}, + "dialyxir": {:hex, :dialyxir, "1.4.6", "7cca478334bf8307e968664343cbdb432ee95b4b68a9cba95bdabb0ad5bdfd9a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "8cf5615c5cd4c2da6c501faae642839c8405b49f8aa057ad4ae401cb808ef64d"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, + "eimp": {:hex, :eimp, "1.0.26", "c0b05f32e35629c4d9bcfb832ff879a92b0f92b19844bc7835e0a45635f2899a", [:rebar3], [{:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "d96d4e8572b9dfc40f271e47f0cb1d8849373bc98a21223268781765ed52044c"}, + "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, + "eredis": {:hex, :eredis, "1.7.1", "39e31aa02adcd651c657f39aafd4d31a9b2f63c6c700dc9cece98d4bc3c897ab", [:mix, :rebar3], [], "hexpm", "7c2b54c566fed55feef3341ca79b0100a6348fd3f162184b7ed5118d258c3cc1"}, + "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, + "esip": {:hex, :esip, "1.0.59", "eb202f8c62928193588091dfedbc545fe3274c34ecd209961f86dcb6c9ebce88", [:rebar3], [{:fast_tls, "1.1.25", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.21", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "0bdf2e3c349dc0b144f173150329e675c6a51ac473d7a0b2e362245faad3fbe6"}, + "ex_doc": {:hex, :ex_doc, "0.38.3", "ddafe36b8e9fe101c093620879f6604f6254861a95133022101c08e75e6c759a", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "ecaa785456a67f63b4e7d7f200e8832fa108279e7eb73fd9928e7e66215a01f9"}, + "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, + "ezlib": {:hex, :ezlib, "1.0.15", "d74f5df191784744726a5b1ae9062522c606334f11086363385eb3b772d91357", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "dd14ba6c12521af5cfe6923e73e3d545f4a0897dc66bfab5287fbb7ae3962eab"}, + "fast_tls": {:hex, :fast_tls, "1.1.25", "da8ed6f05a2452121b087158b17234749f36704c1f2b74dc51db99a1e27ed5e8", [:rebar3], [{:p1_utils, "~> 1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "59e183b5740e670e02b8aa6be673b5e7779e5fe5bfcc679fe2d4993d1949a821"}, + "fast_xml": {:hex, :fast_xml, "1.1.57", "31efc0f9bceda92069704f7a25830407da5dc3dad1272b810d6f2e13e73cc11a", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "eec34e90adacafe467d5ddab635a014ded73b98b4061554b2d1972173d929c39"}, + "fast_yaml": {:hex, :fast_yaml, "1.0.39", "2e71168091949bab0e5f583b340a99072b4d22d93eb86624e7850a12b1517be4", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "24c7b9ab9e2b9269d64e45f4a2a1280966adb17d31e63365cfd3ee277fb0a78d"}, + "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, + "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, + "jiffy": {:hex, :jiffy, "1.1.2", "a9b6c9a7ec268e7cf493d028f0a4c9144f59ccb878b1afe42841597800840a1b", [:rebar3], [], "hexpm", "bb61bc42a720bbd33cb09a410e48bb79a61012c74cb8b3e75f26d988485cf381"}, + "jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"}, + "luerl": {:hex, :luerl, "1.2.3", "df25f41944e57a7c4d9ef09d238bc3e850276c46039cfc12b8bb42eccf36fcb1", [:rebar3], [], "hexpm", "1b4b9d0ca5d7d280d1d2787a6a5ee9f5a212641b62bff91556baa53805df3aed"}, + "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, + "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, + "mqtree": {:hex, :mqtree, "1.0.19", "d769c25f898810725fc7db0dbffe5f72098647048b1be2e6d772f1c2f31d8476", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "c81065715c49a1882812f80a5ae2d842e80dd3f2d130530df35990248bf8ce3c"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, + "p1_acme": {:hex, :p1_acme, "1.0.28", "64d9c17f5412aa92d75b29206b2b984d734a4fe1b7eacb66c3d7a7c697ac612c", [:rebar3], [{:base64url, "~> 1.0", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "~> 1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "~> 1.0.17", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "ce686986de3f9d5fd285afe87523cb45329a349c6c6be7acc1ed916725d46423"}, + "p1_mysql": {:hex, :p1_mysql, "1.0.26", "574d07c9936c53b1ec3556db3cf064cc14a6c39039835b3d940471bfa5ac8e2b", [:rebar3], [], "hexpm", "ea138083f2c54719b9cf549dbf5802a288b0019ea3e5449b354c74cc03fafdec"}, + "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, + "p1_pgsql": {:hex, :p1_pgsql, "1.1.35", "e13d89f14d717553e85c88a152ce77461916b013d88fcb851e354a0b332d4218", [:rebar3], [{:xmpp, "~> 1.11.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "e99594446c411c660696795b062336f5c4bd800451d8f620bb4d4ce304e255c2"}, + "p1_utils": {:hex, :p1_utils, "1.0.28", "9a7088a98d788b4c4880fd3c82d0c135650db13f2e4ef7e10db179791bc94d59", [:rebar3], [], "hexpm", "c49bd44bc4a40ad996691af826dd7e0aa56d4d0cd730817190a1f84d1a7f0033"}, + "pkix": {:hex, :pkix, "1.0.10", "d3bfadf7b7cfe2a3636f1b256c9cce5f646a07ce31e57ee527668502850765a0", [:rebar3], [], "hexpm", "e02164f83094cb124c41b1ab28988a615d54b9adc38575f00f19a597a3ac5d0e"}, + "sqlite3": {:hex, :sqlite3, "1.1.15", "e819defd280145c328457d7af897d2e45e8e5270e18812ee30b607c99cdd21af", [:rebar3], [], "hexpm", "3c0ba4e13322c2ad49de4e2ddd28311366adde54beae8dba9d9e3888f69d2857"}, + "stringprep": {:hex, :stringprep, "1.0.33", "22f42866b4f6f3c238ea2b9cb6241791184ddedbab55e94a025511f46325f3ca", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "96f8b30bc50887f605b33b46bca1d248c19a879319b8c482790e3b4da5da98c0"}, + "stun": {:hex, :stun, "1.2.21", "735855314ad22cb7816b88597d2f5ca22e24aa5e4d6010a0ef3affb33ceed6a5", [:rebar3], [{:fast_tls, "1.1.25", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "3d7fe8efb9d05b240a6aa9a6bf8b8b7bff2d802895d170443c588987dc1e12d9"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"}, + "xmpp": {:git, "https://github.com/processone/xmpp", "e9d901ea84fd3910ad32b715853397eb1155b41c", [ref: "e9d901ea84fd3910ad32b715853397eb1155b41c"]}, + "yconf": {:git, "https://github.com/processone/yconf", "95692795a8a8d950ba560e5b07e6b80660557259", [ref: "95692795a8a8d950ba560e5b07e6b80660557259"]}, +} diff --git a/plugins/configure_deps.erl b/plugins/configure_deps.erl new file mode 100644 index 000000000..181da0b02 --- /dev/null +++ b/plugins/configure_deps.erl @@ -0,0 +1,5 @@ +-module(configure_deps). +-export(['configure-deps'/2]). + +'configure-deps'(Config, Vals) -> + {ok, Config}. diff --git a/plugins/override_deps_versions2.erl b/plugins/override_deps_versions2.erl new file mode 100644 index 000000000..de08b5482 --- /dev/null +++ b/plugins/override_deps_versions2.erl @@ -0,0 +1,126 @@ +-module(override_deps_versions2). +-export([preprocess/2, 'pre_update-deps'/2, new_replace/1, new_replace/0]). + +preprocess(Config, _Dirs) -> + update_deps(Config). + +update_deps(Config) -> + LocalDeps = rebar_config:get_local(Config, deps, []), + TopDeps = case rebar_config:get_xconf(Config, top_deps, []) of + [] -> LocalDeps; + Val -> Val + end, + Config2 = rebar_config:set_xconf(Config, top_deps, TopDeps), + NewDeps = lists:map(fun({Name, _, _} = Dep) -> + case lists:keyfind(Name, 1, TopDeps) of + false -> Dep; + TopDep -> TopDep + end + end, LocalDeps), + %io:format("LD ~p~n", [LocalDeps]), + %io:format("TD ~p~n", [TopDeps]), + + Config3 = rebar_config:set(Config2, deps, NewDeps), + {ok, Config3, []}. + + +'pre_update-deps'(Config, _Dirs) -> + {ok, Config2, _} = update_deps(Config), + + case code:is_loaded(old_rebar_config) of + false -> + {_, Beam, _} = code:get_object_code(rebar_config), + NBeam = rename(Beam, old_rebar_config), + code:load_binary(old_rebar_config, "blank", NBeam), + replace_mod(Beam); + _ -> + ok + end, + {ok, Config2}. + +new_replace() -> + old_rebar_config:new(). +new_replace(Config) -> + NC = old_rebar_config:new(Config), + {ok, Conf, _} = update_deps(NC), + Conf. + +replace_mod(Beam) -> + {ok, {_, [{exports, Exports}]}} = beam_lib:chunks(Beam, [exports]), + Funcs = lists:filtermap( + fun({module_info, _}) -> + false; + ({Name, Arity}) -> + Args = args(Arity), + Call = case Name of + new -> + [erl_syntax:application( + erl_syntax:abstract(override_deps_versions2), + erl_syntax:abstract(new_replace), + Args)]; + _ -> + [erl_syntax:application( + erl_syntax:abstract(old_rebar_config), + erl_syntax:abstract(Name), + Args)] + end, + {true, erl_syntax:function(erl_syntax:abstract(Name), + [erl_syntax:clause(Args, none, + Call)])} + end, Exports), + Forms0 = ([erl_syntax:attribute(erl_syntax:abstract(module), + [erl_syntax:abstract(rebar_config)])] + ++ Funcs), + Forms = [erl_syntax:revert(Form) || Form <- Forms0], + %io:format("--------------------------------------------------~n" + % "~s~n", + % [[erl_pp:form(Form) || Form <- Forms]]), + {ok, Mod, Bin} = compile:forms(Forms, [report, export_all]), + code:purge(rebar_config), + {module, Mod} = code:load_binary(rebar_config, "mock", Bin). + + +args(0) -> + []; +args(N) -> + [arg(N) | args(N-1)]. + +arg(N) -> + erl_syntax:variable(list_to_atom("A"++integer_to_list(N))). + +rename(BeamBin0, Name) -> + BeamBin = replace_in_atab(BeamBin0, Name), + update_form_size(BeamBin). + +%% Replace the first atom of the atom table with the new name +replace_in_atab(<<"Atom", CnkSz0:32, Cnk:CnkSz0/binary, Rest/binary>>, Name) -> + replace_first_atom(<<"Atom">>, Cnk, CnkSz0, Rest, latin1, Name); +replace_in_atab(<<"AtU8", CnkSz0:32, Cnk:CnkSz0/binary, Rest/binary>>, Name) -> + replace_first_atom(<<"AtU8">>, Cnk, CnkSz0, Rest, unicode, Name); +replace_in_atab(<>, Name) -> + <>. + +replace_first_atom(CnkName, Cnk, CnkSz0, Rest, Encoding, Name) -> + <> = Cnk, + NumPad0 = num_pad_bytes(CnkSz0), + <<_:NumPad0/unit:8, NextCnks/binary>> = Rest, + NameBin = atom_to_binary(Name, Encoding), + NameSz = byte_size(NameBin), + CnkSz = CnkSz0 + NameSz - NameSz0, + NumPad = num_pad_bytes(CnkSz), + <>. + + +%% Calculate the number of padding bytes that have to be added for the +%% BinSize to be an even multiple of ?beam_num_bytes_alignment. +num_pad_bytes(BinSize) -> + case 4 - (BinSize rem 4) of + 4 -> 0; + N -> N + end. + +%% Update the size within the top-level form +update_form_size(<<"FOR1", _OldSz:32, Rest/binary>> = Bin) -> + Sz = size(Bin) - 8, +<<"FOR1", Sz:32, Rest/binary>>. diff --git a/plugins/override_opts.erl b/plugins/override_opts.erl new file mode 100644 index 000000000..818f53e87 --- /dev/null +++ b/plugins/override_opts.erl @@ -0,0 +1,43 @@ +-module(override_opts). +-export([preprocess/2]). + +override_opts(override, Config, Opts) -> + lists:foldl(fun({Opt, Value}, Conf) -> + rebar_config:set(Conf, Opt, Value) + end, Config, Opts); +override_opts(add, Config, Opts) -> + lists:foldl(fun({Opt, Value}, Conf) -> + V = rebar_config:get_local(Conf, Opt, []), + rebar_config:set(Conf, Opt, V ++ Value) + end, Config, Opts); +override_opts(del, Config, Opts) -> + lists:foldl(fun({Opt, Value}, Conf) -> + V = rebar_config:get_local(Conf, Opt, []), + rebar_config:set(Conf, Opt, V -- Value) + end, Config, Opts). + +preprocess(Config, _Dirs) -> + Overrides = rebar_config:get_local(Config, overrides, []), + TopOverrides = case rebar_config:get_xconf(Config, top_overrides, []) of + [] -> Overrides; + Val -> Val + end, + Config2 = rebar_config:set_xconf(Config, top_overrides, TopOverrides), + try + Config3 = case rebar_app_utils:load_app_file(Config2, _Dirs) of + {ok, C, AppName, _AppData} -> + lists:foldl(fun({Type, AppName2, Opts}, Conf1) when + AppName2 == AppName -> + override_opts(Type, Conf1, Opts); + ({Type, Opts}, Conf1a) -> + override_opts(Type, Conf1a, Opts); + (_, Conf2) -> + Conf2 + end, C, TopOverrides); + _ -> + Config2 + end, + {ok, Config3, []} + catch + error:badarg -> {ok, Config2, []} + end. diff --git a/priv/css/admin.css b/priv/css/admin.css new file mode 100644 index 000000000..12fa97e22 --- /dev/null +++ b/priv/css/admin.css @@ -0,0 +1,322 @@ +html,body { + margin: 0; + padding: 0; + height: 100%; + background: #f9f9f9; + font-family: sans-serif; +} +body { + min-width: 990px; +} +a { + text-decoration: none; + color: #3eaffa; +} +a:hover, +a:active { + text-decoration: underline; +} +#container { + position: relative; + padding: 0; + margin: 0 auto; + max-width: 1280px; + min-height: 100%; + height: 100%; + margin-bottom: -30px; + z-index: 1; +} +html>body #container { + height: auto; +} +#header h1 { + width: 100%; + height: 50px; + padding: 0; + margin: 0; + background-color: #49cbc1; +} +#header h1 a { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 50px; + padding: 0; + margin: 0; + background: url('@BASE@logo.png') 10px center no-repeat transparent; + background-size: auto 25px; + display: block; + text-indent: -9999px; +} +#clearcopyright { + display: block; + width: 100%; + height: 30px; +} +#copyrightouter { + position: relative; + display: table; + width: 100%; + height: 30px; + z-index: 2; +} +#copyright { + display: table-cell; + vertical-align: bottom; + width: 100%; + height: 30px; +} +#copyright a { + font-weight: bold; + color: #fff; +} +#copyright p { + margin-left: 0; + margin-right: 0; + margin-top: 5px; + margin-bottom: 0; + padding-left: 0; + padding-right: 0; + padding-top: 5px; + padding-bottom: 5px; + width: 100%; + color: #fff; + background-color: #30353E; + font-size: 0.75em; + text-align: center; +} +#navigation { + display: inline-block; + vertical-align: top; + width: 30%; +} +#navigation ul { + padding: 0; + margin: 0; + width: 90%; + background: #fff; +} +#navigation ul li { + list-style: none; + margin: 0; + + border-bottom: 1px solid #f9f9f9; + text-align: left; +} +#navigation ul li a { + margin: 0; + display: inline-block; + padding: 10px; + color: #333; +} +ul li #navhead a, ul li #navheadsub a, ul li #navheadsubsub a { + font-size: 1.5em; + color: inherit; +} +#navitemsub { + border-left: 0.5em solid #424a55; +} +#navitemsubsub { + border-left: 2em solid #424a55; +} +#navheadsub, +#navheadsubsub { + padding-left: 0.5em; +} +#navhead, +#navheadsub, +#navheadsubsub { + border-top: 3px solid #49cbc1; + background: #424a55; + color: #fff; +} + +#navitemlogin-start { + border-top: 0.2em solid #cae7e4; +} +#navitemlogin { + padding: 0.5em; + border-bottom: 0.2em solid #cae7e4; + padding-left: 0.5em; +} +#welcome { + padding: 2em; + border-top: 0.2em solid #cae7e4; + border-bottom: 0.2em solid #cae7e4; + background-color: #f4f9f9; +} +#lastactivity li { + padding: 2px; + margin-bottom: -1px; +} +thead tr td { + background: #cae7e4; +} +td.copy { + text-align: center; +} +tr.head { + color: #fff; + background-color: #3b547a; + text-align: center; +} +tr.oddraw { + color: #412c75; + background-color: #ccd4df; + text-align: center; +} +tr.evenraw { + color: #412c75; + background-color: #dbe0e8; + text-align: center; +} +td.leftheader { + color: #412c75; + background-color: #ccccc1; + padding-left: 5px; + padding-top: 2px; + padding-bottom: 2px; + margin-top: 0px; + margin-bottom: 0px; +} +td.leftcontent { + color: #000044; + background-color: #e6e6df; + padding-left: 5px; + padding-right: 5px; + padding-top: 2px; + padding-bottom: 2px; + margin-top: 0px; + margin-bottom: 0px; +} +td.rightcontent { + color: #000044; + text-align: justify; + padding-left: 10px; + padding-right: 10px; + padding-bottom: 5px; +} + +h1 { + color: #000044; + padding-top: 2px; + padding-bottom: 2px; + margin-top: 0px; + margin-bottom: 0px; +} +h2 { + color: #000044; + text-align: center; + padding-top: 2px; + padding-bottom: 2px; + margin-top: 0px; + margin-bottom: 0px; +} +h3 { + color: #000044; + text-align: left; + padding-top: 20px; + padding-bottom: 2px; + margin-top: 0px; + margin-bottom: 0px; +} +#content ul { + padding-left: 1.1em; + margin-top: 1em; +} +#content ul li { + list-style-type: disc; + padding: 5px; +} +#content ul.nolistyle>li { + list-style-type: none; +} +#content { + display: inline-block; + vertical-align: top; + padding-top: 25px; + width: 70%; +} +div.anchorlink { + display: inline-block; + float: right; + margin-top: 1em; + margin-right: 1em; +} +div.anchorlink a { + padding: 3px; + background: #cae7e4; + font-size: 0.75em; + color: black; +} +div.guidelink, +p[dir=ltr] { + display: inline-block; + float: right; + margin-top: 1em; + margin-right: 1em; +} +div.guidelink a, +p[dir=ltr] a { + padding: 3px; + background: #3eaffa; + font-size: 0.75em; + color: #fff; + border-radius: 2px; +} +table { + margin-top: 1em; +} +table tr td { + padding: 0.5em; +} +table tr:nth-child(odd) { + background: #fff; +} +table.withtextareas>tbody>tr>td { + vertical-align: top; +} +textarea { + margin-bottom: 1em; +} +input, +select { + font-size: 1em; +} +.result { + border: 1px; + border-style: dashed; + border-color: #FE8A02; + padding: 1em; + margin-right: 1em; + background: #FFE3C9; +} +*.alignright { + text-align: right; +} +.btn-danger:hover { + color: #fff; + background-color: #cb2431; +} +.btn-danger { + color: #cb2431; + transition: none; +} +h3.api { + border-bottom: 1px solid #b6b6b6; +} +details > summary { + background-color: #dbeceb; + border: none; + cursor: pointer; + list-style: none; + padding: 8px; + border-radius: 4px; +} +details > pre, details > p { + background-color: #f2f8f7; + border-bottom: 0.2em solid #dbeceb; + margin: 0; + padding: 10px; +} diff --git a/priv/css/bosh.css b/priv/css/bosh.css new file mode 100644 index 000000000..efa6b68b5 --- /dev/null +++ b/priv/css/bosh.css @@ -0,0 +1,51 @@ +body { + margin: 0; + padding: 0; + font-family: sans-serif; + color: #fff; +} +h1 { + font-size: 3em; + color: #444; +} +p { + line-height: 1.5em; + color: #888; +} +a { + color: #fff; +} +a:hover, +a:active { + text-decoration: underline; +} +.container { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: #424A55; + background-image: -webkit-linear-gradient(270deg, rgba(48,52,62,0) 24%, #30353e 100%); + background-image: linear-gradient(-180deg, rgba(48,52,62,0) 24%, #30353e 100%); +} +.section { + padding: 3em; +} +.white.section { + background: #fff; + border-bottom: 4px solid #41AFCA; +} +.white.section a { + text-decoration: none; + color: #41AFCA; +} +.white.section a:hover, +.white.section a:active { + text-decoration: underline; +} +.block { + margin: 0 auto; + max-width: 900px; + width: 100%; +} diff --git a/priv/css/muc.css b/priv/css/muc.css new file mode 100644 index 000000000..b81ad5b52 --- /dev/null +++ b/priv/css/muc.css @@ -0,0 +1,29 @@ +.ts {color: #AAAAAA; text-decoration: none;} +.mrcm {color: #009900; font-style: italic; font-weight: bold;} +.msc {color: #009900; font-style: italic; font-weight: bold;} +.msm {color: #000099; font-style: italic; font-weight: bold;} +.mj {color: #009900; font-style: italic;} +.ml {color: #009900; font-style: italic;} +.mk {color: #009900; font-style: italic;} +.mb {color: #009900; font-style: italic;} +.mnc {color: #009900; font-style: italic;} +.mn {color: #0000AA;} +.mne {color: #AA0099;} +a.nav {color: #AAAAAA; font-family: monospace; letter-spacing: 3px; text-decoration: none;} +div.roomtitle {border-bottom: #224466 solid 3pt; margin-left: 20pt;} +div.roomtitle {color: #336699; font-size: 24px; font-weight: bold; font-family: sans-serif; letter-spacing: 3px; text-decoration: none;} +a.roomjid {color: #336699; font-size: 24px; font-weight: bold; font-family: sans-serif; letter-spacing: 3px; margin-left: 20pt; text-decoration: none;} +div.logdate {color: #663399; font-size: 20px; font-weight: bold; font-family: sans-serif; letter-spacing: 2px; border-bottom: #224466 solid 1pt; margin-left:80pt; margin-top:20px;} +div.roomsubject {color: #336699; font-size: 18px; font-family: sans-serif; margin-left: 80pt; margin-bottom: 10px;} +div.rc {color: #336699; font-size: 12px; font-family: sans-serif; margin-left: 50%; text-align: right; background: #f3f6f9; border-bottom: 1px solid #336699; border-right: 4px solid #336699;} +div.rct {font-weight: bold; background: #e3e6e9; padding-right: 10px; cursor: pointer;} +div.rct:hover {text-decoration: underline;} +div.rcos {padding-right: 10px;} +div.rcoe {color: green;} +div.rcod {color: red;} +div.rcoe:after {content: ": v";} +div.rcod:after {content: ": x";} +div.rcot:after {} +div.jl {display: none;} +.legend {width: 100%; margin-top: 30px; border-top: #224466 solid 1pt; padding: 10px 0px 10px 0px; text-align: left; font-family: monospace; letter-spacing: 2px;} +.w3c {position: absolute; right: 10px; width: 60%; text-align: right; font-family: monospace; letter-spacing: 1px;} diff --git a/priv/css/oauth.css b/priv/css/oauth.css new file mode 100644 index 000000000..41112f39c --- /dev/null +++ b/priv/css/oauth.css @@ -0,0 +1,103 @@ +body { + margin: 0; + padding: 0; + + font-family: sans-serif; + color: #fff; +} + +h1 { + font-size: 3em; + color: #444; +} + +p { + line-height: 1.5em; + color: #888; +} + +a { + color: #fff; +} +a:hover, +a:active { + text-decoration: underline; +} + +em { + display: inline-block; + padding: 0 5px; + background: #f4f4f4; + border-radius: 5px; + font-style: normal; + font-weight: bold; + color: #444; +} + +form { + color: #444; +} +label { + display: block; + font-weight: bold; +} + +input[type=text], +input[type=password] { + margin-bottom: 1em; + padding: 0.4em; + max-width: 330px; + width: 100%; + border: 1px solid #c4c4c4; + border-radius: 5px; + outline: 0; + font-size: 1.2em; +} +input[type=text]:focus, +input[type=password]:focus, +input[type=text]:active, +input[type=password]:active { + border-color: #41AFCA; +} + +input[type=submit] { + font-size: 1em; +} + +.container { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: #424A55; + background-image: -webkit-linear-gradient(270deg, rgba(48,52,62,0) 24%, #30353e 100%); + background-image: linear-gradient(-180deg, rgba(48,52,62,0) 24%, #30353e 100%); +} + +.section { + padding: 3em; +} +.white.section { + background: #fff; + border-bottom: 4px solid #41AFCA; +} + +.white.section a { + text-decoration: none; + color: #41AFCA; +} +.white.section a:hover, +.white.section a:active { + text-decoration: underline; +} + +.container > .section { + background: #424A55; +} + +.block { + margin: 0 auto; + max-width: 900px; + width: 100%; +} diff --git a/priv/css/register.css b/priv/css/register.css new file mode 100644 index 000000000..f38461eb1 --- /dev/null +++ b/priv/css/register.css @@ -0,0 +1,65 @@ +@viewport { + width: device-width; + zoom: 1.0; +} + +html,body { + font-family: sans-serif; + background: white; + + padding: 0.5em; + margin: auto; + max-width: 800px; + height: 100%; +} + +form { + padding: 0.5em 0; +} + +ul { + list-style: none; +} + ul > li { + margin-bottom: 2em; + } + +ol { + list-style: none; + padding: 0; +} + ol > li { + margin-bottom: 2em; + font-weight: bold; + font-size: 0.75em; + } + ol > li > ul { + list-style: decimal; + font-weight: normal; + font-style: italic; + } + ol > li > ul > li { + margin-bottom: auto; + } + +input { + display: block; + padding: 0.25em; + font-size: 1.5em; + border: 1px solid #ccc; + border-radius: 0; + + -webkit-appearance: none; + -moz-appearance: none; +} + input:focus { + border-color: #428bca; + } + input[type=submit] { + padding: 0.33em 1em; + background-color: #428bca; + border-radius: 2px; + cursor: pointer; + border: none; + color: #fff; + } diff --git a/priv/css/sortable.min.css b/priv/css/sortable.min.css new file mode 100644 index 000000000..5296c0f9f --- /dev/null +++ b/priv/css/sortable.min.css @@ -0,0 +1 @@ +.sortable thead th:not(.no-sort){cursor:pointer}.sortable thead th:not(.no-sort)::after,.sortable thead th:not(.no-sort)::before{transition:color .1s ease-in-out;font-size:1.2em;color:rgba(0,0,0,0)}.sortable thead th:not(.no-sort)::after{margin-left:3px;content:"▸"}.sortable thead th:not(.no-sort):hover::after{color:inherit}.sortable thead th:not(.no-sort)[aria-sort=descending]::after{color:inherit;content:"▾"}.sortable thead th:not(.no-sort)[aria-sort=ascending]::after{color:inherit;content:"▴"}.sortable thead th:not(.no-sort).indicator-left::after{content:""}.sortable thead th:not(.no-sort).indicator-left::before{margin-right:3px;content:"▸"}.sortable thead th:not(.no-sort).indicator-left:hover::before{color:inherit}.sortable thead th:not(.no-sort).indicator-left[aria-sort=descending]::before{color:inherit;content:"▾"}.sortable thead th:not(.no-sort).indicator-left[aria-sort=ascending]::before{color:inherit;content:"▴"}/*# sourceMappingURL=sortable-base.min.css.map */ diff --git a/priv/img/admin-logo.png b/priv/img/admin-logo.png new file mode 100644 index 000000000..041b37c69 Binary files /dev/null and b/priv/img/admin-logo.png differ diff --git a/priv/img/bosh-logo.png b/priv/img/bosh-logo.png new file mode 100644 index 000000000..25a46f9f9 Binary files /dev/null and b/priv/img/bosh-logo.png differ diff --git a/priv/img/favicon.png b/priv/img/favicon.png new file mode 100644 index 000000000..56c8b09c9 Binary files /dev/null and b/priv/img/favicon.png differ diff --git a/priv/img/oauth-logo.png b/priv/img/oauth-logo.png new file mode 100644 index 000000000..25a46f9f9 Binary files /dev/null and b/priv/img/oauth-logo.png differ diff --git a/priv/img/powered-by-ejabberd.png b/priv/img/powered-by-ejabberd.png new file mode 100644 index 000000000..2667f57f7 Binary files /dev/null and b/priv/img/powered-by-ejabberd.png differ diff --git a/priv/img/powered-by-erlang.png b/priv/img/powered-by-erlang.png new file mode 100644 index 000000000..4dfdb06a8 Binary files /dev/null and b/priv/img/powered-by-erlang.png differ diff --git a/priv/img/valid-xhtml10.png b/priv/img/valid-xhtml10.png new file mode 100644 index 000000000..2275ee6ea Binary files /dev/null and b/priv/img/valid-xhtml10.png differ diff --git a/priv/img/vcss.png b/priv/img/vcss.png new file mode 100644 index 000000000..9b2f596e0 Binary files /dev/null and b/priv/img/vcss.png differ diff --git a/priv/js/admin.js b/priv/js/admin.js new file mode 100644 index 000000000..d726e42c9 --- /dev/null +++ b/priv/js/admin.js @@ -0,0 +1,15 @@ + +function selectAll() { + for(i=0;i elements are not allowed by RFC6121","Повече от един елемента не се разрешават от RFC6121"}. +{"Multi-User Chat","Групов чат (MUC)"}. +{"Name","Име"}. +{"Natural Language for Room Discussions","Език за дискусии в стаята"}. +{"Natural-Language Room Name","Име на стаята на предпочитания език"}. +{"Neither 'jid' nor 'nick' attribute found","Атрибутите 'jid' и 'nick' не са намерени"}. +{"Neither 'role' nor 'affiliation' attribute found","Атрибути 'role' или 'affiliation' не са намерени"}. +{"Never","Никога"}. +{"New Password:","Нова парола:"}. +{"Nickname can't be empty","Псевдонимът не може да бъде празен"}. +{"Nickname Registration at ","Регистрация на псевдоним в "}. +{"Nickname ~s does not exist in the room","Псевдонимът ~s не присъства в стаята"}. +{"Nickname","Псевдоним"}. +{"No address elements found","Не е намерен адресен елемент"}. +{"No addresses element found","Не са намерени адресни елементи"}. +{"No 'affiliation' attribute found","Атрибут 'affiliation' не е намерен"}. +{"No available resource found","Не е намерен наличен ресурс"}. +{"No body provided for announce message","Не е предоставен текст за съобщение тип обява"}. +{"No child elements found","Не са открити подчинени елементи"}. +{"No data form found","Не е намерена форма за данни"}. +{"No Data","Няма данни"}. +{"No features available","Няма налични функции"}. +{"No element found","Елементът не е намерен"}. +{"No hook has processed this command","Никоя кука не е обработила тази команда"}. +{"No info about last activity found","Няма информация за последна активновт"}. +{"No 'item' element found","Елементът 'item' не е намерен"}. +{"No items found in this query","Няма намерени елементи в тази заявка"}. +{"No limit","Няма ограничение"}. +{"No module is handling this query","Нито един модул не обработва тази заявка"}. +{"No node specified","Не е посочен нод"}. +{"No 'password' found in data form","Не е намерен 'password' във формата за данни"}. +{"No 'password' found in this query","В заявката не е намерен 'password'"}. +{"No 'path' found in data form","Не е намерен 'path' във формата за данни"}. +{"No pending subscriptions found","Не са намерени чакащи абонаменти"}. +{"No privacy list with this name found","Не е намерен списък за поверителност с това име"}. +{"No private data found in this query","Няма открити лични данни в тази заявка"}. +{"No running node found","Не е намерен работещ нод"}. +{"No services available","Няма налични услуги"}. +{"No statistics found for this item","Не е налична статистика за този елемент"}. +{"No 'to' attribute found in the invitation","Атрибутът 'to' не е намерен в поканата"}. +{"Nobody","Никой"}. +{"Node already exists","Нодът вече съществува"}. +{"Node ID","ID на нода"}. +{"Node index not found","Индексът на нода не е намерен"}. +{"Node not found","Нодът не е намерен"}. +{"Node ~p","Нод ~p"}. +{"Nodeprep has failed","Nodeprep е неуспешен"}. +{"Nodes","Нодове"}. +{"Node","Нод"}. +{"None","Нито един"}. +{"Not allowed","Не е разрешено"}. +{"Not Found","Не е намерен"}. +{"Not subscribed","Няма абонамент"}. +{"Notify subscribers when items are removed from the node","Уведоми абонатите, когато елементите бъдат премахнати от нода"}. +{"Notify subscribers when the node configuration changes","Уведоми абонатите, когато конфигурацията на нода се промени"}. +{"Notify subscribers when the node is deleted","Уведоми абонатите, когато нодът бъде изтрит"}. +{"November","Ноември"}. +{"Number of answers required","Брой на необходимите отговори"}. +{"Number of occupants","Брой участници"}. +{"Number of Offline Messages","Брой офлайн съобщения"}. +{"Number of online users","Брой онлайн потребители"}. +{"Number of registered users","Брой регистрирани потребители"}. +{"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","Брой секунди, след които автоматично да се изчистят елементите, или `max` за липса на конкретно ограничение, различно от наложения от сървъра максимум"}. +{"Occupants are allowed to invite others","На участниците е позволено да канят други"}. +{"Occupants are allowed to query others","Участниците могат да отправят заявки към други лица"}. +{"Occupants May Change the Subject","Участниците могат да променят темата"}. +{"October","Октомври"}. +{"OK","ДОБРЕ"}. +{"Old Password:","Стара парола:"}. +{"Online Users","Онлайн потребители"}. +{"Online","Онлайн"}. +{"Only collection node owners may associate leaf nodes with the collection","Само собственици на колекционни нодове имат право да свързват листови нодове към колекцията"}. +{"Only deliver notifications to available users","Доставяне на известия само до наличните потребители"}. +{"Only or tags are allowed","Само тагове и са разрешени"}. +{"Only element is allowed in this query","Само елементът е разрешен за тази заявка"}. +{"Only members may query archives of this room","Само членовете могат да търсят архиви на тази стая"}. +{"Only moderators and participants are allowed to change the subject in this room","Само модератори и участници имат право да променят темата в тази стая"}. +{"Only moderators are allowed to change the subject in this room","Само модераторите имат право да сменят темата в тази стая"}. +{"Only moderators are allowed to retract messages","Само модераторите имат право да оттеглят съобщения"}. +{"Only moderators can approve voice requests","Само модераторите могат да одобряват гласови заявки"}. +{"Only occupants are allowed to send messages to the conference","Само участници имат право да изпращат съобщения до конференцията"}. +{"Only occupants are allowed to send queries to the conference","Само участници имат право да изпращат запитвания до конференцията"}. +{"Only publishers may publish","Само издателите могат да публикуват"}. +{"Only service administrators are allowed to send service messages","Само администраторите на услуги имат право да изпращат системни съобщения"}. +{"Only those on a whitelist may associate leaf nodes with the collection","Само тези от списъка с позволени могат да свързват листови нодове с колекцията"}. +{"Only those on a whitelist may subscribe and retrieve items","Само тези от списъка с позволени могат да се абонират и да извличат елементи"}. +{"Organization Name","Име на организацията"}. +{"Organization Unit","Отдел"}. +{"Other Modules Available:","Други налични модули:"}. +{"Outgoing s2s Connections","Изходящи s2s връзки"}. +{"Owner privileges required","Изискват се привилегии на собственик"}. +{"Packet relay is denied by service policy","Предаването на пакети е отказано от политиката на услугата"}. +{"Participant ID","ID на участник"}. +{"Participant","Участник"}. +{"Password Verification","Проверка на паролата"}. +{"Password Verification:","Проверка на паролата:"}. +{"Password","Парола"}. +{"Password:","Парола:"}. +{"Path to Dir","Път към директория"}. +{"Path to File","Път до файл"}. +{"Payload semantic type information","Информация за семантичен тип полезен товар"}. +{"Period: ","Период: "}. +{"Persist items to storage","Запазване на елементите в хранилището"}. +{"Persistent","Постоянен"}. +{"Ping query is incorrect","Заявката за пинг е неправилна"}. +{"Ping","Пинг"}. +{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Обърнете внимание, че тези опции ще направят резервно копие само на вградената (Mnesia) база данни. Ако използвате модула ODBC, трябва да направите резервно копие на SQL базата данни отделно."}. +{"Please, wait for a while before sending new voice request","Моля, изчакайте известно време, преди да изпратите нова заявка за гласова връзка"}. +{"Pong","Понг"}. +{"Possessing 'ask' attribute is not allowed by RFC6121","Притежаването на атрибут 'ask' не е разрешено от RFC6121"}. +{"Present real Jabber IDs to","Покажи истински Jabber ID-та на"}. +{"Previous session not found","Предишната сесия не е намерена"}. +{"Previous session PID has been killed","PID от предишната сесия е унищожен"}. +{"Previous session PID has exited","Предишният PID на сесията е излязъл"}. +{"Previous session PID is dead","PID от предишната сесия не съществува"}. +{"Previous session timed out","Времето на предишната сесия изтече"}. +{"private, ","частна, "}. +{"Public","Публичен"}. +{"Publish model","Модел за публикуване"}. +{"Publish-Subscribe","Публикуване-Абониране"}. +{"PubSub subscriber request","Заявка от абонат за PubSub"}. +{"Purge all items when the relevant publisher goes offline","Изчисти всички елементи, когато съответният публикуващ премине в режим офлайн"}. +{"Push record not found","Push записът не е намерен"}. +{"Queries to the conference members are not allowed in this room","В тази стая не се допускат запитвания към членовете на конференцията"}. +{"Query to another users is forbidden","Заявка към други потребители е забранена"}. +{"RAM and disc copy","Копие в RAM и на диск"}. +{"RAM copy","Копие в RAM"}. +{"Really delete message of the day?","Наистина ли желаете да изтриете съобщението на деня?"}. +{"Receive notification from all descendent nodes","Получаване на известие от всички низходящи нодове"}. +{"Receive notification from direct child nodes only","Получаване на известия само от директни подчинени нодове"}. +{"Receive notification of new items only","Получаване на известия само за нови елементи"}. +{"Receive notification of new nodes only","Получаване на известия само за нови нодове"}. +{"Recipient is not in the conference room","Получателят не е в конферентната стая"}. +{"Register an XMPP account","Регистрирай XMPP акаунт"}. +{"Register","Регистрирай"}. +{"Remote copy","Отдалечено копие"}. +{"Remove a hat from a user","Премахни шапка от потребител"}. +{"Remove User","Премахни потребител"}. +{"Replaced by new connection","Заменен от нова връзка"}. +{"Request has timed out","Времето за заявка изтече"}. +{"Request is ignored","Заявката е игнорирано"}. +{"Requested role","Заявена роля"}. +{"Resources","Ресурси"}. +{"Restart Service","Рестартирай услугата"}. +{"Restore Backup from File at ","Възстанови резервно копие от файл в "}. +{"Restore binary backup after next ejabberd restart (requires less memory):","Възстановяване на бинарно копие след следващото рестартиране на ejabberd (изисква по-малко памет):"}. +{"Restore binary backup immediately:","Възстанови незабавно двоично копие:"}. +{"Restore plain text backup immediately:","Възстановете незабавно копие от обикновен текст:"}. +{"Restore","Възстанови"}. +{"Roles and Affiliations that May Retrieve Member List","Роли и принадлежности, които могат да извличат списък с членове"}. +{"Roles for which Presence is Broadcasted","Роли, за които се излъчва присъствие"}. +{"Roles that May Send Private Messages","Роли, които могат да изпращат лични съобщения"}. +{"Room Configuration","Конфигурация на стаята"}. +{"Room creation is denied by service policy","Създаването на стая е отказано поради политика на услугата"}. +{"Room description","Описание на стаята"}. +{"Room Occupants","Участници в стаята"}. +{"Room terminates","Стаята се прекратява"}. +{"Room title","Заглавие на стаята"}. +{"Roster groups allowed to subscribe","Групи от списъци с контакти, на които е разрешено да се абонират"}. +{"Roster size","Размер на списъка с контакти"}. +{"Running Nodes","Работещи нодове"}. +{"~s invites you to the room ~s","~s ви кани в стая ~s"}. +{"Saturday","Събота"}. +{"Search from the date","Търси от дата"}. +{"Search Results for ","Резултати от търсенето за "}. +{"Search the text","Търси текста"}. +{"Search until the date","Търси до дата"}. +{"Search users in ","Търси потребители в "}. +{"Send announcement to all online users on all hosts","Изпрати съобщение до всички онлайн потребители на всички хостове"}. +{"Send announcement to all online users","Изпрати съобщение до всички онлайн потребители"}. +{"Send announcement to all users on all hosts","Изпрати съобщение до всички потребители на всички хостове"}. +{"Send announcement to all users","Изпрати съобщение до всички потребители"}. +{"September","Септември"}. +{"Server:","Сървър:"}. +{"Service list retrieval timed out","Времето за изчакване на извличането на списъка с услуги изтече"}. +{"Session state copying timed out","Времето за изчакване на копирането на състоянието на сесията изтече"}. +{"Set message of the day and send to online users","Задай съобщение на деня и го изпрати на онлайн потребителите"}. +{"Set message of the day on all hosts and send to online users","Задавай съобщение на деня на всички хостове и изпрати на онлайн потребителите"}. +{"Shared Roster Groups","Споделени групи от списъци с контакти"}. +{"Show Integral Table","Покажи интегрална таблица"}. +{"Show Occupants Join/Leave","Покажи участници Влязъл/Напускнал"}. +{"Show Ordinary Table","Покажи обикновена таблица"}. +{"Shut Down Service","Изключи услугата"}. +{"SOCKS5 Bytestreams","SOCKS5 байтови потоци"}. +{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Някои XMPP клиенти могат да съхраняват паролата Ви в компютъра, но от съображения за сигурност трябва да го правите само на личния си компютър."}. +{"Sources Specs:","Спецификации на източниците:"}. +{"Specify the access model","Задай модела за достъп"}. +{"Specify the event message type","Задай типа на съобщението за събитие"}. +{"Specify the publisher model","Задайте модела на публикуващия"}. +{"Stanza id is not valid","Невалидно ID на строфата"}. +{"Stanza ID","ID на строфа"}. +{"Statically specify a replyto of the node owner(s)","Статично задаване на replyto на собственика(ците) на нода"}. +{"Stopped Nodes","Спрени нодове"}. +{"Store binary backup:","Запази бинарен архив:"}. +{"Store plain text backup:","Запази архив като обикновен текст:"}. +{"Stream management is already enabled","Управлението на потока вече е активирано"}. +{"Stream management is not enabled","Управлението на потока не е активирано"}. +{"Subject","Тема"}. +{"Submitted","Изпратено"}. +{"Subscriber Address","Адрес на абоната"}. +{"Subscribers may publish","Абонатите могат да публикуват"}. +{"Subscription requests must be approved and only subscribers may retrieve items","Заявките за абонамент трябва да бъдат одобрени и само абонатите могат да извличат елементи"}. +{"Subscriptions are not allowed","Абонаментите не са разрешени"}. +{"Sunday","Неделя"}. +{"Text associated with a picture","Текст, свързан със снимка"}. +{"Text associated with a sound","Текст, свързан със звук"}. +{"Text associated with a video","Текст, свързан с видео"}. +{"Text associated with speech","Текст, свързан с реч"}. +{"That nickname is already in use by another occupant","Този псевдоним вече се използва от друг участник"}. +{"That nickname is registered by another person","Този псевдоним е регистриран от друго лице"}. +{"The account already exists","Профилът вече съществува"}. +{"The account was not unregistered","Профилът не е дерегистриран"}. +{"The body text of the last received message","Текстът на последното получено съобщение"}. +{"The CAPTCHA is valid.","CAPTCHA предизвикателството е валидно."}. +{"The CAPTCHA verification has failed","Проверката на CAPTCHA предизвикателството е неуспешна"}. +{"The captcha you entered is wrong","Въведеният captcha код е грешен"}. +{"The child nodes (leaf or collection) associated with a collection","Дъщерните нодове (листови или колекция), свързани с колекция"}. +{"The collections with which a node is affiliated","Колекциите, с които даден нод е свързан"}. +{"The DateTime at which a leased subscription will end or has ended","Датата и часът, на който абонамент ще приключи или е приключил"}. +{"The datetime when the node was created","Датата, когато нодът е бил създаден"}. +{"The default language of the node","Езикът по подразбиране на нода"}. +{"The feature requested is not supported by the conference","Исканата функция не се поддържа от конференцията"}. +{"The JID of the node creator","JID на създателя на нода"}. +{"The JIDs of those to contact with questions","JID на лицата, с които да се свържете при въпроси"}. +{"The JIDs of those with an affiliation of owner","JID на лицата с принадлежност на собственик"}. +{"The JIDs of those with an affiliation of publisher","JID-та на лица с принадлежност към публикуващи"}. +{"The list of all online users","Списък на всички онлайн потребители"}. +{"The list of all users","Списък на всички потребители"}. +{"The list of JIDs that may associate leaf nodes with a collection","Списъкът с JID, които могат да асоциират листови нодове с колекция"}. +{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","Максимален брой подчинени нодове, които могат да бъдат свързани с колекция, или `max` за липса на конкретен лимит, различен от наложения от сървъра максимум"}. +{"The minimum number of milliseconds between sending any two notification digests","Минималният брой милисекунди между изпращането на две извадки на известия"}. +{"The name of the node","Името на нода"}. +{"The node is a collection node","Нодът е от тип колекция"}. +{"The node is a leaf node (default)","Нодът е листов (по подразбиране)"}. +{"The NodeID of the relevant node","NodeID на съответния нод"}. +{"The number of pending incoming presence subscription requests","Броят на чакащите входящи заявки за абонамент за присъствие"}. +{"The number of subscribers to the node","Бротят абонати на нода"}. +{"The number of unread or undelivered messages","Броят непрочетени или недоставени съобщения"}. +{"The password contains unacceptable characters","Паролата съдържа недопустими символи"}. +{"The password is too weak","Паролата е твърде слаба"}. +{"the password is","паролата е"}. +{"The password of your XMPP account was successfully changed.","Паролата на вашия XMPP профил беше успешно променена."}. +{"The password was not changed","Паролата не е променена"}. +{"The passwords are different","Паролите са различни"}. +{"The presence states for which an entity wants to receive notifications","Състояния на присъствие, за които даден субект желае да получава известия"}. +{"The query is only allowed from local users","Заявката е разрешена само за локални потребители"}. +{"The query must not contain elements","Заявката не може да съдържа елементи "}. +{"The room subject can be modified by participants","Темата на стаята може да бъде променяна от участниците"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","Информацията за семантичния тип данни на нода, обикновено зададена от пространството на имената на прикачените данни (ако има такива)"}. +{"The sender of the last received message","Подателят на последното получено съобщение"}. +{"The stanza MUST contain only one element, one element, or one element","Строфата ТРЯБВА да съдържа само един елемент, един елемент или един елемент"}. +{"The subscription identifier associated with the subscription request","Идентификаторът на абонамента, свързан със заявката за абонамент"}. +{"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","URL адрес на XSL трансформацията, която може да се приложи към прикачените данни, за да се генерира подходящ елемент от тялото на съобщението."}. +{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","URL адрес на XSL трансформацията, която може да се приложи към формата на прикачените данни, за да се генерира валиден резултат от Data Forms, който клиентът може да покаже с помощта на общ механизъм за визуализация на Data Forms"}. +{"There was an error changing the password: ","Възникна грешка при промяна на паролата: "}. +{"There was an error creating the account: ","Възникна грешка при създаването на профила: "}. +{"There was an error deleting the account: ","Възникна грешка при изтриването на профила: "}. +{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Не е чувствително към регистъра (главни и малки букви): \"macbeth\"е същото като \"MacBeth\"и \"Macbeth\"."}. +{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Тази страница позволява регистриране на XMPP профил на този сървър. Вашият JID (Jabber ID) ще бъде във формата: username@server. Моля, прочетете внимателно инструкциите, за да попълните правилно полетата."}. +{"This page allows to unregister an XMPP account in this XMPP server.","Тази страница позволява премахване на XMPP профил от този сървър."}. +{"This room is not anonymous","Тази стая не е анонимна"}. +{"This service can not process the address: ~s","Тази услуга не може да обработи адреса: ~s"}. +{"Thursday","Четвъртък"}. +{"Time delay","Закъснение"}. +{"Timed out waiting for stream resumption","Времето за изчакване за възобновяване на потока изтече"}. +{"To register, visit ~s","За да се регистрирате, посетете ~s"}. +{"To ~ts","До ~ts"}. +{"Token TTL","Токен TTL"}. +{"Too many active bytestreams","Твърде много активни \"bytestreams\" потоци"}. +{"Too many CAPTCHA requests","Твърде много CAPTCHA заявки"}. +{"Too many child elements","Твърде много дъщерни елементи"}. +{"Too many elements","Твърде много елементи"}. +{"Too many elements","Твърде много елементи"}. +{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Твърде много (~p) неуспешни опити за удостоверявания от този IP адрес (~s). Адресът ще бъде деблокиран в ~s UTC"}. +{"Too many receiver fields were specified","Посочени са твърде много полета за получател"}. +{"Too many unacked stanzas","Твърде много непотвърдени строфи"}. +{"Too many users in this conference","Твърде много потребители в тази конференция"}. +{"Traffic rate limit is exceeded","Лимитът за трафик е надвишен"}. +{"~ts's MAM Archive","~ts's MAM архив"}. +{"~ts's Offline Messages Queue","Офлайн съобщения на ~ts"}. +{"Tuesday","Вторник"}. +{"Unable to generate a CAPTCHA","Не може да се генерира CAPTCHA"}. +{"Unable to register route on existing local domain","Не може да се регистрира маршрут в съществуващ локален домейн"}. +{"Unauthorized","Неоторизиран"}. +{"Unexpected action","Неочаквано действие"}. +{"Unexpected error condition: ~p","Неочаквано състояние на грешка: ~p"}. +{"Uninstall","Деинсталирай"}. +{"Unregister an XMPP account","Дерегистрирай XMPP профил"}. +{"Unregister","Дерегистрирай"}. +{"Unsupported element","Неподдържан елемент "}. +{"Unsupported version","Неподдържана версия"}. +{"Update message of the day (don't send)","Актуализирай съобщението на деня (не изпращай)"}. +{"Update message of the day on all hosts (don't send)","Актуализирай съобщението на деня на всички хостове (не изпращай)"}. +{"Update specs to get modules source, then install desired ones.","Актуализирайте спецификациите, за да получите източник на модули, след което инсталирайте желаните."}. +{"Update Specs","Актуализирай спецификациите"}. +{"Updating the vCard is not supported by the vCard storage backend","Актуализирането на vCard не се поддържа от настройката за съхранение на vCard"}. +{"Upgrade","Обнови"}. +{"URL for Archived Discussion Logs","URL адрес за дневници на архивирани дискусии"}. +{"User already exists","Потребителят вече съществува"}. +{"User (jid)","Потребител (jid)"}. +{"User JID","Потребител JID"}. +{"User Management","Управление на потребители"}. +{"User not allowed to perform an IQ set on another user's vCard.","Потребителят не може да извършва IQ настрийка на vCard на друг потребител."}. +{"User removed","Потребителят е премахнат"}. +{"User session not found","Потребителската сесия не е намерена"}. +{"User session terminated","Потребителската сесия е прекратена"}. +{"User ~ts","Потребител ~ts"}. +{"Username:","Потребителско име:"}. +{"Users are not allowed to register accounts so quickly","Не е разрешено потребителите да регистрират профили толкова бързо"}. +{"Users Last Activity","Последна активност на потребителите"}. +{"Users","Потребители"}. +{"User","Потребител"}. +{"Value 'get' of 'type' attribute is not allowed","Стойността 'get' на атрибут 'type' не е разрешена"}. +{"Value of '~s' should be boolean","Стойността на '~s' трябва да е булева"}. +{"Value of '~s' should be datetime string","Стойността на '~s' трябва да бъде низ за дата и час"}. +{"Value of '~s' should be integer","Стойността на '~s' трябва да бъде цяло число"}. +{"Value 'set' of 'type' attribute is not allowed","Стойността 'set' на атрибут 'type' не е разрешена"}. +{"vCard User Search","vCard търсене на потребител"}. +{"View joined MIX channels","Вижте присъединените MIX канали"}. +{"Virtual Hosts","Виртуални хостове"}. +{"Visitors are not allowed to change their nicknames in this room","Посетителите нямат право да променят псевдонимите си в тази стая"}. +{"Visitors are not allowed to send messages to all occupants","На посетителите не е разрешено да изпращат съобщения до всички участници"}. +{"Visitor","Посетител"}. +{"Voice requests are disabled in this conference","Гласовите обаждания са деактивирани в тази конференция"}. +{"Voice request","Заявка за гласово обаждане"}. +{"Wednesday","Сряда"}. +{"When a new subscription is processed and whenever a subscriber comes online","Когато се обработва нов абонамент и всеки път, когато абонат се появи онлайн"}. +{"When a new subscription is processed","Когато се обработва нов абонамент"}. +{"When to send the last published item","Кога да изпратите последния публикуван елемент"}. +{"Whether an entity wants to receive an XMPP message body in addition to the payload format","Дали даден обект иска да получи тяло на XMPP съобщение в допълнение към формата на полезен товар"}. +{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","Дали даден обект желае да получава обобщения за известия или всички известия поотделно"}. +{"Whether an entity wants to receive or disable notifications","Дали даден обект желае да получава или деактивира известия"}. +{"Whether owners or publisher should receive replies to items","Дали собствениците или публикуващите трябва да получават отговори на елементи"}. +{"Whether the node is a leaf (default) or a collection","Дали нодът е листов (по подразбиране) или колекция"}. +{"Whether to allow subscriptions","Дали да се разрешат абонаменти"}. +{"Whether to make all subscriptions temporary, based on subscriber presence","Дали всички абонаменти да бъдат временни въз основа на присъствието на абонат"}. +{"Whether to notify owners about new subscribers and unsubscribes","Дали да се уведомяват собствениците за нови абонати и откази от абонамент"}. +{"Who can send private messages","Кой може да изпраща лични съобщения"}. +{"Who may associate leaf nodes with a collection","Кой може да асоциира листови нодове с колекция"}. +{"Wrong parameters in the web formulary","Грешни параметри в уеб формуляра"}. +{"Wrong xmlns","Грешен xmlns"}. +{"XMPP Account Registration","Регистриране на XMPP профил"}. +{"XMPP Domains","XMPP домейни"}. +{"XMPP Show Value of Away","XMPP покажи стойност на Отсъства"}. +{"XMPP Show Value of Chat","XMPP покажи стойност на Чат"}. +{"XMPP Show Value of DND (Do Not Disturb)","XMPP покажи стойност на DND (Не ме безпокой)"}. +{"XMPP Show Value of XA (Extended Away)","XMPP покажи стойност на Продължително отсъствие"}. +{"XMPP URI of Associated Publish-Subscribe Node","XMPP URI на асоцииран Publish-Subscribe нод"}. +{"You are being removed from the room because of a system shutdown","Премахнати сте от стаята поради изключване на системата"}. +{"You are not allowed to send private messages","Нямате право да изпращате лични съобщения"}. +{"You are not joined to the channel","Не сте присъединени към канала"}. +{"You can later change your password using an XMPP client.","По-късно можете да промените паролата си с помощта на XMPP клиент."}. +{"You have been banned from this room","Достъпът ви до тази стая е забранен"}. +{"You have joined too many conferences","Присъединили сте се към твърде много конференции"}. +{"You must fill in field \"Nickname\" in the form","Трябва да попълните полето \"Псевдоним\" във формата"}. +{"You need a client that supports x:data and CAPTCHA to register","За да се регистрирате Ви е нужен клиент, който поддържа x:data и CAPTCHA"}. +{"You need a client that supports x:data to register the nickname","За да регистрирате псевдонима, Ви е необходим клиент, който поддържа x:data"}. +{"You need an x:data capable client to search","За да търсите, Ви е нужен клиент, който поддържа x:data"}. +{"Your active privacy list has denied the routing of this stanza.","Вашият активен списък за поверителност отказа маршрутизирането на тази строфа."}. +{"Your contact offline message queue is full. The message has been discarded.","Достигнат е максималният брой офлайн съобщения за вашия контакт. Съобщението е отхвърлено."}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Вашата заявка за абонамент и/или съобщения до ~s са блокирани. За да отблокирате заявката си за абонамент, посетете ~s"}. +{"Your XMPP account was successfully registered.","Вашият XMPP акаунт, беше регистриран успешно."}. +{"Your XMPP account was successfully unregistered.","Вашият XMPP акаунт, беше успешно дерегистриран."}. +{"You're not allowed to create nodes","Нямате право да създавате нодове"}. diff --git a/priv/msgs/ca.msg b/priv/msgs/ca.msg index 46ee1afbc..951430a9b 100644 --- a/priv/msgs/ca.msg +++ b/priv/msgs/ca.msg @@ -1,23 +1,31 @@ -%% -*- coding: latin-1 -*- -{"Accept","Acceptar"}. -{"Access Configuration","Configuració d'accesos"}. -{"Access Control List Configuration","Configuració de la Llista de Control d'Accés"}. -{"Access Control Lists","Llista de Control d'Accés"}. -{"Access control lists","Llistes de Control de Accés"}. -{"Access denied by service policy","Accés denegat per la política del servei"}. -{"Access rules","Regles d'accés"}. -{"Access Rules","Regles d'Accés"}. -{"Action on user","Acció en l'usuari"}. -{"Add Jabber ID","Afegir Jabber ID"}. -{"Add New","Afegir nou"}. -{"Add User","Afegir usuari"}. -{"Administration","Administració"}. -{"Administration of ","Administració de "}. -{"Administrator privileges required","Es necessita tenir privilegis d'administrador"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" (Add * to the end of field to match substring)"," (Afegix * al final d'un camp per a buscar subcadenes)"}. +{" has set the subject to: "," ha posat el tema: "}. +{"# participants","# participants"}. +{"A description of the node","Una descripció del node"}. {"A friendly name for the node","Un nom per al node"}. +{"A password is required to enter this room","Es necessita contrasenya per a entrar en aquesta sala"}. +{"A Web Page","Una Pàgina Web"}. +{"Accept","Acceptar"}. +{"Access denied by service policy","Accés denegat per la política del servei"}. +{"Access model","Model d'Accés"}. +{"Account doesn't exist","El compte no existeix"}. +{"Action on user","Acció en l'usuari"}. +{"Add a hat to a user","Afegir un barret a un usuari"}. +{"Add User","Afegir usuari"}. +{"Administration of ","Administració de "}. +{"Administration","Administració"}. +{"Administrator privileges required","Es necessita tenir privilegis d'administrador"}. {"All activity","Tota l'activitat"}. -{"Allow this Jabber ID to subscribe to this pubsub node?","Permetre que aquesta Jabber ID es puga subscriure a aquest node pubsub"}. -{"Allow users to change the subject","Permetre que els usuaris canviin el tema"}. +{"All Users","Tots els usuaris"}. +{"Allow subscription","Permetre subscripció"}. +{"Allow this Jabber ID to subscribe to this pubsub node?","Permetre que aquesta Jabber ID es puga subscriure a aquest node pubsub?"}. +{"Allow this person to register with the room?","Permetre a esta persona registrar-se a la sala?"}. +{"Allow users to change the subject","Permetre que els usuaris canviïn el tema"}. {"Allow users to query other users","Permetre que els usuaris fagen peticions a altres usuaris"}. {"Allow users to send invites","Permetre que els usuaris envien invitacions"}. {"Allow users to send private messages","Permetre que els usuaris envien missatges privats"}. @@ -25,21 +33,49 @@ {"Allow visitors to send private messages to","Permetre als visitants enviar missatges privats a"}. {"Allow visitors to send status text in presence updates","Permetre als visitants enviar text d'estat en les actualitzacions de presència"}. {"Allow visitors to send voice requests","Permetre als visitants enviar peticions de veu"}. -{"All Users","Tots els usuaris"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","Un grup LDAP associat que defineix membresía a la sala; esto deuria ser un Nombre Distinguible de LDAP, d'acord amb una definició de grup específica d'implementació o de instal·lació."}. {"Announcements","Anuncis"}. -{"anyone","qualsevol"}. -{"A password is required to enter this room","Es necessita contrasenya per a entrar en aquesta sala"}. +{"Answer associated with a picture","Resposta associada amb una imatge"}. +{"Answer associated with a video","Resposta associada amb un vídeo"}. +{"Answer associated with speech","Resposta associada amb un parlament"}. +{"Answer to a question","Resposta a una pregunta"}. +{"Anyone in the specified roster group(s) may subscribe and retrieve items","Qualsevol en el grup de contactes especificat pot subscriure's i recuperar elements"}. +{"Anyone may associate leaf nodes with the collection","Qualsevol pot associar nodes fulla amb la col·lecció"}. +{"Anyone may publish","Qualsevol pot publicar"}. +{"Anyone may subscribe and retrieve items","Qualsevol pot publicar i recuperar elements"}. +{"Anyone with a presence subscription of both or from may subscribe and retrieve items","Qualsevol amb una subscripció de presencia de 'both' o 'from' pot subscriure's i publicar elements"}. +{"Anyone with Voice","Qualsevol amb Veu"}. +{"Anyone","Qualsevol"}. +{"API Commands","Comandaments API"}. {"April","Abril"}. +{"Arguments","Arguments"}. +{"Attribute 'channel' is required for this request","L'atribut 'channel' és necessari per a aquesta petició"}. +{"Attribute 'id' is mandatory for MIX messages","L'atribut 'id' es necessari per a missatges MIX"}. +{"Attribute 'jid' is not allowed here","L'atribut 'jid' no està permès ací"}. +{"Attribute 'node' is not allowed here","L'atribut 'node' no està permès ací"}. +{"Attribute 'to' of stanza that triggered challenge","L'atribut 'to' del paquet que va disparar la comprovació"}. {"August","Agost"}. -{"Backup","Guardar còpia de seguretat"}. +{"Automatic node creation is not enabled","La creació automàtica de nodes no està activada"}. {"Backup Management","Gestió de còpia de seguretat"}. {"Backup of ~p","Còpia de seguretat de ~p"}. {"Backup to File at ","Desar còpia de seguretat a fitxer en "}. +{"Backup","Guardar còpia de seguretat"}. {"Bad format","Format erroni"}. {"Birthday","Aniversari"}. +{"Both the username and the resource are required","Es requereixen tant el nom d'usuari com el recurs"}. +{"Bytestream already activated","El Bytestream ja està activat"}. +{"Cannot remove active list","No es pot eliminar la llista activa"}. +{"Cannot remove default list","No es pot eliminar la llista per defecte"}. {"CAPTCHA web page","Pàgina web del CAPTCHA"}. +{"Challenge ID","ID de la comprovació"}. {"Change Password","Canviar Contrasenya"}. {"Change User Password","Canviar Contrasenya d'Usuari"}. +{"Changing password is not allowed","No està permès canviar la contrasenya"}. +{"Changing role/affiliation is not allowed","No està permès canviar el rol/afiliació"}. +{"Channel already exists","El canal ja existeix"}. +{"Channel does not exist","El canal no existeix"}. +{"Channel JID","JID del Canal"}. +{"Channels","Canals"}. {"Characters not allowed:","Caràcters no permesos:"}. {"Chatroom configuration modified","Configuració de la sala de xat modificada"}. {"Chatroom is created","La sala s'ha creat"}. @@ -48,393 +84,547 @@ {"Chatroom is stopped","La sala s'ha aturat"}. {"Chatrooms","Sales de xat"}. {"Choose a username and password to register with this server","Tria nom d'usuari i contrasenya per a registrar-te en aquest servidor"}. -{"Choose modules to stop","Selecciona mòduls a detindre"}. {"Choose storage type of tables","Selecciona el tipus d'almacenament de les taules"}. -{"Choose whether to approve this entity's subscription.","Tria si aprova aquesta entitat de subscripció"}. +{"Choose whether to approve this entity's subscription.","Tria si aproves aquesta entitat de subscripció."}. {"City","Ciutat"}. +{"Client acknowledged more stanzas than sent by server","El client ha reconegut més paquets dels que ha enviat el servidor"}. +{"Clustering","Clustering"}. {"Commands","Comandaments"}. {"Conference room does not exist","La sala de conferències no existeix"}. -{"Configuration","Configuració"}. {"Configuration of room ~s","Configuració de la sala ~s"}. -{"Connected Resources:","Recursos connectats:"}. -{"Connections parameters","Paràmetres de connexió"}. +{"Configuration","Configuració"}. +{"Contact Addresses (normally, room owner or owners)","Adreces de contacte (normalment, propietaris de la sala)"}. {"Country","Pais"}. -{"CPU Time:","Temps de CPU"}. -{"Database","Base de dades"}. -{"Database Tables at ~p","Taules de la base de dades en ~p"}. +{"Current Discussion Topic","Assumpte de discussió actual"}. +{"Database failure","Error a la base de dades"}. {"Database Tables Configuration at ","Configuració de la base de dades en "}. +{"Database","Base de dades"}. {"December","Decembre"}. {"Default users as participants","Els usuaris són participants per defecte"}. -{"Delete message of the day","Eliminar el missatge del dia"}. {"Delete message of the day on all hosts","Elimina el missatge del dis de tots els hosts"}. -{"Delete Selected","Eliminar els seleccionats"}. +{"Delete message of the day","Eliminar el missatge del dia"}. {"Delete User","Eliminar Usuari"}. {"Deliver event notifications","Entrega de notificacions d'events"}. {"Deliver payloads with event notifications","Enviar payloads junt a les notificacions d'events"}. -{"Description:","Descripció:"}. {"Disc only copy","Còpia sols en disc"}. -{"Displayed Groups:","Mostrar grups:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","No li donis la teva contrasenya a ningú, ni tan sols als administradors del servidor Jabber."}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","No li donis la teva contrasenya a ningú, ni tan sols als administradors del servidor XMPP."}. {"Dump Backup to Text File at ","Exporta còpia de seguretat a fitxer de text en "}. {"Dump to Text File","Exportar a fitxer de text"}. +{"Duplicated groups are not allowed by RFC6121","No estan permesos els grups duplicats al RFC6121"}. +{"Dynamically specify a replyto of the item publisher","Especifica dinàmicament l'adreça on respondre al publicador del element"}. {"Edit Properties","Editar propietats"}. -{"Either approve or decline the voice request.","Aprova o denega la petició de veu"}. -{"ejabberd IRC module","mòdul ejabberd IRC"}. +{"Either approve or decline the voice request.","Aprova o denega la petició de veu."}. +{"ejabberd HTTP Upload service","ejabberd - servei de HTTP Upload"}. {"ejabberd MUC module","mòdul ejabberd MUC"}. -{"ejabberd Multicast service","Servei de Multicast d'ejabberd"}. -{"ejabberd Publish-Subscribe module","Mòdul ejabberd Publicar-Subscriure"}. +{"ejabberd Multicast service","ejabberd - servei de Multicast"}. +{"ejabberd Publish-Subscribe module","ejabberd - Mòdul Publicar-Subscriure"}. {"ejabberd SOCKS5 Bytestreams module","mòdul ejabberd SOCKS5 Bytestreams"}. -{"ejabberd vCard module","Mòdul ejabberd vCard"}. -{"ejabberd Web Admin","Web d'administració del ejabberd"}. -{"Elements","Elements"}. -{"Email","Email"}. -{"Empty Rooms","Sales buides "}. +{"ejabberd vCard module","ejabberd mòdul vCard"}. +{"ejabberd Web Admin","ejabberd Web d'administració"}. +{"ejabberd","ejabberd"}. +{"Email Address","Adreça de correu"}. +{"Email","Correu"}. +{"Enable hats","Activar barrets"}. {"Enable logging","Habilitar el registre de la conversa"}. {"Enable message archiving","Activar l'emmagatzematge de missatges"}. -{"Encoding for server ~b","Codificació pel servidor ~b"}. +{"Enabling push without 'node' attribute is not supported","No està suportat activar Push sense l'atribut 'node'"}. {"End User Session","Finalitzar Sesió d'Usuari"}. -{"Enter list of {Module, [Options]}","Introdueix llista de {mòdul, [opcions]}"}. {"Enter nickname you want to register","Introdueix el sobrenom que vols registrar"}. {"Enter path to backup file","Introdueix ruta al fitxer de còpia de seguretat"}. {"Enter path to jabberd14 spool dir","Introdueix la ruta al directori de jabberd14 spools"}. {"Enter path to jabberd14 spool file","Introdueix ruta al fitxer jabberd14 spool"}. {"Enter path to text file","Introdueix ruta al fitxer de text"}. {"Enter the text you see","Introdueix el text que veus"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Introdueix el nom d'usuari i les codificacions de caràcters per a utilitzar als servidors de IRC. Apreta \"Seguent\" per veure més caps per omplir. Apreta \"Completar\" per guardar la configuració. "}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Introdueix el nom d'usuari, les codificacions de caràcters, els ports i contrasenyes per a utilitzar al connectar als servidors de IRC"}. -{"Erlang Jabber Server","Servidor Erlang Jabber"}. -{"Error","Error"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Exemple: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. +{"Erlang XMPP Server","Servidor Erlang XMPP"}. {"Exclude Jabber IDs from CAPTCHA challenge","Excloure Jabber IDs de la comprovació CAPTCHA"}. {"Export all tables as SQL queries to a file:","Exporta totes les taules a un fitxer SQL:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportar dades de tots els usuaris del servidor a arxius PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Exportar dades d'usuaris d'un host a arxius PIEFXIS (XEP-0227):"}. +{"External component failure","Error al component extern"}. +{"External component timeout","Temps esgotat al component extern"}. +{"Failed to activate bytestream","Errada al activar bytestream"}. {"Failed to extract JID from your voice request approval","No s'ha pogut extraure el JID de la teva aprovació de petició de veu"}. +{"Failed to map delegated namespace to external component","Ha fallat mapejar la delegació de l'espai de noms al component extern"}. +{"Failed to parse HTTP response","Ha fallat el processat de la resposta HTTP"}. +{"Failed to process option '~s'","Ha fallat el processat de la opció '~s'"}. {"Family Name","Cognom"}. +{"FAQ Entry","Entrada a la FAQ"}. {"February","Febrer"}. -{"Fill in fields to search for any matching Jabber User","Emplena camps per a buscar usuaris Jabber que concorden"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Emplena el formulari per a buscar usuaris Jabber. Afegix * al final d'un camp per a buscar subcadenes."}. +{"File larger than ~w bytes","El fitxer es més gran que ~w bytes"}. +{"Fill in the form to search for any matching XMPP User","Emplena camps per a buscar usuaris XMPP que concorden"}. {"Friday","Divendres"}. -{"From","De"}. -{"From ~s","De ~s"}. +{"From ~ts","De ~ts"}. +{"Full List of Room Admins","Llista completa de administradors de la sala"}. +{"Full List of Room Owners","Llista completa de propietaris de la sala"}. {"Full Name","Nom complet"}. +{"Get List of Online Users","Obté la llista d'usuaris en línia"}. +{"Get List of Registered Users","Obté la llista d'usuaris registrats"}. {"Get Number of Online Users","Obtenir Número d'Usuaris Connectats"}. {"Get Number of Registered Users","Obtenir Número d'Usuaris Registrats"}. +{"Get Pending","Obtenir Pendents"}. {"Get User Last Login Time","Obtenir la última connexió d'Usuari"}. -{"Get User Password","Obtenir Contrasenya d'usuari"}. {"Get User Statistics","Obtenir Estadístiques d'Usuari"}. +{"Given Name","Nom propi"}. {"Grant voice to this person?","Concedir veu a aquesta persona?"}. -{"Group ","Grup "}. -{"Groups","Grups"}. -{"has been banned","Has sigut banejat"}. -{"has been kicked because of an affiliation change","Has sigut expulsat a causa d'un canvi d'afiliació"}. -{"has been kicked because of a system shutdown","Has sigut expulsat perquè el sistema s'ha apagat"}. -{"has been kicked because the room has been changed to members-only","Has sigut expulsat perquè la sala ha canviat a sols membres"}. -{"has been kicked","Has sigut expulsat"}. -{" has set the subject to: "," ha posat l'assumpte: "}. -{"Host","Host"}. +{"has been banned","ha sigut bloquejat"}. +{"has been kicked because of a system shutdown","ha sigut expulsat perquè el sistema va a apagar-se"}. +{"has been kicked because of an affiliation change","ha sigut expulsat a causa d'un canvi d'afiliació"}. +{"has been kicked because the room has been changed to members-only","ha sigut expulsat perquè la sala ara és només per a membres"}. +{"has been kicked","ha sigut expulsat"}. +{"Hash of the vCard-temp avatar of this room","Hash del avatar a vCard-temp d'esta sala"}. +{"Hat title","Títol del barret"}. +{"Hat URI","URI del barret"}. +{"Hats limit exceeded","El límit de tràfic ha sigut sobrepassat"}. +{"Host unknown","Host desconegut"}. +{"HTTP File Upload","HTTP File Upload"}. +{"Idle connection","Connexió sense us"}. {"If you don't see the CAPTCHA image here, visit the web page.","Si no veus la imatge CAPTCHA açí, visita la pàgina web."}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Si vols especificar codificacions de caràcters diferents per a cada servidor IRC emplena aquesta llista amb els valors amb el format '{\"servidor irc\", \"codificació\", port, \"contrasenya\"}'. Aquest servei utilitza per defecte la codificació \"~s\", port ~p, no contrasenya."}. {"Import Directory","Importar directori"}. {"Import File","Importar fitxer"}. -{"Import user data from jabberd14 spool file:","Importar dades d'usuaris de l'arxiu de spool de jabberd14"}. +{"Import user data from jabberd14 spool file:","Importar dades d'usuaris de l'arxiu de spool de jabberd14:"}. {"Import User from File at ","Importa usuari des de fitxer en "}. {"Import users data from a PIEFXIS file (XEP-0227):","Importar dades d'usuaris des d'un arxiu PIEFXIS (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Importar dades d'usuaris del directori de spool de jabberd14:"}. {"Import Users from Dir at ","Importar usuaris des del directori en "}. {"Import Users From jabberd14 Spool Files","Importar usuaris de jabberd14"}. +{"Improper domain part of 'from' attribute","La part de domini de l'atribut 'from' es impròpia"}. {"Improper message type","Tipus de missatge incorrecte"}. -{"Incoming s2s Connections:","Connexions s2s d'entrada"}. +{"Incorrect CAPTCHA submit","El CAPTCHA proporcionat és incorrecte"}. +{"Incorrect data form","El formulari de dades és incorrecte"}. {"Incorrect password","Contrasenya incorrecta"}. -{"Invalid affiliation: ~s","Afiliació invàlida: ~s"}. -{"Invalid role: ~s","Rol invàlid: ~s"}. +{"Incorrect value of 'action' attribute","Valor incorrecte del atribut 'action'"}. +{"Incorrect value of 'action' in data form","Valor incorrecte de 'action' al formulari de dades"}. +{"Incorrect value of 'path' in data form","Valor incorrecte de 'path' al formulari de dades"}. +{"Installed Modules:","Mòduls instal·lats:"}. +{"Install","Instal·lar"}. +{"Insufficient privilege","Privilegi insuficient"}. +{"Internal server error","Error intern del servidor"}. +{"Invalid 'from' attribute in forwarded message","Atribut 'from' invàlid al missatge reenviat"}. +{"Invalid node name","Nom de node no vàlid"}. +{"Invalid 'previd' value","Valor no vàlid de 'previd'"}. +{"Invitations are not allowed in this conference","Les invitacions no estan permeses en aquesta sala de conferència"}. {"IP addresses","Adreça IP"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","Canal d'IRC (no posis la primera #)"}. -{"IRC server","Servidor d'IRC"}. -{"IRC settings","Configuració d'IRC."}. -{"IRC Transport","Transport a IRC"}. -{"IRC username","Nom d'usuari al IRC"}. -{"IRC Username","Nom d'usuari al IRC"}. {"is now known as","ara es conegut com"}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","No està permés enviar missatges d'error a la sala. El participant (~s) ha enviat un missatge d'error (~s) i ha sigut expulsat de la sala"}. -{"It is not allowed to send private messages","No està permés enviar missatges privats"}. {"It is not allowed to send private messages of type \"groupchat\"","No està permés enviar missatges del tipus \"groupchat\""}. {"It is not allowed to send private messages to the conference","No està permès l'enviament de missatges privats a la sala"}. -{"Jabber Account Registration","Registre de compte Jabber"}. {"Jabber ID","ID Jabber"}. -{"Jabber ID ~s is invalid","El Jabber ID ~s no és vàlid"}. {"January","Gener"}. -{"Join IRC channel","Entra a canal d'IRC"}. -{"joins the room","Entrar a la sala"}. -{"Join the IRC channel here.","Entra al canal d'IRC aquí."}. -{"Join the IRC channel in this Jabber ID: ~s","Entra al canal d'IRC en aquesta Jabber ID: ~s"}. +{"JID normalization denied by service policy","S'ha denegat la normalització del JID per política del servei"}. +{"JID normalization failed","Ha fallat la normalització del JID"}. +{"Joined MIX channels of ~ts","Canals MIX units de ~ts"}. +{"Joined MIX channels:","Canals MIX units:"}. +{"joins the room","entra a la sala"}. {"July","Juliol"}. {"June","Juny"}. +{"Just created","Creació recent"}. {"Last Activity","Última activitat"}. {"Last login","Últim login"}. +{"Last message","Últim missatge"}. {"Last month","Últim mes"}. {"Last year","Últim any"}. -{"leaves the room","Deixar la sala"}. -{"Listened Ports at ","Ports a la escolta en "}. -{"Listened Ports","Ports a l'escolta"}. -{"List of modules to start","Llista de mòduls a iniciar"}. -{"List of rooms","Llista de sales"}. -{"Low level update script","Script d'actualització de baix nivell"}. +{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Els bits menys significants del hash SHA-256 del text deurien ser iguals a l'etiqueta hexadecimal"}. +{"leaves the room","surt de la sala"}. +{"List of users with hats","Llista d'usuaris amb barrets"}. +{"List users with hats","Llista d'usuaris amb barrets"}. +{"Logged Out","Desconectat"}. +{"Logging","Registre"}. {"Make participants list public","Crear una llista de participants pública"}. {"Make room CAPTCHA protected","Crear una sala protegida per CAPTCHA"}. -{"Make room members-only","Crear una sala de \"només membres\""}. +{"Make room members-only","Crear una sala només per a membres"}. {"Make room moderated","Crear una sala moderada"}. {"Make room password protected","Crear una sala amb contrasenya"}. {"Make room persistent","Crear una sala persistent"}. {"Make room public searchable","Crear una sala pública"}. +{"Malformed username","Nom d'usuari mal format"}. +{"MAM preference modification denied by service policy","Se t'ha denegat la modificació de la preferència de MAM per política del servei"}. {"March","Març"}. -{"Maximum Number of Occupants","Número màxim d'ocupants"}. -{"Max # of items to persist","Màxim # d'elements que persistixen"}. +{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","Màxim # d'elements a persistir, o `max` per a no tindre altre límit més que el màxim imposat pel servidor"}. {"Max payload size in bytes","Màxim tamany del payload en bytes"}. +{"Maximum file size","Mida màxima de fitxer"}. +{"Maximum Number of History Messages Returned by Room","Numero màxim de missatges de l'historia que retorna la sala"}. +{"Maximum number of items to persist","Número màxim d'elements que persistixen"}. +{"Maximum Number of Occupants","Número màxim d'ocupants"}. {"May","Maig"}. {"Membership is required to enter this room","Necessites ser membre d'aquesta sala per a poder entrar"}. -{"Members:","Membre:"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Memoritza la teva contrasenya, o escriu-la en un paper guardat a un lloc segur.A Jabber no hi ha una forma automatitzada de recuperar la teva contrasenya si la oblides."}. -{"Memory","Memòria"}. +{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Memoritza la teva contrasenya, o escriu-la en un paper guardat a un lloc segur. A XMPP no hi ha una forma automatitzada de recuperar la teva contrasenya si la oblides."}. +{"Mere Availability in XMPP (No Show Value)","Simplement disponibilitat a XMPP (sense valor de 'show')"}. {"Message body","Missatge"}. +{"Message not found in forwarded payload","Missatge no trobat al contingut reenviat"}. +{"Messages from strangers are rejected","Els missatges de desconeguts son rebutjats"}. +{"Messages of type headline","Missatges de tipus titular"}. +{"Messages of type normal","Missatges de tipus normal"}. {"Middle Name","Segon nom"}. {"Minimum interval between voice requests (in seconds)","Interval mínim entre peticions de veu (en segons)"}. -{"Moderator","Moderador"}. {"Moderator privileges required","Es necessita tenir privilegis de moderador"}. -{"moderators only","només moderadors"}. -{"Modified modules","Mòduls modificats"}. -{"Module","Mòdul"}. -{"Modules at ~p","Mòduls en ~p"}. -{"Modules","Mòduls"}. +{"Moderator","Moderador"}. +{"Moderators Only","Només moderadors"}. +{"Module failed to handle the query","El modul ha fallat al gestionar la petició"}. {"Monday","Dilluns"}. {"Multicast","Multicast"}. +{"Multiple elements are not allowed by RFC6121","No estan permesos múltiples elements per RFC6121"}. {"Multi-User Chat","Multi-Usuari Converses"}. -{"Name:","Nom:"}. {"Name","Nom"}. +{"Natural Language for Room Discussions","Llengua natural per a les discussions a les sales"}. +{"Natural-Language Room Name","Nom de la sala a la seua llengua natural"}. +{"Neither 'jid' nor 'nick' attribute found","No s'han trobat els atributs 'jid' ni 'nick'"}. +{"Neither 'role' nor 'affiliation' attribute found","No s'han trobat els atributs 'role' ni 'affiliation'"}. {"Never","Mai"}. {"New Password:","Nova Contrasenya:"}. +{"Nickname can't be empty","El sobrenom no pot estar buit"}. {"Nickname Registration at ","Registre del sobrenom en "}. {"Nickname ~s does not exist in the room","El sobrenom ~s no existeix a la sala"}. {"Nickname","Sobrenom"}. -{"nobody","ningú"}. +{"No address elements found","No s'han trobat elements d'adreces ('address')"}. +{"No addresses element found","No s'ha trobat l'element d'adreces ('addresses')"}. +{"No 'affiliation' attribute found","No s'ha trobat l'atribut 'affiliation'"}. +{"No available resource found","No s'ha trobat un recurs disponible"}. {"No body provided for announce message","No hi ha proveedor per al missatge anunci"}. +{"No child elements found","No s'han trobat subelements"}. +{"No data form found","No s'ha trobat el formulari de dades"}. {"No Data","No hi ha dades"}. +{"No features available","No n'hi ha característiques disponibles"}. +{"No element found","No s'ha trobat cap element "}. +{"No hook has processed this command","Cap event ha processat este comandament"}. +{"No info about last activity found","No s'ha trobat informació de l'ultima activitat"}. +{"No 'item' element found","No s'ha trobat cap element 'item'"}. +{"No items found in this query","En aquesta petició no s'ha trobat cap element"}. +{"No limit","Sense Llímit"}. +{"No module is handling this query","Cap element està manegant esta petició"}. +{"No node specified","No s'ha especificat node"}. +{"No 'password' found in data form","No s'ha trobat 'password' al formulari de dades"}. +{"No 'password' found in this query","No s'ha trobat 'password' en esta petició"}. +{"No 'path' found in data form","No s'ha trobat 'path' en el formulari de dades"}. +{"No pending subscriptions found","No s'han trobat subscripcions pendents"}. +{"No privacy list with this name found","No s'ha trobat cap llista de privacitat amb aquest nom"}. +{"No private data found in this query","No s'ha trobat dades privades en esta petició"}. +{"No running node found","No s'ha trobat node en marxa"}. +{"No services available","No n'hi ha serveis disponibles"}. +{"No statistics found for this item","No n'hi ha estadístiques disponibles per a aquest element"}. +{"No 'to' attribute found in the invitation","No s'ha trobat l'atribut 'to' en la invitació"}. +{"Nobody","Ningú"}. +{"Node already exists","El node ja existeix"}. {"Node ID","ID del Node"}. +{"Node index not found","Index de node no trobat"}. {"Node not found","Node no trobat"}. {"Node ~p","Node ~p"}. +{"Node","Node"}. +{"Nodeprep has failed","Ha fallat Nodeprep"}. {"Nodes","Nodes"}. -{"No limit","Sense Llímit"}. {"None","Cap"}. -{"No resource provided","Recurs no disponible"}. +{"Not allowed","No permès"}. {"Not Found","No Trobat"}. +{"Not subscribed","No subscrit"}. {"Notify subscribers when items are removed from the node","Notificar subscriptors quan els elements són eliminats del node"}. {"Notify subscribers when the node configuration changes","Notificar subscriptors quan canvia la configuració del node"}. {"Notify subscribers when the node is deleted","Notificar subscriptors quan el node és eliminat"}. {"November","Novembre"}. +{"Number of answers required","Número de respostes requerides"}. {"Number of occupants","Número d'ocupants"}. +{"Number of Offline Messages","Número de missatges offline"}. {"Number of online users","Número d'usuaris connectats"}. {"Number of registered users","Número d'Usuaris Registrats"}. +{"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","Número de segons després dels quals es purgaran automàticament elements, o `max` per a no tindre altre límit més que el màxim imposat pel servidor"}. +{"Occupants are allowed to invite others","Els ocupants poden invitar a altres"}. +{"Occupants are allowed to query others","Els ocupants poden enviar peticions a altres"}. +{"Occupants May Change the Subject","Els ocupants poden canviar el Tema"}. {"October","Octubre"}. -{"Offline Messages:","Missatges fora de línia:"}. -{"Offline Messages","Missatges offline"}. {"OK","Acceptar"}. {"Old Password:","Antiga contrasenya:"}. -{"Online","Connectat"}. {"Online Users","Usuaris conectats"}. -{"Online Users:","Usuaris en línia:"}. +{"Online","Connectat"}. +{"Only collection node owners may associate leaf nodes with the collection","Només els propietaris de la col·lecció de nodes poden associar nodes fulla amb la col·lecció"}. {"Only deliver notifications to available users","Sols enviar notificacions als usuaris disponibles"}. +{"Only or tags are allowed","Només es permeten etiquetes o "}. +{"Only element is allowed in this query","En esta petició només es permet l'element "}. {"Only members may query archives of this room","Només membres poden consultar l'arxiu de missatges d'aquesta sala"}. -{"Only moderators and participants are allowed to change the subject in this room","Només els moderadors i participants poden canviar l'assumpte d'aquesta sala"}. -{"Only moderators are allowed to change the subject in this room","Només els moderadors poden canviar l'assumpte d'aquesta sala"}. +{"Only moderators and participants are allowed to change the subject in this room","Només els moderadors i participants poden canviar el tema d'aquesta sala"}. +{"Only moderators are allowed to change the subject in this room","Només els moderadors poden canviar el tema d'aquesta sala"}. +{"Only moderators are allowed to retract messages","Només els moderadors tenen permís per a retractar missatges"}. {"Only moderators can approve voice requests","Només els moderadors poden aprovar les peticions de veu"}. {"Only occupants are allowed to send messages to the conference","Sols els ocupants poden enviar missatges a la sala"}. {"Only occupants are allowed to send queries to the conference","Sols els ocupants poden enviar sol·licituds a la sala"}. +{"Only publishers may publish","Només els publicadors poden publicar"}. {"Only service administrators are allowed to send service messages","Sols els administradors del servei tenen permís per a enviar missatges de servei"}. -{"Options","Opcions"}. +{"Only those on a whitelist may associate leaf nodes with the collection","Només qui estiga a una llista blanca pot associar nodes fulla amb la col·lecció"}. +{"Only those on a whitelist may subscribe and retrieve items","Només qui estiga a una llista blanca pot subscriure's i recuperar elements"}. {"Organization Name","Nom de la organizació"}. {"Organization Unit","Unitat de la organizació"}. -{"Outgoing s2s Connections:","Connexions d'eixida s2s"}. +{"Other Modules Available:","Altres mòduls disponibles:"}. {"Outgoing s2s Connections","Connexions s2s d'eixida"}. {"Owner privileges required","Es requerixen privilegis de propietari de la sala"}. -{"Packet","Paquet"}. +{"Packet relay is denied by service policy","S'ha denegat el reenviament del paquet per política del servei"}. +{"Participant ID","ID del Participant"}. {"Participant","Participant"}. -{"Password ~b","Contrasenya ~b"}. -{"Password:","Contrasenya:"}. -{"Password","Contrasenya"}. -{"Password Verification:","Verificació de la Contrasenya:"}. {"Password Verification","Verificació de la Contrasenya"}. +{"Password Verification:","Verificació de la Contrasenya:"}. +{"Password","Contrasenya"}. +{"Password:","Contrasenya:"}. {"Path to Dir","Ruta al directori"}. {"Path to File","Ruta al fitxer"}. -{"Pending","Pendent"}. +{"Payload semantic type information","Informació sobre el tipus semàntic de la carrega útil"}. {"Period: ","Període: "}. -{"Permanent rooms","Sales permanents"}. {"Persist items to storage","Persistir elements al guardar"}. +{"Persistent","Persistent"}. +{"Ping query is incorrect","La petició de Ping es incorrecta"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Recorda que aquestes opcions només fan còpia de seguretat de la base de dades Mnesia. Si estàs utilitzant el mòdul d'ODBC també deus de fer una còpia de seguretat de la base de dades de SQL a part."}. -{"Please specify file name.","Per favor especifica el nom del fitxer."}. -{"Please specify file size.","Per favor especifica la mida del fitxer."}. {"Please, wait for a while before sending new voice request","Si us plau, espera una mica abans d'enviar una nova petició de veu"}. {"Pong","Pong"}. -{"Port ~b","Port ~b"}. -{"Port","Port"}. +{"Possessing 'ask' attribute is not allowed by RFC6121","Posseir l'atribut 'ask' no està permès per RFC6121"}. {"Present real Jabber IDs to","Presentar Jabber ID's reals a"}. -{"private, ","privat"}. -{"Protocol","Protocol"}. +{"Previous session not found","No s'ha trobat la sessió prèvia"}. +{"Previous session PID has been killed","El procés de la sessió prèvia ha sigut matat"}. +{"Previous session PID has exited","El procés de la sessió prèvia ha sortit"}. +{"Previous session PID is dead","El procés de la sessió prèvia està mort"}. +{"Previous session timed out","La sessió prèvia ha caducat"}. +{"private, ","privat, "}. +{"Public","Public"}. +{"Publish model","Model de publicació"}. {"Publish-Subscribe","Publicar-subscriure't"}. {"PubSub subscriber request","Petició de subscriptor PubSub"}. {"Purge all items when the relevant publisher goes offline","Eliminar tots els elements quan el publicant relevant es desconnecti"}. -{"Queries to the conference members are not allowed in this room"," En aquesta sala no es permeten sol·licituds als membres de la conferència"}. +{"Push record not found","No s'ha trobat l'element Push"}. +{"Queries to the conference members are not allowed in this room","En aquesta sala no es permeten sol·licituds als membres"}. +{"Query to another users is forbidden","Enviar peticions a altres usuaris no està permès"}. {"RAM and disc copy","Còpia en RAM i disc"}. {"RAM copy","Còpia en RAM"}. -{"Raw","en format text"}. {"Really delete message of the day?","Segur que vols eliminar el missatge del dia?"}. +{"Receive notification from all descendent nodes","Rebre notificació de tots els nodes descendents"}. +{"Receive notification from direct child nodes only","Rebre notificació només de nodes fills directes"}. +{"Receive notification of new items only","Rebre notificació només de nous elements"}. +{"Receive notification of new nodes only","Rebre notificació només de nous nodes"}. {"Recipient is not in the conference room","El receptor no està en la sala de conferència"}. -{"Register a Jabber account","Registrar un compte Jabber"}. -{"Registered nicknames","Sobrenoms registrats"}. -{"Registered Users:","Usuaris registrats:"}. -{"Registered Users","Usuaris registrats"}. +{"Register an XMPP account","Registrar un compte XMPP"}. {"Register","Registrar"}. -{"Registration in mod_irc for ","Registre en mod_irc per a"}. {"Remote copy","Còpia remota"}. -{"Remove All Offline Messages","Eliminar tots els missatges offline"}. -{"Remove","Borrar"}. +{"Remove a hat from a user","Eliminar un barret d'un usuari"}. {"Remove User","Eliminar usuari"}. {"Replaced by new connection","Reemplaçat per una nova connexió"}. +{"Request has timed out","La petició ha caducat"}. +{"Request is ignored","La petició ha sigut ignorada"}. +{"Requested role","Rol sol·licitat"}. {"Resources","Recursos"}. -{"Restart","Reiniciar"}. {"Restart Service","Reiniciar el Servei"}. {"Restore Backup from File at ","Restaura còpia de seguretat des del fitxer en "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Restaurar una còpia de seguretat binària després de reiniciar el ejabberd (requereix menys memòria:"}. -{"Restore binary backup immediately:","Restaurar una còpia de seguretat binària ara mateix."}. +{"Restore binary backup immediately:","Restaurar una còpia de seguretat binària ara mateix:"}. {"Restore plain text backup immediately:","Restaurar una còpia de seguretat en format de text pla ara mateix:"}. {"Restore","Restaurar"}. +{"Result","Resultat"}. +{"Roles and Affiliations that May Retrieve Member List","Rols i Afiliacions que poden recuperar la llista de membres"}. {"Roles for which Presence is Broadcasted","Rols per als que sí se difon la seua presencia"}. +{"Roles that May Send Private Messages","Rols que poden enviar missatges privats"}. {"Room Configuration","Configuració de la sala"}. {"Room creation is denied by service policy","Se t'ha denegat el crear la sala per política del servei"}. -{"Room description","Descripció de la sala:"}. -{"Room Occupants","Nombre d'ocupants"}. +{"Room description","Descripció de la sala"}. +{"Room Occupants","Ocupants de la sala"}. +{"Room terminates","La sala està terminant"}. {"Room title","Títol de la sala"}. {"Roster groups allowed to subscribe","Llista de grups que tenen permés subscriures"}. -{"Roster","Llista de contactes"}. -{"Roster of ","Llista de contactes de "}. -{"Roster size","Tamany de la llista"}. -{"RPC Call Error","Error de cridada RPC"}. +{"Roster size","Mida de la llista"}. {"Running Nodes","Nodes funcionant"}. -{"~s access rule configuration","Configuració de les Regles d'Accés ~s"}. +{"~s invites you to the room ~s","~s et convida a la sala ~s"}. {"Saturday","Dissabte"}. -{"Script check","Comprovar script"}. -{"Search Results for ","Resultat de la búsqueda"}. +{"Search from the date","Buscar des de la data"}. +{"Search Results for ","Resultats de la búsqueda "}. +{"Search the text","Buscar el text"}. +{"Search until the date","Buscar fins la data"}. {"Search users in ","Cerca usuaris en "}. -{"Send announcement to all online users","Enviar anunci a tots els usuaris connectats"}. {"Send announcement to all online users on all hosts","Enviar anunci a tots els usuaris connectats a tots els hosts"}. -{"Send announcement to all users","Enviar anunci a tots els usuaris"}. +{"Send announcement to all online users","Enviar anunci a tots els usuaris connectats"}. {"Send announcement to all users on all hosts","Enviar anunci a tots els usuaris de tots els hosts"}. +{"Send announcement to all users","Enviar anunci a tots els usuaris"}. {"September","Setembre"}. -{"Server ~b","Servidor ~b"}. {"Server:","Servidor:"}. -{"Server","Servidor"}. +{"Service list retrieval timed out","L'intent de recuperar la llista de serveis ha caducat"}. +{"Session state copying timed out","La copia del estat de la sessió ha caducat"}. {"Set message of the day and send to online users","Configurar el missatge del dia i enviar a tots els usuaris"}. {"Set message of the day on all hosts and send to online users","Escriure missatge del dia en tots els hosts i enviar-ho als usuaris connectats"}. {"Shared Roster Groups","Grups de contactes compartits"}. {"Show Integral Table","Mostrar Taula Integral"}. +{"Show Occupants Join/Leave","Mostrar Entrades/Eixides dels Ocupants"}. {"Show Ordinary Table","Mostrar Taula Ordinaria"}. {"Shut Down Service","Apager el Servei"}. -{"~s invites you to the room ~s","~s et convida a la sala ~s"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Alguns clients Jabber poden emmagatzemar la teva contrasenya al teu ordinador. Fes servir aquesta característica només si saps que el teu ordinador és segur."}. +{"SOCKS5 Bytestreams","SOCKS5 Bytestreams"}. +{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Alguns clients XMPP poden emmagatzemar la teva contrasenya al ordinador, però només hauries de fer això al teu ordinador personal, per raons de seguretat."}. +{"Sources Specs:","Especificacions de Codi Font:"}. {"Specify the access model","Especificar el model d'accés"}. {"Specify the event message type","Especifica el tipus de missatge d'event"}. {"Specify the publisher model","Especificar el model del publicant"}. -{"~s's Offline Messages Queue","~s's cua de missatges offline"}. -{"Start","Iniciar"}. -{"Start Modules at ","Iniciar mòduls en "}. -{"Start Modules","Iniciar mòduls"}. -{"Statistics","Estadístiques"}. -{"Statistics of ~p","Estadístiques de ~p"}. -{"Stop","Detindre"}. -{"Stop Modules at ","Detindre mòduls en "}. -{"Stop Modules","Parar mòduls"}. +{"Stanza id is not valid","L'identificador del paquet no es vàlid"}. +{"Stanza ID","ID del paquet"}. +{"Statically specify a replyto of the node owner(s)","Especifica estaticament una adreça on respondre al propietari del node"}. {"Stopped Nodes","Nodes parats"}. -{"Storage Type","Tipus d'emmagatzematge"}. {"Store binary backup:","Guardar una còpia de seguretat binària:"}. {"Store plain text backup:","Guardar una còpia de seguretat en format de text pla:"}. -{"Subject","Assumpte"}. -{"Submit","Enviar"}. +{"Stream management is already enabled","L'administració de la connexió (stream management) ja està activada"}. +{"Stream management is not enabled","L'administració de la conexió (stream management) no està activada"}. +{"Subject","Tema"}. {"Submitted","Enviat"}. {"Subscriber Address","Adreça del Subscriptor"}. -{"Subscription","Subscripció"}. +{"Subscribers may publish","Els subscriptors poden publicar"}. +{"Subscription requests must be approved and only subscribers may retrieve items","Les peticiones de subscripció han de ser aprovades i només els subscriptors poden recuperar elements"}. +{"Subscriptions are not allowed","Les subscripcions no estan permeses"}. {"Sunday","Diumenge"}. -{"That nickname is already in use by another occupant","El Nickname està siguent utilitzat per una altra persona"}. -{"That nickname is registered by another person","El nickname ja està registrat per una altra persona"}. +{"Text associated with a picture","Text associat amb una imatge"}. +{"Text associated with a sound","Text associat amb un so"}. +{"Text associated with a video","Text associat amb un vídeo"}. +{"Text associated with speech","Text associat amb una veu"}. +{"That nickname is already in use by another occupant","El sobrenom ja l'està utilitzant una altra persona"}. +{"That nickname is registered by another person","El sobrenom ja està registrat per una altra persona"}. +{"The account already exists","El compte ha existeix"}. +{"The account was not unregistered","El compte no ha sigut esborrat"}. +{"The body text of the last received message","El contingut del text de l'ultim missatge rebut"}. {"The CAPTCHA is valid.","El CAPTCHA es vàlid."}. {"The CAPTCHA verification has failed","La verificació CAPTCHA ha fallat"}. +{"The captcha you entered is wrong","El CAPTCHA que has proporcionat és incorrecte"}. +{"The child nodes (leaf or collection) associated with a collection","El nodes fills (fulla o col·leccions) associats amb una col·lecció"}. {"The collections with which a node is affiliated","Les col.leccions amb les que un node està afiliat"}. -{"the password is","la contrasenya és"}. +{"The DateTime at which a leased subscription will end or has ended","La Data i Hora a la que una subscripció prestada terminarà o ha terminat"}. +{"The datetime when the node was created","La data i hora a la que un node va ser creat"}. +{"The default language of the node","El llenguatge per defecte d'un node"}. +{"The feature requested is not supported by the conference","La característica sol·licitada no està suportada per la sala de conferència"}. +{"The JID of the node creator","El JID del creador del node"}. +{"The JIDs of those to contact with questions","Els JIDs a qui contactar amb preguntes"}. +{"The JIDs of those with an affiliation of owner","Els JIDs de qui tenen una afiliació de propietaris"}. +{"The JIDs of those with an affiliation of publisher","Els JIDs de qui tenen una afiliació de publicadors"}. +{"The list of all online users","La llista de tots els usuaris en línia"}. +{"The list of all users","La llista de tots els usuaris"}. +{"The list of JIDs that may associate leaf nodes with a collection","La llista de JIDs que poden associar nodes fulla amb una col·lecció"}. +{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","El màxim número de nodes fills que poden associar-se amb una col·lecció, o `max` per a no tindre altre límit més que el màxim imposat pel servidor"}. +{"The minimum number of milliseconds between sending any two notification digests","El número mínim de mil·lisegons entre l'enviament de dos resums de notificacions"}. +{"The name of the node","El nom del node"}. +{"The node is a collection node","El node es una col·lecció"}. +{"The node is a leaf node (default)","El node es un node fulla (per defecte)"}. +{"The NodeID of the relevant node","El NodeID del node rellevant"}. +{"The number of pending incoming presence subscription requests","El número de peticions rebudes de subscripció de presencia pendents"}. +{"The number of subscribers to the node","El número de subscriptors al node"}. +{"The number of unread or undelivered messages","El número de missatges no llegits o no enviats"}. +{"The password contains unacceptable characters","La contrasenya conté caràcters inacceptables"}. {"The password is too weak","La contrasenya és massa simple"}. -{"The password of your Jabber account was successfully changed.","La contrasenya del teu compte Jabber s'ha canviat correctament."}. +{"the password is","la contrasenya és"}. +{"The password of your XMPP account was successfully changed.","La contrasenya del teu compte XMPP s'ha canviat correctament."}. +{"The password was not changed","La contrasenya no ha sigut canviada"}. +{"The passwords are different","Les contrasenyes son diferents"}. +{"The presence states for which an entity wants to receive notifications","El estats de presencia per als quals una entitat vol rebre notificacions"}. +{"The query is only allowed from local users","La petició està permesa només d'usuaris locals"}. +{"The query must not contain elements","La petició no pot contenir elements "}. +{"The room subject can be modified by participants","El tema de la sala pot modificar-lo els participants"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","La informació semàntica de les dades al node, usualment especificat pel espai de noms de la càrrega util (si n'hi ha)"}. +{"The sender of the last received message","Qui ha enviat l'ultim missatge rebut"}. +{"The stanza MUST contain only one element, one element, or one element","El paquet DEU contindre només un element , un element , o un element "}. +{"The subscription identifier associated with the subscription request","L'identificador de subscripció associat amb la petició de subscripció"}. +{"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","La URL de uns transformació XSL que pot ser aplicada als payloads per a generar un element apropiat de contingut de missatge."}. +{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","La URL de una transformació XSL que pot ser aplicada al format de payload per a generar un resultat valid de Data Forms, que el client puga mostrar usant un métode generic de Data Forms"}. {"There was an error changing the password: ","Hi ha hagut un error canviant la contrasenya: "}. {"There was an error creating the account: ","Hi ha hagut un error creant el compte: "}. {"There was an error deleting the account: ","Hi ha hagut un error esborrant el compte: "}. -{"This IP address is blacklisted in ~s","Esta adreça IP està a la llista negra en ~s"}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Això no distingeix majúscules de minúscules: macbeth es el mateix que MacBeth i Macbeth."}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Aquesta pàgina permet crear un compte Jabber en aquest servidor Jabber. El teu JID (Jabber IDentifier; Identificador Jabber) tindrà aquesta forma: usuari@servidor. Si us plau, llegeix amb cura les instruccions per emplenar correctament els camps."}. -{"This page allows to unregister a Jabber account in this Jabber server.","Aquesta pàgina permet anul·lar el registre d'un compte Jabber en aquest servidor Jabber."}. +{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Aquesta pàgina permet crear un compte XMPP en aquest servidor XMPP. El teu JID (Jabber ID; Identificador Jabber) tindrà aquesta forma: usuari@servidor. Si us plau, llegeix amb cura les instruccions per emplenar correctament els camps."}. +{"This page allows to unregister an XMPP account in this XMPP server.","Aquesta pàgina permet esborrar un compte XMPP en aquest servidor XMPP."}. +{"This room is not anonymous","Aquesta sala no és anònima"}. +{"This service can not process the address: ~s","Este servei no pot processar la direcció: ~s"}. {"Thursday","Dijous"}. -{"Time","Data"}. {"Time delay","Temps de retard"}. +{"Timed out waiting for stream resumption","Massa temps esperant que es resumisca la connexió"}. +{"To register, visit ~s","Per a registrar-te, visita ~s"}. +{"To ~ts","A ~ts"}. +{"Token TTL","Token TTL"}. +{"Too many active bytestreams","N'hi ha massa Bytestreams actius"}. {"Too many CAPTCHA requests","Massa peticions de CAPTCHA"}. +{"Too many child elements","N'hi ha massa subelements"}. +{"Too many elements","N'hi ha massa elements "}. +{"Too many elements","N'hi ha massa elements "}. {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Massa autenticacions (~p) han fallat des d'aquesta adreça IP (~s). L'adreça serà desbloquejada en ~s UTC"}. +{"Too many receiver fields were specified","S'han especificat massa camps de receptors"}. {"Too many unacked stanzas","Massa missatges sense haver reconegut la seva recepció"}. -{"To","Per a"}. -{"To ~s","A ~s"}. -{"Total rooms","Nombre total de sales"}. -{"Traffic rate limit is exceeded","El llímit de tràfic ha sigut sobrepassat"}. -{"Transactions Aborted:","Transaccions Avortades"}. -{"Transactions Committed:","Transaccions Realitzades:"}. -{"Transactions Logged:","Transaccions registrades"}. -{"Transactions Restarted:","Transaccions reiniciades"}. +{"Too many users in this conference","N'hi ha massa usuaris en esta sala de conferència"}. +{"Traffic rate limit is exceeded","El límit de tràfic ha sigut sobrepassat"}. +{"~ts's MAM Archive","Arxiu MAM de ~ts"}. +{"~ts's Offline Messages Queue","~ts's cua de missatges offline"}. {"Tuesday","Dimarts"}. {"Unable to generate a CAPTCHA","No s'ha pogut generar un CAPTCHA"}. +{"Unable to register route on existing local domain","No s'ha pogut registrar la ruta al domini local existent"}. {"Unauthorized","No autoritzat"}. -{"Unregister a Jabber account","Anul·lar el registre d'un compte Jabber"}. +{"Unexpected action","Acció inesperada"}. +{"Unexpected error condition: ~p","Condició d'error inesperada: ~p"}. +{"Uninstall","Desinstal·lar"}. +{"Unregister an XMPP account","Anul·lar el registre d'un compte XMPP"}. {"Unregister","Anul·lar el registre"}. -{"Update","Actualitzar"}. +{"Unsupported element","Element no soportat"}. +{"Unsupported version","Versió no suportada"}. {"Update message of the day (don't send)","Actualitzar el missatge del dia (no enviar)"}. {"Update message of the day on all hosts (don't send)","Actualitza el missatge del dia en tots els hosts (no enviar)"}. -{"Update ~p","Actualitzar ~p"}. -{"Update plan","Pla d'actualització"}. -{"Update script","Script d'actualització"}. -{"Uptime:","Temps en marxa"}. -{"Use of STARTTLS required","És obligatori utilitzar STARTTLS"}. -{"User JID","JID del usuari "}. +{"Update specs to get modules source, then install desired ones.","Actualitza les especificacions per obtindre el codi font dels mòduls, després instal·la els que vulgues."}. +{"Update Specs","Actualitzar Especificacions"}. +{"Updating the vCard is not supported by the vCard storage backend","El sistema d'almacenament de vCard no te capacitat per a actualitzar la vCard"}. +{"Upgrade","Actualitza"}. +{"URL for Archived Discussion Logs","URL dels Arxius de Discussions"}. +{"User already exists","El usuari ja existeix"}. +{"User JID","JID del usuari"}. +{"User (jid)","Usuari (jid)"}. {"User Management","Gestió d'Usuaris"}. +{"User not allowed to perform an IQ set on another user's vCard.","L'usuari no te permis per a modificar la vCard d'altre usuari."}. +{"User removed","Usuari borrat"}. +{"User session not found","Sessió d'usuari no trobada"}. +{"User session terminated","Sessió d'usuari terminada"}. +{"User ~ts","Usuari ~ts"}. {"Username:","Nom d'usuari:"}. {"Users are not allowed to register accounts so quickly","Els usuaris no tenen permís per a crear comptes tan depresa"}. {"Users Last Activity","Última activitat d'usuari"}. -{"User ~s","Usuari ~s"}. {"Users","Usuaris"}. {"User","Usuari"}. -{"Validate","Validar"}. -{"vCard User Search","Recerca de vCard d'usuari"}. +{"Value 'get' of 'type' attribute is not allowed","El valor 'get' a l'atribut 'type' no és permès"}. +{"Value of '~s' should be boolean","El valor de '~s' deuria ser booleà"}. +{"Value of '~s' should be datetime string","El valor de '~s' deuria ser una data"}. +{"Value of '~s' should be integer","El valor de '~s' deuria ser un numero enter"}. +{"Value 'set' of 'type' attribute is not allowed","El valor 'set' a l'atribut 'type' no és permès"}. +{"vCard User Search","vCard recerca d'usuari"}. +{"View joined MIX channels","Vore els canals MIX units"}. {"Virtual Hosts","Hosts virtuals"}. {"Visitors are not allowed to change their nicknames in this room","Els visitants no tenen permés canviar el seus Nicknames en esta sala"}. {"Visitors are not allowed to send messages to all occupants","Els visitants no poden enviar missatges a tots els ocupants"}. {"Visitor","Visitant"}. {"Voice request","Petició de veu"}. {"Voice requests are disabled in this conference","Les peticions de veu es troben desactivades en aquesta conferència"}. +{"Web client which allows to join the room anonymously","Client web que permet entrar a la sala anonimament"}. {"Wednesday","Dimecres"}. +{"When a new subscription is processed and whenever a subscriber comes online","Quan es processa una nova subscripció i un subscriptor es connecta"}. +{"When a new subscription is processed","Quan es processa una nova subscripció"}. {"When to send the last published item","Quan s'ha enviat l'última publicació"}. -{"Whether to allow subscriptions","Permetre subscripcions"}. -{"You can later change your password using a Jabber client.","Podràs canviar la teva contrasenya més endavant utilitzant un client Jabber."}. +{"Whether an entity wants to receive an XMPP message body in addition to the payload format","Si una entitat vol rebre un missatge XMPP amb el format payload"}. +{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","Si una entitat vol rebre resums (agregacions) de notificacions o totes les notificacions individualment"}. +{"Whether an entity wants to receive or disable notifications","Si una entitat vol rebre notificacions o no"}. +{"Whether owners or publisher should receive replies to items","Si el propietaris o publicadors deurien de rebre respostes als elements"}. +{"Whether the node is a leaf (default) or a collection","Si el node es fulla (per defecte) o es una col·lecció"}. +{"Whether to allow subscriptions","Si s'hauria de permetre subscripcions"}. +{"Whether to make all subscriptions temporary, based on subscriber presence","Si fer totes les subscripcions temporals, basat en la presencia del subscriptor"}. +{"Whether to notify owners about new subscribers and unsubscribes","Si notificar als propietaris sobre noves subscripcions i desubscripcions"}. +{"Who can send private messages","Qui pot enviar missatges privats"}. +{"Who may associate leaf nodes with a collection","Qui pot associar nodes fulla amb una col·lecció"}. +{"Wrong parameters in the web formulary","Paràmetres incorrectes en el formulari web"}. +{"Wrong xmlns","El xmlns ès incorrecte"}. +{"XMPP Account Registration","Registre de compte XMPP"}. +{"XMPP Domains","Dominis XMPP"}. +{"XMPP Show Value of Away","Valor 'show' de XMPP: Ausent"}. +{"XMPP Show Value of Chat","Valor 'show' de XMPP: Disposat per a xarrar"}. +{"XMPP Show Value of DND (Do Not Disturb)","Valor 'show' de XMPP: DND (No Molestar)"}. +{"XMPP Show Value of XA (Extended Away)","Valor 'show' de XMPP: XA (Molt Ausent)"}. +{"XMPP URI of Associated Publish-Subscribe Node","URI XMPP del Node Associat Publish-Subscribe"}. +{"You are being removed from the room because of a system shutdown","Has sigut expulsat de la sala perquè el sistema va a apagar-se"}. +{"You are not allowed to send private messages","No tens permés enviar missatges privats"}. +{"You are not joined to the channel","No t'has unit al canal"}. +{"You can later change your password using an XMPP client.","Podràs canviar la teva contrasenya més endavant utilitzant un client XMPP."}. {"You have been banned from this room","Has sigut bloquejat en aquesta sala"}. +{"You have joined too many conferences","Has entrat en massa sales de conferència"}. {"You must fill in field \"Nickname\" in the form","Deus d'omplir el camp \"Nickname\" al formulari"}. {"You need a client that supports x:data and CAPTCHA to register","Necessites un client amb suport x:data i de CAPTCHA para poder registrar-te"}. {"You need a client that supports x:data to register the nickname","Necessites un client amb suport x:data per a poder registrar el sobrenom"}. -{"You need an x:data capable client to configure mod_irc settings","Necessites un client amb suport x:data per a configurar les opcions de mod_irc"}. -{"You need an x:data capable client to configure room","Necessites un client amb suport x:data per a configurar la sala"}. {"You need an x:data capable client to search","Necessites un client amb suport x:data per a poder buscar"}. {"Your active privacy list has denied the routing of this stanza.","La teva llista de privacitat activa ha denegat l'encaminament d'aquesta stanza."}. -{"Your contact offline message queue is full. The message has been discarded.","La cua de missatges offline és plena. El missatge ha sigut descartat"}. -{"Your Jabber account was successfully created.","El teu compte Jabber ha sigut creat correctament."}. -{"Your Jabber account was successfully deleted.","El teu compte Jabber ha sigut esborrat correctament."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","Els teus missatges per ~s s'estan bloquejant. Per desbloquejar-los, visita ~s"}. +{"Your contact offline message queue is full. The message has been discarded.","La teua cua de missatges offline és plena. El missatge ha sigut descartat."}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","La teua petició de subscripció i/o missatges a ~s han sigut bloquejats. Per a desbloquejar-los, visita ~s"}. +{"Your XMPP account was successfully registered.","El teu compte XMPP ha sigut creat correctament."}. +{"Your XMPP account was successfully unregistered.","El teu compte XMPP ha sigut esborrat correctament."}. +{"You're not allowed to create nodes","No tens permís per a crear nodes"}. diff --git a/priv/msgs/ca.po b/priv/msgs/ca.po deleted file mode 100644 index ac1d01478..000000000 --- a/priv/msgs/ca.po +++ /dev/null @@ -1,1941 +0,0 @@ -# Jan, 2012. -msgid "" -msgstr "" -"Project-Id-Version: 2.1.0-alpha\n" -"POT-Creation-Date: \n" -"PO-Revision-Date: 2016-01-15 12:25+0100\n" -"Last-Translator: Badlop \n" -"Language-Team: \n" -"Language: ca\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Catalan (català)\n" -"X-Additional-Translator: Vicent Alberola Canet\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 1.6.10\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "És obligatori utilitzar STARTTLS" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Recurs no disponible" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Reemplaçat per una nova connexió" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "Has sigut expulsat" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "" -"La teva llista de privacitat activa ha denegat l'encaminament d'aquesta " -"stanza." - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "Massa missatges sense haver reconegut la seva recepció" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Introdueix el text que veus" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "" -"Els teus missatges per ~s s'estan bloquejant. Per desbloquejar-los, visita ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "Si no veus la imatge CAPTCHA açí, visita la pàgina web." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "Pàgina web del CAPTCHA" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "El CAPTCHA es vàlid." - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Usuari" - -#: ejabberd_oauth.erl:256 -msgid "Server" -msgstr "Servidor" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Contrasenya" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "Acceptar" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "No autoritzat" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "Web d'administració del ejabberd" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Administració" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Llista de Control d'Accés" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Enviat" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Format erroni" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Enviar" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "en format text" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Eliminar els seleccionats" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Regles d'Accés" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "Configuració de les Regles d'Accés ~s" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "Hosts virtuals" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Usuaris" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "Usuaris conectats" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Última activitat d'usuari" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Període: " - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "Últim mes" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "Últim any" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "Tota l'activitat" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Mostrar Taula Ordinaria" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Mostrar Taula Integral" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Estadístiques" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "No Trobat" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Node no trobat" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Afegir nou" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Host" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Usuaris registrats" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Afegir usuari" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Missatges offline" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Última activitat" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Mai" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Connectat" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Usuaris registrats:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Usuaris en línia:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Connexions d'eixida s2s" - -#: ejabberd_web_admin.erl:1559 -msgid "Incoming s2s Connections:" -msgstr "Connexions s2s d'entrada" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Cap" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Canviar Contrasenya" - -#: ejabberd_web_admin.erl:1673 -msgid "User ~s" -msgstr "Usuari ~s" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Recursos connectats:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Contrasenya:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Eliminar usuari" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "No hi ha dades" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Nodes" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Nodes funcionant" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Nodes parats" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -msgid "Node ~p" -msgstr "Node ~p" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "Base de dades" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Guardar còpia de seguretat" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Ports a l'escolta" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Actualitzar" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Reiniciar" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Detindre" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Mòduls" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "Error de cridada RPC" - -#: ejabberd_web_admin.erl:1917 -msgid "Database Tables at ~p" -msgstr "Taules de la base de dades en ~p" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Nom" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Tipus d'emmagatzematge" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Elements" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Memòria" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Error" - -#: ejabberd_web_admin.erl:1955 -msgid "Backup of ~p" -msgstr "Còpia de seguretat de ~p" - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Recorda que aquestes opcions només fan còpia de seguretat de la base de " -"dades Mnesia. Si estàs utilitzant el mòdul d'ODBC també deus de fer una " -"còpia de seguretat de la base de dades de SQL a part." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Guardar una còpia de seguretat binària:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "Acceptar" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Restaurar una còpia de seguretat binària ara mateix." - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"Restaurar una còpia de seguretat binària després de reiniciar el ejabberd " -"(requereix menys memòria:" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Guardar una còpia de seguretat en format de text pla:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Restaurar una còpia de seguretat en format de text pla ara mateix:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "Importar dades d'usuaris des d'un arxiu PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" -"Exportar dades de tots els usuaris del servidor a arxius PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "Exportar dades d'usuaris d'un host a arxius PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "Exporta totes les taules a un fitxer SQL:" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Importar dades d'usuaris de l'arxiu de spool de jabberd14" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Importar dades d'usuaris del directori de spool de jabberd14:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Ports a la escolta en " - -#: ejabberd_web_admin.erl:2144 -msgid "Modules at ~p" -msgstr "Mòduls en ~p" - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "Estadístiques de ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Temps en marxa" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "Temps de CPU" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Transaccions Realitzades:" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Transaccions Avortades" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Transaccions reiniciades" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Transaccions registrades" - -#: ejabberd_web_admin.erl:2243 -msgid "Update ~p" -msgstr "Actualitzar ~p" - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "Pla d'actualització" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Mòduls modificats" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Script d'actualització" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Script d'actualització de baix nivell" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Comprovar script" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Port" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Protocol" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Mòdul" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Opcions" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Iniciar" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Comandaments" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "Segur que vols eliminar el missatge del dia?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Assumpte" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Missatge" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "No hi ha proveedor per al missatge anunci" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Anuncis" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Enviar anunci a tots els usuaris" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Enviar anunci a tots els usuaris de tots els hosts" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Enviar anunci a tots els usuaris connectats" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "Enviar anunci a tots els usuaris connectats a tots els hosts" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Configurar el missatge del dia i enviar a tots els usuaris" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" -"Escriure missatge del dia en tots els hosts i enviar-ho als usuaris " -"connectats" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Actualitzar el missatge del dia (no enviar)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "Actualitza el missatge del dia en tots els hosts (no enviar)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Eliminar el missatge del dia" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Elimina el missatge del dis de tots els hosts" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Configuració" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Iniciar mòduls" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Parar mòduls" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Restaurar" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Exportar a fitxer de text" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Importar fitxer" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Importar directori" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Reiniciar el Servei" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Apager el Servei" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Eliminar Usuari" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Finalitzar Sesió d'Usuari" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Obtenir Contrasenya d'usuari" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Canviar Contrasenya d'Usuari" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "Obtenir la última connexió d'Usuari" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Obtenir Estadístiques d'Usuari" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Obtenir Número d'Usuaris Registrats" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Obtenir Número d'Usuaris Connectats" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Gestió d'Usuaris" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Tots els usuaris" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Connexions s2s d'eixida" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Gestió de còpia de seguretat" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Importar usuaris de jabberd14" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "A ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "De ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Configuració de la base de dades en " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Selecciona el tipus d'almacenament de les taules" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Còpia sols en disc" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "Còpia en RAM i disc" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "Còpia en RAM" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "Còpia remota" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Detindre mòduls en " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Selecciona mòduls a detindre" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Iniciar mòduls en " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Introdueix llista de {mòdul, [opcions]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Llista de mòduls a iniciar" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Desar còpia de seguretat a fitxer en " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Introdueix ruta al fitxer de còpia de seguretat" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Ruta al fitxer" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Restaura còpia de seguretat des del fitxer en " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Exporta còpia de seguretat a fitxer de text en " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Introdueix ruta al fitxer de text" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Importa usuari des de fitxer en " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Introdueix ruta al fitxer jabberd14 spool" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Importar usuaris des del directori en " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Introdueix la ruta al directori de jabberd14 spools" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Ruta al directori" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "Temps de retard" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Configuració de la Llista de Control d'Accés" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Llistes de Control de Accés" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Configuració d'accesos" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Regles d'accés" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "ID Jabber" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Verificació de la Contrasenya" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Número d'Usuaris Registrats" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Número d'usuaris connectats" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Últim login" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "Tamany de la llista" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "Adreça IP" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Recursos" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Administració de " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Acció en l'usuari" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Editar propietats" - -#: mod_fail2ban.erl:95 -msgid "" -"Too many (~p) failed authentications from this IP address (~s). The address " -"will be unblocked at ~s UTC" -msgstr "" -"Massa autenticacions (~p) han fallat des d'aquesta adreça IP (~s). L'adreça " -"serà desbloquejada en ~s UTC" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "Per favor especifica la mida del fitxer." - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "Per favor especifica el nom del fitxer." - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "Esta adreça IP està a la llista negra en ~s" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "Accés denegat per la política del servei" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "Transport a IRC" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "mòdul ejabberd IRC" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "" -"Necessites un client amb suport x:data per a configurar les opcions de " -"mod_irc" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Registre en mod_irc per a" - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Introdueix el nom d'usuari, les codificacions de caràcters, els ports i " -"contrasenyes per a utilitzar al connectar als servidors de IRC" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "Nom d'usuari al IRC" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Si vols especificar codificacions de caràcters diferents per a cada servidor " -"IRC emplena aquesta llista amb els valors amb el format '{\"servidor irc\", " -"\"codificació\", port, \"contrasenya\"}'. Aquest servei utilitza per " -"defecte la codificació \"~s\", port ~p, no contrasenya." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Exemple: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Paràmetres de connexió" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "Entra a canal d'IRC" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "Canal d'IRC (no posis la primera #)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "Servidor d'IRC" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Entra al canal d'IRC aquí." - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Entra al canal d'IRC en aquesta Jabber ID: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "Configuració d'IRC." - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Introdueix el nom d'usuari i les codificacions de caràcters per a utilitzar " -"als servidors de IRC. Apreta \"Seguent\" per veure més caps per omplir. " -"Apreta \"Completar\" per guardar la configuració. " - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "Nom d'usuari al IRC" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Contrasenya ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Port ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Codificació pel servidor ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Servidor ~b" - -#: mod_mam.erl:541 -msgid "Only members may query archives of this room" -msgstr "Només membres poden consultar l'arxiu de missatges d'aquesta sala" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "" -"Sols els administradors del servei tenen permís per a enviar missatges de " -"servei" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "Se t'ha denegat el crear la sala per política del servei" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "La sala de conferències no existeix" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Sales de xat" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "Sales buides " - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "" -"Necessites un client amb suport x:data per a poder registrar el sobrenom" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Registre del sobrenom en " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Introdueix el sobrenom que vols registrar" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Sobrenom" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "El nickname ja està registrat per una altra persona" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Deus d'omplir el camp \"Nickname\" al formulari" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "mòdul ejabberd MUC" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "Multi-Usuari Converses" - -#: mod_muc_admin.erl:249 -msgid "Total rooms" -msgstr "Nombre total de sales" - -#: mod_muc_admin.erl:250 -msgid "Permanent rooms" -msgstr "Sales permanents" - -#: mod_muc_admin.erl:251 -msgid "Registered nicknames" -msgstr "Sobrenoms registrats" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "Llista de sales" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "Configuració de la sala de xat modificada" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "Entrar a la sala" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "Deixar la sala" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "Has sigut banejat" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "Has sigut expulsat a causa d'un canvi d'afiliació" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "Has sigut expulsat perquè la sala ha canviat a sols membres" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "Has sigut expulsat perquè el sistema s'ha apagat" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "ara es conegut com" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " ha posat l'assumpte: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "La sala s'ha creat" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "La sala s'ha destruït" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "La sala s'ha iniciat" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "La sala s'ha aturat" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "Dilluns" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "Dimarts" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "Dimecres" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "Dijous" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "Divendres" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "Dissabte" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "Diumenge" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "Gener" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "Febrer" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "Març" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "Abril" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "Maig" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "Juny" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "Juliol" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "Agost" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "Setembre" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "Octubre" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "Novembre" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "Decembre" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Configuració de la sala" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "Nombre d'ocupants" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "El llímit de tràfic ha sigut sobrepassat" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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 "" -"No està permés enviar missatges d'error a la sala. El participant (~s) ha " -"enviat un missatge d'error (~s) i ha sigut expulsat de la sala" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "No està permès l'enviament de missatges privats a la sala" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "Si us plau, espera una mica abans d'enviar una nova petició de veu" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "Les peticions de veu es troben desactivades en aquesta conferència" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "No s'ha pogut extraure el JID de la teva aprovació de petició de veu" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "Només els moderadors poden aprovar les peticions de veu" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Tipus de missatge incorrecte" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "No està permés enviar missatges del tipus \"groupchat\"" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "El receptor no està en la sala de conferència" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "No està permés enviar missatges privats" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Sols els ocupants poden enviar missatges a la sala" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "Sols els ocupants poden enviar sol·licituds a la sala" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "" -" En aquesta sala no es permeten sol·licituds als membres de la conferència" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "" -"Només els moderadors i participants poden canviar l'assumpte d'aquesta sala" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Només els moderadors poden canviar l'assumpte d'aquesta sala" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "Els visitants no poden enviar missatges a tots els ocupants" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "Els visitants no tenen permés canviar el seus Nicknames en esta sala" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "El Nickname està siguent utilitzat per una altra persona" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Has sigut bloquejat en aquesta sala" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "Necessites ser membre d'aquesta sala per a poder entrar" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "Es necessita contrasenya per a entrar en aquesta sala" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "Massa peticions de CAPTCHA" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "No s'ha pogut generar un CAPTCHA" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Contrasenya incorrecta" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "Es necessita tenir privilegis d'administrador" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "Es necessita tenir privilegis de moderador" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "El Jabber ID ~s no és vàlid" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "El sobrenom ~s no existeix a la sala" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Afiliació invàlida: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Rol invàlid: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "Es requerixen privilegis de propietari de la sala" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "Configuració de la sala ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Títol de la sala" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "Descripció de la sala:" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Crear una sala persistent" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Crear una sala pública" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "Crear una llista de participants pública" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Crear una sala amb contrasenya" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Número màxim d'ocupants" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Sense Llímit" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Presentar Jabber ID's reals a" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "només moderadors" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "qualsevol" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "Rols per als que sí se difon la seua presencia" - -#: mod_muc_room.erl:3486 -msgid "Moderator" -msgstr "Moderador" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "Participant" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "Visitant" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Crear una sala de \"només membres\"" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Crear una sala moderada" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Els usuaris són participants per defecte" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Permetre que els usuaris canviin el tema" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Permetre que els usuaris envien missatges privats" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "Permetre als visitants enviar missatges privats a" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "ningú" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Permetre que els usuaris fagen peticions a altres usuaris" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Permetre que els usuaris envien invitacions" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "" -"Permetre als visitants enviar text d'estat en les actualitzacions de " -"presència" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Permetre als visitants canviar el sobrenom" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "Permetre als visitants enviar peticions de veu" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "Interval mínim entre peticions de veu (en segons)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "Crear una sala protegida per CAPTCHA" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "Activar l'emmagatzematge de missatges" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "Excloure Jabber IDs de la comprovació CAPTCHA" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Habilitar el registre de la conversa" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "Necessites un client amb suport x:data per a configurar la sala" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Número d'ocupants" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "privat" - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "Petició de veu" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "Aprova o denega la petició de veu" - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "JID del usuari " - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "Concedir veu a aquesta persona?" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s et convida a la sala ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "la contrasenya és" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "Multicast" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "Servei de Multicast d'ejabberd" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "La cua de missatges offline és plena. El missatge ha sigut descartat" - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "~s's cua de missatges offline" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Data" - -#: mod_offline.erl:812 -msgid "From" -msgstr "De" - -#: mod_offline.erl:813 -msgid "To" -msgstr "Per a" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Paquet" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Missatges fora de línia:" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "Eliminar tots els missatges offline" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "mòdul ejabberd SOCKS5 Bytestreams" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Publicar-subscriure't" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "Mòdul ejabberd Publicar-Subscriure" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "Petició de subscriptor PubSub" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Tria si aprova aquesta entitat de subscripció" - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "ID del Node" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Adreça del Subscriptor" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "Permetre que aquesta Jabber ID es puga subscriure a aquest node pubsub" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Enviar payloads junt a les notificacions d'events" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Entrega de notificacions d'events" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Notificar subscriptors quan canvia la configuració del node" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Notificar subscriptors quan el node és eliminat" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Notificar subscriptors quan els elements són eliminats del node" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Persistir elements al guardar" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "Un nom per al node" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Màxim # d'elements que persistixen" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Permetre subscripcions" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Especificar el model d'accés" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Llista de grups que tenen permés subscriures" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Especificar el model del publicant" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "Eliminar tots els elements quan el publicant relevant es desconnecti" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "Especifica el tipus de missatge d'event" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Màxim tamany del payload en bytes" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "Quan s'ha enviat l'última publicació" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Sols enviar notificacions als usuaris disponibles" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "Les col.leccions amb les que un node està afiliat" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "La verificació CAPTCHA ha fallat" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "" -"Necessites un client amb suport x:data i de CAPTCHA para poder registrar-te" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "Tria nom d'usuari i contrasenya per a registrar-te en aquest servidor" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "La contrasenya és massa simple" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "Els usuaris no tenen permís per a crear comptes tan depresa" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "El teu compte Jabber ha sigut creat correctament." - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "Hi ha hagut un error creant el compte: " - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "El teu compte Jabber ha sigut esborrat correctament." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "Hi ha hagut un error esborrant el compte: " - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "La contrasenya del teu compte Jabber s'ha canviat correctament." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "Hi ha hagut un error canviant la contrasenya: " - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Registre de compte Jabber" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Registrar un compte Jabber" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Anul·lar el registre d'un compte Jabber" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"Aquesta pàgina permet crear un compte Jabber en aquest servidor Jabber. El " -"teu JID (Jabber IDentifier; Identificador Jabber) tindrà aquesta forma: " -"usuari@servidor. Si us plau, llegeix amb cura les instruccions per emplenar " -"correctament els camps." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "Nom d'usuari:" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" -"Això no distingeix majúscules de minúscules: macbeth es el mateix que " -"MacBeth i Macbeth." - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "Caràcters no permesos:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "Servidor:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" -"No li donis la teva contrasenya a ningú, ni tan sols als administradors del " -"servidor Jabber." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "" -"Podràs canviar la teva contrasenya més endavant utilitzant un client Jabber." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Alguns clients Jabber poden emmagatzemar la teva contrasenya al teu " -"ordinador. Fes servir aquesta característica només si saps que el teu " -"ordinador és segur." - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"Memoritza la teva contrasenya, o escriu-la en un paper guardat a un lloc " -"segur.A Jabber no hi ha una forma automatitzada de recuperar la teva " -"contrasenya si la oblides." - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "Verificació de la Contrasenya:" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "Registrar" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "Antiga contrasenya:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "Nova Contrasenya:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "" -"Aquesta pàgina permet anul·lar el registre d'un compte Jabber en aquest " -"servidor Jabber." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "Anul·lar el registre" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Subscripció" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Pendent" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Grups" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Validar" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Borrar" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Llista de contactes de " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Afegir Jabber ID" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Llista de contactes" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Grups de contactes compartits" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Nom:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Descripció:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Membre:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Mostrar grups:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Grup " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Servidor Erlang Jabber" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Aniversari" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Ciutat" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "Pais" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "Email" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Cognom" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Emplena el formulari per a buscar usuaris Jabber. Afegix * al final d'un " -"camp per a buscar subcadenes." - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Nom complet" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Segon nom" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Nom de la organizació" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Unitat de la organizació" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Cerca usuaris en " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "Necessites un client amb suport x:data per a poder buscar" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "Recerca de vCard d'usuari" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "Mòdul ejabberd vCard" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Resultat de la búsqueda" - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "Emplena camps per a buscar usuaris Jabber que concorden" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Servidors d'eixida de s2s" - -#~ msgid "Delete" -#~ msgstr "Eliminar" - -#~ msgid "This room is not anonymous" -#~ msgstr "Aquesta sala no és anònima" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "" -#~ "Aquest participant ha sigut expulsat de la sala perque ha enviat un " -#~ "missatge d'error" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Aquest participant ha sigut expulsat de la sala perque ha enviat un " -#~ "missatge erroni a un altre participant" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "" -#~ "Aquest participant ha sigut expulsat de la sala perque ha enviat un error " -#~ "de presencia" - -#, fuzzy -#~ msgid "Captcha test failed" -#~ msgstr "El CAPTCHA es vàlid." diff --git a/priv/msgs/cs.msg b/priv/msgs/cs.msg index 01897aadf..cd57ebefb 100644 --- a/priv/msgs/cs.msg +++ b/priv/msgs/cs.msg @@ -1,21 +1,20 @@ -%% -*- coding: latin-1 -*- -{"Accept","Přijmout"}. -{"Access Configuration","Konfigurace přístupů"}. -{"Access Control List Configuration","Konfigurace seznamu přístupových práv (ACL)"}. -{"Access control lists","Seznamy přístupových práv (ACL)"}. -{"Access Control Lists","Seznamy přístupových práv (ACL)"}. -{"Access denied by service policy","Přístup byl zamítnut nastavením služby"}. -{"Access rules","Pravidla přístupů"}. -{"Access Rules","Pravidla přístupů"}. -{"Action on user","Akce aplikovaná na uživatele"}. -{"Add Jabber ID","Přidat Jabber ID"}. -{"Add New","Přidat nový"}. -{"Add User","Přidat uživatele"}. -{"Administration","Administrace"}. -{"Administration of ","Administrace "}. -{"Administrator privileges required","Potřebujete práva administrátora"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" has set the subject to: "," změnil(a) téma na: "}. {"A friendly name for the node","Přívětivé jméno pro uzel"}. +{"A password is required to enter this room","Pro vstup do místnosti musíte zadat heslo"}. +{"Accept","Přijmout"}. +{"Access denied by service policy","Přístup byl zamítnut nastavením služby"}. +{"Action on user","Akce aplikovaná na uživatele"}. +{"Add User","Přidat uživatele"}. +{"Administration of ","Administrace "}. +{"Administration","Administrace"}. +{"Administrator privileges required","Potřebujete práva administrátora"}. {"All activity","Všechny aktivity"}. +{"All Users","Všichni uživatelé"}. {"Allow this Jabber ID to subscribe to this pubsub node?","Povolit tomuto Jabber ID odebírat tento pubsub uzel?"}. {"Allow users to change the subject","Povolit uživatelům měnit téma místnosti"}. {"Allow users to query other users","Povolit uživatelům odesílat požadavky (query) ostatním uživatelům"}. @@ -25,116 +24,103 @@ {"Allow visitors to send private messages to","Povolit návštěvníkům odesílat soukromé zprávy"}. {"Allow visitors to send status text in presence updates","Povolit návštěvníkům posílat stavové zprávy ve statusu"}. {"Allow visitors to send voice requests","Povolit uživatelům posílat žádosti o voice práva"}. -{"All Users","Všichni uživatelé"}. {"Announcements","Oznámení"}. -{"anyone","každému"}. -{"A password is required to enter this room","Pro vstup do místnosti musíte zadat heslo"}. {"April",". dubna"}. {"August",". srpna"}. +{"Automatic node creation is not enabled","Automatické vytváření uzlů není povoleno"}. {"Backup Management","Správa zálohování"}. {"Backup of ~p","Záloha ~p"}. {"Backup to File at ","Záloha do souboru na "}. {"Backup","Zálohovat"}. {"Bad format","Nesprávný formát"}. {"Birthday","Datum narození"}. +{"Both the username and the resource are required","Uživatelské jméno i zdroj jsou požadované položky"}. +{"Bytestream already activated","Bytestream již byl aktivován"}. +{"Cannot remove active list","Aktivní seznam nelze odebrat"}. +{"Cannot remove default list","Výchozí seznam nelze odebrat"}. {"CAPTCHA web page","Webová stránka CAPTCHA"}. {"Change Password","Změnit heslo"}. {"Change User Password","Změnit heslo uživatele"}. +{"Changing password is not allowed","Změna hesla není povolena"}. +{"Changing role/affiliation is not allowed","Změna role/příslušnosti není povolena"}. {"Characters not allowed:","Nepřípustné znaky:"}. {"Chatroom configuration modified","Nastavení diskuzní místnosti bylo změněno"}. -{"Chatroom is created","Konference vytvořena"}. -{"Chatroom is destroyed","Konference zrušena"}. -{"Chatroom is started","Konference spuštěna"}. -{"Chatroom is stopped","Konference zastavena"}. -{"Chatrooms","Konference"}. +{"Chatroom is created","Místnost vytvořena"}. +{"Chatroom is destroyed","Místnost zrušena"}. +{"Chatroom is started","Místnost spuštěna"}. +{"Chatroom is stopped","Místnost zastavena"}. +{"Chatrooms","Místnosti"}. {"Choose a username and password to register with this server","Zadejte jméno uživatele a heslo pro registraci na tomto serveru"}. -{"Choose modules to stop","Vyberte moduly, které mají být zastaveny"}. {"Choose storage type of tables","Vyberte typ úložiště pro tabulky"}. -{"Choose whether to approve this entity's subscription.","Zvolte, zda chcete schválit odebírání touto entitou"}. +{"Choose whether to approve this entity's subscription.","Zvolte, zda chcete schválit odebírání touto entitou."}. {"City","Město"}. {"Commands","Příkazy"}. -{"Conference room does not exist","Konferenční místnost neexistuje"}. -{"Configuration","Konfigurace"}. +{"Conference room does not exist","Místnost neexistuje"}. {"Configuration of room ~s","Konfigurace místnosti ~s"}. -{"Connected Resources:","Připojené zdroje:"}. -{"Connections parameters","Parametry spojení"}. +{"Configuration","Konfigurace"}. {"Country","Země"}. -{"CPU Time:","Čas procesoru"}. -{"Database","Databáze"}. -{"Database Tables at ~p","Databázové tabulky na ~p"}. +{"Database failure","Chyba databáze"}. {"Database Tables Configuration at ","Konfigurace databázových tabulek "}. +{"Database","Databáze"}. {"December",". prosince"}. {"Default users as participants","Uživatelé jsou implicitně členy"}. {"Delete message of the day on all hosts","Smazat zprávu dne na všech hostitelích"}. {"Delete message of the day","Smazat zprávu dne"}. -{"Delete Selected","Smazat vybrané"}. {"Delete User","Smazat uživatele"}. {"Deliver event notifications","Doručovat upozornění na události"}. {"Deliver payloads with event notifications","Doručovat náklad s upozorněním na událost"}. -{"Description:","Popis:"}. {"Disc only copy","Jen kopie disku"}. -{"Displayed Groups:","Zobrazené skupiny:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","Nikdy nikomu nesdělujte své heslo, ani administrátorovi serveru Jabberu."}. {"Dump Backup to Text File at ","Uložit zálohu do textového souboru na "}. {"Dump to Text File","Uložit do textového souboru"}. {"Edit Properties","Upravit vlastnosti"}. {"Either approve or decline the voice request.","Povolit nebo odmítnout voice žádost."}. -{"ejabberd IRC module","ejabberd IRC modul"}. {"ejabberd MUC module","ejabberd MUC modul"}. {"ejabberd Multicast service","Služba ejabberd Multicast"}. {"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe modul"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams modul"}. {"ejabberd vCard module","ejabberd vCard modul"}. {"ejabberd Web Admin","Webová administrace ejabberd"}. -{"Elements","Položek"}. {"Email","E-mail"}. -{"Empty Rooms","Prázdné konference"}. {"Enable logging","Zaznamenávat konverzace"}. {"Enable message archiving","Povolit ukládání historie zpráv"}. -{"Encoding for server ~b","Kódování pro server ~b"}. +{"Enabling push without 'node' attribute is not supported","Aktivováno push bez atributu 'node' není podporováno"}. {"End User Session","Ukončit sezení uživatele"}. -{"Enter list of {Module, [Options]}","Vložte seznam modulů {Modul, [Parametry]}"}. {"Enter nickname you want to register","Zadejte přezdívku, kterou chcete zaregistrovat"}. {"Enter path to backup file","Zadajte cestu k souboru se zálohou"}. {"Enter path to jabberd14 spool dir","Zadejte cestu k jabberd14 spool adresáři"}. {"Enter path to jabberd14 spool file","Zadejte cestu k spool souboru jabberd14"}. {"Enter path to text file","Zadajte cestu k textovému souboru"}. {"Enter the text you see","Zadejte text, který vidíte"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Zadejte přezdívku a kódování, které chcete používat pro připojení k serverům IRC. Stiskněte 'Další' pro více políček k vyplnění. Stiskněte 'Dokončit' pro uložení nastavení."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Zadejte přezdívku, kódování, porty a hesla, které chcete používat pro připojení k serverům IRC"}. -{"Erlang Jabber Server","Erlang Jabber Server"}. -{"Error","Chyba"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Příklad: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].2\"}]."}. {"Exclude Jabber IDs from CAPTCHA challenge","Vyloučit Jabber ID z procesu CAPTCHA ověřování"}. {"Export all tables as SQL queries to a file:","Zálohovat všechny tabulky jako SQL dotazy do souboru:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportovat všechny uživatele do souboru ve formátu PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Exportovat uživatele na hostiteli do souboru ve formátu PIEFXIS (XEP-0227):"}. +{"External component failure","Chyba externí komponenty"}. +{"External component timeout","Timeout externí komponenty"}. +{"Failed to activate bytestream","Chyba při aktivaci bytestreamu"}. {"Failed to extract JID from your voice request approval","Došlo k chybě při získávání Jabber ID z vaší žádosti o voice práva"}. +{"Failed to map delegated namespace to external component","Chyba při mapování namespace pro externí komponentu"}. +{"Failed to parse HTTP response","Chyba parsování HTTP odpovědi"}. +{"Failed to process option '~s'","Chyba při zpracování možnosti '~s'"}. {"Family Name","Příjmení"}. {"February",". února"}. -{"Fill in fields to search for any matching Jabber User","Vyplňte políčka pro vyhledání uživatele Jabberu"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Pro vyhledání uživatele Jabberu vyplňte formulář (na konec přidejte znak * pro vyhledání podřetězce)"}. +{"File larger than ~w bytes","Soubor větší než ~w bytů"}. {"Friday","Pátek"}. -{"From","Od"}. -{"From ~s","Od ~s"}. +{"From ~ts","Od ~ts"}. {"Full Name","Celé jméno"}. {"Get Number of Online Users","Získat počet online uživatelů"}. {"Get Number of Registered Users","Získat počet registrovaných uživatelů"}. {"Get User Last Login Time","Získat čas podleního přihlášení uživatele"}. -{"Get User Password","Získat heslo uživatele"}. {"Get User Statistics","Získat statistiky uživatele"}. +{"Given Name","Křestní jméno"}. {"Grant voice to this person?","Udělit voice práva této osobě?"}. -{"Group ","Skupina "}. -{"Groups","Skupiny"}. {"has been banned","byl(a) zablokován(a)"}. -{"has been kicked because of an affiliation change","byl(a) vyhozen(a) kvůli změně přiřazení"}. {"has been kicked because of a system shutdown","byl(a) vyhozen(a), protože dojde k vypnutí systému"}. +{"has been kicked because of an affiliation change","byl(a) vyhozen(a) kvůli změně přiřazení"}. {"has been kicked because the room has been changed to members-only","byl(a) vyhozen(a), protože mísnost je nyní pouze pro členy"}. {"has been kicked","byl(a) vyhozen(a) z místnosti"}. -{" has set the subject to: "," změnil(a) téma na: "}. -{"Host","Hostitel"}. +{"Host unknown","Neznámý hostitel"}. {"If you don't see the CAPTCHA image here, visit the web page.","Pokud zde nevidíte obrázek CAPTCHA, přejděte na webovou stránku."}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Pokud chcete zadat jiné kódování pro IRC servery, vyplňte seznam s hodnotami ve formátu '{\"irc server\",\"encoding\", port, \"password\"}'. Výchozí kódování pro tuto službu je \"~s\", port ~p, empty password."}. {"Import Directory","Import adresáře"}. {"Import File","Import souboru"}. {"Import user data from jabberd14 spool file:","Importovat uživatele z jabberd14 spool souborů:"}. @@ -143,32 +129,27 @@ {"Import users data from jabberd14 spool directory:","Importovat uživatele z jabberd14 spool souborů:"}. {"Import Users from Dir at ","Importovat uživatele z adresáře na "}. {"Import Users From jabberd14 Spool Files","Importovat uživatele z jabberd14 spool souborů"}. +{"Improper domain part of 'from' attribute","Nesprávná část s doménou atributu 'from'"}. {"Improper message type","Nesprávný typ zprávy"}. -{"Incoming s2s Connections:","Příchozí s2s spojení:"}. +{"Incorrect CAPTCHA submit","Nesprávné odeslání CAPTCHA"}. +{"Incorrect data form","Nesprávný datový formulář"}. {"Incorrect password","Nesprávné heslo"}. -{"Invalid affiliation: ~s","Neplatné přiřazení: ~s"}. -{"Invalid role: ~s","Neplatná role: ~s"}. +{"Incorrect value of 'action' attribute","Nesprávná hodnota atributu 'action'"}. +{"Incorrect value of 'action' in data form","Nesprávná hodnota atributu 'action' v datovém formuláři"}. +{"Incorrect value of 'path' in data form","Nesprávná hodnota atributu 'path' v datovém formuláři"}. +{"Installed Modules:","Instalované moduly:"}. +{"Insufficient privilege","Nedostatečné oprávnění"}. +{"Invalid 'from' attribute in forwarded message","Nesprávný atribut 'from' v přeposlané zprávě"}. +{"Invitations are not allowed in this conference","Pozvánky nejsou povoleny v této místnosti"}. {"IP addresses","IP adresy"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","IRC kanál (bez počátečního #)"}. -{"IRC server","IRC přezdívka"}. -{"IRC settings","Nastavení IRC"}. -{"IRC Transport","IRC transport"}. -{"IRC username","IRC přezdívka"}. -{"IRC Username","IRC přezdívka"}. {"is now known as","se přejmenoval(a) na"}. -{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Není povoleno posílat chybové zprávy do konference. Účastník (~s) odeslal chybovou zprávu (~s) a byl vyhozen z konference."}. -{"It is not allowed to send private messages","Je zakázáno posílat soukromé zprávy"}. -{"It is not allowed to send private messages of type \"groupchat\"","Není dovoleno odeslání soukromé zprávy typu \"skupinová zpráva\" "}. -{"It is not allowed to send private messages to the conference","Není povoleno odesílat soukromé zprávy do konference"}. -{"Jabber Account Registration","Registrace účtu Jabberu"}. +{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Není povoleno posílat chybové zprávy do místnosti. Účastník (~s) odeslal chybovou zprávu (~s) a byl vyhozen z místnosti"}. +{"It is not allowed to send private messages of type \"groupchat\"","Není dovoleno odeslání soukromých zpráv typu \"skupinová zpráva\""}. +{"It is not allowed to send private messages to the conference","Není povoleno odesílat soukromé zprávy v této místnosti"}. {"Jabber ID","Jabber ID"}. -{"Jabber ID ~s is invalid","Jabber ID ~s je neplatné"}. {"January",". ledna"}. -{"Join IRC channel","Vstoupit do IRC kanálu"}. +{"Joined MIX channels:","Připojené MIX kanály:"}. {"joins the room","vstoupil(a) do místnosti"}. -{"Join the IRC channel here.","Vstoupit do tohoto IRC kanálu."}. -{"Join the IRC channel in this Jabber ID: ~s","Vstupte do IRC kanálu s tímto Jabber ID: ~s"}. {"July",". července"}. {"June",". června"}. {"Last Activity","Poslední aktivita"}. @@ -176,11 +157,6 @@ {"Last month","Poslední měsíc"}. {"Last year","Poslední rok"}. {"leaves the room","opustil(a) místnost"}. -{"Listened Ports at ","Otevřené porty na "}. -{"Listened Ports","Otevřené porty"}. -{"List of modules to start","Seznam modulů, které mají být spuštěné"}. -{"List of rooms","Seznam konferencí"}. -{"Low level update script","Nízkoúrovňový aktualizační skript"}. {"Make participants list public","Nastavit seznam účastníků jako veřejný"}. {"Make room CAPTCHA protected","Chránit místnost pomocí CAPTCHA"}. {"Make room members-only","Zpřístupnit místnost jen členům"}. @@ -188,46 +164,63 @@ {"Make room password protected","Chránit místnost heslem"}. {"Make room persistent","Nastavit místnost jako stálou"}. {"Make room public searchable","Nastavit místnost jako veřejnou"}. +{"Malformed username","Chybně formátováné jméno uživatele"}. {"March",". března"}. -{"Maximum Number of Occupants","Počet účastníků"}. -{"Max # of items to persist","Maximální počet položek, které je možné natrvalo uložit"}. {"Max payload size in bytes","Maximální náklad v bajtech"}. +{"Maximum Number of Occupants","Maximální počet účastníků"}. {"May",". května"}. -{"Members:","Členové:"}. {"Membership is required to enter this room","Pro vstup do místnosti musíte být členem"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Svoje heslo si zapamatujte, nebo si jej poznamenejte na papírek a ten uschovejte v bezpečí. Jabber nemá žádný automatizovaný způsob obnovy hesla."}. -{"Memory","Paměť"}. {"Message body","Tělo zprávy"}. +{"Message not found in forwarded payload","Zpráva nebyla nalezena v přeposlaném obsahu"}. {"Middle Name","Druhé jméno"}. {"Minimum interval between voice requests (in seconds)","Minimální interval mezi žádostmi o voice práva (v sekundách)"}. -{"Moderator","Moderátor"}. {"Moderator privileges required","Potřebujete práva moderátora"}. -{"moderators only","moderátorům"}. -{"Modified modules","Aktualizované moduly"}. -{"Module","Modul"}. -{"Modules at ~p","Moduly v ~p"}. -{"Modules","Moduly"}. +{"Moderator","Moderátor"}. +{"Module failed to handle the query","Modul chyboval při zpracování dotazu"}. {"Monday","Pondělí"}. {"Multicast","Multicast"}. {"Multi-User Chat","Víceuživatelský chat"}. -{"Name:","Jméno:"}. {"Name","Jméno"}. +{"Neither 'jid' nor 'nick' attribute found","Nebyl nalezen atribut 'jid' ani 'nick'"}. +{"Neither 'role' nor 'affiliation' attribute found","Nebyl nalezen atribut 'role' ani 'affiliation'"}. {"Never","Nikdy"}. {"New Password:","Nové heslo:"}. -{"Nickname","Přezdívka"}. {"Nickname Registration at ","Registrace přezdívky na "}. {"Nickname ~s does not exist in the room","Přezdívka ~s v místnosti neexistuje"}. -{"nobody","nikdo"}. +{"Nickname","Přezdívka"}. +{"No 'affiliation' attribute found","Chybějící atribut 'affiliation'"}. +{"No available resource found","Nebyl nalezen žádný dostupný zdroj"}. {"No body provided for announce message","Zpráva neobsahuje text"}. +{"No data form found","Nebyl nalezen datový formulář"}. {"No Data","Žádná data"}. +{"No features available","Žádné funce nejsou dostupné"}. +{"No hook has processed this command","Žádný hook nebyl zpracován tímto příkazem"}. +{"No info about last activity found","Nebyla žádná informace o poslední aktivitě"}. +{"No 'item' element found","Element 'item' nebyl nalezen"}. +{"No items found in this query","Žádné položky nebyly nalezeny v tomto dotazu"}. +{"No limit","Bez limitu"}. +{"No module is handling this query","Žádný modul neobsluhuje tento dotaz"}. +{"No node specified","Žádný uzel nebyl specifikován"}. +{"No 'password' found in data form","Chybějící atribut 'password' v datovém formuláři"}. +{"No 'password' found in this query","Chybějící atribut 'password' v tomto dotazu"}. +{"No 'path' found in data form","Chybějící atribut 'path' v datovém formuláři"}. +{"No pending subscriptions found","Žádné čekající předplatné nebylo nalezeno"}. +{"No privacy list with this name found","Žádný privacy list s tímto jménem nebyl nalezen"}. +{"No private data found in this query","Žádná soukromá data nebyla nalezena tímto dotazem"}. +{"No running node found","Nebyl nalezen žádný běžící uzel"}. +{"No services available","Žádné služby nejsou dostupné"}. +{"No statistics found for this item","Nebyly nalezeny statistiky pro uvedenou položku"}. +{"No 'to' attribute found in the invitation","Chybějící atribut 'to' v pozvánce"}. +{"Node already exists","Uzel již existuje"}. {"Node ID","ID uzlu"}. +{"Node index not found","Index uzlu nebyl nalezen"}. {"Node not found","Uzel nenalezen"}. {"Node ~p","Uzel ~p"}. +{"Nodeprep has failed","Nodeprep chyboval"}. {"Nodes","Uzly"}. -{"No limit","Bez limitu"}. {"None","Nic"}. -{"No resource provided","Nebyl poskytnut žádný zdroj"}. {"Not Found","Nenalezeno"}. +{"Not subscribed","Není odebíráno"}. {"Notify subscribers when items are removed from the node","Upozornit odběratele na odstranění položek z uzlu"}. {"Notify subscribers when the node configuration changes","Upozornit odběratele na změnu nastavení uzlu"}. {"Notify subscribers when the node is deleted","Upozornit odběratele na smazání uzlu"}. @@ -236,79 +229,61 @@ {"Number of online users","Počet online uživatelů"}. {"Number of registered users","Počet registrovaných uživatelů"}. {"October",". října"}. -{"Offline Messages:","Offline zprávy:"}. -{"Offline Messages","Offline zprávy"}. {"OK","OK"}. {"Old Password:","Současné heslo:"}. +{"Online Users","Připojení uživatelé"}. {"Online","Online"}. -{"Online Users:","Online uživatelé:"}. -{"Online Users","Online uživatelé"}. {"Only deliver notifications to available users","Doručovat upozornění jen právě přihlášeným uživatelům"}. +{"Only or tags are allowed","Pouze značky nebo jsou povoleny"}. +{"Only element is allowed in this query","Pouze element je povolen v tomto dotazu"}. {"Only members may query archives of this room","Pouze moderátoři mají povoleno měnit téma místnosti"}. {"Only moderators and participants are allowed to change the subject in this room","Jen moderátoři a účastníci mají povoleno měnit téma této místnosti"}. {"Only moderators are allowed to change the subject in this room","Jen moderátoři mají povoleno měnit téma místnosti"}. {"Only moderators can approve voice requests","Pouze moderátoři mohou schválit žádosti o voice práva"}. -{"Only occupants are allowed to send messages to the conference","Jen členové mají povolené zasílat zprávy do konference"}. -{"Only occupants are allowed to send queries to the conference","Jen členové mohou odesílat požadavky (query) do konference"}. +{"Only occupants are allowed to send messages to the conference","Jen členové mají povolené zasílat zprávy do místnosti"}. +{"Only occupants are allowed to send queries to the conference","Jen členové mohou odesílat požadavky (query) do místnosti"}. {"Only service administrators are allowed to send service messages","Pouze správci služby smí odesílat servisní zprávy"}. -{"Options","Nastavení"}. {"Organization Name","Název firmy"}. {"Organization Unit","Oddělení"}. -{"Outgoing s2s Connections:","Odchozí s2s spojení:"}. +{"Other Modules Available:","Ostatní dostupné moduly:"}. {"Outgoing s2s Connections","Odchozí s2s spojení"}. {"Owner privileges required","Jsou vyžadována práva vlastníka"}. -{"Packet","Paket"}. {"Participant","Účastník"}. -{"Password ~b","Heslo ~b"}. -{"Password:","Heslo:"}. -{"Password","Heslo"}. -{"Password Verification:","Ověření hesla:"}. {"Password Verification","Ověření hesla"}. +{"Password Verification:","Ověření hesla:"}. +{"Password","Heslo"}. +{"Password:","Heslo:"}. {"Path to Dir","Cesta k adresáři"}. {"Path to File","Cesta k souboru"}. -{"Pending","Čekající"}. {"Period: ","Čas: "}. -{"Permanent rooms","Stálých konferencí"}. {"Persist items to storage","Uložit položky natrvalo do úložiště"}. +{"Ping query is incorrect","Ping dotaz je nesprávný"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Podotýkáme, že tato nastavení budou zálohována do zabudované databáze Mnesia. Pokud používáte ODBC modul, musíte zálohovat svoji SQL databázi samostatně."}. -{"Please specify file name.","Zvolit jméno souboru."}. -{"Please specify file size.","Zvolit velikost souboru."}. {"Please, wait for a while before sending new voice request","Prosím, počkejte chvíli před posláním nové žádosti o voice práva"}. {"Pong","Pong"}. -{"Port ~b","Port ~b"}. -{"Port","Port"}. {"Present real Jabber IDs to","Odhalovat skutečná Jabber ID"}. {"private, ","soukromá, "}. -{"Protocol","Protokol"}. {"Publish-Subscribe","Publish-Subscribe"}. {"PubSub subscriber request","Žádost odběratele PubSub"}. {"Purge all items when the relevant publisher goes offline","Smazat všechny položky, pokud se příslušný poskytovatel odpojí"}. -{"Queries to the conference members are not allowed in this room","Požadavky (queries) na členy konference nejsou v této místnosti povolené"}. +{"Queries to the conference members are not allowed in this room","Požadavky (queries) na členy místnosti nejsou v této místnosti povolené"}. +{"Query to another users is forbidden","Dotaz na jiné uživatele je zakázán"}. {"RAM and disc copy","Kopie RAM a disku"}. {"RAM copy","Kopie RAM"}. -{"Raw","Zdroj"}. {"Really delete message of the day?","Skutečně smazat zprávu dne?"}. -{"Recipient is not in the conference room","Příjemce se nenachází v konferenční místnosti"}. -{"Register a Jabber account","Zaregistrujte si účet Jabberu"}. -{"Registered nicknames","Registrované přezdívky"}. -{"Registered Users","Registrovaní uživatelé"}. -{"Registered Users:","Registrovaní živatelé:"}. +{"Recipient is not in the conference room","Příjemce se nenachází v místnosti"}. {"Register","Zaregistrovat se"}. -{"Registration in mod_irc for ","Registrace do mod_irc na "}. {"Remote copy","Vzdálená kopie"}. -{"Remove All Offline Messages","Odstranit všechny offline zprávy"}. -{"Remove","Odstranit"}. {"Remove User","Odstranit uživatele"}. {"Replaced by new connection","Nahrazeno novým spojením"}. {"Resources","Zdroje"}. -{"Restart","Restart"}. {"Restart Service","Restartovat službu"}. {"Restore Backup from File at ","Obnovit zálohu ze souboru na "}. -{"Restore binary backup after next ejabberd restart (requires less memory):","Obnovit binární zálohu při následujícím restartu ejabberd (vyžaduje méně paměti)"}. +{"Restore binary backup after next ejabberd restart (requires less memory):","Obnovit binární zálohu při následujícím restartu ejabberd (vyžaduje méně paměti):"}. {"Restore binary backup immediately:","Okamžitě obnovit binární zálohu:"}. -{"Restore","Obnovit"}. {"Restore plain text backup immediately:","Okamžitě obnovit zálohu z textového souboru:"}. +{"Restore","Obnovit"}. {"Roles for which Presence is Broadcasted","Role, pro které je zpráva o stavu šířena"}. {"Room Configuration","Nastavení místnosti"}. {"Room creation is denied by service policy","Pravidla služby nepovolují vytvořit místnost"}. @@ -316,124 +291,106 @@ {"Room Occupants","Počet účastníků"}. {"Room title","Název místnosti"}. {"Roster groups allowed to subscribe","Skupiny kontaktů, které mohou odebírat"}. -{"Roster of ","Seznam kontaktů "}. -{"Roster","Seznam kontaktů"}. {"Roster size","Velikost seznamu kontaktů"}. -{"RPC Call Error","Chyba RPC volání"}. {"Running Nodes","Běžící uzly"}. -{"~s access rule configuration","~s konfigurace pravidla přístupu"}. {"Saturday","Sobota"}. -{"Script check","Kontrola skriptu"}. {"Search Results for ","Výsledky hledání pro "}. {"Search users in ","Hledat uživatele v "}. -{"Send announcement to all online users","Odeslat oznámení všem online uživatelům"}. {"Send announcement to all online users on all hosts","Odeslat oznámení všem online uživatelům na všech hostitelích"}. -{"Send announcement to all users","Odeslat oznámení všem uživatelům"}. +{"Send announcement to all online users","Odeslat oznámení všem online uživatelům"}. {"Send announcement to all users on all hosts","Odeslat oznámení všem uživatelům na všech hostitelích"}. +{"Send announcement to all users","Odeslat oznámení všem uživatelům"}. {"September",". září"}. -{"Server ~b","Server ~b"}. {"Server:","Server:"}. {"Set message of the day and send to online users","Nastavit zprávu dne a odeslat ji online uživatelům"}. -{"Set message of the day on all hosts and send to online users","Nastavit zprávu dne a odeslat ji online uživatelům"}. +{"Set message of the day on all hosts and send to online users","Nastavit zprávu dne na všech hostitelích a odeslat ji online uživatelům"}. {"Shared Roster Groups","Skupiny pro sdílený seznam kontaktů"}. {"Show Integral Table","Zobrazit kompletní tabulku"}. {"Show Ordinary Table","Zobrazit běžnou tabulku"}. {"Shut Down Service","Vypnout službu"}. -{"~s invites you to the room ~s","~s vás zve do místnosti ~s"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Někteří klienti umí uložit vaše heslo na disk počítače. Tuto funkci používejte, pouze pokud věříte zabezpečení svého počítače."}. {"Specify the access model","Uveďte přístupový model"}. {"Specify the event message type","Zvolte typ zpráv pro události"}. {"Specify the publisher model","Specifikovat model pro publikování"}. -{"~s's Offline Messages Queue","Fronta offline zpráv uživatele ~s"}. -{"Start Modules at ","Spustit moduly na "}. -{"Start Modules","Spustit moduly"}. -{"Start","Start"}. -{"Statistics of ~p","Statistiky ~p"}. -{"Statistics","Statistiky"}. -{"Stop Modules at ","Zastavit moduly na "}. -{"Stop Modules","Zastavit moduly"}. {"Stopped Nodes","Zastavené uzly"}. -{"Stop","Stop"}. -{"Storage Type","Typ úložiště"}. {"Store binary backup:","Uložit binární zálohu:"}. {"Store plain text backup:","Uložit zálohu do textového souboru:"}. {"Subject","Předmět"}. -{"Submit","Odeslat"}. {"Submitted","Odeslané"}. {"Subscriber Address","Adresa odběratele"}. -{"Subscription","Přihlášení"}. +{"Subscriptions are not allowed","Předplatné není povoleno"}. {"Sunday","Neděle"}. {"That nickname is already in use by another occupant","Přezdívka je již používána jiným členem"}. {"That nickname is registered by another person","Přezdívka je zaregistrována jinou osobou"}. +{"The account was not unregistered","Účet nebyl smazán"}. {"The CAPTCHA is valid.","CAPTCHA souhlasí."}. {"The CAPTCHA verification has failed","Ověření CAPTCHA se nezdařilo"}. {"The collections with which a node is affiliated","Kolekce, se kterými je uzel spřízněn"}. -{"the password is","heslo je"}. +{"The feature requested is not supported by the conference","Požadovaná vlastnost není podporována touto místností"}. +{"The number of subscribers to the node","Počet odběratelů uzlu"}. +{"The password contains unacceptable characters","Heslo obsahuje nepovolené znaky"}. {"The password is too weak","Heslo je příliš slabé"}. -{"The password of your Jabber account was successfully changed.","Heslo vašeho účtu Jabberu bylo úspěšně změněno."}. +{"the password is","heslo je"}. +{"The query is only allowed from local users","Dotaz je povolen pouze pro místní uživatele"}. +{"The query must not contain elements","Dotaz nesmí obsahovat elementy "}. +{"The stanza MUST contain only one element, one element, or one element","Stanza MUSÍ obsahovat pouze jeden element , jeden element nebo jeden element "}. {"There was an error changing the password: ","Při změně hesla došlo k chybě: "}. -{"There was an error creating the account: ","Při vytváření účtu došlo k chybě."}. +{"There was an error creating the account: ","Při vytváření účtu došlo k chybě: "}. {"There was an error deleting the account: ","Při mazání účtu došlo k chybě: "}. -{"This IP address is blacklisted in ~s","IP adresa je blokována na ~s"}. -{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Zde nezáleží na velikosti písmen: macbeth je stejný jako MacBeth a Macbeth."}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Na této stránce si můžete vytvořit účet na tomto serveru Jabberu. Vaše JID (Jabber IDentifikátor) bude mít tvar: uživatelskéjméno@server. Přečtěte si prosím pozorně instrukce pro vyplnění údajů."}. -{"This page allows to unregister a Jabber account in this Jabber server.","Zde můžete zrušit registraci účtu na tomto serveru Jabberu."}. +{"This room is not anonymous","Tato místnost není anonymní"}. {"Thursday","Čtvrtek"}. -{"Time","Čas"}. {"Time delay","Časový posun"}. +{"To register, visit ~s","Pokud se chcete zaregistrovat, navštivte ~s"}. +{"Token TTL","Token TTL"}. +{"Too many active bytestreams","Příliš mnoho aktivních bytestreamů"}. {"Too many CAPTCHA requests","Přiliš mnoho CAPTCHA žádostí"}. +{"Too many elements","Příliš mnoho elementů "}. +{"Too many elements","Přilíš mnoho elementů "}. {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Příliš mnoho (~p) chybných pokusů o přihlášení z této IP adresy (~s). Adresa bude zablokována do ~s UTC"}. {"Too many unacked stanzas","Příliš mnoho nepotvrzených stanz"}. -{"To","Pro"}. -{"To ~s","Pro ~s"}. -{"Total rooms","Celkem konferencí"}. +{"Too many users in this conference","Přiliš mnoho uživatelů v této místnosti"}. {"Traffic rate limit is exceeded","Byl překročen limit"}. -{"Transactions Aborted:","Transakce zrušena"}. -{"Transactions Committed:","Transakce potvrzena"}. -{"Transactions Logged:","Transakce zaznamenána"}. -{"Transactions Restarted:","Transakce restartována"}. {"Tuesday","Úterý"}. {"Unable to generate a CAPTCHA","Nebylo možné vygenerovat CAPTCHA"}. +{"Unable to register route on existing local domain","Není možné zaregistrovat routu na existující místní doménu"}. {"Unauthorized","Nemáte oprávnění"}. -{"Unregister a Jabber account","Zrušte registraci účtu Jabberu"}. +{"Unexpected action","Neočekávaná akce"}. {"Unregister","Zrušit registraci"}. -{"Update","Aktualizovat"}. +{"Unsupported element","Nepodporovaný element"}. {"Update message of the day (don't send)","Aktualizovat zprávu dne (neodesílat)"}. {"Update message of the day on all hosts (don't send)","Aktualizovat zprávu dne pro všechny hostitele (neodesílat)"}. -{"Update ~p","Aktualizovat ~p"}. -{"Update plan","Aktualizovat plán"}. -{"Update script","Aktualizované skripty"}. -{"Uptime:","Čas běhu:"}. -{"Use of STARTTLS required","Je vyžadováno STARTTLS"}. +{"User already exists","Uživatel již existuje"}. {"User JID","Jabber ID uživatele"}. +{"User (jid)","Uživatel (JID)"}. {"User Management","Správa uživatelů"}. +{"User session not found","Sezení uživatele nebylo nalezeno"}. +{"User session terminated","Sezení uživatele bylo ukončeno"}. {"Username:","Uživatelské jméno:"}. {"Users are not allowed to register accounts so quickly","Je zakázáno registrovat účty v tak rychlém sledu"}. {"Users Last Activity","Poslední aktivita uživatele"}. {"Users","Uživatelé"}. -{"User ~s","Uživatel ~s"}. {"User","Uživatel"}. -{"Validate","Ověřit"}. -{"vCard User Search","Hledání uživatelů podle vizitek"}. +{"Value 'get' of 'type' attribute is not allowed","Hodnota 'get' atrubutu 'type' není povolena"}. +{"Value of '~s' should be boolean","Hodnota '~s' by měla být boolean"}. +{"Value of '~s' should be datetime string","Hodnota '~s' by měla být datetime řetězec"}. +{"Value of '~s' should be integer","Hodnota '~s' by měla být celé číslo"}. +{"Value 'set' of 'type' attribute is not allowed","Hodnota 'set' atrubutu 'type' není povolena"}. +{"vCard User Search","Hledání uživatelů ve vizitkách"}. {"Virtual Hosts","Virtuální hostitelé"}. {"Visitor","Návštěvník"}. {"Visitors are not allowed to change their nicknames in this room","Návštěvníkům této místnosti je zakázáno měnit přezdívku"}. -{"Visitors are not allowed to send messages to all occupants","Návštevníci nemají povoleno zasílat zprávy všem účastníkům konference"}. -{"Voice requests are disabled in this conference","Voice žádosti jsou v této konferenci zakázány"}. +{"Visitors are not allowed to send messages to all occupants","Návštevníci nemají povoleno zasílat zprávy všem účastníkům v této místnosti"}. +{"Voice requests are disabled in this conference","Voice žádosti jsou v této místnosti zakázány"}. {"Voice request","Žádost o voice práva"}. {"Wednesday","Středa"}. {"When to send the last published item","Kdy odeslat poslední publikovanou položku"}. {"Whether to allow subscriptions","Povolit odebírání"}. -{"You can later change your password using a Jabber client.","Později můžete své heslo změnit pomocí klienta Jabberu."}. {"You have been banned from this room","Byl jste vyloučen z této místnosti"}. +{"You have joined too many conferences","Vstoupil jste do příliš velkého množství místností"}. {"You must fill in field \"Nickname\" in the form","Musíte vyplnit políčko \"Přezdívka\" ve formuláři"}. {"You need a client that supports x:data and CAPTCHA to register","Pro registraci potřebujete klienta s podporou x:data a CAPTCHA"}. {"You need a client that supports x:data to register the nickname","Pro registraci přezdívky potřebujete klienta s podporou x:data"}. -{"You need an x:data capable client to configure mod_irc settings","Pro konfiguraci mod_irc potřebujete klienta s podporou x:data"}. -{"You need an x:data capable client to configure room","Ke konfiguraci místnosti potřebujete klienta podporujícího x:data"}. {"You need an x:data capable client to search","K vyhledávání potřebujete klienta podporujícího x:data"}. {"Your active privacy list has denied the routing of this stanza.","Vaše nastavení soukromí znemožnilo směrování této stance."}. {"Your contact offline message queue is full. The message has been discarded.","Fronta offline zpráv pro váš kontakt je plná. Zpráva byla zahozena."}. -{"Your Jabber account was successfully created.","Váš účet Jabberu byl úspěšně vytvořen."}. -{"Your Jabber account was successfully deleted.","Váš účet Jabberu byl úspěšně smazán."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","Nesmíte posílat zprávy na ~s. Pro povolení navštivte ~s"}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Nesmíte posílat zprávy na ~s. Pro povolení navštivte ~s"}. +{"You're not allowed to create nodes","Nemáte povoleno vytvářet uzly"}. diff --git a/priv/msgs/cs.po b/priv/msgs/cs.po deleted file mode 100644 index 4f06621ac..000000000 --- a/priv/msgs/cs.po +++ /dev/null @@ -1,1911 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: 2.1.0-alpha\n" -"Last-Translator: Lukáš Polívka [spike411] xmpp:spike411@jabber.cz\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Czech (čeština)\n" -"X-Additional-Translator: Milos Svasek [DuxforD] from openheads.net\n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Language-Team: \n" -"Language: cs\n" -"X-Generator: Poedit 1.8.6\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "Je vyžadováno STARTTLS" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Nebyl poskytnut žádný zdroj" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Nahrazeno novým spojením" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "byl(a) vyhozen(a) z místnosti" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "Vaše nastavení soukromí znemožnilo směrování této stance." - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "Příliš mnoho nepotvrzených stanz" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Zadejte text, který vidíte" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "Nesmíte posílat zprávy na ~s. Pro povolení navštivte ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "Pokud zde nevidíte obrázek CAPTCHA, přejděte na webovou stránku." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "Webová stránka CAPTCHA" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "CAPTCHA souhlasí." - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Uživatel" - -#: ejabberd_oauth.erl:256 -msgid "Server" -msgstr "Server" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Heslo" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "Přijmout" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "Nemáte oprávnění" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "Webová administrace ejabberd" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Administrace" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Seznamy přístupových práv (ACL)" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Odeslané" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Nesprávný formát" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Odeslat" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "Zdroj" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Smazat vybrané" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Pravidla přístupů" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "~s konfigurace pravidla přístupu" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "Virtuální hostitelé" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Uživatelé" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 mod_configure.erl:524 -msgid "Online Users" -msgstr "Online uživatelé" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Poslední aktivita uživatele" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Čas: " - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "Poslední měsíc" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "Poslední rok" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "Všechny aktivity" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Zobrazit běžnou tabulku" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Zobrazit kompletní tabulku" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Statistiky" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "Nenalezeno" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Uzel nenalezen" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Přidat nový" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Hostitel" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Registrovaní uživatelé" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Přidat uživatele" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Offline zprávy" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Poslední aktivita" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Nikdy" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Online" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Registrovaní živatelé:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Online uživatelé:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Odchozí s2s spojení:" - -#: ejabberd_web_admin.erl:1559 -msgid "Incoming s2s Connections:" -msgstr "Příchozí s2s spojení:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Nic" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Změnit heslo" - -#: ejabberd_web_admin.erl:1673 -msgid "User ~s" -msgstr "Uživatel ~s" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Připojené zdroje:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Heslo:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Odstranit uživatele" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Žádná data" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Uzly" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Běžící uzly" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Zastavené uzly" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -msgid "Node ~p" -msgstr "Uzel ~p" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "Databáze" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Zálohovat" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Otevřené porty" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Aktualizovat" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Restart" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Stop" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Moduly" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "Chyba RPC volání" - -#: ejabberd_web_admin.erl:1917 -msgid "Database Tables at ~p" -msgstr "Databázové tabulky na ~p" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Jméno" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Typ úložiště" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Položek" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Paměť" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Chyba" - -#: ejabberd_web_admin.erl:1955 -msgid "Backup of ~p" -msgstr "Záloha ~p" - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Podotýkáme, že tato nastavení budou zálohována do zabudované databáze " -"Mnesia. Pokud používáte ODBC modul, musíte zálohovat svoji SQL databázi " -"samostatně." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Uložit binární zálohu:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "OK" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Okamžitě obnovit binární zálohu:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"Obnovit binární zálohu při následujícím restartu ejabberd (vyžaduje méně " -"paměti)" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Uložit zálohu do textového souboru:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Okamžitě obnovit zálohu z textového souboru:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "Importovat uživatele ze souboru ve formátu PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "Exportovat všechny uživatele do souboru ve formátu PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "" -"Exportovat uživatele na hostiteli do souboru ve formátu PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "Zálohovat všechny tabulky jako SQL dotazy do souboru:" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Importovat uživatele z jabberd14 spool souborů:" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Importovat uživatele z jabberd14 spool souborů:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Otevřené porty na " - -#: ejabberd_web_admin.erl:2144 -msgid "Modules at ~p" -msgstr "Moduly v ~p" - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "Statistiky ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Čas běhu:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "Čas procesoru" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Transakce potvrzena" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Transakce zrušena" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Transakce restartována" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Transakce zaznamenána" - -#: ejabberd_web_admin.erl:2243 -msgid "Update ~p" -msgstr "Aktualizovat ~p" - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "Aktualizovat plán" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Aktualizované moduly" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Aktualizované skripty" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Nízkoúrovňový aktualizační skript" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Kontrola skriptu" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Port" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Protokol" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Modul" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Nastavení" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Start" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Příkazy" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "Skutečně smazat zprávu dne?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Předmět" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Tělo zprávy" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "Zpráva neobsahuje text" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Oznámení" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Odeslat oznámení všem uživatelům" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Odeslat oznámení všem uživatelům na všech hostitelích" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Odeslat oznámení všem online uživatelům" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "Odeslat oznámení všem online uživatelům na všech hostitelích" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Nastavit zprávu dne a odeslat ji online uživatelům" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "Nastavit zprávu dne a odeslat ji online uživatelům" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Aktualizovat zprávu dne (neodesílat)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "Aktualizovat zprávu dne pro všechny hostitele (neodesílat)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Smazat zprávu dne" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Smazat zprávu dne na všech hostitelích" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Konfigurace" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Spustit moduly" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Zastavit moduly" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Obnovit" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Uložit do textového souboru" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Import souboru" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Import adresáře" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Restartovat službu" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Vypnout službu" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Smazat uživatele" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Ukončit sezení uživatele" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Získat heslo uživatele" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Změnit heslo uživatele" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "Získat čas podleního přihlášení uživatele" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Získat statistiky uživatele" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Získat počet registrovaných uživatelů" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Získat počet online uživatelů" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Správa uživatelů" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Všichni uživatelé" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Odchozí s2s spojení" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Správa zálohování" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Importovat uživatele z jabberd14 spool souborů" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "Pro ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "Od ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Konfigurace databázových tabulek " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Vyberte typ úložiště pro tabulky" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Jen kopie disku" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "Kopie RAM a disku" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "Kopie RAM" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "Vzdálená kopie" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Zastavit moduly na " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Vyberte moduly, které mají být zastaveny" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Spustit moduly na " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Vložte seznam modulů {Modul, [Parametry]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Seznam modulů, které mají být spuštěné" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Záloha do souboru na " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Zadajte cestu k souboru se zálohou" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Cesta k souboru" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Obnovit zálohu ze souboru na " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Uložit zálohu do textového souboru na " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Zadajte cestu k textovému souboru" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Importovat uživatele ze souboru na " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Zadejte cestu k spool souboru jabberd14" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Importovat uživatele z adresáře na " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Zadejte cestu k jabberd14 spool adresáři" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Cesta k adresáři" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "Časový posun" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Konfigurace seznamu přístupových práv (ACL)" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Seznamy přístupových práv (ACL)" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Konfigurace přístupů" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Pravidla přístupů" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Ověření hesla" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Počet registrovaných uživatelů" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Počet online uživatelů" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Poslední přihlášení" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "Velikost seznamu kontaktů" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "IP adresy" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Zdroje" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Administrace " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Akce aplikovaná na uživatele" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Upravit vlastnosti" - -#: mod_fail2ban.erl:95 -msgid "" -"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" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "Zvolit velikost souboru." - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "Zvolit jméno souboru." - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "IP adresa je blokována na ~s" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "Přístup byl zamítnut nastavením služby" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "IRC transport" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "ejabberd IRC modul" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "Pro konfiguraci mod_irc potřebujete klienta s podporou x:data" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Registrace do mod_irc na " - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Zadejte přezdívku, kódování, porty a hesla, které chcete používat pro " -"připojení k serverům IRC" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "IRC přezdívka" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Pokud chcete zadat jiné kódování pro IRC servery, vyplňte seznam s hodnotami " -"ve formátu '{\"irc server\",\"encoding\", port, \"password\"}'. Výchozí " -"kódování pro tuto službu je \"~s\", port ~p, empty password." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Příklad: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].2\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Parametry spojení" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "Vstoupit do IRC kanálu" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "IRC kanál (bez počátečního #)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "IRC přezdívka" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Vstoupit do tohoto IRC kanálu." - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Vstupte do IRC kanálu s tímto Jabber ID: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "Nastavení IRC" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Zadejte přezdívku a kódování, které chcete používat pro připojení k serverům " -"IRC. Stiskněte 'Další' pro více políček k vyplnění. Stiskněte 'Dokončit' pro " -"uložení nastavení." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "IRC přezdívka" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Heslo ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Port ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Kódování pro server ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Server ~b" - -#: mod_mam.erl:541 -msgid "Only members may query archives of this room" -msgstr "Pouze moderátoři mají povoleno měnit téma místnosti" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "Pouze správci služby smí odesílat servisní zprávy" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "Pravidla služby nepovolují vytvořit místnost" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "Konferenční místnost neexistuje" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Konference" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "Prázdné konference" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "Pro registraci přezdívky potřebujete klienta s podporou x:data" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Registrace přezdívky na " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Zadejte přezdívku, kterou chcete zaregistrovat" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Přezdívka" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "Přezdívka je zaregistrována jinou osobou" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Musíte vyplnit políčko \"Přezdívka\" ve formuláři" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "ejabberd MUC modul" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "Víceuživatelský chat" - -#: mod_muc_admin.erl:249 -msgid "Total rooms" -msgstr "Celkem konferencí" - -#: mod_muc_admin.erl:250 -msgid "Permanent rooms" -msgstr "Stálých konferencí" - -#: mod_muc_admin.erl:251 -msgid "Registered nicknames" -msgstr "Registrované přezdívky" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "Seznam konferencí" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "Nastavení diskuzní místnosti bylo změněno" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "vstoupil(a) do místnosti" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "opustil(a) místnost" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "byl(a) zablokován(a)" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "byl(a) vyhozen(a) kvůli změně přiřazení" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "byl(a) vyhozen(a), protože mísnost je nyní pouze pro členy" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "byl(a) vyhozen(a), protože dojde k vypnutí systému" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "se přejmenoval(a) na" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " změnil(a) téma na: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "Konference vytvořena" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "Konference zrušena" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "Konference spuštěna" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "Konference zastavena" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "Pondělí" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "Úterý" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "Středa" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "Čtvrtek" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "Pátek" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "Sobota" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "Neděle" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr ". ledna" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr ". února" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr ". března" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr ". dubna" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr ". května" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr ". června" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr ". července" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr ". srpna" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr ". září" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr ". října" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr ". listopadu" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr ". prosince" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Nastavení místnosti" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "Počet účastníků" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "Byl překročen limit" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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 konference. Účastník (~s) odeslal " -"chybovou zprávu (~s) a byl vyhozen z konference." - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "Není povoleno odesílat soukromé zprávy do konference" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "Prosím, počkejte chvíli před posláním nové žádosti o voice práva" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "Voice žádosti jsou v této konferenci zakázány" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "Došlo k chybě při získávání Jabber ID z vaší žádosti o voice práva" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "Pouze moderátoři mohou schválit žádosti o voice práva" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Nesprávný typ zprávy" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "Není dovoleno odeslání soukromé zprávy typu \"skupinová zpráva\" " - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "Příjemce se nenachází v konferenční místnosti" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "Je zakázáno posílat soukromé zprávy" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Jen členové mají povolené zasílat zprávy do konference" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "Jen členové mohou odesílat požadavky (query) do konference" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "" -"Požadavky (queries) na členy konference nejsou v této místnosti povolené" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "Jen moderátoři a účastníci mají povoleno měnit téma této místnosti" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Jen moderátoři mají povoleno měnit téma místnosti" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "Návštevníci nemají povoleno zasílat zprávy všem účastníkům konference" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "Návštěvníkům této místnosti je zakázáno měnit přezdívku" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "Přezdívka je již používána jiným členem" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Byl jste vyloučen z této místnosti" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "Pro vstup do místnosti musíte být členem" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "Pro vstup do místnosti musíte zadat heslo" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "Přiliš mnoho CAPTCHA žádostí" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "Nebylo možné vygenerovat CAPTCHA" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Nesprávné heslo" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "Potřebujete práva administrátora" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "Potřebujete práva moderátora" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "Jabber ID ~s je neplatné" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "Přezdívka ~s v místnosti neexistuje" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Neplatné přiřazení: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Neplatná role: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "Jsou vyžadována práva vlastníka" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "Konfigurace místnosti ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Název místnosti" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "Popis místnosti" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Nastavit místnost jako stálou" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Nastavit místnost jako veřejnou" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "Nastavit seznam účastníků jako veřejný" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Chránit místnost heslem" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Počet účastníků" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Bez limitu" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Odhalovat skutečná Jabber ID" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "moderátorům" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "každému" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "Role, pro které je zpráva o stavu šířena" - -#: mod_muc_room.erl:3486 -msgid "Moderator" -msgstr "Moderátor" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "Účastník" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "Návštěvník" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Zpřístupnit místnost jen členům" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Nastavit místnost jako moderovanou" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Uživatelé jsou implicitně členy" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Povolit uživatelům měnit téma místnosti" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Povolit uživatelům odesílat soukromé zprávy" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "Povolit návštěvníkům odesílat soukromé zprávy" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "nikdo" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Povolit uživatelům odesílat požadavky (query) ostatním uživatelům" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Povolit uživatelům posílání pozvánek" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "Povolit návštěvníkům posílat stavové zprávy ve statusu" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Povolit návštěvníkům měnit přezdívku" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "Povolit uživatelům posílat žádosti o voice práva" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "Minimální interval mezi žádostmi o voice práva (v sekundách)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "Chránit místnost pomocí CAPTCHA" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "Povolit ukládání historie zpráv" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "Vyloučit Jabber ID z procesu CAPTCHA ověřování" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Zaznamenávat konverzace" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "Ke konfiguraci místnosti potřebujete klienta podporujícího x:data" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Počet účastníků" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "soukromá, " - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "Žádost o voice práva" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "Povolit nebo odmítnout voice žádost." - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "Jabber ID uživatele" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "Udělit voice práva této osobě?" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s vás zve do místnosti ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "heslo je" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "Multicast" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "Služba ejabberd Multicast" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "Fronta offline zpráv pro váš kontakt je plná. Zpráva byla zahozena." - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "Fronta offline zpráv uživatele ~s" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Čas" - -#: mod_offline.erl:812 -msgid "From" -msgstr "Od" - -#: mod_offline.erl:813 -msgid "To" -msgstr "Pro" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Paket" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Offline zprávy:" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "Odstranit všechny offline zprávy" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "ejabberd SOCKS5 Bytestreams modul" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Publish-Subscribe" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "ejabberd Publish-Subscribe modul" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "Žádost odběratele PubSub" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Zvolte, zda chcete schválit odebírání touto entitou" - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "ID uzlu" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Adresa odběratele" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "Povolit tomuto Jabber ID odebírat tento pubsub uzel?" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Doručovat náklad s upozorněním na událost" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Doručovat upozornění na události" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Upozornit odběratele na změnu nastavení uzlu" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Upozornit odběratele na smazání uzlu" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Upozornit odběratele na odstranění položek z uzlu" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Uložit položky natrvalo do úložiště" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "Přívětivé jméno pro uzel" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Maximální počet položek, které je možné natrvalo uložit" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Povolit odebírání" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Uveďte přístupový model" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Skupiny kontaktů, které mohou odebírat" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Specifikovat model pro publikování" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "Smazat všechny položky, pokud se příslušný poskytovatel odpojí" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "Zvolte typ zpráv pro události" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Maximální náklad v bajtech" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "Kdy odeslat poslední publikovanou položku" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Doručovat upozornění jen právě přihlášeným uživatelům" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "Kolekce, se kterými je uzel spřízněn" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "Ověření CAPTCHA se nezdařilo" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "Pro registraci potřebujete klienta s podporou x:data a CAPTCHA" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "Zadejte jméno uživatele a heslo pro registraci na tomto serveru" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "Heslo je příliš slabé" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "Je zakázáno registrovat účty v tak rychlém sledu" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "Váš účet Jabberu byl úspěšně vytvořen." - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "Při vytváření účtu došlo k chybě." - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "Váš účet Jabberu byl úspěšně smazán." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "Při mazání účtu došlo k chybě: " - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "Heslo vašeho účtu Jabberu bylo úspěšně změněno." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "Při změně hesla došlo k chybě: " - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Registrace účtu Jabberu" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Zaregistrujte si účet Jabberu" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Zrušte registraci účtu Jabberu" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"Na této stránce si můžete vytvořit účet na tomto serveru Jabberu. Vaše JID " -"(Jabber IDentifikátor) bude mít tvar: uživatelskéjméno@server. Přečtěte si " -"prosím pozorně instrukce pro vyplnění údajů." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "Uživatelské jméno:" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" -"Zde nezáleží na velikosti písmen: macbeth je stejný jako MacBeth a Macbeth." - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "Nepřípustné znaky:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "Server:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" -"Nikdy nikomu nesdělujte své heslo, ani administrátorovi serveru Jabberu." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "Později můžete své heslo změnit pomocí klienta Jabberu." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Někteří klienti umí uložit vaše heslo na disk počítače. Tuto funkci " -"používejte, pouze pokud věříte zabezpečení svého počítače." - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"Svoje heslo si zapamatujte, nebo si jej poznamenejte na papírek a ten " -"uschovejte v bezpečí. Jabber nemá žádný automatizovaný způsob obnovy hesla." - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "Ověření hesla:" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "Zaregistrovat se" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "Současné heslo:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "Nové heslo:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "Zde můžete zrušit registraci účtu na tomto serveru Jabberu." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "Zrušit registraci" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Přihlášení" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Čekající" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Skupiny" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Ověřit" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Odstranit" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Seznam kontaktů " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Přidat Jabber ID" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Seznam kontaktů" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Skupiny pro sdílený seznam kontaktů" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Jméno:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Popis:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Členové:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Zobrazené skupiny:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Skupina " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Erlang Jabber Server" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Datum narození" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Město" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "Země" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "E-mail" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Příjmení" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Pro vyhledání uživatele Jabberu vyplňte formulář (na konec přidejte znak * " -"pro vyhledání podřetězce)" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Celé jméno" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Druhé jméno" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Název firmy" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Oddělení" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Hledat uživatele v " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "K vyhledávání potřebujete klienta podporujícího x:data" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "Hledání uživatelů podle vizitek" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "ejabberd vCard modul" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Výsledky hledání pro " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "Vyplňte políčka pro vyhledání uživatele Jabberu" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Odchozí s2s servery:" - -#~ msgid "Delete" -#~ msgstr "Smazat" - -#~ msgid "This room is not anonymous" -#~ msgstr "Tato místnost není anonymní" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "Tento účastník byl vyhozen, protože odeslal chybovou zprávu" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Tento účastník byl vyhozen, protože odeslal chybovou zprávu jinému " -#~ "účastníkovi" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "Tento účastník byl vyhozen, protože odeslal chybový status" - -#, fuzzy -#~ msgid "Captcha test failed" -#~ msgstr "Zkouška CAPTCHA neprošla." diff --git a/priv/msgs/de.msg b/priv/msgs/de.msg index dd22e684e..7247d5f55 100644 --- a/priv/msgs/de.msg +++ b/priv/msgs/de.msg @@ -1,440 +1,625 @@ -%% -*- coding: latin-1 -*- +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" (Add * to the end of field to match substring)"," (Fügen Sie * am Ende des Feldes hinzu um nach Teilzeichenketten zu suchen)"}. +{" has set the subject to: "," hat das Thema geändert auf: "}. +{"# participants","# Teilnehmer"}. +{"A description of the node","Eine Beschreibung des Knotens"}. +{"A friendly name for the node","Ein benutzerfreundlicher Name für den Knoten"}. +{"A password is required to enter this room","Ein Passwort ist erforderlich um diesen Raum zu betreten"}. +{"A Web Page","Eine Webseite"}. {"Accept","Akzeptieren"}. -{"Access Configuration","Zugangskonfiguration"}. -{"Access Control List Configuration","Konfiguration der Zugangskontrolllisten"}. -{"Access control lists","Zugangskontroll-Listen (ACL)"}. -{"Access Control Lists","Zugangskontroll-Listen (ACL)"}. -{"Access denied by service policy","Zugang aufgrund der Dienstrichtlinien verweigert"}. -{"Access rules","Zugangsregeln"}. -{"Access Rules","Zugangsregeln"}. +{"Access denied by service policy","Zugriff aufgrund der Dienstrichtlinien verweigert"}. +{"Access model","Zugriffsmodell"}. +{"Account doesn't exist","Konto existiert nicht"}. {"Action on user","Aktion auf Benutzer"}. -{"Add Jabber ID","Jabber-ID hinzufügen"}. -{"Add New","Neue(n) hinzufügen"}. +{"Add a hat to a user","Funktion zu einem Benutzer hinzufügen"}. {"Add User","Benutzer hinzufügen"}. {"Administration of ","Administration von "}. {"Administration","Verwaltung"}. -{"Administrator privileges required","Administratorenrechte benötigt"}. -{"A friendly name for the node","Ein merkbarer Name für den Knoten"}. +{"Administrator privileges required","Administratorrechte erforderlich"}. {"All activity","Alle Aktivitäten"}. +{"All Users","Alle Benutzer"}. +{"Allow subscription","Abonnement erlauben"}. {"Allow this Jabber ID to subscribe to this pubsub node?","Dieser Jabber-ID das Abonnement dieses pubsub-Knotens erlauben?"}. +{"Allow this person to register with the room?","Dieser Person erlauben, sich beim Raum anzumelden?"}. {"Allow users to change the subject","Erlaube Benutzern das Thema zu ändern"}. {"Allow users to query other users","Erlaube Benutzern Informationen über andere Benutzer abzufragen"}. {"Allow users to send invites","Erlaube Benutzern Einladungen zu senden"}. {"Allow users to send private messages","Erlaube Benutzern private Nachrichten zu senden"}. {"Allow visitors to change nickname","Erlaube Besuchern ihren Benutzernamen zu ändern"}. {"Allow visitors to send private messages to","Erlaube Besuchern das Senden von privaten Nachrichten an"}. -{"Allow visitors to send status text in presence updates","Erlaube Besuchern einen Text bei Statusänderung zu senden"}. -{"Allow visitors to send voice requests","Anfragen von Sprachrechten für Benutzer erlauben"}. -{"All Users","Alle Benutzer"}. +{"Allow visitors to send status text in presence updates","Erlaube Besuchern einen Statustext bei Präsenzupdates zu senden"}. +{"Allow visitors to send voice requests","Erlaube Besuchern Sprachrecht-Anforderungen zu senden"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","Eine zugehörige LDAP-Gruppe die Raummitgliedschaft definiert; dies sollte ein 'LDAP Distinguished Name' gemäß einer implementierungs- oder bereitstellungsspezifischen Definition einer Gruppe sein."}. {"Announcements","Ankündigungen"}. -{"anyone","jeden"}. -{"A password is required to enter this room","Sie brauchen ein Passwort um diesen Raum zu betreten"}. +{"Answer associated with a picture","Antwort verbunden mit einem Bild"}. +{"Answer associated with a video","Antwort verbunden mit einem Video"}. +{"Answer associated with speech","Antwort verbunden mit Sprache"}. +{"Answer to a question","Antwort auf eine Frage"}. +{"Anyone in the specified roster group(s) may subscribe and retrieve items","Jeder in der/den angeführten Kontaktlistengruppe(n) darf Items abonnieren und abrufen"}. +{"Anyone may associate leaf nodes with the collection","Jeder darf Blattknoten mit der Sammlung verknüpfen"}. +{"Anyone may publish","Jeder darf veröffentlichen"}. +{"Anyone may subscribe and retrieve items","Jeder darf Items abonnieren und abrufen"}. +{"Anyone with a presence subscription of both or from may subscribe and retrieve items","Jeder mit einem Präsenzabonnement von beiden oder davon darf Items abonnieren oder abrufen"}. +{"Anyone with Voice","Jeder mit Stimme"}. +{"Anyone","Jeder"}. +{"API Commands","API Befehle"}. {"April","April"}. +{"Arguments","Argumente"}. +{"Attribute 'channel' is required for this request","Attribut 'channel' ist für diese Anforderung erforderlich"}. +{"Attribute 'id' is mandatory for MIX messages","Attribut 'id' ist verpflichtend für MIX-Nachrichten"}. +{"Attribute 'jid' is not allowed here","Attribut 'jid' ist hier nicht erlaubt"}. +{"Attribute 'node' is not allowed here","Attribut 'node' ist hier nicht erlaubt"}. +{"Attribute 'to' of stanza that triggered challenge","Attribut 'to' des Stanza das die Herausforderung ausgelöst hat"}. {"August","August"}. -{"Backup","Datensicherung"}. -{"Backup Management","Datensicherungsverwaltung"}. -{"Backup of ~p","Sicherung von ~p"}. -{"Backup to File at ","Datensicherung in die Datei "}. +{"Automatic node creation is not enabled","Automatische Knotenerstellung ist nicht aktiviert"}. +{"Backup Management","Backupverwaltung"}. +{"Backup of ~p","Backup von ~p"}. +{"Backup to File at ","Backup in Datei bei "}. +{"Backup","Backup"}. {"Bad format","Ungültiges Format"}. {"Birthday","Geburtsdatum"}. -{"CAPTCHA web page","CAPTCHA Webseite"}. +{"Both the username and the resource are required","Sowohl der Benutzername als auch die Ressource sind erforderlich"}. +{"Bytestream already activated","Bytestream bereits aktiviert"}. +{"Cannot remove active list","Kann aktive Liste nicht entfernen"}. +{"Cannot remove default list","Kann Standardliste nicht entfernen"}. +{"CAPTCHA web page","CAPTCHA -Webseite"}. +{"Challenge ID","Herausforderungs-ID"}. {"Change Password","Passwort ändern"}. -{"Change User Password","Benutzer-Passwort ändern"}. +{"Change User Password","Benutzerpasswort ändern"}. +{"Changing password is not allowed","Ändern des Passwortes ist nicht erlaubt"}. +{"Changing role/affiliation is not allowed","Ändern der Rolle/Zugehörigkeit ist nicht erlaubt"}. +{"Channel already exists","Kanal existiert bereits"}. +{"Channel does not exist","Kanal existiert nicht"}. +{"Channel JID","Kanal-JID"}. +{"Channels","Kanäle"}. {"Characters not allowed:","Nicht erlaubte Zeichen:"}. {"Chatroom configuration modified","Chatraum-Konfiguration geändert"}. -{"Chatroom is created","Chatraum wurde erstellt"}. -{"Chatroom is destroyed","Chatraum wurde entfernt"}. -{"Chatroom is started","Chatraum wurde gestartet"}. -{"Chatroom is stopped","Chatraum wurde beendet"}. +{"Chatroom is created","Chatraum ist erstellt"}. +{"Chatroom is destroyed","Chatraum ist entfernt"}. +{"Chatroom is started","Chatraum ist gestartet"}. +{"Chatroom is stopped","Chatraum ist beendet"}. {"Chatrooms","Chaträume"}. -{"Choose a username and password to register with this server","Wählen sie zum Registrieren einen Benutzernamen und ein Passwort"}. -{"Choose modules to stop","Wähle zu stoppende Module"}. +{"Choose a username and password to register with this server","Wählen Sie zum Registrieren auf diesem Server einen Benutzernamen und ein Passwort"}. {"Choose storage type of tables","Wähle Speichertyp der Tabellen"}. -{"Choose whether to approve this entity's subscription.","Wählen sie, ob dieses Abonnement akzeptiert werden soll."}. +{"Choose whether to approve this entity's subscription.","Wählen Sie, ob das Abonnement dieser Entität genehmigt werden soll."}. {"City","Stadt"}. +{"Client acknowledged more stanzas than sent by server","Client bestätigte mehr Stanzas als vom Server gesendet"}. {"Commands","Befehle"}. {"Conference room does not exist","Konferenzraum existiert nicht"}. +{"Configuration of room ~s","Konfiguration des Raumes ~s"}. {"Configuration","Konfiguration"}. -{"Configuration of room ~s","Konfiguration für Raum ~s"}. -{"Connected Resources:","Verbundene Ressourcen:"}. -{"Connections parameters","Verbindungsparameter"}. +{"Contact Addresses (normally, room owner or owners)","Kontaktadresse (normalerweise Raumbesitzer)"}. {"Country","Land"}. -{"CPU Time:","CPU-Zeit:"}. +{"Current Discussion Topic","Aktuelles Diskussionsthema"}. +{"Database failure","Datenbankfehler"}. +{"Database Tables Configuration at ","Datenbanktabellen-Konfiguration bei "}. {"Database","Datenbank"}. -{"Database Tables at ~p","Datenbanktabellen auf ~p"}. -{"Database Tables Configuration at ","Datenbanktabellen-Konfiguration auf "}. {"December","Dezember"}. -{"Default users as participants","Benutzer werden standardmäßig vollwertige Teilnehmer"}. -{"Delete message of the day","Lösche Nachricht des Tages"}. +{"Default users as participants","Benutzer werden standardmäßig Teilnehmer"}. {"Delete message of the day on all hosts","Lösche Nachricht des Tages auf allen Hosts"}. -{"Delete Selected","Markierte löschen"}. +{"Delete message of the day","Lösche Nachricht des Tages"}. {"Delete User","Benutzer löschen"}. -{"Deliver event notifications","Ereignisbenachrichtigung zustellen"}. -{"Deliver payloads with event notifications","Nachrichten mit Ereignis-Benachrichtigungen zustellen"}. -{"Description:","Beschreibung:"}. +{"Deliver event notifications","Ereignisbenachrichtigungen zustellen"}. +{"Deliver payloads with event notifications","Nutzdaten mit Ereignisbenachrichtigungen zustellen"}. {"Disc only copy","Nur auf Festplatte"}. -{"Displayed Groups:","Angezeigte Gruppen:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","Geben sie niemandem ihr Passwort, auch nicht den Administratoren des Jabber Servers."}. -{"Dump Backup to Text File at ","Ausgabe der Sicherung in diese Textdatei "}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","Geben Sie niemandem Ihr Passwort, auch nicht den Administratoren des XMPP-Servers."}. +{"Dump Backup to Text File at ","Gib Backup in Textdatei aus bei "}. {"Dump to Text File","Ausgabe in Textdatei"}. -{"Edit Properties","Einstellungen ändern"}. -{"Either approve or decline the voice request.","Diese Anfrage für Sprachrechte bestätigen oder ablehnen."}. -{"ejabberd IRC module","ejabberd IRC-Modul"}. +{"Duplicated groups are not allowed by RFC6121","Doppelte Gruppen sind laut RFC6121 nicht erlaubt"}. +{"Dynamically specify a replyto of the item publisher","Dynamisch ein 'replyto' des Item-Veröffentlichers angeben"}. +{"Edit Properties","Eigenschaften ändern"}. +{"Either approve or decline the voice request.","Sprachrecht-Anforderung entweder genehmigen oder ablehnen."}. +{"ejabberd HTTP Upload service","ejabberd HTTP Upload-Dienst"}. {"ejabberd MUC module","ejabberd MUC-Modul"}. -{"ejabberd Multicast service","ejabberd Multicast Dienst"}. +{"ejabberd Multicast service","ejabberd Multicast-Dienst"}. {"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe-Modul"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5-Bytestreams-Modul"}. {"ejabberd vCard module","ejabberd vCard-Modul"}. {"ejabberd Web Admin","ejabberd Web-Admin"}. -{"Elements","Elemente"}. +{"ejabberd","ejabberd"}. +{"Email Address","E-Mail-Adresse"}. {"Email","E-Mail"}. -{"Empty Rooms","Leere Räume"}. +{"Enable hats","Funktion einschalten"}. {"Enable logging","Protokollierung aktivieren"}. {"Enable message archiving","Nachrichtenarchivierung aktivieren"}. -{"Encoding for server ~b","Kodierung für Server ~b"}. -{"End User Session","Benutzer-Sitzung beenden"}. -{"Enter list of {Module, [Options]}","Geben sie eine Liste bestehend aus {Modul, [Optionen]} ein"}. -{"Enter nickname you want to register","Geben sie den zu registrierenden Benutzernamen ein"}. -{"Enter path to backup file","Geben sie den Pfad zur Datensicherung ein"}. -{"Enter path to jabberd14 spool dir","Geben sie den Pfad zum jabberd14-Spool-Verzeichnis ein"}. -{"Enter path to jabberd14 spool file","Geben sie den Pfad zur jabberd14-Spool-Datei ein"}. -{"Enter path to text file","Geben sie den Pfad zur Textdatei ein"}. -{"Enter the text you see","Geben sie den Text den sie sehen ein"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Geben sie Benutzernamen und Kodierung für Verbindungen zu IRC Servern an. Drücken sie 'Mehr' um leere Felder hinzuzufügen. Drücken sie 'Beenden' um die Einstellungen zu speichern."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Geben sie den Benutzernamen, Zeichenkodierung, Ports und Passwörter, die sie für die Verbindung zum IRC-Server verwenden wollen, an"}. -{"Erlang Jabber Server","Erlang Jabber Server"}. -{"Error","Fehler"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Beispiel: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. -{"Exclude Jabber IDs from CAPTCHA challenge","Von CAPTCHA Überprüfung ausgeschlossene Jabber IDs"}. -{"Export all tables as SQL queries to a file:","Alle Tabellen als SQL Abfragen in eine Datei exportieren:"}. -{"Export data of all users in the server to PIEFXIS files (XEP-0227):","Alle Benutzerdaten des Servers in PIEFXIS Dateien (XEP-0227) exportieren:"}. -{"Export data of users in a host to PIEFXIS files (XEP-0227):","Alle Benutzerdaten des Hosts in PIEFXIS Dateien (XEP-0227) exportieren:"}. -{"Failed to extract JID from your voice request approval","Fehler beim Auslesen der JID aus der Anfragenbestätigung für Sprachrechte"}. +{"Enabling push without 'node' attribute is not supported","push ohne 'node'-Attribut zu aktivieren wird nicht unterstützt"}. +{"End User Session","Benutzersitzung beenden"}. +{"Enter nickname you want to register","Geben Sie den Spitznamen ein den Sie registrieren wollen"}. +{"Enter path to backup file","Geben Sie den Pfad zur Backupdatei ein"}. +{"Enter path to jabberd14 spool dir","Geben Sie den Pfad zum jabberd14-Spoolverzeichnis ein"}. +{"Enter path to jabberd14 spool file","Geben Sie den Pfad zur jabberd14-Spooldatei ein"}. +{"Enter path to text file","Geben Sie den Pfad zur Textdatei ein"}. +{"Enter the text you see","Geben Sie den Text ein den Sie sehen"}. +{"Erlang XMPP Server","Erlang XMPP-Server"}. +{"Exclude Jabber IDs from CAPTCHA challenge","Jabber-IDs von CAPTCHA-Herausforderung ausschließen"}. +{"Export all tables as SQL queries to a file:","Alle Tabellen als SQL-Abfragen in eine Datei exportieren:"}. +{"Export data of all users in the server to PIEFXIS files (XEP-0227):","Alle Benutzerdaten des Servers in PIEFXIS-Dateien (XEP-0227) exportieren:"}. +{"Export data of users in a host to PIEFXIS files (XEP-0227):","Alle Benutzerdaten des Hosts in PIEFXIS-Dateien (XEP-0227) exportieren:"}. +{"External component failure","Fehler externer Komponente"}. +{"External component timeout","Zeitüberschreitung externer Komponente"}. +{"Failed to activate bytestream","Konnte Bytestream nicht aktivieren"}. +{"Failed to extract JID from your voice request approval","Konnte JID nicht aus Ihrer Genehmigung der Sprachrecht-Anforderung extrahieren"}. +{"Failed to map delegated namespace to external component","Konnte delegierten Namensraum nicht externer Komponente zuordnen"}. +{"Failed to parse HTTP response","Konnte HTTP-Antwort nicht parsen"}. +{"Failed to process option '~s'","Konnte Option '~s' nicht verarbeiten"}. {"Family Name","Nachname"}. +{"FAQ Entry","FAQ-Eintrag"}. {"February","Februar"}. -{"Fill in fields to search for any matching Jabber User","Füllen sie die Felder aus, um nach bestimmten Jabber-Benutzern zu suchen"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Füllen sie die Felder aus, um nach passenden Jabber-Benutzern zu suchen (beenden Sie ein Feld mit *, um auch nach Teilzeichenketten zu suchen)"}. +{"File larger than ~w bytes","Datei größer als ~w Bytes"}. +{"Fill in the form to search for any matching XMPP User","Füllen Sie das Formular aus, um nach jeglichen passenden XMPP-Benutzern zu suchen"}. {"Friday","Freitag"}. -{"From ~s","Von ~s"}. -{"From","Von"}. +{"From ~ts","Von ~ts"}. +{"Full List of Room Admins","Vollständige Liste der Raumadmins"}. +{"Full List of Room Owners","Vollständige Liste der Raumbesitzer"}. {"Full Name","Vollständiger Name"}. +{"Get List of Online Users","Liste der angemeldeten Benutzer abrufen"}. +{"Get List of Registered Users","Liste der registrierten Benutzer abrufen"}. {"Get Number of Online Users","Anzahl der angemeldeten Benutzer abrufen"}. {"Get Number of Registered Users","Anzahl der registrierten Benutzer abrufen"}. -{"Get User Last Login Time","letzte Anmeldezeit abrufen"}. -{"Get User Password","Benutzer-Passwort abrufen"}. -{"Get User Statistics","Benutzer-Statistiken abrufen"}. -{"Grant voice to this person?","Sprachrechte dieser Person erteilen?"}. -{"Group ","Gruppe "}. -{"Groups","Gruppen"}. +{"Get Pending","Ausstehende abrufen"}. +{"Get User Last Login Time","letzte Anmeldezeit des Benutzers abrufen"}. +{"Get User Statistics","Benutzerstatistiken abrufen"}. +{"Given Name","Vorname"}. +{"Grant voice to this person?","Dieser Person Sprachrechte erteilen?"}. {"has been banned","wurde gebannt"}. -{"has been kicked because of an affiliation change","wurde wegen Änderung des Mitgliederstatus entfernt"}. -{"has been kicked because of a system shutdown","wurde wegen einer Systemabschaltung entfernt"}. -{"has been kicked because the room has been changed to members-only","wurde entfernt weil der Raum auf Nur-Mitglieder umgestellt wurde"}. -{"has been kicked","wurde entfernt"}. -{" has set the subject to: "," hat das Thema geändert auf: "}. -{"Host","Host"}. -{"If you don't see the CAPTCHA image here, visit the web page.","Wenn sie das CAPTCHA Bild nicht sehen, besuchen sie bitte die Webseite."}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Wenn sie verschiedene Ports, Passwörter und Kodierungen für IRC Server angeben wollen, erstellen sie die Liste mit folgendem Format '{\"IRC Server\", \"Kodierung\", Port, \"Passwort\"}'. Standardmäßig benutzt dieser Dienst die \"~s\" Kodierung, den Port ~p und kein Passwort."}. +{"has been kicked because of a system shutdown","wurde wegen einer Systemabschaltung hinausgeworfen"}. +{"has been kicked because of an affiliation change","wurde wegen einer Änderung der Zugehörigkeit hinausgeworfen"}. +{"has been kicked because the room has been changed to members-only","wurde hinausgeworfen weil der Raum zu Nur-Mitglieder geändert wurde"}. +{"has been kicked","wurde hinausgeworfen"}. +{"Hash of the vCard-temp avatar of this room","Hash des vCard-temp Avatars dieses Raums"}. +{"Hat title","Funktionstitel"}. +{"Hat URI","Funktions-URI"}. +{"Hats limit exceeded","Funktionslimit wurde überschritten"}. +{"Host unknown","Host unbekannt"}. +{"HTTP File Upload","HTTP-Dateiupload"}. +{"Idle connection","Inaktive Verbindung"}. +{"If you don't see the CAPTCHA image here, visit the web page.","Wenn Sie das CAPTCHA-Bild nicht sehen, besuchen Sie die Webseite."}. {"Import Directory","Verzeichnis importieren"}. {"Import File","Datei importieren"}. -{"Import user data from jabberd14 spool file:","Importiere Benutzer von jabberd14 Spool Datei:"}. -{"Import User from File at ","Benutzer aus dieser Datei importieren "}. -{"Import users data from a PIEFXIS file (XEP-0227):","Benutzerdaten von einer PIEFXIS Datei (XEP-0227) importieren:"}. -{"Import users data from jabberd14 spool directory:","Importiere Benutzer von jabberd14 Spool Verzeichnis:"}. -{"Import Users from Dir at ","Benutzer importieren aus dem Verzeichnis "}. -{"Import Users From jabberd14 Spool Files","Importiere Benutzer aus jabberd14-Spool-Dateien"}. +{"Import user data from jabberd14 spool file:","Importiere Benutzer von jabberd14-Spooldatei:"}. +{"Import User from File at ","Benutzer importieren aus Datei bei "}. +{"Import users data from a PIEFXIS file (XEP-0227):","Benutzerdaten von einer PIEFXIS-Datei (XEP-0227) importieren:"}. +{"Import users data from jabberd14 spool directory:","Importiere Benutzer von jabberd14-Spoolverzeichnis:"}. +{"Import Users from Dir at ","Benutzer importieren aus Verzeichnis bei "}. +{"Import Users From jabberd14 Spool Files","Importiere Benutzer aus jabberd14-Spooldateien"}. +{"Improper domain part of 'from' attribute","Falscher Domänenteil des 'from'-Attributs"}. {"Improper message type","Unzulässiger Nachrichtentyp"}. -{"Incoming s2s Connections:","Eingehende s2s-Verbindungen:"}. +{"Incorrect CAPTCHA submit","Falsche CAPTCHA-Eingabe"}. +{"Incorrect data form","Falsches Datenformular"}. {"Incorrect password","Falsches Passwort"}. -{"Invalid affiliation: ~s","Ungültige Mitgliedschaft: ~s"}. -{"Invalid role: ~s","Ungültige Rolle: ~s"}. -{"IP addresses","IP Adressen"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","IRC Channel (ohne dem ersten #)"}. -{"IRC server","IRC Server"}. -{"IRC settings","IRC Einstellungen"}. -{"IRC Transport","IRC Transport"}. -{"IRC username","IRC Benutzername"}. -{"IRC Username","IRC-Benutzername"}. +{"Incorrect value of 'action' attribute","Falscher Wert des 'action'-Attributs"}. +{"Incorrect value of 'action' in data form","Falscher Wert von 'action' in Datenformular"}. +{"Incorrect value of 'path' in data form","Falscher Wert von 'path' in Datenformular"}. +{"Installed Modules:","Installierte Module:"}. +{"Install","Installieren"}. +{"Insufficient privilege","Unzureichende Privilegien"}. +{"Internal server error","Interner Serverfehler"}. +{"Invalid 'from' attribute in forwarded message","Ungültiges 'from'-Attribut in weitergeleiteter Nachricht"}. +{"Invalid node name","Ungültiger Knotenname"}. +{"Invalid 'previd' value","Ungültiger 'previd'-Wert"}. +{"Invitations are not allowed in this conference","Einladungen sind in dieser Konferenz nicht erlaubt"}. +{"IP addresses","IP-Adressen"}. {"is now known as","ist nun bekannt als"}. -{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Es ist nicht erlaubt Fehlermeldungen an den Raum zu senden. Der Teilnehmer (~s) hat eine Fehlermeldung (~s) gesendet und wurde aus dem Raum entfernt"}. -{"It is not allowed to send private messages","Es ist nicht erlaubt private Nachrichten zu senden"}. -{"It is not allowed to send private messages of type \"groupchat\"","Es ist nicht erlaubt private Nachrichten des Typs \"Gruppenchat\" zu senden"}. -{"It is not allowed to send private messages to the conference","Es ist nicht erlaubt private Nachrichten an den Raum zu schicken"}. -{"Jabber Account Registration","Jabber Konto Anmeldung"}. -{"Jabber ID","Jabber ID"}. -{"Jabber ID ~s is invalid","Die Jabber-ID ~s ist ungültig"}. +{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Es ist nicht erlaubt Fehlermeldungen an den Raum zu senden. Der Teilnehmer (~s) hat eine Fehlermeldung (~s) gesendet und wurde aus dem Raum geworfen"}. +{"It is not allowed to send private messages of type \"groupchat\"","Es ist nicht erlaubt private Nachrichten des Typs \"groupchat\" zu senden"}. +{"It is not allowed to send private messages to the conference","Es ist nicht erlaubt private Nachrichten an die Konferenz zu senden"}. +{"Jabber ID","Jabber-ID"}. {"January","Januar"}. -{"Join IRC channel","IRC Channel beitreten"}. -{"joins the room","betretet den Raum"}. -{"Join the IRC channel here.","Hier den IRC Channel beitreten."}. -{"Join the IRC channel in this Jabber ID: ~s","Den IRC Channel mit dieser Jabber ID beitreten: ~s"}. +{"JID normalization denied by service policy","JID-Normalisierung aufgrund der Dienstrichtlinien verweigert"}. +{"JID normalization failed","JID-Normalisierung fehlgeschlagen"}. +{"Joined MIX channels of ~ts","Beigetretene MIX-Channels von ~ts"}. +{"Joined MIX channels:","Beigetretene MIX-Channels:"}. +{"joins the room","betritt den Raum"}. {"July","Juli"}. {"June","Juni"}. +{"Just created","Gerade erstellt"}. {"Last Activity","Letzte Aktivität"}. {"Last login","Letzte Anmeldung"}. +{"Last message","Letzte Nachricht"}. {"Last month","Letzter Monat"}. {"Last year","Letztes Jahr"}. +{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Niederwertigstes Bit des SHA-256-Hashes des Textes sollte hexadezimalem Label gleichen"}. {"leaves the room","verlässt den Raum"}. -{"Listened Ports","Aktive Ports"}. -{"Listened Ports at ","Aktive Ports bei"}. -{"List of modules to start","Liste der zu startenden Module"}. -{"List of rooms","Liste von Chaträumen"}. -{"Low level update script","Low level Aktualisierungsscript"}. +{"List of users with hats","Liste der Benutzer mit Funktionen"}. +{"List users with hats","Benutzer mit Funktionen auflisten"}. +{"Logged Out","Abgemeldet"}. +{"Logging","Protokollierung"}. {"Make participants list public","Teilnehmerliste öffentlich machen"}. -{"Make room CAPTCHA protected","Raum mit Verifizierung (Captcha) versehen"}. +{"Make room CAPTCHA protected","Raum mittels CAPTCHA schützen"}. {"Make room members-only","Raum nur für Mitglieder zugänglich machen"}. {"Make room moderated","Raum moderiert machen"}. {"Make room password protected","Raum mit Passwort schützen"}. {"Make room persistent","Raum persistent machen"}. {"Make room public searchable","Raum öffentlich suchbar machen"}. +{"Malformed username","Ungültiger Benutzername"}. +{"MAM preference modification denied by service policy","Modifikation der MAM-Präferenzen aufgrund der Dienstrichtlinien verweigert"}. {"March","März"}. -{"Maximum Number of Occupants","Maximale Anzahl von Teilnehmern"}. -{"Max # of items to persist","Maximale Anzahl dauerhaft zu speichernder Einträge"}. -{"Max payload size in bytes","Maximale Nutzlastgrösse in Bytes"}. +{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","Maximale Anzahl der aufzubewahrenden Elemente oder `max`, wenn es keine spezifische Begrenzung gibt, außer einer vom Server festgelegten Höchstzahl"}. +{"Max payload size in bytes","Maximale Nutzdatengröße in Bytes"}. +{"Maximum file size","Maximale Dateigröße"}. +{"Maximum Number of History Messages Returned by Room","Maximale Anzahl der vom Raum zurückgegebenen History-Nachrichten"}. +{"Maximum number of items to persist","Maximale Anzahl persistenter Items"}. +{"Maximum Number of Occupants","Maximale Anzahl der Teilnehmer"}. {"May","Mai"}. -{"Membership is required to enter this room","Um diesen Raum zu betreten müssen sie Mitglied sein"}. -{"Members:","Mitglieder:"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Merken sie sich ihr Passwort, oder schreiben sie es auf einen Zettel den sie sicher verwahren. Bei Jabber gibt es keine automatische Möglichkeit, das Passwort wiederherzustellen."}. -{"Memory","Speicher"}. +{"Membership is required to enter this room","Mitgliedschaft ist erforderlich um diesen Raum zu betreten"}. +{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Merken Sie sich Ihr Passwort, oder schreiben Sie es auf einen Zettel den Sie sicher verwahren. Bei XMPP gibt es keine automatische Möglichkeit, das Passwort wiederherzustellen falls Sie es vergessen."}. +{"Mere Availability in XMPP (No Show Value)","Bloße Verfügbarkeit in XMPP (kein Anzeigewert)"}. {"Message body","Nachrichtentext"}. +{"Message not found in forwarded payload","Nachricht nicht in weitergeleiteten Nutzdaten gefunden"}. +{"Messages from strangers are rejected","Nachrichten von Fremden werden zurückgewiesen"}. +{"Messages of type headline","Nachrichten vom Typ 'headline'"}. +{"Messages of type normal","Nachrichten vom Typ 'normal'"}. {"Middle Name","Zweiter Vorname"}. -{"Minimum interval between voice requests (in seconds)","Mindestdauer zwischen Anfragen für Sprachrechte (in Sekunden)"}. +{"Minimum interval between voice requests (in seconds)","Mindestdauer zwischen Sprachrecht-Anforderung (in Sekunden)"}. +{"Moderator privileges required","Moderatorrechte erforderlich"}. {"Moderator","Moderator"}. -{"Moderator privileges required","Moderatorrechte benötigt"}. -{"moderators only","ausschliesslich Moderatoren"}. -{"Modified modules","Geänderte Module"}. -{"Module","Modul"}. -{"Modules at ~p","Module bei ~p"}. -{"Modules","Module"}. +{"Moderators Only","nur Moderatoren"}. +{"Module failed to handle the query","Modul konnte die Anfrage nicht verarbeiten"}. {"Monday","Montag"}. {"Multicast","Multicast"}. +{"Multiple elements are not allowed by RFC6121","Mehrere -Elemente sind laut RFC6121 nicht erlaubt"}. {"Multi-User Chat","Mehrbenutzer-Chat (MUC)"}. -{"Name:","Name:"}. {"Name","Vorname"}. +{"Natural Language for Room Discussions","Natürliche Sprache für Raumdiskussionen"}. +{"Natural-Language Room Name","Raumname in natürlicher Sprache"}. +{"Neither 'jid' nor 'nick' attribute found","Weder 'jid'- noch 'nick'-Attribut gefunden"}. +{"Neither 'role' nor 'affiliation' attribute found","Weder 'role'- noch 'affiliation'-Attribut gefunden"}. {"Never","Nie"}. {"New Password:","Neues Passwort:"}. -{"Nickname","Benutzername"}. -{"Nickname Registration at ","Registrieren des Benutzernames auf "}. -{"Nickname ~s does not exist in the room","Der Benutzername ~s existiert im Raum nicht"}. -{"nobody","niemanden"}. +{"Nickname can't be empty","Spitzname darf nicht leer sein"}. +{"Nickname Registration at ","Registrieren des Spitznamens auf "}. +{"Nickname ~s does not exist in the room","Der Spitzname ~s existiert nicht im Raum"}. +{"Nickname","Spitzname"}. +{"No address elements found","Keine 'address'-Elemente gefunden"}. +{"No addresses element found","Kein 'addresses'-Element gefunden"}. +{"No 'affiliation' attribute found","Kein 'affiliation'-Attribut gefunden"}. +{"No available resource found","Keine verfügbare Ressource gefunden"}. {"No body provided for announce message","Kein Text für die Ankündigungsnachricht angegeben"}. +{"No child elements found","Keine 'child'-Elemente gefunden"}. +{"No data form found","Kein Datenformular gefunden"}. {"No Data","Keine Daten"}. +{"No features available","Keine Eigenschaften verfügbar"}. +{"No element found","Kein -Element gefunden"}. +{"No hook has processed this command","Kein Hook hat diesen Befehl verarbeitet"}. +{"No info about last activity found","Keine Informationen über letzte Aktivität gefunden"}. +{"No 'item' element found","Kein 'item'-Element gefunden"}. +{"No items found in this query","Keine Items in dieser Anfrage gefunden"}. +{"No limit","Keine Begrenzung"}. +{"No module is handling this query","Kein Modul verarbeitet diese Anfrage"}. +{"No node specified","Kein Knoten angegeben"}. +{"No 'password' found in data form","Kein 'password' im Datenformular gefunden"}. +{"No 'password' found in this query","Kein 'password' in dieser Anfrage gefunden"}. +{"No 'path' found in data form","Kein 'path' im Datenformular gefunden"}. +{"No pending subscriptions found","Keine ausstehenden Abonnements gefunden"}. +{"No privacy list with this name found","Keine Privacy-Liste mit diesem Namen gefunden"}. +{"No private data found in this query","Keine privaten Daten in dieser Anfrage gefunden"}. +{"No running node found","Kein laufender Knoten gefunden"}. +{"No services available","Keine Dienste verfügbar"}. +{"No statistics found for this item","Keine Statistiken für dieses Item gefunden"}. +{"No 'to' attribute found in the invitation","Kein 'to'-Attribut in der Einladung gefunden"}. +{"Nobody","Niemand"}. +{"Node already exists","Knoten existiert bereits"}. {"Node ID","Knoten-ID"}. +{"Node index not found","Knotenindex nicht gefunden"}. {"Node not found","Knoten nicht gefunden"}. {"Node ~p","Knoten ~p"}. +{"Node","Knoten"}. +{"Nodeprep has failed","Nodeprep fehlgeschlagen"}. {"Nodes","Knoten"}. -{"No limit","Keine Begrenzung"}. {"None","Keine"}. -{"No resource provided","Keine Ressource angegeben"}. +{"Not allowed","Nicht erlaubt"}. {"Not Found","Nicht gefunden"}. -{"Notify subscribers when items are removed from the node","Abonnenten benachrichtigen, wenn Einträge vom Knoten entfernt werden"}. +{"Not subscribed","Nicht abonniert"}. +{"Notify subscribers when items are removed from the node","Abonnenten benachrichtigen, wenn Items vom Knoten entfernt werden"}. {"Notify subscribers when the node configuration changes","Abonnenten benachrichtigen, wenn sich die Knotenkonfiguration ändert"}. {"Notify subscribers when the node is deleted","Abonnenten benachrichtigen, wenn der Knoten gelöscht wird"}. {"November","November"}. +{"Number of answers required","Anzahl der erforderlichen Antworten"}. {"Number of occupants","Anzahl der Teilnehmer"}. +{"Number of Offline Messages","Anzahl der Offline-Nachrichten"}. {"Number of online users","Anzahl der angemeldeten Benutzer"}. {"Number of registered users","Anzahl der registrierten Benutzer"}. +{"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","Anzahl der Sekunden, nach denen Elemente automatisch gelöscht werden sollen, oder `max`, wenn es keine spezifische Grenze gibt, außer einer vom Server festgelegten Höchstgrenze"}. +{"Occupants are allowed to invite others","Teilnehmer dürfen andere einladen"}. +{"Occupants are allowed to query others","Teilnehmer dürfen andere abfragen"}. +{"Occupants May Change the Subject","Teilnehmer dürfen das Thema ändern"}. {"October","Oktober"}. -{"Offline Messages:","Offline-Nachrichten:"}. -{"Offline Messages","Offline-Nachrichten"}. {"OK","OK"}. -{"Old Password:","Aktuelles Passwort:"}. -{"Online","Angemeldet"}. -{"Online Users:","Angemeldete Benutzer:"}. +{"Old Password:","Altes Passwort:"}. {"Online Users","Angemeldete Benutzer"}. +{"Online","Angemeldet"}. +{"Only collection node owners may associate leaf nodes with the collection","Nur Sammlungsknoten-Besitzer dürfen Blattknoten mit der Sammlung verknüpfen"}. {"Only deliver notifications to available users","Benachrichtigungen nur an verfügbare Benutzer schicken"}. +{"Only or tags are allowed","Nur - oder -Tags sind erlaubt"}. +{"Only element is allowed in this query","Nur -Elemente sind in dieser Anfrage erlaubt"}. {"Only members may query archives of this room","Nur Mitglieder dürfen den Verlauf dieses Raumes abrufen"}. -{"Only moderators and participants are allowed to change the subject in this room","Nur Moderatoren und Mitglieder dürfen das Thema in diesem Raum ändern"}. +{"Only moderators and participants are allowed to change the subject in this room","Nur Moderatoren und Teilnehmer dürfen das Thema in diesem Raum ändern"}. {"Only moderators are allowed to change the subject in this room","Nur Moderatoren dürfen das Thema in diesem Raum ändern"}. -{"Only moderators can approve voice requests","Nur Moderatoren können Anfragen für Sprachrechte bestätigen"}. -{"Only occupants are allowed to send messages to the conference","Nur Teilnehmer dürfen Nachrichten an den Raum schicken"}. -{"Only occupants are allowed to send queries to the conference","Nur Teilnehmer sind berechtigt Anfragen an die Konferenz zu senden"}. -{"Only service administrators are allowed to send service messages","Nur Service-Administratoren sind berechtigt, Servicenachrichten zu versenden"}. -{"Options","Optionen"}. +{"Only moderators are allowed to retract messages","Nur Moderatoren dürfen Nachrichten zurückziehen"}. +{"Only moderators can approve voice requests","Nur Moderatoren können Sprachrecht-Anforderungen genehmigen"}. +{"Only occupants are allowed to send messages to the conference","Nur Teilnehmer dürfen Nachrichten an die Konferenz senden"}. +{"Only occupants are allowed to send queries to the conference","Nur Teilnehmer dürfen Anfragen an die Konferenz senden"}. +{"Only publishers may publish","Nur Veröffentlicher dürfen veröffentlichen"}. +{"Only service administrators are allowed to send service messages","Nur Service-Administratoren dürfen Servicenachrichten senden"}. +{"Only those on a whitelist may associate leaf nodes with the collection","Nur jemand auf einer Whitelist darf Blattknoten mit der Sammlung verknüpfen"}. +{"Only those on a whitelist may subscribe and retrieve items","Nur jemand auf einer Whitelist darf Items abonnieren und abrufen"}. {"Organization Name","Name der Organisation"}. {"Organization Unit","Abteilung"}. -{"Outgoing s2s Connections:","Ausgehende s2s-Verbindungen:"}. +{"Other Modules Available:","Andere Module verfügbar:"}. {"Outgoing s2s Connections","Ausgehende s2s-Verbindungen"}. -{"Owner privileges required","Besitzerrechte benötigt"}. -{"Packet","Paket"}. +{"Owner privileges required","Besitzerrechte erforderlich"}. +{"Packet relay is denied by service policy","Paket-Relay aufgrund der Dienstrichtlinien verweigert"}. +{"Participant ID","Teilnehmer-ID"}. {"Participant","Teilnehmer"}. -{"Password ~b","Passwort ~b"}. -{"Password:","Passwort:"}. -{"Password","Passwort"}. -{"Password Verification:","Passwort bestätigen:"}. {"Password Verification","Passwort bestätigen"}. +{"Password Verification:","Passwort bestätigen:"}. +{"Password","Passwort"}. +{"Password:","Passwort:"}. {"Path to Dir","Pfad zum Verzeichnis"}. {"Path to File","Pfad zur Datei"}. -{"Pending","Schwebend"}. {"Period: ","Zeitraum: "}. -{"Permanent rooms","Permanente Chaträume"}. -{"Persist items to storage","Einträge dauerhaft speichern"}. +{"Persist items to storage","Items dauerhaft speichern"}. +{"Persistent","Persistent"}. +{"Ping query is incorrect","Ping-Anfrage ist falsch"}. {"Ping","Ping"}. -{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Beachten sie, das diese Optionen nur die eingebaute Mnesia-Datenbank sichern. Wenn sie das ODBC-Modul verwenden, müssen sie die SQL-Datenbank manuell sichern."}. -{"Please specify file name.","Bitte geben Sie den Dateinamen an."}. -{"Please specify file size.","Bitte geben Sie die Dateigröße an."}. -{"Please, wait for a while before sending new voice request","Bitte warten sie ein wenig, bevor sie eine weitere Anfrage für Sprachrechte senden"}. +{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Beachten Sie, dass diese Optionen nur die eingebaute Mnesia-Datenbank sichern. Wenn Sie das ODBC-Modul verwenden, müssen Sie auch Ihre SQL-Datenbank separat sichern."}. +{"Please, wait for a while before sending new voice request","Bitte warten Sie ein wenig, bevor Sie eine weitere Sprachrecht-Anforderung senden"}. {"Pong","Pong"}. -{"Port ~b","Port ~b"}. -{"Port","Port"}. +{"Possessing 'ask' attribute is not allowed by RFC6121","Ein 'ask'-Attribut zu besitzen ist laut RFC6121 nicht erlaubt"}. {"Present real Jabber IDs to","Echte Jabber-IDs anzeigen für"}. +{"Previous session not found","Vorherige Sitzung nicht gefunden"}. +{"Previous session PID has been killed","Vorherige Sitzungs-PID wurde getötet"}. +{"Previous session PID has exited","Vorherige Sitzungs-PID wurde beendet"}. +{"Previous session PID is dead","Vorherige Sitzungs-PID ist tot"}. +{"Previous session timed out","Zeitüberschreitung bei vorheriger Sitzung"}. {"private, ","privat, "}. -{"Protocol","Protokoll"}. +{"Public","Öffentlich"}. +{"Publish model","Veröffentlichungsmodell"}. {"Publish-Subscribe","Publish-Subscribe"}. -{"PubSub subscriber request","PubSub-Abonnenten-Anfrage"}. -{"Purge all items when the relevant publisher goes offline","Alle Einträge entfernen, wenn der relevante Veröffentlicher offline geht"}. -{"Queries to the conference members are not allowed in this room","Anfragen an die Teilnehmer sind in diesem Raum nicht erlaubt"}. +{"PubSub subscriber request","PubSub-Abonnenten-Anforderung"}. +{"Purge all items when the relevant publisher goes offline","Alle Items löschen, wenn der relevante Veröffentlicher offline geht"}. +{"Push record not found","Push-Eintrag nicht gefunden"}. +{"Queries to the conference members are not allowed in this room","Anfragen an die Konferenzteilnehmer sind in diesem Raum nicht erlaubt"}. +{"Query to another users is forbidden","Anfrage an andere Benutzer ist verboten"}. {"RAM and disc copy","RAM und Festplatte"}. {"RAM copy","Nur RAM"}. -{"Raw","Unformatiert"}. -{"Really delete message of the day?","Die Nachricht des Tages wirklich löschen?"}. -{"Recipient is not in the conference room","Der Empfänger ist nicht im Raum"}. -{"Register a Jabber account","Jabber Konto registrieren"}. +{"Really delete message of the day?","Nachricht des Tages wirklich löschen?"}. +{"Receive notification from all descendent nodes","Benachrichtigung von allen abstammenden Nodes erhalten"}. +{"Receive notification from direct child nodes only","Benachrichtigung nur von direkten Kindknoten erhalten"}. +{"Receive notification of new items only","Benachrichtigung nur von neuen Items erhalten"}. +{"Receive notification of new nodes only","Benachrichtigung nur von neuen Knoten erhalten"}. +{"Recipient is not in the conference room","Empfänger ist nicht im Konferenzraum"}. +{"Register an XMPP account","Ein XMPP-Konto registrieren"}. {"Register","Anmelden"}. -{"Registered nicknames","Registrierte Benutzernamen"}. -{"Registered Users:","Registrierte Benutzer:"}. -{"Registered Users","Registrierte Benutzer"}. -{"Registration in mod_irc for ","Registrierung in mod_irc für "}. {"Remote copy","Fernkopie"}. -{"Remove All Offline Messages","Alle Offline Nachrichten löschen"}. -{"Remove","Entfernen"}. +{"Remove a hat from a user","Eine Funktion bei einem Benutzer entfernen"}. {"Remove User","Benutzer löschen"}. {"Replaced by new connection","Durch neue Verbindung ersetzt"}. +{"Request has timed out","Zeitüberschreitung bei Anforderung"}. +{"Request is ignored","Anforderung wird ignoriert"}. +{"Requested role","Angeforderte Rolle"}. {"Resources","Ressourcen"}. -{"Restart","Neustart"}. {"Restart Service","Dienst neustarten"}. -{"Restore Backup from File at ","Datenwiederherstellung aus der Datei "}. -{"Restore binary backup after next ejabberd restart (requires less memory):","Stelle binäre Sicherung beim nächsten ejabberd-Neustart wieder her (benötigt weniger Speicher):"}. -{"Restore binary backup immediately:","Stelle binäre Sicherung sofort wieder her:"}. -{"Restore plain text backup immediately:","Stelle Klartext-Sicherung sofort wieder her:"}. +{"Restore Backup from File at ","Backup wiederherstellen aus Datei bei "}. +{"Restore binary backup after next ejabberd restart (requires less memory):","Stelle binäres Backup beim nächsten ejabberd-Neustart wieder her (benötigt weniger Speicher):"}. +{"Restore binary backup immediately:","Stelle binäres Backup sofort wieder her:"}. +{"Restore plain text backup immediately:","Stelle Klartext-Backup sofort wieder her:"}. {"Restore","Wiederherstellung"}. -{"Roles for which Presence is Broadcasted","Rollen, für die der Status übertragen wird"}. -{"Room Configuration","Raum-Konfiguration"}. +{"Result","Ergebnis"}. +{"Roles and Affiliations that May Retrieve Member List","Rollen und Zugehörigkeiten die Mitgliederliste abrufen dürfen"}. +{"Roles for which Presence is Broadcasted","Rollen für welche die Präsenz übertragen wird"}. +{"Roles that May Send Private Messages","Rollen die Privatnachrichten senden dürfen"}. +{"Room Configuration","Raumkonfiguration"}. {"Room creation is denied by service policy","Anlegen des Raumes aufgrund der Dienstrichtlinien verweigert"}. -{"Room description","Raum Beschreibung"}. -{"Room Occupants","Teilnehmer in diesem Raum"}. +{"Room description","Raumbeschreibung"}. +{"Room Occupants","Raumteilnehmer"}. +{"Room terminates","Raum wird beendet"}. {"Room title","Raumname"}. -{"Roster groups allowed to subscribe","Kontaktlisten-Gruppen die abonnieren dürfen"}. -{"Roster","Kontaktliste"}. -{"Roster of ","Kontaktliste von "}. +{"Roster groups allowed to subscribe","Kontaktlistengruppen die abonnieren dürfen"}. {"Roster size","Kontaktlistengröße"}. -{"RPC Call Error","Fehler bei RPC-Aufruf"}. -{"Running Nodes","Aktive Knoten"}. -{"~s access rule configuration","~s Zugangsregel-Konfiguration"}. +{"Running Nodes","Laufende Knoten"}. +{"~s invites you to the room ~s","~s lädt Sie in den Raum ~s ein"}. {"Saturday","Samstag"}. -{"Script check","Script-Überprüfung"}. +{"Search from the date","Suche ab Datum"}. {"Search Results for ","Suchergebnisse für "}. -{"Search users in ","Benutzer suchen in "}. -{"Send announcement to all online users on all hosts","Sende Ankündigung an alle angemeldeten Benutzer auf allen Hosts"}. -{"Send announcement to all online users","Sende Ankündigung an alle angemeldeten Benutzer"}. -{"Send announcement to all users on all hosts","Sende Ankündigung an alle Benutzer auf allen Hosts"}. -{"Send announcement to all users","Sende Ankündigung an alle Benutzer"}. +{"Search the text","Text durchsuchen"}. +{"Search until the date","Suche bis Datum"}. +{"Search users in ","Suche Benutzer in "}. +{"Send announcement to all online users on all hosts","Ankündigung an alle angemeldeten Benutzer auf allen Hosts senden"}. +{"Send announcement to all online users","Ankündigung an alle angemeldeten Benutzer senden"}. +{"Send announcement to all users on all hosts","Ankündigung an alle Benutzer auf allen Hosts senden"}. +{"Send announcement to all users","Ankündigung an alle Benutzer senden"}. {"September","September"}. -{"Server ~b","Server ~b"}. {"Server:","Server:"}. -{"Server","Server"}. -{"Set message of the day and send to online users","Setze Nachricht des Tages und sende sie an alle angemeldeten Benutzer"}. -{"Set message of the day on all hosts and send to online users","Setze Nachricht des Tages auf allen Hosts und sende sie an alle angemeldeten Benutzer"}. +{"Service list retrieval timed out","Zeitüberschreitung bei Abfrage der Serviceliste"}. +{"Session state copying timed out","Zeitüberschreitung beim Kopieren des Sitzungszustandes"}. +{"Set message of the day and send to online users","Nachricht des Tages setzen und an alle angemeldeten Benutzer senden"}. +{"Set message of the day on all hosts and send to online users","Nachricht des Tages auf allen Hosts setzen und an alle angemeldeten Benutzer senden"}. {"Shared Roster Groups","Gruppen der gemeinsamen Kontaktliste"}. -{"Show Integral Table","Integrale Tabelle anzeigen"}. +{"Show Integral Table","Integral-Tabelle anzeigen"}. {"Show Ordinary Table","Gewöhnliche Tabelle anzeigen"}. {"Shut Down Service","Dienst herunterfahren"}. -{"~s invites you to the room ~s","~s lädt sie in den Raum ~s ein"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Einige Jabber Client Programme speichern ihr Passwort auf ihrem Computer. Verwenden sie diese Möglichkeit nur auf Computern, die sie als sicher einstufen."}. -{"Specify the access model","Geben sie das Zugangsmodell an"}. -{"Specify the event message type","Geben sie den Ereignis-Nachrichtentyp an"}. -{"Specify the publisher model","Geben sie das Publikationsmodell an"}. -{"~s's Offline Messages Queue","~s's Offline-Nachrichten-Warteschlange"}. -{"Start Modules at ","Starte Module auf "}. -{"Start Modules","Module starten"}. -{"Start","Starten"}. -{"Statistics of ~p","Statistiken von ~p"}. -{"Statistics","Statistiken"}. -{"Stop Modules at ","Stoppe Module auf "}. -{"Stop Modules","Module stoppen"}. +{"SOCKS5 Bytestreams","SOCKS5-Bytestreams"}. +{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Einige XMPP-Clients speichern Ihr Passwort auf dem Computer. Aus Sicherheitsgründen sollten Sie das nur auf Ihrem persönlichen Computer tun."}. +{"Sources Specs:","Quellenspezifikationen:"}. +{"Specify the access model","Geben Sie das Zugangsmodell an"}. +{"Specify the event message type","Geben Sie den Ereignisnachrichtentyp an"}. +{"Specify the publisher model","Geben Sie das Veröffentlichermodell an"}. +{"Stanza id is not valid","Stanza-ID ist ungültig"}. +{"Stanza ID","Stanza-ID"}. +{"Statically specify a replyto of the node owner(s)","Ein 'replyto' des/der Nodebesitzer(s) statisch angeben"}. {"Stopped Nodes","Angehaltene Knoten"}. -{"Stop","Stoppen"}. -{"Storage Type","Speichertyp"}. -{"Store binary backup:","Speichere binäre Sicherung:"}. -{"Store plain text backup:","Speichere Klartext-Sicherung:"}. +{"Store binary backup:","Speichere binäres Backup:"}. +{"Store plain text backup:","Speichere Klartext-Backup:"}. +{"Stream management is already enabled","Stream-Verwaltung ist bereits aktiviert"}. +{"Stream management is not enabled","Stream-Verwaltung ist nicht aktiviert"}. {"Subject","Betreff"}. -{"Submit","Senden"}. {"Submitted","Gesendet"}. {"Subscriber Address","Abonnenten-Adresse"}. -{"Subscription","Abonnement"}. +{"Subscribers may publish","Abonnenten dürfen veröffentlichen"}. +{"Subscription requests must be approved and only subscribers may retrieve items","Abonnement-Anforderungen müssen genehmigt werden und nur Abonnenten dürfen Items abrufen"}. +{"Subscriptions are not allowed","Abonnements sind nicht erlaubt"}. {"Sunday","Sonntag"}. -{"That nickname is already in use by another occupant","Dieser Benutzername wird bereits von einem Teilnehmer genutzt"}. -{"That nickname is registered by another person","Dieser Benutzername wurde bereits von jemand anderem registriert"}. -{"The CAPTCHA is valid.","Die Verifizierung ist gültig."}. -{"The CAPTCHA verification has failed","Die CAPTCHA Verifizierung schlug fehl"}. -{"The collections with which a node is affiliated","Sammlungen, mit denen ein Knoten verknüpft ist"}. +{"Text associated with a picture","Text verbunden mit einem Bild"}. +{"Text associated with a sound","Text verbunden mit einem Klang"}. +{"Text associated with a video","Text verbunden mit einem Video"}. +{"Text associated with speech","Text verbunden mit Sprache"}. +{"That nickname is already in use by another occupant","Dieser Spitzname wird bereits von einem anderen Teilnehmer verwendet"}. +{"That nickname is registered by another person","Dieser Spitzname wurde von jemand anderem registriert"}. +{"The account already exists","Das Konto existiert bereits"}. +{"The account was not unregistered","Das Konto wurde nicht entfernt"}. +{"The body text of the last received message","Der Nachrichtenkörper der letzten erhaltenen Nachricht"}. +{"The CAPTCHA is valid.","Das CAPTCHA ist gültig."}. +{"The CAPTCHA verification has failed","Die CAPTCHA-Verifizierung ist fehlgeschlagen"}. +{"The captcha you entered is wrong","Das CAPTCHA das Sie eingegeben haben ist falsch"}. +{"The child nodes (leaf or collection) associated with a collection","Die mit einer Sammlung verknüpften Kindknoten (Blatt oder Sammlung)"}. +{"The collections with which a node is affiliated","Sammlungen, mit welchen ein Knoten in Verbindung steht"}. +{"The DateTime at which a leased subscription will end or has ended","Das DateTime an welchem ein geleastes Abonnement enden wird oder geendet hat"}. +{"The datetime when the node was created","Das DateTime an welchem der Knoten erstellt wurde"}. +{"The default language of the node","Die voreingestellte Sprache des Knotens"}. +{"The feature requested is not supported by the conference","Die angeforderte Eigenschaft wird von der Konferenz nicht unterstützt"}. +{"The JID of the node creator","Die JID des Nodeerstellers"}. +{"The JIDs of those to contact with questions","Die JIDs jener, die bei Fragen zu kontaktieren sind"}. +{"The JIDs of those with an affiliation of owner","Die JIDs jener mit einer Zugehörigkeit von Besitzer"}. +{"The JIDs of those with an affiliation of publisher","Die JIDs jener mit einer Zugehörigkeit von Veröffentlicher"}. +{"The list of all online users","Die Liste aller angemeldeter Benutzer"}. +{"The list of all users","Die Liste aller Benutzer"}. +{"The list of JIDs that may associate leaf nodes with a collection","Die Liste der JIDs die Blattknoten mit einer Sammlung verknüpfen dürfen"}. +{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","Die Höchstzahl der untergeordneten Knoten, die einer Sammlung zugeordnet werden können, oder `max`, wenn es keine spezifische Begrenzung gibt, sondern nur eine vom Server festgelegte Höchstzahl"}. +{"The minimum number of milliseconds between sending any two notification digests","Die minimale Anzahl an Millisekunden zwischen dem Senden von zwei Benachrichtigungs-Übersichten"}. +{"The name of the node","Der Name des Knotens"}. +{"The node is a collection node","Der Knoten ist ein Sammlungsknoten"}. +{"The node is a leaf node (default)","Der Knoten ist ein Blattknoten (Voreinstellung)"}. +{"The NodeID of the relevant node","Die NodeID des relevanten Knotens"}. +{"The number of pending incoming presence subscription requests","Die Anzahl der ausstehenden eintreffenden Präsenzabonnement-Anforderungen"}. +{"The number of subscribers to the node","Die Anzahl der Abonnenten des Knotens"}. +{"The number of unread or undelivered messages","Die Anzahl der ungelesenen oder nicht zugestellten Nachrichten"}. +{"The password contains unacceptable characters","Das Passwort enthält ungültige Zeichen"}. +{"The password is too weak","Das Passwort ist zu schwach"}. {"the password is","das Passwort lautet"}. -{"The password is too weak","Das Passwort ist zu einfach"}. -{"The password of your Jabber account was successfully changed.","Das Passwort von ihrem Jabber Konto wurde geändert."}. -{"There was an error changing the password: ","Es trat ein Fehler beim Ändern des Passworts auf: "}. +{"The password of your XMPP account was successfully changed.","Das Passwort Ihres XMPP-Kontos wurde erfolgreich geändert."}. +{"The password was not changed","Das Passwort wurde nicht geändert"}. +{"The passwords are different","Die Passwörter sind unterschiedlich"}. +{"The presence states for which an entity wants to receive notifications","Die Präsenzzustände für welche eine Entität Benachrichtigungen erhalten will"}. +{"The query is only allowed from local users","Die Anfrage ist nur von lokalen Benutzern erlaubt"}. +{"The query must not contain elements","Die Anfrage darf keine -Elemente enthalten"}. +{"The room subject can be modified by participants","Das Raum-Thema kann von Teilnehmern geändert werden"}. +{"The sender of the last received message","Der Absender der letzten erhaltenen Nachricht"}. +{"The stanza MUST contain only one element, one element, or one element","Das Stanza darf nur ein -Element, ein -Element oder ein -Element enthalten"}. +{"The subscription identifier associated with the subscription request","Die mit der Abonnement-Anforderung verknüpfte Abonnement-Bezeichnung"}. +{"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","Die URL einer XSL-Transformation welche auf Nutzdaten angewendet werden kann, um ein geeignetes Nachrichtenkörper-Element zu generieren."}. +{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","Die URL einer XSL-Transformation welche auf das Nutzdaten-Format angewendet werden kann, um ein gültiges Data Forms-Ergebnis zu generieren das der Client mit Hilfe einer generischen Data Forms-Rendering-Engine anzeigen könnte"}. +{"There was an error changing the password: ","Es trat ein Fehler beim Ändern des Passwortes auf: "}. {"There was an error creating the account: ","Es trat ein Fehler beim Erstellen des Kontos auf: "}. {"There was an error deleting the account: ","Es trat ein Fehler beim Löschen des Kontos auf: "}. -{"This IP address is blacklisted in ~s","Diese IP Adresse ist blockiert in ~s"}. -{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Groß/Klein-Schreibung spielt hierbei keine Rolle: macbeth ist gleich MacBeth und Macbeth."}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Diese Seite erlaubt das anlegen eines Jabber Kontos auf diesem Jabber Server. Ihre JID (Jabber IDentifier) setzt sich folgend zusammen: benutzername@server. Bitte lesen sie die Hinweise genau durch, um die Felder korrekt auszufüllen."}. -{"This page allows to unregister a Jabber account in this Jabber server.","Diese Seite erlaubt es, ein Jabber Konto von diesem Server zu entfernen."}. +{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Dies ist schreibungsunabhängig: macbeth ist gleich MacBeth und Macbeth."}. +{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Diese Seite erlaubt das Anlegen eines XMPP-Kontos auf diesem XMPP-Server. Ihre JID (Jabber-ID) wird diese Form aufweisen: benutzername@server. Bitte lesen Sie die Anweisungen genau durch, um die Felder korrekt auszufüllen."}. +{"This page allows to unregister an XMPP account in this XMPP server.","Diese Seite erlaubt es, ein XMPP-Konto von diesem XMPP-Server zu entfernen."}. +{"This room is not anonymous","Dieser Raum ist nicht anonym"}. +{"This service can not process the address: ~s","Dieser Dienst kann die Adresse nicht verarbeiten: ~s"}. {"Thursday","Donnerstag"}. {"Time delay","Zeitverzögerung"}. -{"Time","Zeit"}. -{"To","An"}. -{"Too many CAPTCHA requests","Zu viele CAPTCHA Anfragen"}. -{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Zu viele (~p) fehlgeschlagene Anmeldeversuche von dieser IP Adresse (~s). Die Adresse wird bis ~s UTC blockiert."}. +{"Timed out waiting for stream resumption","Zeitüberschreitung beim Warten auf Streamfortsetzung"}. +{"To register, visit ~s","Um sich zu registrieren, besuchen Sie ~s"}. +{"To ~ts","An ~ts"}. +{"Token TTL","Token-TTL"}. +{"Too many active bytestreams","Zu viele aktive Bytestreams"}. +{"Too many CAPTCHA requests","Zu viele CAPTCHA-Anforderungen"}. +{"Too many child elements","Zu viele 'child'-Elemente"}. +{"Too many elements","Zu viele -Elemente"}. +{"Too many elements","Zu viele -Elemente"}. +{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Zu viele (~p) fehlgeschlagene Authentifizierungen von dieser IP-Adresse (~s). Die Adresse wird an ~s UTC entsperrt"}. +{"Too many receiver fields were specified","Zu viele Empfängerfelder wurden angegeben"}. {"Too many unacked stanzas","Zu viele unbestätigte Stanzas"}. -{"To ~s","An ~s"}. -{"Total rooms","Alle Chaträume"}. +{"Too many users in this conference","Zu viele Benutzer in dieser Konferenz"}. {"Traffic rate limit is exceeded","Datenratenlimit wurde überschritten"}. -{"Transactions Aborted:","Abgebrochene Transaktionen:"}. -{"Transactions Committed:","Durchgeführte Transaktionen:"}. -{"Transactions Logged:","Protokollierte Transaktionen:"}. -{"Transactions Restarted:","Neu gestartete Transaktionen:"}. +{"~ts's MAM Archive","~ts's MAM Archiv"}. +{"~ts's Offline Messages Queue","Offline-Nachrichten-Warteschlange von ~ts"}. {"Tuesday","Dienstag"}. -{"Unable to generate a CAPTCHA","Konnte CAPTCHA nicht erstellen"}. -{"Unauthorized","Nicht berechtigt"}. -{"Unregister","Abmelden"}. -{"Unregister a Jabber account","Jabber Konto entfernen"}. -{"Update","Aktualisieren"}. +{"Unable to generate a CAPTCHA","Konnte kein CAPTCHA erstellen"}. +{"Unable to register route on existing local domain","Konnte Route auf existierender lokaler Domäne nicht registrieren"}. +{"Unauthorized","Nicht autorisiert"}. +{"Unexpected action","Unerwartete Aktion"}. +{"Unexpected error condition: ~p","Unerwarteter Fehlerzustand: ~p"}. +{"Uninstall","Deinstallieren"}. +{"Unregister an XMPP account","Ein XMPP-Konto entfernen"}. +{"Unregister","Deregistrieren"}. +{"Unsupported element","Nicht unterstütztes -Element"}. +{"Unsupported version","Nicht unterstützte Version"}. {"Update message of the day (don't send)","Aktualisiere Nachricht des Tages (nicht senden)"}. {"Update message of the day on all hosts (don't send)","Aktualisiere Nachricht des Tages auf allen Hosts (nicht senden)"}. -{"Update ~p","Aktualisierung ~p"}. -{"Update plan","Aktualisierungsplan"}. -{"Update script","Aktualisierungsscript"}. -{"Uptime:","Betriebszeit:"}. -{"Use of STARTTLS required","Verwendung von STARTTLS erforderlich"}. -{"User","Benutzer"}. -{"User JID","Benutzer JID"}. +{"Update specs to get modules source, then install desired ones.","Aktualisieren Sie die Spezifikationen, um den Quellcode der Module zu erhalten und installieren Sie dann die gewünschten Module."}. +{"Update Specs","Spezifikationen aktualisieren"}. +{"Updating the vCard is not supported by the vCard storage backend","Aktualisierung der vCard wird vom vCard-Speicher-Backend nicht unterstützt"}. +{"Upgrade","Upgrade"}. +{"URL for Archived Discussion Logs","URL für archivierte Diskussionsprotokolle"}. +{"User already exists","Benutzer existiert bereits"}. +{"User (jid)","Benutzer (JID)"}. +{"User JID","Benutzer-JID"}. {"User Management","Benutzerverwaltung"}. +{"User removed","Benutzer entfernt"}. +{"User session not found","Benutzersitzung nicht gefunden"}. +{"User session terminated","Benutzersitzung beendet"}. +{"User ~ts","Benutzer ~ts"}. +{"User","Benutzer"}. {"Username:","Benutzername:"}. {"Users are not allowed to register accounts so quickly","Benutzer dürfen Konten nicht so schnell registrieren"}. -{"Users","Benutzer"}. -{"User ~s","Benutzer ~s"}. {"Users Last Activity","Letzte Benutzeraktivität"}. -{"Validate","Validieren"}. +{"Users","Benutzer"}. +{"Value 'get' of 'type' attribute is not allowed","Wert 'get' des 'type'-Attributs ist nicht erlaubt"}. +{"Value of '~s' should be boolean","Wert von '~s' sollte boolesch sein"}. +{"Value of '~s' should be datetime string","Wert von '~s' sollte DateTime-Zeichenkette sein"}. +{"Value of '~s' should be integer","Wert von '~s' sollte eine Ganzzahl sein"}. +{"Value 'set' of 'type' attribute is not allowed","Wert 'set' des 'type'-Attributs ist nicht erlaubt"}. {"vCard User Search","vCard-Benutzer-Suche"}. +{"View joined MIX channels","Beitretene MIX-Channel ansehen"}. {"Virtual Hosts","Virtuelle Hosts"}. {"Visitor","Besucher"}. -{"Visitors are not allowed to change their nicknames in this room","Besucher dürfen in diesem Raum ihren Benutzernamen nicht ändern"}. -{"Visitors are not allowed to send messages to all occupants","Besucher dürfen nicht an alle Teilnehmer Nachrichten verschicken"}. -{"Voice request","Anfrage für Sprachrechte"}. -{"Voice requests are disabled in this conference","Anfragen für Sprachrechte sind in diesem Raum deaktiviert"}. +{"Visitors are not allowed to change their nicknames in this room","Besucher dürfen in diesem Raum ihren Spitznamen nicht ändern"}. +{"Visitors are not allowed to send messages to all occupants","Besucher dürfen nicht an alle Teilnehmer Nachrichten versenden"}. +{"Voice requests are disabled in this conference","Sprachrecht-Anforderungen sind in diesem Raum deaktiviert"}. +{"Voice request","Sprachrecht-Anforderung"}. +{"Web client which allows to join the room anonymously","Web-Client, der es ermöglicht, dem Raum anonym beizutreten"}. {"Wednesday","Mittwoch"}. -{"When to send the last published item","Wann das letzte veröffentlichte Objekt gesendet werden soll"}. +{"When a new subscription is processed and whenever a subscriber comes online","Sobald ein neues Abonnement verarbeitet wird und wann immer ein Abonnent sich anmeldet"}. +{"When a new subscription is processed","Sobald ein neues Abonnement verarbeitet wird"}. +{"When to send the last published item","Wann das letzte veröffentlichte Item gesendet werden soll"}. +{"Whether an entity wants to receive an XMPP message body in addition to the payload format","Ob eine Entität zusätzlich zum Nutzdatenformat einen XMPP-Nachrichtenkörper erhalten will"}. +{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","Ob eine Entität Übersichten (Gruppierungen) von Benachrichtigungen oder alle Benachrichtigungen separat erhalten will"}. +{"Whether an entity wants to receive or disable notifications","Ob eine Entität Benachrichtigungen erhalten oder deaktivieren will"}. +{"Whether owners or publisher should receive replies to items","Ob Besitzer oder Veröffentlicher Antworten auf Items erhalten sollen"}. +{"Whether the node is a leaf (default) or a collection","Ob der Knoten ein Blatt (Voreinstellung) oder eine Sammlung ist"}. {"Whether to allow subscriptions","Ob Abonnements erlaubt sind"}. -{"You can later change your password using a Jabber client.","Sie können das Passwort später mit einem Jabber Client Programm ändern."}. +{"Whether to make all subscriptions temporary, based on subscriber presence","Ob alle Abonnements temporär gemacht werden sollen, basierend auf der Abonnentenpräsenz"}. +{"Whether to notify owners about new subscribers and unsubscribes","Ob Besitzer über neue Abonnenten und Abbestellungen benachrichtigt werden sollen"}. +{"Who can send private messages","Wer kann private Nachrichten senden"}. +{"Who may associate leaf nodes with a collection","Wer Blattknoten mit einer Sammlung verknüpfen darf"}. +{"Wrong parameters in the web formulary","Falsche Parameter im Webformular"}. +{"Wrong xmlns","Falscher xmlns"}. +{"XMPP Account Registration","XMPP-Konto-Registrierung"}. +{"XMPP Domains","XMPP-Domänen"}. +{"XMPP Show Value of Away","XMPP-Anzeigewert von Abwesend"}. +{"XMPP Show Value of Chat","XMPP-Anzeigewert von Chat"}. +{"XMPP Show Value of DND (Do Not Disturb)","XMPP-Anzeigewert von DND (Do Not Disturb/Bitte nicht stören)"}. +{"XMPP Show Value of XA (Extended Away)","XMPP-Anzeigewert von XA (Extended Away/für längere Zeit abwesend)"}. +{"XMPP URI of Associated Publish-Subscribe Node","XMPP-URI des verknüpften Publish-Subscribe-Knotens"}. +{"You are being removed from the room because of a system shutdown","Sie werden wegen einer Systemabschaltung aus dem Raum entfernt"}. +{"You are not allowed to send private messages","Sie dürfen keine privaten Nachrichten senden"}. +{"You are not joined to the channel","Sie sind dem Raum nicht beigetreten"}. +{"You can later change your password using an XMPP client.","Sie können Ihr Passwort später mit einem XMPP-Client ändern."}. {"You have been banned from this room","Sie wurden aus diesem Raum verbannt"}. -{"You must fill in field \"Nickname\" in the form","Sie müssen das Feld \"Benutzername\" ausfüllen"}. -{"You need a client that supports x:data and CAPTCHA to register","Sie benötigen einen Client, der x:data und CAPTCHA unterstützt, um Ihren Benutzernamen zu registrieren"}. -{"You need a client that supports x:data to register the nickname","Sie benötigen einen Client, der x:data unterstützt, um Ihren Benutzernamen zu registrieren"}. -{"You need an x:data capable client to configure mod_irc settings","Sie benötigen einen Client, der x:data unterstützt, um die mod_irc-Einstellungen zu konfigurieren"}. -{"You need an x:data capable client to configure room","Sie benötigen einen Client, der x:data unterstützt, um den Raum zu konfigurieren"}. -{"You need an x:data capable client to search","Sie benötigen einen Client, der x:data unterstützt, um die Suche verwenden zu können"}. -{"Your active privacy list has denied the routing of this stanza.","Ihre aktive Privacy Liste hat die Weiterleitung des Stanzas unterbunden."}. -{"Your contact offline message queue is full. The message has been discarded.","Ihre Offline-Nachrichten-Warteschlange ist voll. Die Nachricht wurde verworfen."}. -{"Your Jabber account was successfully created.","Ihr Jabber Konto wurde erfolgreich erstellt."}. -{"Your Jabber account was successfully deleted.","Ihr Jabber Konto wurde erfolgreich gelöscht."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","Ihre Nachrichten an ~s werden blockiert. Um dies zu ändern, besuchen sie ~s"}. +{"You have joined too many conferences","Sie sind zu vielen Konferenzen beigetreten"}. +{"You must fill in field \"Nickname\" in the form","Sie müssen das Feld \"Spitzname\" im Formular ausfüllen"}. +{"You need a client that supports x:data and CAPTCHA to register","Sie benötigen einen Client der x:data und CAPTCHA unterstützt, um sich zu registrieren"}. +{"You need a client that supports x:data to register the nickname","Sie benötigen einen Client der x:data unterstützt, um Ihren Spitznamen zu registrieren"}. +{"You need an x:data capable client to search","Sie benötigen einen Client der x:data unterstützt, um zu suchen"}. +{"Your active privacy list has denied the routing of this stanza.","Ihre aktive Privacy-Liste hat das Routing dieses Stanzas verweigert."}. +{"Your contact offline message queue is full. The message has been discarded.","Die Offline-Nachrichten-Warteschlange Ihres Kontaktes ist voll. Die Nachricht wurde verworfen."}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Ihre Abonnement-Anforderung und/oder Nachrichten an ~s wurden blockiert. Um Ihre Abonnement-Anforderungen freizugeben, besuchen Sie ~s"}. +{"Your XMPP account was successfully registered.","Ihr XMPP-Konto wurde erfolgreich registriert."}. +{"Your XMPP account was successfully unregistered.","Ihr XMPP-Konto wurde erfolgreich entfernt."}. +{"You're not allowed to create nodes","Sie dürfen keine Knoten erstellen"}. diff --git a/priv/msgs/de.po b/priv/msgs/de.po deleted file mode 100644 index 09df3e9a9..000000000 --- a/priv/msgs/de.po +++ /dev/null @@ -1,1952 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: 2.1.0-alpha\n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: Nikolaus Polak \n" -"Language-Team: \n" -"Language: de\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: German (deutsch)\n" -"X-Additional-Translator: Florian Zumbiehl\n" -"X-Additional-Translator: Cord Beermann\n" -"X-Additional-Translator: Marvin Preuss\n" -"X-Additional-Translator: Patrick Dreker\n" -"X-Additional-Translator: Torsten Werner\n" -"X-Additional-Translator: Marina Hahn\n" -"X-Generator: Poedit 1.8.6\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "Verwendung von STARTTLS erforderlich" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Keine Ressource angegeben" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Durch neue Verbindung ersetzt" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "wurde entfernt" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "" -"Ihre aktive Privacy Liste hat die Weiterleitung des Stanzas unterbunden." - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "Zu viele unbestätigte Stanzas" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Geben sie den Text den sie sehen ein" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "" -"Ihre Nachrichten an ~s werden blockiert. Um dies zu ändern, besuchen sie ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "" -"Wenn sie das CAPTCHA Bild nicht sehen, besuchen sie bitte die Webseite." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "CAPTCHA Webseite" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "Die Verifizierung ist gültig." - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Benutzer" - -#: ejabberd_oauth.erl:256 -msgid "Server" -msgstr "Server" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Passwort" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "Akzeptieren" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "Nicht berechtigt" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "ejabberd Web-Admin" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Verwaltung" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Zugangskontroll-Listen (ACL)" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Gesendet" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Ungültiges Format" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Senden" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "Unformatiert" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Markierte löschen" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Zugangsregeln" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "~s Zugangsregel-Konfiguration" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "Virtuelle Hosts" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Benutzer" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 mod_configure.erl:524 -msgid "Online Users" -msgstr "Angemeldete Benutzer" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Letzte Benutzeraktivität" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Zeitraum: " - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "Letzter Monat" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "Letztes Jahr" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "Alle Aktivitäten" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Gewöhnliche Tabelle anzeigen" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Integrale Tabelle anzeigen" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Statistiken" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "Nicht gefunden" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Knoten nicht gefunden" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Neue(n) hinzufügen" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Host" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Registrierte Benutzer" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Benutzer hinzufügen" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Offline-Nachrichten" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Letzte Aktivität" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Nie" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Angemeldet" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Registrierte Benutzer:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Angemeldete Benutzer:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Ausgehende s2s-Verbindungen:" - -#: ejabberd_web_admin.erl:1559 -msgid "Incoming s2s Connections:" -msgstr "Eingehende s2s-Verbindungen:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Keine" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Passwort ändern" - -#: ejabberd_web_admin.erl:1673 -msgid "User ~s" -msgstr "Benutzer ~s" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Verbundene Ressourcen:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Passwort:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Benutzer löschen" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Keine Daten" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Knoten" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Aktive Knoten" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Angehaltene Knoten" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -msgid "Node ~p" -msgstr "Knoten ~p" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "Datenbank" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Datensicherung" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Aktive Ports" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Aktualisieren" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Neustart" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Stoppen" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Module" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "Fehler bei RPC-Aufruf" - -#: ejabberd_web_admin.erl:1917 -msgid "Database Tables at ~p" -msgstr "Datenbanktabellen auf ~p" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Vorname" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Speichertyp" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Elemente" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Speicher" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Fehler" - -#: ejabberd_web_admin.erl:1955 -msgid "Backup of ~p" -msgstr "Sicherung von ~p" - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Beachten sie, das diese Optionen nur die eingebaute Mnesia-Datenbank " -"sichern. Wenn sie das ODBC-Modul verwenden, müssen sie die SQL-Datenbank " -"manuell sichern." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Speichere binäre Sicherung:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "OK" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Stelle binäre Sicherung sofort wieder her:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"Stelle binäre Sicherung beim nächsten ejabberd-Neustart wieder her (benötigt " -"weniger Speicher):" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Speichere Klartext-Sicherung:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Stelle Klartext-Sicherung sofort wieder her:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "Benutzerdaten von einer PIEFXIS Datei (XEP-0227) importieren:" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" -"Alle Benutzerdaten des Servers in PIEFXIS Dateien (XEP-0227) exportieren:" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "" -"Alle Benutzerdaten des Hosts in PIEFXIS Dateien (XEP-0227) exportieren:" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "Alle Tabellen als SQL Abfragen in eine Datei exportieren:" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Importiere Benutzer von jabberd14 Spool Datei:" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Importiere Benutzer von jabberd14 Spool Verzeichnis:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Aktive Ports bei" - -#: ejabberd_web_admin.erl:2144 -msgid "Modules at ~p" -msgstr "Module bei ~p" - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "Statistiken von ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Betriebszeit:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "CPU-Zeit:" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Durchgeführte Transaktionen:" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Abgebrochene Transaktionen:" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Neu gestartete Transaktionen:" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Protokollierte Transaktionen:" - -#: ejabberd_web_admin.erl:2243 -msgid "Update ~p" -msgstr "Aktualisierung ~p" - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "Aktualisierungsplan" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Geänderte Module" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Aktualisierungsscript" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Low level Aktualisierungsscript" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Script-Überprüfung" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Port" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Protokoll" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Modul" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Optionen" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Starten" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Befehle" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "Die Nachricht des Tages wirklich löschen?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Betreff" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Nachrichtentext" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "Kein Text für die Ankündigungsnachricht angegeben" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Ankündigungen" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Sende Ankündigung an alle Benutzer" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Sende Ankündigung an alle Benutzer auf allen Hosts" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Sende Ankündigung an alle angemeldeten Benutzer" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "Sende Ankündigung an alle angemeldeten Benutzer auf allen Hosts" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Setze Nachricht des Tages und sende sie an alle angemeldeten Benutzer" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" -"Setze Nachricht des Tages auf allen Hosts und sende sie an alle angemeldeten " -"Benutzer" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Aktualisiere Nachricht des Tages (nicht senden)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "Aktualisiere Nachricht des Tages auf allen Hosts (nicht senden)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Lösche Nachricht des Tages" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Lösche Nachricht des Tages auf allen Hosts" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Konfiguration" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Module starten" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Module stoppen" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Wiederherstellung" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Ausgabe in Textdatei" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Datei importieren" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Verzeichnis importieren" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Dienst neustarten" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Dienst herunterfahren" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Benutzer löschen" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Benutzer-Sitzung beenden" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Benutzer-Passwort abrufen" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Benutzer-Passwort ändern" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "letzte Anmeldezeit abrufen" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Benutzer-Statistiken abrufen" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Anzahl der registrierten Benutzer abrufen" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Anzahl der angemeldeten Benutzer abrufen" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Benutzerverwaltung" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Alle Benutzer" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Ausgehende s2s-Verbindungen" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Datensicherungsverwaltung" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Importiere Benutzer aus jabberd14-Spool-Dateien" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "An ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "Von ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Datenbanktabellen-Konfiguration auf " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Wähle Speichertyp der Tabellen" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Nur auf Festplatte" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "RAM und Festplatte" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "Nur RAM" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "Fernkopie" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Stoppe Module auf " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Wähle zu stoppende Module" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Starte Module auf " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Geben sie eine Liste bestehend aus {Modul, [Optionen]} ein" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Liste der zu startenden Module" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Datensicherung in die Datei " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Geben sie den Pfad zur Datensicherung ein" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Pfad zur Datei" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Datenwiederherstellung aus der Datei " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Ausgabe der Sicherung in diese Textdatei " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Geben sie den Pfad zur Textdatei ein" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Benutzer aus dieser Datei importieren " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Geben sie den Pfad zur jabberd14-Spool-Datei ein" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Benutzer importieren aus dem Verzeichnis " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Geben sie den Pfad zum jabberd14-Spool-Verzeichnis ein" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Pfad zum Verzeichnis" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "Zeitverzögerung" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Konfiguration der Zugangskontrolllisten" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Zugangskontroll-Listen (ACL)" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Zugangskonfiguration" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Zugangsregeln" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Passwort bestätigen" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Anzahl der registrierten Benutzer" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Anzahl der angemeldeten Benutzer" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Letzte Anmeldung" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "Kontaktlistengröße" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "IP Adressen" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Ressourcen" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Administration von " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Aktion auf Benutzer" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Einstellungen ändern" - -#: mod_fail2ban.erl:95 -msgid "" -"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." - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "Bitte geben Sie die Dateigröße an." - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "Bitte geben Sie den Dateinamen an." - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "Diese IP Adresse ist blockiert in ~s" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "Zugang aufgrund der Dienstrichtlinien verweigert" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "IRC Transport" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "ejabberd IRC-Modul" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "" -"Sie benötigen einen Client, der x:data unterstützt, um die mod_irc-" -"Einstellungen zu konfigurieren" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Registrierung in mod_irc für " - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Geben sie den Benutzernamen, Zeichenkodierung, Ports und Passwörter, die sie " -"für die Verbindung zum IRC-Server verwenden wollen, an" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "IRC-Benutzername" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Wenn sie verschiedene Ports, Passwörter und Kodierungen für IRC Server " -"angeben wollen, erstellen sie die Liste mit folgendem Format '{\"IRC Server" -"\", \"Kodierung\", Port, \"Passwort\"}'. Standardmäßig benutzt dieser " -"Dienst die \"~s\" Kodierung, den Port ~p und kein Passwort." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Beispiel: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta." -"fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Verbindungsparameter" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "IRC Channel beitreten" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "IRC Channel (ohne dem ersten #)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "IRC Server" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Hier den IRC Channel beitreten." - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Den IRC Channel mit dieser Jabber ID beitreten: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "IRC Einstellungen" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Geben sie Benutzernamen und Kodierung für Verbindungen zu IRC Servern an. " -"Drücken sie 'Mehr' um leere Felder hinzuzufügen. Drücken sie 'Beenden' um " -"die Einstellungen zu speichern." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "IRC Benutzername" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Passwort ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Port ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Kodierung für Server ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Server ~b" - -#: mod_mam.erl:541 -msgid "Only members may query archives of this room" -msgstr "Nur Mitglieder dürfen den Verlauf dieses Raumes abrufen" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "" -"Nur Service-Administratoren sind berechtigt, Servicenachrichten zu versenden" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "Anlegen des Raumes aufgrund der Dienstrichtlinien verweigert" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "Konferenzraum existiert nicht" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Chaträume" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "Leere Räume" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "" -"Sie benötigen einen Client, der x:data unterstützt, um Ihren Benutzernamen " -"zu registrieren" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Registrieren des Benutzernames auf " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Geben sie den zu registrierenden Benutzernamen ein" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Benutzername" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "Dieser Benutzername wurde bereits von jemand anderem registriert" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Sie müssen das Feld \"Benutzername\" ausfüllen" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "ejabberd MUC-Modul" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "Mehrbenutzer-Chat (MUC)" - -#: mod_muc_admin.erl:249 -msgid "Total rooms" -msgstr "Alle Chaträume" - -#: mod_muc_admin.erl:250 -msgid "Permanent rooms" -msgstr "Permanente Chaträume" - -#: mod_muc_admin.erl:251 -msgid "Registered nicknames" -msgstr "Registrierte Benutzernamen" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "Liste von Chaträumen" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "Chatraum-Konfiguration geändert" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "betretet den Raum" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "verlässt den Raum" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "wurde gebannt" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "wurde wegen Änderung des Mitgliederstatus entfernt" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "wurde entfernt weil der Raum auf Nur-Mitglieder umgestellt wurde" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "wurde wegen einer Systemabschaltung entfernt" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "ist nun bekannt als" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " hat das Thema geändert auf: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "Chatraum wurde erstellt" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "Chatraum wurde entfernt" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "Chatraum wurde gestartet" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "Chatraum wurde beendet" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "Montag" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "Dienstag" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "Mittwoch" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "Donnerstag" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "Freitag" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "Samstag" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "Sonntag" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "Januar" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "Februar" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "März" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "April" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "Mai" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "Juni" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "Juli" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "August" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "September" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "Oktober" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "November" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "Dezember" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Raum-Konfiguration" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "Teilnehmer in diesem Raum" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "Datenratenlimit wurde überschritten" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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 entfernt" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "Es ist nicht erlaubt private Nachrichten an den Raum zu schicken" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "" -"Bitte warten sie ein wenig, bevor sie eine weitere Anfrage für Sprachrechte " -"senden" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "Anfragen für Sprachrechte sind in diesem Raum deaktiviert" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "" -"Fehler beim Auslesen der JID aus der Anfragenbestätigung für Sprachrechte" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "Nur Moderatoren können Anfragen für Sprachrechte bestätigen" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Unzulässiger Nachrichtentyp" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "" -"Es ist nicht erlaubt private Nachrichten des Typs \"Gruppenchat\" zu senden" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "Der Empfänger ist nicht im Raum" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "Es ist nicht erlaubt private Nachrichten zu senden" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Nur Teilnehmer dürfen Nachrichten an den Raum schicken" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "Nur Teilnehmer sind berechtigt Anfragen an die Konferenz zu senden" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "Anfragen an die Teilnehmer sind in diesem Raum nicht erlaubt" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "Nur Moderatoren und Mitglieder dürfen das Thema in diesem Raum ändern" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Nur Moderatoren dürfen das Thema in diesem Raum ändern" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "Besucher dürfen nicht an alle Teilnehmer Nachrichten verschicken" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "Besucher dürfen in diesem Raum ihren Benutzernamen nicht ändern" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "Dieser Benutzername wird bereits von einem Teilnehmer genutzt" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Sie wurden aus diesem Raum verbannt" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "Um diesen Raum zu betreten müssen sie Mitglied sein" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "Sie brauchen ein Passwort um diesen Raum zu betreten" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "Zu viele CAPTCHA Anfragen" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "Konnte CAPTCHA nicht erstellen" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Falsches Passwort" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "Administratorenrechte benötigt" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "Moderatorrechte benötigt" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "Die Jabber-ID ~s ist ungültig" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "Der Benutzername ~s existiert im Raum nicht" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Ungültige Mitgliedschaft: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Ungültige Rolle: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "Besitzerrechte benötigt" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "Konfiguration für Raum ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Raumname" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "Raum Beschreibung" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Raum persistent machen" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Raum öffentlich suchbar machen" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "Teilnehmerliste öffentlich machen" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Raum mit Passwort schützen" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Maximale Anzahl von Teilnehmern" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Keine Begrenzung" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Echte Jabber-IDs anzeigen für" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "ausschliesslich Moderatoren" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "jeden" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "Rollen, für die der Status übertragen wird" - -#: mod_muc_room.erl:3486 -msgid "Moderator" -msgstr "Moderator" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "Teilnehmer" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "Besucher" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Raum nur für Mitglieder zugänglich machen" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Raum moderiert machen" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Benutzer werden standardmäßig vollwertige Teilnehmer" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Erlaube Benutzern das Thema zu ändern" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Erlaube Benutzern private Nachrichten zu senden" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "Erlaube Besuchern das Senden von privaten Nachrichten an" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "niemanden" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Erlaube Benutzern Informationen über andere Benutzer abzufragen" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Erlaube Benutzern Einladungen zu senden" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "Erlaube Besuchern einen Text bei Statusänderung zu senden" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Erlaube Besuchern ihren Benutzernamen zu ändern" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "Anfragen von Sprachrechten für Benutzer erlauben" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "Mindestdauer zwischen Anfragen für Sprachrechte (in Sekunden)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "Raum mit Verifizierung (Captcha) versehen" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "Nachrichtenarchivierung aktivieren" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "Von CAPTCHA Überprüfung ausgeschlossene Jabber IDs" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Protokollierung aktivieren" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "" -"Sie benötigen einen Client, der x:data unterstützt, um den Raum zu " -"konfigurieren" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Anzahl der Teilnehmer" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "privat, " - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "Anfrage für Sprachrechte" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "Diese Anfrage für Sprachrechte bestätigen oder ablehnen." - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "Benutzer JID" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "Sprachrechte dieser Person erteilen?" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s lädt sie in den Raum ~s ein" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "das Passwort lautet" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "Multicast" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "ejabberd Multicast Dienst" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "" -"Ihre Offline-Nachrichten-Warteschlange ist voll. Die Nachricht wurde " -"verworfen." - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "~s's Offline-Nachrichten-Warteschlange" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Zeit" - -#: mod_offline.erl:812 -msgid "From" -msgstr "Von" - -#: mod_offline.erl:813 -msgid "To" -msgstr "An" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Paket" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Offline-Nachrichten:" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "Alle Offline Nachrichten löschen" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "ejabberd SOCKS5-Bytestreams-Modul" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Publish-Subscribe" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "ejabberd Publish-Subscribe-Modul" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "PubSub-Abonnenten-Anfrage" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Wählen sie, ob dieses Abonnement akzeptiert werden soll." - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "Knoten-ID" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Abonnenten-Adresse" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "Dieser Jabber-ID das Abonnement dieses pubsub-Knotens erlauben?" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Nachrichten mit Ereignis-Benachrichtigungen zustellen" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Ereignisbenachrichtigung zustellen" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Abonnenten benachrichtigen, wenn sich die Knotenkonfiguration ändert" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Abonnenten benachrichtigen, wenn der Knoten gelöscht wird" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Abonnenten benachrichtigen, wenn Einträge vom Knoten entfernt werden" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Einträge dauerhaft speichern" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "Ein merkbarer Name für den Knoten" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Maximale Anzahl dauerhaft zu speichernder Einträge" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Ob Abonnements erlaubt sind" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Geben sie das Zugangsmodell an" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Kontaktlisten-Gruppen die abonnieren dürfen" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Geben sie das Publikationsmodell an" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "" -"Alle Einträge entfernen, wenn der relevante Veröffentlicher offline geht" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "Geben sie den Ereignis-Nachrichtentyp an" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Maximale Nutzlastgrösse in Bytes" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "Wann das letzte veröffentlichte Objekt gesendet werden soll" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Benachrichtigungen nur an verfügbare Benutzer schicken" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "Sammlungen, mit denen ein Knoten verknüpft ist" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "Die CAPTCHA Verifizierung schlug fehl" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "" -"Sie benötigen einen Client, der x:data und CAPTCHA unterstützt, um Ihren " -"Benutzernamen zu registrieren" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "Wählen sie zum Registrieren einen Benutzernamen und ein Passwort" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "Das Passwort ist zu einfach" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "Benutzer dürfen Konten nicht so schnell registrieren" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "Ihr Jabber Konto wurde erfolgreich erstellt." - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "Es trat ein Fehler beim Erstellen des Kontos auf: " - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "Ihr Jabber Konto wurde erfolgreich gelöscht." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "Es trat ein Fehler beim Löschen des Kontos auf: " - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "Das Passwort von ihrem Jabber Konto wurde geändert." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "Es trat ein Fehler beim Ändern des Passworts auf: " - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Jabber Konto Anmeldung" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Jabber Konto registrieren" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Jabber Konto entfernen" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"Diese Seite erlaubt das anlegen eines Jabber Kontos auf diesem Jabber " -"Server. Ihre JID (Jabber IDentifier) setzt sich folgend zusammen: " -"benutzername@server. Bitte lesen sie die Hinweise genau durch, um die Felder " -"korrekt auszufüllen." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "Benutzername:" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" -"Groß/Klein-Schreibung spielt hierbei keine Rolle: macbeth ist gleich MacBeth " -"und Macbeth." - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "Nicht erlaubte Zeichen:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "Server:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" -"Geben sie niemandem ihr Passwort, auch nicht den Administratoren des Jabber " -"Servers." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "" -"Sie können das Passwort später mit einem Jabber Client Programm ändern." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Einige Jabber Client Programme speichern ihr Passwort auf ihrem Computer. " -"Verwenden sie diese Möglichkeit nur auf Computern, die sie als sicher " -"einstufen." - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"Merken sie sich ihr Passwort, oder schreiben sie es auf einen Zettel den sie " -"sicher verwahren. Bei Jabber gibt es keine automatische Möglichkeit, das " -"Passwort wiederherzustellen." - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "Passwort bestätigen:" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "Anmelden" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "Aktuelles Passwort:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "Neues Passwort:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "" -"Diese Seite erlaubt es, ein Jabber Konto von diesem Server zu entfernen." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "Abmelden" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Abonnement" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Schwebend" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Gruppen" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Validieren" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Entfernen" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Kontaktliste von " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Jabber-ID hinzufügen" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Kontaktliste" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Gruppen der gemeinsamen Kontaktliste" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Name:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Beschreibung:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Mitglieder:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Angezeigte Gruppen:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Gruppe " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Erlang Jabber Server" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Geburtsdatum" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Stadt" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "Land" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "E-Mail" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Nachname" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Füllen sie die Felder aus, um nach passenden Jabber-Benutzern zu suchen " -"(beenden Sie ein Feld mit *, um auch nach Teilzeichenketten zu suchen)" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Vollständiger Name" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Zweiter Vorname" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Name der Organisation" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Abteilung" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Benutzer suchen in " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "" -"Sie benötigen einen Client, der x:data unterstützt, um die Suche verwenden " -"zu können" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "vCard-Benutzer-Suche" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "ejabberd vCard-Modul" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Suchergebnisse für " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "" -"Füllen sie die Felder aus, um nach bestimmten Jabber-Benutzern zu suchen" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Ausgehende s2s-Server:" - -#~ msgid "Delete" -#~ msgstr "Löschen" - -#~ msgid "This room is not anonymous" -#~ msgstr "Dieser Raum ist nicht anonym" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "" -#~ "Dieser Teilnehmer wurde aus dem Raum geworfen, da er eine fehlerhafte " -#~ "Nachricht gesendet hat" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Dieser Teilnehmer wurde aus dem Raum geworfen, da er eine fehlerhafte " -#~ "Nachricht an einen anderen Teilnehmer gesendet hat" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "" -#~ "Dieser Teilnehmer wurde aus dem Raum gekickt, da er einen fehlerhaften " -#~ "Status gesendet hat" - -#, fuzzy -#~ msgid "Captcha test failed" -#~ msgstr "Die Verifizierung ist gültig." diff --git a/priv/msgs/ejabberd.pot b/priv/msgs/ejabberd.pot deleted file mode 100644 index c62ee20b1..000000000 --- a/priv/msgs/ejabberd.pot +++ /dev/null @@ -1,1846 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: 15.11.127\n" -"X-Language: Language Name\n" -"Last-Translator: Translator name and contact method\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "" - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "" - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "" - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "" - -#: ejabberd_oauth.erl:256 -msgid "Server" -msgstr "" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "" - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "" - -#: ejabberd_web_admin.erl:1559 -msgid "Incoming s2s Connections:" -msgstr "" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "" - -#: ejabberd_web_admin.erl:1673 -msgid "User ~s" -msgstr "" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -msgid "Node ~p" -msgstr "" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "" - -#: ejabberd_web_admin.erl:1917 -msgid "Database Tables at ~p" -msgstr "" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "" - -#: ejabberd_web_admin.erl:1955 -msgid "Backup of ~p" -msgstr "" - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "" - -#: ejabberd_web_admin.erl:2144 -msgid "Modules at ~p" -msgstr "" - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "" - -#: ejabberd_web_admin.erl:2243 -msgid "Update ~p" -msgstr "" - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "" - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "" - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "" - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "" - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "" - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "" - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "" - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "" - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "" - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "" - -#: mod_fail2ban.erl:95 -msgid "" -"Too many (~p) failed authentications from this IP address (~s). The address " -"will be unblocked at ~s UTC" -msgstr "" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "" - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "" - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "" - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "" - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "" - -#: mod_mam.erl:541 -msgid "Only members may query archives of this room" -msgstr "" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "" - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "" - -#: mod_muc_admin.erl:249 -msgid "Total rooms" -msgstr "" - -#: mod_muc_admin.erl:250 -msgid "Permanent rooms" -msgstr "" - -#: mod_muc_admin.erl:251 -msgid "Registered nicknames" -msgstr "" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr "" - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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 "" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "" - -#: mod_muc_room.erl:3486 -msgid "Moderator" -msgstr "" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "" - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "" - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "" - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "" - -#: mod_offline.erl:812 -msgid "From" -msgstr "" - -#: mod_offline.erl:813 -msgid "To" -msgstr "" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "" - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "" - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "" - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "" - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "" - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "" - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "" - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "" - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "" - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "" - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "" - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "" - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "" diff --git a/priv/msgs/el.msg b/priv/msgs/el.msg index ce1632dc7..0d18b30c4 100644 --- a/priv/msgs/el.msg +++ b/priv/msgs/el.msg @@ -1,133 +1,184 @@ -%% -*- coding: latin-1 -*- -{"Access Configuration","Διαμόρφωση Πρόσβασης"}. -{"Access Control List Configuration","Διαχείριση στις Λίστες Ελέγχου Πρόσβασης"}. -{"Access control lists","Λίστες Ελέγχου Πρόσβασης"}. -{"Access Control Lists","Λίστες Ελέγχου Πρόσβασης"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" (Add * to the end of field to match substring)"," (Προσθέστε * στο τέλος του πεδίου για ταίριασμα με το substring)"}. +{" has set the subject to: "," έχει θέσει το θέμα σε: "}. +{"# participants","# συμμετέχοντες"}. +{"A description of the node","Μία περιγραφή του κόμβου"}. +{"A friendly name for the node","Ένα φιλικό όνομα για τον κόμβο"}. +{"A password is required to enter this room","Απαιτείται κωδικός πρόσβασης για είσοδο σε αυτή την αίθουσα"}. +{"A Web Page","Μία ιστοσελίδα"}. +{"Accept","Αποδοχή"}. {"Access denied by service policy","Άρνηση πρόσβασης, λόγω τακτικής παροχής υπηρεσιών"}. -{"Access rules","Κανόνες Πρόσβασης"}. -{"Access Rules","Κανόνες Πρόσβασης"}. +{"Access model","Μοντέλο πρόσβασης"}. +{"Account doesn't exist","Ο λογαριασμός δεν υπάρχει"}. {"Action on user","Eνέργεια για το χρήστη"}. -{"Add Jabber ID","Προσθήκη Jabber Ταυτότητας"}. -{"Add New","Προσθήκη νέου"}. +{"Add a hat to a user","Προσθέστε ένα καπέλο σε έναν χρήστη"}. {"Add User","Προσθήκη Χρήστη"}. -{"Administration of ","Διαχείριση του"}. +{"Administration of ","Διαχείριση του "}. {"Administration","Διαχείριση"}. {"Administrator privileges required","Aπαιτούνται προνόμια διαχειριστή"}. -{"A friendly name for the node","Ένα φιλικό όνομα για τον κόμβο"}. {"All activity","Όλες οι δραστηριότητες"}. +{"All Users","Όλοι οι χρήστες"}. +{"Allow subscription","Επιτρέψτε την Συνδρομή"}. {"Allow this Jabber ID to subscribe to this pubsub node?","Επιτρέπετε σε αυτή την Jabber Ταυτότητα να εγγραφεί σε αυτό τον κόμβο Δημοσίευσης-Εγγραφής;"}. -{"Allow users to change the subject","Επιτρέψετε στους χρήστες να αλλάζουν το θέμα"}. -{"Allow users to query other users","Επιτρέπστε στους χρήστες να ερωτούν άλλους χρήστες"}. -{"Allow users to send invites","Επιτρέψετε στους χρήστες να αποστέλλουν προσκλήσεις"}. -{"Allow users to send private messages","Επιτρέψετε στους χρήστες να αποστέλλουν ιδιωτικά μηνύματα"}. -{"Allow visitors to change nickname","Επιτρέψετε στους επισκέπτες να αλλάζου ψευδώνυμο"}. -{"Allow visitors to send private messages to","Επιτρέψετε στους χρήστες να αποστέλλουν ιδιωτικά μηνύματα σε"}. +{"Allow this person to register with the room?","Επιτρέπτε στο άτομο να καταχωρηθεί στην αίθουσα;"}. +{"Allow users to change the subject","Επιτρέψτε στους χρήστες να αλλάζουν το θέμα"}. +{"Allow users to query other users","Επιτρέψτε στους χρήστες να ερωτούν άλλους χρήστες"}. +{"Allow users to send invites","Επιτρέψτε στους χρήστες να αποστέλλουν προσκλήσεις"}. +{"Allow users to send private messages","Επιτρέψτε στους χρήστες να αποστέλλουν ιδιωτικά μηνύματα"}. +{"Allow visitors to change nickname","Επιτρέψτε στους επισκέπτες να αλλάζου ψευδώνυμο"}. +{"Allow visitors to send private messages to","Επιτρέψτε στους χρήστες να αποστέλλουν ιδιωτικά μηνύματα σε"}. {"Allow visitors to send status text in presence updates","Επιτρέψτε στους επισκέπτες να αποστέλλουν κατάσταση στις ενημερώσεις παρουσίας"}. {"Allow visitors to send voice requests","Επιτρέψτε στους επισκέπτες να στέλνουν αιτήματα φωνής"}. -{"All Users","Όλοι οι χρήστες"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","Μία σχετική LDAP ομάδα που καθορίζει τις συνδρομές στην αίθουσα: αυτό θα πρέπει να είναι ένα Διακεκριμένο Όνομα LDAP, σύμφωνα με τον προσδιορισμό κατεύθυνσης υλοποίησης ή ανάπτυξης της ομάδας."}. {"Announcements","Ανακοινώσεις"}. -{"anyone","οποιοσδήποτε"}. -{"A password is required to enter this room","Απαιτείται κωδικός πρόσβασης για είσοδο σε αυτή την αίθουσα"}. +{"Answer associated with a picture","Η απάντηση σχετίστηκε με μια εικόνα"}. +{"Answer associated with a video","Η απάντηση σχετίστηκε με ένα βίντεο"}. +{"Answer associated with speech","Η απάντηση σχετίστηκε με ομιλία"}. +{"Answer to a question","Απάντηση σε ερώτηση"}. +{"Anyone in the specified roster group(s) may subscribe and retrieve items","Οποιοσδήποτε στις ορισθείσες Λίστες Ομάδων δύναται να εγγραφεί και να παραλάβει αντικείμενα"}. +{"Anyone may associate leaf nodes with the collection","Οποιοσδήποτε μπορεί να συσχετίσει κόμβους φύλλων με τη συλλογή"}. +{"Anyone may publish","Οποιοσδήποτε δύναται να δημοσιέυει"}. +{"Anyone may subscribe and retrieve items","Οποιοσδήποτε δύναται να εγγραφεί και να παραλάβει αντικείμενα"}. +{"Anyone with a presence subscription of both or from may subscribe and retrieve items","Όποιος έχει συνδρομή παρουσίας και των δύο ή από μπορεί να εγγραφεί και να ανακτήσει στοιχεία"}. +{"Anyone with Voice","Οποιοσδήποτε με Φωνή"}. +{"Anyone","Οποιοσδήποτε"}. +{"API Commands","Εντολές του API"}. {"April","Απρίλιος"}. +{"Arguments","Επιχειρήματα"}. +{"Attribute 'channel' is required for this request","Το δηλωτικό 'channel' απαιτείται για αυτό το Ερώτημα"}. +{"Attribute 'id' is mandatory for MIX messages","Το δηλωτικό 'id' επιτακτικό για μηνύματα MIX"}. +{"Attribute 'jid' is not allowed here","Το δηλωτικό 'jid' δεν επιτρέπεται εδώ"}. +{"Attribute 'node' is not allowed here","Το δηλωτικό 'node' δεν επιτρέπεται εδώ"}. +{"Attribute 'to' of stanza that triggered challenge","Το δηλωτικό 'to' του Δωματίου που ξεκίνησε την πρόκληση"}. {"August","Αύγουστος"}. +{"Automatic node creation is not enabled","Η αυτόματη δημιουργία κόμβων δεν είναι ενεργοποιημένη"}. {"Backup Management","Διαχείριση Αντιγράφου Ασφαλείας"}. +{"Backup of ~p","Αντιγράφο Ασφαλείας του ~p"}. {"Backup to File at ","Αποθήκευση Αντιγράφου Ασφαλείας σε Αρχείο στο "}. {"Backup","Αποθήκευση Αντιγράφου Ασφαλείας"}. {"Bad format","Ακατάλληλη μορφή"}. {"Birthday","Γενέθλια"}. -{"CAPTCHA web page","Ιστοσελίδα CAPTCHA "}. -{"Change Password","Αλλαγή κωδικού"}. -{"Change User Password","Αλλαγή Κωδικού Πρόσβασης Χρήστη"}. -{"Characters not allowed:","Χαρακτήρες δεν επιτρέπονται:"}. -{"Chatroom configuration modified","Διαμόρφωση Αίθουσaς σύνεδριασης τροποποιηθηκε"}. -{"Chatroom is created","Η αίθουσα σύνεδριασης δημιουργήθηκε"}. +{"Both the username and the resource are required","Τόσο το όνομα χρήστη όσο και ο πόρος είναι απαραίτητα"}. +{"Bytestream already activated","Το Bytestream έχει ήδη ενεργοποιηθεί"}. +{"Cannot remove active list","Δεν είναι δυνατή η κατάργηση της ενεργής λίστας"}. +{"Cannot remove default list","Δεν μπορείτε να καταργήσετε την προεπιλεγμένη λίστα"}. +{"CAPTCHA web page","Ιστοσελίδα CAPTCHA"}. +{"Challenge ID","Ταυτότητα Πρόκλησης"}. +{"Change Password","Αλλαγή κωδικού πρόσβασης"}. +{"Change User Password","Αλλαγή κωδικού πρόσβασης χρήστη"}. +{"Changing password is not allowed","Η αλλαγή του κωδικού πρόσβασης δεν επιτρέπεται"}. +{"Changing role/affiliation is not allowed","Η αλλαγή ρόλου/ομάδας δεν επιτρέπεται"}. +{"Channel already exists","Το κανάλι υπάρχει ήδη"}. +{"Channel does not exist","Το κανάλι δεν υπάρχει"}. +{"Channel JID","JID καναλιού"}. +{"Channels","Κανάλια"}. +{"Characters not allowed:","Χαρακτήρες που δεν επιτρέπονται:"}. +{"Chatroom configuration modified","Η ρύθμιση παραμέτρων της αίθουσας σύνεδριασης τροποποιηθηκε"}. +{"Chatroom is created","Η αίθουσα συνομιλίας έχει δημιουργηθεί"}. {"Chatroom is destroyed","Η αίθουσα σύνεδριασης διαγράφηκε"}. {"Chatroom is started","Η αίθουσα σύνεδριασης έχει ξεκινήσει"}. {"Chatroom is stopped","Η αίθουσα σύνεδριασης έχει σταματήσει"}. {"Chatrooms","Αίθουσες σύνεδριασης"}. {"Choose a username and password to register with this server","Επιλέξτε ένα όνομα χρήστη και κωδικό πρόσβασης για να εγγραφείτε σε αυτό τον διακομιστή"}. -{"Choose modules to stop","Επιλέξτε modules για να σταματήσουν"}. {"Choose storage type of tables","Επιλέξτε τύπο αποθήκευσης των πινάκων"}. {"Choose whether to approve this entity's subscription.","Επιλέξτε αν θα εγκρίθεί η εγγραφή αυτής της οντότητας."}. {"City","Πόλη"}. +{"Client acknowledged more stanzas than sent by server","Ο πελάτης γνωρίζει περισσότερα δωμάτια από αυτά που στάλθηκαν από τον εξυπηρετητή"}. +{"Clustering","Συσταδοποίηση"}. {"Commands","Εντολές"}. -{"Conference room does not exist","Αίθουσα σύνεδριασης δεν υπάρχει"}. -{"Configuration of room ~s","Διαμόρφωση Αίθουσας σύνεδριασης ~s"}. -{"Configuration","Διαμόρφωση"}. -{"Connected Resources:","Συνδεδεμένοι Πόροι:"}. -{"Connections parameters","Παράμετροι Συνδέσης"}. +{"Conference room does not exist","Η αίθουσα σύνεδριασης δεν υπάρχει"}. +{"Configuration of room ~s","Διαμόρφωση δωματίου ~ s"}. +{"Configuration","Ρύθμιση παραμέτρων"}. +{"Contact Addresses (normally, room owner or owners)","Διευθύνσεις της Επαφής (κανονικά, ιδιοκτήτης (-ες) αίθουσας)"}. {"Country","Χώρα"}. -{"CPU Time:","Ώρα CPU:"}. +{"Current Discussion Topic","Τρέχων θέμα συζήτησης"}. +{"Database failure","Αποτυχία βάσης δεδομένων"}. {"Database Tables Configuration at ","Διαμόρφωση Πίνακων βάσης δεδομένων στο "}. {"Database","Βάση δεδομένων"}. {"December","Δεκέμβριος"}. -{"Default users as participants","Προεπιλογη χρήστων ως συμμετέχοντες"}. +{"Default users as participants","Προρυθμισμένοι χρήστες ως συμμετέχοντες"}. {"Delete message of the day on all hosts","Διαγράψτε το μήνυμα της ημέρας σε όλους τους κεντρικούς υπολογιστές"}. {"Delete message of the day","Διαγράψτε το μήνυμα της ημέρας"}. -{"Delete Selected","Διαγραφή επιλεγμένων"}. {"Delete User","Διαγραφή Χρήστη"}. -{"Deliver event notifications","Κοινοποιήσεις παράδοσης"}. -{"Deliver payloads with event notifications","Κοινοποιήσεις με την παράδοση φορτίων"}. -{"Description:","Περιγραφή:"}. +{"Deliver event notifications","Παράδοση ειδοποιήσεων συμβάντων"}. +{"Deliver payloads with event notifications","Κοινοποίηση φόρτου εργασιών με τις ειδοποιήσεις συμβάντων"}. {"Disc only copy","Αντίγραφο μόνο σε δίσκο"}. -{"Displayed Groups:","Εμφανίσμενες Ομάδες:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","Μην πείτε τον κωδικό πρόσβασής σας σε κανέναν, ακόμη και στους διαχειριστές του διακομιστή Jabber."}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","Μην πείτε τον κωδικό πρόσβασής σας σε κανέναν, ούτε στους διαχειριστές του διακομιστή XMPP."}. {"Dump Backup to Text File at ","Αποθήκευση Αντιγράφου Ασφαλείας σε αρχείο κειμένου στο "}. {"Dump to Text File","Αποθήκευση σε αρχείο κειμένου"}. +{"Duplicated groups are not allowed by RFC6121","Δεν επιτρέπονται διπλότυπες ομάδες από το RFC6121"}. +{"Dynamically specify a replyto of the item publisher","Δυναμικά προσδιορίστε το Απάντηση σε του εκδότη του αντικειμένου"}. {"Edit Properties","Επεξεργασία ιδιοτήτων"}. {"Either approve or decline the voice request.","Είτε εγκρίνετε ή απορρίψτε το αίτημα φωνής."}. -{"ejabberd IRC module","ejabberd IRC module"}. -{"ejabberd MUC module","ejabberd MUC module"}. +{"ejabberd HTTP Upload service","Υπηρεσία ανεβάσματος αρχείων του ejabberd"}. +{"ejabberd MUC module","ενότητα ejabberd MUC"}. +{"ejabberd Multicast service","υπηρεσία ejabberd Multicast"}. {"ejabberd Publish-Subscribe module","ejabberd module Δημοσίευσης-Εγγραφής"}. -{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams module"}. +{"ejabberd SOCKS5 Bytestreams module","ενότητα ejabberd SOCKS5 Bytestreams"}. {"ejabberd vCard module","ejabberd vCard module"}. {"ejabberd Web Admin","ejabberd Web Admin"}. -{"Elements","Στοιχεία"}. -{"Email","Email"}. +{"ejabberd","ejabberd"}. +{"Email Address","Ηλεκτρονική Διεύθυνση"}. +{"Email","Ηλεκτρονικό ταχυδρομείο"}. +{"Enable hats","Ενεργοποίηση καπέλων"}. {"Enable logging","Ενεργοποίηση καταγραφής"}. -{"Encoding for server ~b","Κωδικοποίηση για διακομιστή ~b"}. +{"Enable message archiving","Ενεργοποιήστε την αρχειοθέτηση μηνυμάτων"}. +{"Enabling push without 'node' attribute is not supported","Η ενεργοποίηση της ώθησης χωρίς το χαρακτηριστικό 'κόμβος' δεν υποστηρίζεται"}. {"End User Session","Τερματισμός Συνεδρίας Χρήστη"}. -{"Enter list of {Module, [Options]}","Εισάγετε κατάλογο των (Module, [Επιλογές])"}. -{"Enter nickname you want to register","Πληκτρολογήστε το ψευδώνυμο που θέλετε να εγγραφείτε"}. +{"Enter nickname you want to register","Πληκτρολογήστε το ψευδώνυμο που θέλετε να καταχωρήσετε"}. {"Enter path to backup file","Εισάγετε τοποθεσία αρχείου αντιγράφου ασφαλείας"}. {"Enter path to jabberd14 spool dir","Εισάγετε κατάλογο αρχείων σειράς jabberd14"}. {"Enter path to jabberd14 spool file","Εισάγετε τοποθεσία αρχείου σειράς jabberd14"}. {"Enter path to text file","Εισάγετε Τοποθεσία Αρχείου Κειμένου"}. {"Enter the text you see","Πληκτρολογήστε το κείμενο που βλέπετε"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Πληκτρολογήστε το όνομα χρήστη και κωδικοποιήσεις που θέλετε να χρησιμοποιήσετε για τη σύνδεση με διακομιστές IRC. Πατήστε 'Next' για να πάρετε περισσότερα πεδία να συμπληρώσετε. Πατήστε 'Complete' για να αποθηκεύσετε ρυθμίσεις."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Εισάγετε το όνομα χρήστη, κωδικοποιήσεις, τις θύρες και τους κωδικούς πρόσβασης που θέλετε να χρησιμοποιήσετε για σύνδεση με IRC διακομιστή"}. -{"Erlang Jabber Server","Erlang Jabber Διακομιστής"}. -{"Error","Σφάλμα"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Παράδειγμα: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. -{"Exclude Jabber IDs from CAPTCHA challenge","Εξαίρεση από τις ταυτότητες Jabber, ή CAPTCHA πρόκληση"}. +{"Erlang XMPP Server","Διακομιστής Erlang XMPP"}. +{"Exclude Jabber IDs from CAPTCHA challenge","Εξαίρεσε αυτές τις ταυτότητες Jabber από την CAPTCHA πρόκληση"}. +{"Export all tables as SQL queries to a file:","Εξαγωγή όλων των πινάκων ως ερωτημάτων SQL σε ένα αρχείο:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Εξαγωγή δεδομένων όλων των χρηστών του διακομιστή σε PIEFXIS αρχεία (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Εξαγωγή δεδομένων των χρηστών κεντρικού υπολογιστή σε PIEFXIS αρχεία (XEP-0227):"}. +{"External component failure","Βλάβη εξωτερικού στοιχείου"}. +{"External component timeout","Τέλος χρονικού όριου εξωτερικού στοιχείου"}. +{"Failed to activate bytestream","Απέτυχε η ενεργοποίηση του bytestream"}. {"Failed to extract JID from your voice request approval","Απέτυχε η εξαγωγή JID από την έγκριση του αιτήματος φωνής σας"}. +{"Failed to map delegated namespace to external component","Αποτυχία ταξιθέτησης μεταγεγραμμένου χώρου ονομάτων σε εξωτερικό στοιχείο"}. +{"Failed to parse HTTP response","Αποτυχία ανάλυσης της απόκρισης HTTP"}. +{"Failed to process option '~s'","Αποτυχία επεξεργασίας της επιλογής '~s'"}. {"Family Name","Επώνυμο"}. +{"FAQ Entry","Καταχώριση συχνών ερωτήσεων"}. {"February","Φεβρουάριος"}. -{"Fill in fields to search for any matching Jabber User","Συμπληρώστε τα πεδία για να αναζητήσετε οποιαδήποτε ταιριάζοντα Jabber χρήστη"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Συμπληρώστε τη φόρμα για να αναζητήσετε οποιαδήποτε Jabber χρήστη που ταιριάζει (Προσθέστε * στο τέλος τού πεδίου για να ταιριάξει σε μεγαλύτερες γραμματοσηρές)"}. +{"File larger than ~w bytes","Αρχείο μεγαλύτερο από ~w bytes"}. +{"Fill in the form to search for any matching XMPP User","Συμπληρώστε την φόρμα για αναζήτηση χρηστών XMPP"}. {"Friday","Παρασκευή"}. -{"From ~s","Από ~s"}. -{"From","Από"}. +{"From ~ts","Από ~ts"}. +{"Full List of Room Admins","Πλήρης Κατάλογος Διαχειριστών αιθουσών"}. +{"Full List of Room Owners","Πλήρης Κατάλογος Ιδιοκτητών αιθουσών"}. {"Full Name","Ονοματεπώνυμο"}. +{"Get List of Online Users","Λίστα online χρηστών"}. +{"Get List of Registered Users","Λίστα εγγεγραμμένων χρηστών"}. {"Get Number of Online Users","Έκθεση αριθμού συνδεδεμένων χρηστών"}. {"Get Number of Registered Users","Έκθεση αριθμού εγγεγραμμένων χρηστών"}. +{"Get Pending","Λήψη των εκκρεμοτήτων"}. {"Get User Last Login Time","Έκθεση Τελευταίας Ώρας Σύνδεσης Χρήστη"}. -{"Get User Password","Έκθεση Κωδικού Πρόσβασης Χρήστη"}. {"Get User Statistics","Έκθεση Στατιστικών Χρήστη"}. +{"Given Name","Όνομα"}. {"Grant voice to this person?","Παραχώρηση φωνής σε αυτό το άτομο;"}. -{"Groups","Ομάδες"}. -{"Group ","Ομάδα"}. -{"has been banned","έχει απαγορευθεί"}. -{"has been kicked because of an affiliation change","Έχει αποβληθεί λόγω αλλαγής υπαγωγής"}. +{"has been banned","έχει αποβληθεί διαπαντώς"}. {"has been kicked because of a system shutdown","αποβλήθηκε λόγω τερματισμού συστήματος"}. +{"has been kicked because of an affiliation change","έχει αποβληθεί λόγω αλλαγής υπαγωγής"}. {"has been kicked because the room has been changed to members-only","αποβλήθηκε επειδή η αίθουσα αλλάξε γιά μέλη μόνο"}. -{"has been kicked","αποβλήθηκε "}. -{" has set the subject to: "," έχει θέσει το θέμα σε: "}. -{"Host","Κεντρικός Υπολογιστής"}. +{"has been kicked","αποβλήθηκε"}. +{"Hash of the vCard-temp avatar of this room","Hash του vCard-temp avatar αυτού του δωματίου"}. +{"Hat title","Τίτλος καπέλου"}. +{"Hat URI","Καπέλο URI"}. +{"Hats limit exceeded","Υπέρβαση του ορίου καπέλων"}. +{"Host unknown","Άγνωστος εξυπηρετητής"}. +{"HTTP File Upload","Ανέβασμα αρχείου"}. +{"Idle connection","Αδρανής σύνδεση"}. {"If you don't see the CAPTCHA image here, visit the web page.","Εάν δεν βλέπετε την εικόνα CAPTCHA εδώ, επισκεφθείτε την ιστοσελίδα."}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Εάν θέλετε να καθορίσετε διαφορετικές θύρες, κωδικούς πρόσβασης, κωδικοποιήσεις για IRC διακομιστές, εισάγετε πληροφορίες στη μορφή '{\"irc διακομιστής\", \"κωδικοποιήσεις\", θύρα, \"κωδικός πρόσβασης\"}'. Προεπιλεγμενα αυτή η υπηρεσία χρησιμοποιεί \"~s\" κωδικοποιήση, θύρα ~p, κενό κωδικό πρόσβασης."}. {"Import Directory","Εισαγωγή κατάλογου αρχείων"}. {"Import File","Εισαγωγή αρχείων"}. {"Import user data from jabberd14 spool file:","Εισαγωγή δεδομένων χρήστη από το αρχείο σειράς jabberd14:"}. @@ -136,275 +187,444 @@ {"Import users data from jabberd14 spool directory:","Εισαγωγή δεδομένων χρηστών από κατάλογο αρχείων σειράς jabberd14:"}. {"Import Users from Dir at ","Εισαγωγή χρηστών από κατάλογο αρχείων στο "}. {"Import Users From jabberd14 Spool Files","Εισαγωγή Χρηστών από αρχεία σειράς jabberd14"}. +{"Improper domain part of 'from' attribute","Ανάρμοστο τμήμα τομέα του χαρακτηριστικού 'from'"}. {"Improper message type","Ακατάλληλο είδος μηνύματος"}. +{"Incorrect CAPTCHA submit","Λάθος υποβολή CAPTCHA"}. +{"Incorrect data form","Εσφαλμένη φόρμα δεδομένων"}. {"Incorrect password","Εσφαλμένος κωδικός πρόσβασης"}. -{"Invalid affiliation: ~s","Άκυρη υπαγωγή: ~s"}. -{"Invalid role: ~s","Άκυρος ρόλο: ~s"}. +{"Incorrect value of 'action' attribute","Λανθασμένη τιμή του χαρακτηριστικού 'action'"}. +{"Incorrect value of 'action' in data form","Λανθασμένη τιμή 'action' στη φόρμα δεδομένων"}. +{"Incorrect value of 'path' in data form","Λανθασμένη τιμή 'path' στη φόρμα δεδομένων"}. +{"Installed Modules:","Εγκατεστημένες ενότητες:"}. +{"Install","Εγκατάσταση"}. +{"Insufficient privilege","Ανεπαρκή προνόμια"}. +{"Internal server error","Εσωτερικό σφάλμα"}. +{"Invalid 'from' attribute in forwarded message","Μη έγκυρο χαρακτηριστικό 'από' στο προωθούμενο μήνυμα"}. +{"Invalid node name","Μη έγκυρο όνομα κόμβου"}. +{"Invalid 'previd' value","Μη έγκυρη τιμή 'previd'"}. +{"Invitations are not allowed in this conference","Οι προσκλήσεις δεν επιτρέπονται σε αυτή τη διάσκεψη"}. {"IP addresses","Διευθύνσεις IP"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","IRC κανάλι (μην τεθεί το πρώτο #)"}. -{"IRC server","Διακομιστής IRC"}. -{"IRC settings","IRC Ρυθμίσεις"}. -{"IRC Transport","IRC Διαβιβάσεις"}. -{"IRC username","IRC όνομα χρήστη"}. -{"IRC Username","IRC Όνομα χρήστη"}. {"is now known as","είναι τώρα γνωστή ως"}. -{"It is not allowed to send private messages of type \"groupchat\"","Δεν επιτρέπεται να στείλει προσωπικά μηνύματα του τύπου \"groupchat\""}. +{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Δεν επιτρέπεται η αποστολή μηνυμάτων σφάλματος στο δωμάτιο. Ο συμμετέχων (~s) έχει στείλει ένα μήνυμα σφάλματος (~s) και έχει πεταχτεί έξω από την αίθουσα"}. +{"It is not allowed to send private messages of type \"groupchat\"","Δεν επιτρέπεται η αποστολή προσωπικών μηνυμάτων του τύπου \"groupchat\""}. {"It is not allowed to send private messages to the conference","Δεν επιτρέπεται να στείλει προσωπικά μηνύματα για τη διάσκεψη"}. -{"It is not allowed to send private messages","Δεν επιτρέπεται η αποστολή προσωπικών μηνυμάτων"}. -{"Jabber Account Registration","Εγγραφή λογαριασμού Jabber"}. -{"Jabber ID ~s is invalid","Η Jabber Ταυτότητα ~s είναι άκυρη"}. {"Jabber ID","Ταυτότητα Jabber"}. {"January","Ιανουάριος"}. -{"Join IRC channel","Είσοδος στο IRC κανάλι"}. -{"joins the room","συνδέετε στην αίθουσα"}. -{"Join the IRC channel here.","Είσοδος στο κανάλι IRC εδώ."}. -{"Join the IRC channel in this Jabber ID: ~s","Είσοδος στο κανάλι IRC αυτής της Jabber Ταυτότητας: ~s"}. +{"JID normalization denied by service policy","Απετράπη η κανονικοποίηση του JID, λόγω της τακτικής Παροχής Υπηρεσιών"}. +{"JID normalization failed","Απετράπη η κανονικοποίηση του JID"}. +{"Joined MIX channels of ~ts","Ενσωματωμένα κανάλια MIX του ~ts"}. +{"Joined MIX channels:","Ενσωματωμένα κανάλια MIX:"}. +{"joins the room","συνδέεται στην αίθουσα"}. {"July","Ιούλιος"}. {"June","Ιούνιος"}. +{"Just created","Μόλις δημιουργήθηκε"}. {"Last Activity","Τελευταία Δραστηριότητα"}. {"Last login","Τελευταία σύνδεση"}. +{"Last message","Τελευταίο μήνυμα"}. {"Last month","Περασμένο μήνα"}. {"Last year","Πέρυσι"}. +{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Τα ψηφία μικρότερης αξίας του αθροίσματος SHA-256 του κειμένου θα έπρεπε να ισούνται με την δεκαεξαδική ετικέτα"}. {"leaves the room","εγκαταλείπει την αίθουσα"}. -{"Listened Ports at ","Παρακολουθούμενες Θύρες στο "}. -{"Listened Ports","Παρακολουθούμενες Θύρες"}. -{"List of modules to start","Λίστα των Module για Εκκίνηση"}. -{"Low level update script","Προγράμα ενημέρωσης χαμηλού επίπεδου "}. -{"Make participants list public","Κάντε κοινό τον κατάλογο συμμετεχόντων"}. -{"Make room CAPTCHA protected","Κάντε την αίθουσα CAPTCHA προστατεύονομενη"}. +{"List of users with hats","Λίστα των χρηστών με καπέλα"}. +{"List users with hats","Λίστα χρηστών με καπέλα"}. +{"Logged Out","Αποσυνδεδεμένος"}. +{"Logging","Καταγραφή"}. +{"Make participants list public","Κάντε δημόσιο τον κατάλογο συμμετεχόντων"}. +{"Make room CAPTCHA protected","Κάντε την αίθουσα προστατεύομενη με CAPTCHA"}. {"Make room members-only","Κάντε την αίθουσα μόνο για μέλη"}. -{"Make room moderated","Κάντε την αίθουσα εποπτεύονομενη"}. +{"Make room moderated","Κάντε την αίθουσα εποπτεύομενη"}. {"Make room password protected","Κάντε την αίθουσα προστατεύομενη με κωδικό πρόσβασης"}. -{"Make room persistent","Κάντε αίθουσα μόνιμη"}. +{"Make room persistent","Κάντε την αίθουσα μόνιμη"}. {"Make room public searchable","Κάντε την δημόσια αναζήτηση δυνατή για αυτή την αίθουσα"}. +{"Malformed username","Λανθασμένη μορφή ονόματος χρήστη"}. +{"MAM preference modification denied by service policy","Άρνηση αλλαγής προτιμήσεων MAM, λόγω της τακτικής Παροχής Υπηρεσιών"}. {"March","Μάρτιος"}. -{"Maximum Number of Occupants","Μέγιστος αριθμός συμετεχόντων"}. -{"Max # of items to persist","Μέγιστος αριθμός μόνιμων στοιχείων"}. +{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","Μέγιστος αριθμός στοιχείων που πρέπει να παραμείνουν, ή « Μέγιστο » για κανένα συγκεκριμένο όριο εκτός από το μέγιστο που επιβάλλει ο διακομιστής"}. {"Max payload size in bytes","Μέγιστο μέγεθος φορτίου σε bytes"}. +{"Maximum file size","Μέγιστο μέγεθος αρχείου"}. +{"Maximum Number of History Messages Returned by Room","Μέγιστος αριθμός μηνυμάτων Ιστορικού που επιστρέφονται από την Αίθουσα"}. +{"Maximum number of items to persist","Μέγιστος αριθμός μόνιμων στοιχείων"}. +{"Maximum Number of Occupants","Μέγιστος αριθμός συμμετεχόντων"}. {"May","Μάιος"}. {"Membership is required to enter this room","Απαιτείται αίτηση συμετοχής για είσοδο σε αυτή την αίθουσα"}. -{"Members:","Μέλη:"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Απομνημονεύστε τον κωδικό πρόσβασής σας, ή γράψετε τον σε ένα χαρτί που είχε τοποθετηθεί σε ασφαλές μέρος. Στο Jabber δεν υπάρχει αυτοματοποιημένος τρόπος για να ανακτήσετε τον κωδικό σας αν τον ξεχάσετε."}. -{"Memory","Μνήμη"}. -{"Message body","Περιεχόμενο μηνυμάτως"}. +{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Απομνημονεύστε τον κωδικό πρόσβασής σας ή γράψτε τον σε χαρτί που βρίσκεται σε ασφαλές μέρος. Στο XMPP δεν υπάρχει αυτοματοποιημένος τρόπος ανάκτησης του κωδικού πρόσβασής σας εάν τον ξεχάσετε."}. +{"Mere Availability in XMPP (No Show Value)","Διαθεσιμότητα στο XMPP (Χωρίς ένδειξη)"}. +{"Message body","Περιεχόμενο μηνύματος"}. +{"Message not found in forwarded payload","Δεν βρέθηκε μήνυμα στον προωθημένο φόρτο εργασίας"}. +{"Messages from strangers are rejected","Τα μηνύματα από αγνώστους απορρίπτονται"}. +{"Messages of type headline","Μηνύματα του τύπου headline"}. +{"Messages of type normal","Μηνύματα του τύπου normal"}. {"Middle Name","Πατρώνυμο"}. {"Minimum interval between voice requests (in seconds)","Ελάχιστο χρονικό διάστημα μεταξύ αιτημάτων φωνής (σε δευτερόλεπτα)"}. -{"Moderator privileges required","Aπαιτούνται προνόμια συντονιστή"}. -{"moderators only","συντονιστές μόνο"}. -{"Modified modules","Τροποποιημένα modules"}. -{"Module","Module"}. -{"Modules","Modules"}. +{"Moderator privileges required","Aπαιτούνται προνόμια επόπτου"}. +{"Moderators Only","Επόπτες μόμον"}. +{"Moderator","Επόπτης"}. +{"Module failed to handle the query","Το module απέτυχε να χειριστεί το ερώτημα"}. {"Monday","Δευτέρα"}. -{"Name:","Όνομα:"}. +{"Multicast","Πολλαπλή διανομή (Multicast)"}. +{"Multiple elements are not allowed by RFC6121","Πολλαπλά στοιχεία δεν επιτρέπονται από το RFC6121"}. +{"Multi-User Chat","Συνομιλία με πολλούς χρήστες"}. {"Name","Όνομα"}. +{"Natural Language for Room Discussions","Μητρική Γλώσσα για τις Συζητήσεις Αιθουσών"}. +{"Natural-Language Room Name","Αίθουσα Μητρικής Γλώσσας"}. +{"Neither 'jid' nor 'nick' attribute found","Δεν βρέθηκε κανένα χαρακτηριστικό 'jid' ούτε 'nick'"}. +{"Neither 'role' nor 'affiliation' attribute found","Δεν βρέθηκε ούτε χαρακτηριστικό 'role' ούτε 'affiliation'"}. {"Never","Ποτέ"}. {"New Password:","Νέος κωδικός πρόσβασης:"}. +{"Nickname can't be empty","Το Ψευδώνυμο δεν μπορεί να είναι άδειο"}. {"Nickname Registration at ","Εγγραφή με Ψευδώνυμο στο "}. {"Nickname ~s does not exist in the room","Ψευδώνυμο ~s δεν υπάρχει σε αυτή την αίθουσα"}. {"Nickname","Ψευδώνυμο"}. -{"No body provided for announce message","Δεν προμηθεύτικε περιεχόμενο ανακοινώσης"}. -{"nobody","κανείς"}. +{"No address elements found","Δεν βρέθηκαν στοιχεία διεύθυνσης"}. +{"No addresses element found","Δεν βρέθηκε στοιχείο διεύθυνσης"}. +{"No 'affiliation' attribute found","Δεν βρέθηκε το χαρακτηριστικό 'affiliation'"}. +{"No available resource found","Δεν βρέθηκε διαθέσιμος πόρος"}. +{"No body provided for announce message","Δεν προμηθεύτηκε περιεχόμενο ανακοινώσης"}. +{"No child elements found","Δεν βρέθηκαν θυγατρικά στοιχεία"}. +{"No data form found","Δεν βρέθηκε φόρμα δεδομένων"}. {"No Data","Κανένα στοιχείο"}. -{"Node ID","Ταυτότητα Κόμβου"}. -{"Node not found","Κόμβος δεν βρέθηκε"}. -{"Nodes","Κόμβοι"}. +{"No features available","Δεν υπάρχουν διαθέσιμες λειτουργίες"}. +{"No element found","Δεν βρέθηκε στοιχείο "}. +{"No hook has processed this command","Κανένα άγκιστρο δεν έχει επεξεργαστεί αυτήν την εντολή"}. +{"No info about last activity found","Δεν βρέθηκαν πληροφορίες για την τελευταία δραστηριότητα"}. +{"No 'item' element found","Δεν βρέθηκε το στοιχείο 'item'"}. +{"No items found in this query","Δεν βρέθηκαν στοιχεία σε αυτό το ερώτημα"}. {"No limit","Χωρίς όριο"}. +{"No module is handling this query","Κανένα module δεν χειρίζεται αυτό το ερώτημα"}. +{"No node specified","Δεν καθορίστηκε κόμβος"}. +{"No 'password' found in data form","Δεν υπάρχει 'password' στη φόρμα δεδομένων"}. +{"No 'password' found in this query","Δεν βρέθηκε 'password' σε αυτό το ερώτημα"}. +{"No 'path' found in data form","Δεν υπάρχει 'path' στη φόρμα δεδομένων"}. +{"No pending subscriptions found","Δεν βρέθηκαν εκκρεμείς συνδρομές"}. +{"No privacy list with this name found","Δεν βρέθηκε κατάλογος απορρήτου με αυτό το όνομα"}. +{"No private data found in this query","Δεν βρέθηκαν ιδιωτικά δεδομένα σε αυτό το ερώτημα"}. +{"No running node found","Δεν βρέθηκε ενεργός κόμβος"}. +{"No services available","Δεν υπάρχουν διαθέσιμες υπηρεσίες"}. +{"No statistics found for this item","Δεν βρέθηκαν στατιστικά στοιχεία για αυτό το στοιχείο"}. +{"No 'to' attribute found in the invitation","Δε βρέθηκε το χαρακτηριστικό 'to' στην πρόσκληση"}. +{"Nobody","Κανείς"}. +{"Node already exists","Ο κόμβος υπάρχει ήδη"}. +{"Node ID","Ταυτότητα Κόμβου"}. +{"Node index not found","Ο δείκτης κόμβου δεν βρέθηκε"}. +{"Node not found","Κόμβος δεν βρέθηκε"}. +{"Node ~p","Κόμβος ~p"}. +{"Nodeprep has failed","Το Nodeprep απέτυχε"}. +{"Nodes","Κόμβοι"}. +{"Node","Κόμβος"}. {"None","Κανένα"}. -{"No resource provided","Δεν προμηθεύτικε πόρος"}. -{"Not Found","Δεν Βρέθηκε"}. -{"Notify subscribers when items are removed from the node","Ειδοποιηση στους συνδρομητές όταν αφαίρούντε στοιχεία από τον κόμβο"}. -{"Notify subscribers when the node configuration changes","Ειδοποιηση στους συνδρομητές όταν αλλάζει η διαμόρφωση κόμβου"}. -{"Notify subscribers when the node is deleted","Ειδοποιηση στους συνδρομητές όταν ο κόμβος διαγράφεται"}. +{"Not allowed","Δεν επιτρέπεται"}. +{"Not Found","Δε βρέθηκε"}. +{"Not subscribed","Δεν έχετε εγγραφεί"}. +{"Notify subscribers when items are removed from the node","Ειδοποιήστε τους συνδρομητές όταν αφαιρούνται στοιχεία από τον κόμβο"}. +{"Notify subscribers when the node configuration changes","Ειδοποίηση στους συνδρομητές όταν αλλάζει η διαμόρφωση κόμβου"}. +{"Notify subscribers when the node is deleted","Ειδοποίηση στους συνδρομητές όταν ο κόμβος διαγράφεται"}. {"November","Νοέμβριος"}. +{"Number of answers required","Πλήθος αναζητημένων ερωτημάτων"}. {"Number of occupants","Αριθμός συμετεχόντων"}. +{"Number of Offline Messages","Πλήθος μηνυμάτων Χωρίς Σύνδεση"}. {"Number of online users","Αριθμός συνδεδεμένων χρηστών"}. {"Number of registered users","Αριθμός εγγεγραμμένων χρηστών"}. +{"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","Αριθμός δευτερολέπτων μετά από τα οποία θα καθαρίζονται αυτόματα τα στοιχεία, ή `max` για κανένα συγκεκριμένο όριο εκτός από το μέγιστο που επιβάλλει ο διακομιστής"}. +{"Occupants are allowed to invite others","Οι συμμετέχοντες μπορούν να προσκαλέσουν και άλλους"}. +{"Occupants are allowed to query others","Οι κάτοικοι επιτρέπεται να ρωτούν άλλους"}. +{"Occupants May Change the Subject","Επιτρέψτε στους χρήστες να αλλάζουν το Θέμα"}. {"October","Οκτώβριος"}. -{"Offline Messages:","Χωρίς Σύνδεση Μηνύματα:"}. -{"Offline Messages","Χωρίς Σύνδεση Μηνύματα"}. -{"OK","Όλλα Καλά"}. +{"OK","Εντάξει"}. {"Old Password:","Παλαιός κωδικός πρόσβασης:"}. -{"Online Users:","Online Χρήστες:"}. {"Online Users","Συνδεμένοι χρήστες"}. {"Online","Συνδεδεμένο"}. -{"Only deliver notifications to available users","Παράδωση κοινοποιήσεων μόνο σε διαθέσιμους χρήστες"}. +{"Only collection node owners may associate leaf nodes with the collection","Μόνον οι ιδιοκτήτες των κόμβων μπορούν να συσχετίσουν leaf nodes με την Συλλογή"}. +{"Only deliver notifications to available users","Παράδοση ειδοποιήσεων μόνο σε διαθέσιμους χρήστες"}. +{"Only or tags are allowed","Επιτρέπονται μόνο tags ή "}. +{"Only element is allowed in this query","Στο ερώτημα αυτό επιτρέπεται μόνο το στοιχείο "}. +{"Only members may query archives of this room","Μόνο μέλη μπορούν να δούνε τα αρχεία αυτής της αίθουσας"}. {"Only moderators and participants are allowed to change the subject in this room","Μόνο οι συντονιστές και οι συμμετέχοντες μπορούν να αλλάξουν το θέμα αυτής της αίθουσας"}. {"Only moderators are allowed to change the subject in this room","Μόνο οι συντονιστές μπορούν να αλλάξουν το θέμα αυτής της αίθουσας"}. +{"Only moderators are allowed to retract messages","Μόνο οι συντονιστές επιτρέπεται να αποσύρουν μηνύματα"}. {"Only moderators can approve voice requests","Μόνο οι συντονιστές μπορούν να εγκρίνουν τις αιτήσεις φωνής"}. -{"Only occupants are allowed to send messages to the conference","Μόνο οι συμμετέχωντες μπορούν να στέλνουν μηνύματα στο συνέδριο"}. -{"Only occupants are allowed to send queries to the conference","Μόνο οι συμετεχόντες μπορούν να στείλουν ερωτήματα στη διάσκεψη"}. +{"Only occupants are allowed to send messages to the conference","Μόνο οι συμμετέχοντες επιτρέπεται να στέλνουν μηνύματα στο συνέδριο"}. +{"Only occupants are allowed to send queries to the conference","Μόνο οι συμμετέχοντες επιτρέπεται να στείλουν ερωτήματα στη διάσκεψη"}. +{"Only publishers may publish","Μόνον εκδότες μπορούν να δημοσιεύσουν"}. {"Only service administrators are allowed to send service messages","Μόνο οι διαχειριστές των υπηρεσιών επιτρέπεται να στείλουν υπηρεσιακά μηνύματα"}. -{"Options","Επιλογές"}. +{"Only those on a whitelist may associate leaf nodes with the collection","Μόνον οι εξαιρεθέντες μπορούν να συσχετίσουν leaf nodes με τη συλλογή"}. +{"Only those on a whitelist may subscribe and retrieve items","Μόνο όσοι βρίσκονται στη λίστα επιτρεπόμενων μπορούν να εγγραφούν και να ανακτήσουν αντικείμενα"}. {"Organization Name","Όνομα Οργανισμού"}. {"Organization Unit","Μονάδα Οργανισμού"}. -{"Outgoing s2s Connections:","Εξερχόμενες S2S Συνδέσεις:"}. +{"Other Modules Available:","Διαθέσιμες άλλες ενότητες:"}. {"Outgoing s2s Connections","Εξερχόμενες S2S Συνδέσεις"}. {"Owner privileges required","Aπαιτούνται προνόμια ιδιοκτήτη"}. -{"Packet","Πακέτο"}. -{"Password ~b","Κωδικός πρόσβασης ~b"}. -{"Password Verification:","Επαλήθευση κωδικού πρόσβασης:"}. +{"Packet relay is denied by service policy","Απαγορεύεται η αναμετάδοση πακέτων, λόγω της τακτικής Παροχής Υπηρεσιών"}. +{"Participant ID","ID συμμετέχοντος"}. +{"Participant","Συμμετέχων"}. {"Password Verification","Επαλήθευση κωδικού πρόσβασης"}. -{"Password","Κωδικός Πρόσβασης"}. +{"Password Verification:","Επαλήθευση κωδικού πρόσβασης:"}. +{"Password","Κωδικός πρόσβασης"}. {"Password:","Κωδικός πρόσβασης:"}. {"Path to Dir","Τοποθεσία κατάλογου αρχείων"}. {"Path to File","Τοποθεσία Αρχείου"}. -{"Pending","Εκκρεμεί"}. +{"Payload semantic type information","Πληροφορίες σημασιολογικού τύπου ωφέλιμου φορτίου"}. {"Period: ","Περίοδος: "}. -{"Persist items to storage","Μονιμη αποθήκευση στοιχείων"}. -{"Ping","Πινγκ"}. -{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Παρακαλώ σημειώστε ότι οι επιλογές αυτές θα αποθήκευσουν Αντιγράφο Ασφαλείας μόνο της ενσωματωμένης βάσης δεδομένων Mnesia. Εάν χρησιμοποιείτε το module ODBC, θα πρέπει επίσης να κάνετε χωριστά Αντιγράφο Ασφαλείας της SQL βάση δεδομένων σας ."}. +{"Persist items to storage","Μόνιμη αποθήκευση στοιχείων"}. +{"Persistent","Μόνιμη"}. +{"Ping query is incorrect","Το Ping είναι λανθασμένο"}. +{"Ping","Ping"}. +{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Παρακαλώ σημειώστε ότι οι επιλογές αυτές θα αποθήκευσουν Αντιγράφο Ασφαλείας μόνο της ενσωματωμένης βάσης δεδομένων Mnesia. Εάν χρησιμοποιείτε το module ODBC, θα πρέπει επίσης να κάνετε χωριστά Αντιγράφο Ασφαλείας της SQL βάσης δεδομένων σας."}. {"Please, wait for a while before sending new voice request","Παρακαλώ, περιμένετε για λίγο πριν την αποστολή νέου αιτήματος φωνής"}. {"Pong","Πονγκ"}. -{"Port ~b","Θύρα ~b"}. -{"Port","Θύρα"}. +{"Possessing 'ask' attribute is not allowed by RFC6121","Η κατοχή χαρακτηριστικού \"ask\" δεν επιτρέπεται από το RFC6121"}. {"Present real Jabber IDs to","Παρούσιαση πραγματικών ταυτοτήτων Jabber σε"}. -{"private, ","ιδιωτικό,"}. -{"Protocol","Πρωτόκολλο"}. +{"Previous session not found","Η προηγούμενη περίοδος σύνδεσης χρήστη δεν βρέθηκε"}. +{"Previous session PID has been killed","Το προηγούμενο αναγνωριστικό περιόδου σύνδεσης PID αφαιρέθηκε"}. +{"Previous session PID has exited","Το προηγούμενο αναγνωριστικό περιόδου σύνδεσης PID τερμάτισε"}. +{"Previous session PID is dead","Το προηγούμενο αναγνωριστικό περιόδου σύνδεσης PID είναι νεκρό"}. +{"Previous session timed out","Η προηγούμενη περίοδος σύνδεσης χρήστη έληξε"}. +{"private, ","ιδιωτικό, "}. +{"Public","Δημόσιο"}. +{"Publish model","Δημοσιεύστε μοντέλο"}. {"Publish-Subscribe","Δημοσίευση-Εγγραφή"}. {"PubSub subscriber request","Αίτηση συνδρομητή Δημοσίευσης-Εγγραφής"}. {"Purge all items when the relevant publisher goes offline","Διαγραφή όλων των στοιχείων όταν ο σχετικός εκδότης αποσυνδέεται"}. -{"Queries to the conference members are not allowed in this room","Ερωτήματα πρώς τα μέλη της διασκέψεως δεν επιτρέπονται σε αυτήν την αίθουσα"}. +{"Push record not found","Το αρχείο Ώσης δεν βρέθηκε"}. +{"Queries to the conference members are not allowed in this room","Ερωτήματα προς τα μέλη της διασκέψεως δεν επιτρέπονται σε αυτήν την αίθουσα"}. +{"Query to another users is forbidden","Το ερώτημα σε άλλους χρήστες είναι απαγορευμένο"}. {"RAM and disc copy","Αντίγραφο μόνο σε RAM καί δίσκο"}. {"RAM copy","Αντίγραφο σε RAM"}. -{"Raw","Ακατέργαστο"}. -{"Really delete message of the day?","Πραγματικά να διαγράψετε το μήνυμα της ημέρας;"}. -{"Recipient is not in the conference room","Παραλήπτης δεν είναι στην αίθουσα συνεδριάσεων"}. -{"Register a Jabber account","Καταχωρήστε έναν λογαριασμό Jabber"}. -{"Registered Users:","Εγγεγραμμένοι Χρήστες:"}. -{"Registered Users","Εγγεγραμμένοι Χρήστες"}. +{"Really delete message of the day?","Πραγματικά να διαγραφεί το μήνυμα της ημέρας;"}. +{"Receive notification from all descendent nodes","Λάβετε ειδοποίηση από όλους τους υπό-κόμβους"}. +{"Receive notification from direct child nodes only","Λάβετε ειδοποίηση μόνο από direct child κόμβους"}. +{"Receive notification of new items only","Λάβετε ειδοποίηση μόνο από νέα αντικείμενα"}. +{"Receive notification of new nodes only","Λάβετε ειδοποίηση μόνο από νέους κόμβους"}. +{"Recipient is not in the conference room","Ο παραλήπτης δεν είναι στην αίθουσα συνεδριάσεων"}. +{"Register an XMPP account","Καταχωρείστε έναν XMPP λογαριασμό χρήστη"}. {"Register","Καταχωρήστε"}. -{"Registration in mod_irc for ","Εγγραφή στο mod_irc για "}. -{"Remote copy","Απομεμακρυσμένο αντίγραφο"}. -{"Remove All Offline Messages","Αφαίρεση Όλων των Χωρίς Σύνδεση Μηνύματων"}. +{"Remote copy","Εξ αποστάσεως αντίγραφο"}. +{"Remove a hat from a user","Αφαίρεση ενός καπέλου από έναν χρήστη"}. {"Remove User","Αφαίρεση χρήστη"}. -{"Remove","Αφαίρεστε"}. -{"Replaced by new connection","Αντικαταστάθικε από νέα σύνδεση"}. +{"Replaced by new connection","Αντικαταστάθηκε από μια νέα σύνδεση"}. +{"Request has timed out","Το αίτημα έληξε"}. +{"Request is ignored","Το αίτημα θα αγνοηθεί"}. +{"Requested role","Αιτούμενος ρόλος"}. {"Resources","Πόροι"}. {"Restart Service","Επανεκκίνηση Υπηρεσίας"}. -{"Restart","Επανεκκίνηση"}. {"Restore Backup from File at ","Επαναφορά Αντιγράφου Ασφαλείας από αρχείο στο "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Επαναφορά δυαδικού αντιγράφου ασφαλείας μετά την επόμενη επανεκκίνηση του ejabberd (απαιτεί λιγότερη μνήμη):"}. {"Restore binary backup immediately:","Επαναφορά δυαδικού αντιγράφου ασφαλείας αμέσως:"}. {"Restore plain text backup immediately:","Επαναφορά αντιγράφου ασφαλείας από αρχείο κειμένου αμέσως:"}. {"Restore","Επαναφορά Αντιγράφου Ασφαλείας"}. +{"Result","Αποτέλεσμα"}. +{"Roles and Affiliations that May Retrieve Member List","Ρόλοι και δεσμοί που μπορούν να λάβουν την λίστα μελών"}. +{"Roles for which Presence is Broadcasted","Ρόλοι των οποίων η παρουσία δηλώνεται δημόσια"}. +{"Roles that May Send Private Messages","Ρόλοι που επιτρέπεται να αποστέλλουν ιδιωτικά μηνύματα"}. {"Room Configuration","Διαμόρφωση Αίθουσας σύνεδριασης"}. -{"Room creation is denied by service policy","Άρνηση δημιουργίας αίθουσας , λόγω τακτικής παροχής υπηρεσιών"}. -{"Room description","Περιγραφή Αίθουσας"}. -{"Room Occupants","Συμετεχόντες Αίθουσας σύνεδριασης"}. -{"Room title","Τίτλος Αίθουσας "}. +{"Room creation is denied by service policy","Άρνηση δημιουργίας αίθουσας, λόγω της τακτικής Παροχής Υπηρεσιών"}. +{"Room description","Περιγραφή αίθουσας"}. +{"Room Occupants","Συμμετεχόντες Αίθουσας σύνεδριασης"}. +{"Room terminates","Τερματισμός Αίθουσας"}. +{"Room title","Τίτλος Αίθουσας"}. {"Roster groups allowed to subscribe","Ομάδες Καταλόγου Επαφών μπορούν να εγγραφούν"}. -{"Roster of ","Καταλόγος Επαφών τού"}. {"Roster size","Μέγεθος Καταλόγου Επαφών"}. -{"Roster","Καταλόγος Επαφών"}. -{"RPC Call Error","Σφάλμα RPC Κλήσης"}. {"Running Nodes","Ενεργοί Κόμβοι"}. -{"~s access rule configuration","~s διαμόρφωση κανόνα πρόσβασης"}. +{"~s invites you to the room ~s","~s Σας καλεί στο δωμάτιο ~s"}. {"Saturday","Σάββατο"}. -{"Script check","Script ελέγχου"}. +{"Search from the date","Αναζήτηση από της"}. {"Search Results for ","Αποτελέσματα αναζήτησης για "}. -{"Search users in ","Αναζήτηση χρηστών στο"}. +{"Search the text","Αναζήτηση του κειμένου"}. +{"Search until the date","Αναζήτηση μέχρι της"}. +{"Search users in ","Αναζήτηση χρηστών στο "}. {"Send announcement to all online users on all hosts","Αποστολή ανακοίνωσης σε όλους τους συνδεδεμένους χρήστες σε όλους τους κεντρικούς υπολογιστές"}. {"Send announcement to all online users","Αποστολή ανακοίνωσης σε όλους τους συνδεδεμένους χρήστες"}. {"Send announcement to all users on all hosts","Αποστολή ανακοίνωσης σε όλους τους χρήστες σε όλους τους κεντρικούς υπολογιστές"}. {"Send announcement to all users","Αποστολή ανακοίνωσης σε όλους τους χρήστες"}. {"September","Σεπτέμβριος"}. -{"Server ~b","Διακομιστής ~b"}. {"Server:","Διακομιστής:"}. +{"Service list retrieval timed out","Η λήψη της λίστας Υπηρεσιών έληξε"}. +{"Session state copying timed out","Η αντιγραφή της καταστασης περιόδου σύνδεσης έληξε"}. {"Set message of the day and send to online users","Ορίστε μήνυμα ημέρας και αποστολή στους συνδεδεμένους χρήστες"}. {"Set message of the day on all hosts and send to online users","Ορίστε μήνυμα ημέρας και άμεση αποστολή στους συνδεδεμένους χρήστες σε όλους τους κεντρικούς υπολογιστές"}. {"Shared Roster Groups","Κοινές Ομάδες Καταλόγων Επαφών"}. {"Show Integral Table","Δείτε Ολοκληρωτικό Πίνακα"}. +{"Show Occupants Join/Leave","Εμφάνιση ενοίκων Join/Leave"}. {"Show Ordinary Table","Δείτε Κοινό Πίνακα"}. -{"Shut Down Service","Κλείσιμο Υπηρεσίας"}. -{"~s invites you to the room ~s","~s σας προσκαλεί στην αίθουσα ~s"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Μερικοί πελάτες Jabber μπορεί να αποθηκεύσουν τον κωδικό πρόσβασής σας στον υπολογιστή σας. Χρησιμοποιήστε αυτό το χαρακτηριστικό μόνο εάν εμπιστεύεστε την ασφάλεια του υπολογιστή σας."}. +{"Shut Down Service","Τερματισμός Υπηρεσίας"}. +{"SOCKS5 Bytestreams","Bytestreams του SOCKS5"}. +{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Ορισμένοι πελάτες XMPP μπορούν να αποθηκεύσουν τον κωδικό πρόσβασής σας στον υπολογιστή, αλλά θα πρέπει να το κάνετε μόνο στον προσωπικό σας υπολογιστή για λόγους ασφαλείας."}. +{"Sources Specs:","Πηγές Προδιαγραφές:"}. {"Specify the access model","Καθορίστε το μοντέλο πρόσβασης"}. {"Specify the event message type","Καθορίστε τον τύπο μηνύματος συμβάντος"}. {"Specify the publisher model","Καθορίστε το μοντέλο εκδότη"}. -{"~s's Offline Messages Queue","Η Σειρά Χωρίς Σύνδεση Μηνύματων τού ~s"}. -{"Start Modules at ","Εκκίνηση Modules στο "}. -{"Start Modules","Εκκίνηση Modules"}. -{"Start","Εκκίνηση"}. -{"Statistics of ~p","Στατιστικές του ~p"}. -{"Statistics","Στατιστικές"}. -{"Stop Modules at ","Παύση Modules στο "}. -{"Stop Modules","ΠαύσηModules"}. +{"Stanza id is not valid","Το Stanza id δεν είναι έγκυρο"}. +{"Stanza ID","Ταυτότητα Δωματίου"}. +{"Statically specify a replyto of the node owner(s)","Προσδιορίστε (στατικά) το Απάντηση Προς του ιδιοκτήτη-ων του κόμβου"}. {"Stopped Nodes","Σταματημένοι Κόμβοι"}. -{"Stop","Σταμάτημα"}. -{"Storage Type","Τύπος Αποθήκευσης"}. {"Store binary backup:","Αποθηκεύση δυαδικού αντιγράφου ασφαλείας:"}. {"Store plain text backup:","Αποθηκεύση αντιγράφου ασφαλείας σε αρχείο κειμένου:"}. +{"Stream management is already enabled","Η διαχείριση Ροών επιτρέπεται ηδη"}. +{"Stream management is not enabled","Η διαχείριση Ροών δεν είναι ενεργοποιημένη"}. {"Subject","Θέμα"}. {"Submitted","Υποβλήθηκε"}. -{"Submit","Υποβοβολή"}. {"Subscriber Address","Διεύθυνση Συνδρομητή"}. -{"Subscription","Συνδρομή"}. +{"Subscribers may publish","Οι συνδρομητές μπορούν να δημοσιεύσουν"}. +{"Subscription requests must be approved and only subscribers may retrieve items","Τα αιτήματα για συνδρομή πρέπει να εγκριθούν και μόνο οι συνδρομητές μπορούν να λάβουν αντικείμενα"}. +{"Subscriptions are not allowed","Οι συνδρομές δεν επιτρέπονται"}. {"Sunday","Κυριακή"}. -{"That nickname is already in use by another occupant","Αυτό το ψευδώνυμο είναι ήδη σε χρήση από άλλον συμμετέχων"}. +{"Text associated with a picture","Το κείμενο σχετίστηκε με μία εικόνα"}. +{"Text associated with a sound","Το κείμενο σχετίστηκε με έναν ήχο"}. +{"Text associated with a video","Το κείμενο σχετίστηκε με ένα βίντεο"}. +{"Text associated with speech","Το κείμενο σχετίστηκε με ομιλία"}. +{"That nickname is already in use by another occupant","Αυτό το ψευδώνυμο είναι ήδη σε χρήση από άλλον συμμετέχοντα"}. {"That nickname is registered by another person","Αυτό το ψευδώνυμο είναι καταχωρημένο από άλλο πρόσωπο"}. +{"The account already exists","Ο λογαριασμός υπάρχει ήδη"}. +{"The account was not unregistered","Ο λογαριασμός δεν καταχωρήθηκε"}. +{"The body text of the last received message","Ο κορμός του κειμένου του τελευταίου μηνύματος"}. {"The CAPTCHA is valid.","Το CAPTCHA είναι έγκυρο."}. {"The CAPTCHA verification has failed","Η επαλήθευση της εικόνας CAPTCHA απέτυχε"}. -{"The collections with which a node is affiliated","Οι συλλογές με την οποία είναι ένας κόμβος συνδέεται"}. -{"The password is too weak","Ο κωδικός πρόσβασης είναι πολύ ασθενές"}. +{"The captcha you entered is wrong","Το captcha που εισαγάγατε είναι λάθος"}. +{"The child nodes (leaf or collection) associated with a collection","Οι θυγατρικοί κόμβοι (leaf ή collection) που σχετίζονται με μια συλλογή"}. +{"The collections with which a node is affiliated","Οι συλλογές με τις οποίες ένας κόμβος σχετίζεται"}. +{"The DateTime at which a leased subscription will end or has ended","Ο Χρόνος στον οποίο μια μισθωμένη συνδρομή θα Εκπνεύσει ή Τελειώσει"}. +{"The datetime when the node was created","Η χρονοσφραγίδα δημιουργίας του κόμβου"}. +{"The default language of the node","Η προρυθμισμένη γλώσσα του κόμβου"}. +{"The feature requested is not supported by the conference","Η λειτουργία που ζητήθηκε δεν υποστηρίζεται από τη διάσκεψη"}. +{"The JID of the node creator","Το JID του δημιουγού του κόμβου"}. +{"The JIDs of those to contact with questions","Το JID αυτών με τους οποίους θα επικοινωνήσετε με ερωτήσεις"}. +{"The JIDs of those with an affiliation of owner","Το JID αυτών που σχετίζονται με τον ιδιοκτήτη"}. +{"The JIDs of those with an affiliation of publisher","Το JID αυτών που σχετίζονται με τον εκδότη"}. +{"The list of all online users","Ο κατάλογος όλων των online χρηστών"}. +{"The list of all users","Ο κατάλογος όλων των χρηστών"}. +{"The list of JIDs that may associate leaf nodes with a collection","Λίστα των JIDs που μπορούν να σχετίζουν leaf κόμβους με μια Συλλογή"}. +{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","Ο μέγιστος αριθμός των παιδικών κόμβων που μπορούν να συσχετιστούν με μια συλλογή, ή `max` για κανένα συγκεκριμένο όριο εκτός από το μέγιστο που επιβάλλει ο διακομιστής"}. +{"The minimum number of milliseconds between sending any two notification digests","Το ελάχιστο πλήθος χιλιοστών του δευτερολέπτου μεταξύ της αποστολής δύο συγχωνεύσεων ειδοποιήσεων"}. +{"The name of the node","Το όνομα του κόμβου"}. +{"The node is a collection node","Ο κόμβος είναι κόμβος Συλλογής"}. +{"The node is a leaf node (default)","Ο κόμβος είναι leaf κόμβος (προεπιλογή)"}. +{"The NodeID of the relevant node","Το NodeID του σχετικού κόμβου"}. +{"The number of pending incoming presence subscription requests","Το πλήθος των αιτημάτων εισερχομένων συνδρομών παρουσίας σε αναμονή"}. +{"The number of subscribers to the node","Το πλήθος των συνδρομητών στον κόμβο"}. +{"The number of unread or undelivered messages","Το πλήθος των μη αναγνωσμένων ή μη παραδοτέων μηνυμάτων"}. +{"The password contains unacceptable characters","Ο κωδικός πρόσβασης περιέχει μη αποδεκτούς χαρακτήρες"}. +{"The password is too weak","Ο κωδικός πρόσβασης είναι πολύ αδύναμος"}. {"the password is","ο κωδικός πρόσβασης είναι"}. -{"The password of your Jabber account was successfully changed.","Ο κωδικός πρόσβασης του Jabber λογαριασμού σας έχει αλλάξει επιτυχώς."}. -{"There was an error changing the password: ","Υπήρξε ένα σφάλμα κατά την αλλαγή του κωδικού πρόσβασης:"}. -{"There was an error creating the account: ","Υπήρξε ένα σφάλμα κατά τη δημιουργία του λογαριασμού:"}. -{"There was an error deleting the account: ","Υπήρξε ένα σφάλμα κατά τη διαγραφή του λογαριασμού:"}. -{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Ανεξαρτήτως με πεζά ή κεφαλαία: 'μιαλεξη' είναι το ίδιο με 'ΜιαΛέξη' και 'Μιαλέξη'."}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Αυτή η σελίδα σας επιτρέπει να δημιουργήσετε ένα λογαριασμό Jabber σε αυτόν το διακομιστή Jabber. JID σας (Jabber Identifier) θα είναι της μορφής: όνομα_χρήστη@διακομιστής_Jabber. Παρακαλώ διαβάστε προσεκτικά τις οδηγίες για να συμπληρώσετε σωστά τα πεδία."}. -{"This page allows to unregister a Jabber account in this Jabber server.","Η σελίδα αυτή δίνει τη δυνατότητα να καταργήσετε την καταχώρηση ενός λογαριασμό Jabber σε αυτόν το διακομιστή Jabber."}. +{"The password of your XMPP account was successfully changed.","Ο κωδικός πρόσβασης του XMPP λογαριασμού σας έχει αλλάξει επιτυχώς."}. +{"The password was not changed","Ο κωδικός πρόσβασης δεν άλλαξε"}. +{"The passwords are different","Οι κωδικοί πρόσβασης δεν ταιριάζουν"}. +{"The presence states for which an entity wants to receive notifications","Η παρουσία δηλώνει για ποιους θέλει κάποιος να λαμβάνει ειδοποιήσεις"}. +{"The query is only allowed from local users","Το ερώτημα επιτρέπεται μόνο από τοπικούς χρήστες"}. +{"The query must not contain elements","Το ερώτημα δεν πρέπει να περιέχει στοιχείο "}. +{"The room subject can be modified by participants","Το θέμα μπορεί να τροποποιηθεί από τους συμμετέχοντες"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","Οι πληροφορίες σημασιολογικού τύπου των δεδομένων στον κόμβο, που συνήθως καθορίζονται από το χώρο ονομάτων του ωφέλιμου φορτίου (εάν υπάρχει)"}. +{"The sender of the last received message","Ο αποστολέας του τελευταίου εισερχομένου μηνύματος"}. +{"The stanza MUST contain only one element, one element, or one element","Η stanza ΠΡΕΠΕΙ να περιέχει μόνο ένα στοιχείο , ένα στοιχείο ή ένα στοιχείο "}. +{"The subscription identifier associated with the subscription request","Το αναγνωριστικό συνδρομής συσχετίστηκε με το αίτημα συνδρομής"}. +{"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","Το URL ενός μετασχηματισμού XSL το οποίο μπορεί να εφαρμοστεί σε φόρτους εργασίας για να παραχθεί το κατάλληλο στοιχείο του σώματος του μηνύματος."}. +{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","Το URL ενός μετασχηματισμού XSL, το οποίο μπορεί να εφαρμοστεί στους τύπους φόρτου εργασίας για να παραχθεί έγκυρο αποτέλεσμα Data Forms, τέτοιο που ο πελάτης μπορεί να εμφανίσει, χρησιμοποιώντας μια ευρείας χρήσης μηχανή επεξεργασίας Data Forms"}. +{"There was an error changing the password: ","Παρουσιάστηκε σφάλμα κατά την αλλαγή του κωδικού πρόσβασης: "}. +{"There was an error creating the account: ","Υπήρξε ένα σφάλμα κατά τη δημιουργία του λογαριασμού: "}. +{"There was an error deleting the account: ","Υπήρξε ένα σφάλμα κατά τη διαγραφή του λογαριασμού: "}. +{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Αυτό σημαίνει ότι δεν έχει σημασία αν είναι κεφαλαία ή πεζά γράμματα: \"κατσαντώνης\" είναι το ίδιο με \"ΚατσΑντώνης\" , όπως και \"Κατσαντώνης\"."}. +{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Αυτή η σελίδα επιτρέπει την εγγραφή ενός λογαριασμού XMPP σε αυτόν τον διακομιστή XMPP. Το JID (Jabber ID) θα έχει τη μορφή: όνομαχρήστη@διακομιστής. Διαβάστε προσεκτικά τις οδηγίες για να συμπληρώσετε σωστά τα πεδία."}. +{"This page allows to unregister an XMPP account in this XMPP server.","Αυτή η σελίδα επιτρέπει την κατάργηση εγγραφής ενός λογαριασμού XMPP σε αυτόν τον διακομιστή XMPP."}. +{"This room is not anonymous","Η αίθουσα αυτή δεν είναι ανώνυμη"}. +{"This service can not process the address: ~s","Αυτή η υπηρεσία δεν μπορεί να επεξεργαστεί την διεύθυνση: ~s"}. {"Thursday","Πέμπτη"}. {"Time delay","Χρόνος καθυστέρησης"}. -{"Time","Χρόνος"}. +{"Timed out waiting for stream resumption","Υπερέβην το όριο αναμονής για επανασύνδεση της Ροής"}. +{"To register, visit ~s","Για να εγγραφείτε, επισκεφθείτε το ~s"}. +{"To ~ts","Προς ~ts"}. +{"Token TTL","Διακριτικό TTL"}. +{"Too many active bytestreams","Πάρα πολλά ενεργά bytestreams"}. {"Too many CAPTCHA requests","Πάρα πολλά αιτήματα CAPTCHA"}. -{"To ~s","Πρώς ~s"}. -{"To","Πρώς"}. +{"Too many child elements","Πάρα πολλά θυγατρικά στοιχεία"}. +{"Too many elements","Πάρα πολλά στοιχεία "}. +{"Too many elements","Πάρα πολλά στοιχεία "}. +{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Πάρα πολλές (~p) αποτυχημένες προσπάθειες σύνδεσης από την IP σας (~s). Άρση φραγής στις ~s UTC"}. +{"Too many receiver fields were specified","Πάρα πολλά πεδία δεκτών προσδιορίστηκαν"}. +{"Too many unacked stanzas","Πάρα πολλές μη αναγνωρισμένες stanzas"}. +{"Too many users in this conference","Πάρα πολλοί χρήστες σε αυτή τη διάσκεψη"}. {"Traffic rate limit is exceeded","Υπέρφορτωση"}. -{"Transactions Aborted:","Αποτυχημένες συναλλαγές:"}. -{"Transactions Committed:","Παραδοθείς συναλλαγές:"}. -{"Transactions Logged:","Καταγραμμένες συναλλαγές:"}. -{"Transactions Restarted:","Επανειλημμένες συναλλαγές:"}. +{"~ts's MAM Archive","Αρχείο MAM του ~ts"}. +{"~ts's Offline Messages Queue","~ts's Χωρίς Σύνδεση Μηνύματα"}. {"Tuesday","Τρίτη"}. -{"Unable to generate a CAPTCHA","Αδήνατο να δημιουργηθεί CAPTCHA"}. -{"Unauthorized","Χορίς Εξουσιοδότηση"}. -{"Unregister a Jabber account","Καταργήστε την εγγραφή ενός λογαριασμού Jabber"}. +{"Unable to generate a CAPTCHA","Αδύνατη η δημιουργία CAPTCHA"}. +{"Unable to register route on existing local domain","Δεν είναι δυνατή η καταχώρηση της διαδρομής σε υπάρχοντα τοπικό τομέα"}. +{"Unauthorized","Χωρίς Εξουσιοδότηση"}. +{"Unexpected action","Απροσδόκητη ενέργεια"}. +{"Unexpected error condition: ~p","Απροσδόκητες συνθήκες σφάλματος: ~p"}. +{"Uninstall","Απεγκατάσταση"}. +{"Unregister an XMPP account","Καταργήση λογαριασμού XMPP"}. {"Unregister","Καταργήση εγγραφής"}. -{"Update message of the day (don't send)","Ενημέρωση μηνύματως ημέρας (χωρίς άμεση αποστολή)"}. -{"Update message of the day on all hosts (don't send)","Ενημέρωση μηνύματως ημέρας σε όλους τους κεντρικούς υπολογιστές (χωρίς άμεση αποστολή)"}. -{"Update plan","Σχέδιο ενημέρωσης"}. -{"Update script","Προγράμα ενημέρωσης"}. -{"Update","Ενημέρωση"}. -{"Uptime:","Uptime:"}. -{"Use of STARTTLS required","Απαιτείται χρήση STARTTLS "}. +{"Unsupported element","Μη υποστηριζόμενο στοιχείο "}. +{"Unsupported version","Μη υποστηριζόμενη έκδοση"}. +{"Update message of the day (don't send)","Ενημέρωση μηνύματος ημέρας (χωρίς άμεση αποστολή)"}. +{"Update message of the day on all hosts (don't send)","Ενημέρωση μηνύματος ημέρας σε όλους τους κεντρικούς υπολογιστές (χωρίς άμεση αποστολή)"}. +{"Update specs to get modules source, then install desired ones.","Ενημερώστε τις προδιαγραφές για να λάβετε την πηγή των ενοτήτων και, στη συνέχεια, εγκαταστήστε τις επιθυμητές."}. +{"Update Specs","Προδιαγραφές ενημέρωσης"}. +{"Updating the vCard is not supported by the vCard storage backend","Η ενημέρωση της vCard δεν υποστηρίζεται από το backend αποθήκευσης vCard"}. +{"Upgrade","Αναβάθμιση"}. +{"URL for Archived Discussion Logs","URL αρχειοθετημένων καταγραφών συζητήσεων"}. +{"User already exists","Ο χρήστης υπάρχει ήδη"}. {"User JID","JID Χρήστη"}. +{"User (jid)","Χρήστης (jid)"}. {"User Management","Διαχείριση χρηστών"}. -{"Username:","Όνομα χρήστη"}. -{"Users are not allowed to register accounts so quickly","Οι χρήστες δεν επιτρέπεται να εγγραφούν λογαριασμούς τόσο γρήγορα"}. +{"User not allowed to perform an IQ set on another user's vCard.","Ο χρήστης δεν επιτρέπεται να εκτελέσει ένα σετ IQ στην vCard ενός άλλου χρήστη."}. +{"User removed","Ο Χρήστης αφαιρέθηκε"}. +{"User session not found","Η περίοδος σύνδεσης χρήστη δεν βρέθηκε"}. +{"User session terminated","Η περίοδος σύνδεσης χρήστη τερματίστηκε"}. +{"User ~ts","Χρήστης ~ts"}. +{"Username:","Όνομα χρήστη:"}. +{"Users are not allowed to register accounts so quickly","Οι χρήστες δεν επιτρέπεται να δημιουργούν λογαριασμούς τόσο γρήγορα"}. {"Users Last Activity","Τελευταία Δραστηριότητα Χρήστη"}. {"Users","Χρήστες"}. {"User","Χρήστης"}. -{"Validate","Επαληθεύστε"}. +{"Value 'get' of 'type' attribute is not allowed","Η τιμή 'get' του 'type' δεν επιτρέπεται"}. +{"Value of '~s' should be boolean","Η τιμή του '~s' πρέπει να είναι boolean"}. +{"Value of '~s' should be datetime string","Η τιμή του '~s' θα πρέπει να είναι χρονοσειρά"}. +{"Value of '~s' should be integer","Η τιμή του '~s' θα πρέπει να είναι ακέραιος"}. +{"Value 'set' of 'type' attribute is not allowed","Δεν επιτρέπεται η παράμετρος 'set' του 'type'"}. {"vCard User Search","vCard Αναζήτηση χρηστών"}. -{"Virtual Hosts","εικονικοί κεντρικοί υπολογιστές"}. +{"View joined MIX channels","Προβολή ενταγμένων καναλιών MIX"}. +{"Virtual Hosts","Eικονικοί κεντρικοί υπολογιστές"}. {"Visitors are not allowed to change their nicknames in this room","Οι επισκέπτες δεν επιτρέπεται να αλλάξουν τα ψευδώνυμα τους σε αυτή την αίθουσα"}. -{"Visitors are not allowed to send messages to all occupants","Οι επισκέπτες δεν επιτρέπεται να στείλουν μηνύματα σε όλους τους συμμετέχωντες"}. +{"Visitors are not allowed to send messages to all occupants","Οι επισκέπτες δεν επιτρέπεται να στείλουν μηνύματα σε όλους τους συμμετέχοντες"}. +{"Visitor","Επισκέπτης"}. {"Voice requests are disabled in this conference","Τα αιτήματα φωνής είναι απενεργοποιημένα, σε αυτό το συνέδριο"}. {"Voice request","Αίτημα φωνής"}. +{"Web client which allows to join the room anonymously","Web client που επιτρέπει την ανώνυμη είσοδο στην αίθουσα"}. {"Wednesday","Τετάρτη"}. -{"When to send the last published item","Πότε να αποσταλθεί το τελευταίο στοιχείο που δημοσιεύθηκε"}. -{"Whether to allow subscriptions","Εάν επιτρέποντε συνδρομές"}. -{"You can later change your password using a Jabber client.","Μπορείτε αργότερα να αλλάξετε τον κωδικό πρόσβασής σας χρησιμοποιώντας έναν πελάτη Jabber."}. +{"When a new subscription is processed and whenever a subscriber comes online","Όταν μία νέα συνδρομή βρίσκεται εν επεξεργασία και όποτε ένας συνδρομητής συνδεθεί"}. +{"When a new subscription is processed","Όταν μία νέα συνδρομή βρίσκεται εν επεξεργασία"}. +{"When to send the last published item","Πότε να αποσταλεί το τελευταίο στοιχείο που δημοσιεύθηκε"}. +{"Whether an entity wants to receive an XMPP message body in addition to the payload format","Εάν κάποιος θέλει να λάβει το κυρίως XMPP μήνυμα, επιπροσθέτως του τύπου φόρτου εργασιών"}. +{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","Εάν μία οντότητα επιθυμεί να λαμβάνει αθροιστικές συνόψεις των ειδοποιήσεων ή όλες τις ειδοποιήσεις ξεχωριστά"}. +{"Whether an entity wants to receive or disable notifications","Εάν μία οντότητα επιθυμεί να λαμβάνει ή όχι ειδοποιήσεις"}. +{"Whether owners or publisher should receive replies to items","Εάν οι ιδιοκτήτες επιθυμούν να λαμβάνουν απαντήσεις στα αντικείμενα"}. +{"Whether the node is a leaf (default) or a collection","Εάν ο κόμβος είναι leaf (προεπιλογή) ή συλλογή"}. +{"Whether to allow subscriptions","Εάν επιτρέπονται συνδρομές"}. +{"Whether to make all subscriptions temporary, based on subscriber presence","Αν επιτρέπεται να γίνουν όλες οι συνδρομές προσωρινές, βασιζόμενοι στην παρουσία του συνδρομητή"}. +{"Whether to notify owners about new subscribers and unsubscribes","Αν πρέπει να ειδοποιούνται οι ιδιοκτήτες για νέους συνδρομητές και αποχωρήσεις"}. +{"Who can send private messages","Ποιος μπορεί να στείλει ιδιωτικά μηνύματα"}. +{"Who may associate leaf nodes with a collection","Ποιός μπορεί να συσχετίζει leaf nodes με μία συλλογή"}. +{"Wrong parameters in the web formulary","Εσφαλμένες παράμετροι στην διαμόρφωση τυπικότητας του δυκτίου"}. +{"Wrong xmlns","Εσφαλμένο xmlns"}. +{"XMPP Account Registration","Εγγραφή λογαριασμού XMPP"}. +{"XMPP Domains","Ονόματα χώρου XMPP"}. +{"XMPP Show Value of Away","Δείξε τιμή XMPP Απεμακρύνθην"}. +{"XMPP Show Value of Chat","Δείξε τιμή XMPP Αξία Συνομιλίας"}. +{"XMPP Show Value of DND (Do Not Disturb)","Δείξε τιμή XMPP Αξία του Μην Ενοχλείτε"}. +{"XMPP Show Value of XA (Extended Away)","Δείξε τιμή XMPP Αξία του Λίαν Απομακρυσμένος"}. +{"XMPP URI of Associated Publish-Subscribe Node","XMPP URI του συσχετισμένου κόμβου Δημοσίευσης-Εγγραφής"}. +{"You are being removed from the room because of a system shutdown","Απαιτείται η απομάκρυνσή σας από την αίθουσα, λόγω τερματισμού συστήματος"}. +{"You are not allowed to send private messages","Δεν επιτρέπεται η αποστολή ιδιωτικών μηνυμάτων"}. +{"You are not joined to the channel","Δεν λαμβάνετε μέρος στο κανάλι"}. +{"You can later change your password using an XMPP client.","Μπορείτε αργότερα να αλλάξετε τον κωδικό πρόσβασής σας χρησιμοποιώντας ένα πρόγραμμα-πελάτη XMPP."}. {"You have been banned from this room","Σας έχει απαγορευθεί η είσοδος σε αυτή την αίθουσα"}. -{"You must fill in field \"Nickname\" in the form","Θα πρέπει να συμπληρώσετε το πεδίο \"Ψευδώνυμο\" στη φόρμα"}. -{"You need a client that supports x:data and CAPTCHA to register","Χρειάζεστε ένα x:data και CAPTCHA ικανό πελάτη για εγγραφή"}. -{"You need a client that supports x:data to register the nickname","Χρειάζεστε ένα x:data ικανό πελάτη για εγγραφή με ψευδώνυμο"}. -{"You need an x:data capable client to configure mod_irc settings","Χρειάζεστε ένα x:data ικανό πελάτη για να ρυθμίσετε το mod_irc"}. -{"You need an x:data capable client to configure room","Χρειάζεστε ένα x:data ικανό πελάτη για να ρυθμίσετε την αίθουσα "}. -{"You need an x:data capable client to search","Χρειάζεστε ένα x:data ικανό πελάτη για αναζήτηση"}. +{"You have joined too many conferences","Είσθε σε πάρα πολλά συνέδρια"}. +{"You must fill in field \"Nickname\" in the form","Απαιτείται να συμπληρώσετε το πεδίο \"Ψευδώνυμο\" στη φόρμα"}. +{"You need a client that supports x:data and CAPTCHA to register","Χρειάζεστε έναν πελάτη που να υποστηρίζει x:data και CAPTCHA"}. +{"You need a client that supports x:data to register the nickname","Χρειάζεστε έναν πελάτη που να υποστηρίζει x:data για εγγραφή του ψευδώνυμου"}. +{"You need an x:data capable client to search","Χρειάζεστε έναν πελάτη που να υποστηρίζει x:data για να αναζητήσετε"}. {"Your active privacy list has denied the routing of this stanza.","Ο ενεργός κατάλογος απορρήτου, έχει αρνηθεί τη δρομολόγηση αυτής της στροφής (stanza)."}. -{"Your contact offline message queue is full. The message has been discarded.","Η μνήμη χωρίς σύνδεση μήνυματών είναι πλήρης. Το μήνυμα έχει απορριφθεί."}. -{"Your Jabber account was successfully created.","Ο Jabber λογαριασμός σας δημιουργήθηκε με επιτυχία."}. -{"Your Jabber account was successfully deleted.","Ο Jabber λογαριασμός σας διαγράφηκε με επιτυχία."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","Τα μηνύματά σας πρως ~s είναι αποκλεισμένα. Για αποδεσμεύση, επισκεφθείτε ~s"}. +{"Your contact offline message queue is full. The message has been discarded.","Η μνήμη μηνυμάτων χωρίς σύνδεση είναι πλήρης. Το μήνυμα έχει απορριφθεί."}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Τα μηνύματά σας προς ~s είναι αποκλεισμένα. Για αποδεσμεύση, επισκεφθείτε ~s"}. +{"Your XMPP account was successfully registered.","Ο λογαριασμός σας XMPP καταχωρήθηκε με επιτυχία."}. +{"Your XMPP account was successfully unregistered.","Ο XMPP λογαριασμός σας διαγράφηκε με επιτυχία."}. +{"You're not allowed to create nodes","Δεν σας επιτρέπεται η δημιουργία κόμβων"}. diff --git a/priv/msgs/el.po b/priv/msgs/el.po deleted file mode 100644 index b97ac1d54..000000000 --- a/priv/msgs/el.po +++ /dev/null @@ -1,1967 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: el\n" -"POT-Creation-Date: \n" -"PO-Revision-Date: 2012-04-18 12:50-0000\n" -"Last-Translator: James Iakovos Mandelis \n" -"Language-Team: \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Greek (ελληνικά)\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "Απαιτείται χρήση STARTTLS " - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Δεν προμηθεύτικε πόρος" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Αντικαταστάθικε από νέα σύνδεση" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "αποβλήθηκε " - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "" -"Ο ενεργός κατάλογος απορρήτου, έχει αρνηθεί τη δρομολόγηση αυτής της στροφής " -"(stanza)." - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Πληκτρολογήστε το κείμενο που βλέπετε" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "" -"Τα μηνύματά σας πρως ~s είναι αποκλεισμένα. Για αποδεσμεύση, επισκεφθείτε ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "Εάν δεν βλέπετε την εικόνα CAPTCHA εδώ, επισκεφθείτε την ιστοσελίδα." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "Ιστοσελίδα CAPTCHA " - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "Το CAPTCHA είναι έγκυρο." - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Χρήστης" - -#: ejabberd_oauth.erl:256 -#, fuzzy -msgid "Server" -msgstr "Διακομιστής:" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Κωδικός Πρόσβασης" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "Χορίς Εξουσιοδότηση" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "ejabberd Web Admin" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Διαχείριση" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Λίστες Ελέγχου Πρόσβασης" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Υποβλήθηκε" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Ακατάλληλη μορφή" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Υποβοβολή" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "Ακατέργαστο" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Διαγραφή επιλεγμένων" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Κανόνες Πρόσβασης" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "~s διαμόρφωση κανόνα πρόσβασης" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "εικονικοί κεντρικοί υπολογιστές" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Χρήστες" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "Συνδεμένοι χρήστες" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Τελευταία Δραστηριότητα Χρήστη" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Περίοδος: " - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "Περασμένο μήνα" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "Πέρυσι" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "Όλες οι δραστηριότητες" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Δείτε Κοινό Πίνακα" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Δείτε Ολοκληρωτικό Πίνακα" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Στατιστικές" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "Δεν Βρέθηκε" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Κόμβος δεν βρέθηκε" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Προσθήκη νέου" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Κεντρικός Υπολογιστής" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Εγγεγραμμένοι Χρήστες" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Προσθήκη Χρήστη" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Χωρίς Σύνδεση Μηνύματα" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Τελευταία Δραστηριότητα" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Ποτέ" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Συνδεδεμένο" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Εγγεγραμμένοι Χρήστες:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Online Χρήστες:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Εξερχόμενες S2S Συνδέσεις:" - -#: ejabberd_web_admin.erl:1559 -#, fuzzy -msgid "Incoming s2s Connections:" -msgstr "Εξερχόμενες S2S Συνδέσεις:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Κανένα" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Αλλαγή κωδικού" - -#: ejabberd_web_admin.erl:1673 -#, fuzzy -msgid "User ~s" -msgstr "Χρήστης" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Συνδεδεμένοι Πόροι:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Κωδικός πρόσβασης:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Αφαίρεση χρήστη" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Κανένα στοιχείο" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Κόμβοι" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Ενεργοί Κόμβοι" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Σταματημένοι Κόμβοι" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -#, fuzzy -msgid "Node ~p" -msgstr "Κόμβος" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "Βάση δεδομένων" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Αποθήκευση Αντιγράφου Ασφαλείας" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Παρακολουθούμενες Θύρες" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Ενημέρωση" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Επανεκκίνηση" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Σταμάτημα" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Modules" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "Σφάλμα RPC Κλήσης" - -#: ejabberd_web_admin.erl:1917 -#, fuzzy -msgid "Database Tables at ~p" -msgstr "Πίνακες βάσης δεδομένων στο " - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Όνομα" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Τύπος Αποθήκευσης" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Στοιχεία" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Μνήμη" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Σφάλμα" - -#: ejabberd_web_admin.erl:1955 -#, fuzzy -msgid "Backup of ~p" -msgstr "Αντιγράφο Ασφαλείας του " - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Παρακαλώ σημειώστε ότι οι επιλογές αυτές θα αποθήκευσουν Αντιγράφο Ασφαλείας " -"μόνο της ενσωματωμένης βάσης δεδομένων Mnesia. Εάν χρησιμοποιείτε το module " -"ODBC, θα πρέπει επίσης να κάνετε χωριστά Αντιγράφο Ασφαλείας της SQL βάση " -"δεδομένων σας ." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Αποθηκεύση δυαδικού αντιγράφου ασφαλείας:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "Όλλα Καλά" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Επαναφορά δυαδικού αντιγράφου ασφαλείας αμέσως:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"Επαναφορά δυαδικού αντιγράφου ασφαλείας μετά την επόμενη επανεκκίνηση του " -"ejabberd (απαιτεί λιγότερη μνήμη):" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Αποθηκεύση αντιγράφου ασφαλείας σε αρχείο κειμένου:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Επαναφορά αντιγράφου ασφαλείας από αρχείο κειμένου αμέσως:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "Εισαγωγή δεδομένων χρηστών από ένα αρχείο PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" -"Εξαγωγή δεδομένων όλων των χρηστών του διακομιστή σε PIEFXIS αρχεία " -"(XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "" -"Εξαγωγή δεδομένων των χρηστών κεντρικού υπολογιστή σε PIEFXIS αρχεία " -"(XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Εισαγωγή δεδομένων χρήστη από το αρχείο σειράς jabberd14:" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Εισαγωγή δεδομένων χρηστών από κατάλογο αρχείων σειράς jabberd14:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Παρακολουθούμενες Θύρες στο " - -#: ejabberd_web_admin.erl:2144 -#, fuzzy -msgid "Modules at ~p" -msgstr "Modules στο " - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "Στατιστικές του ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Uptime:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "Ώρα CPU:" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Παραδοθείς συναλλαγές:" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Αποτυχημένες συναλλαγές:" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Επανειλημμένες συναλλαγές:" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Καταγραμμένες συναλλαγές:" - -#: ejabberd_web_admin.erl:2243 -#, fuzzy -msgid "Update ~p" -msgstr "Ενημέρωση" - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "Σχέδιο ενημέρωσης" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Τροποποιημένα modules" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Προγράμα ενημέρωσης" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Προγράμα ενημέρωσης χαμηλού επίπεδου " - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Script ελέγχου" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Θύρα" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Πρωτόκολλο" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Module" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Επιλογές" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Εκκίνηση" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Εντολές" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Πινγκ" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Πονγκ" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "Πραγματικά να διαγράψετε το μήνυμα της ημέρας;" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Θέμα" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Περιεχόμενο μηνυμάτως" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "Δεν προμηθεύτικε περιεχόμενο ανακοινώσης" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Ανακοινώσεις" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Αποστολή ανακοίνωσης σε όλους τους χρήστες" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "" -"Αποστολή ανακοίνωσης σε όλους τους χρήστες σε όλους τους κεντρικούς " -"υπολογιστές" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Αποστολή ανακοίνωσης σε όλους τους συνδεδεμένους χρήστες" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "" -"Αποστολή ανακοίνωσης σε όλους τους συνδεδεμένους χρήστες σε όλους τους " -"κεντρικούς υπολογιστές" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Ορίστε μήνυμα ημέρας και αποστολή στους συνδεδεμένους χρήστες" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" -"Ορίστε μήνυμα ημέρας και άμεση αποστολή στους συνδεδεμένους χρήστες σε όλους " -"τους κεντρικούς υπολογιστές" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Ενημέρωση μηνύματως ημέρας (χωρίς άμεση αποστολή)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "" -"Ενημέρωση μηνύματως ημέρας σε όλους τους κεντρικούς υπολογιστές (χωρίς άμεση " -"αποστολή)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Διαγράψτε το μήνυμα της ημέρας" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Διαγράψτε το μήνυμα της ημέρας σε όλους τους κεντρικούς υπολογιστές" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Διαμόρφωση" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Εκκίνηση Modules" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "ΠαύσηModules" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Επαναφορά Αντιγράφου Ασφαλείας" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Αποθήκευση σε αρχείο κειμένου" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Εισαγωγή αρχείων" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Εισαγωγή κατάλογου αρχείων" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Επανεκκίνηση Υπηρεσίας" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Κλείσιμο Υπηρεσίας" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Διαγραφή Χρήστη" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Τερματισμός Συνεδρίας Χρήστη" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Έκθεση Κωδικού Πρόσβασης Χρήστη" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Αλλαγή Κωδικού Πρόσβασης Χρήστη" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "Έκθεση Τελευταίας Ώρας Σύνδεσης Χρήστη" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Έκθεση Στατιστικών Χρήστη" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Έκθεση αριθμού εγγεγραμμένων χρηστών" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Έκθεση αριθμού συνδεδεμένων χρηστών" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Διαχείριση χρηστών" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Όλοι οι χρήστες" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Εξερχόμενες S2S Συνδέσεις" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Διαχείριση Αντιγράφου Ασφαλείας" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Εισαγωγή Χρηστών από αρχεία σειράς jabberd14" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "Πρώς ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "Από ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Διαμόρφωση Πίνακων βάσης δεδομένων στο " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Επιλέξτε τύπο αποθήκευσης των πινάκων" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Αντίγραφο μόνο σε δίσκο" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "Αντίγραφο μόνο σε RAM καί δίσκο" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "Αντίγραφο σε RAM" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "Απομεμακρυσμένο αντίγραφο" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Παύση Modules στο " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Επιλέξτε modules για να σταματήσουν" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Εκκίνηση Modules στο " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Εισάγετε κατάλογο των (Module, [Επιλογές])" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Λίστα των Module για Εκκίνηση" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Αποθήκευση Αντιγράφου Ασφαλείας σε Αρχείο στο " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Εισάγετε τοποθεσία αρχείου αντιγράφου ασφαλείας" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Τοποθεσία Αρχείου" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Επαναφορά Αντιγράφου Ασφαλείας από αρχείο στο " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Αποθήκευση Αντιγράφου Ασφαλείας σε αρχείο κειμένου στο " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Εισάγετε Τοποθεσία Αρχείου Κειμένου" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Εισαγωγή χρηστών από αρχείο στο " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Εισάγετε τοποθεσία αρχείου σειράς jabberd14" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Εισαγωγή χρηστών από κατάλογο αρχείων στο " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Εισάγετε κατάλογο αρχείων σειράς jabberd14" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Τοποθεσία κατάλογου αρχείων" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "Χρόνος καθυστέρησης" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Διαχείριση στις Λίστες Ελέγχου Πρόσβασης" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Λίστες Ελέγχου Πρόσβασης" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Διαμόρφωση Πρόσβασης" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Κανόνες Πρόσβασης" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Ταυτότητα Jabber" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Επαλήθευση κωδικού πρόσβασης" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Αριθμός εγγεγραμμένων χρηστών" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Αριθμός συνδεδεμένων χρηστών" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Τελευταία σύνδεση" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "Μέγεθος Καταλόγου Επαφών" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "Διευθύνσεις IP" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Πόροι" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Διαχείριση του" - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Eνέργεια για το χρήστη" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Επεξεργασία ιδιοτήτων" - -#: mod_fail2ban.erl:95 -msgid "" -"Too many (~p) failed authentications from this IP address (~s). The address " -"will be unblocked at ~s UTC" -msgstr "" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "" - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "" - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "Άρνηση πρόσβασης, λόγω τακτικής παροχής υπηρεσιών" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "IRC Διαβιβάσεις" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "ejabberd IRC module" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "Χρειάζεστε ένα x:data ικανό πελάτη για να ρυθμίσετε το mod_irc" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Εγγραφή στο mod_irc για " - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Εισάγετε το όνομα χρήστη, κωδικοποιήσεις, τις θύρες και τους κωδικούς " -"πρόσβασης που θέλετε να χρησιμοποιήσετε για σύνδεση με IRC διακομιστή" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "IRC Όνομα χρήστη" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Εάν θέλετε να καθορίσετε διαφορετικές θύρες, κωδικούς πρόσβασης, " -"κωδικοποιήσεις για IRC διακομιστές, εισάγετε πληροφορίες στη μορφή '{\"irc " -"διακομιστής\", \"κωδικοποιήσεις\", θύρα, \"κωδικός πρόσβασης\"}'. " -"Προεπιλεγμενα αυτή η υπηρεσία χρησιμοποιεί \"~s\" κωδικοποιήση, θύρα ~p, " -"κενό κωδικό πρόσβασης." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Παράδειγμα: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta." -"fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Παράμετροι Συνδέσης" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "Είσοδος στο IRC κανάλι" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "IRC κανάλι (μην τεθεί το πρώτο #)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "Διακομιστής IRC" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Είσοδος στο κανάλι IRC εδώ." - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Είσοδος στο κανάλι IRC αυτής της Jabber Ταυτότητας: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "IRC Ρυθμίσεις" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Πληκτρολογήστε το όνομα χρήστη και κωδικοποιήσεις που θέλετε να " -"χρησιμοποιήσετε για τη σύνδεση με διακομιστές IRC. Πατήστε 'Next' για να " -"πάρετε περισσότερα πεδία να συμπληρώσετε. Πατήστε 'Complete' για να " -"αποθηκεύσετε ρυθμίσεις." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "IRC όνομα χρήστη" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Κωδικός πρόσβασης ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Θύρα ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Κωδικοποίηση για διακομιστή ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Διακομιστής ~b" - -#: mod_mam.erl:541 -#, fuzzy -msgid "Only members may query archives of this room" -msgstr "Μόνο οι συντονιστές μπορούν να αλλάξουν το θέμα αυτής της αίθουσας" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "" -"Μόνο οι διαχειριστές των υπηρεσιών επιτρέπεται να στείλουν υπηρεσιακά " -"μηνύματα" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "Άρνηση δημιουργίας αίθουσας , λόγω τακτικής παροχής υπηρεσιών" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "Αίθουσα σύνεδριασης δεν υπάρχει" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Αίθουσες σύνεδριασης" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "Χρειάζεστε ένα x:data ικανό πελάτη για εγγραφή με ψευδώνυμο" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Εγγραφή με Ψευδώνυμο στο " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Πληκτρολογήστε το ψευδώνυμο που θέλετε να εγγραφείτε" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Ψευδώνυμο" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "Αυτό το ψευδώνυμο είναι καταχωρημένο από άλλο πρόσωπο" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Θα πρέπει να συμπληρώσετε το πεδίο \"Ψευδώνυμο\" στη φόρμα" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "ejabberd MUC module" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "" - -#: mod_muc_admin.erl:249 -#, fuzzy -msgid "Total rooms" -msgstr "Αίθουσες σύνεδριασης" - -#: mod_muc_admin.erl:250 -#, fuzzy -msgid "Permanent rooms" -msgstr "εγκαταλείπει την αίθουσα" - -#: mod_muc_admin.erl:251 -#, fuzzy -msgid "Registered nicknames" -msgstr "Εγγεγραμμένοι Χρήστες" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "Διαμόρφωση Αίθουσaς σύνεδριασης τροποποιηθηκε" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "συνδέετε στην αίθουσα" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "εγκαταλείπει την αίθουσα" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "έχει απαγορευθεί" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "Έχει αποβληθεί λόγω αλλαγής υπαγωγής" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "αποβλήθηκε επειδή η αίθουσα αλλάξε γιά μέλη μόνο" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "αποβλήθηκε λόγω τερματισμού συστήματος" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "είναι τώρα γνωστή ως" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " έχει θέσει το θέμα σε: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "Η αίθουσα σύνεδριασης δημιουργήθηκε" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "Η αίθουσα σύνεδριασης διαγράφηκε" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "Η αίθουσα σύνεδριασης έχει ξεκινήσει" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "Η αίθουσα σύνεδριασης έχει σταματήσει" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "Δευτέρα" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "Τρίτη" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "Τετάρτη" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "Πέμπτη" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "Παρασκευή" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "Σάββατο" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "Κυριακή" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "Ιανουάριος" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "Φεβρουάριος" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "Μάρτιος" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "Απρίλιος" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "Μάιος" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "Ιούνιος" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "Ιούλιος" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "Αύγουστος" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "Σεπτέμβριος" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "Οκτώβριος" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "Νοέμβριος" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "Δεκέμβριος" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Διαμόρφωση Αίθουσας σύνεδριασης" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "Συμετεχόντες Αίθουσας σύνεδριασης" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "Υπέρφορτωση" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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 "" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "Δεν επιτρέπεται να στείλει προσωπικά μηνύματα για τη διάσκεψη" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "Παρακαλώ, περιμένετε για λίγο πριν την αποστολή νέου αιτήματος φωνής" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "Τα αιτήματα φωνής είναι απενεργοποιημένα, σε αυτό το συνέδριο" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "Απέτυχε η εξαγωγή JID από την έγκριση του αιτήματος φωνής σας" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "Μόνο οι συντονιστές μπορούν να εγκρίνουν τις αιτήσεις φωνής" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Ακατάλληλο είδος μηνύματος" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "Δεν επιτρέπεται να στείλει προσωπικά μηνύματα του τύπου \"groupchat\"" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "Παραλήπτης δεν είναι στην αίθουσα συνεδριάσεων" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "Δεν επιτρέπεται η αποστολή προσωπικών μηνυμάτων" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Μόνο οι συμμετέχωντες μπορούν να στέλνουν μηνύματα στο συνέδριο" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "Μόνο οι συμετεχόντες μπορούν να στείλουν ερωτήματα στη διάσκεψη" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "" -"Ερωτήματα πρώς τα μέλη της διασκέψεως δεν επιτρέπονται σε αυτήν την αίθουσα" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "" -"Μόνο οι συντονιστές και οι συμμετέχοντες μπορούν να αλλάξουν το θέμα αυτής " -"της αίθουσας" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Μόνο οι συντονιστές μπορούν να αλλάξουν το θέμα αυτής της αίθουσας" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "" -"Οι επισκέπτες δεν επιτρέπεται να στείλουν μηνύματα σε όλους τους " -"συμμετέχωντες" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "" -"Οι επισκέπτες δεν επιτρέπεται να αλλάξουν τα ψευδώνυμα τους σε αυτή την " -"αίθουσα" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "Αυτό το ψευδώνυμο είναι ήδη σε χρήση από άλλον συμμετέχων" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Σας έχει απαγορευθεί η είσοδος σε αυτή την αίθουσα" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "Απαιτείται αίτηση συμετοχής για είσοδο σε αυτή την αίθουσα" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "Απαιτείται κωδικός πρόσβασης για είσοδο σε αυτή την αίθουσα" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "Πάρα πολλά αιτήματα CAPTCHA" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "Αδήνατο να δημιουργηθεί CAPTCHA" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Εσφαλμένος κωδικός πρόσβασης" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "Aπαιτούνται προνόμια διαχειριστή" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "Aπαιτούνται προνόμια συντονιστή" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "Η Jabber Ταυτότητα ~s είναι άκυρη" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "Ψευδώνυμο ~s δεν υπάρχει σε αυτή την αίθουσα" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Άκυρη υπαγωγή: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Άκυρος ρόλο: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "Aπαιτούνται προνόμια ιδιοκτήτη" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "Διαμόρφωση Αίθουσας σύνεδριασης ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Τίτλος Αίθουσας " - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "Περιγραφή Αίθουσας" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Κάντε αίθουσα μόνιμη" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Κάντε την δημόσια αναζήτηση δυνατή για αυτή την αίθουσα" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "Κάντε κοινό τον κατάλογο συμμετεχόντων" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Κάντε την αίθουσα προστατεύομενη με κωδικό πρόσβασης" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Μέγιστος αριθμός συμετεχόντων" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Χωρίς όριο" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Παρούσιαση πραγματικών ταυτοτήτων Jabber σε" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "συντονιστές μόνο" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "οποιοσδήποτε" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "" - -#: mod_muc_room.erl:3486 -#, fuzzy -msgid "Moderator" -msgstr "συντονιστές μόνο" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Κάντε την αίθουσα μόνο για μέλη" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Κάντε την αίθουσα εποπτεύονομενη" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Προεπιλογη χρήστων ως συμμετέχοντες" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Επιτρέψετε στους χρήστες να αλλάζουν το θέμα" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Επιτρέψετε στους χρήστες να αποστέλλουν ιδιωτικά μηνύματα" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "Επιτρέψετε στους χρήστες να αποστέλλουν ιδιωτικά μηνύματα σε" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "κανείς" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Επιτρέπστε στους χρήστες να ερωτούν άλλους χρήστες" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Επιτρέψετε στους χρήστες να αποστέλλουν προσκλήσεις" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "" -"Επιτρέψτε στους επισκέπτες να αποστέλλουν κατάσταση στις ενημερώσεις " -"παρουσίας" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Επιτρέψετε στους επισκέπτες να αλλάζου ψευδώνυμο" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "Επιτρέψτε στους επισκέπτες να στέλνουν αιτήματα φωνής" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "Ελάχιστο χρονικό διάστημα μεταξύ αιτημάτων φωνής (σε δευτερόλεπτα)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "Κάντε την αίθουσα CAPTCHA προστατεύονομενη" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "Εξαίρεση από τις ταυτότητες Jabber, ή CAPTCHA πρόκληση" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Ενεργοποίηση καταγραφής" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "Χρειάζεστε ένα x:data ικανό πελάτη για να ρυθμίσετε την αίθουσα " - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Αριθμός συμετεχόντων" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "ιδιωτικό," - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "Αίτημα φωνής" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "Είτε εγκρίνετε ή απορρίψτε το αίτημα φωνής." - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "JID Χρήστη" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "Παραχώρηση φωνής σε αυτό το άτομο;" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s σας προσκαλεί στην αίθουσα ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "ο κωδικός πρόσβασης είναι" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "" -"Η μνήμη χωρίς σύνδεση μήνυματών είναι πλήρης. Το μήνυμα έχει απορριφθεί." - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "Η Σειρά Χωρίς Σύνδεση Μηνύματων τού ~s" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Χρόνος" - -#: mod_offline.erl:812 -msgid "From" -msgstr "Από" - -#: mod_offline.erl:813 -msgid "To" -msgstr "Πρώς" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Πακέτο" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Χωρίς Σύνδεση Μηνύματα:" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "Αφαίρεση Όλων των Χωρίς Σύνδεση Μηνύματων" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "ejabberd SOCKS5 Bytestreams module" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Δημοσίευση-Εγγραφή" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "ejabberd module Δημοσίευσης-Εγγραφής" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "Αίτηση συνδρομητή Δημοσίευσης-Εγγραφής" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Επιλέξτε αν θα εγκρίθεί η εγγραφή αυτής της οντότητας." - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "Ταυτότητα Κόμβου" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Διεύθυνση Συνδρομητή" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "" -"Επιτρέπετε σε αυτή την Jabber Ταυτότητα να εγγραφεί σε αυτό τον κόμβο " -"Δημοσίευσης-Εγγραφής;" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Κοινοποιήσεις με την παράδοση φορτίων" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Κοινοποιήσεις παράδοσης" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Ειδοποιηση στους συνδρομητές όταν αλλάζει η διαμόρφωση κόμβου" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Ειδοποιηση στους συνδρομητές όταν ο κόμβος διαγράφεται" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Ειδοποιηση στους συνδρομητές όταν αφαίρούντε στοιχεία από τον κόμβο" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Μονιμη αποθήκευση στοιχείων" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "Ένα φιλικό όνομα για τον κόμβο" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Μέγιστος αριθμός μόνιμων στοιχείων" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Εάν επιτρέποντε συνδρομές" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Καθορίστε το μοντέλο πρόσβασης" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Ομάδες Καταλόγου Επαφών μπορούν να εγγραφούν" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Καθορίστε το μοντέλο εκδότη" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "Διαγραφή όλων των στοιχείων όταν ο σχετικός εκδότης αποσυνδέεται" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "Καθορίστε τον τύπο μηνύματος συμβάντος" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Μέγιστο μέγεθος φορτίου σε bytes" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "Πότε να αποσταλθεί το τελευταίο στοιχείο που δημοσιεύθηκε" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Παράδωση κοινοποιήσεων μόνο σε διαθέσιμους χρήστες" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "Οι συλλογές με την οποία είναι ένας κόμβος συνδέεται" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "Η επαλήθευση της εικόνας CAPTCHA απέτυχε" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "Χρειάζεστε ένα x:data και CAPTCHA ικανό πελάτη για εγγραφή" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "" -"Επιλέξτε ένα όνομα χρήστη και κωδικό πρόσβασης για να εγγραφείτε σε αυτό τον " -"διακομιστή" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "Ο κωδικός πρόσβασης είναι πολύ ασθενές" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "Οι χρήστες δεν επιτρέπεται να εγγραφούν λογαριασμούς τόσο γρήγορα" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "Ο Jabber λογαριασμός σας δημιουργήθηκε με επιτυχία." - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "Υπήρξε ένα σφάλμα κατά τη δημιουργία του λογαριασμού:" - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "Ο Jabber λογαριασμός σας διαγράφηκε με επιτυχία." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "Υπήρξε ένα σφάλμα κατά τη διαγραφή του λογαριασμού:" - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "Ο κωδικός πρόσβασης του Jabber λογαριασμού σας έχει αλλάξει επιτυχώς." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "Υπήρξε ένα σφάλμα κατά την αλλαγή του κωδικού πρόσβασης:" - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Εγγραφή λογαριασμού Jabber" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Καταχωρήστε έναν λογαριασμό Jabber" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Καταργήστε την εγγραφή ενός λογαριασμού Jabber" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"Αυτή η σελίδα σας επιτρέπει να δημιουργήσετε ένα λογαριασμό Jabber σε αυτόν " -"το διακομιστή Jabber. JID σας (Jabber Identifier) θα είναι της μορφής: " -"όνομα_χρήστη@διακομιστής_Jabber. Παρακαλώ διαβάστε προσεκτικά τις οδηγίες " -"για να συμπληρώσετε σωστά τα πεδία." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "Όνομα χρήστη" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" -"Ανεξαρτήτως με πεζά ή κεφαλαία: 'μιαλεξη' είναι το ίδιο με 'ΜιαΛέξη' και " -"'Μιαλέξη'." - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "Χαρακτήρες δεν επιτρέπονται:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "Διακομιστής:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" -"Μην πείτε τον κωδικό πρόσβασής σας σε κανέναν, ακόμη και στους διαχειριστές " -"του διακομιστή Jabber." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "" -"Μπορείτε αργότερα να αλλάξετε τον κωδικό πρόσβασής σας χρησιμοποιώντας έναν " -"πελάτη Jabber." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Μερικοί πελάτες Jabber μπορεί να αποθηκεύσουν τον κωδικό πρόσβασής σας στον " -"υπολογιστή σας. Χρησιμοποιήστε αυτό το χαρακτηριστικό μόνο εάν εμπιστεύεστε " -"την ασφάλεια του υπολογιστή σας." - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"Απομνημονεύστε τον κωδικό πρόσβασής σας, ή γράψετε τον σε ένα χαρτί που είχε " -"τοποθετηθεί σε ασφαλές μέρος. Στο Jabber δεν υπάρχει αυτοματοποιημένος " -"τρόπος για να ανακτήσετε τον κωδικό σας αν τον ξεχάσετε." - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "Επαλήθευση κωδικού πρόσβασης:" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "Καταχωρήστε" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "Παλαιός κωδικός πρόσβασης:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "Νέος κωδικός πρόσβασης:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "" -"Η σελίδα αυτή δίνει τη δυνατότητα να καταργήσετε την καταχώρηση ενός " -"λογαριασμό Jabber σε αυτόν το διακομιστή Jabber." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "Καταργήση εγγραφής" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Συνδρομή" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Εκκρεμεί" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Ομάδες" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Επαληθεύστε" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Αφαίρεστε" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Καταλόγος Επαφών τού" - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Προσθήκη Jabber Ταυτότητας" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Καταλόγος Επαφών" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Κοινές Ομάδες Καταλόγων Επαφών" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Όνομα:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Περιγραφή:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Μέλη:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Εμφανίσμενες Ομάδες:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Ομάδα" - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Erlang Jabber Διακομιστής" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Γενέθλια" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Πόλη" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "Χώρα" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "Email" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Επώνυμο" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Συμπληρώστε τη φόρμα για να αναζητήσετε οποιαδήποτε Jabber χρήστη που " -"ταιριάζει (Προσθέστε * στο τέλος τού πεδίου για να ταιριάξει σε μεγαλύτερες " -"γραμματοσηρές)" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Ονοματεπώνυμο" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Πατρώνυμο" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Όνομα Οργανισμού" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Μονάδα Οργανισμού" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Αναζήτηση χρηστών στο" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "Χρειάζεστε ένα x:data ικανό πελάτη για αναζήτηση" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "vCard Αναζήτηση χρηστών" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "ejabberd vCard module" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Αποτελέσματα αναζήτησης για " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "" -"Συμπληρώστε τα πεδία για να αναζητήσετε οποιαδήποτε ταιριάζοντα Jabber χρήστη" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Εξερχόμενοι S2S διακομιστές:" - -#~ msgid "Delete" -#~ msgstr "Διαγραφή" - -#~ msgid "This room is not anonymous" -#~ msgstr "Η αίθουσα αυτή δεν είναι ανώνυμη" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "" -#~ "Αυτός ο συμμετέχων αποβλήθηκε από την αίθουσα, επειδή έστειλε ένα μήνυμα " -#~ "σφάλματος" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Αυτός ο συμμετέχων αποβλήθηκε από την αίθουσα, επειδή έστειλε ένα μήνυμα " -#~ "σφάλματος σε άλλον συμμετέχων" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "" -#~ "Αυτός ο συμμετέχων αποβλήθηκε από την αίθουσα, επειδή έστειλε σφάλμα " -#~ "παρουσίας " - -#, fuzzy -#~ msgid "Captcha test failed" -#~ msgstr "Το CAPTCHA είναι έγκυρο." diff --git a/priv/msgs/eo.msg b/priv/msgs/eo.msg index 52a2f53a4..553980422 100644 --- a/priv/msgs/eo.msg +++ b/priv/msgs/eo.msg @@ -1,21 +1,28 @@ -%% -*- coding: latin-1 -*- -{"Access Configuration","Agordo de atingo"}. -{"Access Control List Configuration","Agordo de atingokontrolo"}. -{"Access control lists","Atingokontrol-listoj"}. -{"Access Control Lists","Atingokontrol-listoj"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" (Add * to the end of field to match substring)"," (Aldonu * al la fino de la kampo por kongruigi subĉenon)"}. +{" has set the subject to: "," ŝanĝis la temon al: "}. +{"# participants","Nombro de partoprenantoj"}. +{"A description of the node","Priskribo de la nodo"}. +{"A friendly name for the node","Kromnomo de la nodo"}. +{"A password is required to enter this room","Pasvorto estas bezonata por eniri ĉi tiun babilejon"}. +{"A Web Page","Retpaĝo"}. +{"Accept","Akcepti"}. {"Access denied by service policy","Atingo rifuzita de serv-politiko"}. -{"Access rules","Atingo-reguloj"}. -{"Access Rules","Atingo-reguloj"}. +{"Access model","Atingomodelo"}. +{"Account doesn't exist","Konto ne ekzistas"}. {"Action on user","Ago je uzanto"}. -{"Add Jabber ID","Aldonu Jabber ID"}. -{"Add New","Aldonu novan"}. {"Add User","Aldonu Uzanton"}. -{"Administration","Administro"}. {"Administration of ","Mastrumado de "}. +{"Administration","Administro"}. {"Administrator privileges required","Administrantaj rajtoj bezonata"}. -{"A friendly name for the node","Kromnomo por ĉi tiu nodo"}. {"All activity","Ĉiu aktiveco"}. -{"Allow this Jabber ID to subscribe to this pubsub node?","Ĉu permesi ĉi tiun Jabber ID aboni al la jena PubAbo-nodo"}. +{"All Users","Ĉiuj Uzantoj"}. +{"Allow subscription","Permesi abonon"}. +{"Allow this Jabber ID to subscribe to this pubsub node?","Ĉu permesi ĉi tiun Jabber ID aboni al la jena PubAbo-nodo?"}. {"Allow users to change the subject","Permesu uzantojn ŝanĝi la temon"}. {"Allow users to query other users","Permesu uzantojn informpeti aliajn uzantojn"}. {"Allow users to send invites","Permesu uzantojn sendi invitojn"}. @@ -24,21 +31,34 @@ {"Allow visitors to send private messages to","Permesu uzantojn sendi privatajn mesaĝojn al"}. {"Allow visitors to send status text in presence updates","Permesu al vizitantoj sendi statmesaĝon en ĉeest-sciigoj"}. {"Allow visitors to send voice requests","Permesu uzantojn sendi voĉ-petojn"}. -{"All Users","Ĉiuj Uzantoj"}. {"Announcements","Anoncoj"}. -{"anyone","iu ajn"}. -{"A password is required to enter this room","Pasvorto estas bezonata por eniri ĉi tiun babilejon"}. +{"Answer associated with a picture","Respondo asociita kun bildo"}. +{"Answer associated with a video","Respondo asociita kun filmeto"}. +{"Answer associated with speech","Respondo asociita kun parolo"}. +{"Answer to a question","Respondo al demando"}. +{"Anyone may publish","Ĉiu rajtas publici"}. +{"Anyone with Voice","Iu ajn kun Voĉo"}. +{"Anyone","Iu ajn"}. {"April","Aprilo"}. +{"Attribute 'channel' is required for this request","Atributo 'channel' necesas por ĉi tiu peto"}. +{"Attribute 'id' is mandatory for MIX messages","Atributo 'id' estas deviga en MIX-mesaĝo"}. +{"Attribute 'jid' is not allowed here","Atributo 'jid' ne estas permesita ĉi tie"}. +{"Attribute 'node' is not allowed here","Atributo 'node' ne estas permesita ĉi tie"}. {"August","Aŭgusto"}. -{"Backup","Faru Sekurkopion"}. {"Backup Management","Mastrumado de sekurkopioj"}. {"Backup of ~p","Sekurkopio de ~p"}. {"Backup to File at ","Faru sekurkopion je "}. +{"Backup","Faru Sekurkopion"}. {"Bad format","Malĝusta formo"}. {"Birthday","Naskiĝtago"}. +{"Cannot remove active list","Ne povas forigi aktivan liston"}. {"CAPTCHA web page","CAPTCHA teksaĵ-paĝo"}. +{"Challenge ID","Identigilo de Defio"}. {"Change Password","Ŝanĝu pasvorton"}. {"Change User Password","Ŝanĝu pasvorton de uzanto"}. +{"Channel already exists","Kanalo jam ekzistas"}. +{"Channel does not exist","Kanalo ne ekzistas"}. +{"Channels","Kanaloj"}. {"Characters not allowed:","Karaktroj ne permesata:"}. {"Chatroom configuration modified","Agordo de babilejo ŝanĝita"}. {"Chatroom is created","Babilejo kreita"}. @@ -47,62 +67,48 @@ {"Chatroom is stopped","Babilejo haltita"}. {"Chatrooms","Babilejoj"}. {"Choose a username and password to register with this server","Elektu uzantnomon kaj pasvorton por registri je ĉi tiu servilo"}. -{"Choose modules to stop","Elektu modulojn por fini"}. {"Choose storage type of tables","Elektu konserv-tipon de tabeloj"}. -{"Choose whether to approve this entity's subscription.","Elektu ĉu permesi la abonon de ĉi tiu ento"}. +{"Choose whether to approve this entity's subscription.","Elektu ĉu permesi la abonon de ĉi tiu ento."}. {"City","Urbo"}. {"Commands","Ordonoj"}. {"Conference room does not exist","Babilejo ne ekzistas"}. -{"Configuration","Agordo"}. {"Configuration of room ~s","Agordo de babilejo ~s"}. -{"Connected Resources:","Konektataj risurcoj:"}. -{"Connections parameters","Konekto-parametroj"}. +{"Configuration","Agordo"}. {"Country","Lando"}. -{"CPU Time:","CPU-tempo"}. -{"Database","Datumbazo"}. -{"Database Tables at ~p","Datumbaz-tabeloj je ~p"}. +{"Current Discussion Topic","Aktuala Diskuta Temo"}. {"Database Tables Configuration at ","Agordo de datumbaz-tabeloj je "}. +{"Database","Datumbazo"}. {"December","Decembro"}. {"Default users as participants","Kutime farigu uzantojn kiel partpoprenantoj"}. -{"Delete message of the day","Forigu mesaĝo de la tago"}. {"Delete message of the day on all hosts","Forigu mesaĝo de la tago je ĉiu gastigo"}. -{"Delete Selected","Forigu elektata(j)n"}. +{"Delete message of the day","Forigu mesaĝo de la tago"}. {"Delete User","Forigu Uzanton"}. {"Deliver event notifications","Liveru event-sciigojn"}. {"Deliver payloads with event notifications","Liveru aĵojn de event-sciigoj"}. -{"Description:","Priskribo:"}. {"Disc only copy","Nur disk-kopio"}. -{"Displayed Groups:","Montrataj grupoj:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","Ne donu vian pasvorton al iun ajn, eĉ ne al la administrantoj de la Ĵabber-servilo."}. {"Dump Backup to Text File at ","Skribu sekurkopion en plata teksto al "}. {"Dump to Text File","Skribu en plata tekst-dosiero"}. +{"Duplicated groups are not allowed by RFC6121","RFC 6121 ne permesas duplikatajn grupojn"}. {"Edit Properties","Redaktu atributojn"}. {"Either approve or decline the voice request.","Ĉu aprobu, aŭ malaprobu la voĉ-peton."}. -{"ejabberd IRC module","ejabberd IRC-modulo"}. {"ejabberd MUC module","ejabberd MUC-modulo"}. {"ejabberd Multicast service","ejabberd Multicast-servo"}. {"ejabberd Publish-Subscribe module","ejabberd Public-Abonada modulo"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bajtfluo modulo"}. {"ejabberd vCard module","ejabberd vCard-modulo"}. {"ejabberd Web Admin","ejabberd Teksaĵa Administro"}. -{"Elements","Eroj"}. +{"ejabberd","ejabberd"}. +{"Email Address","Retpoŝta Adreso"}. {"Email","Retpoŝto"}. {"Enable logging","Ŝaltu protokoladon"}. {"Enable message archiving","Ŝaltu mesaĝo-arkivo"}. -{"Encoding for server ~b","Enkodigo por servilo ~b"}. {"End User Session","Haltigu Uzant-seancon"}. -{"Enter list of {Module, [Options]}","Enmetu liston de {Modulo, [Elektebloj]}"}. {"Enter nickname you want to register","Enmetu kaŝnomon kiun vi volas registri"}. {"Enter path to backup file","Enmetu vojon por sekurkopio"}. {"Enter path to jabberd14 spool dir","Enmetu vojon al jabberd14-uzantdosierujo"}. {"Enter path to jabberd14 spool file","Enmetu vojon al jabberd14-uzantdosiero"}. {"Enter path to text file","Enmetu vojon al plata teksto"}. {"Enter the text you see","Enmetu montrita teksto"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Enmetu uzantnomon kaj enkodigojn kiujn vi volas uzi por konektoj al IRC-serviloj. Elektu 'Sekvonto' por ekhavi pliajn kampojn. Elektu 'Kompletigu' por savi agordojn."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Enmetu uzantnomon,j enkodigojn, pordojn kaj pasvortojn kiujn vi volas uzi por konektoj al IRC-serviloj"}. -{"Erlang Jabber Server","Erlang-a Jabber-Servilo"}. -{"Error","Eraro"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Ekzemplo: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"sekreto\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.iutestservilo.net\", \"utf-8\"}]."}. {"Exclude Jabber IDs from CAPTCHA challenge","Esceptu Ĵabber-identigilojn je CAPTCHA-defio"}. {"Export all tables as SQL queries to a file:","Eksportu ĉiuj tabeloj kiel SQL-informmendo al dosierujo:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Eksportu datumojn de ĉiuj uzantoj en servilo al PIEFXIS dosieroj (XEP-0227):"}. @@ -110,29 +116,21 @@ {"Failed to extract JID from your voice request approval","Malsukcesis ekstrakti JID-on de via voĉ-pet-aprobo"}. {"Family Name","Lasta Nomo"}. {"February","Februaro"}. -{"Fill in fields to search for any matching Jabber User","Kompletigu la formon por serĉi rekonata Jabber-uzanto"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Kompletigu la formon por serĉi rekonata Jabber-uzanto (Aldonu * je la fino de la kampo por rekoni subĉenon"}. +{"File larger than ~w bytes","Dosiero pli granda ol ~w bajtoj"}. {"Friday","Vendredo"}. -{"From","De"}. -{"From ~s","De ~s"}. {"Full Name","Plena Nomo"}. {"Get Number of Online Users","Montru nombron de konektataj uzantoj"}. {"Get Number of Registered Users","Montru nombron de registritaj uzantoj"}. {"Get User Last Login Time","Montru tempon de lasta ensaluto"}. -{"Get User Password","Montru pasvorton de uzanto"}. {"Get User Statistics","Montru statistikojn de uzanto"}. +{"Given Name","Persona Nomo"}. {"Grant voice to this person?","Koncedu voĉon al ĉi-persono?"}. -{"Group ","Grupo "}. -{"Groups","Grupoj"}. {"has been banned","estas forbarita"}. -{"has been kicked because of an affiliation change","estas forpelita pro aparteneca ŝanĝo"}. {"has been kicked because of a system shutdown","estas forpelita pro sistem-haltigo"}. +{"has been kicked because of an affiliation change","estas forpelita pro aparteneca ŝanĝo"}. {"has been kicked because the room has been changed to members-only","estas forpelita ĉar la babilejo fariĝis sole por membroj"}. {"has been kicked","estas forpelita"}. -{" has set the subject to: "," ŝanĝis la temon al: "}. -{"Host","Gastigo"}. {"If you don't see the CAPTCHA image here, visit the web page.","Se vi ne vidas la CAPTCHA-imagon jene, vizitu la teksaĵ-paĝon."}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Se vi volas specifi diversajn pordojn, pasvortojn, enkodigojn por IRC-serviloj, kompletigu la jenan liston kun la formo '{\"irc-servilo\", \"enkodigo\", porto, \"pasvorto\"}'. Se ne specifita, ĉi tiu servilo uzas la enkodigo \"~s\", porto ~p, malplena pasvorto."}. {"Import Directory","Importu dosierujo"}. {"Import File","Importu dosieron"}. {"Import user data from jabberd14 spool file:","Importu uzantojn de jabberd14-uzantdosieroj"}. @@ -142,86 +140,68 @@ {"Import Users from Dir at ","Importu uzantojn de dosierujo ĉe "}. {"Import Users From jabberd14 Spool Files","Importu uzantojn de jabberd14-uzantdosieroj"}. {"Improper message type","Malĝusta mesaĝo-tipo"}. +{"Incorrect CAPTCHA submit","Neĝusta CAPTCHA-submeto"}. {"Incorrect password","Nekorekta pasvorto"}. -{"Invalid affiliation: ~s","Nevalida aparteneco: ~s"}. -{"Invalid role: ~s","Nevalida rolo: ~s"}. {"IP addresses","IP-adresoj"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","IRC-babilejo (ne aldonu #-prefikson)"}. -{"IRC server","IRC-servilo"}. -{"IRC settings","IRC agordoj"}. -{"IRC Transport","IRC-transportilo"}. -{"IRC Username","IRC-kaŝnomo"}. -{"IRC username","IRC-uzantnomo"}. {"is now known as","nun nomiĝas"}. -{"It is not allowed to send private messages","Ne estas permesata sendi privatajn mesaĝojn"}. {"It is not allowed to send private messages of type \"groupchat\"","Malpermesas sendi mesaĝojn de tipo \"groupchat\""}. {"It is not allowed to send private messages to the conference","Nur partoprenantoj rajtas sendi privatajn mesaĝojn al la babilejo"}. -{"Jabber Account Registration","Ĵabber-konto registrado"}. {"Jabber ID","Jabber ID"}. -{"Jabber ID ~s is invalid","Jabber ID ~s estas nevalida"}. {"January","Januaro"}. -{"Join IRC channel","Eniras IRC-babilejon"}. {"joins the room","eniras la babilejo"}. -{"Join the IRC channel here.","Eniru IRC-babilejon jen"}. -{"Join the IRC channel in this Jabber ID: ~s","Eniru IRC-babilejon en ĉi Jabber-ID: ~s"}. {"July","Julio"}. {"June","Junio"}. +{"Just created","Ĵus kreita"}. {"Last Activity","Lasta aktiveco"}. {"Last login","Lasta ensaluto"}. {"Last month","Lasta monato"}. {"Last year","Lasta jaro"}. {"leaves the room","eliras la babilejo"}. -{"Listened Ports at ","Atentataj pordoj je "}. -{"Listened Ports","Atentataj pordoj"}. -{"List of modules to start","Listo de moduloj por starti"}. -{"List of rooms","Listo de babilejoj"}. -{"Low level update script","Bazanivela ĝisdatigo-skripto"}. {"Make participants list public","Farigu partoprento-liston publika"}. -{"Make room CAPTCHA protected","Farigu babilejon protektata per CAPTCHA"}. +{"Make room CAPTCHA protected","Protektu babilejon per CAPTCHA"}. {"Make room members-only","Farigu babilejon sole por membroj"}. {"Make room moderated","Farigu babilejon moderigata"}. {"Make room password protected","Farigu babilejon protektata per pasvorto"}. {"Make room persistent","Farigu babilejon daŭra"}. {"Make room public searchable","Farigu babilejon publike trovebla"}. {"March","Marĉo"}. -{"Maximum Number of Occupants","Limigo de nombro de partoprenantoj"}. -{"Max # of items to persist","Maksimuma kiomo de eroj en konservado"}. {"Max payload size in bytes","Maksimuma aĵo-grando je bajtoj"}. +{"Maximum file size","Maksimuma grando de dosiero"}. +{"Maximum Number of Occupants","Limigo de nombro de partoprenantoj"}. {"May","Majo"}. {"Membership is required to enter this room","Membreco estas bezonata por eniri ĉi tiun babilejon"}. -{"Members:","Membroj:"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Memoru vian pasvorton, aŭ skribu ĝin sur papero formetata je sekura loko. Je Ĵabber ne ekzistas aŭtomata metodo por reakiri vian pasvorton se vi forgesas ĝin."}. -{"Memory","Memoro"}. {"Message body","Teksto de mesaĝo"}. {"Middle Name","Meza Nomo"}. {"Minimum interval between voice requests (in seconds)","Minimuma intervalo inter voĉ-petoj (je sekundoj)"}. {"Moderator privileges required","Moderantaj rajtoj bezonata"}. -{"moderators only","moderantoj sole"}. -{"Modified modules","Ĝisdatigitaj moduloj"}. -{"Module","Modulo"}. -{"Modules at ~p","Moduloj je ~p"}. -{"Modules","Moduloj"}. +{"Module failed to handle the query","Modulo malsukcesis trakti la informpeton"}. {"Monday","Lundo"}. {"Multicast","Multicast"}. +{"Multiple elements are not allowed by RFC6121","RFC 6121 ne permesas plurajn -elementojn"}. {"Multi-User Chat","Grupbabilado"}. -{"Name:","Nomo:"}. {"Name","Nomo"}. +{"Natural Language for Room Discussions","Homa Lingvo por Diskutoj en Babilejo"}. {"Never","Neniam"}. {"New Password:","Nova Pasvorto:"}. -{"Nickname","Kaŝnomo"}. {"Nickname Registration at ","Kaŝnomo-registrado je "}. {"Nickname ~s does not exist in the room","Kaŝnomo ~s ne ekzistas en la babilejo"}. -{"nobody","neniu"}. +{"Nickname","Kaŝnomo"}. +{"No address elements found","Adresa elemento ne trovita"}. +{"No addresses element found","Adresa elemento ne trovita"}. {"No body provided for announce message","Neniu teksto donita por anonc-mesaĝo"}. +{"No child elements found","Ida elemento ne trovita"}. {"No Data","Neniu datumo"}. +{"No element found","Elemento ne trovita"}. +{"No items found in this query","Neniu elemento trovita en ĉi tiu informpeto"}. +{"No limit","Neniu limigo"}. +{"No module is handling this query","Neniu modulo traktas ĉi tiun informpeton"}. +{"No 'password' found in this query","Neniu pasvorto trovita en ĉi tiu informpeto"}. +{"No private data found in this query","Neniu privata dateno trovita en ĉi tiu informpeto"}. {"Node ID","Nodo ID"}. {"Node not found","Nodo ne trovita"}. {"Node ~p","Nodo ~p"}. {"Nodes","Nodoj"}. -{"No limit","Neniu limigo"}. {"None","Nenio"}. -{"No resource provided","Neniu risurco donita"}. {"Not Found","Ne trovita"}. {"Notify subscribers when items are removed from the node","Sciigu abonantoj kiam eroj estas forigita de la nodo"}. {"Notify subscribers when the node configuration changes","Sciigu abonantoj kiam la agordo de la nodo ŝanĝas"}. @@ -231,69 +211,54 @@ {"Number of online users","Nombro de konektataj uzantoj"}. {"Number of registered users","Nombro de registritaj uzantoj"}. {"October","Oktobro"}. -{"Offline Messages:","Liverontaj mesaĝoj"}. -{"Offline Messages","Liverontaj mesaĝoj"}. {"OK","Bone"}. {"Old Password:","Malnova Pasvorto:"}. -{"Online","Konektata"}. -{"Online Users:","Konektataj uzantoj:"}. {"Online Users","Konektataj Uzantoj"}. +{"Online","Konektata"}. {"Only deliver notifications to available users","Nur liveru sciigojn al konektataj uzantoj"}. +{"Only element is allowed in this query","Nur la elemento estas permesita en ĉi tiu informpeto"}. {"Only moderators and participants are allowed to change the subject in this room","Nur moderigantoj kaj partoprenantoj rajtas ŝanĝi la temon en ĉi tiu babilejo"}. {"Only moderators are allowed to change the subject in this room","Nur moderigantoj rajtas ŝanĝi la temon en ĉi tiu babilejo"}. {"Only moderators can approve voice requests","Nur moderigantoj povas aprobi voĉ-petojn"}. {"Only occupants are allowed to send messages to the conference","Nur partoprenantoj rajtas sendi mesaĝojn al la babilejo"}. {"Only occupants are allowed to send queries to the conference","Nur partoprenantoj rajtas sendi informmendojn al la babilejoj"}. +{"Only publishers may publish","Nur publicantoj rajtas publici"}. {"Only service administrators are allowed to send service messages","Nur servo-administrantoj rajtas sendi serv-mesaĝojn"}. -{"Options","Elektebloj"}. +{"Only those on a whitelist may associate leaf nodes with the collection","Nur tiuj en permesolisto rajtas asocii foliajn nodojn kun la kolekto"}. +{"Only those on a whitelist may subscribe and retrieve items","Nur tiuj en permesolisto rajtas aboni kaj preni erojn"}. {"Organization Name","Organiz-nomo"}. {"Organization Unit","Organiz-parto"}. -{"Outgoing s2s Connections:","Elirantaj s-al-s-konektoj:"}. {"Outgoing s2s Connections","Elirantaj s-al-s-konektoj"}. {"Owner privileges required","Mastraj rajtoj bezonata"}. -{"Packet","Pakaĵo"}. -{"Password ~b","Pasvorto ~b"}. -{"Password:","Pasvorto:"}. -{"Password","Pasvorto"}. -{"Password Verification:","Pasvortkontrolo:"}. {"Password Verification","Pasvortkontrolo"}. +{"Password Verification:","Pasvortkontrolo:"}. +{"Password","Pasvorto"}. +{"Password:","Pasvorto:"}. {"Path to Dir","Vojo al dosierujo"}. {"Path to File","Voje de dosiero"}. -{"Pending","Atendanta"}. {"Period: ","Periodo: "}. -{"Permanent rooms","Permanentaj babilejoj"}. {"Persist items to storage","Savu erojn en konservado"}. {"Ping","Sondaĵo"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Rimarku ke ĉi tiuj elektebloj nur sekurkopias la propran Mnesia-datumbazon. Se vi uzas la ODBC-modulon, vi ankaŭ devas sekurkopii tiujn SQL-datumbazoj aparte."}. {"Please, wait for a while before sending new voice request","Bonvolu atendi iomete antaŭ ol sendi plian voĉ-peton"}. {"Pong","Resondaĵo"}. -{"Port ~b","Pordo ~b"}. -{"Port","Pordo"}. {"Present real Jabber IDs to","Montru verajn Jabber ID-ojn al"}. {"private, ","privata, "}. -{"Protocol","Protokolo"}. -{"Publish-Subscribe","Public-Abonado"}. +{"Publish model","Publici modelon"}. +{"Publish-Subscribe","Publici-Aboni"}. {"PubSub subscriber request","PubAbo abonpeto"}. -{"Purge all items when the relevant publisher goes offline","Forigu ĉiujn erojn kiam la rilata publikanto malkonektiĝas"}. +{"Purge all items when the relevant publisher goes offline","Forigu ĉiujn erojn kiam la rilata publicanto malkonektiĝas"}. {"Queries to the conference members are not allowed in this room","Malpermesas informmendoj al partoprenantoj en ĉi tiu babilejo"}. +{"Query to another users is forbidden","Informpeto al aliaj uzantoj estas malpermesita"}. {"RAM and disc copy","RAM- kaj disk-kopio"}. {"RAM copy","RAM-kopio"}. -{"Raw","Kruda"}. {"Really delete message of the day?","Ĉu vere forigi mesaĝon de la tago?"}. -{"Recipient is not in the conference room","Ricevanto ne ĉeestas en la babilejo "}. -{"Register a Jabber account","Registru Ĵabber-konton"}. -{"Registered nicknames","Registritaj uzantnomoj"}. -{"Registered Users:","Registritaj uzantoj:"}. -{"Registered Users","Registritaj uzantoj"}. +{"Recipient is not in the conference room","Ricevanto ne ĉeestas en la babilejo"}. {"Register","Registru"}. -{"Registration in mod_irc for ","Registraĵo en mod_irc de "}. {"Remote copy","Fora kopio"}. -{"Remove All Offline Messages","Forigu ĉiujn liverontajn mesaĝojn"}. -{"Remove","Forigu"}. {"Remove User","Forigu uzanton"}. {"Replaced by new connection","Anstataŭigita je nova konekto"}. {"Resources","Risurcoj"}. -{"Restart","Restartu"}. {"Restart Service","Restartu Servon"}. {"Restore Backup from File at ","Restaŭrigu de dosiero el "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Restaŭrigu duuman sekurkopion post sekvonta ejabberd-restarto"}. @@ -306,14 +271,9 @@ {"Room Occupants","Nombro de ĉeestantoj"}. {"Room title","Babilejo-nomo"}. {"Roster groups allowed to subscribe","Kontaktlist-grupoj kiuj rajtas aboni"}. -{"Roster","Kontaktlisto"}. -{"Roster of ","Kontaktlisto de "}. {"Roster size","Kontaktlist-grando"}. -{"RPC Call Error","Eraro de RPC-alvoko"}. {"Running Nodes","Funkciantaj Nodoj"}. -{"~s access rule configuration","Agordo de atingo-reguloj de ~s"}. {"Saturday","Sabato"}. -{"Script check","Skript-kontrolo"}. {"Search Results for ","Serĉ-rezultoj de "}. {"Search users in ","Serĉu uzantojn en "}. {"Send announcement to all online users on all hosts","Sendu anoncon al ĉiu konektata uzanto de ĉiu gastigo"}. @@ -321,7 +281,6 @@ {"Send announcement to all users on all hosts","Sendu anoncon al ĉiu uzanto de ĉiu gastigo"}. {"Send announcement to all users","Sendu anoncon al ĉiu uzanto"}. {"September","Septembro"}. -{"Server ~b","Servilo ~b"}. {"Server:","Servilo:"}. {"Set message of the day and send to online users","Enmetu mesaĝon de la tago kaj sendu al konektataj uzantoj"}. {"Set message of the day on all hosts and send to online users","Enmetu mesaĝon de la tago je ĉiu gastigo kaj sendu al konektataj uzantoj"}. @@ -329,81 +288,50 @@ {"Show Integral Table","Montru integran tabelon"}. {"Show Ordinary Table","Montru ordinaran tabelon"}. {"Shut Down Service","Haltigu Servon"}. -{"~s invites you to the room ~s","~s invitas vin al la babilejo ~s"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Kelkaj Ĵabber-klientoj povas memori vian pasvorton je via komputilo. Nur uzu tiun eblon se vi fidas ke via komputilo estas sekura."}. {"Specify the access model","Specifu atingo-modelon"}. {"Specify the event message type","Specifu tipo de event-mesaĝo"}. {"Specify the publisher model","Enmetu publikadan modelon"}. -{"~s's Offline Messages Queue","Mesaĝo-atendovico de ~s"}. -{"Start Modules at ","Startu modulojn je "}. -{"Start Modules","Startu Modulojn"}. -{"Start","Startu"}. -{"Statistics of ~p","Statistikoj de ~p"}. -{"Statistics","Statistikoj"}. -{"Stop","Haltigu"}. -{"Stop Modules at ","Haltigu modulojn je "}. -{"Stop Modules","Haltigu Modulojn"}. {"Stopped Nodes","Neaktivaj Nodoj"}. -{"Storage Type","Konserv-tipo"}. {"Store binary backup:","Konservu duuman sekurkopion:"}. {"Store plain text backup:","Skribu sekurkopion en plata tekstdosiero"}. {"Subject","Temo"}. -{"Submit","Sendu"}. {"Submitted","Sendita"}. {"Subscriber Address","Abonanta adreso"}. -{"Subscription","Abono"}. +{"Subscribers may publish","Abonantoj rajtas publici"}. {"Sunday","Dimanĉo"}. {"That nickname is already in use by another occupant","Tiu kaŝnomo jam estas uzata de alia partoprenanto"}. {"That nickname is registered by another person","Kaŝnomo estas registrita de alia persono"}. -{"The CAPTCHA is valid.","La CAPTCHA ĝustas"}. +{"The CAPTCHA is valid.","La CAPTCHA ĝustas."}. {"The CAPTCHA verification has failed","La CAPTCHA-kontrolado malsukcesis"}. +{"The captcha you entered is wrong","La CAPTCHA enigita de vi malĝustas"}. {"The collections with which a node is affiliated","Aro kun kiu nodo estas filigita"}. -{"the password is","la pasvorto estas"}. {"The password is too weak","La pasvorto estas ne sufiĉe forta"}. -{"The password of your Jabber account was successfully changed.","La pasvorto de via Ĵabber-konto estas sukcese ŝanĝata."}. -{"There was an error changing the password: ","Estis eraro dum ŝanĝi de la pasvortro:"}. +{"the password is","la pasvorto estas"}. +{"The query is only allowed from local users","La informpeto estas permesita nur de lokaj uzantoj"}. +{"The query must not contain elements","La informpeto devas ne enhavi elementojn "}. {"There was an error creating the account: ","Estis eraro dum kreado de la konto:"}. {"There was an error deleting the account: ","Estis eraro dum forigado de la konto:"}. -{"This IP address is blacklisted in ~s","Ĉi tiu IP-adreso estas barata in ~s"}. -{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Uskleco ne signifas: macbeth estas la sama ol MacBeth kaj Macbeth."}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Jena paĝo ebligas kreadon de Ĵabber-konto je ĉi-Ĵabber-servilo. Via JID (Ĵabber-IDentigilo) estos ĉi-tiel: uzantnomo@servilo. Bonvolu legu bone la instrukciojn por korekta enmetigo de la kampoj. "}. -{"This page allows to unregister a Jabber account in this Jabber server.","Jena pagxo ebligas malregistri Jxabber-konton je ĉi-servilo."}. +{"This room is not anonymous","Ĉi tiu babilejo ne estas anonima"}. {"Thursday","Ĵaŭdo"}. {"Time delay","Prokrasto"}. -{"Time","Tempo"}. -{"To","Ĝis"}. {"Too many CAPTCHA requests","Tro multaj CAPTCHA-petoj"}. {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Tro da malsukcesaj aŭtentprovoj (~p) de ĉi tiu IP-adreso (~s). La adreso estos malbarata je ~s UTC."}. {"Too many unacked stanzas","Tro da neagnoskitaj stancoj"}. -{"To ~s","Al ~s"}. -{"Total rooms","Babilejoj"}. {"Traffic rate limit is exceeded","Trafikrapida limigo superita"}. -{"Transactions Aborted:","Transakcioj nuligitaj"}. -{"Transactions Committed:","Transakcioj enmetitaj"}. -{"Transactions Logged:","Transakcioj protokolitaj"}. -{"Transactions Restarted:","Transakcioj restartitaj"}. {"Tuesday","Mardo"}. {"Unable to generate a CAPTCHA","Ne eblis krei CAPTCHA"}. {"Unauthorized","Nepermesita"}. -{"Unregister a Jabber account","Malregistru Ĵabber-konton"}. {"Unregister","Malregistru"}. -{"Update","Ĝisdatigu"}. {"Update message of the day (don't send)","Ŝanĝu mesaĝon de la tago (ne sendu)"}. {"Update message of the day on all hosts (don't send)","Ŝanĝu mesaĝon de la tago je ĉiu gastigo (ne sendu)"}. -{"Update ~p","Ĝisdatigu ~p-n"}. -{"Update plan","Ĝisdatigo-plano"}. -{"Update script","Ĝisdatigo-skripto"}. -{"Uptime:","Daŭro de funkciado"}. -{"Use of STARTTLS required","Uzo de STARTTLS bezonata"}. +{"URL for Archived Discussion Logs","Retpaĝa adreso de Enarkivigitaj Diskutprotokoloj"}. {"User JID","Uzant-JID"}. {"User Management","Uzanto-administrado"}. {"Username:","Uzantnomo"}. {"Users are not allowed to register accounts so quickly","Ne estas permesata al uzantoj registri tiel rapide"}. {"Users Last Activity","Lasta aktiveco de uzanto"}. {"Users","Uzantoj"}. -{"User ~s","Uzanto ~s"}. {"User","Uzanto"}. -{"Validate","Validigu"}. {"vCard User Search","Serĉado de vizitkartoj"}. {"Virtual Hosts","Virtual-gastigoj"}. {"Visitors are not allowed to change their nicknames in this room","Ne estas permesata al vizitantoj ŝanĝi siajn kaŝnomojn en ĉi tiu ĉambro"}. @@ -413,16 +341,13 @@ {"Wednesday","Merkredo"}. {"When to send the last published item","Kiam sendi la laste publicitan eron"}. {"Whether to allow subscriptions","Ĉu permesi aboni"}. -{"You can later change your password using a Jabber client.","Poste vi povas ŝanĝi vian pasvorton per Ĵabber-kliento."}. +{"Wrong xmlns","Malĝusta XML-nomspaco (xmlns)"}. +{"XMPP Account Registration","Registrado de XMPP-Konto"}. {"You have been banned from this room","Vi estas malpermesata en ĉi tiu babilejo"}. {"You must fill in field \"Nickname\" in the form","Vi devas kompletigi la \"Kaŝnomo\" kampon"}. {"You need a client that supports x:data and CAPTCHA to register","Vi bezonas klienton subtenante x:data-funkcio kaj CAPTCHA por registri kaŝnomon"}. {"You need a client that supports x:data to register the nickname","Vi bezonas klienton subtenante x:data-funkcio por registri kaŝnomon"}. -{"You need an x:data capable client to configure mod_irc settings","Vi bezonas klienton kun x:data-funkcio por agordi mod_irc"}. -{"You need an x:data capable client to configure room","Vi bezonas klienton kun x:data-funkcio por agordi la babilejon"}. {"You need an x:data capable client to search","Vi bezonas klienton kun x:data-funkcio por serĉado"}. {"Your active privacy list has denied the routing of this stanza.","Via aktiva privatec-listo malpermesas enkursigi ĉi-tiun pakaĵon"}. {"Your contact offline message queue is full. The message has been discarded.","Mesaĝo-atendovico de la senkonekta kontakto estas plena. La mesaĝo estas forĵetita"}. -{"Your Jabber account was successfully created.","Via Ĵabber-konto estis sukcese kreata."}. -{"Your Jabber account was successfully deleted.","Via Ĵabber-konto estas sukcese forigita."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","Viaj mesaĝoj al ~s estas blokata. Por malbloki ilin, iru al ~s"}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Viaj mesaĝoj al ~s estas blokata. Por malbloki ilin, iru al ~s"}. diff --git a/priv/msgs/eo.po b/priv/msgs/eo.po deleted file mode 100644 index 0662768fc..000000000 --- a/priv/msgs/eo.po +++ /dev/null @@ -1,1938 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: 2.1.0-alpha\n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: Andreas van Cranenburgh \n" -"Language-Team: \n" -"Language: eo\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Esperanto\n" -"X-Generator: Poedit 1.6.10\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "Uzo de STARTTLS bezonata" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Neniu risurco donita" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Anstataŭigita je nova konekto" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "estas forpelita" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "Via aktiva privatec-listo malpermesas enkursigi ĉi-tiun pakaĵon" - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "Tro da neagnoskitaj stancoj" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Enmetu montrita teksto" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "Viaj mesaĝoj al ~s estas blokata. Por malbloki ilin, iru al ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "Se vi ne vidas la CAPTCHA-imagon jene, vizitu la teksaĵ-paĝon." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "CAPTCHA teksaĵ-paĝo" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "La CAPTCHA ĝustas" - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Uzanto" - -#: ejabberd_oauth.erl:256 -#, fuzzy -msgid "Server" -msgstr "Servilo:" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Pasvorto" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "Nepermesita" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "ejabberd Teksaĵa Administro" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Administro" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Atingokontrol-listoj" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Sendita" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Malĝusta formo" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Sendu" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "Kruda" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Forigu elektata(j)n" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Atingo-reguloj" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "Agordo de atingo-reguloj de ~s" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "Virtual-gastigoj" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Uzantoj" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "Konektataj Uzantoj" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Lasta aktiveco de uzanto" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Periodo: " - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "Lasta monato" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "Lasta jaro" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "Ĉiu aktiveco" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Montru ordinaran tabelon" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Montru integran tabelon" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Statistikoj" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "Ne trovita" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Nodo ne trovita" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Aldonu novan" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Gastigo" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Registritaj uzantoj" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Aldonu Uzanton" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Liverontaj mesaĝoj" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Lasta aktiveco" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Neniam" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Konektata" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Registritaj uzantoj:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Konektataj uzantoj:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Elirantaj s-al-s-konektoj:" - -#: ejabberd_web_admin.erl:1559 -#, fuzzy -msgid "Incoming s2s Connections:" -msgstr "Elirantaj s-al-s-konektoj:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Nenio" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Ŝanĝu pasvorton" - -#: ejabberd_web_admin.erl:1673 -msgid "User ~s" -msgstr "Uzanto ~s" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Konektataj risurcoj:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Pasvorto:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Forigu uzanton" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Neniu datumo" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Nodoj" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Funkciantaj Nodoj" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Neaktivaj Nodoj" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -msgid "Node ~p" -msgstr "Nodo ~p" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "Datumbazo" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Faru Sekurkopion" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Atentataj pordoj" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Ĝisdatigu" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Restartu" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Haltigu" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Moduloj" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "Eraro de RPC-alvoko" - -#: ejabberd_web_admin.erl:1917 -msgid "Database Tables at ~p" -msgstr "Datumbaz-tabeloj je ~p" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Nomo" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Konserv-tipo" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Eroj" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Memoro" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Eraro" - -#: ejabberd_web_admin.erl:1955 -msgid "Backup of ~p" -msgstr "Sekurkopio de ~p" - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Rimarku ke ĉi tiuj elektebloj nur sekurkopias la propran Mnesia-datumbazon. " -"Se vi uzas la ODBC-modulon, vi ankaŭ devas sekurkopii tiujn SQL-datumbazoj " -"aparte." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Konservu duuman sekurkopion:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "Bone" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Restaŭrigu duuman sekurkopion tuj:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "Restaŭrigu duuman sekurkopion post sekvonta ejabberd-restarto" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Skribu sekurkopion en plata tekstdosiero" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Restaŭrigu sekurkopion el plata tekstdosiero tuj" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "Importu uzanto-datumojn de PIEFXIS dosiero (XEP-0227):" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" -"Eksportu datumojn de ĉiuj uzantoj en servilo al PIEFXIS dosieroj (XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "Eksportu datumoj de uzantoj en gastigo al PIEFXIS dosieroj (XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "Eksportu ĉiuj tabeloj kiel SQL-informmendo al dosierujo:" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Importu uzantojn de jabberd14-uzantdosieroj" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Importu uzantojn de jabberd14-uzantdosieroj" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Atentataj pordoj je " - -#: ejabberd_web_admin.erl:2144 -msgid "Modules at ~p" -msgstr "Moduloj je ~p" - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "Statistikoj de ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Daŭro de funkciado" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "CPU-tempo" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Transakcioj enmetitaj" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Transakcioj nuligitaj" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Transakcioj restartitaj" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Transakcioj protokolitaj" - -#: ejabberd_web_admin.erl:2243 -msgid "Update ~p" -msgstr "Ĝisdatigu ~p-n" - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "Ĝisdatigo-plano" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Ĝisdatigitaj moduloj" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Ĝisdatigo-skripto" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Bazanivela ĝisdatigo-skripto" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Skript-kontrolo" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Pordo" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Protokolo" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Modulo" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Elektebloj" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Startu" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Ordonoj" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Sondaĵo" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Resondaĵo" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "Ĉu vere forigi mesaĝon de la tago?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Temo" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Teksto de mesaĝo" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "Neniu teksto donita por anonc-mesaĝo" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Anoncoj" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Sendu anoncon al ĉiu uzanto" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Sendu anoncon al ĉiu uzanto de ĉiu gastigo" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Sendu anoncon al ĉiu konektata uzanto" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "Sendu anoncon al ĉiu konektata uzanto de ĉiu gastigo" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Enmetu mesaĝon de la tago kaj sendu al konektataj uzantoj" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" -"Enmetu mesaĝon de la tago je ĉiu gastigo kaj sendu al konektataj uzantoj" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Ŝanĝu mesaĝon de la tago (ne sendu)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "Ŝanĝu mesaĝon de la tago je ĉiu gastigo (ne sendu)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Forigu mesaĝo de la tago" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Forigu mesaĝo de la tago je ĉiu gastigo" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Agordo" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Startu Modulojn" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Haltigu Modulojn" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Restaŭru" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Skribu en plata tekst-dosiero" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Importu dosieron" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Importu dosierujo" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Restartu Servon" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Haltigu Servon" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Forigu Uzanton" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Haltigu Uzant-seancon" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Montru pasvorton de uzanto" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Ŝanĝu pasvorton de uzanto" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "Montru tempon de lasta ensaluto" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Montru statistikojn de uzanto" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Montru nombron de registritaj uzantoj" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Montru nombron de konektataj uzantoj" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Uzanto-administrado" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Ĉiuj Uzantoj" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Elirantaj s-al-s-konektoj" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Mastrumado de sekurkopioj" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Importu uzantojn de jabberd14-uzantdosieroj" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "Al ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "De ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Agordo de datumbaz-tabeloj je " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Elektu konserv-tipon de tabeloj" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Nur disk-kopio" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "RAM- kaj disk-kopio" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "RAM-kopio" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "Fora kopio" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Haltigu modulojn je " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Elektu modulojn por fini" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Startu modulojn je " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Enmetu liston de {Modulo, [Elektebloj]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Listo de moduloj por starti" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Faru sekurkopion je " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Enmetu vojon por sekurkopio" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Voje de dosiero" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Restaŭrigu de dosiero el " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Skribu sekurkopion en plata teksto al " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Enmetu vojon al plata teksto" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Importu uzanton de dosiero el " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Enmetu vojon al jabberd14-uzantdosiero" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Importu uzantojn de dosierujo ĉe " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Enmetu vojon al jabberd14-uzantdosierujo" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Vojo al dosierujo" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "Prokrasto" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Agordo de atingokontrolo" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Atingokontrol-listoj" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Agordo de atingo" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Atingo-reguloj" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Pasvortkontrolo" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Nombro de registritaj uzantoj" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Nombro de konektataj uzantoj" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Lasta ensaluto" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "Kontaktlist-grando" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "IP-adresoj" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Risurcoj" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Mastrumado de " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Ago je uzanto" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Redaktu atributojn" - -#: mod_fail2ban.erl:95 -msgid "" -"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." - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "" - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "" - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "Ĉi tiu IP-adreso estas barata in ~s" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "Atingo rifuzita de serv-politiko" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "IRC-transportilo" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "ejabberd IRC-modulo" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "Vi bezonas klienton kun x:data-funkcio por agordi mod_irc" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Registraĵo en mod_irc de " - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Enmetu uzantnomon,j enkodigojn, pordojn kaj pasvortojn kiujn vi volas uzi " -"por konektoj al IRC-serviloj" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "IRC-kaŝnomo" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Se vi volas specifi diversajn pordojn, pasvortojn, enkodigojn por IRC-" -"serviloj, kompletigu la jenan liston kun la formo '{\"irc-servilo\", " -"\"enkodigo\", porto, \"pasvorto\"}'. Se ne specifita, ĉi tiu servilo uzas la " -"enkodigo \"~s\", porto ~p, malplena pasvorto." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Ekzemplo: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"sekreto\"}, {\"vendetta." -"fef.net\", \"iso8859-1\", 7000}, {\"irc.iutestservilo.net\", \"utf-8\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Konekto-parametroj" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "Eniras IRC-babilejon" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "IRC-babilejo (ne aldonu #-prefikson)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "IRC-servilo" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Eniru IRC-babilejon jen" - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Eniru IRC-babilejon en ĉi Jabber-ID: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "IRC agordoj" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Enmetu uzantnomon kaj enkodigojn kiujn vi volas uzi por konektoj al IRC-" -"serviloj. Elektu 'Sekvonto' por ekhavi pliajn kampojn. Elektu 'Kompletigu' " -"por savi agordojn." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "IRC-uzantnomo" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Pasvorto ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Pordo ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Enkodigo por servilo ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Servilo ~b" - -#: mod_mam.erl:541 -#, fuzzy -msgid "Only members may query archives of this room" -msgstr "Nur moderigantoj rajtas ŝanĝi la temon en ĉi tiu babilejo" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "Nur servo-administrantoj rajtas sendi serv-mesaĝojn" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "Ĉi tiu serv-politiko ne permesas babilejo-kreadon" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "Babilejo ne ekzistas" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Babilejoj" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "Vi bezonas klienton subtenante x:data-funkcio por registri kaŝnomon" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Kaŝnomo-registrado je " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Enmetu kaŝnomon kiun vi volas registri" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Kaŝnomo" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "Kaŝnomo estas registrita de alia persono" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Vi devas kompletigi la \"Kaŝnomo\" kampon" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "ejabberd MUC-modulo" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "Grupbabilado" - -#: mod_muc_admin.erl:249 -msgid "Total rooms" -msgstr "Babilejoj" - -#: mod_muc_admin.erl:250 -msgid "Permanent rooms" -msgstr "Permanentaj babilejoj" - -#: mod_muc_admin.erl:251 -msgid "Registered nicknames" -msgstr "Registritaj uzantnomoj" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "Listo de babilejoj" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "Agordo de babilejo ŝanĝita" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "eniras la babilejo" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "eliras la babilejo" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "estas forbarita" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "estas forpelita pro aparteneca ŝanĝo" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "estas forpelita ĉar la babilejo fariĝis sole por membroj" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "estas forpelita pro sistem-haltigo" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "nun nomiĝas" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " ŝanĝis la temon al: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "Babilejo kreita" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "Babilejo neniigita" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "Babilejo lanĉita" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "Babilejo haltita" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "Lundo" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "Mardo" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "Merkredo" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "Ĵaŭdo" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "Vendredo" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "Sabato" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "Dimanĉo" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "Januaro" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "Februaro" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "Marĉo" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "Aprilo" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "Majo" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "Junio" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "Julio" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "Aŭgusto" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "Septembro" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "Oktobro" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "Novembro" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "Decembro" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Babilejo-agordo" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "Nombro de ĉeestantoj" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "Trafikrapida limigo superita" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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 "" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "Nur partoprenantoj rajtas sendi privatajn mesaĝojn al la babilejo" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "Bonvolu atendi iomete antaŭ ol sendi plian voĉ-peton" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "Voĉ-petoj estas malebligita en jena babilejo" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "Malsukcesis ekstrakti JID-on de via voĉ-pet-aprobo" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "Nur moderigantoj povas aprobi voĉ-petojn" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Malĝusta mesaĝo-tipo" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "Malpermesas sendi mesaĝojn de tipo \"groupchat\"" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "Ricevanto ne ĉeestas en la babilejo " - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "Ne estas permesata sendi privatajn mesaĝojn" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Nur partoprenantoj rajtas sendi mesaĝojn al la babilejo" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "Nur partoprenantoj rajtas sendi informmendojn al la babilejoj" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "Malpermesas informmendoj al partoprenantoj en ĉi tiu babilejo" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "" -"Nur moderigantoj kaj partoprenantoj rajtas ŝanĝi la temon en ĉi tiu babilejo" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Nur moderigantoj rajtas ŝanĝi la temon en ĉi tiu babilejo" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "Vizitantoj ne rajtas sendi mesaĝojn al ĉiuj partoprenantoj" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "" -"Ne estas permesata al vizitantoj ŝanĝi siajn kaŝnomojn en ĉi tiu ĉambro" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "Tiu kaŝnomo jam estas uzata de alia partoprenanto" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Vi estas malpermesata en ĉi tiu babilejo" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "Membreco estas bezonata por eniri ĉi tiun babilejon" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "Pasvorto estas bezonata por eniri ĉi tiun babilejon" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "Tro multaj CAPTCHA-petoj" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "Ne eblis krei CAPTCHA" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Nekorekta pasvorto" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "Administrantaj rajtoj bezonata" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "Moderantaj rajtoj bezonata" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "Jabber ID ~s estas nevalida" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "Kaŝnomo ~s ne ekzistas en la babilejo" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Nevalida aparteneco: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Nevalida rolo: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "Mastraj rajtoj bezonata" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "Agordo de babilejo ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Babilejo-nomo" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "Babilejo-priskribo" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Farigu babilejon daŭra" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Farigu babilejon publike trovebla" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "Farigu partoprento-liston publika" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Farigu babilejon protektata per pasvorto" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Limigo de nombro de partoprenantoj" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Neniu limigo" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Montru verajn Jabber ID-ojn al" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "moderantoj sole" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "iu ajn" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "" - -#: mod_muc_room.erl:3486 -#, fuzzy -msgid "Moderator" -msgstr "moderantoj sole" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Farigu babilejon sole por membroj" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Farigu babilejon moderigata" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Kutime farigu uzantojn kiel partpoprenantoj" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Permesu uzantojn ŝanĝi la temon" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Permesu uzantojn sendi privatajn mesaĝojn" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "Permesu uzantojn sendi privatajn mesaĝojn al" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "neniu" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Permesu uzantojn informpeti aliajn uzantojn" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Permesu uzantojn sendi invitojn" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "Permesu al vizitantoj sendi statmesaĝon en ĉeest-sciigoj" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Permesu al vizitantoj ŝanĝi siajn kaŝnomojn" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "Permesu uzantojn sendi voĉ-petojn" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "Minimuma intervalo inter voĉ-petoj (je sekundoj)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "Farigu babilejon protektata per CAPTCHA" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "Ŝaltu mesaĝo-arkivo" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "Esceptu Ĵabber-identigilojn je CAPTCHA-defio" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Ŝaltu protokoladon" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "Vi bezonas klienton kun x:data-funkcio por agordi la babilejon" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Nombro de ĉeestantoj" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "privata, " - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "Voĉ-peto" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "Ĉu aprobu, aŭ malaprobu la voĉ-peton." - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "Uzant-JID" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "Koncedu voĉon al ĉi-persono?" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s invitas vin al la babilejo ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "la pasvorto estas" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "Multicast" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "ejabberd Multicast-servo" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "" -"Mesaĝo-atendovico de la senkonekta kontakto estas plena. La mesaĝo estas " -"forĵetita" - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "Mesaĝo-atendovico de ~s" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Tempo" - -#: mod_offline.erl:812 -msgid "From" -msgstr "De" - -#: mod_offline.erl:813 -msgid "To" -msgstr "Ĝis" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Pakaĵo" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Liverontaj mesaĝoj" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "Forigu ĉiujn liverontajn mesaĝojn" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "ejabberd SOCKS5 Bajtfluo modulo" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Public-Abonado" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "ejabberd Public-Abonada modulo" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "PubAbo abonpeto" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Elektu ĉu permesi la abonon de ĉi tiu ento" - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "Nodo ID" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Abonanta adreso" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "Ĉu permesi ĉi tiun Jabber ID aboni al la jena PubAbo-nodo" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Liveru aĵojn de event-sciigoj" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Liveru event-sciigojn" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Sciigu abonantoj kiam la agordo de la nodo ŝanĝas" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Sciigu abonantoj kiam la nodo estas forigita" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Sciigu abonantoj kiam eroj estas forigita de la nodo" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Savu erojn en konservado" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "Kromnomo por ĉi tiu nodo" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Maksimuma kiomo de eroj en konservado" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Ĉu permesi aboni" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Specifu atingo-modelon" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Kontaktlist-grupoj kiuj rajtas aboni" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Enmetu publikadan modelon" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "Forigu ĉiujn erojn kiam la rilata publikanto malkonektiĝas" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "Specifu tipo de event-mesaĝo" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Maksimuma aĵo-grando je bajtoj" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "Kiam sendi la laste publicitan eron" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Nur liveru sciigojn al konektataj uzantoj" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "Aro kun kiu nodo estas filigita" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "La CAPTCHA-kontrolado malsukcesis" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "" -"Vi bezonas klienton subtenante x:data-funkcio kaj CAPTCHA por registri " -"kaŝnomon" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "Elektu uzantnomon kaj pasvorton por registri je ĉi tiu servilo" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "La pasvorto estas ne sufiĉe forta" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "Ne estas permesata al uzantoj registri tiel rapide" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "Via Ĵabber-konto estis sukcese kreata." - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "Estis eraro dum kreado de la konto:" - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "Via Ĵabber-konto estas sukcese forigita." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "Estis eraro dum forigado de la konto:" - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "La pasvorto de via Ĵabber-konto estas sukcese ŝanĝata." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "Estis eraro dum ŝanĝi de la pasvortro:" - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Ĵabber-konto registrado" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Registru Ĵabber-konton" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Malregistru Ĵabber-konton" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"Jena paĝo ebligas kreadon de Ĵabber-konto je ĉi-Ĵabber-servilo. Via JID " -"(Ĵabber-IDentigilo) estos ĉi-tiel: uzantnomo@servilo. Bonvolu legu bone la " -"instrukciojn por korekta enmetigo de la kampoj. " - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "Uzantnomo" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "Uskleco ne signifas: macbeth estas la sama ol MacBeth kaj Macbeth." - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "Karaktroj ne permesata:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "Servilo:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" -"Ne donu vian pasvorton al iun ajn, eĉ ne al la administrantoj de la Ĵabber-" -"servilo." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "Poste vi povas ŝanĝi vian pasvorton per Ĵabber-kliento." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Kelkaj Ĵabber-klientoj povas memori vian pasvorton je via komputilo. Nur uzu " -"tiun eblon se vi fidas ke via komputilo estas sekura." - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"Memoru vian pasvorton, aŭ skribu ĝin sur papero formetata je sekura loko. Je " -"Ĵabber ne ekzistas aŭtomata metodo por reakiri vian pasvorton se vi forgesas " -"ĝin." - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "Pasvortkontrolo:" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "Registru" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "Malnova Pasvorto:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "Nova Pasvorto:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "Jena pagxo ebligas malregistri Jxabber-konton je ĉi-servilo." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "Malregistru" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Abono" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Atendanta" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Grupoj" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Validigu" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Forigu" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Kontaktlisto de " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Aldonu Jabber ID" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Kontaktlisto" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Komuna Kontaktlist-grupo" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Nomo:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Priskribo:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Membroj:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Montrataj grupoj:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Grupo " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Erlang-a Jabber-Servilo" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Naskiĝtago" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Urbo" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "Lando" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "Retpoŝto" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Lasta Nomo" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Kompletigu la formon por serĉi rekonata Jabber-uzanto (Aldonu * je la fino " -"de la kampo por rekoni subĉenon" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Plena Nomo" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Meza Nomo" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Organiz-nomo" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Organiz-parto" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Serĉu uzantojn en " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "Vi bezonas klienton kun x:data-funkcio por serĉado" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "Serĉado de vizitkartoj" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "ejabberd vCard-modulo" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Serĉ-rezultoj de " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "Kompletigu la formon por serĉi rekonata Jabber-uzanto" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Elirantaj s-al-s-serviloj" - -#~ msgid "Delete" -#~ msgstr "Forigu" - -#~ msgid "This room is not anonymous" -#~ msgstr "Ĉi tiu babilejo ne estas anonima" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "" -#~ "Ĉi tiu partoprenanta estas forpelata de la babilejo pro sendado de erar-" -#~ "mesaĝo" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Ĉi tiu partoprenanto estas forpelata de la babilejo pro sendo de erar-" -#~ "mesaĝo al alia partoprenanto" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "" -#~ "Ĉi tiu partoprenanto estas forpelata de la babilejo pro sendo de erar-" -#~ "ĉeesto" - -#, fuzzy -#~ msgid "Captcha test failed" -#~ msgstr "La aŭtomata Turingtesto estas ĝusta" - -#~ msgid "Encodings" -#~ msgstr "Enkodigoj" - -#~ msgid "(Raw)" -#~ msgstr "(Kruda)" - -#~ msgid "Specified nickname is already registered" -#~ msgstr "Donita kaŝnomo jam estas registrita" - -#~ msgid "Size" -#~ msgstr "Grando" - -#~ msgid "You must fill in field \"nick\" in the form" -#~ msgstr "Vi devas enmeti kampon \"kaŝnomo\"" diff --git a/priv/msgs/es.msg b/priv/msgs/es.msg index 0d585ec51..a914a43a1 100644 --- a/priv/msgs/es.msg +++ b/priv/msgs/es.msg @@ -1,22 +1,30 @@ -%% -*- coding: latin-1 -*- -{"Accept","Aceptar"}. -{"Access Configuration","Configuración de accesos"}. -{"Access Control List Configuration","Configuración de la Lista de Control de Acceso"}. -{"Access control lists","Listas de Control de Acceso"}. -{"Access Control Lists","Listas de Control de Acceso"}. -{"Access denied by service policy","Acceso denegado por la política del servicio"}. -{"Access rules","Reglas de acceso"}. -{"Access Rules","Reglas de Acceso"}. -{"Action on user","Acción en el usuario"}. -{"Add Jabber ID","Añadir Jabber ID"}. -{"Add New","Añadir nuevo"}. -{"Add User","Añadir usuario"}. -{"Administration","Administración"}. -{"Administration of ","Administración de "}. -{"Administrator privileges required","Se necesita privilegios de administrador"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" (Add * to the end of field to match substring)"," (Añade * al final del campo para buscar subcadenas)"}. +{" has set the subject to: "," ha puesto el asunto: "}. +{"# participants","# participantes"}. +{"A description of the node","Una descripción del nodo"}. {"A friendly name for the node","Un nombre sencillo para el nodo"}. +{"A password is required to enter this room","(Añade * al final del campo para buscar subcadenas)"}. +{"A Web Page","Una página web"}. +{"Accept","Aceptar"}. +{"Access denied by service policy","Acceso denegado por la política del servicio"}. +{"Access model","Modelo de Acceso"}. +{"Account doesn't exist","La cuenta no existe"}. +{"Action on user","Acción en el usuario"}. +{"Add a hat to a user","Añade un sombrero a un usuario"}. +{"Add User","Añadir usuario"}. +{"Administration of ","Administración de "}. +{"Administration","Administración"}. +{"Administrator privileges required","Se necesita privilegios de administrador"}. {"All activity","Toda la actividad"}. +{"All Users","Todos los usuarios"}. +{"Allow subscription","Permitir la subscripción"}. {"Allow this Jabber ID to subscribe to this pubsub node?","¿Deseas permitir a este Jabber ID que se subscriba a este nodo PubSub?"}. +{"Allow this person to register with the room?","¿Permitir a esta persona que se registre en la sala?"}. {"Allow users to change the subject","Permitir a los usuarios cambiar el asunto"}. {"Allow users to query other users","Permitir a los usuarios consultar a otros usuarios"}. {"Allow users to send invites","Permitir a los usuarios enviar invitaciones"}. @@ -25,21 +33,49 @@ {"Allow visitors to send private messages to","Permitir a los visitantes enviar mensajes privados a"}. {"Allow visitors to send status text in presence updates","Permitir a los visitantes enviar texto de estado en las actualizaciones de presencia"}. {"Allow visitors to send voice requests","Permitir a los visitantes enviar peticiones de voz"}. -{"All Users","Todos los usuarios"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","Un grupo LDAP asociado que define la membresía a la sala; este debería ser un Nombre Distinguido de LDAP, de acuerdo con una definición de grupo específica de la implementación o de esta instalación."}. {"Announcements","Anuncios"}. -{"anyone","cualquiera"}. -{"A password is required to enter this room","Se necesita contraseña para entrar en esta sala"}. -{"April","abril"}. -{"August","agosto"}. -{"Backup","Guardar copia de seguridad"}. +{"Answer associated with a picture","Respuesta asociada con una imagen"}. +{"Answer associated with a video","Respuesta asociada con un video"}. +{"Answer associated with speech","Respuesta asociada con un audio"}. +{"Answer to a question","Responde a una pregunta"}. +{"Anyone in the specified roster group(s) may subscribe and retrieve items","Cualquiera que esté en el grupo(s) de contactos especificado puede suscribirse y recibir elementos"}. +{"Anyone may associate leaf nodes with the collection","Cualquiera puede asociar nodos hoja con la colección"}. +{"Anyone may publish","Cualquiera puede publicar"}. +{"Anyone may subscribe and retrieve items","Cualquiera puede suscribirse y recibir elementos"}. +{"Anyone with a presence subscription of both or from may subscribe and retrieve items","Cualquiera con una suscripción a la presencia de 'ambos' o 'de' puede suscribirse y recibir elementos"}. +{"Anyone with Voice","Cualquiera con Voz"}. +{"Anyone","Cualquiera"}. +{"API Commands","Comandos API"}. +{"April","Abril"}. +{"Arguments","Argumentos"}. +{"Attribute 'channel' is required for this request","El atributo 'channel' es necesario para esta petición"}. +{"Attribute 'id' is mandatory for MIX messages","El atributo 'id' es necesario para mensajes MIX"}. +{"Attribute 'jid' is not allowed here","El atributo 'jid' no está permitido aqui"}. +{"Attribute 'node' is not allowed here","El atributo 'node' no está permitido aqui"}. +{"Attribute 'to' of stanza that triggered challenge","Atributo 'to' del paquete que disparó la comprobación"}. +{"August","Agosto"}. +{"Automatic node creation is not enabled","La creación automática de nodo no está activada"}. {"Backup Management","Gestión de copia de seguridad"}. {"Backup of ~p","Copia de seguridad de ~p"}. {"Backup to File at ","Guardar copia de seguridad en fichero en "}. +{"Backup","Guardar copia de seguridad"}. {"Bad format","Mal formato"}. {"Birthday","Cumpleaños"}. +{"Both the username and the resource are required","Se requiere tanto el nombre de usuario como el recurso"}. +{"Bytestream already activated","Bytestream ya está activado"}. +{"Cannot remove active list","No se puede borrar la lista activa"}. +{"Cannot remove default list","No se puede borrar la lista por defecto"}. {"CAPTCHA web page","Página web de CAPTCHA"}. +{"Challenge ID","ID de la comprobación"}. {"Change Password","Cambiar contraseña"}. {"Change User Password","Cambiar contraseña de usuario"}. +{"Changing password is not allowed","No está permitido cambiar la contraseña"}. +{"Changing role/affiliation is not allowed","No está permitido cambiar el rol/afiliación"}. +{"Channel already exists","El canal ya existe"}. +{"Channel does not exist","El canal no existe"}. +{"Channel JID","JID del Canal"}. +{"Channels","Canales"}. {"Characters not allowed:","Caracteres no permitidos:"}. {"Chatroom configuration modified","Configuración de la sala modificada"}. {"Chatroom is created","Se ha creado la sala"}. @@ -48,93 +84,101 @@ {"Chatroom is stopped","Se ha detenido la sala"}. {"Chatrooms","Salas de charla"}. {"Choose a username and password to register with this server","Escoge un nombre de usuario y contraseña para registrarte en este servidor"}. -{"Choose modules to stop","Selecciona módulos a detener"}. {"Choose storage type of tables","Selecciona tipo de almacenamiento de las tablas"}. {"Choose whether to approve this entity's subscription.","Decidir si aprobar la subscripción de esta entidad."}. {"City","Ciudad"}. +{"Client acknowledged more stanzas than sent by server","El cliente ha reconocido más paquetes de los que el servidor ha enviado"}. +{"Clustering","Clustering"}. {"Commands","Comandos"}. {"Conference room does not exist","La sala de conferencias no existe"}. -{"Configuration","Configuración"}. {"Configuration of room ~s","Configuración para la sala ~s"}. -{"Connected Resources:","Recursos conectados:"}. -{"Connections parameters","Parámetros de conexiones"}. +{"Configuration","Configuración"}. +{"Contact Addresses (normally, room owner or owners)","Direcciones de contacto (normalmente la del dueño o dueños de la sala)"}. {"Country","País"}. -{"CPU Time:","Tiempo consumido de CPU:"}. -{"Database","Base de datos"}. -{"Database Tables at ~p","Tablas de la base de datos en ~p"}. +{"Current Discussion Topic","Tema de discusión actual"}. +{"Database failure","Error en la base de datos"}. {"Database Tables Configuration at ","Configuración de tablas de la base de datos en "}. -{"December","diciembre"}. +{"Database","Base de datos"}. +{"December","Diciembre"}. {"Default users as participants","Los usuarios son participantes por defecto"}. -{"Delete message of the day","Borrar mensaje del dia"}. {"Delete message of the day on all hosts","Borrar el mensaje del día en todos los dominios"}. -{"Delete Selected","Eliminar los seleccionados"}. +{"Delete message of the day","Borrar mensaje del dia"}. {"Delete User","Borrar usuario"}. {"Deliver event notifications","Entregar notificaciones de eventos"}. {"Deliver payloads with event notifications","Enviar contenidos junto con las notificaciones de eventos"}. -{"Description:","Descripción:"}. {"Disc only copy","Copia en disco solamente"}. -{"Displayed Groups:","Mostrar grupos:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","No le digas tu contraseña a nadie, ni siquiera a los administradores del servidor Jabber."}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","No le digas tu contraseña a nadie, ni siquiera a los administradores del servidor XMPP."}. {"Dump Backup to Text File at ","Exporta copia de seguridad a fichero de texto en "}. {"Dump to Text File","Exportar a fichero de texto"}. +{"Duplicated groups are not allowed by RFC6121","Los grupos duplicados no están permitidos por RFC6121"}. +{"Dynamically specify a replyto of the item publisher","Especificar dinámicamente como dirección de respuesta al publicador del elemento"}. {"Edit Properties","Editar propiedades"}. {"Either approve or decline the voice request.","Aprueba o rechaza la petición de voz."}. -{"ejabberd IRC module","Módulo de IRC para ejabberd"}. +{"ejabberd HTTP Upload service","Servicio HTTP Upload de ejabberd"}. {"ejabberd MUC module","Módulo de MUC para ejabberd"}. {"ejabberd Multicast service","Servicio Multicast de ejabberd"}. {"ejabberd Publish-Subscribe module","Módulo de Publicar-Subscribir de ejabberd"}. {"ejabberd SOCKS5 Bytestreams module","Módulo SOCKS5 Bytestreams para ejabberd"}. {"ejabberd vCard module","Módulo vCard para ejabberd"}. {"ejabberd Web Admin","ejabberd Web Admin"}. -{"Elements","Elementos"}. -{"Email","correo"}. -{"Empty Rooms","Salas vacías"}. +{"ejabberd","ejabberd"}. +{"Email Address","Dirección de correo electrónico"}. +{"Email","Correo electrónico"}. +{"Enable hats","Activar sombreros"}. {"Enable logging","Guardar históricos"}. {"Enable message archiving","Activar el almacenamiento de mensajes"}. -{"Encoding for server ~b","Codificación del servidor ~b"}. +{"Enabling push without 'node' attribute is not supported","No está soportado activar Push sin el atributo 'node'"}. {"End User Session","Cerrar sesión de usuario"}. -{"Enter list of {Module, [Options]}","Introduce lista de {módulo, [opciones]}"}. {"Enter nickname you want to register","Introduce el apodo que quieras registrar"}. {"Enter path to backup file","Introduce ruta al fichero de copia de seguridad"}. {"Enter path to jabberd14 spool dir","Introduce la ruta al directorio de jabberd14 spools"}. {"Enter path to jabberd14 spool file","Introduce ruta al fichero jabberd14 spool"}. {"Enter path to text file","Introduce ruta al fichero de texto"}. {"Enter the text you see","Teclea el texto que ves"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Introduce el nombre de usuario y codificaciones de carácteres que quieras usar al conectar en los servidores de IRC. Pulsa Siguiente para conseguir más campos en el formulario. Pulsa Completar para guardar las opciones."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Introduce el nombre de usuario, codificaciones de carácteres, puertos y contraseñas que quieras usar al conectar en los servidores de IRC"}. -{"Erlang Jabber Server","Servidor Jabber en Erlang"}. -{"Error","Error"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Ejemplo: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. +{"Erlang XMPP Server","Servidor XMPP en Erlang"}. {"Exclude Jabber IDs from CAPTCHA challenge","Excluir Jabber IDs de las pruebas de CAPTCHA"}. {"Export all tables as SQL queries to a file:","Exportar todas las tablas a un fichero SQL:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportar datos de todos los usuarios del servidor a ficheros PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Exportar datos de los usuarios de un dominio a ficheros PIEFXIS (XEP-0227):"}. +{"External component failure","Fallo en el componente externo"}. +{"External component timeout","Demasiado retraso (timeout) en el componente externo"}. +{"Failed to activate bytestream","Falló la activación de bytestream"}. {"Failed to extract JID from your voice request approval","Fallo al extraer el Jabber ID de tu aprobación de petición de voz"}. +{"Failed to map delegated namespace to external component","Falló el mapeo de espacio de nombres delegado al componente externo"}. +{"Failed to parse HTTP response","Falló la comprensión de la respuesta HTTP"}. +{"Failed to process option '~s'","Falló el procesado de la opción '~s'"}. {"Family Name","Apellido"}. -{"February","febrero"}. -{"Fill in fields to search for any matching Jabber User","Rellena campos para buscar usuarios Jabber que concuerden"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Rellena el formulario para buscar usuarios Jabber. Añade * al final de un campo para buscar subcadenas."}. -{"Friday","viernes"}. -{"From","De"}. -{"From ~s","De ~s"}. +{"FAQ Entry","Apunte en la FAQ"}. +{"February","Febrero"}. +{"File larger than ~w bytes","El fichero es más grande que ~w bytes"}. +{"Fill in the form to search for any matching XMPP User","Rellena campos para buscar usuarios XMPP que concuerden"}. +{"Friday","Viernes"}. +{"From ~ts","De ~ts"}. +{"Full List of Room Admins","Lista completa de administradores de la sala"}. +{"Full List of Room Owners","Lista completa de dueños de la sala"}. {"Full Name","Nombre completo"}. +{"Get List of Online Users","Ver lista de usuarios conectados"}. +{"Get List of Registered Users","Ver lista de usuarios registrados"}. {"Get Number of Online Users","Ver número de usuarios conectados"}. {"Get Number of Registered Users","Ver número de usuarios registrados"}. +{"Get Pending","Obtener pendientes"}. {"Get User Last Login Time","Ver fecha de la última conexión de usuario"}. -{"Get User Password","Ver contraseña de usuario"}. {"Get User Statistics","Ver estadísticas de usuario"}. +{"Given Name","Nombre de pila"}. {"Grant voice to this person?","¿Conceder voz a esta persona?"}. -{"Group ","Grupo "}. -{"Groups","Grupos"}. {"has been banned","ha sido bloqueado"}. -{"has been kicked because of an affiliation change","ha sido expulsado por un cambio de su afiliación"}. {"has been kicked because of a system shutdown","ha sido expulsado porque el sistema se va a detener"}. +{"has been kicked because of an affiliation change","ha sido expulsado por un cambio de su afiliación"}. {"has been kicked because the room has been changed to members-only","ha sido expulsado porque la sala es ahora solo para miembros"}. {"has been kicked","ha sido expulsado"}. -{" has set the subject to: "," ha puesto el asunto: "}. -{"Host","Dominio"}. +{"Hash of the vCard-temp avatar of this room","Hash del avatar vCard-temp de esta sala"}. +{"Hat title","Título del sombrero"}. +{"Hat URI","Dirección del sombrero"}. +{"Hats limit exceeded","Se ha excedido el límite de sombreros"}. +{"Host unknown","Dominio desconocido"}. +{"HTTP File Upload","Subir fichero por HTTP"}. +{"Idle connection","Conexión sin uso"}. {"If you don't see the CAPTCHA image here, visit the web page.","Si no ves la imagen CAPTCHA aquí, visita la página web."}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Si quieres especificar distintos codificaciones de carácteres, contraseñas o puertos para cada servidor IRC rellena esta lista con valores en el formato '{\"servidor irc\", \"codificación\", \"puerto\", \"contrasela\"}'. Este servicio usa por defecto la codificación \"~s\", puerto ~p, sin contraseña."}. {"Import Directory","Importar directorio"}. {"Import File","Importar fichero"}. {"Import user data from jabberd14 spool file:","Importar usuario de fichero spool de jabberd14:"}. @@ -143,44 +187,48 @@ {"Import users data from jabberd14 spool directory:","Importar usuarios del directorio spool de jabberd14:"}. {"Import Users from Dir at ","Importar usuarios desde el directorio en "}. {"Import Users From jabberd14 Spool Files","Importar usuarios de ficheros spool de jabberd-1.4"}. +{"Improper domain part of 'from' attribute","Parte de dominio impropia en el atributo 'from'"}. {"Improper message type","Tipo de mensaje incorrecto"}. -{"Incoming s2s Connections:","Conexiones S2S entrantes:"}. +{"Incorrect CAPTCHA submit","El CAPTCHA proporcionado es incorrecto"}. +{"Incorrect data form","Formulario de datos incorrecto"}. {"Incorrect password","Contraseña incorrecta"}. -{"Invalid affiliation: ~s","Afiliación no válida: ~s"}. -{"Invalid role: ~s","Rol no válido: ~s"}. +{"Incorrect value of 'action' attribute","Valor incorrecto del atributo 'action'"}. +{"Incorrect value of 'action' in data form","Valor incorrecto de 'action' en el formulario de datos"}. +{"Incorrect value of 'path' in data form","Valor incorrecto de 'path' en el formulario de datos"}. +{"Installed Modules:","Módulos Instalados:"}. +{"Install","Instalar"}. +{"Insufficient privilege","Privilegio insuficiente"}. +{"Internal server error","Error interno en el servidor"}. +{"Invalid 'from' attribute in forwarded message","Atributo 'from' no válido en el mensaje reenviado"}. +{"Invalid node name","Nombre de nodo no válido"}. +{"Invalid 'previd' value","Valor de 'previd' no válido"}. +{"Invitations are not allowed in this conference","Las invitaciones no están permitidas en esta sala"}. {"IP addresses","Direcciones IP"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","Canal IRC (no pongas el # del principio)"}. -{"IRC server","Servidor IRC"}. -{"IRC settings","Opciones de IRC"}. -{"IRC Transport","Transporte de IRC"}. -{"IRC username","Nombre de usuario en IRC"}. -{"IRC Username","Nombre de usuario en IRC"}. {"is now known as","se cambia el nombre a"}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","No está permitido enviar mensajes de error a la sala. Este participante (~s) ha enviado un mensaje de error (~s) y fue expulsado de la sala"}. -{"It is not allowed to send private messages","No está permitido enviar mensajes privados"}. {"It is not allowed to send private messages of type \"groupchat\"","No está permitido enviar mensajes privados del tipo \"groupchat\""}. {"It is not allowed to send private messages to the conference","Impedir el envio de mensajes privados a la sala"}. -{"Jabber Account Registration","Registro de Cuenta Jabber"}. {"Jabber ID","Jabber ID"}. -{"Jabber ID ~s is invalid","El Jabber ID ~s no es válido"}. -{"January","enero"}. -{"Join IRC channel","Entrar en canal IRC"}. +{"January","Enero"}. +{"JID normalization denied by service policy","Se ha denegado la normalización del JID por política del servicio"}. +{"JID normalization failed","Ha fallado la normalización del JID"}. +{"Joined MIX channels of ~ts","Canales MIX unidos de ~ts"}. +{"Joined MIX channels:","Canales MIX unidos:"}. {"joins the room","entra en la sala"}. -{"Join the IRC channel here.","Entrar en el canal de IRC aquí"}. -{"Join the IRC channel in this Jabber ID: ~s","Entra en el canal de IRC en esta dirección Jabber: ~s"}. -{"July","julio"}. -{"June","junio"}. +{"July","Julio"}. +{"June","Junio"}. +{"Just created","Recién creada"}. {"Last Activity","Última actividad"}. {"Last login","Última conexión"}. +{"Last message","Último mensaje"}. {"Last month","Último mes"}. {"Last year","Último año"}. +{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Los bits menos significativos del hash SHA-256 del texto deberían ser iguales a la etiqueta hexadecimal"}. {"leaves the room","sale de la sala"}. -{"Listened Ports at ","Puertos de escucha en "}. -{"Listened Ports","Puertos de escucha"}. -{"List of modules to start","Lista de módulos a iniciar"}. -{"List of rooms","Lista de salas"}. -{"Low level update script","Script de actualización a bajo nivel"}. +{"List of users with hats","Lista de usuarios con sombreros"}. +{"List users with hats","Listar usuarios con sombreros"}. +{"Logged Out","Desconectad@"}. +{"Logging","Histórico de mensajes"}. {"Make participants list public","La lista de participantes es pública"}. {"Make room CAPTCHA protected","Proteger la sala con CAPTCHA"}. {"Make room members-only","Sala sólo para miembros"}. @@ -188,253 +236,395 @@ {"Make room password protected","Proteger la sala con contraseña"}. {"Make room persistent","Sala permanente"}. {"Make room public searchable","Sala públicamente visible"}. -{"March","marzo"}. -{"Maximum Number of Occupants","Número máximo de ocupantes"}. -{"Max # of items to persist","Máximo # de elementos que persisten"}. +{"Malformed username","Nombre de usuario mal formado"}. +{"MAM preference modification denied by service policy","Se ha denegado modificar la preferencia MAM por política del servicio"}. +{"March","Marzo"}. +{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","Máximo # de elementos a persistir, o `max` para no especificar un límite, más que el máximo impuesto por el servidor"}. {"Max payload size in bytes","Máximo tamaño del contenido en bytes"}. -{"May","mayo"}. +{"Maximum file size","Tamaño máximo de fichero"}. +{"Maximum Number of History Messages Returned by Room","Máximo número de mensajes del historial devueltos por la sala"}. +{"Maximum number of items to persist","Máximo número de elementos que persisten"}. +{"Maximum Number of Occupants","Número máximo de ocupantes"}. +{"May","Mayo"}. {"Membership is required to enter this room","Necesitas ser miembro de esta sala para poder entrar"}. -{"Members:","Miembros:"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Memoriza tu contraseña, o apúntala en un papel en un lugar seguro. En Jabber no hay un método automatizado para recuperar la contraseña si la olvidas."}. -{"Memory","Memoria"}. +{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Memoriza tu contraseña, o apúntala en un papel en un lugar seguro. En XMPP no hay un método automatizado para recuperar la contraseña si la olvidas."}. +{"Mere Availability in XMPP (No Show Value)","Disponible en XMPP (sin valor de Mostrado)"}. {"Message body","Cuerpo del mensaje"}. +{"Message not found in forwarded payload","Mensaje no encontrado en el contenido reenviado"}. +{"Messages from strangers are rejected","Los mensajes de extraños son rechazados"}. +{"Messages of type headline","Mensajes de tipo titular"}. +{"Messages of type normal","Mensajes de tipo normal"}. {"Middle Name","Segundo nombre"}. {"Minimum interval between voice requests (in seconds)","Intervalo mínimo entre peticiones de voz (en segundos)"}. -{"Moderator","Moderador"}. {"Moderator privileges required","Se necesita privilegios de moderador"}. -{"moderators only","solo moderadores"}. -{"Modified modules","Módulos modificados"}. -{"Module","Módulo"}. -{"Modules at ~p","Módulos en ~p"}. -{"Modules","Módulos"}. -{"Monday","lunes"}. -{"Multicast","Multicast"}. +{"Moderator","Moderador"}. +{"Moderators Only","Solo moderadores"}. +{"Module failed to handle the query","El módulo falló al gestionar la petición"}. +{"Monday","Lunes"}. +{"Multicast","Multidifusión"}. +{"Multiple elements are not allowed by RFC6121","No se permiten múltiples elementos en RFC6121"}. {"Multi-User Chat","Salas de Charla"}. -{"Name:","Nombre:"}. {"Name","Nombre"}. +{"Natural Language for Room Discussions","Idioma natural en las charlas de la sala"}. +{"Natural-Language Room Name","Nombre de la sala en el idioma natural de la sala"}. +{"Neither 'jid' nor 'nick' attribute found","No se encontraron los atributos 'jid' ni 'nick'"}. +{"Neither 'role' nor 'affiliation' attribute found","No se encontraron los atributos 'role' ni 'affiliation'"}. {"Never","Nunca"}. {"New Password:","Nueva contraseña:"}. -{"Nickname","Apodo"}. +{"Nickname can't be empty","El apodo no puede estar vacío"}. {"Nickname Registration at ","Registro del apodo en "}. {"Nickname ~s does not exist in the room","El apodo ~s no existe en la sala"}. -{"nobody","nadie"}. +{"Nickname","Apodo"}. +{"No address elements found","No se encontraron elementos de dirección ('address')"}. +{"No addresses element found","No se encontró elemento de direcciones ('addresses')"}. +{"No 'affiliation' attribute found","No se encontró el atributo 'affiliation'"}. +{"No available resource found","No se encontró un recurso conectado"}. {"No body provided for announce message","No se ha proporcionado cuerpo de mensaje para el anuncio"}. +{"No child elements found","No se encontraron subelementos"}. +{"No data form found","No se encontró formulario de datos"}. {"No Data","Sin datos"}. +{"No features available","No hay características disponibles"}. +{"No element found","No se ha encontrado elemento "}. +{"No hook has processed this command","Ningún evento ha procesado este comando"}. +{"No info about last activity found","No hay información respeto a la última actividad"}. +{"No 'item' element found","No se encontró el elemento 'item'"}. +{"No items found in this query","No se han encontrado elementos en esta petición"}. +{"No limit","Sin límite"}. +{"No module is handling this query","Ningún modulo está gestionando esta petición"}. +{"No node specified","No se ha especificado ningún nodo"}. +{"No 'password' found in data form","No se encontró 'password' en el formulario de datos"}. +{"No 'password' found in this query","No se encontró 'password' en esta petición"}. +{"No 'path' found in data form","No se encontró 'path' en este formulario de datos"}. +{"No pending subscriptions found","No se han encontrado suscripciones pendientes"}. +{"No privacy list with this name found","No se ha encontrado una lista de privacidad con este nombre"}. +{"No private data found in this query","No se ha encontrado ningún elemento de dato privado en esta petición"}. +{"No running node found","No se ha encontrado ningún nodo activo"}. +{"No services available","No hay servicios disponibles"}. +{"No statistics found for this item","No se han encontrado estadísticas para este elemento"}. +{"No 'to' attribute found in the invitation","No se encontró el atributo 'to' en la invitación"}. +{"Nobody","Nadie"}. +{"Node already exists","El nodo ya existe"}. {"Node ID","Nodo ID"}. +{"Node index not found","No se ha encontrado índice de nodo"}. {"Node not found","Nodo no encontrado"}. {"Node ~p","Nodo ~p"}. +{"Node","Nodo"}. +{"Nodeprep has failed","Ha fallado el procesado del nombre de nodo (nodeprep)"}. {"Nodes","Nodos"}. -{"No limit","Sin límite"}. {"None","Ninguno"}. -{"No resource provided","No se ha proporcionado recurso"}. +{"Not allowed","No permitido"}. {"Not Found","No encontrado"}. +{"Not subscribed","No suscrito"}. {"Notify subscribers when items are removed from the node","Notificar subscriptores cuando los elementos se borran del nodo"}. {"Notify subscribers when the node configuration changes","Notificar subscriptores cuando cambia la configuración del nodo"}. {"Notify subscribers when the node is deleted","Notificar subscriptores cuando el nodo se borra"}. -{"November","noviembre"}. +{"November","Noviembre"}. +{"Number of answers required","Número de respuestas necesarias"}. {"Number of occupants","Número de ocupantes"}. +{"Number of Offline Messages","Número de mensajes diferidos"}. {"Number of online users","Número de usuarios conectados"}. {"Number of registered users","Número de usuarios registrados"}. -{"October","octubre"}. -{"Offline Messages:","Mensajes diferidos:"}. -{"Offline Messages","Mensajes diferidos"}. +{"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","Número de segundos después de los cuales se purgarán elementos automáticamente, o `max` para no especificar un límite, más que el máximo impuesto por el servidor"}. +{"Occupants are allowed to invite others","Los ocupantes pueden invitar a otras personas"}. +{"Occupants are allowed to query others","Los ocupantes pueden enviar peticiones a otros"}. +{"Occupants May Change the Subject","Los ocupantes pueden cambiar el Asunto"}. +{"October","Octubre"}. {"OK","Aceptar"}. {"Old Password:","Contraseña antigua:"}. -{"Online","Conectado"}. -{"Online Users:","Usuarios conectados:"}. {"Online Users","Usuarios conectados"}. +{"Online","Conectado"}. +{"Only collection node owners may associate leaf nodes with the collection","Solo los dueños e la colección de nodos pueden asociar nodos hoja a la colección"}. {"Only deliver notifications to available users","Solo enviar notificaciones a los usuarios disponibles"}. +{"Only or tags are allowed","Solo se permiten las etiquetas o "}. +{"Only element is allowed in this query","Solo se permite el elemento en esta petición"}. {"Only members may query archives of this room","Solo miembros pueden consultar el archivo de mensajes de la sala"}. {"Only moderators and participants are allowed to change the subject in this room","Solo los moderadores y participantes pueden cambiar el asunto de esta sala"}. {"Only moderators are allowed to change the subject in this room","Solo los moderadores pueden cambiar el asunto de esta sala"}. +{"Only moderators are allowed to retract messages","Solo los moderadores pueden retractarse de los mensajes"}. {"Only moderators can approve voice requests","Solo los moderadores pueden aprobar peticiones de voz"}. {"Only occupants are allowed to send messages to the conference","Solo los ocupantes pueden enviar mensajes a la sala"}. {"Only occupants are allowed to send queries to the conference","Solo los ocupantes pueden enviar solicitudes a la sala"}. +{"Only publishers may publish","Solo los publicadores pueden publicar"}. {"Only service administrators are allowed to send service messages","Solo los administradores del servicio tienen permiso para enviar mensajes de servicio"}. -{"Options","Opciones"}. +{"Only those on a whitelist may associate leaf nodes with the collection","Solo quienes están en una lista blanca pueden asociar nodos hoja a la colección"}. +{"Only those on a whitelist may subscribe and retrieve items","Solo quienes están en una lista blanca pueden suscribirse y recibir elementos"}. {"Organization Name","Nombre de la organización"}. {"Organization Unit","Unidad de la organización"}. -{"Outgoing s2s Connections:","Conexiones S2S salientes:"}. +{"Other Modules Available:","Otros módulos disponibles:"}. {"Outgoing s2s Connections","Conexiones S2S salientes"}. {"Owner privileges required","Se requieren privilegios de propietario de la sala"}. -{"Packet","Paquete"}. +{"Packet relay is denied by service policy","Se ha denegado el reenvío del paquete por política del servicio"}. +{"Participant ID","ID del Participante"}. {"Participant","Participante"}. -{"Password ~b","Contraseña ~b"}. -{"Password:","Contraseña:"}. -{"Password","Contraseña"}. -{"Password Verification:","Verificación de la contraseña:"}. {"Password Verification","Verificación de la contraseña"}. +{"Password Verification:","Verificación de la contraseña:"}. +{"Password","Contraseña"}. +{"Password:","Contraseña:"}. {"Path to Dir","Ruta al directorio"}. {"Path to File","Ruta al fichero"}. -{"Pending","Pendiente"}. +{"Payload semantic type information","Información sobre el tipo semántico de la carga útil"}. {"Period: ","Periodo: "}. -{"Permanent rooms","Salas permanentes"}. {"Persist items to storage","Persistir elementos al almacenar"}. +{"Persistent","Permanente"}. +{"Ping query is incorrect","La petición de Ping es incorrecta"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Ten en cuenta que estas opciones solo harán copia de seguridad de la base de datos Mnesia embebida. Si estás usando ODBC tendrás que hacer también copia de seguridad de tu base de datos SQL."}. -{"Please specify file name.","Por favor especifica el nombre del fichero."}. -{"Please specify file size.","Por favor especifica el tamaño del fichero."}. {"Please, wait for a while before sending new voice request","Por favor, espera un poco antes de enviar otra petición de voz"}. {"Pong","Pong"}. -{"Port ~b","Puerto ~b"}. -{"Port","Puerto"}. +{"Possessing 'ask' attribute is not allowed by RFC6121","Poseer el atributo 'ask' no está permitido por RFC6121"}. {"Present real Jabber IDs to","Los Jabber ID reales pueden verlos"}. -{"private, ","privado"}. -{"Protocol","Protocolo"}. +{"Previous session not found","La sesión previa no ha sido encontrada"}. +{"Previous session PID has been killed","El proceso de la sesión previa ha sido cerrado"}. +{"Previous session PID has exited","El proceso de la sesión previa ha terminado"}. +{"Previous session PID is dead","El proceso de la sesión previa está muerto"}. +{"Previous session timed out","La sesión previa ha caducado"}. +{"private, ","privado, "}. +{"Public","Público"}. +{"Publish model","Modelo de publicación"}. {"Publish-Subscribe","Servicio de Publicar-Subscribir"}. {"PubSub subscriber request","Petición de subscriptor de PubSub"}. {"Purge all items when the relevant publisher goes offline","Borra todos los elementos cuando el publicador relevante se desconecta"}. +{"Push record not found","No se encontró registro Push"}. {"Queries to the conference members are not allowed in this room","En esta sala no se permiten solicitudes a los miembros de la sala"}. +{"Query to another users is forbidden","Enviar solicitudes a otros usuarios está prohibido"}. {"RAM and disc copy","Copia en RAM y disco"}. {"RAM copy","Copia en RAM"}. -{"Raw","Crudo"}. {"Really delete message of the day?","¿Está seguro de quere borrar el mensaje del dia?"}. +{"Receive notification from all descendent nodes","Recibir notificaciones de todos los nodos descendientes"}. +{"Receive notification from direct child nodes only","Recibir notificaciones solo de los nodos que son hijos directos"}. +{"Receive notification of new items only","Recibir notificaciones solo de nuevos elementos"}. +{"Receive notification of new nodes only","Recibir notificaciones solo de nuevos nodos"}. {"Recipient is not in the conference room","El receptor no está en la sala de conferencia"}. -{"Register a Jabber account","Registrar una cuenta Jabber"}. -{"Registered nicknames","Apodos registrados"}. -{"Registered Users:","Usuarios registrados:"}. -{"Registered Users","Usuarios registrados"}. +{"Register an XMPP account","Registrar una cuenta XMPP"}. {"Register","Registrar"}. -{"Registration in mod_irc for ","Registro en mod_irc para"}. {"Remote copy","Copia remota"}. -{"Remove All Offline Messages","Borrar todos los mensajes diferidos"}. -{"Remove","Borrar"}. +{"Remove a hat from a user","Quitarle un sombrero a un usuario"}. {"Remove User","Eliminar usuario"}. {"Replaced by new connection","Reemplazado por una nueva conexión"}. +{"Request has timed out","La petición ha caducado"}. +{"Request is ignored","La petición ha sido ignorada"}. +{"Requested role","Rol solicitado"}. {"Resources","Recursos"}. -{"Restart","Reiniciar"}. {"Restart Service","Reiniciar el servicio"}. {"Restore Backup from File at ","Restaura copia de seguridad desde el fichero en "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Restaurar copia de seguridad binaria en el siguiente reinicio de ejabberd (requiere menos memoria que si instantánea):"}. {"Restore binary backup immediately:","Restaurar inmediatamente copia de seguridad binaria:"}. {"Restore plain text backup immediately:","Restaurar copias de seguridad de texto plano inmediatamente:"}. {"Restore","Restaurar"}. +{"Result","Resultado"}. +{"Roles and Affiliations that May Retrieve Member List","Roles y Afiliaciones que pueden obtener la lista de miembros"}. {"Roles for which Presence is Broadcasted","Roles para los que sí se difunde su Presencia"}. +{"Roles that May Send Private Messages","Roles que pueden enviar mensajes privados"}. {"Room Configuration","Configuración de la sala"}. {"Room creation is denied by service policy","Se te ha denegado crear la sala por política del servicio"}. {"Room description","Descripción de la sala"}. {"Room Occupants","Ocupantes de la sala"}. +{"Room terminates","Cerrando la sala"}. {"Room title","Título de la sala"}. {"Roster groups allowed to subscribe","Grupos de contactos que pueden suscribirse"}. -{"Roster","Lista de contactos"}. -{"Roster of ","Lista de contactos de "}. {"Roster size","Tamaño de la lista de contactos"}. -{"RPC Call Error","Error en la llamada RPC"}. {"Running Nodes","Nodos funcionando"}. -{"~s access rule configuration","Configuración de las Regla de Acceso ~s"}. -{"Saturday","sábado"}. -{"Script check","Comprobación de script"}. +{"~s invites you to the room ~s","~s te invita a la sala ~s"}. +{"Saturday","Sábado"}. +{"Search from the date","Buscar desde la fecha"}. {"Search Results for ","Buscar resultados por "}. +{"Search the text","Buscar el texto"}. +{"Search until the date","Buscar hasta la fecha"}. {"Search users in ","Buscar usuarios en "}. -{"Send announcement to all online users","Enviar anuncio a todos los usuarios conectados"}. {"Send announcement to all online users on all hosts","Enviar anuncio a todos los usuarios conectados en todos los dominios"}. -{"Send announcement to all users","Enviar anuncio a todos los usuarios"}. +{"Send announcement to all online users","Enviar anuncio a todos los usuarios conectados"}. {"Send announcement to all users on all hosts","Enviar anuncio a todos los usuarios en todos los dominios"}. -{"September","septiembre"}. -{"Server ~b","Servidor ~b"}. +{"Send announcement to all users","Enviar anuncio a todos los usuarios"}. +{"September","Septiembre"}. {"Server:","Servidor:"}. -{"Server","Servidor"}. +{"Service list retrieval timed out","Ha caducado la obtención de la lista de servicio"}. +{"Session state copying timed out","El copiado del estado de la sesión ha caducado"}. {"Set message of the day and send to online users","Poner mensaje del dia y enviar a todos los usuarios conectados"}. {"Set message of the day on all hosts and send to online users","Poner mensaje del día en todos los dominios y enviar a los usuarios conectados"}. {"Shared Roster Groups","Grupos Compartidos"}. {"Show Integral Table","Mostrar Tabla Integral"}. +{"Show Occupants Join/Leave","Mostrar personas activas Entrar/Salir"}. {"Show Ordinary Table","Mostrar Tabla Ordinaria"}. {"Shut Down Service","Detener el servicio"}. -{"~s invites you to the room ~s","~s te invita a la sala ~s"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Algunos clientes Jabber pueden recordar tu contraseña en la máquina. Usa esa opción solo si confías en que la máquina que usas es segura."}. +{"SOCKS5 Bytestreams","SOCKS5 Bytestreams"}. +{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Algunos clientes XMPP pueden guardar tu contraseña en la máquina, pero solo deberías hacer esto en tu propia máquina personal, por razones de seguridad."}. +{"Sources Specs:","Especificaciones de Códigos Fuente:"}. {"Specify the access model","Especifica el modelo de acceso"}. {"Specify the event message type","Especifica el tipo del mensaje de evento"}. {"Specify the publisher model","Especificar el modelo del publicante"}. -{"~s's Offline Messages Queue","Cola de mensajes diferidos de ~s"}. -{"Start","Iniciar"}. -{"Start Modules at ","Iniciar módulos en "}. -{"Start Modules","Iniciar módulos"}. -{"Statistics","Estadísticas"}. -{"Statistics of ~p","Estadísticas de ~p"}. -{"Stop","Detener"}. -{"Stop Modules at ","Detener módulos en "}. -{"Stop Modules","Detener módulos"}. +{"Stanza id is not valid","El identificador de la estrofa no es válido"}. +{"Stanza ID","ID del paquete"}. +{"Statically specify a replyto of the node owner(s)","Especificar de forma estática un 'replyto' de dueño(s) del nodo"}. {"Stopped Nodes","Nodos detenidos"}. -{"Storage Type","Tipo de almacenamiento"}. {"Store binary backup:","Guardar copia de seguridad binaria:"}. {"Store plain text backup:","Guardar copia de seguridad en texto plano:"}. +{"Stream management is already enabled","Ya está activada la administración de la conexión"}. +{"Stream management is not enabled","No está activada la administración de la conexión"}. {"Subject","Asunto"}. -{"Submit","Enviar"}. {"Submitted","Enviado"}. {"Subscriber Address","Dirección del subscriptor"}. -{"Subscription","Subscripción"}. -{"Sunday","domingo"}. +{"Subscribers may publish","Los suscriptores pueden publicar"}. +{"Subscription requests must be approved and only subscribers may retrieve items","Las peticiones de suscripción deben ser aprobadas y solo los suscriptores pueden obtener elementos"}. +{"Subscriptions are not allowed","Las subscripciones no están permitidas"}. +{"Sunday","Domingo"}. +{"Text associated with a picture","Texto asociado con una imagen"}. +{"Text associated with a sound","Texto asociado con un sonido"}. +{"Text associated with a video","Texto asociado con un vídeo"}. +{"Text associated with speech","Texto asociado con una charla"}. {"That nickname is already in use by another occupant","Ese apodo ya está siendo usado por otro ocupante"}. {"That nickname is registered by another person","El apodo ya está registrado por otra persona"}. +{"The account already exists","La cuenta ya existe"}. +{"The account was not unregistered","La cuenta no fue eliminada"}. +{"The body text of the last received message","El contenido de texto del último mensaje recibido"}. {"The CAPTCHA is valid.","El CAPTCHA es válido."}. {"The CAPTCHA verification has failed","La verificación de CAPTCHA ha fallado"}. +{"The captcha you entered is wrong","El CAPTCHA que has introducido es erróneo"}. +{"The child nodes (leaf or collection) associated with a collection","Los nodos hijos (ya sean hojas o colecciones) asociados con una colección"}. {"The collections with which a node is affiliated","Las colecciones a las que un nodo está afiliado"}. -{"the password is","la contraseña es"}. +{"The DateTime at which a leased subscription will end or has ended","La FechayHora en la que una suscripción prestada acabará o ha terminado"}. +{"The datetime when the node was created","La fechayhora cuando el nodo fue creado"}. +{"The default language of the node","El nombre por defecto del nodo"}. +{"The feature requested is not supported by the conference","La característica solicitada no está soportada por la sala de conferencia"}. +{"The JID of the node creator","El JID del creador del nodo"}. +{"The JIDs of those to contact with questions","Los JIDs a quienes contactar con preguntas"}. +{"The JIDs of those with an affiliation of owner","Los JIDs de quienes tienen una afiliación de dueños"}. +{"The JIDs of those with an affiliation of publisher","Los JIDs de quienes tienen una afiliación de publicadores"}. +{"The list of all online users","La lista de todos los usuarios conectados"}. +{"The list of all users","La lista de todos los usuarios"}. +{"The list of JIDs that may associate leaf nodes with a collection","La lista de JIDs que pueden asociar nodos hijo con una colección"}. +{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","El número máximo de nodos hijo que pueden asociarse a una colección, o `max` para no especificar un límite, más que el máximo impuesto por el servidor"}. +{"The minimum number of milliseconds between sending any two notification digests","El número mínimo de milisegundos entre dos envíos de resumen de notificaciones"}. +{"The name of the node","El nombre del nodo"}. +{"The node is a collection node","El nodo es una colección"}. +{"The node is a leaf node (default)","El nodo es un nodo hoja (por defecto)"}. +{"The NodeID of the relevant node","El NodoID del nodo relevante"}. +{"The number of pending incoming presence subscription requests","El número de peticiones de suscripción a presencia que están pendientes de llegar"}. +{"The number of subscribers to the node","El número de suscriptores al nodo"}. +{"The number of unread or undelivered messages","El número de mensajes sin leer o sin entregar"}. +{"The password contains unacceptable characters","La contraseña contiene caracteres inaceptables"}. {"The password is too weak","La contraseña es demasiado débil"}. -{"The password of your Jabber account was successfully changed.","La contraseña de tu cuenta Jabber se ha cambiado correctamente."}. -{"There was an error changing the password: ","Hubo un error cambiando la contraseña."}. -{"There was an error creating the account: ","Hubo uno error al crear la cuenta:"}. -{"There was an error deleting the account: ","Hubo un error borrando la cuenta."}. -{"This IP address is blacklisted in ~s","Esta dirección IP está en la lista negra en ~s"}. +{"the password is","la contraseña es"}. +{"The password of your XMPP account was successfully changed.","La contraseña de tu cuenta XMPP se ha cambiado correctamente."}. +{"The password was not changed","La contraseña no fue cambiada"}. +{"The passwords are different","Las contraseñas son diferentes"}. +{"The presence states for which an entity wants to receive notifications","Los estados de presencia para los cuales una entidad quiere recibir notificaciones"}. +{"The query is only allowed from local users","La solicitud está permitida solo para usuarios locales"}. +{"The query must not contain elements","La solicitud no debe contener elementos "}. +{"The room subject can be modified by participants","El asunto de la sala puede ser modificado por los participantes"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","La información semántica de los datos del nodo, normalmente es especificada por el espacio de los nombres de la carga útil (si existe)"}. +{"The sender of the last received message","El emisor del último mensaje recibido"}. +{"The stanza MUST contain only one element, one element, or one element","El paquete DEBE contener solo un elemento , un elemento , o un elemento "}. +{"The subscription identifier associated with the subscription request","El identificador de suscripción asociado con la petición de suscripción"}. +{"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","La URL de una transformación XSL que puede aplicarse a payloads para generar un elemento de contenido del mensaje apropiado."}. +{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","La URL de una transformación XSL que puede aplicarse al formato de payload para generar un resultado de Formulario de Datos válido, que el cliente pueda mostrar usando un mecanismo de dibujado genérico de Formulario de Datos"}. +{"There was an error changing the password: ","Hubo uno error al cambiar la contaseña: "}. +{"There was an error creating the account: ","Hubo uno error al crear la cuenta: "}. +{"There was an error deleting the account: ","Hubo un error borrando la cuenta: "}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","No importa si usas mayúsculas: macbeth es lo mismo que MacBeth y Macbeth."}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Esta página te permite crear una cuenta Jabber este servidor Jabber. Tu JID (Jabber IDentificador) será de la forma: nombredeusuario@servidor. Por favor lee detenidamente las instrucciones para rellenar correctamente los campos."}. -{"This page allows to unregister a Jabber account in this Jabber server.","Esta página te permite borrar tu cuenta Jabber en este servidor Jabber."}. -{"Thursday","jueves"}. +{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Esta página te permite crear una cuenta XMPP este servidor XMPP. Tu JID (Jabber ID) será de la forma: nombredeusuario@servidor. Por favor lee detenidamente las instrucciones para rellenar correctamente los campos."}. +{"This page allows to unregister an XMPP account in this XMPP server.","Esta página te permite borrar tu cuenta XMPP en este servidor XMPP."}. +{"This room is not anonymous","Sala no anónima"}. +{"This service can not process the address: ~s","Este servicio no puede procesar la dirección: ~s"}. +{"Thursday","Jueves"}. {"Time delay","Retraso temporal"}. -{"Time","Fecha"}. +{"Timed out waiting for stream resumption","Ha pasado demasiado tiempo esperando que la conexión se restablezca"}. +{"To register, visit ~s","Para registrarte, visita ~s"}. +{"To ~ts","A ~ts"}. +{"Token TTL","Token TTL"}. +{"Too many active bytestreams","Demasiados bytestreams activos"}. {"Too many CAPTCHA requests","Demasiadas peticiones de CAPTCHA"}. +{"Too many child elements","Demasiados subelementos"}. +{"Too many elements","Demasiados elementos "}. +{"Too many elements","Demasiados elementos "}. {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Demasiadas (~p) autenticaciones fallidas de esta dirección IP (~s). La dirección será desbloqueada en ~s UTC"}. -{"Too many unacked stanzas","Demasiados mensajes sin haber reconocido recibirlos"}. -{"To","Para"}. -{"To ~s","A ~s"}. -{"Total rooms","Salas totales"}. -{"Traffic rate limit is exceeded","Se ha exedido el límite de tráfico"}. -{"Transactions Aborted:","Transacciones abortadas:"}. -{"Transactions Committed:","Transacciones finalizadas:"}. -{"Transactions Logged:","Transacciones registradas:"}. -{"Transactions Restarted:","Transacciones reiniciadas:"}. -{"Tuesday","martes"}. +{"Too many receiver fields were specified","Se han especificado demasiados campos de destinatario"}. +{"Too many unacked stanzas","Demasiados mensajes sin haber reconocido recibirlos"}. +{"Too many users in this conference","Demasiados usuarios en esta sala"}. +{"Traffic rate limit is exceeded","Se ha excedido el límite de tráfico"}. +{"~ts's MAM Archive","Archivo MAM de ~ts"}. +{"~ts's Offline Messages Queue","Cola de mensajes diferidos de ~ts"}. +{"Tuesday","Martes"}. {"Unable to generate a CAPTCHA","No se pudo generar un CAPTCHA"}. +{"Unable to register route on existing local domain","No se ha podido registrar la ruta en este dominio local existente"}. {"Unauthorized","No autorizado"}. -{"Unregister a Jabber account","Borrar una cuenta Jabber"}. +{"Unexpected action","Acción inesperada"}. +{"Unexpected error condition: ~p","Condición de error inesperada: ~p"}. +{"Uninstall","Desinstalar"}. +{"Unregister an XMPP account","Borrar una cuenta XMPP"}. {"Unregister","Borrar"}. -{"Update","Actualizar"}. +{"Unsupported element","Elemento no soportado"}. +{"Unsupported version","Versión no soportada"}. {"Update message of the day (don't send)","Actualizar mensaje del dia, pero no enviarlo"}. {"Update message of the day on all hosts (don't send)","Actualizar el mensaje del día en todos los dominos (pero no enviarlo)"}. -{"Update ~p","Actualizar ~p"}. -{"Update plan","Plan de actualización"}. -{"Update script","Script de actualización"}. -{"Uptime:","Tiempo desde el inicio:"}. -{"Use of STARTTLS required","Es obligatorio usar STARTTLS"}. +{"Update specs to get modules source, then install desired ones.","Actualizar Especificaciones para conseguir el código fuente de los módulos, luego instala los que quieras."}. +{"Update Specs","Actualizar Especificaciones"}. +{"Updating the vCard is not supported by the vCard storage backend","La actualización de la vCard no es compatible con el vCard almacenamiento backend"}. +{"Upgrade","Actualizar"}. +{"URL for Archived Discussion Logs","URL del registro de discusiones archivadas"}. +{"User already exists","El usuario ya existe"}. {"User JID","Jabber ID del usuario"}. +{"User (jid)","Usuario (jid)"}. {"User Management","Administración de usuarios"}. +{"User not allowed to perform an IQ set on another user's vCard.","No se permite al usuario realizar un IQ establecido en la vCard de otro usuario."}. +{"User removed","Usuario eliminado"}. +{"User session not found","Sesión de usuario no encontrada"}. +{"User session terminated","Sesión de usuario terminada"}. +{"User ~ts","Usuario ~ts"}. {"Username:","Nombre de usuario:"}. {"Users are not allowed to register accounts so quickly","Los usuarios no tienen permitido crear cuentas con tanta rapidez"}. {"Users Last Activity","Última actividad de los usuarios"}. -{"User ~s","Usuario ~s"}. {"Users","Usuarios"}. {"User","Usuario"}. -{"Validate","Validar"}. -{"vCard User Search","Buscar vCard de usuario"}. +{"Value 'get' of 'type' attribute is not allowed","El valor 'get' del atributo 'type' no está permitido"}. +{"Value of '~s' should be boolean","El valor de '~s' debería ser booleano"}. +{"Value of '~s' should be datetime string","El valor de '~s' debería ser una fecha"}. +{"Value of '~s' should be integer","El valor de '~s' debería ser un entero"}. +{"Value 'set' of 'type' attribute is not allowed","El valor 'set' del atributo 'type' no está permitido"}. +{"vCard User Search","Búsqueda de vCard de usuarios"}. +{"View joined MIX channels","Ver los canales MIX unidos"}. {"Virtual Hosts","Dominios Virtuales"}. {"Visitors are not allowed to change their nicknames in this room","Los visitantes no tienen permitido cambiar sus apodos en esta sala"}. {"Visitors are not allowed to send messages to all occupants","Los visitantes no pueden enviar mensajes a todos los ocupantes"}. {"Visitor","Visitante"}. {"Voice request","Petición de voz"}. {"Voice requests are disabled in this conference","Las peticiones de voz están desactivadas en esta sala"}. -{"Wednesday","miércoles"}. +{"Web client which allows to join the room anonymously","Cliente web que permite entrar en la sala anonimamente"}. +{"Wednesday","Miércoles"}. +{"When a new subscription is processed and whenever a subscriber comes online","Cuando se procesa una nueva suscripción y cuando un suscriptor se conecta"}. +{"When a new subscription is processed","Cuando se procesa una nueva suscripción"}. {"When to send the last published item","Cuando enviar el último elemento publicado"}. +{"Whether an entity wants to receive an XMPP message body in addition to the payload format","Si una entidad quiere recibir un cuerpo de mensaje XMPP adicionalmente al formato de payload"}. +{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","Si una entidad quiere recibir resúmenes (agregados) de notificaciones o todas las notificaciones individualmente"}. +{"Whether an entity wants to receive or disable notifications","Si una entidad quiere recibir o desactivar las notificaciones"}. +{"Whether owners or publisher should receive replies to items","Si dueños y publicadores deberían recibir respuestas de los elementos"}. +{"Whether the node is a leaf (default) or a collection","Si el nodo es una hoja (por defecto) o una colección"}. {"Whether to allow subscriptions","Permitir subscripciones"}. -{"You can later change your password using a Jabber client.","Puedes cambiar tu contraseña después, usando un cliente Jabber."}. +{"Whether to make all subscriptions temporary, based on subscriber presence","Si hacer que todas las suscripciones sean temporales, basado en la presencia del suscriptor"}. +{"Whether to notify owners about new subscribers and unsubscribes","Si notificar a los dueños sobre nuevas suscripciones y desuscripciones"}. +{"Who can send private messages","Quién puede enviar mensajes privados"}. +{"Who may associate leaf nodes with a collection","Quien puede asociar nodos hoja con una colección"}. +{"Wrong parameters in the web formulary","Parámetros incorrectos en el formulario web"}. +{"Wrong xmlns","XMLNS incorrecto"}. +{"XMPP Account Registration","Registro de Cuenta XMPP"}. +{"XMPP Domains","Dominios XMPP"}. +{"XMPP Show Value of Away","Valor 'Show' de XMPP: Ausente"}. +{"XMPP Show Value of Chat","Valor 'Show' de XMPP: Charlador"}. +{"XMPP Show Value of DND (Do Not Disturb)","Valor 'Show' de XMPP: DND (No Molestar)"}. +{"XMPP Show Value of XA (Extended Away)","Valor 'Show' de XMPP: XA (Ausente Extendido)"}. +{"XMPP URI of Associated Publish-Subscribe Node","URI XMPP del Nodo Asociado de Publicar-Subscribir"}. +{"You are being removed from the room because of a system shutdown","Estás siendo expulsado de la sala porque el sistema se va a detener"}. +{"You are not allowed to send private messages","No tienes permitido enviar mensajes privados"}. +{"You are not joined to the channel","No has entrado en el canal"}. +{"You can later change your password using an XMPP client.","Puedes cambiar tu contraseña después, usando un cliente XMPP."}. {"You have been banned from this room","Has sido bloqueado en esta sala"}. +{"You have joined too many conferences","Has entrado en demasiadas salas de conferencia"}. {"You must fill in field \"Nickname\" in the form","Debes rellenar el campo \"Apodo\" en el formulario"}. {"You need a client that supports x:data and CAPTCHA to register","Necesitas un cliente con soporte de x:data y CAPTCHA para registrarte"}. {"You need a client that supports x:data to register the nickname","Necesitas un cliente con soporte de x:data para poder registrar el apodo"}. -{"You need an x:data capable client to configure mod_irc settings","Necesitas un cliente con soporte de x:data para configurar las opciones de mod_irc"}. -{"You need an x:data capable client to configure room","Necesitas un cliente con soporte de x:data para configurar la sala"}. {"You need an x:data capable client to search","Necesitas un cliente con soporte de x:data para poder buscar"}. -{"Your active privacy list has denied the routing of this stanza.","Tu lista de privacidad activa ha denegado el encío de este paquete."}. +{"Your active privacy list has denied the routing of this stanza.","Tu lista de privacidad activa ha denegado el envío de este paquete."}. {"Your contact offline message queue is full. The message has been discarded.","Tu cola de mensajes diferidos de contactos está llena. El mensaje se ha descartado."}. -{"Your Jabber account was successfully created.","Tu cuenta Jabber se ha creado correctamente."}. -{"Your Jabber account was successfully deleted.","Tu cuenta Jabber se ha borrado correctamente."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","Tus mensajes a ~s están siendo bloqueados. Para desbloquearlos, visita ~s"}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Tu petición de suscripción y/o mensajes a ~s ha sido bloqueado. Para desbloquear tu petición de suscripción visita ~s"}. +{"Your XMPP account was successfully registered.","Tu cuenta XMPP se ha registrado correctamente."}. +{"Your XMPP account was successfully unregistered.","Tu cuenta XMPP se ha borrado correctamente."}. +{"You're not allowed to create nodes","No tienes permitido crear nodos"}. diff --git a/priv/msgs/es.po b/priv/msgs/es.po deleted file mode 100644 index b9a950e3c..000000000 --- a/priv/msgs/es.po +++ /dev/null @@ -1,1931 +0,0 @@ -# translation of es.po to -# Badlop , 2009. -msgid "" -msgstr "" -"Project-Id-Version: es\n" -"POT-Creation-Date: \n" -"PO-Revision-Date: 2016-01-15 12:19+0100\n" -"Last-Translator: Badlop \n" -"Language-Team: \n" -"Language: es\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Spanish (castellano)\n" -"X-Generator: Poedit 1.6.10\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "Es obligatorio usar STARTTLS" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "No se ha proporcionado recurso" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Reemplazado por una nueva conexión" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "ha sido expulsado" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "Tu lista de privacidad activa ha denegado el encío de este paquete." - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "Demasiados mensajes sin haber reconocido recibirlos" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Teclea el texto que ves" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "" -"Tus mensajes a ~s están siendo bloqueados. Para desbloquearlos, visita ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "Si no ves la imagen CAPTCHA aquí, visita la página web." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "Página web de CAPTCHA" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "El CAPTCHA es válido." - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Usuario" - -#: ejabberd_oauth.erl:256 -msgid "Server" -msgstr "Servidor" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Contraseña" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "Aceptar" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "No autorizado" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "ejabberd Web Admin" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Administración" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Listas de Control de Acceso" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Enviado" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Mal formato" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Enviar" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "Crudo" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Eliminar los seleccionados" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Reglas de Acceso" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "Configuración de las Regla de Acceso ~s" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "Dominios Virtuales" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Usuarios" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "Usuarios conectados" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Última actividad de los usuarios" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Periodo: " - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "Último mes" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "Último año" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "Toda la actividad" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Mostrar Tabla Ordinaria" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Mostrar Tabla Integral" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Estadísticas" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "No encontrado" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Nodo no encontrado" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Añadir nuevo" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Dominio" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Usuarios registrados" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Añadir usuario" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Mensajes diferidos" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Última actividad" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Nunca" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Conectado" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Usuarios registrados:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Usuarios conectados:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Conexiones S2S salientes:" - -#: ejabberd_web_admin.erl:1559 -msgid "Incoming s2s Connections:" -msgstr "Conexiones S2S entrantes:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Ninguno" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Cambiar contraseña" - -#: ejabberd_web_admin.erl:1673 -msgid "User ~s" -msgstr "Usuario ~s" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Recursos conectados:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Contraseña:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Eliminar usuario" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Sin datos" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Nodos" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Nodos funcionando" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Nodos detenidos" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -msgid "Node ~p" -msgstr "Nodo ~p" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "Base de datos" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Guardar copia de seguridad" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Puertos de escucha" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Actualizar" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Reiniciar" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Detener" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Módulos" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "Error en la llamada RPC" - -#: ejabberd_web_admin.erl:1917 -msgid "Database Tables at ~p" -msgstr "Tablas de la base de datos en ~p" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Nombre" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Tipo de almacenamiento" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Elementos" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Memoria" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Error" - -#: ejabberd_web_admin.erl:1955 -msgid "Backup of ~p" -msgstr "Copia de seguridad de ~p" - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Ten en cuenta que estas opciones solo harán copia de seguridad de la base de " -"datos Mnesia embebida. Si estás usando ODBC tendrás que hacer también copia " -"de seguridad de tu base de datos SQL." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Guardar copia de seguridad binaria:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "Aceptar" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Restaurar inmediatamente copia de seguridad binaria:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"Restaurar copia de seguridad binaria en el siguiente reinicio de ejabberd " -"(requiere menos memoria que si instantánea):" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Guardar copia de seguridad en texto plano:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Restaurar copias de seguridad de texto plano inmediatamente:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "Importar usuarios desde un fichero PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" -"Exportar datos de todos los usuarios del servidor a ficheros PIEFXIS " -"(XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "" -"Exportar datos de los usuarios de un dominio a ficheros PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "Exportar todas las tablas a un fichero SQL:" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Importar usuario de fichero spool de jabberd14:" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Importar usuarios del directorio spool de jabberd14:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Puertos de escucha en " - -#: ejabberd_web_admin.erl:2144 -msgid "Modules at ~p" -msgstr "Módulos en ~p" - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "Estadísticas de ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Tiempo desde el inicio:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "Tiempo consumido de CPU:" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Transacciones finalizadas:" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Transacciones abortadas:" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Transacciones reiniciadas:" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Transacciones registradas:" - -#: ejabberd_web_admin.erl:2243 -msgid "Update ~p" -msgstr "Actualizar ~p" - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "Plan de actualización" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Módulos modificados" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Script de actualización" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Script de actualización a bajo nivel" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Comprobación de script" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Puerto" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Protocolo" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Módulo" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Opciones" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Iniciar" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Comandos" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "¿Está seguro de quere borrar el mensaje del dia?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Asunto" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Cuerpo del mensaje" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "No se ha proporcionado cuerpo de mensaje para el anuncio" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Anuncios" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Enviar anuncio a todos los usuarios" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Enviar anuncio a todos los usuarios en todos los dominios" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Enviar anuncio a todos los usuarios conectados" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "Enviar anuncio a todos los usuarios conectados en todos los dominios" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Poner mensaje del dia y enviar a todos los usuarios conectados" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" -"Poner mensaje del día en todos los dominios y enviar a los usuarios " -"conectados" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Actualizar mensaje del dia, pero no enviarlo" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "Actualizar el mensaje del día en todos los dominos (pero no enviarlo)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Borrar mensaje del dia" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Borrar el mensaje del día en todos los dominios" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Configuración" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Iniciar módulos" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Detener módulos" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Restaurar" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Exportar a fichero de texto" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Importar fichero" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Importar directorio" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Reiniciar el servicio" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Detener el servicio" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Borrar usuario" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Cerrar sesión de usuario" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Ver contraseña de usuario" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Cambiar contraseña de usuario" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "Ver fecha de la última conexión de usuario" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Ver estadísticas de usuario" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Ver número de usuarios registrados" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Ver número de usuarios conectados" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Administración de usuarios" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Todos los usuarios" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Conexiones S2S salientes" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Gestión de copia de seguridad" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Importar usuarios de ficheros spool de jabberd-1.4" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "A ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "De ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Configuración de tablas de la base de datos en " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Selecciona tipo de almacenamiento de las tablas" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Copia en disco solamente" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "Copia en RAM y disco" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "Copia en RAM" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "Copia remota" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Detener módulos en " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Selecciona módulos a detener" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Iniciar módulos en " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Introduce lista de {módulo, [opciones]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Lista de módulos a iniciar" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Guardar copia de seguridad en fichero en " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Introduce ruta al fichero de copia de seguridad" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Ruta al fichero" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Restaura copia de seguridad desde el fichero en " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Exporta copia de seguridad a fichero de texto en " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Introduce ruta al fichero de texto" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Importa usuario desde fichero en " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Introduce ruta al fichero jabberd14 spool" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Importar usuarios desde el directorio en " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Introduce la ruta al directorio de jabberd14 spools" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Ruta al directorio" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "Retraso temporal" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Configuración de la Lista de Control de Acceso" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Listas de Control de Acceso" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Configuración de accesos" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Reglas de acceso" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Verificación de la contraseña" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Número de usuarios registrados" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Número de usuarios conectados" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Última conexión" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "Tamaño de la lista de contactos" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "Direcciones IP" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Recursos" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Administración de " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Acción en el usuario" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Editar propiedades" - -#: mod_fail2ban.erl:95 -msgid "" -"Too many (~p) failed authentications from this IP address (~s). The address " -"will be unblocked at ~s UTC" -msgstr "" -"Demasiadas (~p) autenticaciones fallidas de esta dirección IP (~s). La " -"dirección será desbloqueada en ~s UTC" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "Por favor especifica el tamaño del fichero." - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "Por favor especifica el nombre del fichero." - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "Esta dirección IP está en la lista negra en ~s" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "Acceso denegado por la política del servicio" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "Transporte de IRC" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "Módulo de IRC para ejabberd" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "" -"Necesitas un cliente con soporte de x:data para configurar las opciones de " -"mod_irc" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Registro en mod_irc para" - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Introduce el nombre de usuario, codificaciones de carácteres, puertos y " -"contraseñas que quieras usar al conectar en los servidores de IRC" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "Nombre de usuario en IRC" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Si quieres especificar distintos codificaciones de carácteres, contraseñas o " -"puertos para cada servidor IRC rellena esta lista con valores en el formato " -"'{\"servidor irc\", \"codificación\", \"puerto\", \"contrasela\"}'. Este " -"servicio usa por defecto la codificación \"~s\", puerto ~p, sin contraseña." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Ejemplo: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Parámetros de conexiones" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "Entrar en canal IRC" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "Canal IRC (no pongas el # del principio)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "Servidor IRC" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Entrar en el canal de IRC aquí" - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Entra en el canal de IRC en esta dirección Jabber: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "Opciones de IRC" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Introduce el nombre de usuario y codificaciones de carácteres que quieras " -"usar al conectar en los servidores de IRC. Pulsa Siguiente para conseguir " -"más campos en el formulario. Pulsa Completar para guardar las opciones." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "Nombre de usuario en IRC" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Contraseña ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Puerto ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Codificación del servidor ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Servidor ~b" - -#: mod_mam.erl:541 -msgid "Only members may query archives of this room" -msgstr "Solo miembros pueden consultar el archivo de mensajes de la sala" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "" -"Solo los administradores del servicio tienen permiso para enviar mensajes de " -"servicio" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "Se te ha denegado crear la sala por política del servicio" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "La sala de conferencias no existe" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Salas de charla" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "Salas vacías" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "" -"Necesitas un cliente con soporte de x:data para poder registrar el apodo" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Registro del apodo en " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Introduce el apodo que quieras registrar" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Apodo" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "El apodo ya está registrado por otra persona" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Debes rellenar el campo \"Apodo\" en el formulario" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "Módulo de MUC para ejabberd" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "Salas de Charla" - -#: mod_muc_admin.erl:249 -msgid "Total rooms" -msgstr "Salas totales" - -#: mod_muc_admin.erl:250 -msgid "Permanent rooms" -msgstr "Salas permanentes" - -#: mod_muc_admin.erl:251 -msgid "Registered nicknames" -msgstr "Apodos registrados" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "Lista de salas" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "Configuración de la sala modificada" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "entra en la sala" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "sale de la sala" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "ha sido bloqueado" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "ha sido expulsado por un cambio de su afiliación" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "ha sido expulsado porque la sala es ahora solo para miembros" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "ha sido expulsado porque el sistema se va a detener" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "se cambia el nombre a" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " ha puesto el asunto: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "Se ha creado la sala" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "Se ha destruido la sala" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "Se ha iniciado la sala" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "Se ha detenido la sala" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "lunes" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "martes" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "miércoles" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "jueves" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "viernes" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "sábado" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "domingo" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "enero" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "febrero" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "marzo" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "abril" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "mayo" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "junio" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "julio" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "agosto" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "septiembre" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "octubre" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "noviembre" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "diciembre" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Configuración de la sala" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "Ocupantes de la sala" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "Se ha exedido el límite de tráfico" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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 "" -"No está permitido enviar mensajes de error a la sala. Este participante (~s) " -"ha enviado un mensaje de error (~s) y fue expulsado de la sala" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "Impedir el envio de mensajes privados a la sala" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "Por favor, espera un poco antes de enviar otra petición de voz" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "Las peticiones de voz están desactivadas en esta sala" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "Fallo al extraer el Jabber ID de tu aprobación de petición de voz" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "Solo los moderadores pueden aprobar peticiones de voz" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Tipo de mensaje incorrecto" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "No está permitido enviar mensajes privados del tipo \"groupchat\"" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "El receptor no está en la sala de conferencia" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "No está permitido enviar mensajes privados" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Solo los ocupantes pueden enviar mensajes a la sala" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "Solo los ocupantes pueden enviar solicitudes a la sala" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "En esta sala no se permiten solicitudes a los miembros de la sala" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "" -"Solo los moderadores y participantes pueden cambiar el asunto de esta sala" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Solo los moderadores pueden cambiar el asunto de esta sala" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "Los visitantes no pueden enviar mensajes a todos los ocupantes" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "Los visitantes no tienen permitido cambiar sus apodos en esta sala" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "Ese apodo ya está siendo usado por otro ocupante" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Has sido bloqueado en esta sala" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "Necesitas ser miembro de esta sala para poder entrar" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "Se necesita contraseña para entrar en esta sala" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "Demasiadas peticiones de CAPTCHA" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "No se pudo generar un CAPTCHA" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Contraseña incorrecta" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "Se necesita privilegios de administrador" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "Se necesita privilegios de moderador" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "El Jabber ID ~s no es válido" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "El apodo ~s no existe en la sala" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Afiliación no válida: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Rol no válido: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "Se requieren privilegios de propietario de la sala" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "Configuración para la sala ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Título de la sala" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "Descripción de la sala" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Sala permanente" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Sala públicamente visible" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "La lista de participantes es pública" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Proteger la sala con contraseña" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Número máximo de ocupantes" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Sin límite" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Los Jabber ID reales pueden verlos" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "solo moderadores" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "cualquiera" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "Roles para los que sí se difunde su Presencia" - -#: mod_muc_room.erl:3486 -msgid "Moderator" -msgstr "Moderador" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "Participante" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "Visitante" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Sala sólo para miembros" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Sala moderada" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Los usuarios son participantes por defecto" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Permitir a los usuarios cambiar el asunto" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Permitir a los usuarios enviar mensajes privados" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "Permitir a los visitantes enviar mensajes privados a" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "nadie" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Permitir a los usuarios consultar a otros usuarios" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Permitir a los usuarios enviar invitaciones" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "" -"Permitir a los visitantes enviar texto de estado en las actualizaciones de " -"presencia" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Permitir a los visitantes cambiarse el apodo" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "Permitir a los visitantes enviar peticiones de voz" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "Intervalo mínimo entre peticiones de voz (en segundos)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "Proteger la sala con CAPTCHA" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "Activar el almacenamiento de mensajes" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "Excluir Jabber IDs de las pruebas de CAPTCHA" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Guardar históricos" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "Necesitas un cliente con soporte de x:data para configurar la sala" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Número de ocupantes" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "privado" - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "Petición de voz" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "Aprueba o rechaza la petición de voz." - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "Jabber ID del usuario" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "¿Conceder voz a esta persona?" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s te invita a la sala ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "la contraseña es" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "Multicast" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "Servicio Multicast de ejabberd" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "" -"Tu cola de mensajes diferidos de contactos está llena. El mensaje se ha " -"descartado." - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "Cola de mensajes diferidos de ~s" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Fecha" - -#: mod_offline.erl:812 -msgid "From" -msgstr "De" - -#: mod_offline.erl:813 -msgid "To" -msgstr "Para" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Paquete" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Mensajes diferidos:" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "Borrar todos los mensajes diferidos" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "Módulo SOCKS5 Bytestreams para ejabberd" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Servicio de Publicar-Subscribir" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "Módulo de Publicar-Subscribir de ejabberd" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "Petición de subscriptor de PubSub" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Decidir si aprobar la subscripción de esta entidad." - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "Nodo ID" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Dirección del subscriptor" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "¿Deseas permitir a este Jabber ID que se subscriba a este nodo PubSub?" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Enviar contenidos junto con las notificaciones de eventos" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Entregar notificaciones de eventos" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Notificar subscriptores cuando cambia la configuración del nodo" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Notificar subscriptores cuando el nodo se borra" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Notificar subscriptores cuando los elementos se borran del nodo" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Persistir elementos al almacenar" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "Un nombre sencillo para el nodo" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Máximo # de elementos que persisten" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Permitir subscripciones" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Especifica el modelo de acceso" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Grupos de contactos que pueden suscribirse" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Especificar el modelo del publicante" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "Borra todos los elementos cuando el publicador relevante se desconecta" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "Especifica el tipo del mensaje de evento" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Máximo tamaño del contenido en bytes" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "Cuando enviar el último elemento publicado" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Solo enviar notificaciones a los usuarios disponibles" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "Las colecciones a las que un nodo está afiliado" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "La verificación de CAPTCHA ha fallado" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "Necesitas un cliente con soporte de x:data y CAPTCHA para registrarte" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "" -"Escoge un nombre de usuario y contraseña para registrarte en este servidor" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "La contraseña es demasiado débil" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "Los usuarios no tienen permitido crear cuentas con tanta rapidez" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "Tu cuenta Jabber se ha creado correctamente." - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "Hubo uno error al crear la cuenta:" - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "Tu cuenta Jabber se ha borrado correctamente." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "Hubo un error borrando la cuenta." - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "La contraseña de tu cuenta Jabber se ha cambiado correctamente." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "Hubo un error cambiando la contraseña." - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Registro de Cuenta Jabber" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Registrar una cuenta Jabber" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Borrar una cuenta Jabber" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"Esta página te permite crear una cuenta Jabber este servidor Jabber. Tu JID " -"(Jabber IDentificador) será de la forma: nombredeusuario@servidor. Por favor " -"lee detenidamente las instrucciones para rellenar correctamente los campos." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "Nombre de usuario:" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" -"No importa si usas mayúsculas: macbeth es lo mismo que MacBeth y Macbeth." - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "Caracteres no permitidos:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "Servidor:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" -"No le digas tu contraseña a nadie, ni siquiera a los administradores del " -"servidor Jabber." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "Puedes cambiar tu contraseña después, usando un cliente Jabber." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Algunos clientes Jabber pueden recordar tu contraseña en la máquina. Usa esa " -"opción solo si confías en que la máquina que usas es segura." - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"Memoriza tu contraseña, o apúntala en un papel en un lugar seguro. En Jabber " -"no hay un método automatizado para recuperar la contraseña si la olvidas." - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "Verificación de la contraseña:" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "Registrar" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "Contraseña antigua:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "Nueva contraseña:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "" -"Esta página te permite borrar tu cuenta Jabber en este servidor Jabber." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "Borrar" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Subscripción" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Pendiente" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Grupos" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Validar" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Borrar" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Lista de contactos de " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Añadir Jabber ID" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Lista de contactos" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Grupos Compartidos" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Nombre:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Descripción:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Miembros:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Mostrar grupos:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Grupo " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Servidor Jabber en Erlang" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Cumpleaños" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Ciudad" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "País" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "correo" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Apellido" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Rellena el formulario para buscar usuarios Jabber. Añade * al final de un " -"campo para buscar subcadenas." - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Nombre completo" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Segundo nombre" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Nombre de la organización" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Unidad de la organización" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Buscar usuarios en " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "Necesitas un cliente con soporte de x:data para poder buscar" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "Buscar vCard de usuario" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "Módulo vCard para ejabberd" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Buscar resultados por " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "Rellena campos para buscar usuarios Jabber que concuerden" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Servidores S2S salientes:" - -#~ msgid "Delete" -#~ msgstr "Eliminar" - -#~ msgid "This room is not anonymous" -#~ msgstr "Sala no anónima" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "" -#~ "Este participante ha sido expulsado de la sala porque envió un mensaje de " -#~ "error" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Este participante ha sido expulsado de la sala porque envió un mensaje de " -#~ "error a otro participante" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "" -#~ "Este participante ha sido expulsado de la sala porque envió una presencia " -#~ "de error" diff --git a/priv/msgs/fr.msg b/priv/msgs/fr.msg index 697faf955..0f8e99eeb 100644 --- a/priv/msgs/fr.msg +++ b/priv/msgs/fr.msg @@ -1,181 +1,213 @@ -%% -*- coding: latin-1 -*- -{"Access Configuration","Configuration d'accès"}. -{"Access Control List Configuration","Configuration des droits (ACL)"}. -{"Access control lists","Droits (ACL)"}. -{"Access Control Lists","Droits (ACL)"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" (Add * to the end of field to match substring)"," (Ajouter * à la fin du champ pour correspondre à la sous-chaîne)"}. +{" has set the subject to: "," a défini le sujet sur : "}. +{"# participants","# participants"}. +{"A description of the node","Une description du nœud"}. +{"A friendly name for the node","Un nom convivial pour le nœud"}. +{"A password is required to enter this room","Un mot de passe est nécessaire pour accéder à ce salon"}. +{"A Web Page","Une page Web"}. +{"Accept","Accepter"}. {"Access denied by service policy","L'accès au service est refusé"}. -{"Access rules","Règles d'accès"}. -{"Access Rules","Règles d'accès"}. +{"Access model","Modèle d’accès"}. +{"Account doesn't exist","Le compte n’existe pas"}. {"Action on user","Action sur l'utilisateur"}. -{"Add Jabber ID","Ajouter un Jabber ID"}. -{"Add New","Ajouter"}. {"Add User","Ajouter un utilisateur"}. -{"Administration","Administration"}. {"Administration of ","Administration de "}. +{"Administration","Administration"}. {"Administrator privileges required","Les droits d'administrateur sont nécessaires"}. -{"A friendly name for the node","Un nom convivial pour le noeud"}. {"All activity","Toute activité"}. -{"Allow this Jabber ID to subscribe to this pubsub node?","Autoriser ce Jabber ID à s'abonner à ce nœud PubSub"}. +{"All Users","Tous les utilisateurs"}. +{"Allow subscription","Autoriser l’abonnement"}. +{"Allow this Jabber ID to subscribe to this pubsub node?","Autoriser ce Jabber ID à s'abonner à ce nœud PubSub ?"}. +{"Allow this person to register with the room?","Autoriser cette personne à enregistrer ce salon ?"}. {"Allow users to change the subject","Autoriser les utilisateurs à changer le sujet"}. -{"Allow users to query other users","Permettre aux utilisateurs d'envoyer des requêtes aux autres utilisateurs"}. -{"Allow users to send invites","Permettre aux utilisateurs d'envoyer des invitations"}. +{"Allow users to query other users","Autoriser les utilisateurs à envoyer des requêtes aux autres utilisateurs"}. +{"Allow users to send invites","Autoriser les utilisateurs à envoyer des invitations"}. {"Allow users to send private messages","Autoriser les utilisateurs à envoyer des messages privés"}. {"Allow visitors to change nickname","Autoriser les visiteurs à changer de pseudo"}. {"Allow visitors to send private messages to","Autoriser les visiteurs à envoyer des messages privés"}. {"Allow visitors to send status text in presence updates","Autoriser les visiteurs à envoyer un message d'état avec leur présence"}. {"Allow visitors to send voice requests","Permettre aux visiteurs d'envoyer des demandes de 'voice'"}. -{"All Users","Tous les utilisateurs"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","Un groupe LDAP associé qui définit l’adhésion à un salon ; cela devrait être un nom distingué LDAP selon la définition spécifique à l’implémentation ou au déploiement d’un groupe."}. {"Announcements","Annonces"}. -{"anyone","tout le monde"}. -{"A password is required to enter this room","Un mot de passe est nécessaire pour accèder à ce salon"}. +{"Answer associated with a picture","Réponse associée à une image"}. +{"Answer associated with a video","Réponse associée à une vidéo"}. +{"Answer associated with speech","Réponse associée à un discours"}. +{"Answer to a question","Réponse à une question"}. +{"Anyone in the specified roster group(s) may subscribe and retrieve items","N’importe qui dans le groupe de la liste spécifiée peut s’abonner et récupérer les items"}. +{"Anyone may associate leaf nodes with the collection","N’importe qui peut associer les feuilles avec la collection"}. +{"Anyone may publish","N’importe qui peut publier"}. +{"Anyone may subscribe and retrieve items","N’importe qui peut s’abonner et récupérer les items"}. +{"Anyone with a presence subscription of both or from may subscribe and retrieve items","N’importe qui avec un abonnement de présence peut s’abonner et récupérer les items"}. +{"Anyone with Voice","N’importe qui avec Voice"}. +{"Anyone","N’importe qui"}. {"April","Avril"}. +{"Attribute 'channel' is required for this request","L’attribut « channel » est requis pour la requête"}. +{"Attribute 'id' is mandatory for MIX messages","L’attribut « id » est obligatoire pour les messages MIX"}. +{"Attribute 'jid' is not allowed here","L’attribut « jid » n’est pas autorisé ici"}. +{"Attribute 'node' is not allowed here","L’attribut « node » n’est pas autorisé ici"}. +{"Attribute 'to' of stanza that triggered challenge","L’attribut « to » de la strophe qui a déclenché le défi"}. {"August","Août"}. +{"Automatic node creation is not enabled","La creation implicite de nœud n'est pas disponible"}. {"Backup Management","Gestion des sauvegardes"}. {"Backup of ~p","Sauvegarde de ~p"}. +{"Backup to File at ","Sauvegarde fichier sur "}. {"Backup","Sauvegarde"}. -{"Backup to File at ","Sauvegarde sur fichier sur "}. {"Bad format","Mauvais format"}. {"Birthday","Date d'anniversaire"}. +{"Both the username and the resource are required","Le nom d'utilisateur et sa ressource sont nécessaires"}. +{"Bytestream already activated","Le flux SOCKS5 est déjà activé"}. +{"Cannot remove active list","La liste active ne peut être supprimée"}. +{"Cannot remove default list","La liste par défaut ne peut être supprimée"}. {"CAPTCHA web page","Page web de CAPTCHA"}. +{"Challenge ID","Identifiant du défi"}. {"Change Password","Modifier le mot de passe"}. {"Change User Password","Changer le mot de passe de l'utilisateur"}. -{"Characters not allowed:","Caractères non-autorisés :"}. +{"Changing password is not allowed","La modification du mot de passe n'est pas autorisée"}. +{"Changing role/affiliation is not allowed","La modification role/affiliation n'est pas autorisée"}. +{"Channel already exists","Ce canal existe déjà"}. +{"Channel does not exist","Le canal n’existe pas"}. +{"Channels","Canaux"}. +{"Characters not allowed:","Caractères non autorisés :"}. {"Chatroom configuration modified","Configuration du salon modifiée"}. {"Chatroom is created","Le salon de discussion est créé"}. {"Chatroom is destroyed","Le salon de discussion est détruit"}. {"Chatroom is started","Le salon de discussion a démarré"}. {"Chatroom is stopped","Le salon de discussion est stoppé"}. {"Chatrooms","Salons de discussion"}. -{"Choose a username and password to register with this server","Choisissez un nom d'utilisateur et un mot de passe pour s'enregistrer sur ce serveur"}. -{"Choose modules to stop","Sélectionnez les modules à arrêter"}. +{"Choose a username and password to register with this server","Choisissez un nom d'utilisateur et un mot de passe pour ce serveur"}. {"Choose storage type of tables","Choisissez un type de stockage pour les tables"}. -{"Choose whether to approve this entity's subscription.","Accepter cet abonnement ?"}. +{"Choose whether to approve this entity's subscription.","Choisissez d'approuver ou non l'abonnement de cette entité."}. {"City","Ville"}. +{"Client acknowledged more stanzas than sent by server","Le client accuse réception de plus de strophes que ce qui est envoyé par le serveur"}. {"Commands","Commandes"}. -{"Conference room does not exist","La salle de conférence n'existe pas"}. -{"Configuration","Configuration"}. +{"Conference room does not exist","Le salon de discussion n'existe pas"}. {"Configuration of room ~s","Configuration pour le salon ~s"}. -{"Connected Resources:","Ressources connectées:"}. -{"Connections parameters","Paramètres de connexion"}. +{"Configuration","Configuration"}. +{"Contact Addresses (normally, room owner or owners)","Adresses de contact (normalement les administrateurs du salon)"}. {"Country","Pays"}. -{"CPU Time:","Temps CPU :"}. -{"Database","Base de données"}. -{"Database Tables at ~p","Tables de base de données sur ~p"}. +{"Current Discussion Topic","Sujet de discussion courant"}. +{"Database failure","Échec sur la base de données"}. {"Database Tables Configuration at ","Configuration des tables de base de données sur "}. +{"Database","Base de données"}. {"December","Décembre"}. -{"Default users as participants","Les utilisateurs sont par défaut participant"}. +{"Default users as participants","Les utilisateurs sont participant par défaut"}. {"Delete message of the day on all hosts","Supprimer le message du jour sur tous les domaines"}. {"Delete message of the day","Supprimer le message du jour"}. -{"Delete Selected","Suppression des éléments sélectionnés"}. {"Delete User","Supprimer l'utilisateur"}. {"Deliver event notifications","Envoyer les notifications d'événement"}. {"Deliver payloads with event notifications","Inclure le contenu du message avec la notification"}. -{"Description:","Description :"}. {"Disc only copy","Copie sur disque uniquement"}. -{"Displayed Groups:","Groupes affichés :"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","Ne révélez votre mot de passe à personne, pas même l'administrateur de ce serveur."}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","Ne révélez votre mot de passe à personne, pas même aux administrateurs du serveur XMPP."}. {"Dump Backup to Text File at ","Enregistrer la sauvegarde dans un fichier texte sur "}. {"Dump to Text File","Sauvegarder dans un fichier texte"}. +{"Duplicated groups are not allowed by RFC6121","Les groupes dupliqués ne sont pas autorisés par la RFC6121"}. +{"Dynamically specify a replyto of the item publisher","Spécifie dynamiquement un « réponse à » de l’item de l’éditeur"}. {"Edit Properties","Modifier les propriétés"}. -{"Either approve or decline the voice request.","Approuver ou refuser la demande de 'voice'"}. -{"ejabberd IRC module","Module IRC ejabberd"}. +{"Either approve or decline the voice request.","Accepter ou refuser la demande de voix."}. {"ejabberd MUC module","Module MUC ejabberd"}. {"ejabberd Multicast service","Service de Multidiffusion d'ejabberd"}. {"ejabberd Publish-Subscribe module","Module Publish-Subscribe d'ejabberd"}. -{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams module"}. +{"ejabberd SOCKS5 Bytestreams module","Module SOCKS5 Bytestreams per ejabberd"}. {"ejabberd vCard module","Module vCard ejabberd"}. {"ejabberd Web Admin","Console Web d'administration de ejabberd"}. -{"Elements","Éléments"}. -{"Email","Email"}. +{"ejabberd","ejabberd"}. +{"Email Address","Adresse courriel"}. +{"Email","Courriel"}. {"Enable logging","Activer l'archivage"}. {"Enable message archiving","Activer l'archivage de messages"}. -{"Encoding for server ~b","Codage pour le serveur ~b"}. +{"Enabling push without 'node' attribute is not supported","L'activation push ne peut se faire sans l'attribut 'node'"}. {"End User Session","Terminer la session de l'utilisateur"}. -{"Enter list of {Module, [Options]}","Entrez une liste de {Module, [Options]}"}. {"Enter nickname you want to register","Entrez le pseudo que vous souhaitez enregistrer"}. {"Enter path to backup file","Entrez le chemin vers le fichier de sauvegarde"}. -{"Enter path to jabberd14 spool dir","Entrez le chemin vers le répertoire de spool jabberd14"}. -{"Enter path to jabberd14 spool file","Entrez le chemin vers le fichier spool jabberd14"}. +{"Enter path to jabberd14 spool dir","Entrez le chemin vers le répertoire spool de Jabberd 1.4"}. +{"Enter path to jabberd14 spool file","Entrez le chemin vers le fichier spool de Jabberd 1.4"}. {"Enter path to text file","Entrez le chemin vers le fichier texte"}. {"Enter the text you see","Tapez le texte que vous voyez"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Entrez le nom d'utilisateur et les encodages que vous souhaitez utiliser pour vous connecter aux serveurs IRC. Appuyez sur 'Suivant' pour pour avoir d'autres champs à remplir. Appuyez sur 'Terminer' pour sauver les paramètres."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Entrez le nom d'utilisateur, les encodages, les ports et mots de passe que vous souhaitez utiliser pour vous connecter aux serveurs IRC"}. -{"Erlang Jabber Server","Serveur Jabber Erlang"}. -{"Error","Erreur"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Exemple: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. +{"Erlang XMPP Server","Serveur XMPP Erlang"}. {"Exclude Jabber IDs from CAPTCHA challenge","Exempter des Jabberd IDs du test CAPTCHA"}. -{"Export all tables as SQL queries to a file:","Exporter toutes les tables en tant que requêtes SQL vers un fichier:"}. -{"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exporter les données de tous les utilisateurs du serveur vers un fichier PIEFXIS (XEP-0227):"}. -{"Export data of users in a host to PIEFXIS files (XEP-0227):","Exporter les données utilisateurs d'un hôte vers un fichier PIEFXIS (XEP-0227):"}. +{"Export all tables as SQL queries to a file:","Exporter toutes les tables vers un fichier SQL :"}. +{"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exporter les données de tous les utilisateurs du serveur vers un fichier PIEFXIS (XEP-0227) :"}. +{"Export data of users in a host to PIEFXIS files (XEP-0227):","Exporter les données utilisateurs d'un hôte vers un fichier PIEFXIS (XEP-0227) :"}. +{"External component failure","Erreur de composant externe"}. +{"External component timeout","Dépassement de delai du composant externe"}. +{"Failed to activate bytestream","Échec d'activation de bytestream"}. +{"Failed to extract JID from your voice request approval","Échec d'extraction du JID dans la requête de voix"}. +{"Failed to map delegated namespace to external component","Échec d'association d'espace de nom vers un composant externe"}. +{"Failed to parse HTTP response","Échec de lecture de la réponse HTTP"}. +{"Failed to process option '~s'","Échec de traitement de l'option '~s'"}. {"Family Name","Nom de famille"}. +{"FAQ Entry","Entrée FAQ"}. {"February","Février"}. -{"Fill in fields to search for any matching Jabber User","Remplissez les champs pour rechercher un utilisateur Jabber"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Remplissez le formulaire pour recherche un utilisateur Jabber (Ajouter * à la fin du champ pour chercher n'importe quelle fin de chaîne"}. +{"File larger than ~w bytes","Taille de fichier suppérieur à ~w octets"}. +{"Fill in the form to search for any matching XMPP User","Complétez le formulaire pour rechercher un utilisateur XMPP correspondant"}. {"Friday","Vendredi"}. -{"From","De"}. -{"From ~s","De ~s"}. +{"From ~ts","De ~ts"}. +{"Full List of Room Admins","Liste complète des administrateurs des salons"}. +{"Full List of Room Owners","Liste complète des propriétaires des salons"}. {"Full Name","Nom complet"}. +{"Get List of Online Users","Récupérer les utilisateurs en ligne"}. +{"Get List of Registered Users","Récupérer les utilisateurs enregistrés"}. {"Get Number of Online Users","Récupérer le nombre d'utilisateurs en ligne"}. {"Get Number of Registered Users","Récupérer le nombre d'utilisateurs enregistrés"}. {"Get User Last Login Time","Récupérer la dernière date de connexion de l'utilisateur"}. -{"Get User Password","Récupérer le mot de passe de l'utilisateur"}. {"Get User Statistics","Récupérer les statistiques de l'utilisateur"}. -{"Grant voice to this person?","Accorder 'voice' à cet utilisateur"}. -{"Group ","Groupe "}. -{"Groups","Groupes"}. +{"Given Name","Nom"}. +{"Grant voice to this person?","Accorder le droit de parole à cet utilisateur ?"}. {"has been banned","a été banni"}. -{"has been kicked","a été expulsé"}. -{"has been kicked because of an affiliation change","a été éjecté à cause d'un changement d'autorisation"}. {"has been kicked because of a system shutdown","a été éjecté en raison de l'arrêt du système"}. +{"has been kicked because of an affiliation change","a été éjecté à cause d'un changement d'autorisation"}. {"has been kicked because the room has been changed to members-only","a été éjecté car la salle est désormais réservée aux membres"}. -{" has set the subject to: "," a changé le sujet pour: "}. -{"Host","Serveur"}. +{"has been kicked","a été expulsé"}. +{"Hats limit exceeded","La limite a été dépassée"}. +{"Host unknown","Serveur inconnu"}. +{"HTTP File Upload","Téléversement de fichier HTTP"}. +{"Idle connection","Connexion inactive"}. {"If you don't see the CAPTCHA image here, visit the web page.","SI vous ne voyez pas l'image CAPTCHA ici, visitez la page web."}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Si vous voulez préciser différents ports, mots de passe, et encodages pour les serveurs IRC, remplissez cette liste avec des valeurs dans le format '{\"serveur irc\", \"encodage\", port, \"mot de passe\"}'. Par défaut ce service utilise l'encodage \"~s\", port ~p, mot de passe vide."}. -{"Import Directory","Importer une répertoire"}. +{"Import Directory","Importer un répertoire"}. {"Import File","Importer un fichier"}. -{"Import user data from jabberd14 spool file:","Importer des utilisateurs depuis un fichier spool Jabberd 1.4:"}. +{"Import user data from jabberd14 spool file:","Importer des utilisateurs depuis un fichier spool Jabberd 1.4 :"}. {"Import User from File at ","Importer un utilisateur depuis le fichier sur "}. -{"Import users data from a PIEFXIS file (XEP-0227):","Importer les données utilisateurs à partir d'un fichier PIEFXIS (XEP-0227):"}. -{"Import users data from jabberd14 spool directory:","Importer des utilisateurs depuis un fichier spool Jabberd 1.4:"}. +{"Import users data from a PIEFXIS file (XEP-0227):","Importer les données utilisateurs à partir d'un fichier PIEFXIS (XEP-0227) :"}. +{"Import users data from jabberd14 spool directory:","Importer des utilisateurs depuis un fichier spool Jabberd 1.4 :"}. {"Import Users from Dir at ","Importer des utilisateurs depuis le répertoire sur "}. {"Import Users From jabberd14 Spool Files","Importer des utilisateurs depuis un fichier spool Jabberd 1.4"}. +{"Improper domain part of 'from' attribute","Le domaine de l'attribut 'from' est incorrect"}. {"Improper message type","Mauvais type de message"}. +{"Incorrect CAPTCHA submit","Entrée CAPTCHA incorrecte"}. +{"Incorrect data form","Formulaire incorrect"}. {"Incorrect password","Mot de passe incorrect"}. -{"Invalid affiliation: ~s","Affiliation invalide : ~s"}. -{"Invalid role: ~s","Role invalide : ~s"}. +{"Incorrect value of 'action' attribute","Valeur de l'attribut 'action' incorrecte"}. +{"Incorrect value of 'action' in data form","Valeur de l'attribut 'action' incorrecte dans le formulaire"}. +{"Incorrect value of 'path' in data form","Valeur de l'attribut 'path' incorrecte dans le formulaire"}. +{"Install","Installer"}. +{"Insufficient privilege","Droits insuffisants"}. +{"Internal server error","Erreur interne du serveur"}. +{"Invalid 'from' attribute in forwarded message","L'attribut 'from' du message transféré est incorrect"}. +{"Invalid node name","Nom de nœud invalide"}. +{"Invalid 'previd' value","Valeur 'previd' invalide"}. +{"Invitations are not allowed in this conference","Les invitations ne sont pas autorisées dans ce salon"}. {"IP addresses","Adresses IP"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","Canal IRC (ne pas insérer le premier caractère #)"}. -{"IRC server","Serveur IRC"}. -{"IRC settings","Configuration IRC"}. -{"IRC Transport","Passerelle IRC"}. -{"IRC username","Nom d'utilisateur IRC"}. -{"IRC Username","Nom d'utilisateur IRC"}. {"is now known as","est maintenant connu comme"}. -{"It is not allowed to send private messages","L'envoi de messages privés n'est pas autorisé"}. +{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","L'envoyer de messages d'erreur au salon n'est pas autorisé. Le participant (~s) à envoyé un message d'erreur (~s) et à été expulsé du salon"}. {"It is not allowed to send private messages of type \"groupchat\"","Il n'est pas permis d'envoyer des messages privés de type \"groupchat\""}. -{"It is not allowed to send private messages to the conference","Il n'est pas permis d'envoyer des messages \"normaux\" à la conférence"}. -{"Jabber Account Registration","Enregistrement du Compte Jabber"}. +{"It is not allowed to send private messages to the conference","Il n'est pas permis d'envoyer des messages privés à la conférence"}. {"Jabber ID","Jabber ID"}. -{"Jabber ID ~s is invalid","Le Jabber ID ~s n'est pas valide"}. {"January","Janvier"}. -{"Join IRC channel","Rejoindre un canal IRC"}. {"joins the room","rejoint le salon"}. -{"Join the IRC channel here.","Rejoindre un canal IRC ici"}. -{"Join the IRC channel in this Jabber ID: ~s","Rejoindre un canal IRC avec ce Jabber ID: ~s"}. {"July","Juillet"}. {"June","Juin"}. -{"Last Activity","Dernière Activité"}. +{"Just created","Vient d'être créé"}. +{"Last Activity","Dernière activité"}. {"Last login","Dernière connexion"}. +{"Last message","Dernier message"}. {"Last month","Dernier mois"}. {"Last year","Dernière année"}. {"leaves the room","quitte le salon"}. -{"Listened Ports at ","Ports ouverts sur "}. -{"Listened Ports","Ports ouverts"}. -{"List of modules to start","Liste des modules à démarrer"}. -{"List of rooms","Liste des salons"}. -{"Low level update script","Script de mise à jour de bas-niveau"}. {"Make participants list public","Rendre la liste des participants publique"}. {"Make room CAPTCHA protected","Protéger le salon par un CAPTCHA"}. {"Make room members-only","Réserver le salon aux membres uniquement"}. @@ -183,241 +215,329 @@ {"Make room password protected","Protéger le salon par mot de passe"}. {"Make room persistent","Rendre le salon persistant"}. {"Make room public searchable","Rendre le salon public"}. +{"Malformed username","Nom d'utilisateur invalide"}. {"March","Mars"}. -{"Maximum Number of Occupants","Nombre maximum d'occupants"}. -{"Max # of items to persist","Nombre maximum d'éléments à stocker"}. {"Max payload size in bytes","Taille maximum pour le contenu du message en octet"}. +{"Maximum file size","Taille maximale du fichier"}. +{"Maximum Number of History Messages Returned by Room","Nombre maximal de messages d'historique renvoyés par salle"}. +{"Maximum number of items to persist","Nombre maximal d'éléments à conserver"}. +{"Maximum Number of Occupants","Nombre maximal d'occupants"}. {"May","Mai"}. {"Membership is required to enter this room","Vous devez être membre pour accèder à ce salon"}. -{"Members:","Membres :"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Mémorisez votre mot de passe, ou écrivez-le sur un papier conservé dans un endroit secret. Dans Jabber il n'y a pas de mécanisme pour retrouver votre mot de passe si vous l'avez oublié."}. -{"Memory","Mémoire"}. +{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Mémorisez votre mot de passe, ou écrivez-le sur un papier conservé dans un endroit secret. Dans XMPP il n'y a pas de mécanisme pour retrouver votre mot de passe si vous l'avez oublié."}. {"Message body","Corps du message"}. +{"Message not found in forwarded payload","Message non trouvé dans l'enveloppe transférée"}. +{"Messages from strangers are rejected","Les messages d'étrangers sont rejetés"}. +{"Messages of type headline","Messages de type titre"}. +{"Messages of type normal","Messages de type normal"}. {"Middle Name","Autre nom"}. {"Minimum interval between voice requests (in seconds)","Intervalle minimum entre les demandes de 'voice' (en secondes)"}. {"Moderator privileges required","Les droits de modérateur sont nécessaires"}. -{"moderators only","modérateurs seulement"}. -{"Modified modules","Modules mis à jour"}. -{"Module","Module"}. -{"Modules at ~p","Modules sur ~p"}. -{"Modules","Modules"}. +{"Moderator","Modérateur"}. +{"Moderators Only","Modérateurs uniquement"}. +{"Module failed to handle the query","Échec de traitement de la demande"}. {"Monday","Lundi"}. {"Multicast","Multidiffusion"}. +{"Multiple elements are not allowed by RFC6121","Les multiples éléments ne sont pas autorisés avec RFC6121"}. {"Multi-User Chat","Discussion de groupe"}. -{"Name:","Nom :"}. {"Name","Nom"}. +{"Natural Language for Room Discussions","Langue naturelle pour les discussions en salle"}. +{"Natural-Language Room Name","Nom de la salle en langue naturelle"}. +{"Neither 'jid' nor 'nick' attribute found","Attribut 'jid' ou 'nick' absent"}. +{"Neither 'role' nor 'affiliation' attribute found","Attribut 'role' ou 'affiliation' absent"}. {"Never","Jamais"}. -{"New Password:","Nouveau mot de passe:"}. -{"Nickname","Pseudo"}. +{"New Password:","Nouveau mot de passe :"}. +{"Nickname can't be empty","Le pseudonyme ne peut être laissé vide"}. {"Nickname Registration at ","Enregistrement d'un pseudo sur "}. {"Nickname ~s does not exist in the room","Le pseudo ~s n'existe pas dans ce salon"}. -{"nobody","personne"}. +{"Nickname","Pseudo"}. +{"No address elements found","Aucun élément d'adresse trouvé"}. +{"No addresses element found","Aucun élément d'adresses trouvé"}. +{"No 'affiliation' attribute found","Attribut 'affiliation' absent"}. +{"No available resource found","Aucune ressource disponible"}. {"No body provided for announce message","Pas de corps de message pour l'annonce"}. +{"No child elements found","Aucun élément enfant trouvé"}. +{"No data form found","Formulaire non trouvé"}. {"No Data","Aucune information disponible"}. -{"Node ID","Identifiant du nœud"}. -{"Node not found","Noeud non trouvé"}. -{"Node ~p","Noeud ~p"}. -{"Nodes","Noeuds"}. +{"No features available","Aucune fonctionalité disponible"}. +{"No element found","Aucun élément trouvé"}. +{"No hook has processed this command","Aucun gestionnaire n'a pris en charge cette commande"}. +{"No info about last activity found","Aucune activité précédente trouvée"}. +{"No 'item' element found","Aucun élément 'item' trouvé"}. +{"No items found in this query","Aucun item trouvé dans cette requête"}. {"No limit","Pas de limite"}. +{"No module is handling this query","Aucun module ne supporte cette requête"}. +{"No node specified","Nœud non spécifié"}. +{"No 'password' found in data form","Entrée 'password' absente du formulaire"}. +{"No 'password' found in this query","L'élément 'password' est absent de la requête"}. +{"No 'path' found in data form","Entrée 'path' absente du formulaire"}. +{"No pending subscriptions found","Aucune demande d'abonnement trouvée"}. +{"No privacy list with this name found","Liste non trouvée"}. +{"No private data found in this query","Aucune donnée privée trouvée dans cette requête"}. +{"No running node found","Nœud non trouvé"}. +{"No services available","Aucun service disponible"}. +{"No statistics found for this item","Pas de statistiques"}. +{"No 'to' attribute found in the invitation","L'élément 'to' est absent de l'invitation"}. +{"Nobody","Personne"}. +{"Node already exists","Ce nœud existe déjà"}. +{"Node ID","Identifiant du nœud"}. +{"Node index not found","Index de nœud non trouvé"}. +{"Node not found","Nœud non trouvé"}. +{"Node ~p","Nœud ~p"}. +{"Node","Nœud"}. +{"Nodeprep has failed","Échec de formattage"}. +{"Nodes","Nœuds"}. {"None","Aucun"}. -{"No resource provided","Aucune ressource fournie"}. +{"Not allowed","Non autorisé"}. {"Not Found","Nœud non trouvé"}. +{"Not subscribed","Pas abonné"}. {"Notify subscribers when items are removed from the node","Avertir les abonnés lorsque des éléments sont supprimés sur le nœud"}. {"Notify subscribers when the node configuration changes","Avertir les abonnés lorsque la configuration du nœud change"}. {"Notify subscribers when the node is deleted","Avertir les abonnés lorsque le nœud est supprimé"}. {"November","Novembre"}. +{"Number of answers required","Nombre de réponses requises"}. {"Number of occupants","Nombre d'occupants"}. +{"Number of Offline Messages","Nombre de messages hors ligne"}. {"Number of online users","Nombre d'utilisateurs en ligne"}. {"Number of registered users","Nombre d'utilisateurs enregistrés"}. +{"Occupants are allowed to invite others","Les occupants sont autorisés à inviter d’autres personnes"}. +{"Occupants May Change the Subject","Les occupants peuvent changer le sujet"}. {"October","Octobre"}. -{"Offline Messages:","Messages en attente :"}. -{"Offline Messages","Messages en attente"}. {"OK","OK"}. -{"Old Password:","Ancien mot de passe:"}. -{"Online","En ligne"}. -{"Online Users:","Utilisateurs connectés:"}. +{"Old Password:","Ancien mot de passe :"}. {"Online Users","Utilisateurs en ligne"}. +{"Online","En ligne"}. {"Only deliver notifications to available users","Envoyer les notifications uniquement aux utilisateurs disponibles"}. +{"Only or tags are allowed","Seul le tag ou est autorisé"}. +{"Only element is allowed in this query","Seul l'élément est autorisé dans cette requête"}. +{"Only members may query archives of this room","Seuls les membres peuvent accéder aux archives de ce salon"}. {"Only moderators and participants are allowed to change the subject in this room","Seuls les modérateurs et les participants peuvent changer le sujet dans ce salon"}. {"Only moderators are allowed to change the subject in this room","Seuls les modérateurs peuvent changer le sujet dans ce salon"}. +{"Only moderators can approve voice requests","Seuls les modérateurs peuvent accépter les requêtes voix"}. {"Only occupants are allowed to send messages to the conference","Seuls les occupants peuvent envoyer des messages à la conférence"}. {"Only occupants are allowed to send queries to the conference","Seuls les occupants sont autorisés à envoyer des requêtes à la conférence"}. +{"Only publishers may publish","Seuls les éditeurs peuvent publier"}. {"Only service administrators are allowed to send service messages","Seuls les administrateurs du service sont autoriser à envoyer des messages de service"}. -{"Options","Options"}. {"Organization Name","Nom de l'organisation"}. {"Organization Unit","Unité de l'organisation"}. -{"Outgoing s2s Connections:","Connexions s2s sortantes:"}. {"Outgoing s2s Connections","Connexions s2s sortantes"}. {"Owner privileges required","Les droits de propriétaire sont nécessaires"}. -{"Packet","Paquet"}. -{"Password ~b","Mot de passe ~b"}. -{"Password:","Mot de passe:"}. -{"Password","Mot de passe"}. -{"Password Verification:","Vérification du mot de passe :"}. +{"Participant","Participant"}. {"Password Verification","Vérification du mot de passe"}. +{"Password Verification:","Vérification du mot de passe :"}. +{"Password","Mot de passe"}. +{"Password:","Mot de passe :"}. {"Path to Dir","Chemin vers le répertoire"}. {"Path to File","Chemin vers le fichier"}. -{"Pending","En suspens"}. -{"Period: ","Période :"}. -{"Permanent rooms","Salons persistent"}. +{"Period: ","Période : "}. {"Persist items to storage","Stockage persistant des éléments"}. +{"Persistent","Persistant"}. +{"Ping query is incorrect","Requête ping incorrecte"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Ces options sauvegardent uniquement la base de données interne Mnesia. Si vous utilisez le module ODBC vous devez sauvegarde votre base SQL séparément."}. +{"Please, wait for a while before sending new voice request","Attendez un moment avant de re-lancer une requête de voix"}. {"Pong","Pong"}. -{"Port ~b","Port ~b"}. -{"Port","Port"}. +{"Possessing 'ask' attribute is not allowed by RFC6121","L'appartenance de l'attribut 'ask' n'est pas autorisé avec RFC6121"}. {"Present real Jabber IDs to","Rendre le Jabber ID réel visible pour"}. -{"private, ","privé"}. -{"Protocol","Protocole"}. +{"Previous session not found","Session précédente introuvable"}. +{"Previous session PID has been killed","Le précédent PID de session a été tuée"}. +{"Previous session PID has exited","Le précédent PID de session a quitté"}. +{"Previous session PID is dead","Le précédent PID de session est mort"}. +{"Previous session timed out","La session précédente a expiré"}. +{"private, ","privé, "}. +{"Public","Public"}. {"Publish-Subscribe","Publication-Abonnement"}. {"PubSub subscriber request","Demande d'abonnement PubSub"}. {"Purge all items when the relevant publisher goes offline","Purger tous les items lorsque publieur est hors-ligne"}. {"Queries to the conference members are not allowed in this room","Les requêtes sur les membres de la conférence ne sont pas autorisé dans ce salon"}. +{"Query to another users is forbidden","Requête vers un autre utilisateur interdite"}. {"RAM and disc copy","Copie en mémoire vive (RAM) et sur disque"}. {"RAM copy","Copie en mémoire vive (RAM)"}. -{"Raw","Brut"}. {"Really delete message of the day?","Confirmer la suppression du message du jour ?"}. +{"Receive notification from all descendent nodes","Recevoir les notifications de tous les nœuds descendants"}. +{"Receive notification from direct child nodes only","Recevoir les notifications des nœuds enfants seulement"}. +{"Receive notification of new items only","Recevoir les notifications des nouveaux éléments uniquement"}. +{"Receive notification of new nodes only","Recevoir les notifications de tous les nouveaux nœuds descendants"}. {"Recipient is not in the conference room","Le destinataire n'est pas dans la conférence"}. -{"Register a Jabber account","Enregistrer un compte Jabber"}. -{"Registered nicknames","Pseudos enregistrés"}. -{"Registered Users:","Utilisateurs enregistrés:"}. -{"Registered Users","Utilisateurs enregistrés"}. +{"Register an XMPP account","Inscrire un compte XMPP"}. {"Register","Enregistrer"}. -{"Registration in mod_irc for ","Enregistrement du mod_irc pour "}. {"Remote copy","Copie distante"}. -{"Remove All Offline Messages","Effacer tous les messages hors ligne"}. -{"Remove","Enlever"}. {"Remove User","Supprimer l'utilisateur"}. {"Replaced by new connection","Remplacé par une nouvelle connexion"}. +{"Request has timed out","La demande a expiré"}. +{"Request is ignored","La demande est ignorée"}. +{"Requested role","Rôle demandé"}. {"Resources","Ressources"}. -{"Restart","Redémarrer"}. {"Restart Service","Redémarrer le service"}. {"Restore Backup from File at ","Restaurer la sauvegarde depuis le fichier sur "}. -{"Restore binary backup after next ejabberd restart (requires less memory):","Restauration de la sauvegarde binaire après redémarrage (nécessite moins de mémoire):"}. -{"Restore binary backup immediately:","Restauration immédiate d'une sauvegarde binaire:"}. -{"Restore plain text backup immediately:","Restauration immédiate d'une sauvegarde texte:"}. +{"Restore binary backup after next ejabberd restart (requires less memory):","Restauration de la sauvegarde binaire après redémarrage (nécessite moins de mémoire) :"}. +{"Restore binary backup immediately:","Restauration immédiate d'une sauvegarde binaire :"}. +{"Restore plain text backup immediately:","Restauration immédiate d'une sauvegarde texte :"}. {"Restore","Restauration"}. {"Room Configuration","Configuration du salon"}. {"Room creation is denied by service policy","La création de salons est interdite par le service"}. -{"Room description","Description :"}. +{"Room description","Description du salon"}. {"Room Occupants","Occupants du salon"}. {"Room title","Titre du salon"}. {"Roster groups allowed to subscribe","Groupes de liste de contact autorisés à s'abonner"}. -{"Roster","Liste de contacts"}. -{"Roster of ","Liste de contact de "}. {"Roster size","Taille de la liste de contacts"}. -{"RPC Call Error","Erreur d'appel RPC"}. -{"Running Nodes","Noeuds actifs"}. -{"~s access rule configuration","Configuration des règles d'accès ~s"}. +{"Running Nodes","Nœuds actifs"}. +{"~s invites you to the room ~s","~s vous invite dans la salle de discussion ~s"}. {"Saturday","Samedi"}. -{"Script check","Validation du script"}. {"Search Results for ","Résultats de recherche pour "}. +{"Search the text","Recherche le texte"}. +{"Search until the date","Rechercher jusqu’à la date"}. {"Search users in ","Rechercher des utilisateurs "}. -{"Send announcement to all online users","Envoyer l'annonce à tous les utilisateurs en ligne"}. {"Send announcement to all online users on all hosts","Envoyer l'annonce à tous les utilisateurs en ligne sur tous les serveurs"}. -{"Send announcement to all users","Envoyer l'annonce à tous les utilisateurs"}. +{"Send announcement to all online users","Envoyer l'annonce à tous les utilisateurs en ligne"}. {"Send announcement to all users on all hosts","Envoyer une annonce à tous les utilisateurs de tous les domaines"}. +{"Send announcement to all users","Envoyer l'annonce à tous les utilisateurs"}. {"September","Septembre"}. -{"Server ~b","Serveur ~b"}. -{"Server:","Serveur :"}. +{"Server:","Serveur :"}. +{"Service list retrieval timed out","La récupération de la liste des services a expiré"}. {"Set message of the day and send to online users","Définir le message du jour et l'envoyer aux utilisateurs en ligne"}. {"Set message of the day on all hosts and send to online users","Définir le message du jour pour tous domaines et l'envoyer aux utilisateurs en ligne"}. {"Shared Roster Groups","Groupes de liste de contacts partagée"}. {"Show Integral Table","Montrer la table intégralement"}. {"Show Ordinary Table","Montrer la table ordinaire"}. {"Shut Down Service","Arrêter le service"}. -{"~s invites you to the room ~s","~s vous a invité dans la salle de discussion ~s"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Certains clients Jabber peuvent stocker votre mot de passe sur votre ordinateur. N'utilisez cette fonctionnalité que si vous avez confiance en la sécurité de votre ordinateur."}. +{"SOCKS5 Bytestreams","SOCKS5 Bytestreams"}. +{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Certains clients XMPP peuvent stocker votre mot de passe sur votre ordinateur. N'utilisez cette fonctionnalité que si vous avez confiance en la sécurité de votre ordinateur."}. {"Specify the access model","Définir le modèle d'accès"}. {"Specify the event message type","Définir le type de message d'événement"}. {"Specify the publisher model","Définir le modèle de publication"}. -{"~s's Offline Messages Queue","~s messages en file d'attente"}. -{"Start","Démarrer"}. -{"Start Modules at ","Démarrer les modules sur "}. -{"Start Modules","Modules de démarrage"}. -{"Statistics of ~p","Statistiques de ~p"}. -{"Statistics","Statistiques"}. -{"Stop","Arrêter"}. -{"Stop Modules at ","Arrêter les modules sur "}. -{"Stop Modules","Modules d'arrêt"}. -{"Stopped Nodes","Noeuds arrêtés"}. -{"Storage Type","Type de stockage"}. -{"Store binary backup:","Sauvegarde binaire:"}. -{"Store plain text backup:","Sauvegarde texte:"}. +{"Stanza ID","Identifiant Stanza"}. +{"Stopped Nodes","Nœuds arrêtés"}. +{"Store binary backup:","Sauvegarde binaire :"}. +{"Store plain text backup:","Sauvegarde texte :"}. +{"Stream management is already enabled","La gestion des flux est déjà activée"}. {"Subject","Sujet"}. -{"Submit","Soumettre"}. {"Submitted","Soumis"}. {"Subscriber Address","Adresse de l'abonné"}. -{"Subscription","Abonnement"}. +{"Subscribers may publish","Les souscripteurs peuvent publier"}. +{"Subscriptions are not allowed","Les abonnement ne sont pas autorisés"}. {"Sunday","Dimanche"}. +{"Text associated with a picture","Texte associé à une image"}. +{"Text associated with a sound","Texte associé à un son"}. +{"Text associated with a video","Texte associé à une vidéo"}. +{"Text associated with speech","Texte associé au discours"}. {"That nickname is already in use by another occupant","Le pseudo est déjà utilisé par un autre occupant"}. {"That nickname is registered by another person","Le pseudo est enregistré par une autre personne"}. -{"The CAPTCHA is valid.","Le CAPTCHA est valide"}. +{"The account already exists","Le compte existe déjà"}. +{"The account was not unregistered","Le compte n’a pas été désinscrit"}. +{"The body text of the last received message","Le corps du texte du dernier message reçu"}. +{"The CAPTCHA is valid.","Le CAPTCHA est valide."}. {"The CAPTCHA verification has failed","La vérification du CAPTCHA a échoué"}. +{"The captcha you entered is wrong","Le captcha que vous avez saisi est erroné"}. {"The collections with which a node is affiliated","Les collections avec lesquelle un nœud est affilié"}. -{"the password is","le mot de passe est"}. +{"The datetime when the node was created","La date à laquelle le nœud a été créé"}. +{"The default language of the node","La langue par défaut du nœud"}. +{"The feature requested is not supported by the conference","La demande de fonctionalité n'est pas supportée par la conférence"}. +{"The JID of the node creator","Le JID du créateur du nœud"}. +{"The list of all online users","Les utilisateurs en ligne"}. +{"The list of all users","La liste de tous les utilisateurs"}. +{"The name of the node","Le nom du nœud"}. +{"The node is a collection node","Le nœud est un nœud de collecte"}. +{"The node is a leaf node (default)","Ce nœud est un nœud feuille (défaut)"}. +{"The number of subscribers to the node","Le nombre d’enregistrés au nœud"}. +{"The number of unread or undelivered messages","Le nombre de messages non lus ou non remis"}. +{"The password contains unacceptable characters","Le mot de passe contient des caractères non-acceptables"}. {"The password is too weak","Le mot de passe est trop faible"}. -{"The password of your Jabber account was successfully changed.","Le mot de passe de votre compte Jabber a été changé avec succès."}. -{"There was an error changing the password: ","Il y a eu une erreur en changeant le mot de passe :"}. -{"There was an error creating the account: ","Il y a eu une erreur en créant le compte :"}. -{"There was an error deleting the account: ","Il y a eu une erreur en effaçant le compte :"}. -{"This IP address is blacklisted in ~s","Cette adresse IP est blacklistée dans ~s"}. +{"the password is","le mot de passe est"}. +{"The password of your XMPP account was successfully changed.","Le mot de passe de votre compte XMPP a été modifié avec succès."}. +{"The password was not changed","Le mot de passe n’a pas été modifié"}. +{"The passwords are different","Les mots de passe sont différents"}. +{"The query is only allowed from local users","La requête n'est autorisé qu'aux utilisateurs locaux"}. +{"The query must not contain elements","La requête ne doit pas contenir d'élément "}. +{"The room subject can be modified by participants","Le sujet du salon peut être modifié par les participants"}. +{"The sender of the last received message","L’expéditeur du dernier message reçu"}. +{"The subscription identifier associated with the subscription request","L’identificateur d’abonnement associé à la demande d’abonnement"}. +{"There was an error changing the password: ","Une erreur s’est produite lors de la modification du mot de passe : "}. +{"There was an error creating the account: ","Il y a eu une erreur en créant le compte : "}. +{"There was an error deleting the account: ","Il y a eu une erreur en effaçant le compte : "}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","C'est insensible à la casse : macbeth est identique à MacBeth et Macbeth."}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Cette page permet de créer un compte Jabber sur ce serveur Jabber. Votre JID (Jabber IDentifier, identifiant Jabber) sera de la forme : nom@serveur. Prière de lire avec attention les instructions pour remplir correctement ces champs."}. -{"This page allows to unregister a Jabber account in this Jabber server.","Cette page permet d'effacer un compte Jabber sur ce serveur Jabber."}. +{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Cette page permet de créer un compte XMPP sur ce serveur XMPP. Votre JID (Jabber IDentifier, identifiant Jabber) sera de la forme : nom@serveur. Prière de lire avec attention les instructions pour remplir correctement ces champs."}. +{"This page allows to unregister an XMPP account in this XMPP server.","Cette page permet de désenregistrer un compte XMPP sur ce serveur XMPP."}. +{"This room is not anonymous","Ce salon n'est pas anonyme"}. +{"This service can not process the address: ~s","Ce service ne peut pas traiter l’adresse : ~s"}. {"Thursday","Jeudi"}. {"Time delay","Délais"}. -{"Time","Heure"}. -{"To","A"}. +{"Timed out waiting for stream resumption","Expiration du délai d’attente pour la reprise du flux"}. +{"To register, visit ~s","Pour vous enregistrer, visitez ~s"}. +{"To ~ts","À ~ts"}. +{"Token TTL","Jeton TTL"}. +{"Too many active bytestreams","Trop de flux SOCKS5 actifs"}. +{"Too many CAPTCHA requests","Trop de requêtes CAPTCHA"}. +{"Too many elements","Trop d'éléments "}. +{"Too many elements","Trop d'éléments "}. {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Trop (~p) d'authentification ont échoué pour cette adresse IP (~s). L'adresse sera débloquée à ~s UTC"}. +{"Too many receiver fields were specified","Trop de champs de récepteurs ont été spécifiés"}. {"Too many unacked stanzas","Trop de stanzas sans accusé de réception (ack)"}. -{"To ~s","A ~s"}. -{"Total rooms","Nombre de salons"}. +{"Too many users in this conference","Trop d'utilisateurs dans cette conférence"}. {"Traffic rate limit is exceeded","La limite de trafic a été dépassée"}. -{"Transactions Aborted:","Transactions annulées :"}. -{"Transactions Committed:","Transactions commitées :"}. -{"Transactions Logged:","Transactions journalisées :"}. -{"Transactions Restarted:","Transactions redémarrées :"}. {"Tuesday","Mardi"}. {"Unable to generate a CAPTCHA","Impossible de générer le CAPTCHA"}. +{"Unable to register route on existing local domain","Impossible d'enregistrer la route sur un domaine locale existant"}. {"Unauthorized","Non autorisé"}. -{"Unregister a Jabber account","Effacer un compte Jabber"}. -{"Unregister","Effacer"}. +{"Unexpected action","Action inattendu"}. +{"Unexpected error condition: ~p","Condition d’erreur inattendue : ~p"}. +{"Uninstall","Désinstaller"}. +{"Unregister an XMPP account","Annuler l’enregistrement d’un compte XMPP"}. +{"Unregister","Désinscrire"}. +{"Unsupported element","Elément non supporté"}. +{"Unsupported version","Version non prise en charge"}. {"Update message of the day (don't send)","Mise à jour du message du jour (pas d'envoi)"}. {"Update message of the day on all hosts (don't send)","Mettre à jour le message du jour sur tous les domaines (ne pas envoyer)"}. -{"Update","Mettre à jour"}. -{"Update plan","Plan de mise à jour"}. -{"Update ~p","Mise à jour de ~p"}. -{"Update script","Script de mise à jour"}. -{"Uptime:","Temps depuis le démarrage :"}. -{"Use of STARTTLS required","L'utilisation de STARTTLS est impérative"}. -{"User JID","JID de l'utilisateur "}. +{"Upgrade","Mise à niveau"}. +{"URL for Archived Discussion Logs","URL des journaux de discussion archivés"}. +{"User already exists","L'utilisateur existe déjà"}. +{"User JID","JID de l'utilisateur"}. +{"User (jid)","Utilisateur (jid)"}. {"User Management","Gestion des utilisateurs"}. -{"Username:","Nom d'utilisateur :"}. +{"User removed","Utilisateur supprimé"}. +{"User session not found","Session utilisateur non trouvée"}. +{"User session terminated","Session utilisateur terminée"}. +{"User ~ts","Utilisateur ~ts"}. +{"Username:","Nom d'utilisateur :"}. {"Users are not allowed to register accounts so quickly","Les utilisateurs ne sont pas autorisés à enregistrer des comptes si rapidement"}. {"Users Last Activity","Dernière activité des utilisateurs"}. -{"User ~s","Utilisateur ~s"}. {"Users","Utilisateurs"}. {"User","Utilisateur"}. -{"Validate","Valider"}. +{"Value 'get' of 'type' attribute is not allowed","La valeur de l'attribut 'type' ne peut être 'get'"}. +{"Value of '~s' should be boolean","La valeur de '~s' ne peut être booléen"}. +{"Value of '~s' should be datetime string","La valeur de '~s' doit être une chaine datetime"}. +{"Value of '~s' should be integer","La valeur de '~s' doit être un entier"}. +{"Value 'set' of 'type' attribute is not allowed","La valeur de l'attribut 'type' ne peut être 'set'"}. {"vCard User Search","Recherche dans l'annnuaire"}. {"Virtual Hosts","Serveurs virtuels"}. {"Visitors are not allowed to change their nicknames in this room","Les visiteurs ne sont pas autorisés à changer de pseudo dans ce salon"}. {"Visitors are not allowed to send messages to all occupants","Les visiteurs ne sont pas autorisés à envoyer des messages à tout les occupants"}. -{"Voice request","Demande de 'voice'"}. +{"Visitor","Visiteur"}. +{"Voice request","Demande de voix"}. +{"Voice requests are disabled in this conference","Les demandes de voix sont désactivées dans cette conférence"}. {"Wednesday","Mercredi"}. +{"When a new subscription is processed","Quand un nouvel abonnement est traité"}. {"When to send the last published item","A quel moment envoyer le dernier élément publié"}. -{"Whether to allow subscriptions","Autoriser l'abonnement ?"}. -{"You can later change your password using a Jabber client.","Vous pouvez changer votre mot de passe plus tard en utilisant un client Jabber."}. +{"Whether an entity wants to receive or disable notifications","Quand une entité veut recevoir ou désactiver les notifications"}. +{"Whether owners or publisher should receive replies to items","Quand les propriétaires ou annonceurs doivent revoir des réponses à leurs éléments"}. +{"Whether to allow subscriptions","Autoriser ou non les abonnements"}. +{"Whether to notify owners about new subscribers and unsubscribes","Quand notifier le propriétaire à propos des nouvelles souscriptions et désinscriptions"}. +{"Wrong parameters in the web formulary","Paramètres erronés dans le formulaire Web"}. +{"Wrong xmlns","Xmlns incorrect"}. +{"XMPP Account Registration","Enregistrement de compte XMPP"}. +{"XMPP Domains","Domaines XMPP"}. +{"You are being removed from the room because of a system shutdown","Vous avez été éjecté du salon de discussion en raison de l'arrêt du système"}. +{"You are not joined to the channel","Vous n'avez pas rejoint ce canal"}. +{"You can later change your password using an XMPP client.","Vous pouvez modifier ultérieurement votre mot de passe à l’aide d’un client XMPP."}. {"You have been banned from this room","Vous avez été exclus de ce salon"}. +{"You have joined too many conferences","Vous avec rejoint trop de conférences"}. {"You must fill in field \"Nickname\" in the form","Vous devez préciser le champ \"pseudo\" dans le formulaire"}. {"You need a client that supports x:data and CAPTCHA to register","Vous avez besoin d'un client prenant en charge x:data et CAPTCHA pour enregistrer un pseudo"}. {"You need a client that supports x:data to register the nickname","Vous avez besoin d'un client prenant en charge x:data pour enregistrer un pseudo"}. -{"You need an x:data capable client to configure mod_irc settings","Vous avez besoin d'un client supportant x:data pour configurer le module IRC"}. -{"You need an x:data capable client to configure room","Vous avez besoin d'un client supportant x:data pour configurer le salon"}. {"You need an x:data capable client to search","Vous avez besoin d'un client supportant x:data pour faire une recherche"}. {"Your active privacy list has denied the routing of this stanza.","Votre règle de flitrage active a empêché le routage de ce stanza."}. {"Your contact offline message queue is full. The message has been discarded.","La file d'attente de message de votre contact est pleine. Votre message a été détruit."}. -{"Your Jabber account was successfully created.","Votre compte Jabber a été créé avec succès."}. -{"Your Jabber account was successfully deleted.","Votre compte Jabber a été effacé avec succès."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","Vos messages pour ~s sont bloqués. Pour les débloquer, veuillez visiter ~s"}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Vos messages pour ~s sont bloqués. Pour les débloquer, veuillez visiter ~s"}. +{"Your XMPP account was successfully registered.","Votre compte XMPP a été enregistré avec succès."}. +{"Your XMPP account was successfully unregistered.","Votre compte XMPP a été désinscrit avec succès."}. +{"You're not allowed to create nodes","Vous n'êtes pas autorisé à créer des nœuds"}. diff --git a/priv/msgs/fr.po b/priv/msgs/fr.po deleted file mode 100644 index 47a1377e8..000000000 --- a/priv/msgs/fr.po +++ /dev/null @@ -1,1963 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: 2.1.0-alpha\n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: Nicolas Vérité \n" -"Language-Team: \n" -"Language: fr\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: French (française)\n" -"X-Additional-Translator: Christophe Romain\n" -"X-Additional-Translator: Mickaël Rémond\n" -"X-Additional-Translator: Vincent Ricard\n" -"X-Generator: Poedit 1.8.4\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "L'utilisation de STARTTLS est impérative" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Aucune ressource fournie" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Remplacé par une nouvelle connexion" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "a été expulsé" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "Votre règle de flitrage active a empêché le routage de ce stanza." - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "Trop de stanzas sans accusé de réception (ack)" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Tapez le texte que vous voyez" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "" -"Vos messages pour ~s sont bloqués. Pour les débloquer, veuillez visiter ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "SI vous ne voyez pas l'image CAPTCHA ici, visitez la page web." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "Page web de CAPTCHA" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "Le CAPTCHA est valide" - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Utilisateur" - -#: ejabberd_oauth.erl:256 -#, fuzzy -msgid "Server" -msgstr "Serveur :" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Mot de passe" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "Non autorisé" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "Console Web d'administration de ejabberd" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Administration" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Droits (ACL)" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Soumis" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Mauvais format" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Soumettre" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "Brut" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Suppression des éléments sélectionnés" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Règles d'accès" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "Configuration des règles d'accès ~s" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "Serveurs virtuels" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Utilisateurs" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "Utilisateurs en ligne" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Dernière activité des utilisateurs" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Période :" - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "Dernier mois" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "Dernière année" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "Toute activité" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Montrer la table ordinaire" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Montrer la table intégralement" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Statistiques" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "Nœud non trouvé" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Noeud non trouvé" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Ajouter" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Serveur" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Utilisateurs enregistrés" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Ajouter un utilisateur" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Messages en attente" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Dernière Activité" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Jamais" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "En ligne" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Utilisateurs enregistrés:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Utilisateurs connectés:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Connexions s2s sortantes:" - -#: ejabberd_web_admin.erl:1559 -#, fuzzy -msgid "Incoming s2s Connections:" -msgstr "Connexions s2s sortantes:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Aucun" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Modifier le mot de passe" - -#: ejabberd_web_admin.erl:1673 -msgid "User ~s" -msgstr "Utilisateur ~s" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Ressources connectées:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Mot de passe:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Supprimer l'utilisateur" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Aucune information disponible" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Noeuds" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Noeuds actifs" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Noeuds arrêtés" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -msgid "Node ~p" -msgstr "Noeud ~p" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "Base de données" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Sauvegarde" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Ports ouverts" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Mettre à jour" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Redémarrer" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Arrêter" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Modules" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "Erreur d'appel RPC" - -#: ejabberd_web_admin.erl:1917 -msgid "Database Tables at ~p" -msgstr "Tables de base de données sur ~p" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Nom" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Type de stockage" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Éléments" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Mémoire" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Erreur" - -#: ejabberd_web_admin.erl:1955 -msgid "Backup of ~p" -msgstr "Sauvegarde de ~p" - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Ces options sauvegardent uniquement la base de données interne Mnesia. Si " -"vous utilisez le module ODBC vous devez sauvegarde votre base SQL séparément." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Sauvegarde binaire:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "OK" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Restauration immédiate d'une sauvegarde binaire:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"Restauration de la sauvegarde binaire après redémarrage (nécessite moins de " -"mémoire):" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Sauvegarde texte:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Restauration immédiate d'une sauvegarde texte:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "" -"Importer les données utilisateurs à partir d'un fichier PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" -"Exporter les données de tous les utilisateurs du serveur vers un fichier " -"PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "" -"Exporter les données utilisateurs d'un hôte vers un fichier PIEFXIS " -"(XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "Exporter toutes les tables en tant que requêtes SQL vers un fichier:" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Importer des utilisateurs depuis un fichier spool Jabberd 1.4:" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Importer des utilisateurs depuis un fichier spool Jabberd 1.4:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Ports ouverts sur " - -#: ejabberd_web_admin.erl:2144 -msgid "Modules at ~p" -msgstr "Modules sur ~p" - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "Statistiques de ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Temps depuis le démarrage :" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "Temps CPU :" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Transactions commitées :" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Transactions annulées :" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Transactions redémarrées :" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Transactions journalisées :" - -#: ejabberd_web_admin.erl:2243 -msgid "Update ~p" -msgstr "Mise à jour de ~p" - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "Plan de mise à jour" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Modules mis à jour" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Script de mise à jour" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Script de mise à jour de bas-niveau" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Validation du script" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Port" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Protocole" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Module" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Options" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Démarrer" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Commandes" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "Confirmer la suppression du message du jour ?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Sujet" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Corps du message" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "Pas de corps de message pour l'annonce" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Annonces" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Envoyer l'annonce à tous les utilisateurs" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Envoyer une annonce à tous les utilisateurs de tous les domaines" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Envoyer l'annonce à tous les utilisateurs en ligne" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "" -"Envoyer l'annonce à tous les utilisateurs en ligne sur tous les serveurs" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Définir le message du jour et l'envoyer aux utilisateurs en ligne" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" -"Définir le message du jour pour tous domaines et l'envoyer aux utilisateurs " -"en ligne" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Mise à jour du message du jour (pas d'envoi)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "" -"Mettre à jour le message du jour sur tous les domaines (ne pas envoyer)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Supprimer le message du jour" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Supprimer le message du jour sur tous les domaines" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Configuration" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Modules de démarrage" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Modules d'arrêt" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Restauration" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Sauvegarder dans un fichier texte" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Importer un fichier" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Importer une répertoire" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Redémarrer le service" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Arrêter le service" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Supprimer l'utilisateur" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Terminer la session de l'utilisateur" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Récupérer le mot de passe de l'utilisateur" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Changer le mot de passe de l'utilisateur" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "Récupérer la dernière date de connexion de l'utilisateur" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Récupérer les statistiques de l'utilisateur" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Récupérer le nombre d'utilisateurs enregistrés" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Récupérer le nombre d'utilisateurs en ligne" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Gestion des utilisateurs" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Tous les utilisateurs" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Connexions s2s sortantes" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Gestion des sauvegardes" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Importer des utilisateurs depuis un fichier spool Jabberd 1.4" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "A ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "De ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Configuration des tables de base de données sur " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Choisissez un type de stockage pour les tables" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Copie sur disque uniquement" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "Copie en mémoire vive (RAM) et sur disque" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "Copie en mémoire vive (RAM)" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "Copie distante" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Arrêter les modules sur " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Sélectionnez les modules à arrêter" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Démarrer les modules sur " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Entrez une liste de {Module, [Options]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Liste des modules à démarrer" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Sauvegarde sur fichier sur " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Entrez le chemin vers le fichier de sauvegarde" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Chemin vers le fichier" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Restaurer la sauvegarde depuis le fichier sur " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Enregistrer la sauvegarde dans un fichier texte sur " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Entrez le chemin vers le fichier texte" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Importer un utilisateur depuis le fichier sur " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Entrez le chemin vers le fichier spool jabberd14" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Importer des utilisateurs depuis le répertoire sur " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Entrez le chemin vers le répertoire de spool jabberd14" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Chemin vers le répertoire" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "Délais" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Configuration des droits (ACL)" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Droits (ACL)" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Configuration d'accès" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Règles d'accès" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Vérification du mot de passe" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Nombre d'utilisateurs enregistrés" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Nombre d'utilisateurs en ligne" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Dernière connexion" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "Taille de la liste de contacts" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "Adresses IP" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Ressources" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Administration de " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Action sur l'utilisateur" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Modifier les propriétés" - -#: mod_fail2ban.erl:95 -msgid "" -"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" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "" - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "" - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "Cette adresse IP est blacklistée dans ~s" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "L'accès au service est refusé" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "Passerelle IRC" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "Module IRC ejabberd" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "" -"Vous avez besoin d'un client supportant x:data pour configurer le module IRC" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Enregistrement du mod_irc pour " - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Entrez le nom d'utilisateur, les encodages, les ports et mots de passe que " -"vous souhaitez utiliser pour vous connecter aux serveurs IRC" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "Nom d'utilisateur IRC" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Si vous voulez préciser différents ports, mots de passe, et encodages pour " -"les serveurs IRC, remplissez cette liste avec des valeurs dans le format " -"'{\"serveur irc\", \"encodage\", port, \"mot de passe\"}'. Par défaut ce " -"service utilise l'encodage \"~s\", port ~p, mot de passe vide." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Exemple: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Paramètres de connexion" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "Rejoindre un canal IRC" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "Canal IRC (ne pas insérer le premier caractère #)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "Serveur IRC" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Rejoindre un canal IRC ici" - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Rejoindre un canal IRC avec ce Jabber ID: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "Configuration IRC" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Entrez le nom d'utilisateur et les encodages que vous souhaitez utiliser " -"pour vous connecter aux serveurs IRC. Appuyez sur 'Suivant' pour pour avoir " -"d'autres champs à remplir. Appuyez sur 'Terminer' pour sauver les paramètres." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "Nom d'utilisateur IRC" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Mot de passe ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Port ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Codage pour le serveur ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Serveur ~b" - -#: mod_mam.erl:541 -#, fuzzy -msgid "Only members may query archives of this room" -msgstr "Seuls les modérateurs peuvent changer le sujet dans ce salon" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "" -"Seuls les administrateurs du service sont autoriser à envoyer des messages " -"de service" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "La création de salons est interdite par le service" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "La salle de conférence n'existe pas" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Salons de discussion" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "" -"Vous avez besoin d'un client prenant en charge x:data pour enregistrer un " -"pseudo" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Enregistrement d'un pseudo sur " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Entrez le pseudo que vous souhaitez enregistrer" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Pseudo" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "Le pseudo est enregistré par une autre personne" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Vous devez préciser le champ \"pseudo\" dans le formulaire" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "Module MUC ejabberd" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "Discussion de groupe" - -#: mod_muc_admin.erl:249 -msgid "Total rooms" -msgstr "Nombre de salons" - -#: mod_muc_admin.erl:250 -msgid "Permanent rooms" -msgstr "Salons persistent" - -#: mod_muc_admin.erl:251 -msgid "Registered nicknames" -msgstr "Pseudos enregistrés" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "Liste des salons" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "Configuration du salon modifiée" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "rejoint le salon" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "quitte le salon" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "a été banni" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "a été éjecté à cause d'un changement d'autorisation" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "a été éjecté car la salle est désormais réservée aux membres" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "a été éjecté en raison de l'arrêt du système" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "est maintenant connu comme" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " a changé le sujet pour: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "Le salon de discussion est créé" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "Le salon de discussion est détruit" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "Le salon de discussion a démarré" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "Le salon de discussion est stoppé" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "Lundi" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "Mardi" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "Mercredi" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "Jeudi" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "Vendredi" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "Samedi" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "Dimanche" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "Janvier" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "Février" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "Mars" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "Avril" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "Mai" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "Juin" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "Juillet" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "Août" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "Septembre" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "Octobre" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "Novembre" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "Décembre" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Configuration du salon" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "Occupants du salon" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "La limite de trafic a été dépassée" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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 "" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "Il n'est pas permis d'envoyer des messages \"normaux\" à la conférence" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "" - -#: mod_muc_room.erl:377 -#, fuzzy -msgid "Only moderators can approve voice requests" -msgstr "Permettre aux visiteurs d'envoyer des demandes de 'voice'" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Mauvais type de message" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "" -"Il n'est pas permis d'envoyer des messages privés de type \"groupchat\"" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "Le destinataire n'est pas dans la conférence" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "L'envoi de messages privés n'est pas autorisé" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Seuls les occupants peuvent envoyer des messages à la conférence" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "" -"Seuls les occupants sont autorisés à envoyer des requêtes à la conférence" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "" -"Les requêtes sur les membres de la conférence ne sont pas autorisé dans ce " -"salon" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "" -"Seuls les modérateurs et les participants peuvent changer le sujet dans ce " -"salon" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Seuls les modérateurs peuvent changer le sujet dans ce salon" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "" -"Les visiteurs ne sont pas autorisés à envoyer des messages à tout les " -"occupants" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "Les visiteurs ne sont pas autorisés à changer de pseudo dans ce salon" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "Le pseudo est déjà utilisé par un autre occupant" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Vous avez été exclus de ce salon" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "Vous devez être membre pour accèder à ce salon" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "Un mot de passe est nécessaire pour accèder à ce salon" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "Impossible de générer le CAPTCHA" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Mot de passe incorrect" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "Les droits d'administrateur sont nécessaires" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "Les droits de modérateur sont nécessaires" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "Le Jabber ID ~s n'est pas valide" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "Le pseudo ~s n'existe pas dans ce salon" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Affiliation invalide : ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Role invalide : ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "Les droits de propriétaire sont nécessaires" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "Configuration pour le salon ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Titre du salon" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "Description :" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Rendre le salon persistant" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Rendre le salon public" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "Rendre la liste des participants publique" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Protéger le salon par mot de passe" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Nombre maximum d'occupants" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Pas de limite" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Rendre le Jabber ID réel visible pour" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "modérateurs seulement" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "tout le monde" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "" - -#: mod_muc_room.erl:3486 -#, fuzzy -msgid "Moderator" -msgstr "modérateurs seulement" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Réserver le salon aux membres uniquement" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Rendre le salon modéré" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Les utilisateurs sont par défaut participant" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Autoriser les utilisateurs à changer le sujet" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Autoriser les utilisateurs à envoyer des messages privés" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "Autoriser les visiteurs à envoyer des messages privés" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "personne" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "" -"Permettre aux utilisateurs d'envoyer des requêtes aux autres utilisateurs" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Permettre aux utilisateurs d'envoyer des invitations" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "Autoriser les visiteurs à envoyer un message d'état avec leur présence" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Autoriser les visiteurs à changer de pseudo" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "Permettre aux visiteurs d'envoyer des demandes de 'voice'" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "Intervalle minimum entre les demandes de 'voice' (en secondes)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "Protéger le salon par un CAPTCHA" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "Activer l'archivage de messages" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "Exempter des Jabberd IDs du test CAPTCHA" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Activer l'archivage" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "" -"Vous avez besoin d'un client supportant x:data pour configurer le salon" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Nombre d'occupants" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "privé" - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "Demande de 'voice'" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "Approuver ou refuser la demande de 'voice'" - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "JID de l'utilisateur " - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "Accorder 'voice' à cet utilisateur" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s vous a invité dans la salle de discussion ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "le mot de passe est" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "Multidiffusion" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "Service de Multidiffusion d'ejabberd" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "" -"La file d'attente de message de votre contact est pleine. Votre message a " -"été détruit." - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "~s messages en file d'attente" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Heure" - -#: mod_offline.erl:812 -msgid "From" -msgstr "De" - -#: mod_offline.erl:813 -msgid "To" -msgstr "A" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Paquet" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Messages en attente :" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "Effacer tous les messages hors ligne" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "ejabberd SOCKS5 Bytestreams module" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Publication-Abonnement" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "Module Publish-Subscribe d'ejabberd" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "Demande d'abonnement PubSub" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Accepter cet abonnement ?" - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "Identifiant du nœud" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Adresse de l'abonné" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "Autoriser ce Jabber ID à s'abonner à ce nœud PubSub" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Inclure le contenu du message avec la notification" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Envoyer les notifications d'événement" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Avertir les abonnés lorsque la configuration du nœud change" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Avertir les abonnés lorsque le nœud est supprimé" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Avertir les abonnés lorsque des éléments sont supprimés sur le nœud" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Stockage persistant des éléments" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "Un nom convivial pour le noeud" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Nombre maximum d'éléments à stocker" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Autoriser l'abonnement ?" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Définir le modèle d'accès" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Groupes de liste de contact autorisés à s'abonner" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Définir le modèle de publication" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "Purger tous les items lorsque publieur est hors-ligne" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "Définir le type de message d'événement" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Taille maximum pour le contenu du message en octet" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "A quel moment envoyer le dernier élément publié" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Envoyer les notifications uniquement aux utilisateurs disponibles" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "Les collections avec lesquelle un nœud est affilié" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "La vérification du CAPTCHA a échoué" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "" -"Vous avez besoin d'un client prenant en charge x:data et CAPTCHA pour " -"enregistrer un pseudo" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "" -"Choisissez un nom d'utilisateur et un mot de passe pour s'enregistrer sur ce " -"serveur" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "Le mot de passe est trop faible" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "" -"Les utilisateurs ne sont pas autorisés à enregistrer des comptes si " -"rapidement" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "Votre compte Jabber a été créé avec succès." - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "Il y a eu une erreur en créant le compte :" - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "Votre compte Jabber a été effacé avec succès." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "Il y a eu une erreur en effaçant le compte :" - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "Le mot de passe de votre compte Jabber a été changé avec succès." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "Il y a eu une erreur en changeant le mot de passe :" - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Enregistrement du Compte Jabber" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Enregistrer un compte Jabber" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Effacer un compte Jabber" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"Cette page permet de créer un compte Jabber sur ce serveur Jabber. Votre JID " -"(Jabber IDentifier, identifiant Jabber) sera de la forme : nom@serveur. " -"Prière de lire avec attention les instructions pour remplir correctement ces " -"champs." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "Nom d'utilisateur :" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" -"C'est insensible à la casse : macbeth est identique à MacBeth et Macbeth." - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "Caractères non-autorisés :" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "Serveur :" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" -"Ne révélez votre mot de passe à personne, pas même l'administrateur de ce " -"serveur." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "" -"Vous pouvez changer votre mot de passe plus tard en utilisant un client " -"Jabber." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Certains clients Jabber peuvent stocker votre mot de passe sur votre " -"ordinateur. N'utilisez cette fonctionnalité que si vous avez confiance en la " -"sécurité de votre ordinateur." - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"Mémorisez votre mot de passe, ou écrivez-le sur un papier conservé dans un " -"endroit secret. Dans Jabber il n'y a pas de mécanisme pour retrouver votre " -"mot de passe si vous l'avez oublié." - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "Vérification du mot de passe :" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "Enregistrer" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "Ancien mot de passe:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "Nouveau mot de passe:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "Cette page permet d'effacer un compte Jabber sur ce serveur Jabber." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "Effacer" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Abonnement" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "En suspens" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Groupes" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Valider" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Enlever" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Liste de contact de " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Ajouter un Jabber ID" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Liste de contacts" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Groupes de liste de contacts partagée" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Nom :" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Description :" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Membres :" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Groupes affichés :" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Groupe " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Serveur Jabber Erlang" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Date d'anniversaire" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Ville" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "Pays" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "Email" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Nom de famille" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Remplissez le formulaire pour recherche un utilisateur Jabber (Ajouter * à " -"la fin du champ pour chercher n'importe quelle fin de chaîne" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Nom complet" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Autre nom" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Nom de l'organisation" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Unité de l'organisation" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Rechercher des utilisateurs " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "" -"Vous avez besoin d'un client supportant x:data pour faire une recherche" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "Recherche dans l'annnuaire" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "Module vCard ejabberd" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Résultats de recherche pour " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "Remplissez les champs pour rechercher un utilisateur Jabber" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Serveurs s2s sortants" - -#~ msgid "Delete" -#~ msgstr "Supprimer" - -#~ msgid "This room is not anonymous" -#~ msgstr "Ce salon n'est pas anonyme" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "" -#~ "Ce participant est expulsé du salon pour avoir envoyé un message erronée" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Ce participant est expulsé du salon pour avoir envoyé un message erronée " -#~ "à un autre participant" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "" -#~ "Ce participant est expulsé du salon pour avoir envoyé une présence erronée" - -#, fuzzy -#~ msgid "Captcha test failed" -#~ msgstr "Le CAPTCHA est valide" - -#~ msgid "Encodings" -#~ msgstr "Encodages" - -#~ msgid "(Raw)" -#~ msgstr "(Brut)" diff --git a/priv/msgs/gl.msg b/priv/msgs/gl.msg index b3e364d5d..a72e07360 100644 --- a/priv/msgs/gl.msg +++ b/priv/msgs/gl.msg @@ -1,150 +1,152 @@ -%% -*- coding: latin-1 -*- -{"Access Configuration","Configuración de accesos"}. -{"Access Control List Configuration","Configuración da Lista de Control de Acceso"}. -{"Access control lists","Listas de Control de Acceso"}. -{"Access Control Lists","Listas de Control de Acceso"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" has set the subject to: "," puxo o asunto: "}. +{"A friendly name for the node","Un nome sinxelo para o nodo"}. +{"A password is required to enter this room","Necesítase contrasinal para entrar nesta sala"}. +{"Accept","Aceptar"}. {"Access denied by service policy","Acceso denegado pola política do servizo"}. -{"Access rules","Regras de acceso"}. -{"Access Rules","Regras de Acceso"}. {"Action on user","Acción no usuario"}. -{"Add Jabber ID","Engadir ID Jabber"}. -{"Add New","Engadir novo"}. {"Add User","Engadir usuario"}. -{"Administration","Administración"}. {"Administration of ","Administración de "}. +{"Administration","Administración"}. {"Administrator privileges required","Necesítase privilexios de administrador"}. -{"A friendly name for the node","Un nome para o nodo"}. {"All activity","Toda a actividade"}. +{"All Users","Todos os usuarios"}. {"Allow this Jabber ID to subscribe to this pubsub node?","Desexas permitir a este JabberID que se subscriba a este nodo PubSub?"}. {"Allow users to change the subject","Permitir aos usuarios cambiar o asunto"}. {"Allow users to query other users","Permitir aos usuarios consultar a outros usuarios"}. {"Allow users to send invites","Permitir aos usuarios enviar invitacións"}. {"Allow users to send private messages","Permitir aos usuarios enviar mensaxes privadas"}. {"Allow visitors to change nickname","Permitir aos visitantes cambiarse o alcume"}. +{"Allow visitors to send private messages to","Permitir aos visitantes enviar mensaxes privadas a"}. {"Allow visitors to send status text in presence updates","Permitir aos visitantes enviar texto de estado nas actualizacións depresenza"}. -{"All Users","Todos os usuarios"}. +{"Allow visitors to send voice requests","Permitir aos visitantes enviar peticións de voz"}. {"Announcements","Anuncios"}. -{"anyone","calquera"}. -{"A password is required to enter this room","Necesítase contrasinal para entrar nesta sala"}. {"April","Abril"}. {"August","Agosto"}. -{"Backup","Gardar copia de seguridade"}. +{"Automatic node creation is not enabled","A creación automática de nodos non está habilitada"}. {"Backup Management","Xestión de copia de seguridade"}. +{"Backup of ~p","Copia de seguridade de ~p"}. {"Backup to File at ","Copia de seguridade de arquivos en "}. +{"Backup","Copia de seguridade"}. {"Bad format","Mal formato"}. {"Birthday","Aniversario"}. +{"Both the username and the resource are required","Tanto o nome de usuario como o recurso son necesarios"}. +{"Bytestream already activated","Bytestream xa está activado"}. +{"Cannot remove active list","Non se pode eliminar a lista activa"}. +{"Cannot remove default list","Non se pode eliminar a lista predeterminada"}. +{"CAPTCHA web page","CAPTCHA páxina Web"}. {"Change Password","Cambiar contrasinal"}. {"Change User Password","Cambiar contrasinal de usuario"}. -{"Chatroom configuration modified","Configuración de la sala modificada"}. +{"Changing password is not allowed","Non se permite cambiar o contrasinal"}. +{"Changing role/affiliation is not allowed","O cambio de rol/afiliación non está permitido"}. +{"Characters not allowed:","Caracteres non permitidos:"}. +{"Chatroom configuration modified","Configuración da sala modificada"}. +{"Chatroom is created","Creouse a sala"}. +{"Chatroom is destroyed","Destruíuse a sala"}. +{"Chatroom is started","Iniciouse a sala"}. +{"Chatroom is stopped","Detívose a sala"}. {"Chatrooms","Salas de charla"}. {"Choose a username and password to register with this server","Escolle un nome de usuario e contrasinal para rexistrarche neste servidor"}. -{"Choose modules to stop","Selecciona módulos a deter"}. {"Choose storage type of tables","Selecciona tipo de almacenamento das táboas"}. {"Choose whether to approve this entity's subscription.","Decidir se aprobar a subscripción desta entidade."}. {"City","Cidade"}. {"Commands","Comandos"}. {"Conference room does not exist","A sala de conferencias non existe"}. -{"Configuration","Configuración"}. {"Configuration of room ~s","Configuración para a sala ~s"}. -{"Connected Resources:","Recursos conectados:"}. -{"Connections parameters","Parámetros de conexiones"}. +{"Configuration","Configuración"}. {"Country","País"}. -{"CPU Time:","Tempo consumido de CPU:"}. -{"Database","Base de datos"}. +{"Database failure","Erro na base de datos"}. {"Database Tables Configuration at ","Configuración de táboas da base de datos en "}. +{"Database","Base de datos"}. {"December","Decembro"}. {"Default users as participants","Os usuarios son participantes por defecto"}. -{"Delete message of the day","Borrar mensaxe do dia"}. {"Delete message of the day on all hosts","Borrar a mensaxe do día en todos os dominios"}. -{"Delete Selected","Eliminar os seleccionados"}. +{"Delete message of the day","Borrar mensaxe do dia"}. {"Delete User","Borrar usuario"}. {"Deliver event notifications","Entregar notificacións de eventos"}. {"Deliver payloads with event notifications","Enviar payloads xunto coas notificacións de eventos"}. -{"Description:","Descrición:"}. {"Disc only copy","Copia en disco soamente"}. -{"Displayed Groups:","Mostrar grupos:"}. {"Dump Backup to Text File at ","Exporta copia de seguridade a ficheiro de texto en "}. {"Dump to Text File","Exportar a ficheiro de texto"}. -{"Edit Properties","Editar propiedades"}. -{"ejabberd IRC module","Módulo de IRC para ejabberd"}. +{"Edit Properties","Editar Propiedades"}. +{"Either approve or decline the voice request.","Aproba ou rexeita a petición de voz."}. {"ejabberd MUC module","Módulo de MUC para ejabberd"}. +{"ejabberd Multicast service","Servizo Multicast de ejabberd"}. {"ejabberd Publish-Subscribe module","Módulo de Publicar-Subscribir de ejabberd"}. -{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams module"}. +{"ejabberd SOCKS5 Bytestreams module","Módulo SOCKS5 Bytestreams para ejabberd"}. {"ejabberd vCard module","Módulo vCard para ejabberd"}. -{"ejabberd Web Admin","Ejabberd Administrador Web"}. -{"Elements","Elementos"}. +{"ejabberd Web Admin","ejabberd Administrador Web"}. {"Email","Email"}. {"Enable logging","Gardar históricos"}. -{"Encoding for server ~b","Codificación de servidor ~b"}. +{"Enable message archiving","Activar o almacenamento de mensaxes"}. +{"Enabling push without 'node' attribute is not supported","Non se admite a activación do empuxe sen o atributo 'nodo'"}. {"End User Session","Pechar sesión de usuario"}. -{"Enter list of {Module, [Options]}","Introduce lista de {Módulo, [Opcións]}"}. {"Enter nickname you want to register","Introduce o alcume que queiras rexistrar"}. {"Enter path to backup file","Introduce ruta ao ficheiro de copia de seguridade"}. {"Enter path to jabberd14 spool dir","Introduce a ruta ao directorio de jabberd14 spools"}. {"Enter path to jabberd14 spool file","Introduce ruta ao ficheiro jabberd14 spool"}. {"Enter path to text file","Introduce ruta ao ficheiro de texto"}. {"Enter the text you see","Introduza o texto que ves"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Introduce o nome de usuario e codificaciones de carácteres que queiras usar ao conectar nos servidores de IRC. Presione 'Siguiente' para obtener más campos para rellenar Presione 'completo' para guardar axustes."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Introduza o nome de usuario, codificaciones de carácter, portos e contrasinal que pretende utilizar a conectar a servidores de IRC"}. -{"Erlang Jabber Server","Servidor Jabber en Erlang"}. -{"Error","Erro"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Exemplo: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. +{"Exclude Jabber IDs from CAPTCHA challenge","Excluír Jabber IDs das probas de CAPTCHA"}. +{"Export all tables as SQL queries to a file:","Exportar todas as táboas a un ficheiro SQL:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportar datos de todos os usuarios do servidor a ficheros PIEFXIS (XEP-0227):"}. -{"Export data of users in a host to PIEFXIS files (XEP-0227):","Exportar datos de todos os usuarios do servidor a ficheros PIEFXIS (XEP-0227):"}. +{"Export data of users in a host to PIEFXIS files (XEP-0227):","Exportar datos dos usuarios dun dominio a ficheiros PIEFXIS (XEP-0227):"}. +{"External component failure","Fallo de compoñente externo"}. +{"External component timeout","Paso o tempo de espera do compoñente externo"}. +{"Failed to activate bytestream","Fallo ao activar bytestream"}. +{"Failed to extract JID from your voice request approval","Fallo ao extraer o Jabber ID da túa aprobación de petición de voz"}. +{"Failed to map delegated namespace to external component","O mapeo de espazo de nomes delegado fallou ao compoñente externo"}. +{"Failed to parse HTTP response","Non se puido analizar a resposta HTTP"}. +{"Failed to process option '~s'","Fallo ao procesar a opción '~s'"}. {"Family Name","Apelido"}. {"February","Febreiro"}. -{"Fill in fields to search for any matching Jabber User","Rechea campos para buscar usuarios Jabber que concuerden"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Enche o formulario para buscar usuarios Jabber. Engade * ao final dun campo para buscar subcadenas."}. +{"File larger than ~w bytes","O ficheiro é maior que ~w bytes"}. {"Friday","Venres"}. -{"From","De"}. -{"From ~s","De ~s"}. {"Full Name","Nome completo"}. {"Get Number of Online Users","Ver número de usuarios conectados"}. {"Get Number of Registered Users","Ver número de usuarios rexistrados"}. {"Get User Last Login Time","Ver data da última conexión de usuario"}. -{"Get User Password","Ver contrasinal de usuario"}. {"Get User Statistics","Ver estatísticas de usuario"}. -{"Group ","Grupo "}. -{"Groups","Grupos"}. +{"Given Name","Nome"}. +{"Grant voice to this person?","¿Conceder voz a esta persoa?"}. {"has been banned","foi bloqueado"}. +{"has been kicked because of a system shutdown","foi expulsado porque o sistema vaise a deter"}. {"has been kicked because of an affiliation change","foi expulsado debido a un cambio de afiliación"}. -{"has been kicked because of a system shutdown","foi expulsado por mor dun sistema de peche"}. {"has been kicked because the room has been changed to members-only","foi expulsado, porque a sala cambiouse a só-membros"}. {"has been kicked","foi expulsado"}. -{" has set the subject to: "," puxo o asunto: "}. -{"Host","Host"}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Se quere especificar codificaciones de caracteres diferentes, contrasinal ou servidor IRC rechea esta lista con valores no formato '{\"servidor irc\", \"codificación\", \"porto\", \"contrasinal\"}'. Este servizo utiliza por defecto a codificación \"~s\", porto ~p, sen contrasinal."}. +{"Host unknown","Dominio descoñecido"}. +{"If you don't see the CAPTCHA image here, visit the web page.","Si non ves a imaxe CAPTCHA aquí, visita a páxina web."}. {"Import Directory","Importar directorio"}. {"Import File","Importar ficheiro"}. -{"Import user data from jabberd14 spool file:","Importar usuario de fichero spool de jabberd14:"}. +{"Import user data from jabberd14 spool file:","Importar usuario de ficheiro spool de jabberd14:"}. {"Import User from File at ","Importa usuario desde ficheiro en "}. -{"Import users data from a PIEFXIS file (XEP-0227):","Importar usuarios desde un fichero PIEFXIS"}. +{"Import users data from a PIEFXIS file (XEP-0227):","Importar usuarios en un fichero PIEFXIS (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Importar usuarios do directorio spool de jabberd14:"}. {"Import Users from Dir at ","Importar usuarios desde o directorio en "}. {"Import Users From jabberd14 Spool Files","Importar usuarios de ficheiros spool de jabberd-1.4"}. +{"Improper domain part of 'from' attribute","Parte de dominio impropio no atributo 'from'"}. {"Improper message type","Tipo de mensaxe incorrecta"}. +{"Incorrect CAPTCHA submit","O CAPTCHA proporcionado é incorrecto"}. +{"Incorrect data form","Formulario de datos incorrecto"}. {"Incorrect password","Contrasinal incorrecta"}. -{"Invalid affiliation: ~s","Afiliación non válida: ~s"}. -{"Invalid role: ~s","Rol non válido: ~s"}. +{"Incorrect value of 'action' attribute","Valor incorrecto do atributo 'action'"}. +{"Incorrect value of 'action' in data form","Valor incorrecto de 'action' no formulario de datos"}. +{"Incorrect value of 'path' in data form","Valor incorrecto de 'path' no formulario de datos"}. +{"Insufficient privilege","Privilexio insuficiente"}. +{"Invalid 'from' attribute in forwarded message","Atributo 'from'' non é válido na mensaxe reenviada"}. +{"Invitations are not allowed in this conference","As invitacións non están permitidas nesta sala"}. {"IP addresses","Direccións IP"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","Canle de IRC (non poñer o primeiro #)"}. -{"IRC server","Servidor IRC"}. -{"IRC settings","IRC axustes"}. -{"IRC Transport","Transporte IRC"}. -{"IRC username","Nome de usuario en IRC"}. -{"IRC Username","Nome de usuario en IRC"}. -{"is now known as","cámbiase o nome a"}. -{"It is not allowed to send private messages","Non está permitido enviar mensaxes privadas"}. +{"is now known as","agora coñécese como"}. +{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Non está permitido enviar mensaxes de erro á sala. Este participante (~s) enviou unha mensaxe de erro (~s) e foi expulsado da sala"}. {"It is not allowed to send private messages of type \"groupchat\"","Non está permitido enviar mensaxes privadas do tipo \"groupchat\""}. {"It is not allowed to send private messages to the conference","Impedir o envio de mensaxes privadas á sala"}. {"Jabber ID","Jabber ID"}. -{"Jabber ID ~s is invalid","O Jabber ID ~s non é válido"}. {"January","Xaneiro"}. -{"Join IRC channel","Entrar en canle IRC"}. -{"joins the room","entra en la sala"}. -{"Join the IRC channel here.","Únete á canle de IRC aquí."}. -{"Join the IRC channel in this Jabber ID: ~s","Únete á canle de IRC con este IDE de Jabber: ~s"}. +{"joins the room","entra na sala"}. {"July","Xullo"}. {"June","Xuño"}. {"Last Activity","Última actividade"}. @@ -152,10 +154,6 @@ {"Last month","Último mes"}. {"Last year","Último ano"}. {"leaves the room","sae da sala"}. -{"Listened Ports at ","Portos de escoita en "}. -{"Listened Ports","Portos de escoita"}. -{"List of modules to start","Lista de módulos a iniciar"}. -{"Low level update script","Script de actualización a baixo nivel"}. {"Make participants list public","A lista de participantes é pública"}. {"Make room CAPTCHA protected","Protexer a sala con CAPTCHA"}. {"Make room members-only","Sala só para membros"}. @@ -163,37 +161,63 @@ {"Make room password protected","Protexer a sala con contrasinal"}. {"Make room persistent","Sala permanente"}. {"Make room public searchable","Sala publicamente visible"}. +{"Malformed username","Nome de usuario mal formado"}. {"March","Marzo"}. -{"Maximum Number of Occupants","Número máximo de ocupantes"}. -{"Max # of items to persist","Máximo # de elementos que persisten"}. {"Max payload size in bytes","Máximo tamaño do payload en bytes"}. +{"Maximum Number of Occupants","Número máximo de ocupantes"}. {"May","Maio"}. {"Membership is required to enter this room","Necesitas ser membro desta sala para poder entrar"}. -{"Members:","Membros:"}. -{"Memory","Memoria"}. {"Message body","Corpo da mensaxe"}. +{"Message not found in forwarded payload","Mensaxe non atopada no contido reenviado"}. {"Middle Name","Segundo nome"}. +{"Minimum interval between voice requests (in seconds)","Intervalo mínimo entre peticións de voz (en segundos)"}. {"Moderator privileges required","Necesítase privilexios de moderador"}. -{"moderators only","só moderadores"}. -{"Modified modules","Módulos Modificados"}. -{"Module","Módulo"}. -{"Modules","Módulos"}. +{"Moderator","Moderator"}. +{"Module failed to handle the query","O módulo non puido xestionar a consulta"}. {"Monday","Luns"}. -{"Name:","Nome:"}. +{"Multicast","Multicast"}. +{"Multi-User Chat","Salas de Charla"}. {"Name","Nome"}. +{"Neither 'jid' nor 'nick' attribute found","Non se atopou o atributo 'jid' nin 'nick'"}. +{"Neither 'role' nor 'affiliation' attribute found","Non se atopou o atributo 'role' nin 'affiliation'"}. {"Never","Nunca"}. -{"Nickname","Alcume"}. +{"New Password:","Novo contrasinal:"}. {"Nickname Registration at ","Rexistro do alcume en "}. {"Nickname ~s does not exist in the room","O alcume ~s non existe na sala"}. +{"Nickname","Alcume"}. +{"No 'affiliation' attribute found","Non se atopou o atributo de 'affiliation'"}. +{"No available resource found","Non se atopou ningún recurso"}. {"No body provided for announce message","Non se proporcionou corpo de mensaxe para o anuncio"}. +{"No data form found","Non se atopou formulario de datos"}. {"No Data","Sen datos"}. -{"Node ID","Nodo IDE"}. -{"Node not found","Nodo non atopado"}. -{"Nodes","Nodos"}. +{"No features available","Non hai características dispoñibles"}. +{"No hook has processed this command","Ningún evento procesou este comando"}. +{"No info about last activity found","Non se atopou información sobre a última actividade"}. +{"No 'item' element found","Non se atopou o elemento 'item'"}. +{"No items found in this query","Non se atoparon elementos nesta consulta"}. {"No limit","Sen límite"}. +{"No module is handling this query","Ningún módulo manexa esta consulta"}. +{"No node specified","Non se especificou nodo"}. +{"No 'password' found in data form","Non se atopou 'password' no formulario de datos"}. +{"No 'password' found in this query","Non se atopou 'password' nesta solicitude"}. +{"No 'path' found in data form","Non se atopou 'path' neste formulario de datos"}. +{"No pending subscriptions found","Non se atoparon subscricións pendentes"}. +{"No privacy list with this name found","Non se atopou ningunha lista de privacidade con este nome"}. +{"No private data found in this query","Non se atopou ningún elemento de datos privado nesta solicitude"}. +{"No running node found","Non se atoparon nodos activos"}. +{"No services available","Non hai servizos dispoñibles"}. +{"No statistics found for this item","Non se atopou ningunha estatística para este elemento"}. +{"No 'to' attribute found in the invitation","O atributo 'to' non se atopou na invitación"}. +{"Node already exists","O nodo xa existe"}. +{"Node ID","Nodo ID"}. +{"Node index not found","Non se atopou índice de nodo"}. +{"Node not found","Nodo non atopado"}. +{"Node ~p","Nodo ~p"}. +{"Nodeprep has failed","Nodeprep fallou"}. +{"Nodes","Nodos"}. {"None","Ningún"}. -{"No resource provided","Non se proporcionou recurso"}. {"Not Found","Non atopado"}. +{"Not subscribed","Non subscrito"}. {"Notify subscribers when items are removed from the node","Notificar subscriptores cando os elementos bórranse do nodo"}. {"Notify subscribers when the node configuration changes","Notificar subscriptores cando cambia a configuración do nodo"}. {"Notify subscribers when the node is deleted","Notificar subscriptores cando o nodo bórrase"}. @@ -202,157 +226,164 @@ {"Number of online users","Número de usuarios conectados"}. {"Number of registered users","Número de usuarios rexistrados"}. {"October","Outubro"}. -{"Offline Messages","Mensaxes diferidas"}. -{"Offline Messages:","Mensaxes sen conexión:"}. {"OK","Aceptar"}. -{"Online","Conectado"}. -{"Online Users:","Usuarios conectados:"}. +{"Old Password:","Contrasinal anterior:"}. {"Online Users","Usuarios conectados"}. +{"Online","Conectado"}. {"Only deliver notifications to available users","Só enviar notificacións aos usuarios dispoñibles"}. +{"Only or tags are allowed","Só se permiten etiquetas ou "}. +{"Only element is allowed in this query","Só se admite o elemento nesta consulta"}. +{"Only members may query archives of this room","Só membros poden consultar o arquivo de mensaxes da sala"}. {"Only moderators and participants are allowed to change the subject in this room","Só os moderadores e os participantes se lles permite cambiar o tema nesta sala"}. {"Only moderators are allowed to change the subject in this room","Só os moderadores están autorizados a cambiar o tema nesta sala"}. +{"Only moderators can approve voice requests","Só os moderadores poden aprobar peticións de voz"}. {"Only occupants are allowed to send messages to the conference","Só os ocupantes poden enviar mensaxes á sala"}. {"Only occupants are allowed to send queries to the conference","Só os ocupantes poden enviar solicitudes á sala"}. {"Only service administrators are allowed to send service messages","Só os administradores do servizo teñen permiso para enviar mensaxes de servizo"}. -{"Options","Opcións"}. {"Organization Name","Nome da organización"}. {"Organization Unit","Unidade da organización"}. -{"Outgoing s2s Connections:","Conexións S2S saíntes:"}. {"Outgoing s2s Connections","Conexións S2S saíntes"}. {"Owner privileges required","Requírense privilexios de propietario da sala"}. -{"Packet","Paquete"}. -{"Password ~b","Contrasinal ~b"}. -{"Password:","Contrasinal:"}. -{"Password","Contrasinal"}. +{"Participant","Participante"}. {"Password Verification","Verificación da contrasinal"}. +{"Password Verification:","Verificación da Contrasinal:"}. +{"Password","Contrasinal"}. +{"Password:","Contrasinal:"}. {"Path to Dir","Ruta ao directorio"}. {"Path to File","Ruta ao ficheiro"}. -{"Pending","Pendente"}. {"Period: ","Periodo: "}. {"Persist items to storage","Persistir elementos ao almacenar"}. +{"Ping query is incorrect","A solicitude de Ping é incorrecta"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Ten en conta que estas opcións só farán copia de seguridade da base de datos Mnesia. Se está a utilizar o módulo de ODBC, tamén necesita unha copia de seguridade da súa base de datos SQL por separado."}. +{"Please, wait for a while before sending new voice request","Por favor, espera un pouco antes de enviar outra petición de voz"}. {"Pong","Pong"}. -{"Port ~b","Porto ~b"}. -{"Port","Porto"}. {"Present real Jabber IDs to","Os Jabber ID reais poden velos"}. -{"private, ","privado"}. -{"Protocol","Protocolo"}. +{"private, ","privado, "}. {"Publish-Subscribe","Publicar-Subscribir"}. {"PubSub subscriber request","Petición de subscriptor de PubSub"}. +{"Purge all items when the relevant publisher goes offline","Purgar todos os elementos cando o editor correspondente desconéctase"}. {"Queries to the conference members are not allowed in this room","Nesta sala non se permiten solicitudes aos membros da sala"}. +{"Query to another users is forbidden","É prohibido enviar solicitudes a outros usuarios"}. {"RAM and disc copy","Copia en RAM e disco"}. {"RAM copy","Copia en RAM"}. -{"Raw","Cru"}. -{"Really delete message of the day?","Está seguro de quere borrar a mensaxe do dia?"}. +{"Really delete message of the day?","¿Está seguro que quere borrar a mensaxe do dia?"}. {"Recipient is not in the conference room","O receptor non está na sala de conferencia"}. -{"Registered Users:","Usuarios rexistrados:"}. -{"Registered Users","Usuarios rexistrados"}. -{"Registration in mod_irc for ","Rexistro en mod_irc para"}. +{"Register","Rexistrar"}. {"Remote copy","Copia remota"}. -{"Remove All Offline Messages","Borrar Todas as Mensaxes Sen conexión"}. -{"Remove","Borrar"}. {"Remove User","Eliminar usuario"}. {"Replaced by new connection","Substituído por unha nova conexión"}. {"Resources","Recursos"}. -{"Restart","Reiniciar"}. {"Restart Service","Reiniciar o servizo"}. {"Restore Backup from File at ","Restaura copia de seguridade desde o ficheiro en "}. -{"Restore binary backup after next ejabberd restart (requires less memory):","Restaurar copia de seguridade binaria no seguinte reinicio de ejabberd (require menos memoria que se instantánea):"}. +{"Restore binary backup after next ejabberd restart (requires less memory):","Restaurar copia de seguridade binaria no seguinte reinicio de ejabberd (require menos memoria):"}. {"Restore binary backup immediately:","Restaurar inmediatamente copia de seguridade binaria:"}. {"Restore plain text backup immediately:","Restaurar copias de seguridade de texto plano inmediatamente:"}. {"Restore","Restaurar"}. +{"Roles for which Presence is Broadcasted","Roles para os que si se difunde a súa Presenza"}. {"Room Configuration","Configuración da Sala"}. {"Room creation is denied by service policy","Denegar crear a sala por política do servizo"}. {"Room description","Descrición da sala"}. {"Room Occupants","Ocupantes da sala"}. {"Room title","Título da sala"}. {"Roster groups allowed to subscribe","Lista de grupos autorizados a subscribir"}. -{"Roster","Lista de contactos"}. -{"Roster of ","Lista de contactos de "}. {"Roster size","Tamaño da lista de contactos"}. -{"RPC Call Error","Erro na chamada RPC"}. {"Running Nodes","Nodos funcionando"}. -{"~s access rule configuration","Configuración das Regra de Acceso ~s"}. {"Saturday","Sábado"}. -{"Script check","Comprobación de script"}. {"Search Results for ","Buscar resultados por "}. {"Search users in ","Buscar usuarios en "}. -{"Send announcement to all online users","Enviar anuncio a todos los usuarios conectados"}. {"Send announcement to all online users on all hosts","Enviar anuncio a todos os usuarios conectados en todos os dominios"}. -{"Send announcement to all users","Enviar anuncio a todos os usuarios"}. +{"Send announcement to all online users","Enviar anuncio a todos os usuarios conectados"}. {"Send announcement to all users on all hosts","Enviar anuncio a todos os usuarios en todos os dominios"}. +{"Send announcement to all users","Enviar anuncio a todos os usuarios"}. {"September","Setembro"}. -{"Server ~b","Servidor ~b"}. +{"Server:","Servidor:"}. {"Set message of the day and send to online users","Pór mensaxe do dia e enviar a todos os usuarios conectados"}. {"Set message of the day on all hosts and send to online users","Pór mensaxe do día en todos os dominios e enviar aos usuarios conectados"}. {"Shared Roster Groups","Grupos Compartidos"}. {"Show Integral Table","Mostrar Táboa Integral"}. {"Show Ordinary Table","Mostrar Táboa Ordinaria"}. {"Shut Down Service","Deter o servizo"}. -{"~s invites you to the room ~s","~s invítache á sala ~s"}. {"Specify the access model","Especifica o modelo de acceso"}. +{"Specify the event message type","Especifica o tipo da mensaxe de evento"}. {"Specify the publisher model","Especificar o modelo do publicante"}. -{"~s's Offline Messages Queue","Cola de mensaxes diferidas de ~s"}. -{"Start","Iniciar"}. -{"Start Modules at ","Iniciar módulos en "}. -{"Start Modules","Iniciar módulos"}. -{"Statistics","Estatísticas"}. -{"Statistics of ~p","Estatísticas de ~p"}. -{"Stop","Deter"}. -{"Stop Modules at ","Deter módulos en "}. -{"Stop Modules","Detener módulos"}. {"Stopped Nodes","Nodos detidos"}. -{"Storage Type","Tipo de almacenamiento"}. {"Store binary backup:","Gardar copia de seguridade binaria:"}. {"Store plain text backup:","Gardar copia de seguridade en texto plano:"}. {"Subject","Asunto"}. -{"Submit","Enviar"}. {"Submitted","Enviado"}. {"Subscriber Address","Dirección do subscriptor"}. -{"Subscription","Subscripción"}. +{"Subscriptions are not allowed","Non se permiten subscricións"}. {"Sunday","Domingo"}. -{"That nickname is already in use by another occupant","Ese alcume que xa está en uso por outro ocupante"}. +{"That nickname is already in use by another occupant","Ese alcume xa está a ser usado por outro ocupante"}. {"That nickname is registered by another person","O alcume xa está rexistrado por outra persoa"}. {"The CAPTCHA is valid.","O CAPTCHA é válido."}. +{"The CAPTCHA verification has failed","A verificación de CAPTCHA fallou"}. {"The collections with which a node is affiliated","As coleccións coas que un nodo está afiliado"}. +{"The feature requested is not supported by the conference","A sala de conferencias non admite a función solicitada"}. +{"The password contains unacceptable characters","O contrasinal contén caracteres inaceptables"}. +{"The password is too weak","O contrasinal é demasiado débil"}. {"the password is","a contrasinal é"}. +{"The query is only allowed from local users","A solicitude só se permite para usuarios locais"}. +{"The query must not contain elements","A solicitude non debe conter elementos "}. +{"The stanza MUST contain only one element, one element, or one element","A estroa DEBEN conter un elemento , un elemento ou un elemento "}. +{"There was an error creating the account: ","Produciuse un erro ao crear a conta: "}. +{"There was an error deleting the account: ","Produciuse un erro ao eliminar a conta: "}. +{"This room is not anonymous","Sala non anónima"}. {"Thursday","Xoves"}. -{"Time","Data"}. {"Time delay","Atraso temporal"}. -{"To","Para"}. -{"To ~s","A ~s"}. +{"To register, visit ~s","Para rexistrarse, visita ~s"}. +{"Token TTL","Token TTL"}. +{"Too many active bytestreams","Demasiados bytestreams activos"}. +{"Too many CAPTCHA requests","Demasiadas solicitudes CAPTCHA"}. +{"Too many elements","Demasiados elementos "}. +{"Too many elements","Demasiados elementos "}. +{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Demasiados (~p) fallou autenticaciones desde esta dirección IP (~s). A dirección será desbloqueada as ~s UTC"}. +{"Too many unacked stanzas","Demasiadas mensaxes sen recoñecer recibilos"}. +{"Too many users in this conference","Demasiados usuarios nesta sala"}. {"Traffic rate limit is exceeded","Hase exedido o límite de tráfico"}. -{"Transactions Aborted:","Transaccións abortadas:"}. -{"Transactions Committed:","Transaccións finalizadas:"}. -{"Transactions Logged:","Transaccións rexistradas:"}. -{"Transactions Restarted:","Transaccións reiniciadas:"}. {"Tuesday","Martes"}. +{"Unable to generate a CAPTCHA","No se pudo generar un CAPTCHA"}. +{"Unable to register route on existing local domain","Non se pode rexistrar a ruta no dominio local existente"}. {"Unauthorized","Non autorizado"}. -{"Update","Actualizar"}. +{"Unexpected action","Acción inesperada"}. +{"Unregister","Eliminar rexistro"}. +{"Unsupported element","Elemento non soportado"}. {"Update message of the day (don't send)","Actualizar mensaxe do dia, pero non envialo"}. {"Update message of the day on all hosts (don't send)","Actualizar a mensaxe do día en todos os dominos (pero non envialo)"}. -{"Update plan","Plan de actualización"}. -{"Update script","Script de actualización"}. -{"Uptime:","Tempo desde o inicio:"}. -{"Use of STARTTLS required","É obrigatorio usar STARTTLS"}. +{"User already exists","O usuario xa existe"}. +{"User JID","Jabber ID do usuario"}. +{"User (jid)","Usuario (jid)"}. {"User Management","Administración de usuarios"}. +{"User session not found","Sesión de usuario non atopada"}. +{"User session terminated","Sesión de usuario completada"}. +{"Username:","Nome de usuario:"}. {"Users are not allowed to register accounts so quickly","Os usuarios non están autorizados a rexistrar contas con tanta rapidez"}. {"Users Last Activity","Última actividade dos usuarios"}. {"Users","Usuarios"}. {"User","Usuario"}. -{"Validate","Validar"}. -{"vCard User Search","Procura de usuario en vCard"}. +{"Value 'get' of 'type' attribute is not allowed","O valor \"get\" do atributo 'type' non está permitido"}. +{"Value of '~s' should be boolean","O valor de '~s' debería ser booleano"}. +{"Value of '~s' should be datetime string","O valor de '~s' debería ser unha data"}. +{"Value of '~s' should be integer","O valor de '~s' debería ser un enteiro"}. +{"Value 'set' of 'type' attribute is not allowed","O valor \"set\" do atributo 'type' non está permitido"}. +{"vCard User Search","vCard busqueda de usuario"}. {"Virtual Hosts","Hosts Virtuais"}. -{"Visitors are not allowed to change their nicknames in this room","Os visitantes non están autorizados a cambiar os seus That alcumes nesta sala"}. +{"Visitors are not allowed to change their nicknames in this room","Os visitantes non teñen permitido cambiar os seus alcumes nesta sala"}. {"Visitors are not allowed to send messages to all occupants","Os visitantes non poden enviar mensaxes a todos os ocupantes"}. +{"Visitor","Visitante"}. +{"Voice request","Petición de voz"}. +{"Voice requests are disabled in this conference","As peticións de voz están desactivadas nesta sala"}. {"Wednesday","Mércores"}. {"When to send the last published item","Cando enviar o último elemento publicado"}. {"Whether to allow subscriptions","Permitir subscripciones"}. -{"You have been banned from this room","fuches bloqueado nesta sala"}. +{"You have been banned from this room","Fuches bloqueado nesta sala"}. +{"You have joined too many conferences","Entrou en demasiadas salas de conferencia"}. {"You must fill in field \"Nickname\" in the form","Debes encher o campo \"Alcumo\" no formulario"}. -{"You need an x:data capable client to configure mod_irc settings","Necesitas un cliente con soporte de x:data para configurar as opcións de mod_irc"}. -{"You need an x:data capable client to configure room","Necesitas un cliente con soporte de x:data para configurar a sala"}. +{"You need a client that supports x:data and CAPTCHA to register","Necesitas un cliente con soporte de x:data e CAPTCHA para rexistrarche"}. +{"You need a client that supports x:data to register the nickname","Necesitas un cliente con soporte de x:data para poder rexistrar o alcume"}. {"You need an x:data capable client to search","Necesitas un cliente con soporte de x:data para poder buscar"}. +{"Your active privacy list has denied the routing of this stanza.","A súa lista de privacidade activa negou o encaminamiento desta estrofa."}. {"Your contact offline message queue is full. The message has been discarded.","A túa cola de mensaxes diferidas de contactos está chea. A mensaxe descartouse."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","As súas mensaxes a ~s encóntranse bloqueadas. Para desbloquear, visite ~s"}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","As súas mensaxes a ~s encóntranse bloqueadas. Para desbloquear, visite ~s"}. +{"You're not allowed to create nodes","Non tes permiso para crear nodos"}. diff --git a/priv/msgs/gl.po b/priv/msgs/gl.po deleted file mode 100644 index 4d75d21da..000000000 --- a/priv/msgs/gl.po +++ /dev/null @@ -1,1937 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: 16.02\n" -"Last-Translator: Carlos E. Lopez - carlos AT suchat.org\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Galician (galego)\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "Requírese o uso de STARTTLS" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Non se proporcionou recurso" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Substituído por unha nova conexión" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "foi expulsado" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "A súa lista de privacidade activa negou o encaminamiento desta estrofa." - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "Demasiadas mensaxes sen recoñecer recibilos" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Introduza o texto que ves" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "" -"As súas mensaxes a ~s encóntranse bloqueadas. Para desbloquear, visite ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "Si non ves a imaxe CAPTCHA aquí, visita a páxina web." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "CAPTCHA páxina Web" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "O CAPTCHA é válido." - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Usuario" - -#: ejabberd_oauth.erl:256 -msgid "Server" -msgstr "Servidor" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Contrasinal" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "Aceptar" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "Non autorizado" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "Ejabberd Administrador Web" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Administración" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Listas de Control de Acceso" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Enviado" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Mal formato" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Enviar" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "Cru" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Eliminar os seleccionados" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Regras de Acceso" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "Configuración das Regra de Acceso ~s" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "Hosts Virtuais" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Usuarios" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "Usuarios conectados" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Última actividade dos usuarios" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Periodo: " - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "Último mes" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "Último ano" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "Toda a actividade" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Mostrar Táboa Ordinaria" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Mostrar Táboa Integral" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Estatísticas" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "Non atopado" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Nodo non atopado" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Engadir novo" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Host" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Usuarios rexistrados" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Engadir usuario" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Mensaxes diferidas" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Última actividade" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Nunca" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Conectado" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Usuarios rexistrados:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Usuarios conectados:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Conexións S2S saíntes:" - -#: ejabberd_web_admin.erl:1559 -msgid "Incoming s2s Connections:" -msgstr "Conexións S2S saíntes:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Ningún" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Cambiar contrasinal" - -#: ejabberd_web_admin.erl:1673 -msgid "User ~s" -msgstr "Usuario ~s" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Recursos conectados:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Contrasinal:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Eliminar usuario" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Sen datos" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Nodos" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Nodos funcionando" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Nodos detidos" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -msgid "Node ~p" -msgstr "Nodo ~p" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "Base de datos" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Copia de seguridade" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Portos de escoita" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Actualizar" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Reiniciar" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Deter" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Módulos" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "Erro na chamada RPC" - -#: ejabberd_web_admin.erl:1917 -msgid "Database Tables at ~p" -msgstr "Táboas da base de datos en ~p" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Nome" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Tipo de almacenamento" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Elementos" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Memoria" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Erro" - -#: ejabberd_web_admin.erl:1955 -msgid "Backup of ~p" -msgstr "Copia de seguridade de ~p" - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Ten en conta que estas opcións só farán copia de seguridade da base de datos " -"Mnesia. Se está a utilizar o módulo de ODBC, tamén necesita unha copia de " -"seguridade da súa base de datos SQL por separado." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Gardar copia de seguridade binaria:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "Aceptar" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Restaurar inmediatamente copia de seguridade binaria:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"Restaurar copia de seguridade binaria no seguinte reinicio de ejabberd " -"(require menos memoria):" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Gardar copia de seguridade en texto plano:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Restaurar copias de seguridade de texto plano inmediatamente:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "Importar usuarios en un fichero PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" -"Exportar datos de todos os usuarios do servidor a ficheros PIEFXIS " -"(XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "Exportar datos dos usuarios dun dominio a ficheiros PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "Exportar todas as táboas a un ficheiro SQL:" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Importar usuario de ficheiro spool de jabberd14:" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Importar usuarios do directorio spool de jabberd14:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Portos de escoita en " - -#: ejabberd_web_admin.erl:2144 -msgid "Modules at ~p" -msgstr "Módulos en ~p" - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "Estatísticas de ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Tempo desde o inicio:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "Tempo consumido de CPU:" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Transaccións finalizadas:" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Transaccións abortadas:" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Transaccións reiniciadas:" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Transaccións rexistradas:" - -#: ejabberd_web_admin.erl:2243 -msgid "Update ~p" -msgstr "Actualizar ~p" - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "Plan de actualización" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Módulos Modificados" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Script de actualización" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Script de actualización a baixo nivel" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Comprobación de script" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Porto" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Protocolo" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Módulo" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Opcións" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Iniciar" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Comandos" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "¿Está seguro que quere borrar a mensaxe do dia?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Asunto" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Corpo da mensaxe" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "Non se proporcionou corpo de mensaxe para o anuncio" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Anuncios" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Enviar anuncio a todos os usuarios" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Enviar anuncio a todos os usuarios en todos os dominios" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Enviar anuncio a todos os usuarios conectados" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "Enviar anuncio a todos os usuarios conectados en todos os dominios" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Pór mensaxe do dia e enviar a todos os usuarios conectados" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" -"Pór mensaxe do día en todos os dominios e enviar aos usuarios conectados" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Actualizar mensaxe do dia, pero non envialo" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "Actualizar a mensaxe do día en todos os dominos (pero non envialo)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Borrar mensaxe do dia" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Borrar a mensaxe do día en todos os dominios" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Configuración" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Iniciar módulos" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Deter módulos" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Restaurar" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Exportar a ficheiro de texto" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Importar ficheiro" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Importar directorio" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Reiniciar o servizo" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Deter o servizo" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Borrar usuario" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Pechar sesión de usuario" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Ver contrasinal de usuario" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Cambiar contrasinal de usuario" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "Ver data da última conexión de usuario" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Ver estatísticas de usuario" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Ver número de usuarios rexistrados" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Ver número de usuarios conectados" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Administración de usuarios" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Todos os usuarios" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Conexións S2S saíntes" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Xestión de copia de seguridade" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Importar usuarios de ficheiros spool de jabberd-1.4" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "A ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "De ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Configuración de táboas da base de datos en " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Selecciona tipo de almacenamento das táboas" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Copia en disco soamente" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "Copia en RAM e disco" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "Copia en RAM" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "Copia remota" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Deter módulos en " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Selecciona módulos a deter" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Iniciar módulos en " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Introduce lista de {Módulo, [Opcións]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Lista de módulos a iniciar" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Copia de seguridade de arquivos en " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Introduce ruta ao ficheiro de copia de seguridade" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Ruta ao ficheiro" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Restaura copia de seguridade desde o ficheiro en " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Exporta copia de seguridade a ficheiro de texto en " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Introduce ruta ao ficheiro de texto" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Importa usuario desde ficheiro en " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Introduce ruta ao ficheiro jabberd14 spool" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Importar usuarios desde o directorio en " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Introduce a ruta ao directorio de jabberd14 spools" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Ruta ao directorio" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "Atraso temporal" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Configuración da Lista de Control de Acceso" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Listas de Control de Acceso" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Configuración de accesos" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Regras de acceso" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Verificación da contrasinal" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Número de usuarios rexistrados" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Número de usuarios conectados" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Última conexión" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "Tamaño da lista de contactos" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "Direccións IP" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Recursos" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Administración de " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Acción no usuario" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Editar propiedades" - -#: mod_fail2ban.erl:95 -msgid "" -"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" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "Por favor, especifica o tamaño do arquivo" - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "Por favor, indique o nome do arquivo." - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "Esta dirección IP está na lista negra en ~s" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "Acceso denegado pola política do servizo" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "Transporte IRC" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "Módulo de IRC para ejabberd" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "" -"Necesitas un cliente con soporte de x:data para configurar as opcións de " -"mod_irc" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Rexistro en mod_irc para" - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Introduce o nome de usuario, codificaciones de carácteres, portos e " -"contrasinai que queiras usar ao conectar nos servidores de IRC" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "Nome de usuario en IRC" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Se quere especificar codificaciones de caracteres diferentes, contrasinal ou " -"servidor IRC rechea esta lista con valores no formato '{\"servidor irc\", " -"\"codificación\", \"porto\", \"contrasinal\"}'. Este servizo utiliza por " -"defecto a codificación \"~s\", porto ~p, sen contrasinal." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Exemplo: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Parámetros de conexiones" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "Entrar en canle IRC" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "Canle de IRC (non poñer o primeiro #)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "Servidor IRC" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Únete á canle de IRC aquí." - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Únete á canle de IRC con este IDE de Jabber: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "IRC axustes" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Introduce o nome de usuario e codificaciones de carácteres que queiras usar " -"ao conectar nos servidores de IRC. Presione 'Siguiente' para obtener más " -"campos para rellenar Presione 'completo' para guardar axustes." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "Nome de usuario en IRC" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Contrasinal ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Porto ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Codificación de servidor ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Servidor ~b" - -#: mod_mam.erl:541 -msgid "Only members may query archives of this room" -msgstr "Só membros poden consultar o arquivo de mensaxes da sala" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "" -"Só os administradores do servizo teñen permiso para enviar mensaxes de " -"servizo" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "Denegar crear a sala por política do servizo" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "A sala de conferencias non existe" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Salas de charla" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "Salas baleiras" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "" -"Necesitas un cliente con soporte de x:data para poder rexistrar o alcume" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Rexistro do alcume en " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Introduce o alcume que queiras rexistrar" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Alcume" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "O alcume xa está rexistrado por outra persoa" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Debes encher o campo \"Alcumo\" no formulario" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "Módulo de MUC para ejabberd" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "Salas de Charla" - -#: mod_muc_admin.erl:249 -msgid "Total rooms" -msgstr "Salas totais" - -#: mod_muc_admin.erl:250 -msgid "Permanent rooms" -msgstr "Salas permanentes" - -#: mod_muc_admin.erl:251 -msgid "Registered nicknames" -msgstr "Alcumes rexistrados" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "Lista de salas" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "Configuración da sala modificada" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "entra na sala" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "sae da sala" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "foi bloqueado" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "foi expulsado debido a un cambio de afiliación" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "foi expulsado, porque a sala cambiouse a só-membros" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "foi expulsado porque o sistema vaise a deter" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "cámbiase o nome a" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " puxo o asunto: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "Creouse a sala" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "Destruíuse a sala" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "Iniciouse a sala" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "Detívose a sala" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "Luns" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "Martes" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "Mércores" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "Xoves" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "Venres" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "Sábado" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "Domingo" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "Xaneiro" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "Febreiro" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "Marzo" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "Abril" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "Maio" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "Xuño" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "Xullo" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "Agosto" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "Setembro" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "Outubro" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "Novembro" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "Decembro" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Configuración da Sala" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "Ocupantes da sala" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "Hase exedido o límite de tráfico" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "Impedir o envio de mensaxes privadas á sala" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "Por favor, espera un pouco antes de enviar outra petición de voz" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "As peticións de voz están desactivadas nesta sala" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "Fallo ao extraer o Jabber ID da túa aprobación de petición de voz" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "Só os moderadores poden aprobar peticións de voz" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Tipo de mensaxe incorrecta" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "Non está permitido enviar mensaxes privadas do tipo \"groupchat\"" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "O receptor non está na sala de conferencia" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "Non está permitido enviar mensaxes privadas" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Só os ocupantes poden enviar mensaxes á sala" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "Só os ocupantes poden enviar solicitudes á sala" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "Nesta sala non se permiten solicitudes aos membros da sala" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "" -"Só os moderadores e os participantes se lles permite cambiar o tema nesta " -"sala" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Só os moderadores están autorizados a cambiar o tema nesta sala" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "Os visitantes non poden enviar mensaxes a todos os ocupantes" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "" -"Os visitantes non teñen permitido cambiar os seus alcumes nesta sala" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "Ese alcume xa está a ser usado por outro ocupante" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "fuches bloqueado nesta sala" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "Necesitas ser membro desta sala para poder entrar" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "Necesítase contrasinal para entrar nesta sala" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "Demasiadas peticións de CAPTCHA" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "No se pudo generar un CAPTCHA" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Contrasinal incorrecta" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "Necesítase privilexios de administrador" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "Necesítase privilexios de moderador" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "O Jabber ID ~s non é válido" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "O alcume ~s non existe na sala" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Afiliación non válida: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Rol non válido: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "Requírense privilexios de propietario da sala" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "Configuración para a sala ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Título da sala" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "Descrición da sala" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Sala permanente" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Sala publicamente visible" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "A lista de participantes é pública" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Protexer a sala con contrasinal" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Número máximo de ocupantes" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Sen límite" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Os Jabber ID reais poden velos" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "só moderadores" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "calquera" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "Roles para os que si se difunde a súa Presenza" - -#: mod_muc_room.erl:3486 -msgid "Moderator" -msgstr "Moderator" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "Participante" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "Visitante" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Sala só para membros" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Facer sala moderada" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Os usuarios son participantes por defecto" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Permitir aos usuarios cambiar o asunto" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Permitir aos usuarios enviar mensaxes privadas" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "Permitir aos visitantes enviar mensaxes privadas a" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "ninguén" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Permitir aos usuarios consultar a outros usuarios" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Permitir aos usuarios enviar invitacións" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "" -"Permitir aos visitantes enviar texto de estado nas actualizacións depresenza" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Permitir aos visitantes cambiarse o alcume" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "Permitir aos visitantes enviar peticións de voz" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "Intervalo mínimo entre peticións de voz (en segundos)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "Protexer a sala con CAPTCHA" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "Activar o almacenamento de mensaxes" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "Excluír Jabber IDs das probas de CAPTCHA" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Gardar históricos" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "Necesitas un cliente con soporte de x:data para configurar a sala" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Número de ocupantes" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "privado" - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "Petición de voz" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "Aproba ou rexeita a petición de voz." - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "Jabber ID do usuario" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "¿Conceder voz a esta persoa?" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s invítache á sala ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "a contrasinal é" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "Multicast" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "Servizo Multicast de ejabberd" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "" -"A túa cola de mensaxes diferidas de contactos está chea. A mensaxe " -"descartouse." - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "Cola de mensaxes diferidas de ~s" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Data" - -#: mod_offline.erl:812 -msgid "From" -msgstr "De" - -#: mod_offline.erl:813 -msgid "To" -msgstr "Para" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Paquete" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Mensaxes sen conexión:" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "Borrar Todas as Mensaxes Sen conexión" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "Módulo SOCKS5 Bytestreams para ejabberd" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Publicar-Subscribir" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "Módulo de Publicar-Subscribir de ejabberd" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "Petición de subscriptor de PubSub" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Decidir se aprobar a subscripción desta entidade." - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "Nodo ID" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Dirección do subscriptor" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "Desexas permitir a este JabberID que se subscriba a este nodo PubSub?" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Enviar payloads xunto coas notificacións de eventos" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Entregar notificacións de eventos" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Notificar subscriptores cando cambia a configuración do nodo" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Notificar subscriptores cando o nodo bórrase" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Notificar subscriptores cando os elementos bórranse do nodo" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Persistir elementos ao almacenar" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "Un nome sinxelo para o nodo" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Máximo # de elementos que persisten" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Permitir subscripciones" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Especifica o modelo de acceso" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Lista de grupos autorizados a subscribir" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Especificar o modelo do publicante" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "Purgar todos os elementos cando o editor correspondente desconéctase" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "Especifica o tipo da mensaxe de evento" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Máximo tamaño do payload en bytes" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "Cando enviar o último elemento publicado" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Só enviar notificacións aos usuarios dispoñibles" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "As coleccións coas que un nodo está afiliado" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "A verificación de CAPTCHA fallou" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "" -"Necesitas un cliente con soporte de x:data e CAPTCHA para rexistrarche" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "" -"Escolle un nome de usuario e contrasinal para rexistrarche neste servidor" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "O contrasinal é demasiado débil" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "Os usuarios non están autorizados a rexistrar contas con tanta rapidez" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "A súa conta Jabber creouse correctamente." - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "Produciuse un erro ao crear a conta: " - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "A súa conta Jabber eliminouse correctamente." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "Produciuse un erro ao eliminar a conta: " - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "O contrasinal da súa conta Jabber cambiouse correctamente." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "Produciuse un erro ao cambiar o contrasinal: " - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Rexistro de conta Jabber" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Rexistrar unha conta Jabber" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Eliminar o rexistro dunha conta Jabber" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"Esta páxina permite crear unha conta Jabber neste servidor Jabber. o seu JID " -"(Jabber IDentificador) será da forma: nomeusuario@servidor. Por favor le " -"coidadosamente as instrucións para encher correctamente os campos." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "Nome de usuario:" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "Esta é insensible: Macbeth é o mesmo que MacBeth e Macbeth." - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "Caracteres non permitidos:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "Servidor:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber Server." -msgstr "" -"Non lle diga o seu contrasinal a ninguén, nin sequera os administradores do " -"Servidor Jabber." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "Máis tarde, pode cambiar o seu contrasinal utilizando un cliente Jabber." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Algúns clientes Jabber pode almacenar o contrasinal no computador, pero debe " -"facer isto só no seu computador persoal por razóns de seguridade." - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"Memorice o seu contrasinal ou escribilo nun papel colocado nun lugar seguro. En " -"Jabber non hai unha forma automatizada para recuperar o seu contrasinal si " -"a esquece" - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "Verificación da contrasinal" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "Rexistrar" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "Contrasinal anterior:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "Novo contrasinal:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "Esta páxina permite anular o rexistro dunha conta Jabber neste servidor Jabber." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "Eliminar rexistro" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Subscripción" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Pendente" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Grupos" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Validar" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Borrar" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Lista de contactos de " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Engadir ID Jabber" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Lista de contactos" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Grupos Compartidos" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Nome:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Descrición:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Membros:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Mostrar grupos:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Grupo " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Servidor Jabber en Erlang" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Aniversario" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Cidade" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "País" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "Email" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Apelido" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Enche o formulario para buscar usuarios Jabber (Engade * ao final dun campo " -"para buscar subcadenas)" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Nome completo" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Segundo nome" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Nome da organización" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Unidade da organización" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Buscar usuarios en " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "Necesitas un cliente con soporte de x:data para poder buscar" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "vCard busqueda de usuario" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "Módulo vCard para ejabberd" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Buscar resultados por " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "Rechea campos para buscar usuarios Jabber que concuerden" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Servidores S2S saíntes:" - -#~ msgid "Delete" -#~ msgstr "Eliminar" - -#~ msgid "This room is not anonymous" -#~ msgstr "Sala non anónima" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "" -#~ "Este participante é expulsado da sala, xa que enviou unha mensaxe de erro" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Este participante é expulsado da sala, porque el enviou unha mensaxe de " -#~ "erro a outro participante" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "" -#~ "Este participante é expulsado da sala, porque el enviou un erro de " -#~ "presenza" - -#~ msgid "CAPTCHA test failed" -#~ msgstr "O CAPTCHA é válido." - -#~ msgid "Encodings" -#~ msgstr "Codificaciones" - -#~ msgid "(Raw)" -#~ msgstr "(Cru)" - -#~ msgid "Specified nickname is already registered" -#~ msgstr "O alcume especificado xa está rexistrado, terás que buscar outro" - -#~ msgid "Size" -#~ msgstr "Tamaño" diff --git a/priv/msgs/he.msg b/priv/msgs/he.msg index 65092de43..1dabbd028 100644 --- a/priv/msgs/he.msg +++ b/priv/msgs/he.msg @@ -1,21 +1,20 @@ -%% -*- coding: latin-1 -*- +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" has set the subject to: "," הגדיר/ה את הנושא אל: "}. +{"A friendly name for the node","שם ידידותי עבור הצומת"}. +{"A password is required to enter this room","נדרשת סיסמה כדי להיכנס אל חדר זה"}. {"Accept","קבל"}. -{"Access Configuration","תצורת גישה"}. -{"Access Control List Configuration","תצורת רשימת בקרת גישה"}. -{"Access control lists","רשימות בקרת גישה"}. -{"Access Control Lists","רשימות בקרת גישה"}. {"Access denied by service policy","גישה נדחתה על ידי פוליסת שירות"}. -{"Access rules","כללי גישה"}. -{"Access Rules","כללי גישה"}. {"Action on user","פעולה על משתמש"}. -{"Add Jabber ID","הוסף מזהה Jabber"}. -{"Add New","הוסף חדש"}. {"Add User","הוסף משתמש"}. {"Administration of ","ניהול של "}. {"Administration","הנהלה"}. {"Administrator privileges required","נדרשות הרשאות מנהל"}. -{"A friendly name for the node","שם ידידותי עבור הצומת"}. {"All activity","כל פעילות"}. +{"All Users","כל המשתמשים"}. {"Allow this Jabber ID to subscribe to this pubsub node?","להתיר למזהה Jabber זה להירשם לצומת PubSub זה?"}. {"Allow users to change the subject","התר למשתמשים לשנות את הנושא"}. {"Allow users to query other users","התר למשתמשים לתשאל משתמשים אחרים"}. @@ -25,21 +24,23 @@ {"Allow visitors to send private messages to","התר למבקרים לשלוח הודעות פרטיות אל"}. {"Allow visitors to send status text in presence updates","התר למבקרים לשלוח טקסט מצב בתוך עדכוני נוכחות"}. {"Allow visitors to send voice requests","התר למבקרים לשלוח בקשות ביטוי"}. -{"All Users","כל המשתמשים"}. {"Announcements","בשורות"}. -{"anyone","לכל אחד"}. -{"A password is required to enter this room","נדרשת סיסמה כדי להיכנס אל חדר זה"}. {"April","אפריל"}. {"August","אוגוסט"}. +{"Automatic node creation is not enabled","יצירה אוטומטית של צומת אינה מאופשרת"}. {"Backup Management","ניהול גיבוי"}. {"Backup of ~p","גיבוי של ~p"}. {"Backup to File at ","גבה לקובץ אצל "}. {"Backup","גיבוי"}. {"Bad format","פורמט רע"}. {"Birthday","יום הולדת"}. +{"Cannot remove active list","לא ניתן להסיר רשימה פעילה"}. +{"Cannot remove default list","לא ניתן להסיר רשימה שגרתית"}. {"CAPTCHA web page","עמוד רשת CAPTCHA"}. {"Change Password","שנה סיסמה"}. {"Change User Password","שנה סיסמת משתמש"}. +{"Changing password is not allowed","שינוי סיסמה אינו מותר"}. +{"Changing role/affiliation is not allowed","שינוי תפקיד/שיוך אינו מותר"}. {"Characters not allowed:","תווים לא מורשים:"}. {"Chatroom configuration modified","תצורת חדר שיחה שונתה"}. {"Chatroom is created","חדר שיחה נוצר כעת"}. @@ -48,7 +49,6 @@ {"Chatroom is stopped","חדר שיחה הינו מופסק"}. {"Chatrooms","חדרי שיחה"}. {"Choose a username and password to register with this server","בחר שם משתמש וסיסמה כדי להירשם בעזרת שרת זה"}. -{"Choose modules to stop","בחר מודולים להפסקה"}. {"Choose storage type of tables","בחר טיפוס אחסון של טבלאות"}. {"Choose whether to approve this entity's subscription.","בחר האם לאשר את ההרשמה של ישות זו."}. {"City","עיר"}. @@ -56,85 +56,64 @@ {"Conference room does not exist","חדר ועידה לא קיים"}. {"Configuration of room ~s","תצורת חדר ~s"}. {"Configuration","תצורה"}. -{"Connected Resources:","משאבים מחוברים:"}. -{"Connections parameters","פרמטרים של חיבור"}. {"Country","ארץ"}. -{"CPU Time:","זמן מחשב (CPU):"}. -{"Database Tables at ~p","טבלאות מסד נתונים אצל ~p"}. +{"Database failure","כשל מסד נתונים"}. {"Database Tables Configuration at ","תצורת טבלאות מסד נתונים אצל "}. {"Database","מסד נתונים"}. {"December","דצמבר"}. {"Default users as participants","משתמשים שגרתיים כמשתתפים"}. {"Delete message of the day on all hosts","מחק את בשורת היום בכל המארחים"}. {"Delete message of the day","מחק את בשורת היום"}. -{"Delete Selected","מחק נבחרות"}. {"Delete User","מחק משתמש"}. {"Deliver event notifications","מסור התראות אירוע"}. {"Deliver payloads with event notifications","מסור מטעני ייעוד (מטע״ד) יחד עם התראות אירוע"}. -{"Description:","תיאור:"}. {"Disc only copy","העתק של תקליטור בלבד"}. -{"Displayed Groups:","קבוצות מוצגות:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","אל תגלה את הסיסמה שלך לאף אחד, אפילו לא למנהלים של שרת Jabber."}. {"Dump Backup to Text File at ","השלך גיבוי לקובץ טקסט אצל "}. {"Dump to Text File","השלך לקובץ טקסט"}. {"Edit Properties","ערוך מאפיינים"}. {"Either approve or decline the voice request.","אשר או דחה בקשת ביטוי."}. -{"ejabberd IRC module","מודול IRC של ejabberd"}. {"ejabberd MUC module","מודול MUC של ejabberd"}. {"ejabberd Multicast service","שירות שידור מרובב של ejabberd"}. {"ejabberd Publish-Subscribe module","מודול Publish-Subscribe של ejabberd"}. {"ejabberd SOCKS5 Bytestreams module","מודול SOCKS5 Bytestreams של ejabberd"}. {"ejabberd vCard module","מודול vCard של ejabberd"}. {"ejabberd Web Admin","מנהל רשת ejabberd"}. -{"Elements","אלמנטים"}. {"Email","דוא״ל"}. -{"Empty Rooms","חדרים ריקים"}. {"Enable logging","אפשר רישום פעילות"}. {"Enable message archiving","אפשר אחסון הודעות"}. -{"Encoding for server ~b","קידוד עבור שרת ~b"}. {"End User Session","סיים סשן משתמש"}. -{"Enter list of {Module, [Options]}","הזן רשימה של {מודול, [אפשרויות]}"}. {"Enter nickname you want to register","הזן שם כינוי אשר ברצונך לרשום"}. {"Enter path to backup file","הזן נתיב לקובץ גיבוי"}. {"Enter path to jabberd14 spool dir","הזן נתיב למדור סליל (spool dir) של jabberd14"}. {"Enter path to jabberd14 spool file","הזן נתיב לקובץ סליל (spool file) של jabberd14"}. {"Enter path to text file","הזן נתיב לקובץ טקסט"}. {"Enter the text you see","הזן את הכיתוב שאתה רואה"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","הזן שם משתמש וקידודים בהם ברצונך להשתמש לצורך התחברות לשרתי IRC. לחץ 'הבא' כדי להשיג עוד שדות למילוי. לחץ 'סיים' כדי לשמור הגדרות."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","הזן שם משתמש, קידודים, פורטים וסיסמאות בהם ברצונך להשתמש לצורך התחברות לשרתי IRC"}. -{"Erlang Jabber Server","שרת ג׳אבּר Erlang"}. -{"Error","שגיאה"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","דוגמא: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Exclude Jabber IDs from CAPTCHA challenge","הוצא כתובות Jabber מתוך אתגר CAPTCHA"}. {"Export all tables as SQL queries to a file:","יצא את כל הטבלאות בתור שאילתות SQL לתוך קובץ:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","יצא מידע של כל המשתמשים שבתוך שרת זה לתוך קבצי PIEFXIS ‏(XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","יצא מידע של כל המשתמשים שבתוך מארח לתוך קבצי PIEFXIS ‏(XEP-0227):"}. +{"Failed to activate bytestream","נכשל להפעיל bytestream"}. {"Failed to extract JID from your voice request approval","נכשל לחלץ JID מתוך אישור בקשת הביטוי שלך"}. +{"Failed to parse HTTP response","נכשל לפענח תגובת HTTP"}. +{"Failed to process option '~s'","נכשל לעבד אפשרות '~s'"}. {"Family Name","שם משפחה"}. {"February","פברואר"}. -{"Fill in fields to search for any matching Jabber User","מלא את שדות אלו כדי לחפש עבור כל משתמש Jabber מבוקש"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","מלא את הטופס כדי לחפש אחר כל משתמש Jabber מבוקש (באפשרותך להוסיף * בסוף שדה כדי להתאים למחרוזת-משנה)"}. +{"File larger than ~w bytes","קובץ גדול יותר משיעור של ~w בייטים"}. {"Friday","יום שישי"}. -{"From ~s","מאת ~s"}. -{"From","מאת"}. {"Full Name","שם מלא"}. {"Get Number of Online Users","השג מספר של משתמשים מקוונים"}. {"Get Number of Registered Users","השג מספר של משתמשים רשומים"}. {"Get User Last Login Time","השג זמן כניסה אחרון של משתמש"}. -{"Get User Password","השג סיסמת משתמש"}. {"Get User Statistics","השג סטטיסטיקת משתמש"}. +{"Given Name","שם פרטי"}. {"Grant voice to this person?","להעניק ביטוי לאישיות זו?"}. -{"Groups","קבוצות"}. -{"Group ","קבוצה "}. {"has been banned","נאסר/ה"}. -{"has been kicked because of an affiliation change","נבעט/ה משום שינוי סינוף"}. {"has been kicked because of a system shutdown","נבעט/ה משום כיבוי מערכת"}. +{"has been kicked because of an affiliation change","נבעט/ה משום שינוי סינוף"}. {"has been kicked because the room has been changed to members-only","נבעט/ה משום שהחדר שונה אל חברים-בלבד"}. {"has been kicked","נבעט/ה"}. -{" has set the subject to: "," הגדיר/ה את הנושא אל: "}. -{"Host","מארח"}. +{"Host unknown","מארח לא ידוע"}. {"If you don't see the CAPTCHA image here, visit the web page.","אם אינך רואה תמונת CAPTCHA כאן, בקר בעמוד רשת."}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","אם ברצונך לציין פורטים, סיסמאות, קידודים אחרים עבור שרתים של IRC, מלא את רשימה זו עם ערכים בפורמט '{\"irc server\", \"encoding\", port, \"password\"}'. באופן שגרתי שירות זה משתמש בקידוד \"~s\", פורט ~p, סיסמה ריקה."}. {"Import Directory","ייבוא מדור"}. {"Import File","ייבוא קובץ"}. {"Import user data from jabberd14 spool file:","יבא נתוני משתמש מתוך קובץ סליל (spool file) של jabberd14:"}. @@ -144,31 +123,19 @@ {"Import Users from Dir at ","ייבוא משתמשים מתוך מדור אצל "}. {"Import Users From jabberd14 Spool Files","יבא משתמשים מתוך קבצי סליל (Spool Files) של jabberd14"}. {"Improper message type","טיפוס הודעה לא מתאים"}. -{"Incoming s2s Connections:","חיבורי s2s נכנסים:"}. +{"Incorrect CAPTCHA submit","נשלחה CAPTCHA שגויה"}. +{"Incorrect data form","טופס מידע לא תקין"}. {"Incorrect password","מילת מעבר שגויה"}. -{"Invalid affiliation: ~s","סינוף שגוי: ~s"}. -{"Invalid role: ~s","תפקיד שגוי: ~s"}. +{"Insufficient privilege","הרשאה לא מספיקה"}. +{"Invitations are not allowed in this conference","הזמנות אינן מותרות בועידה זו"}. {"IP addresses","כתובות IP"}. -{"IP","‫IP"}. -{"IRC channel (don't put the first #)","ערוץ IRC (אל תשים סימן # ראשון)"}. -{"IRC server","שרת IRC"}. -{"IRC settings","הגדרות IRC"}. -{"IRC Transport","טרנספורט IRC"}. -{"IRC username","שם משתמש IRC"}. -{"IRC Username","שם משתמש IRC"}. {"is now known as","ידועה כעת בכינוי"}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","אין זה מותר לשלוח הודעות שגיאה לחדר. משתתף זה (~s) שלח הודעת שגיאה (~s) ונבעט מתוך החדר"}. {"It is not allowed to send private messages of type \"groupchat\"","אין זה מותר לשלוח הודעות פרטיות מן טיפוס \"groupchat\""}. {"It is not allowed to send private messages to the conference","אין זה מותר לשלוח הודעות פרטיות לועידה"}. -{"It is not allowed to send private messages","אין זה מותר לשלוח הודעות פרטיות"}. -{"Jabber Account Registration","רישום חשבון Jabber"}. -{"Jabber ID ~s is invalid","מזהה Jabber ‏~s הינו שגוי"}. {"Jabber ID","מזהה Jabber"}. {"January","ינואר"}. -{"Join IRC channel","הצטרף לערוץ IRC"}. {"joins the room","נכנס/ת אל החדר"}. -{"Join the IRC channel here.","הצטרף לערוץ IRC כאן."}. -{"Join the IRC channel in this Jabber ID: ~s","הצטרף לערוץ IRC במזהה Jabber זה: ~s"}. {"July","יולי"}. {"June","יוני"}. {"Last Activity","פעילות אחרונה"}. @@ -176,11 +143,6 @@ {"Last month","חודש אחרון"}. {"Last year","שנה אחרונה"}. {"leaves the room","עוזב/ת את החדר"}. -{"Listened Ports at ","פורטים מואזנים אצל "}. -{"Listened Ports","פורטים מואזנים"}. -{"List of modules to start","רשימה של מודולים להפעלה"}. -{"List of rooms","רשימה של חדרים"}. -{"Low level update script","תסריט עדכון Low level"}. {"Make participants list public","הפוך רשימת משתתפים לפומבית"}. {"Make room CAPTCHA protected","הפוך חדר לחדר מוגן CAPTCHA"}. {"Make room members-only","הפוך חדר לחדר עבור חברים-בלבד"}. @@ -188,46 +150,50 @@ {"Make room password protected","הפוך חדר לחדר מוגן במילת מעבר"}. {"Make room persistent","הפוך חדר לחדר קבוע"}. {"Make room public searchable","הפוך חדר לחדר שנתון לחיפוש פומבי"}. +{"Malformed username","שם משתמש פגום"}. {"March","מרץ"}. -{"Maximum Number of Occupants","מספר מרבי של נוכחים"}. -{"Max # of items to persist","מספר מרבי של פריטים לקיבוע"}. {"Max payload size in bytes","גודל מרבי של מטען ייעוד (payload) ביחידות מידה של byte"}. +{"Maximum Number of Occupants","מספר מרבי של נוכחים"}. {"May","מאי"}. {"Membership is required to enter this room","נדרשת חברות כדי להיכנס אל חדר זה"}. -{"Members:","חברים:"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","שנן את הסיסמה שלך, או רשום אותה בנייר שמור במקום בטוח. אצל Jabber אין דרך אוטומטית לשחזר את הסיסמה שלך במידה וזו תישמט מתוך זיכרונך."}. -{"Memory","זיכרון"}. {"Message body","גוף הודעה"}. {"Middle Name","שם אמצעי"}. {"Minimum interval between voice requests (in seconds)","תדירות מינימלית בין בקשות ביטוי (בשניות)"}. {"Moderator privileges required","נדרשות הרשאות אחראי"}. -{"moderators only","לאחראים בלבד"}. {"Moderator","אחראי"}. -{"Modified modules","מודולים שהותאמו"}. -{"Modules at ~p","מודולים אצל ~p"}. -{"Modules","מודולים"}. -{"Module","מודול"}. +{"Module failed to handle the query","מודול נכשל לטפל בשאילתא"}. {"Monday","יום שני"}. {"Multicast","שידור מרובב"}. {"Multi-User Chat","שיחה מרובת משתמשים"}. -{"Name:","שם:"}. {"Name","שם"}. {"Never","אף פעם"}. {"New Password:","סיסמה חדשה:"}. {"Nickname Registration at ","רישום שם כינוי אצל "}. -{"Nickname ~s does not exist in the room","שם כינוי ~s לא קיים בחדר"}. {"Nickname","שם כינוי"}. +{"No available resource found","לא נמצא משאב זמין"}. {"No body provided for announce message","לא סופק גוף עבור הודעת בשורה"}. -{"nobody","אף אחד"}. {"No Data","אין מידע"}. +{"No features available","אין תכונות זמינות"}. +{"No items found in this query","לא נמצאו פריטים בתוך שאילתא זו"}. +{"No limit","ללא הגבלה"}. +{"No module is handling this query","אין מודול אשר מטפל בשאילתא זו"}. +{"No node specified","לא צויין צומת"}. +{"No pending subscriptions found","לא נמצאו הרשמות ממתינות"}. +{"No privacy list with this name found","לא נמצאה רשימת פרטיות בשם זה"}. +{"No private data found in this query","לא נמצא מידע פרטי בתוך שאילתא זו"}. +{"No running node found","לא נמצא צומת מורץ"}. +{"No services available","אין שירות זמין"}. +{"No statistics found for this item","לא נמצאה סטטיסטיקה לגבי פריט זה"}. +{"Node already exists","צומת כבר קיים"}. {"Node ID","מזהה צומת (NID)"}. +{"Node index not found","מפתח צומת לא נמצא"}. {"Node not found","צומת לא נמצא"}. {"Node ~p","צומת ~p"}. +{"Nodeprep has failed","‏Nodeprep נכשל"}. {"Nodes","צמתים"}. -{"No limit","ללא הגבלה"}. {"None","אין"}. -{"No resource provided","לא סופק משאב"}. {"Not Found","לא נמצא"}. +{"Not subscribed","לא רשום"}. {"Notify subscribers when items are removed from the node","הודע מנויים כאשר פריטים מוסרים מתוך הצומת"}. {"Notify subscribers when the node configuration changes","הודע מנויים כאשר תצורת הצומת משתנה"}. {"Notify subscribers when the node is deleted","הודע מנויים כאשר הצומת נמחק"}. @@ -236,14 +202,12 @@ {"Number of online users","מספר של משתמשים מקוונים"}. {"Number of registered users","מספר של משתמשים רשומים"}. {"October","אוקטובר"}. -{"Offline Messages:","הודעות לא מקוונות:"}. -{"Offline Messages","הודעות לא מקוונות"}. {"OK","אישור"}. {"Old Password:","סיסמה ישנה:"}. -{"Online Users:","משתמשים מקוונים:"}. {"Online Users","משתמשים מקוונים"}. {"Online","מקוון"}. {"Only deliver notifications to available users","מסור התראות למשתמשים זמינים בלבד"}. +{"Only or tags are allowed","רק תגיות או הינן מורשות"}. {"Only members may query archives of this room","רק חברים רשאים לתשאל ארכיונים של חדר זה"}. {"Only moderators and participants are allowed to change the subject in this room","רק אחראים ומשתתפים רשאים לשנות את הנושא בחדר זה"}. {"Only moderators are allowed to change the subject in this room","רק אחראים רשאים לשנות את הנושא בחדר זה"}. @@ -251,59 +215,40 @@ {"Only occupants are allowed to send messages to the conference","רק נוכחים רשאים לשלוח הודעות אל הועידה"}. {"Only occupants are allowed to send queries to the conference","רק נוכחים רשאים לשלוח שאילתות אל הועידה"}. {"Only service administrators are allowed to send service messages","רק מנהלי שירות רשאים לשלוח הודעות שירות"}. -{"Options","אפשרויות"}. {"Organization Name","שם ארגון"}. {"Organization Unit","יחידת איגוד"}. -{"Outgoing s2s Connections:","חיבורי s2s יוצאים:"}. {"Outgoing s2s Connections","חיבורי s2s יוצאים"}. {"Owner privileges required","נדרשות הרשאות בעלים"}. -{"Packet","חבילת מידע"}. {"Participant","משתתף"}. -{"Password ~b","סיסמה ~b"}. -{"Password Verification:","אימות סיסמה:"}. {"Password Verification","אימות סיסמה"}. -{"Password:","סיסמה:"}. +{"Password Verification:","אימות סיסמה:"}. {"Password","סיסמה"}. +{"Password:","סיסמה:"}. {"Path to Dir","נתיב למדור"}. {"Path to File","נתיב לקובץ"}. -{"Pending","ממתינות"}. {"Period: ","משך זמן: "}. -{"Permanent rooms","חדרים קבועים"}. {"Persist items to storage","פריטים קבועים לאחסון"}. +{"Ping query is incorrect","שאילתת פינג הינה שגויה"}. {"Ping","פינג"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","אנא שים לב כי אפשרויות אלו יגבו את מסד הנתונים המובנה Mnesia בלבד. אם הינך עושה שימוש במודול ODBC, עליך גם לגבות את מסד הנתונים SQL אשר מצוי ברשותך בנפרד."}. -{"Please specify file name.","אנא ציין שם קובץ."}. -{"Please specify file size.","אנא ציין גודל קובץ."}. {"Please, wait for a while before sending new voice request","אנא, המתן לזמן מה לפני שליחת בקשת ביטוי חדשה"}. {"Pong","פונג"}. -{"Port ~b","פורט ~b"}. -{"Port","פורט"}. {"Present real Jabber IDs to","הצג כתובות Jabber ממשיות"}. {"private, ","פרטי, "}. -{"Protocol","פרוטוקול"}. {"Publish-Subscribe","‫Publish-Subscribe"}. {"PubSub subscriber request","בקשת מנוי PubSub"}. {"Purge all items when the relevant publisher goes offline","טהר את כל הפריטים כאשר המפרסם הרלוונטי הופך לבלתי מקוון"}. {"Queries to the conference members are not allowed in this room","שאילתות אל חברי הועידה אינן מותרות בחדר זה"}. {"RAM and disc copy","העתק RAM וגם תקליטור"}. {"RAM copy","העתק RAM"}. -{"Raw","גולמי"}. {"Really delete message of the day?","באמת למחוק את בשורת היום?"}. {"Recipient is not in the conference room","מקבל אינו מצוי בחדר הועידה"}. -{"Register a Jabber account","רשום חשבון Jabber"}. -{"Registered nicknames","שמות כינוי רשומים"}. -{"Registered Users:","משתמשים רשומים:"}. -{"Registered Users","משתמשים רשומים"}. {"Register","הרשם"}. -{"Registration in mod_irc for ","רישום בתוך mod_irc עבור "}. {"Remote copy","העתק מרוחק"}. -{"Remove All Offline Messages","הסר את כל ההודעות הלא מקוונות"}. {"Remove User","הסר משתמש"}. -{"Remove","הסר"}. {"Replaced by new connection","הוחלף בחיבור חדש"}. {"Resources","משאבים"}. {"Restart Service","אתחל שירות"}. -{"Restart","אתחל"}. {"Restore Backup from File at ","שחזר גיבוי מתוך קובץ אצל "}. {"Restore binary backup after next ejabberd restart (requires less memory):","שחזר גיבוי בינארי לאחר האתחול הבא של ejabberd (מצריך פחות זיכרון):"}. {"Restore binary backup immediately:","שחזר גיבוי בינארי לאלתר:"}. @@ -316,14 +261,9 @@ {"Room Occupants","נוכחי חדר"}. {"Room title","כותרת חדר"}. {"Roster groups allowed to subscribe","קבוצות רשימה מורשות להירשם"}. -{"Roster of ","רשימה של "}. {"Roster size","גודל רשימה"}. -{"Roster","רשימה"}. -{"RPC Call Error","שגיאת קריאת RPC"}. {"Running Nodes","צמתים מורצים"}. -{"~s access rule configuration","~s תצורת כללי גישה"}. {"Saturday","יום שבת"}. -{"Script check","בדיקת תסריט"}. {"Search Results for ","תוצאות חיפוש עבור "}. {"Search users in ","חיפוש משתמשים אצל "}. {"Send announcement to all online users on all hosts","שלח בשורה לכל המשתמשים המקוונים בכל המארחים"}. @@ -331,38 +271,23 @@ {"Send announcement to all users on all hosts","שלח בשורה לכל המשתמשים בכל המארחים"}. {"Send announcement to all users","שלח בשורה לכל המשתמשים"}. {"September","ספטמבר"}. -{"Server ~b","שרת ~b"}. {"Server:","שרת:"}. -{"Server","שרת"}. {"Set message of the day and send to online users","קבע את בשורת היום ושלח למשתמשים מקוונים"}. {"Set message of the day on all hosts and send to online users","קבע את בשורת היום בכל המארחים ושלח למשתמשים מקוונים"}. {"Shared Roster Groups","קבוצות רשימה משותפות"}. {"Show Integral Table","הצג טבלה אינטגרלית"}. {"Show Ordinary Table","הצג טבלה רגילה"}. {"Shut Down Service","כבה שירות"}. -{"~s invites you to the room ~s","‫~s מזמינך לחדר ~s"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","ישנם לקוחות Jabber אשר מסוגלים לאחסן את הסיסמה שלך בתוך המחשב, אולם עליך לעשות זאת רק בתוך המחשב האישי שלך מסיבות ביטחוניות."}. {"Specify the access model","ציין מודל גישה"}. {"Specify the event message type","ציין טיפוס הודעת אירוע"}. {"Specify the publisher model","ציין מודל פרסום"}. -{"~s's Offline Messages Queue","תור הודעות לא מקוונות של ~s"}. -{"Start Modules at ","התחל מודולים אצל "}. -{"Start Modules","התחל מודולים"}. -{"Start","התחל"}. -{"Statistics of ~p","סטטיסטיקות של ~p"}. -{"Statistics","סטטיסטיקה"}. -{"Stop Modules at ","הפסק מודולים אצל "}. -{"Stop Modules","הפסק מודולים"}. {"Stopped Nodes","צמתים שנפסקו"}. -{"Stop","הפסק"}. -{"Storage Type","טיפוס אחסון"}. {"Store binary backup:","אחסן גיבוי בינארי:"}. {"Store plain text backup:","אחסן גיבוי טקסט גלוי (plain text):"}. {"Subject","נושא"}. {"Submitted","נשלח"}. -{"Submit","שלח"}. {"Subscriber Address","כתובת מנוי"}. -{"Subscription","הרשמה"}. +{"Subscriptions are not allowed","הרשמות אינן מורשות"}. {"Sunday","יום ראשון"}. {"That nickname is already in use by another occupant","שם כינוי זה כבר מצוי בשימוש על ידי נוכח אחר"}. {"That nickname is registered by another person","שם כינוי זה הינו רשום על ידי מישהו אחר"}. @@ -371,50 +296,40 @@ {"The collections with which a node is affiliated","האוספים עמם צומת מסונף"}. {"The password is too weak","הסיסמה חלשה מדי"}. {"the password is","הסיסמה היא"}. -{"The password of your Jabber account was successfully changed.","סיסמת חשבון Jabber שונתה בהצלחה."}. -{"There was an error changing the password: ","אירעה שגיאה בשינוי הסיסמה: "}. {"There was an error creating the account: ","אירעה שגיאה ביצירת החשבון: "}. {"There was an error deleting the account: ","אירעה שגיאה במחיקת החשבון: "}. -{"This IP address is blacklisted in ~s","כתובת IP זו רשומה ברשימה שחורה בתוך ~s"}. -{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","חלק זה אינו ער לרישיות: macbeth הינה זהה למחרוזת MacBeth וגם Macbeth."}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","עמוד זה מתיר ליצור חשבון Jabber בשרת Jabber זה. כתובת JID ‏(Jabber IDentifier) תגובש באופן של: username@server. אנא קרא בזהירות את ההוראות למילוי נכון של השדות."}. -{"This page allows to unregister a Jabber account in this Jabber server.","עמוד זה מתיר לך לבטל רישום של חשבון Jabber בתוך שרת Jabber זה."}. +{"This room is not anonymous","חדר זה אינו אנונימי"}. {"Thursday","יום חמישי"}. {"Time delay","זמן שיהוי"}. -{"Time","זמן"}. +{"To register, visit ~s","כדי להירשם, בקרו ~s"}. +{"Token TTL","סימן TTL"}. +{"Too many active bytestreams","יותר מדי יחידות bytestream פעילות"}. {"Too many CAPTCHA requests","יותר מדי בקשות CAPTCHA"}. {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","יותר מדי (~p) אימותים כושלים מתוך כתובת IP זו (~s). הכתובת תורשה לקבל גישה בשעה ~s UTC"}. {"Too many unacked stanzas","יותר מדי סטנזות בלי אישורי קבלה"}. -{"To ~s","אל ~s"}. -{"Total rooms","חדרים סה״כ"}. -{"To","לכבוד"}. +{"Too many users in this conference","יותר מדי משתמשים בועידה זו"}. {"Traffic rate limit is exceeded","מגבלת שיעור תעבורה נחצתה"}. -{"Transactions Aborted:","טרנזקציות שבוטלו:"}. -{"Transactions Committed:","טרנזקציות שבוצעו:"}. -{"Transactions Logged:","טרנזקציות שנרשמו:"}. -{"Transactions Restarted:","טרנזקציות שהותחלו מחדש:"}. {"Tuesday","יום שלישי"}. {"Unable to generate a CAPTCHA","אין אפשרות להפיק CAPTCHA"}. {"Unauthorized","לא מורשה"}. -{"Unregister a Jabber account","בטל רישום חשבון Jabber"}. +{"Unexpected action","פעולה לא צפויה"}. {"Unregister","בטל רישום"}. {"Update message of the day (don't send)","עדכן את בשורת היום (אל תשלח)"}. {"Update message of the day on all hosts (don't send)","עדכן את בשורת היום בכל המארחים (אל תשלח)"}. -{"Update plan","תכנית עדכון"}. -{"Update ~p","עדכון ~p"}. -{"Update script","תסריט עדכון"}. -{"Update","עדכן"}. -{"Uptime:","זמן פעילות:"}. -{"Use of STARTTLS required","נדרש שימוש של STARTTLS"}. +{"User already exists","משתמש כבר קיים"}. {"User JID","‏JID משתמש"}. +{"User (jid)","משתמש (jid)"}. {"User Management","ניהול משתמשים"}. +{"User session not found","סשן משתמש לא נמצא"}. +{"User session terminated","סשן משתמש הסתיים"}. {"Username:","שם משתמש:"}. {"Users are not allowed to register accounts so quickly","משתמשים אינם מורשים לרשום חשבונות כל כך במהירות"}. {"Users Last Activity","פעילות משתמשים אחרונה"}. -{"User ~s","משתמש ~s"}. {"Users","משתמשים"}. {"User","משתמש"}. -{"Validate","הענק תוקף"}. +{"Value of '~s' should be boolean","ערך של '~s' צריך להיות boolean"}. +{"Value of '~s' should be datetime string","ערך של '~s' צריך להיות מחרוזת datetime"}. +{"Value of '~s' should be integer","ערך של '~s' צריך להיות integer"}. {"vCard User Search","חיפוש משתמש vCard"}. {"Virtual Hosts","מארחים מדומים"}. {"Visitors are not allowed to change their nicknames in this room","מבקרים אינם מורשים לשנות את שמות הכינויים שלהם בחדר זה"}. @@ -425,16 +340,13 @@ {"Wednesday","יום רביעי"}. {"When to send the last published item","מתי לשלוח את הפריט המפורסם האחרון"}. {"Whether to allow subscriptions","האם להתיר הרשמות"}. -{"You can later change your password using a Jabber client.","באפשרותך לשנות את הסיסמה שלך מאוחר יותר באמצעות לקוח Jabber."}. {"You have been banned from this room","נאסרת מן חדר זה"}. +{"You have joined too many conferences","הצטרפת ליותר מדי ועידות"}. {"You must fill in field \"Nickname\" in the form","עליך למלא את השדה \"שם כינוי\" בתוך התבנית"}. {"You need a client that supports x:data and CAPTCHA to register","עליך להשתמש בלקוח אשר תומך x:data וגם CAPTCHA כדי להירשם"}. {"You need a client that supports x:data to register the nickname","עליך להשתמש בלקוח אשר תומך x:data כדי לרשום את השם כינוי"}. -{"You need an x:data capable client to configure mod_irc settings","עליך להשתמש בלקוח אשר מסוגל להבין x:data כדי להגדיר הגדרות mod_irc"}. -{"You need an x:data capable client to configure room","עליך להשתמש בלקוח אשר מסוגל להבין x:data כדי להגדיר חדר"}. {"You need an x:data capable client to search","עליך להשתמש בלקוח אשר מסוגל להבין x:data כדי לחפש"}. {"Your active privacy list has denied the routing of this stanza.","רשימת הפרטיות הפעילה שלך אסרה את הניתוב של סטנזה זו."}. {"Your contact offline message queue is full. The message has been discarded.","תור הודעות קשר לא מקוונות הינו מלא. ההודעה סולקה."}. -{"Your Jabber account was successfully created.","חשבון Jabber נוצר בהצלחה."}. -{"Your Jabber account was successfully deleted.","חשבון Jabber נמחק בהצלחה."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","ההודעות שלך לערוץ ~s הינן חסומות. כדי לבטל את חסימתן, בקר בכתובת ~s"}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","ההודעות שלך לערוץ ~s הינן חסומות. כדי לבטל את חסימתן, בקר בכתובת ~s"}. +{"You're not allowed to create nodes","אינך מורשה ליצור צמתים"}. diff --git a/priv/msgs/he.po b/priv/msgs/he.po deleted file mode 100644 index 80c1155e5..000000000 --- a/priv/msgs/he.po +++ /dev/null @@ -1,1964 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: ejabberd 2.1.x\n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: Isratine Citizen \n" -"Language-Team: Rahut \n" -"Language: he\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Hebrew\n" -"X-Poedit-Language: Hebrew (עברית)\n" -"X-Generator: Poedit 1.5.4\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Source-Language: en\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "נדרש שימוש של STARTTLS" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "לא סופק משאב" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "הוחלף בחיבור חדש" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "נבעט/ה" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "רשימת הפרטיות הפעילה שלך אסרה את הניתוב של סטנזה זו." - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "יותר מדי סטנזות בלי אישורי קבלה" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "הזן את הכיתוב שאתה רואה" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "ההודעות שלך לערוץ ~s הינן חסומות. כדי לבטל את חסימתן, בקר בכתובת ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "אם אינך רואה תמונת CAPTCHA כאן, בקר בעמוד רשת." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "עמוד רשת CAPTCHA" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "‏CAPTCHA הינה תקפה." - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "משתמש" - -#: ejabberd_oauth.erl:256 -msgid "Server" -msgstr "שרת" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "סיסמה" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "קבל" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "לא מורשה" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "מנהל רשת ejabberd" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "הנהלה" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "רשימות בקרת גישה" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "נשלח" - -# פגום -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "פורמט רע" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "שלח" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "גולמי" - -# נבחרים -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "מחק נבחרות" - -# חוקי -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "כללי גישה" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "~s תצורת כללי גישה" - -# וירטואליים -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "מארחים מדומים" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "משתמשים" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "משתמשים מקוונים" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "פעילות משתמשים אחרונה" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "משך זמן: " - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "חודש אחרון" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "שנה אחרונה" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "כל פעילות" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "הצג טבלה רגילה" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "הצג טבלה אינטגרלית" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "סטטיסטיקה" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "לא נמצא" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "צומת לא נמצא" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "הוסף חדש" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "מארח" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "משתמשים רשומים" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "הוסף משתמש" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "הודעות לא מקוונות" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "פעילות אחרונה" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "אף פעם" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "מקוון" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "משתמשים רשומים:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "משתמשים מקוונים:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "חיבורי s2s יוצאים:" - -#: ejabberd_web_admin.erl:1559 -msgid "Incoming s2s Connections:" -msgstr "חיבורי s2s נכנסים:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "אין" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "שנה סיסמה" - -#: ejabberd_web_admin.erl:1673 -msgid "User ~s" -msgstr "משתמש ~s" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "משאבים מחוברים:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "סיסמה:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "הסר משתמש" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "אין מידע" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "צמתים" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "צמתים מורצים" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "צמתים שנפסקו" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -msgid "Node ~p" -msgstr "צומת ~p" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "מסד נתונים" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "גיבוי" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "פורטים מואזנים" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "עדכן" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "אתחל" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "הפסק" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "מודולים" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "שגיאת קריאת RPC" - -#: ejabberd_web_admin.erl:1917 -msgid "Database Tables at ~p" -msgstr "טבלאות מסד נתונים אצל ~p" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "שם" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "טיפוס אחסון" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "אלמנטים" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "זיכרון" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "שגיאה" - -#: ejabberd_web_admin.erl:1955 -msgid "Backup of ~p" -msgstr "גיבוי של ~p" - -# האינטגרלי לחוד -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"אנא שים לב כי אפשרויות אלו יגבו את מסד הנתונים המובנה Mnesia בלבד. אם הינך " -"עושה שימוש במודול ODBC, עליך גם לגבות את מסד הנתונים SQL אשר מצוי ברשותך " -"בנפרד." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "אחסן גיבוי בינארי:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "אישור" - -# ללא דיחוי -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "שחזר גיבוי בינארי לאלתר:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "שחזר גיבוי בינארי לאחר האתחול הבא של ejabberd (מצריך פחות זיכרון):" - -# תמליל ברור -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "אחסן גיבוי טקסט גלוי (plain text):" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "שחזר גיבוי טקסט גלוי (plain text) לאלתר:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "יבא מידע משתמשים מתוך קובץ PIEFXIS ‏(XEP-0227):" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "יצא מידע של כל המשתמשים שבתוך שרת זה לתוך קבצי PIEFXIS ‏(XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "יצא מידע של כל המשתמשים שבתוך מארח לתוך קבצי PIEFXIS ‏(XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "יצא את כל הטבלאות בתור שאילתות SQL לתוך קובץ:" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "יבא נתוני משתמש מתוך קובץ סליל (spool file) של jabberd14:" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "יבא נתוני משתמשים מתוך מדור סליל (spool directory) של jabberd14:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "פורטים מואזנים אצל " - -#: ejabberd_web_admin.erl:2144 -msgid "Modules at ~p" -msgstr "מודולים אצל ~p" - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "סטטיסטיקות של ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "זמן פעילות:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "זמן מחשב (CPU):" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "טרנזקציות שבוצעו:" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "טרנזקציות שבוטלו:" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "טרנזקציות שהותחלו מחדש:" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "טרנזקציות שנרשמו:" - -#: ejabberd_web_admin.erl:2243 -msgid "Update ~p" -msgstr "עדכון ~p" - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "תכנית עדכון" - -# adjusted -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "מודולים שהותאמו" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "תסריט עדכון" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "תסריט עדכון Low level" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "בדיקת תסריט" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "‫IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "פורט" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "פרוטוקול" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "מודול" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "אפשרויות" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "התחל" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "פקודות" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "פינג" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "פונג" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "באמת למחוק את בשורת היום?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "נושא" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "גוף הודעה" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "לא סופק גוף עבור הודעת בשורה" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "בשורות" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "שלח בשורה לכל המשתמשים" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "שלח בשורה לכל המשתמשים בכל המארחים" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "שלח בשורה לכל המשתמשים המקוונים" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "שלח בשורה לכל המשתמשים המקוונים בכל המארחים" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "קבע את בשורת היום ושלח למשתמשים מקוונים" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "קבע את בשורת היום בכל המארחים ושלח למשתמשים מקוונים" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "עדכן את בשורת היום (אל תשלח)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "עדכן את בשורת היום בכל המארחים (אל תשלח)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "מחק את בשורת היום" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "מחק את בשורת היום בכל המארחים" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "תצורה" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "התחל מודולים" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "הפסק מודולים" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "שחזר" - -# הטל אל קובץ תמליל -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "השלך לקובץ טקסט" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "ייבוא קובץ" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "ייבוא מדור" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "אתחל שירות" - -# שירות כיבוי -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "כבה שירות" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "מחק משתמש" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "סיים סשן משתמש" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "השג סיסמת משתמש" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "שנה סיסמת משתמש" - -# התחברות -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "השג זמן כניסה אחרון של משתמש" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "השג סטטיסטיקת משתמש" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "השג מספר של משתמשים רשומים" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "השג מספר של משתמשים מקוונים" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "ניהול משתמשים" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "כל המשתמשים" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "חיבורי s2s יוצאים" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "ניהול גיבוי" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "יבא משתמשים מתוך קבצי סליל (Spool Files) של jabberd14" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "אל ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "מאת ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "תצורת טבלאות מסד נתונים אצל " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "בחר טיפוס אחסון של טבלאות" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "העתק של תקליטור בלבד" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "העתק RAM וגם תקליטור" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "העתק RAM" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "העתק מרוחק" - -# at (time)? בשעה -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "הפסק מודולים אצל " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "בחר מודולים להפסקה" - -# at (time)? בשעה -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "התחל מודולים אצל " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "הזן רשימה של {מודול, [אפשרויות]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "רשימה של מודולים להפעלה" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "גבה לקובץ אצל " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "הזן נתיב לקובץ גיבוי" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "נתיב לקובץ" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "שחזר גיבוי מתוך קובץ אצל " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "השלך גיבוי לקובץ טקסט אצל " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "הזן נתיב לקובץ טקסט" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "ייבוא משתמש מתוך קובץ אצל " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "הזן נתיב לקובץ סליל (spool file) של jabberd14" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "ייבוא משתמשים מתוך מדור אצל " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "הזן נתיב למדור סליל (spool dir) של jabberd14" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "נתיב למדור" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "זמן שיהוי" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "תצורת רשימת בקרת גישה" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "רשימות בקרת גישה" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "תצורת גישה" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "כללי גישה" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "מזהה Jabber" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "אימות סיסמה" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "מספר של משתמשים רשומים" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "מספר של משתמשים מקוונים" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "כניסה אחרונה" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "גודל רשימה" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "כתובות IP" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "משאבים" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "ניהול של " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "פעולה על משתמש" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "ערוך מאפיינים" - -#: mod_fail2ban.erl:95 -msgid "" -"Too many (~p) failed authentications from this IP address (~s). The address " -"will be unblocked at ~s UTC" -msgstr "" -"יותר מדי (~p) אימותים כושלים מתוך כתובת IP זו (~s). הכתובת תורשה לקבל גישה " -"בשעה ~s UTC" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "אנא ציין גודל קובץ." - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "אנא ציין שם קובץ." - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "כתובת IP זו רשומה ברשימה שחורה בתוך ~s" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "גישה נדחתה על ידי פוליסת שירות" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "טרנספורט IRC" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "מודול IRC של ejabberd" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "עליך להשתמש בלקוח אשר מסוגל להבין x:data כדי להגדיר הגדרות mod_irc" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "רישום בתוך mod_irc עבור " - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"הזן שם משתמש, קידודים, פורטים וסיסמאות בהם ברצונך להשתמש לצורך התחברות לשרתי " -"IRC" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "שם משתמש IRC" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"אם ברצונך לציין פורטים, סיסמאות, קידודים אחרים עבור שרתים של IRC, מלא את " -"רשימה זו עם ערכים בפורמט '{\"irc server\", \"encoding\", port, \"password" -"\"}'. באופן שגרתי שירות זה משתמש בקידוד \"~s\", פורט ~p, סיסמה ריקה." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"דוגמא: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." - -# פרמטרי חיבור -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "פרמטרים של חיבור" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "הצטרף לערוץ IRC" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "ערוץ IRC (אל תשים סימן # ראשון)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "שרת IRC" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "הצטרף לערוץ IRC כאן." - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "הצטרף לערוץ IRC במזהה Jabber זה: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "הגדרות IRC" - -# השלם -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"הזן שם משתמש וקידודים בהם ברצונך להשתמש לצורך התחברות לשרתי IRC. לחץ 'הבא' " -"כדי להשיג עוד שדות למילוי. לחץ 'סיים' כדי לשמור הגדרות." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "שם משתמש IRC" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "סיסמה ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "פורט ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "קידוד עבור שרת ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "שרת ~b" - -#: mod_mam.erl:541 -msgid "Only members may query archives of this room" -msgstr "רק חברים רשאים לתשאל ארכיונים של חדר זה" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "רק מנהלי שירות רשאים לשלוח הודעות שירות" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "יצירת חדר נדחתה על ידי פוליסת שירות" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "חדר ועידה לא קיים" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "חדרי שיחה" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "חדרים ריקים" - -# to register nickname -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "עליך להשתמש בלקוח אשר תומך x:data כדי לרשום את השם כינוי" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "רישום שם כינוי אצל " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "הזן שם כינוי אשר ברצונך לרשום" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "שם כינוי" - -# note: another person > someone else -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "שם כינוי זה הינו רשום על ידי מישהו אחר" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "עליך למלא את השדה \"שם כינוי\" בתוך התבנית" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "מודול MUC של ejabberd" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "שיחה מרובת משתמשים" - -#: mod_muc_admin.erl:249 -msgid "Total rooms" -msgstr "חדרים סה״כ" - -#: mod_muc_admin.erl:250 -msgid "Permanent rooms" -msgstr "חדרים קבועים" - -#: mod_muc_admin.erl:251 -msgid "Registered nicknames" -msgstr "שמות כינוי רשומים" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "רשימה של חדרים" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "תצורת חדר שיחה שונתה" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "נכנס/ת אל החדר" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "עוזב/ת את החדר" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "נאסר/ה" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "נבעט/ה משום שינוי סינוף" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "נבעט/ה משום שהחדר שונה אל חברים-בלבד" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "נבעט/ה משום כיבוי מערכת" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "ידועה כעת בכינוי" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " הגדיר/ה את הנושא אל: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "חדר שיחה נוצר כעת" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "חדר שיחה הינו הרוס" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "חדר שיחה מותחל כעת" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "חדר שיחה הינו מופסק" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "יום שני" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "יום שלישי" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "יום רביעי" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "יום חמישי" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "יום שישי" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "יום שבת" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "יום ראשון" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "ינואר" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "פברואר" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "מרץ" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "אפריל" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "מאי" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "יוני" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "יולי" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "אוגוסט" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "ספטמבר" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "אוקטובר" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "נובמבר" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "דצמבר" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "תצורת חדר" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "נוכחי חדר" - -# נעברה -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "מגבלת שיעור תעבורה נחצתה" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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) " -"ונבעט מתוך החדר" - -# חל איסור -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "אין זה מותר לשלוח הודעות פרטיות לועידה" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "אנא, המתן לזמן מה לפני שליחת בקשת ביטוי חדשה" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "בקשות ביטוי מנוטרלות בועידה זו" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "נכשל לחלץ JID מתוך אישור בקשת הביטוי שלך" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "רק אחראים יכולים לאשר בקשות ביטוי" - -# הולם -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "טיפוס הודעה לא מתאים" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "אין זה מותר לשלוח הודעות פרטיות מן טיפוס \"groupchat\"" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "מקבל אינו מצוי בחדר הועידה" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "אין זה מותר לשלוח הודעות פרטיות" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "רק נוכחים רשאים לשלוח הודעות אל הועידה" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "רק נוכחים רשאים לשלוח שאילתות אל הועידה" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "שאילתות אל חברי הועידה אינן מותרות בחדר זה" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "רק אחראים ומשתתפים רשאים לשנות את הנושא בחדר זה" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "רק אחראים רשאים לשנות את הנושא בחדר זה" - -# רשאים -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "מבקרים אינם מורשים לשלוח הודעות אל כל הנוכחים" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "מבקרים אינם מורשים לשנות את שמות הכינויים שלהם בחדר זה" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "שם כינוי זה כבר מצוי בשימוש על ידי נוכח אחר" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "נאסרת מן חדר זה" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "נדרשת חברות כדי להיכנס אל חדר זה" - -# מילת־מעבר -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "נדרשת סיסמה כדי להיכנס אל חדר זה" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "יותר מדי בקשות CAPTCHA" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "אין אפשרות להפיק CAPTCHA" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "מילת מעבר שגויה" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "נדרשות הרשאות מנהל" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "נדרשות הרשאות אחראי" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "מזהה Jabber ‏~s הינו שגוי" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "שם כינוי ~s לא קיים בחדר" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "סינוף שגוי: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "תפקיד שגוי: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "נדרשות הרשאות בעלים" - -# תצורה של חדר -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "תצורת חדר ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "כותרת חדר" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "תיאור חדר" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "הפוך חדר לחדר קבוע" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "הפוך חדר לחדר שנתון לחיפוש פומבי" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "הפוך רשימת משתתפים לפומבית" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "הפוך חדר לחדר מוגן במילת מעבר" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "מספר מרבי של נוכחים" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "ללא הגבלה" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "הצג כתובות Jabber ממשיות" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "לאחראים בלבד" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "לכל אחד" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "תפקידים להם נוכחות הינה משודרת" - -#: mod_muc_room.erl:3486 -msgid "Moderator" -msgstr "אחראי" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "משתתף" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "מבקר" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "הפוך חדר לחדר עבור חברים-בלבד" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "הפוך חדר לחדר מבוקר" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "משתמשים שגרתיים כמשתתפים" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "התר למשתמשים לשנות את הנושא" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "התר למשתמשים לשלוח הודעות פרטיות" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "התר למבקרים לשלוח הודעות פרטיות אל" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "אף אחד" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "התר למשתמשים לתשאל משתמשים אחרים" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "התר למשתמשים לשלוח הזמנות" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "התר למבקרים לשלוח טקסט מצב בתוך עדכוני נוכחות" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "התר למבקרים לשנות שם כינוי" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "התר למבקרים לשלוח בקשות ביטוי" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "תדירות מינימלית בין בקשות ביטוי (בשניות)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "הפוך חדר לחדר מוגן CAPTCHA" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "אפשר אחסון הודעות" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "הוצא כתובות Jabber מתוך אתגר CAPTCHA" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "אפשר רישום פעילות" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "עליך להשתמש בלקוח אשר מסוגל להבין x:data כדי להגדיר חדר" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "מספר של נוכחים" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "פרטי, " - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "בקשת ביטוי" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "אשר או דחה בקשת ביטוי." - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "‏JID משתמש" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "להעניק ביטוי לאישיות זו?" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "‫~s מזמינך לחדר ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "הסיסמה היא" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "שידור מרובב" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "שירות שידור מרובב של ejabberd" - -# תור הודעות לא מקוונות של הקשר שלך הינו -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "תור הודעות קשר לא מקוונות הינו מלא. ההודעה סולקה." - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "תור הודעות לא מקוונות של ~s" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "זמן" - -#: mod_offline.erl:812 -msgid "From" -msgstr "מאת" - -#: mod_offline.erl:813 -msgid "To" -msgstr "לכבוד" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "חבילת מידע" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "הודעות לא מקוונות:" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "הסר את כל ההודעות הלא מקוונות" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "מודול SOCKS5 Bytestreams של ejabberd" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "‫Publish-Subscribe" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "מודול Publish-Subscribe של ejabberd" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "בקשת מנוי PubSub" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "בחר האם לאשר את ההרשמה של ישות זו." - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "מזהה צומת (NID)" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "כתובת מנוי" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "להתיר למזהה Jabber זה להירשם לצומת PubSub זה?" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "מסור מטעני ייעוד (מטע״ד) יחד עם התראות אירוע" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "מסור התראות אירוע" - -# משתמשים רשומים -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "הודע מנויים כאשר תצורת הצומת משתנה" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "הודע מנויים כאשר הצומת נמחק" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "הודע מנויים כאשר פריטים מוסרים מתוך הצומת" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "פריטים קבועים לאחסון" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "שם ידידותי עבור הצומת" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "מספר מרבי של פריטים לקיבוע" - -# בין אם -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "האם להתיר הרשמות" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "ציין מודל גישה" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "קבוצות רשימה מורשות להירשם" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "ציין מודל פרסום" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "טהר את כל הפריטים כאשר המפרסם הרלוונטי הופך לבלתי מקוון" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "ציין טיפוס הודעת אירוע" - -# בבתים בבייטים (bytes) -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "גודל מרבי של מטען ייעוד (payload) ביחידות מידה של byte" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "מתי לשלוח את הפריט המפורסם האחרון" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "מסור התראות למשתמשים זמינים בלבד" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "האוספים עמם צומת מסונף" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "אימות CAPTCHA נכשל" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "עליך להשתמש בלקוח אשר תומך x:data וגם CAPTCHA כדי להירשם" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "בחר שם משתמש וסיסמה כדי להירשם בעזרת שרת זה" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "הסיסמה חלשה מדי" - -# כה מהר -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "משתמשים אינם מורשים לרשום חשבונות כל כך במהירות" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "חשבון Jabber נוצר בהצלחה." - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "אירעה שגיאה ביצירת החשבון: " - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "חשבון Jabber נמחק בהצלחה." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "אירעה שגיאה במחיקת החשבון: " - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "סיסמת חשבון Jabber שונתה בהצלחה." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "אירעה שגיאה בשינוי הסיסמה: " - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "רישום חשבון Jabber" - -# Why masculine form matters. -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "רשום חשבון Jabber" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "בטל רישום חשבון Jabber" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"עמוד זה מתיר ליצור חשבון Jabber בשרת Jabber זה. כתובת JID ‏(Jabber " -"IDentifier) תגובש באופן של: username@server. אנא קרא בזהירות את ההוראות " -"למילוי נכון של השדות." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "שם משתמש:" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "חלק זה אינו ער לרישיות: macbeth הינה זהה למחרוזת MacBeth וגם Macbeth." - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "תווים לא מורשים:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "שרת:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "אל תגלה את הסיסמה שלך לאף אחד, אפילו לא למנהלים של שרת Jabber." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "באפשרותך לשנות את הסיסמה שלך מאוחר יותר באמצעות לקוח Jabber." - -# בוטח -# trust that your -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"ישנם לקוחות Jabber אשר מסוגלים לאחסן את הסיסמה שלך בתוך המחשב, אולם עליך " -"לעשות זאת רק בתוך המחשב האישי שלך מסיבות ביטחוניות." - -# תישכח -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"שנן את הסיסמה שלך, או רשום אותה בנייר שמור במקום בטוח. אצל Jabber אין דרך " -"אוטומטית לשחזר את הסיסמה שלך במידה וזו תישמט מתוך זיכרונך." - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "אימות סיסמה:" - -# רשום -#: mod_register_web.erl:269 -msgid "Register" -msgstr "הרשם" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "סיסמה ישנה:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "סיסמה חדשה:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "עמוד זה מתיר לך לבטל רישום של חשבון Jabber בתוך שרת Jabber זה." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "בטל רישום" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "הרשמה" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "ממתינות" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "קבוצות" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "הענק תוקף" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "הסר" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "רשימה של " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "הוסף מזהה Jabber" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "רשימה" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "קבוצות רשימה משותפות" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "שם:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "תיאור:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "חברים:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "קבוצות מוצגות:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "קבוצה " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "שרת ג׳אבּר Erlang" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "יום הולדת" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "עיר" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "ארץ" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "דוא״ל" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "שם משפחה" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"מלא את הטופס כדי לחפש אחר כל משתמש Jabber מבוקש (באפשרותך להוסיף * בסוף שדה " -"כדי להתאים למחרוזת-משנה)" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "שם מלא" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "שם אמצעי" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "שם ארגון" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "יחידת איגוד" - -# בקרב -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "חיפוש משתמשים אצל " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "עליך להשתמש בלקוח אשר מסוגל להבין x:data כדי לחפש" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "חיפוש משתמש vCard" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "מודול vCard של ejabberd" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "תוצאות חיפוש עבור " - -# שקול -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "מלא את שדות אלו כדי לחפש עבור כל משתמש Jabber מבוקש" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "שרתי s2s יוצאים:" - -#~ msgid "Code Update" -#~ msgstr "עדכון קוד" - -#~ msgid "Delete" -#~ msgstr "מחק" - -#~ msgid "This room is not anonymous" -#~ msgstr "חדר זה אינו אנונימי" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "משתתף זה נבעט מתוך החדר מכיוון שהוא שלח הודעת שגיאה" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "משתתף זה נבעט מתוך החדר משום שהוא שלח הודעת שגיאה אל משתתף אחר" - -# שגיאת נוכחות -# נוכחות שגויה -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "משתתף זה נבעט מתוך החדר משום שהוא שלח נוכחות שגיאה" - -#~ msgid "Notify owners about new subscribers and unsubscribers" -#~ msgstr "הודע בעלים אודות מנויים חדשים ומנויים שביטלו מנוי" - -#~ msgid "" -#~ "The type of node data, usually specified by the namespace of the payload " -#~ "(if any)" -#~ msgstr "סוג מידע ממסר, לרוב מצוין לפי מרחב־שמות של מטען הייעוד (אם בכלל)" - -#~ msgid "Group ID:" -#~ msgstr "קבוצה (GID):" - -#~ msgid "vCard" -#~ msgstr "‫vCard" - -#~ msgid "vCard Photo:" -#~ msgstr "תצלום vCard:" - -#~ msgid "Remove vCard" -#~ msgstr "הסרת vCard" - -#~ msgid "vCard size (characters):" -#~ msgstr "גודל vCard (תווים):" diff --git a/priv/msgs/hu.msg b/priv/msgs/hu.msg new file mode 100644 index 000000000..32e3174d0 --- /dev/null +++ b/priv/msgs/hu.msg @@ -0,0 +1,415 @@ +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" (Add * to the end of field to match substring)"," (adjon * karaktert a mező végéhez a részkarakterláncra illesztéshez)"}. +{" has set the subject to: "," beállította a tárgyat erre: "}. +{"A password is required to enter this room","Jelszó szükséges a szobába történő belépéshez"}. +{"Accept","Elfogadás"}. +{"Access denied by service policy","Hozzáférés megtagadva a szolgáltatási irányelv miatt"}. +{"Account doesn't exist","A fiók nem létezik"}. +{"Action on user","Művelet a felhasználón"}. +{"Add User","Felhasználó hozzáadása"}. +{"Administration of ","Adminisztrációja ennek: "}. +{"Administration","Adminisztráció"}. +{"Administrator privileges required","Adminisztrátori jogosultságok szükségesek"}. +{"All activity","Összes tevékenység"}. +{"All Users","Összes felhasználó"}. +{"Allow users to change the subject","Lehetővé tenni a felhasználóknak a tárgy megváltoztatását"}. +{"Allow users to query other users","Lehetővé tenni a felhasználóknak más felhasználók lekérdezését"}. +{"Allow users to send invites","Lehetővé tenni a felhasználóknak meghívók küldését"}. +{"Allow users to send private messages","Lehetővé tenni a felhasználóknak személyes üzenetek küldését"}. +{"Allow visitors to change nickname","Lehetővé tenni a látogatóknak a becenév megváltoztatását"}. +{"Allow visitors to send private messages to","Lehetővé tenni a látogatóknak személyes üzenetek küldését"}. +{"Allow visitors to send status text in presence updates","Lehetővé tenni a látogatóknak állapotszöveg küldését a jelenlét frissítéseiben"}. +{"Announcements","Közlemények"}. +{"April","április"}. +{"Attribute 'channel' is required for this request","A „channel” attribútum kötelező ennél a kérésnél"}. +{"Attribute 'id' is mandatory for MIX messages","Az „id” attribútum kötelező a MIX üzeneteknél"}. +{"Attribute 'jid' is not allowed here","A „jid” attribútum itt nem engedélyezett"}. +{"Attribute 'node' is not allowed here","A „node” attribútum itt nem engedélyezett"}. +{"August","augusztus"}. +{"Automatic node creation is not enabled","Automatikus csomópont-létrehozás nincs engedélyezve"}. +{"Backup Management","Biztonságimentés-kezelés"}. +{"Backup of ~p","~p biztonsági mentése"}. +{"Backup to File at ","Biztonsági mentés fájlba ekkor: "}. +{"Backup","Biztonsági mentés"}. +{"Bad format","Hibás formátum"}. +{"Birthday","Születésnap"}. +{"Both the username and the resource are required","A felhasználónév és az erőforrás is szükséges"}. +{"Bytestream already activated","A bájtfolyam már be van kapcsolva"}. +{"Cannot remove active list","Nem lehet eltávolítani az aktív listát"}. +{"Cannot remove default list","Nem lehet eltávolítani az alapértelmezett listát"}. +{"Change Password","Jelszó megváltoztatása"}. +{"Change User Password","Felhasználó jelszavának megváltoztatása"}. +{"Changing password is not allowed","A jelszó megváltoztatása nem engedélyezett"}. +{"Changing role/affiliation is not allowed","A szerep vagy a hovatartozás megváltoztatása nem engedélyezett"}. +{"Channel already exists","A csatorna már létezik"}. +{"Channel does not exist","A csatorna nem létezik"}. +{"Channels","Csatornák"}. +{"Characters not allowed:","Nem engedélyezett karakterek:"}. +{"Chatroom configuration modified","Csevegőszoba beállítása módosítva"}. +{"Chatroom is created","Csevegőszoba létrehozva"}. +{"Chatroom is destroyed","Csevegőszoba megszüntetve"}. +{"Chatroom is started","Csevegőszoba elindítva"}. +{"Chatroom is stopped","Csevegőszoba leállítva"}. +{"Chatrooms","Csevegőszobák"}. +{"Choose a username and password to register with this server","Válasszon felhasználónevet és jelszót a kiszolgálóra történő regisztráláshoz"}. +{"Choose storage type of tables","Táblák tárolótípusának kiválasztása"}. +{"Choose whether to approve this entity's subscription.","Annak kiválasztása, hogy elfogadja-e ennek a bejegyzésnek a feliratkozását."}. +{"City","Település"}. +{"Client acknowledged more stanzas than sent by server","Az ügyfél több stanzát nyugtázott, mint amennyit a kiszolgáló küldött"}. +{"Commands","Parancsok"}. +{"Conference room does not exist","A konferenciaszoba nem létezik"}. +{"Configuration of room ~s","A(z) ~s szoba beállítása"}. +{"Configuration","Beállítás"}. +{"Country","Ország"}. +{"Database failure","Adatbázishiba"}. +{"Database Tables Configuration at ","Adatbázistáblák beállítása itt: "}. +{"Database","Adatbázis"}. +{"December","december"}. +{"Default users as participants","Alapértelmezett felhasználók mint résztvevők"}. +{"Delete message of the day on all hosts","Napi üzenet törlése az összes gépen"}. +{"Delete message of the day","Napi üzenet törlése"}. +{"Delete User","Felhasználó törlése"}. +{"Disc only copy","Csak lemez másolása"}. +{"Dump Backup to Text File at ","Biztonsági mentés kiírása szövegfájlba itt: "}. +{"Dump to Text File","Kiírás szövegfájlba"}. +{"Edit Properties","Tulajdonságok szerkesztése"}. +{"Either approve or decline the voice request.","Hagyja jóvá vagy utasítsa el a hangkérelmet."}. +{"ejabberd HTTP Upload service","ejabberd HTTP feltöltési szolgáltatás"}. +{"ejabberd MUC module","ejabberd MUC modul"}. +{"ejabberd Multicast service","ejabberd üzenetszórási szolgáltatás"}. +{"ejabberd Publish-Subscribe module","ejabberd publikálás-feliratkozás modul"}. +{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 bájtfolyam modul"}. +{"ejabberd vCard module","ejabberd vCard modul"}. +{"ejabberd Web Admin","ejabberd webes adminisztráció"}. +{"ejabberd","ejabberd"}. +{"Email","E-mail"}. +{"Enable logging","Naplózás engedélyezése"}. +{"Enabling push without 'node' attribute is not supported","A „node” attribútum nélküli felküldés engedélyezése nem támogatott"}. +{"End User Session","Felhasználói munkamenet befejezése"}. +{"Enter nickname you want to register","Adja meg a becenevet, amelyet regisztrálni szeretne"}. +{"Enter path to backup file","Adja meg a biztonsági mentés fájl útvonalát"}. +{"Enter path to jabberd14 spool dir","Adja meg a jabberd14 tárolókönyvtár útvonalát"}. +{"Enter path to jabberd14 spool file","Adja meg a jabberd14 tárolófájl útvonalát"}. +{"Enter path to text file","Adja meg a szövegfájl útvonalát"}. +{"Enter the text you see","Írja be a látott szöveget"}. +{"Export all tables as SQL queries to a file:","Összes tábla exportálása SQL-lekérdezésekként egy fájlba:"}. +{"Export data of all users in the server to PIEFXIS files (XEP-0227):","A kiszolgálón lévő összes felhasználó adatainak exportálása PIEFXIS-fájlokba (XEP-0227):"}. +{"Export data of users in a host to PIEFXIS files (XEP-0227):","Egy gépen lévő felhasználók adatainak exportálása PIEFXIS-fájlokba (XEP-0227):"}. +{"External component failure","Külső összetevő hiba"}. +{"External component timeout","Külső összetevő időtúllépés"}. +{"Failed to activate bytestream","Nem sikerült bekapcsolni a bájtfolyamot"}. +{"Failed to extract JID from your voice request approval","Nem sikerült kinyerni a Jabber-azonosítót a hangkérelem jóváhagyásból"}. +{"Failed to map delegated namespace to external component","Nem sikerült leképezni a delegált névteret külső összetevőre"}. +{"Failed to parse HTTP response","Nem sikerült feldolgozni a HTTP választ"}. +{"Family Name","Családnév"}. +{"February","február"}. +{"File larger than ~w bytes","A fájl nagyobb ~w bájtnál"}. +{"Friday","péntek"}. +{"From ~ts","Feladó: ~ts"}. +{"Full Name","Teljes név"}. +{"Get Number of Online Users","Elérhető felhasználók számának lekérése"}. +{"Get Number of Registered Users","Regisztrált felhasználók számának lekérése"}. +{"Get Pending","Függőben lévő lekérése"}. +{"Get User Last Login Time","Felhasználó legutolsó bejelentkezési idejének lekérése"}. +{"Get User Statistics","Felhasználói statisztikák lekérése"}. +{"Given Name","Keresztnév"}. +{"has been banned","ki lett tiltva"}. +{"has been kicked because of a system shutdown","ki lett rúgva egy rendszerleállítás miatt"}. +{"has been kicked because of an affiliation change","ki lett rúgva egy hovatartozás megváltozása miatt"}. +{"has been kicked because the room has been changed to members-only","ki lett rúgva, mert a szobát megváltoztatták csak tagok részére"}. +{"has been kicked","ki lett rúgva"}. +{"Host unknown","Gép ismeretlen"}. +{"HTTP File Upload","HTTP fájlfeltöltés"}. +{"Idle connection","Tétlen kapcsolat"}. +{"If you don't see the CAPTCHA image here, visit the web page.","Ha nem látja itt a CAPTCHA képet, akkor látogassa meg a weboldalt."}. +{"Import Directory","Könyvtár importálása"}. +{"Import File","Fájl importálása"}. +{"Import user data from jabberd14 spool file:","Felhasználóadatok importálása jabberd14 tárolófájlból:"}. +{"Import User from File at ","Felhasználó importálása fájlból itt: "}. +{"Import users data from a PIEFXIS file (XEP-0227):","Felhasználók adatainak importálása PIEFXIS-fájlból (XEP-0227):"}. +{"Import users data from jabberd14 spool directory:","Felhasználók adatainak importálása jabberd14 tárolókönyvtárból:"}. +{"Import Users from Dir at ","Felhasználók importálása könyvtárból itt: "}. +{"Import Users From jabberd14 Spool Files","Felhasználók importálása jabberd14 tárolófájlokból"}. +{"Improper domain part of 'from' attribute","A „from” attribútum tartományrésze helytelen"}. +{"Improper message type","Helytelen üzenettípus"}. +{"Incorrect CAPTCHA submit","Hibás CAPTCHA beküldés"}. +{"Incorrect data form","Hibás adatűrlap"}. +{"Incorrect password","Hibás jelszó"}. +{"Incorrect value of 'action' attribute","Az „action” attribútum értéke hibás"}. +{"Incorrect value of 'action' in data form","Az „action” értéke hibás az adatűrlapon"}. +{"Incorrect value of 'path' in data form","A „path” értéke hibás az adatűrlapon"}. +{"Insufficient privilege","Nincs elegendő jogosultság"}. +{"Internal server error","Belső kiszolgálóhiba"}. +{"Invalid 'from' attribute in forwarded message","Érvénytelen „from” attribútum a továbbított üzenetben"}. +{"Invalid node name","Érvénytelen csomópontnév"}. +{"Invalid 'previd' value","Érvénytelen „previd” érték"}. +{"Invitations are not allowed in this conference","Meghívások nem engedélyezettek ebben a konferenciában"}. +{"IP addresses","IP-címek"}. +{"is now known as","mostantól úgy ismert mint"}. +{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Nem engedélyezett hibaüzeneteket küldeni a szobába. A résztvevő (~s) hibaüzenetet (~s) küldött, és ki lett rúgva a szobából"}. +{"It is not allowed to send private messages of type \"groupchat\"","Nem engedélyezett „groupchat” típusú személyes üzeneteket küldeni"}. +{"It is not allowed to send private messages to the conference","Nem engedélyezett személyes üzeneteket küldeni a konferenciába"}. +{"Jabber ID","Jabber-azonosító"}. +{"January","január"}. +{"JID normalization denied by service policy","A Jabber-azonosító normalizálása megtagadva a szolgáltatási irányelv miatt"}. +{"JID normalization failed","A Jabber-azonosító normalizálása nem sikerült"}. +{"joins the room","belépett a szobába"}. +{"July","július"}. +{"June","június"}. +{"Last Activity","Utolsó tevékenység"}. +{"Last login","Utolsó belépés"}. +{"Last month","Múlt hónap"}. +{"Last year","Múlt év"}. +{"leaves the room","elhagyta a szobát"}. +{"Make participants list public","Résztvevőlista nyilvánossá tétele"}. +{"Make room CAPTCHA protected","Szoba CAPTCHA-védetté tétele"}. +{"Make room members-only","Szoba beállítása csak tagoknak"}. +{"Make room moderated","Szoba moderálttá tétele"}. +{"Make room password protected","Szoba jelszóval védetté tétele"}. +{"Make room persistent","Szoba állandóvá tétele"}. +{"Make room public searchable","Szoba nyilvánosan kereshetővé tétele"}. +{"Malformed username","Helytelenül formázott felhasználónév"}. +{"MAM preference modification denied by service policy","MAM beállítások módosítása megtagadva a szolgáltatási irányelv miatt"}. +{"March","március"}. +{"Maximum Number of Occupants","Résztvevők legnagyobb száma"}. +{"May","május"}. +{"Membership is required to enter this room","Tagság szükséges a szobába lépéshez"}. +{"Message body","Üzenettörzs"}. +{"Message not found in forwarded payload","Nem található üzenet a továbbított adatokban"}. +{"Messages from strangers are rejected","Idegenektől származó üzenetek vissza vannak utasítva"}. +{"Middle Name","Középső név"}. +{"Moderator privileges required","Moderátori jogosultságok szükségesek"}. +{"Module failed to handle the query","A modul nem tudta kezelni a lekérdezést"}. +{"Monday","hétfő"}. +{"Multicast","Csoportcímzés"}. +{"Multi-User Chat","Többfelhasználós csevegés"}. +{"Name","Név"}. +{"Neither 'jid' nor 'nick' attribute found","Sem a „jid”, sem a „nick” attribútum nem található"}. +{"Neither 'role' nor 'affiliation' attribute found","Sem a „role”, sem az „affiliation” attribútum nem található"}. +{"Never","Soha"}. +{"New Password:","Új jelszó:"}. +{"Nickname can't be empty","A becenév nem lehet üres"}. +{"Nickname Registration at ","Becenév regisztrációja itt: "}. +{"Nickname","Becenév"}. +{"No address elements found","Nem találhatók cím elemek"}. +{"No addresses element found","Nem található címek elem"}. +{"No 'affiliation' attribute found","Nem található „affiliation” attribútum"}. +{"No available resource found","Nem található elérhető erőforrás"}. +{"No body provided for announce message","Nincs törzs megadva a közleményüzenethez"}. +{"No child elements found","Nem találhatók gyermekelemek"}. +{"No data form found","Nem található adatűrlap"}. +{"No Data","Nincs adat"}. +{"No features available","Nincsenek elérhető funkciók"}. +{"No element found","Nem található elem"}. +{"No hook has processed this command","Egyetlen horog sem dolgozta fel ezt a parancsot"}. +{"No info about last activity found","Nem található információ a legutolsó tevékenységgel kapcsolatban"}. +{"No 'item' element found","Nem található „item” elem"}. +{"No items found in this query","Nem találhatók elemek ebben a lekérdezésben"}. +{"No limit","Nincs korlát"}. +{"No module is handling this query","Egyetlen modul sem kezeli ezt a lekérdezést"}. +{"No node specified","Nincs csomópont megadva"}. +{"No 'password' found in data form","Nem található „password” az adatűrlapon"}. +{"No 'password' found in this query","Nem található „password” ebben a lekérdezésben"}. +{"No 'path' found in data form","Nem található „path” az adatűrlapon"}. +{"No pending subscriptions found","Nem találhatók függőben lévő feliratkozások"}. +{"No privacy list with this name found","Nem található ilyen nevű adatvédelmi lista"}. +{"No private data found in this query","Nem található személyes adat ebben a lekérdezésben"}. +{"No running node found","Nem található futó csomópont"}. +{"No services available","Nincsenek elérhető szolgáltatások"}. +{"No statistics found for this item","Nem találhatók statisztikák ehhez az elemhez"}. +{"No 'to' attribute found in the invitation","Nem található „to” attribútum a meghívásban"}. +{"Node already exists","A csomópont már létezik"}. +{"Node index not found","A csomópontindex nem található"}. +{"Node not found","A csomópont nem található"}. +{"Node ~p","~p csomópont"}. +{"Nodeprep has failed","A csomópont-előkészítés sikertelen"}. +{"Nodes","Csomópontok"}. +{"None","Nincs"}. +{"Not allowed","Nem engedélyezett"}. +{"Not Found","Nem található"}. +{"Not subscribed","Nincs feliratkozva"}. +{"November","november"}. +{"Number of online users","Elérhető felhasználók száma"}. +{"Number of registered users","Regisztrált felhasználók száma"}. +{"October","október"}. +{"OK","Rendben"}. +{"Old Password:","Régi jelszó:"}. +{"Online Users","Elérhető felhasználók"}. +{"Online","Elérhető"}. +{"Only or tags are allowed","Csak az vagy címkék engedélyezettek"}. +{"Only element is allowed in this query","Csak a elem engedélyezett ebben a lekérdezésben"}. +{"Only members may query archives of this room","Csak tagok kérdezhetik le ennek a szobának az archívumát"}. +{"Only moderators and participants are allowed to change the subject in this room","Csak moderátoroknak és résztvevőknek engedélyezett megváltoztatni a tárgyat ebben a szobában"}. +{"Only moderators are allowed to change the subject in this room","Csak moderátoroknak engedélyezett megváltoztatni a tárgyat ebben a szobában"}. +{"Only moderators can approve voice requests","Csak moderátorok hagyhatnak jóvá hangkérelmeket"}. +{"Only occupants are allowed to send messages to the conference","Csak résztvevőknek engedélyezett üzeneteket küldeni a konferenciába"}. +{"Only occupants are allowed to send queries to the conference","Csak résztvevőknek engedélyezett lekérdezéseket küldeni a konferenciába"}. +{"Only service administrators are allowed to send service messages","Csak szolgáltatás-adminisztrátoroknak engedélyezett szolgáltatási üzeneteket küldeni"}. +{"Organization Name","Szervezet neve"}. +{"Organization Unit","Szervezeti egység"}. +{"Outgoing s2s Connections","Kimenő s2s kapcsolatok"}. +{"Owner privileges required","Tulajdonosi jogosultságok szükségesek"}. +{"Packet relay is denied by service policy","Csomagátjátszás megtagadva a szolgáltatási irányelv miatt"}. +{"Password Verification","Jelszó ellenőrzése"}. +{"Password Verification:","Jelszó ellenőrzése:"}. +{"Password","Jelszó"}. +{"Password:","Jelszó:"}. +{"Path to Dir","Útvonal a könyvtárhoz"}. +{"Path to File","Útvonal a fájlhoz"}. +{"Period: ","Időszak: "}. +{"Ping query is incorrect","A lekérdezés pingelése hibás"}. +{"Ping","Ping"}. +{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Ne feledje, hogy ezek a beállítások csak a beépített Mnesia adatbázisról készítenek biztonsági mentést. Ha az ODBC modult használja, akkor az SQL adatbázisról is különálló biztonsági mentést kell készítenie."}. +{"Please, wait for a while before sending new voice request","Várjon egy kicsit az új hangkérelem küldése előtt"}. +{"Pong","Pong"}. +{"Previous session not found","Az előző munkamenet nem található"}. +{"Previous session PID has been killed","Az előző munkamenet folyamat-azonosítója ki lett lőve"}. +{"Previous session PID has exited","Az előző munkamenet folyamat-azonosítója kilépett"}. +{"Previous session PID is dead","Az előző munkamenet folyamat-azonosítója halott"}. +{"Previous session timed out","Az előző munkamenet túllépte az időkorlátot"}. +{"private, ","személyes, "}. +{"Publish-Subscribe","Publikálás-feliratkozás"}. +{"PubSub subscriber request","Publikálás-feliratkozás feliratkozási kérelem"}. +{"Push record not found","Leküldési rekord nem található"}. +{"Queries to the conference members are not allowed in this room","A konferenciatagok lekérdezései nem engedélyezettek ebben a szobában"}. +{"Query to another users is forbidden","Egy másik felhasználó lekérdezése tiltva van"}. +{"RAM and disc copy","RAM és lemezmásolás"}. +{"RAM copy","RAM másolás"}. +{"Really delete message of the day?","Valóban törli a napi üzenetet?"}. +{"Recipient is not in the conference room","A címzett nincs a konferenciaszobában"}. +{"Register","Regisztráció"}. +{"Remote copy","Távoli másolás"}. +{"Remove User","Felhasználó eltávolítása"}. +{"Replaced by new connection","Kicserélve egy új kapcsolattal"}. +{"Request has timed out","A kérés túllépte az időkorlátot"}. +{"Request is ignored","A kérés mellőzve lett"}. +{"Resources","Erőforrások"}. +{"Restart Service","Szolgáltatás újraindítása"}. +{"Restore Backup from File at ","Biztonsági mentés visszaállítása fájlból itt: "}. +{"Restore binary backup after next ejabberd restart (requires less memory):","Bináris biztonsági mentés visszaállítása az ejabberd következő újraindítása után (kevesebb memóriát igényel):"}. +{"Restore binary backup immediately:","Bináris biztonsági mentés visszaállítása azonnal:"}. +{"Restore plain text backup immediately:","Egyszerű szöveges biztonsági mentés visszaállítása azonnal:"}. +{"Restore","Visszaállítás"}. +{"Room Configuration","Szoba beállítása"}. +{"Room creation is denied by service policy","Szobalétrehozás megtagadva a szolgáltatási irányelv miatt"}. +{"Room description","Szoba leírása"}. +{"Room Occupants","Szoba résztvevői"}. +{"Room terminates","Szoba megszűnik"}. +{"Room title","Szoba címe"}. +{"Roster size","Névsor mérete"}. +{"Running Nodes","Futó csomópontok"}. +{"Saturday","szombat"}. +{"Search Results for ","Keresési eredménye ennek: "}. +{"Search users in ","Felhasználók keresése ebben: "}. +{"Send announcement to all online users on all hosts","Közlemény küldése az összes elérhető felhasználónak az összes gépen"}. +{"Send announcement to all online users","Közlemény küldése az összes elérhető felhasználónak"}. +{"Send announcement to all users on all hosts","Közlemény küldése az összes felhasználónak az összes gépen"}. +{"Send announcement to all users","Közlemény küldése az összes felhasználónak"}. +{"September","szeptember"}. +{"Server:","Kiszolgáló:"}. +{"Session state copying timed out","A munkamenet állapotának másolása túllépte az időkorlátot"}. +{"Set message of the day and send to online users","Napi üzenet beállítása és küldés az elérhető felhasználóknak"}. +{"Set message of the day on all hosts and send to online users","Napi üzenet beállítása az összes gépen és küldés az elérhető felhasználóknak"}. +{"Shared Roster Groups","Megosztott névsorcsoportok"}. +{"Show Integral Table","Integráltáblázat megjelenítése"}. +{"Show Ordinary Table","Szokásos táblázat megjelenítése"}. +{"Shut Down Service","Szolgáltatás leállítása"}. +{"SOCKS5 Bytestreams","SOCKS5 bájtfolyamok"}. +{"Stopped Nodes","Leállított csomópontok"}. +{"Store binary backup:","Bináris biztonsági mentés tárolása:"}. +{"Store plain text backup:","Egyszerű szöveges biztonsági mentés tárolása:"}. +{"Stream management is already enabled","A folyamkezelés már engedélyezve van"}. +{"Stream management is not enabled","A folyamkezelés nincs engedélyezve"}. +{"Subject","Tárgy"}. +{"Submitted","Elküldve"}. +{"Subscriptions are not allowed","Feliratkozások nem engedélyezettek"}. +{"Sunday","vasárnap"}. +{"That nickname is already in use by another occupant","Ezt a becenevet már használja egy másik résztvevő"}. +{"That nickname is registered by another person","Ezt a becenevet egy másik személy regisztrálta"}. +{"The account already exists","A fiók már létezik"}. +{"The CAPTCHA is valid.","A CAPTCHA érvényes."}. +{"The CAPTCHA verification has failed","A CAPTCHA ellenőrzése nem sikerült"}. +{"The captcha you entered is wrong","A beírt CAPTCHA hibás"}. +{"The feature requested is not supported by the conference","A kért funkciót nem támogatja a konferencia"}. +{"The password contains unacceptable characters","A jelszó elfogadhatatlan karaktereket tartalmaz"}. +{"The password is too weak","A jelszó túl gyenge"}. +{"the password is","a jelszó"}. +{"The password was not changed","A jelszó nem lett megváltoztatva"}. +{"The passwords are different","A jelszavak különböznek"}. +{"The query is only allowed from local users","A lekérdezés csak helyi felhasználóktól engedélyezett"}. +{"The query must not contain elements","A lekérdezés nem tartalmazhat elemeket"}. +{"The stanza MUST contain only one element, one element, or one element","A stanzának csak egyetlen elemet, egyetlen elemet vagy egyetlen elemet KELL tartalmaznia"}. +{"There was an error creating the account: ","Hiba történt a fiók létrehozásakor: "}. +{"There was an error deleting the account: ","Hiba történt a fiók törlésekor: "}. +{"This room is not anonymous","Ez a szoba nem névtelen"}. +{"This service can not process the address: ~s","Ez a szolgáltatás nem tudja feldolgozni a címet: ~s"}. +{"Thursday","csütörtök"}. +{"Time delay","Időkésleltetés"}. +{"Timed out waiting for stream resumption","Időtúllépés a folyam újrakezdésére várakozásnál"}. +{"To register, visit ~s","Regisztráláshoz látogassa meg ezt az oldalt: ~s"}. +{"To ~ts","Címzett: ~ts"}. +{"Token TTL","Token élettartama"}. +{"Too many active bytestreams","Túl sok aktív bájtfolyam"}. +{"Too many CAPTCHA requests","Túl sok CAPTCHA kérés"}. +{"Too many child elements","Túl sok gyermekelem"}. +{"Too many elements","Túl sok elem"}. +{"Too many elements","Túl sok elem"}. +{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Túl sok (~p) sikertelen hitelesítés erről az IP-címről (~s) A cím ~s-kor lesz feloldva UTC szerint"}. +{"Too many receiver fields were specified","Túl sok fogadómező lett meghatározva"}. +{"Too many unacked stanzas","Túl sok nyugtázatlan stanza"}. +{"Too many users in this conference","Túl sok felhasználó ebben a konferenciában"}. +{"Traffic rate limit is exceeded","Forgalom sebességkorlátja elérve"}. +{"~ts's Offline Messages Queue","~ts kapcsolat nélküli üzeneteinek tárolója"}. +{"Tuesday","kedd"}. +{"Unable to generate a CAPTCHA","Nem lehet előállítani CAPTCHA-t"}. +{"Unable to register route on existing local domain","Nem lehet útvonalat regisztrálni egy meglévő helyi tartományon"}. +{"Unauthorized","Nem engedélyezett"}. +{"Unexpected action","Váratlan művelet"}. +{"Unexpected error condition: ~p","Váratlan hibafeltétel: ~p"}. +{"Unregister","Regisztráció törlése"}. +{"Unsupported element","Nem támogatott elem"}. +{"Unsupported version","Nem támogatott verzió"}. +{"Update message of the day (don't send)","Napi üzenet frissítése (ne küldje el)"}. +{"Update message of the day on all hosts (don't send)","Napi üzenet frissítése az összes gépen (ne küldje el)"}. +{"User already exists","A felhasználó már létezik"}. +{"User (jid)","Felhasználó (JID)"}. +{"User Management","Felhasználó-kezelés"}. +{"User removed","Felhasználó eltávolítva"}. +{"User session not found","Felhasználói munkamenet nem található"}. +{"User session terminated","Felhasználói munkamenet befejeződött"}. +{"User ~ts","~ts felhasználó"}. +{"User","Felhasználó"}. +{"Username:","Felhasználónév:"}. +{"Users are not allowed to register accounts so quickly","A felhasználóknak nem engedélyezett fiókokat regisztrálni ilyen gyorsan"}. +{"Users Last Activity","Felhasználók utolsó tevékenysége"}. +{"Users","Felhasználók"}. +{"Value 'get' of 'type' attribute is not allowed","A „type” attribútum „get” értéke nem engedélyezett"}. +{"Value of '~s' should be boolean","A(z) „~s” értéke csak logikai lehet"}. +{"Value of '~s' should be datetime string","A(z) „~s” értéke csak dátum és idő karakterlánc lehet"}. +{"Value of '~s' should be integer","A(z) „~s” értéke csak egész szám lehet"}. +{"Value 'set' of 'type' attribute is not allowed","A „type” attribútum „set” értéke nem engedélyezett"}. +{"vCard User Search","vCard felhasználó-keresés"}. +{"Virtual Hosts","Virtuális gépek"}. +{"Visitors are not allowed to change their nicknames in this room","A látogatóknak nem engedélyezett megváltoztatni a beceneveiket ebben a szobában"}. +{"Visitors are not allowed to send messages to all occupants","A látogatóknak nem engedélyezett üzeneteket küldeni az összes résztvevőnek"}. +{"Voice request","Hangkérelem"}. +{"Voice requests are disabled in this conference","A hangkérelmek le vannak tiltva ebben a konferenciában"}. +{"Wednesday","szerda"}. +{"Wrong parameters in the web formulary","Hibás paraméterek a webes modelldokumentumban"}. +{"Wrong xmlns","Hibás xmlns"}. +{"You are being removed from the room because of a system shutdown","El lett távolítva a szobából egy rendszerleállítás miatt"}. +{"You are not joined to the channel","Nincs csatlakozva a csatornához"}. +{"You have been banned from this room","Ki lett tiltva ebből a szobából"}. +{"You have joined too many conferences","Túl sok konferenciához csatlakozott"}. +{"You must fill in field \"Nickname\" in the form","Ki kell töltenie a „becenév” mezőt az űrlapon"}. +{"You need a client that supports x:data and CAPTCHA to register","Olyan programra van szüksége, amelynek x:data és CAPTCHA támogatása van a regisztráláshoz"}. +{"You need a client that supports x:data to register the nickname","Olyan programra van szüksége, amelynek x:data támogatása van a becenév regisztráláshoz"}. +{"You need an x:data capable client to search","Egy x:data támogatású programra van szüksége a kereséshez"}. +{"Your active privacy list has denied the routing of this stanza.","Az aktív adatvédelmi listája megtagadta ennek a stanzának az útválasztását."}. +{"Your contact offline message queue is full. The message has been discarded.","A partnere kapcsolat nélküli üzenettárolója megtelt. Az üzenet el lett dobva."}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","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: ~s"}. +{"You're not allowed to create nodes","Önnek nincs engedélye csomópontokat létrehozni"}. diff --git a/priv/msgs/id.msg b/priv/msgs/id.msg index c0ffb8de4..ac6db281c 100644 --- a/priv/msgs/id.msg +++ b/priv/msgs/id.msg @@ -1,41 +1,74 @@ -%% -*- coding: latin-1 -*- -{"Access Configuration","Akses Konfigurasi"}. -{"Access Control List Configuration","Konfigurasi Daftar Akses Pengendalian"}. -{"Access Control Lists","Akses Daftar Pengendalian"}. -{"Access control lists","Daftar Pengendalian Akses"}. -{"Access denied by service policy","Akses ditolak oleh kebijakan layanan"}. -{"Access rules","Akses peraturan"}. -{"Access Rules","Aturan Akses"}. -{"Action on user","Tindakan pada pengguna"}. -{"Add Jabber ID","Tambah Jabber ID"}. -{"Add New","Tambah Baru"}. -{"Add User","Tambah Pengguna"}. -{"Administration","Administrasi"}. -{"Administration of ","Administrasi"}. -{"Administrator privileges required","Hak istimewa Administrator dibutuhkan"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" (Add * to the end of field to match substring)"," Isi formulir untuk pencarian pengguna Jabber yang cocok (Tambahkan * ke mengakhiri pengisian untuk menyamakan kata)"}. +{" has set the subject to: "," telah menetapkan topik yaitu: "}. +{"# participants","# pengguna"}. +{"A description of the node","Deskripsi node"}. {"A friendly name for the node","Nama yang dikenal untuk node"}. +{"A password is required to enter this room","Diperlukan kata sandi untuk masuk ruangan ini"}. +{"A Web Page","Halaman web"}. +{"Accept","Diterima"}. +{"Access denied by service policy","Akses ditolak oleh kebijakan layanan"}. +{"Access model","Model akses"}. +{"Account doesn't exist","Akun tidak ada"}. +{"Action on user","Tindakan pada pengguna"}. +{"Add User","Tambah Pengguna"}. +{"Administration of ","Administrasi "}. +{"Administration","Administrasi"}. +{"Administrator privileges required","Hak istimewa Administrator dibutuhkan"}. {"All activity","Semua aktifitas"}. +{"All Users","Semua Pengguna"}. +{"Allow subscription","Ijinkan berlangganan"}. {"Allow this Jabber ID to subscribe to this pubsub node?","Izinkan ID Jabber ini untuk berlangganan pada node pubsub ini?"}. +{"Allow this person to register with the room?","Ijinkan orang ini mendaftar masuk kamar?"}. {"Allow users to change the subject","Perbolehkan pengguna untuk mengganti topik"}. {"Allow users to query other users","Perbolehkan pengguna untuk mengetahui pengguna lain"}. {"Allow users to send invites","Perbolehkan pengguna mengirimkan undangan"}. {"Allow users to send private messages","perbolehkan pengguna mengirimkan pesan ke pengguna lain secara pribadi"}. {"Allow visitors to change nickname","Perbolehkan visitor mengganti nama julukan"}. +{"Allow visitors to send private messages to","Izinkan pengunjung mengirimkan pesan privat ke"}. {"Allow visitors to send status text in presence updates","Izinkan pengunjung untuk mengirim teks status terbaru"}. -{"All Users","Semua Pengguna"}. +{"Allow visitors to send voice requests","Izinkan pengunjung mengirim permintaan suara"}. {"Announcements","Pengumuman"}. -{"anyone","Siapapun"}. -{"A password is required to enter this room","Diperlukan kata sandi untuk masuk ruangan ini"}. +{"Answer associated with a picture","Jawaban yang berhubungan dengan gambar"}. +{"Answer associated with a video","Jawaban yang berhubungan dengan video"}. +{"Answer associated with speech","Jawaban yang berhubungan dengan ucapan"}. +{"Answer to a question","Jawaban pertanyaan"}. +{"Anyone in the specified roster group(s) may subscribe and retrieve items","Siapapun dalam keanggotaan grup tertentu dapat berlangganan dan mengambil item"}. +{"Anyone may publish","Siapapun dapat mempublikasi"}. +{"Anyone may subscribe and retrieve items","Siapapun dapat berlangganan dan mengambil item"}. +{"Anyone with Voice","Siapapun dengan fungsi suara"}. +{"Anyone","Siapapun"}. {"April","April"}. +{"Attribute 'channel' is required for this request","Atribut 'channel' diperlukan untuk permintaan ini"}. +{"Attribute 'id' is mandatory for MIX messages","Atribut 'id' harus ada untuk pesan MIX"}. +{"Attribute 'jid' is not allowed here","Atribut 'jid' tidak diijinkan disini"}. +{"Attribute 'node' is not allowed here","Atribut 'node' tidak diijinkan disini"}. +{"Attribute 'to' of stanza that triggered challenge","Atribut 'to' dari stanza yang memicu respon"}. {"August","Agustus"}. -{"Backup","Backup"}. +{"Automatic node creation is not enabled","Pembuatan node otomatis tidak diijinkan"}. {"Backup Management","Manajemen Backup"}. -{"Backup to File at ","Backup ke File pada"}. +{"Backup of ~p","Cadangan dari ~p"}. +{"Backup to File at ","Backup ke File di lokasi "}. +{"Backup","Backup"}. {"Bad format","Format yang buruk"}. {"Birthday","Hari Lahir"}. +{"Both the username and the resource are required","Baik nama pengguna dan sumber daya diperlukan"}. +{"Bytestream already activated","Bytestream telah aktif"}. +{"Cannot remove active list","Tidak bisa menghapus daftar aktif"}. +{"Cannot remove default list","Tidak bisa menghapus daftar standar"}. {"CAPTCHA web page","CAPTCHA laman web"}. +{"Challenge ID","ID tantangan"}. {"Change Password","Ubah Kata Sandi"}. {"Change User Password","Ubah User Password"}. +{"Changing password is not allowed","Tidak diijinkan mengubah kata sandi"}. +{"Changing role/affiliation is not allowed","Tidak diijinkan mengubah peran/afiliasi"}. +{"Channel already exists","Channel sudah ada"}. +{"Channel does not exist","Channel tidak ada"}. +{"Channels","Channel"}. {"Characters not allowed:","Karakter tidak diperbolehkan:"}. {"Chatroom configuration modified","Konfigurasi ruang chat diubah"}. {"Chatroom is created","Ruang chat telah dibuat"}. @@ -44,116 +77,115 @@ {"Chatroom is stopped","Ruang chat dihentikan"}. {"Chatrooms","Ruangan Chat"}. {"Choose a username and password to register with this server","Pilih nama pengguna dan kata sandi untuk mendaftar dengan layanan ini"}. -{"Choose modules to stop","Pilih Modul untuk berhenti"}. {"Choose storage type of tables","Pilih jenis penyimpanan tabel"}. {"Choose whether to approve this entity's subscription.","Pilih apakah akan menyetujui hubungan pertemanan ini."}. {"City","Kota"}. +{"Client acknowledged more stanzas than sent by server","Klien menerima lebih banyak stanza daripada yang dikirim oleh server"}. {"Commands","Perintah"}. {"Conference room does not exist","Ruang Konferensi tidak ada"}. {"Configuration of room ~s","Pengaturan ruangan ~s"}. {"Configuration","Pengaturan"}. -{"Connected Resources:","Sumber Daya Terhubung:"}. -{"Connections parameters","Parameter Koneksi"}. +{"Contact Addresses (normally, room owner or owners)","Alamat Kontak (biasanya, pemilik atau pemilik kamar)"}. {"Country","Negara"}. -{"CPU Time:","Waktu CPU:"}. +{"Current Discussion Topic","Topik diskusi saat ini"}. +{"Database failure","Kegagalan database"}. +{"Database Tables Configuration at ","Konfigurasi Tabel Database pada "}. {"Database","Database"}. -{"Database Tables Configuration at ","Database Tabel Konfigurasi pada"}. {"December","Desember"}. {"Default users as participants","pengguna pertama kali masuk sebagai participant"}. -{"Delete message of the day","Hapus pesan harian"}. {"Delete message of the day on all hosts","Hapus pesan harian pada semua host"}. -{"Delete Selected","Hapus Yang Terpilih"}. +{"Delete message of the day","Hapus pesan harian"}. {"Delete User","Hapus Pengguna"}. {"Deliver event notifications","Memberikan pemberitahuan acara"}. {"Deliver payloads with event notifications","Memberikan muatan dengan pemberitahuan acara"}. -{"Description:","Keterangan:"}. {"Disc only copy","Hanya salinan dari disc"}. -{"Displayed Groups:","Tampilkan Grup:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","Jangan memberitahukan kata sandi Anda ke siapapun, bahkan para administrator dari layanan Jabber."}. -{"Dump Backup to Text File at ","Dump Backup ke File Teks di"}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","Jangan beritahukan kata sandi Anda ke siapapun, bahkan ke administrator layanan XMPP."}. +{"Dump Backup to Text File at ","Dump Backup menjadi File Teks di "}. {"Dump to Text File","Dump menjadi File Teks"}. +{"Duplicated groups are not allowed by RFC6121","Grup duplikat tidak diperbolehkan oleh RFC6121"}. {"Edit Properties","Ganti Properti"}. -{"ejabberd IRC module","ejabberd IRC modul"}. -{"ejabberd MUC module","ejabberd MUC Module"}. +{"Either approve or decline the voice request.","Antara terima atau tolak permintaan suara."}. +{"ejabberd HTTP Upload service","Layanan HTTP Upload ejabberd"}. +{"ejabberd MUC module","Modul MUC ejabberd"}. +{"ejabberd Multicast service","Layanan Multicast ejabberd"}. {"ejabberd Publish-Subscribe module","Modul ejabberd Setujui-Pertemanan"}. {"ejabberd SOCKS5 Bytestreams module","modul ejabberd SOCKS5 Bytestreams"}. {"ejabberd vCard module","Modul ejabberd vCard"}. {"ejabberd Web Admin","Admin Web ejabberd"}. -{"Elements","Elemen-elemen"}. +{"ejabberd","ejabberd"}. +{"Email Address","Alamat email"}. {"Email","Email"}. -{"Enable logging","Aktifkan catatan"}. -{"Encoding for server ~b","Pengkodean untuk layanan ~b"}. +{"Enable logging","Aktifkan log"}. +{"Enable message archiving","Aktifkan pengarsipan pesan"}. +{"Enabling push without 'node' attribute is not supported","Aktivasi push tanpa atribut 'node' tidak didukung"}. {"End User Session","Akhir Sesi Pengguna"}. -{"Enter list of {Module, [Options]}","Masukkan daftar {Modul, [Options]}"}. {"Enter nickname you want to register","Masukkan nama julukan Anda jika ingin mendaftar"}. {"Enter path to backup file","Masukkan path untuk file cadangan"}. {"Enter path to jabberd14 spool dir","Masukkan path ke direktori spool jabberd14"}. {"Enter path to jabberd14 spool file","Masukkan path ke file jabberd14 spool"}. {"Enter path to text file","Masukkan path ke file teks"}. {"Enter the text you see","Masukkan teks yang Anda lihat"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Masukkan username dan pengkodean yang ingin Anda gunakan untuk menghubungkan ke layanan IRC. Tekan 'Selanjutnya' untuk mendapatkan lagi formulir kemudian Tekan 'Lengkap' untuk menyimpan pengaturan."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Masukkan username, pengkodean, port dan sandi yang ingin Anda gunakan untuk menghubungkan ke layanan IRC"}. -{"Erlang Jabber Server","Layanan Erlang Jabber"}. -{"Error","Kesalahan"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Contoh: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. +{"Erlang XMPP Server","Server Erlang XMPP"}. +{"Exclude Jabber IDs from CAPTCHA challenge","Kecualikan Jabber IDs dari tantangan CAPTCHA"}. +{"Export all tables as SQL queries to a file:","Ekspor semua tabel sebagai kueri SQL ke file:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Ekspor data dari semua pengguna pada layanan ke berkas PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Ekspor data pengguna pada sebuah host ke berkas PIEFXIS (XEP-0227):"}. +{"External component failure","Kegagalan komponen eksternal"}. +{"External component timeout","Komponen eksternal kehabisan waktu"}. +{"Failed to activate bytestream","Gagal mengaktifkan bytestream"}. +{"Failed to extract JID from your voice request approval","Gagal mendapatkan JID dari permintaan akses suara"}. +{"Failed to parse HTTP response","Gagal mengurai respon HTTP"}. +{"Failed to process option '~s'","Gagal memproses dengan opsi '~s'"}. {"Family Name","Nama Keluarga (marga)"}. +{"FAQ Entry","Entri FAQ"}. {"February","Februari"}. -{"Fill in fields to search for any matching Jabber User","Isi kolom untuk mencari pengguna Jabber yang sama"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Isi formulir untuk pencarian pengguna Jabber yang cocok (Tambahkan * ke mengakhiri pengisian untuk menyamakan kata)"}. +{"File larger than ~w bytes","File lebih besar dari ~w bytes"}. +{"Fill in the form to search for any matching XMPP User","Isi kolom untuk mencari pengguna XMPP"}. {"Friday","Jumat"}. -{"From","Dari"}. -{"From ~s","Dari ~s"}. +{"From ~ts","Dari ~ts"}. +{"Full List of Room Admins","Daftar Lengkap Admin Kamar"}. +{"Full List of Room Owners","Daftar Lengkap Pemilik Kamar"}. {"Full Name","Nama Lengkap"}. {"Get Number of Online Users","Dapatkan Jumlah User Yang Online"}. {"Get Number of Registered Users","Dapatkan Jumlah Pengguna Yang Terdaftar"}. -{"Get User Last Login Time","Dapatkan Waktu Login Terakhir Pengguna "}. -{"Get User Password","Dapatkan User Password"}. +{"Get Pending","Lihat yang tertunda"}. +{"Get User Last Login Time","Lihat Waktu Login Terakhir Pengguna"}. {"Get User Statistics","Dapatkan Statistik Pengguna"}. -{"Group ","Grup"}. -{"Groups","Grup"}. +{"Given Name","Nama"}. +{"Grant voice to this person?","Ijinkan akses suara kepadanya?"}. {"has been banned","telah dibanned"}. -{"has been kicked because of an affiliation change","telah dikick karena perubahan afiliasi"}. {"has been kicked because of a system shutdown","telah dikick karena sistem shutdown"}. +{"has been kicked because of an affiliation change","telah dikick karena perubahan afiliasi"}. {"has been kicked because the room has been changed to members-only","telah dikick karena ruangan telah diubah menjadi hanya untuk member"}. {"has been kicked","telah dikick"}. -{" has set the subject to: ","telah menetapkan topik yaitu:"}. -{"Host","Host"}. +{"Host unknown","Host tidak dikenal"}. +{"HTTP File Upload","Unggah Berkas HTTP"}. +{"Idle connection","Koneksi menganggur"}. {"If you don't see the CAPTCHA image here, visit the web page.","Jika Anda tidak melihat gambar CAPTCHA disini, silahkan kunjungi halaman web."}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Jika Anda ingin menentukan port yang berbeda, sandi, pengkodean untuk layanan IRC, isi daftar ini dengan nilai-nilai dalam format '{\"server irc \", \"encoding \", port, \"sandi \"}'. Secara default ini menggunakan layanan \"~s \" pengkodean, port ~p, kata sandi kosong."}. {"Import Directory","Impor Direktori"}. {"Import File","Impor File"}. {"Import user data from jabberd14 spool file:","Impor data pengguna dari sekumpulan berkas jabberd14:"}. -{"Import User from File at ","Impor Pengguna dari File pada"}. +{"Import User from File at ","Impor Pengguna dari File pada "}. {"Import users data from a PIEFXIS file (XEP-0227):","impor data-data pengguna dari sebuah PIEFXIS (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Импорт пользовательских данных из буферной директории jabberd14:"}. -{"Import Users from Dir at ","Impor Pengguna dari Dir di"}. +{"Import Users from Dir at ","Impor Pengguna dari Dir di "}. {"Import Users From jabberd14 Spool Files","Impor Pengguna Dari jabberd14 Spool File"}. {"Improper message type","Jenis pesan yang tidak benar"}. +{"Incorrect CAPTCHA submit","Isian CAPTCHA salah"}. +{"Incorrect data form","Formulir data salah"}. {"Incorrect password","Kata sandi salah"}. -{"Invalid affiliation: ~s","Afiliasi tidak valid: ~s"}. -{"Invalid role: ~s","Peran tidak valid: ~s"}. +{"Incorrect value of 'action' attribute","Nilai atribut 'aksi' salah"}. +{"Incorrect value of 'action' in data form","Nilai 'tindakan' yang salah dalam formulir data"}. +{"Insufficient privilege","Hak tidak mencukupi"}. +{"Internal server error","Galat server internal"}. +{"Invalid node name","Nama node tidak valid"}. {"IP addresses","Alamat IP"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","Channel IRC (tidak perlu menempatkan # sebelumnya)"}. -{"IRC server","Layanan IRC"}. -{"IRC settings","Pengaturan IRC"}. -{"IRC Transport","IRC Transport"}. -{"IRC username","Nama Pengguna IRC"}. -{"IRC Username","Nama Pengguna IRC"}. {"is now known as","sekarang dikenal sebagai"}. -{"It is not allowed to send private messages","Hal ini tidak diperbolehkan untuk mengirim pesan pribadi"}. {"It is not allowed to send private messages of type \"groupchat\"","Hal ini tidak diperbolehkan untuk mengirim pesan pribadi jenis \"groupchat \""}. {"It is not allowed to send private messages to the conference","Hal ini tidak diperbolehkan untuk mengirim pesan pribadi ke konferensi"}. -{"Jabber Account Registration","Pendaftaran Akun Jabber"}. {"Jabber ID","Jabber ID"}. -{"Jabber ID ~s is invalid","Jabber ID ~s tidak valid"}. {"January","Januari"}. -{"Join IRC channel","Gabung channel IRC"}. {"joins the room","bergabung ke ruangan"}. -{"Join the IRC channel here.","Gabung ke channel IRC disini"}. -{"Join the IRC channel in this Jabber ID: ~s","Gabung ke channel IRC dengan Jabber ID: ~s"}. {"July","Juli"}. {"June","Juni"}. {"Last Activity","Aktifitas Terakhir"}. @@ -161,10 +193,6 @@ {"Last month","Akhir bulan"}. {"Last year","Akhir tahun"}. {"leaves the room","meninggalkan ruangan"}. -{"Listened Ports at ","Mendeteksi Port-port di"}. -{"Listened Ports","Port Terdeteksi"}. -{"List of modules to start","Daftar modul untuk memulai"}. -{"Low level update script","Perbaruan naskah tingkat rendah"}. {"Make participants list public","Buat daftar participant diketahui oleh public"}. {"Make room CAPTCHA protected","Buat ruangan dilindungi dengan CAPTCHA"}. {"Make room members-only","Buat ruangan hanya untuk member saja"}. @@ -173,37 +201,29 @@ {"Make room persistent","Buat ruangan menjadi permanent"}. {"Make room public searchable","Buat ruangan dapat dicari"}. {"March","Maret"}. -{"Maximum Number of Occupants","Maksimum Jumlah Penghuni"}. -{"Max # of items to persist","Max item untuk bertahan"}. {"Max payload size in bytes","Max kapasitas ukuran dalam bytes"}. +{"Maximum Number of Occupants","Maksimum Jumlah Penghuni"}. {"May","Mei"}. -{"Members:","Anggota:"}. {"Membership is required to enter this room","Hanya Member yang dapat masuk ruangan ini"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Hafalkan kata sandi Anda, atau dicatat dan letakkan di tempat yang aman. Didalam Jabber tidak ada cara otomatis untuk mendapatkan kembali password Anda jika Anda lupa."}. -{"Memory","Memori"}. {"Message body","Isi Pesan"}. {"Middle Name","Nama Tengah"}. {"Moderator privileges required","Hak istimewa moderator dibutuhkan"}. -{"moderators only","Hanya moderator"}. -{"Modified modules","Modifikasi modul-modul"}. -{"Module","Modul"}. -{"Modules","Modul"}. {"Monday","Senin"}. -{"Name:","Nama:"}. +{"Multiple elements are not allowed by RFC6121","Beberapa elemen tidak diizinkan oleh RFC6121"}. {"Name","Nama"}. {"Never","Tidak Pernah"}. {"New Password:","Password Baru:"}. -{"Nickname","Nama Julukan"}. -{"Nickname Registration at ","Pendaftaran Julukan pada"}. +{"Nickname Registration at ","Pendaftaran Julukan pada "}. {"Nickname ~s does not exist in the room","Nama Julukan ~s tidak berada di dalam ruangan"}. +{"Nickname","Nama Julukan"}. {"No body provided for announce message","Tidak ada isi pesan yang disediakan untuk mengirimkan pesan"}. {"No Data","Tidak Ada Data"}. +{"No element found","Tidak ada elemen yang ditemukan"}. +{"No limit","Tidak terbatas"}. {"Node ID","ID Node"}. {"Node not found","Node tidak ditemukan"}. {"Nodes","Node-node"}. -{"No limit","Tidak terbatas"}. {"None","Tak satupun"}. -{"No resource provided","Tidak ada sumber daya yang disediakan"}. {"Not Found","Tidak Ditemukan"}. {"Notify subscribers when items are removed from the node","Beritahu pelanggan ketika item tersebut dikeluarkan dari node"}. {"Notify subscribers when the node configuration changes","Beritahu pelanggan ketika ada perubahan konfigurasi node"}. @@ -213,93 +233,93 @@ {"Number of online users","Jumlah pengguna online"}. {"Number of registered users","Jumlah pengguna terdaftar"}. {"October","Oktober"}. -{"Offline Messages:","Pesan Offline:"}. -{"Offline Messages","Pesan Offline"}. {"OK","YA"}. {"Old Password:","Password Lama:"}. -{"Online","Online"}. -{"Online Users:","Pengguna Online:"}. {"Online Users","Pengguna Yang Online"}. +{"Online","Online"}. {"Only deliver notifications to available users","Hanya mengirimkan pemberitahuan kepada pengguna yang tersedia"}. {"Only moderators and participants are allowed to change the subject in this room","Hanya moderator dan peserta yang diizinkan untuk mengganti topik pembicaraan di ruangan ini"}. {"Only moderators are allowed to change the subject in this room","Hanya moderator yang diperbolehkan untuk mengubah topik dalam ruangan ini"}. {"Only occupants are allowed to send messages to the conference","Hanya penghuni yang diizinkan untuk mengirim pesan ke konferensi"}. {"Only occupants are allowed to send queries to the conference","Hanya penghuni diizinkan untuk mengirim permintaan ke konferensi"}. {"Only service administrators are allowed to send service messages","Layanan hanya diperuntukan kepada administrator yang diizinkan untuk mengirim layanan pesan"}. -{"Options","Pilihan-pilihan"}. {"Organization Name","Nama Organisasi"}. {"Organization Unit","Unit Organisasi"}. {"Outgoing s2s Connections","Koneksi Keluar s2s"}. -{"Outgoing s2s Connections:","Koneksi s2s yang keluar:"}. {"Owner privileges required","Hak istimewa owner dibutuhkan"}. -{"Packet","Paket"}. -{"Password ~b","Kata Sandi ~b"}. -{"Password:","Kata Sandi:"}. -{"Password","Sandi"}. +{"Participant","Partisipan"}. {"Password Verification:","Verifikasi Kata Sandi:"}. {"Password Verification","Verifikasi Sandi"}. +{"Password:","Kata Sandi:"}. +{"Password","Sandi"}. {"Path to Dir","Jalur ke Dir"}. {"Path to File","Jalur ke File"}. -{"Pending","Tertunda"}. -{"Period: ","Periode:"}. +{"Period: ","Periode: "}. {"Persist items to storage","Pertahankan item ke penyimpanan"}. +{"Persistent","Persisten"}. +{"Ping query is incorrect","Kueri ping salah"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Harap dicatat bahwa pilihan ini hanya akan membuat cadangan builtin Mnesia database. Jika Anda menggunakan modul ODBC, anda juga perlu untuk membuat cadangan database SQL Anda secara terpisah."}. {"Pong","Pong"}. -{"Port ~b","Port ~b"}. -{"Port","Port"}. {"Present real Jabber IDs to","Tampilkan Jabber ID secara lengkap"}. +{"Previous session not found","Sesi sebelumnya tidak ditemukan"}. +{"Previous session PID has been killed","Sesi PID sebelumnya telah dimatikan"}. +{"Previous session PID has exited","Sesi PID sebelumnya telah keluar"}. +{"Previous session PID is dead","Sesi PID sebelumnya mati"}. +{"Previous session timed out","Sesi sebelumnya habis waktu"}. {"private, ","pribadi, "}. -{"Protocol","Protocol"}. +{"Public","Publik"}. +{"Publish model","Model penerbitan"}. {"Publish-Subscribe","Setujui-Pertemanan"}. {"PubSub subscriber request","Permintaan pertemanan PubSub"}. {"Purge all items when the relevant publisher goes offline","Bersihkan semua item ketika penerbit yang relevan telah offline"}. {"Queries to the conference members are not allowed in this room","Permintaan untuk para anggota konferensi tidak diperbolehkan di ruangan ini"}. +{"Query to another users is forbidden","Kueri ke pengguna lain dilarang"}. {"RAM and disc copy","RAM dan disc salinan"}. {"RAM copy","Salinan RAM"}. -{"Raw","mentah"}. {"Really delete message of the day?","Benar-benar ingin menghapus pesan harian?"}. +{"Receive notification from all descendent nodes","Terima notifikasi dari semua node turunan"}. +{"Receive notification from direct child nodes only","Terima notifikasi dari child node saja"}. +{"Receive notification of new items only","Terima notifikasi dari item baru saja"}. +{"Receive notification of new nodes only","Terima notifikasi dari node baru saja"}. {"Recipient is not in the conference room","Penerima tidak berada di ruangan konferensi"}. -{"Register a Jabber account","Daftarkan sebuah akun jabber"}. -{"Registered Users:","Pengguna Terdaftar:"}. -{"Registered Users","Pengguna Terdaftar"}. +{"Register an XMPP account","Daftarkan sebuah akun XMPP"}. {"Register","Mendaftar"}. -{"Registration in mod_irc for ","Pendaftaran di mod_irc untuk"}. {"Remote copy","Salinan Remote"}. -{"Remove All Offline Messages","Hapus Semua Pesan Offline"}. -{"Remove","Menghapus"}. {"Remove User","Hapus Pengguna"}. {"Replaced by new connection","Diganti dengan koneksi baru"}. +{"Request has timed out","Waktu permintaan telah habis"}. +{"Request is ignored","Permintaan diabaikan"}. +{"Requested role","Peran yang diminta"}. {"Resources","Sumber daya"}. -{"Restart","Jalankan Ulang"}. {"Restart Service","Restart Layanan"}. -{"Restore Backup from File at ","Kembalikan Backup dari File pada"}. +{"Restore Backup from File at ","Kembalikan Backup dari File pada "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Mengembalikan cadangan yang berpasanagn setelah ejabberd berikutnya dijalankan ulang (memerlukan memori lebih sedikit):"}. {"Restore binary backup immediately:","Segera mengembalikan cadangan yang berpasangan:"}. -{"Restore","Mengembalikan"}. {"Restore plain text backup immediately:","Segera mengembalikan cadangan teks biasa:"}. +{"Restore","Mengembalikan"}. +{"Roles that May Send Private Messages","Peran yang Dapat Mengirim Pesan Pribadi"}. {"Room Configuration","Konfigurasi Ruangan"}. {"Room creation is denied by service policy","Pembuatan Ruangan ditolak oleh kebijakan layanan"}. {"Room description","Keterangan ruangan"}. {"Room Occupants","Penghuni Ruangan"}. +{"Room terminates","Ruang dihentikan"}. {"Room title","Nama Ruangan"}. {"Roster groups allowed to subscribe","Kelompok kontak yang diizinkan untuk berlangganan"}. -{"Roster","Kontak"}. -{"Roster of ","Kontak dari"}. {"Roster size","Ukuran Daftar Kontak"}. -{"RPC Call Error","Panggilan Kesalahan RPC"}. {"Running Nodes","Menjalankan Node"}. -{"~s access rule configuration","~s aturan akses konfigurasi"}. +{"~s invites you to the room ~s","~s mengundang anda masuk kamar ~s"}. {"Saturday","Sabtu"}. -{"Script check","Periksa naskah"}. -{"Search Results for ","Hasil Pencarian untuk"}. -{"Search users in ","Pencarian pengguna dalam"}. -{"Send announcement to all online users","Kirim pengumuman untuk semua pengguna yang online"}. +{"Search from the date","Cari dari tanggal"}. +{"Search Results for ","Hasil Pencarian untuk "}. +{"Search the text","Cari teks"}. +{"Search until the date","Cari sampai tanggal"}. +{"Search users in ","Pencarian pengguna dalam "}. {"Send announcement to all online users on all hosts","Kirim pengumuman untuk semua pengguna yang online pada semua host"}. -{"Send announcement to all users","Kirim pengumuman untuk semua pengguna"}. +{"Send announcement to all online users","Kirim pengumuman untuk semua pengguna yang online"}. {"Send announcement to all users on all hosts","Kirim pengumuman untuk semua pengguna pada semua host"}. +{"Send announcement to all users","Kirim pengumuman untuk semua pengguna"}. {"September","September"}. -{"Server ~b","Layanan ~b"}. {"Server:","Layanan:"}. {"Set message of the day and send to online users","Mengatur pesan harian dan mengirimkan ke pengguna yang online"}. {"Set message of the day on all hosts and send to online users","Mengatur pesan harian pada semua host dan kirimkan ke pengguna yang online"}. @@ -307,90 +327,139 @@ {"Show Integral Table","Tampilkan Tabel Terpisah"}. {"Show Ordinary Table","Tampilkan Tabel Normal"}. {"Shut Down Service","Shut Down Layanan"}. -{"~s invites you to the room ~s","~s mengundang anda ke ruangan ~s"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Beberapa klien Jabber dapat menyimpan password di komputer Anda. Gunakan fitur itu hanya jika Anda mempercayai komputer Anda aman."}. +{"SOCKS5 Bytestreams","SOCKS5 Bytestreams"}. {"Specify the access model","Tentukan model akses"}. {"Specify the event message type","Tentukan jenis acara pesan"}. {"Specify the publisher model","Tentukan model penerbitan"}. -{"~s's Offline Messages Queue","Antrian Pesan Offline ~s"}. -{"Start Modules at ","Mulai Modul pada"}. -{"Start Modules","Memulai Modul"}. -{"Start","Mulai"}. -{"Statistics of ~p","statistik dari ~p"}. -{"Statistics","Statistik"}. -{"Stop","Hentikan"}. -{"Stop Modules at ","Hentikan Modul pada"}. -{"Stop Modules","Hentikan Modul"}. +{"Stanza ID","ID Stanza"}. {"Stopped Nodes","Menghentikan node"}. -{"Storage Type","Jenis Penyimpanan"}. {"Store binary backup:","Penyimpanan cadangan yang berpasangan:"}. {"Store plain text backup:","Simpan cadangan teks biasa:"}. +{"Stream management is already enabled","Manajemen stream sudah diaktifkan"}. +{"Stream management is not enabled","Manajemen stream tidak diaktifkan"}. {"Subject","Subyek"}. -{"Submit","Serahkan"}. {"Submitted","Ulangi masukan"}. {"Subscriber Address","Alamat Pertemanan"}. -{"Subscription","Berlangganan"}. +{"Subscribers may publish","Pelanggan dapat mempublikasikan"}. +{"Subscriptions are not allowed","Langganan tidak diperbolehkan"}. {"Sunday","Minggu"}. +{"Text associated with a picture","Teks yang terkait dengan gambar"}. +{"Text associated with a sound","Teks yang terkait dengan suara"}. +{"Text associated with a video","Teks yang terkait dengan video"}. +{"Text associated with speech","Teks yang terkait dengan ucapan"}. {"That nickname is already in use by another occupant","Julukan itu sudah digunakan oleh penghuni lain"}. {"That nickname is registered by another person","Julukan tersebut telah didaftarkan oleh orang lain"}. +{"The account was not unregistered","Akun tidak terdaftar"}. {"The CAPTCHA is valid.","Captcha ini benar."}. {"The CAPTCHA verification has failed","Verifikasi CAPTCHA telah gagal"}. +{"The captcha you entered is wrong","Isian captcha yang anda masukkan salah"}. {"The collections with which a node is affiliated","Koleksi dengan yang berafiliasi dengan sebuah node"}. -{"the password is","kata sandi yaitu:"}. +{"The JID of the node creator","JID dari pembuat node"}. +{"The JIDs of those to contact with questions","JID dari mereka untuk dihubungi dengan pertanyaan"}. +{"The JIDs of those with an affiliation of owner","JID dari mereka yang memiliki afiliasi pemilik"}. +{"The JIDs of those with an affiliation of publisher","JID dari mereka yang memiliki afiliasi penerbit"}. +{"The name of the node","Nama node"}. +{"The node is a collection node","Node adalah node koleksi"}. +{"The node is a leaf node (default)","Node adalah leaf node (default)"}. +{"The NodeID of the relevant node","NodeID dari node yang relevan"}. +{"The number of subscribers to the node","Jumlah pendaftar di node"}. +{"The number of unread or undelivered messages","Jumlah pesan yang belum dibaca atau tidak terkirim"}. +{"The password contains unacceptable characters","Kata sandi mengandung karakter yang tidak dapat diterima"}. {"The password is too weak","Kata sandi terlalu lemah"}. -{"The password of your Jabber account was successfully changed.","Kata sandi pada akun Jabber Anda telah berhasil diubah."}. -{"There was an error changing the password: ","Ada kesalahan dalam mengubah password:"}. -{"There was an error creating the account: ","Ada kesalahan saat membuat akun:"}. -{"There was an error deleting the account: ","Ada kesalahan saat menghapus akun:"}. -{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Pada bagian ini huruf besar dan kecil tidak dibedakan: Misalnya macbeth adalah sama dengan MacBeth juga Macbeth."}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Halaman ini memungkinkan untuk membuat akun Jabber di layanan Jabber ini. JID Anda (Jabber Pengenal) akan berbentuk: namapengguna@layanan. Harap baca dengan seksama petunjuk-petunjuk untuk mengisi kolom dengan benar."}. -{"This page allows to unregister a Jabber account in this Jabber server.","Pada bagian ini memungkinkan Anda untuk membatalkan pendaftaran akun Jabber pada layanan Jabber ini."}. +{"the password is","kata sandinya"}. +{"The password was not changed","Kata sandi belum berubah"}. +{"The passwords are different","Kata sandi berbeda"}. +{"There was an error changing the password: ","Ada kesalahan saat merubah kata kunci: "}. +{"There was an error creating the account: ","Ada kesalahan saat membuat akun: "}. +{"There was an error deleting the account: ","Ada kesalahan saat menghapus akun: "}. +{"This room is not anonymous","Ruangan ini tidak dikenal"}. +{"This service can not process the address: ~s","Layanan ini tidak dapat memproses alamat: ~s"}. {"Thursday","Kamis"}. {"Time delay","Waktu tunda"}. -{"Time","Waktu"}. -{"To","Kepada"}. -{"To ~s","Kepada ~s"}. -{"Traffic rate limit is exceeded","Lalu lintas melebihi batas"}. -{"Transactions Aborted:","Transaksi yang dibatalkan:"}. -{"Transactions Committed:","Transaksi yang dilakukan:"}. -{"Transactions Logged:","Transaksi yang ditempuh:"}. -{"Transactions Restarted:","Transaksi yang dijalankan ulang:"}. +{"To register, visit ~s","Untuk mendaftar, kunjungi ~s"}. +{"To ~ts","Kepada ~ts"}. +{"Token TTL","TTL Token"}. +{"Too many active bytestreams","Terlalu banyak bytestream aktif"}. +{"Too many CAPTCHA requests","Terlalu banyak permintaan CAPTCHA"}. +{"Too many child elements","Terlalu banyak elemen turunan"}. +{"Too many elements","Terlalu banyak elemen"}. +{"Too many elements","Terlalu banyak elemen"}. +{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Terlalu banyak (~p) percobaan otentifikasi yang gagal dari alamat IP (~s). Alamat akan di unblok pada ~s UTC"}. +{"Too many receiver fields were specified","Terlalu banyak bidang penerima yang ditentukan"}. +{"Too many users in this conference","Terlalu banyak pengguna di grup ini"}. +{"Traffic rate limit is exceeded","Batas tingkat lalu lintas terlampaui"}. +{"~ts's Offline Messages Queue","~ts's antrian Pesan Offline"}. {"Tuesday","Selasa"}. {"Unable to generate a CAPTCHA","Tidak dapat menghasilkan CAPTCHA"}. +{"Unable to register route on existing local domain","Tidak dapat mendaftarkan rute di domain lokal yang ada"}. {"Unauthorized","Ditolak"}. -{"Unregister a Jabber account","Nonaktifkan akun jabber"}. +{"Unexpected action","Aksi yang tidak diharapkan"}. +{"Unexpected error condition: ~p","Kondisi kerusakan yang tidak diduga: ~p"}. +{"Unregister an XMPP account","Nonaktifkan akun XMPP"}. {"Unregister","Nonaktifkan"}. -{"Update","Memperbarui"}. +{"Unsupported element","Elemen tidak didukung"}. +{"Unsupported version","Versi tidak didukung"}. {"Update message of the day (don't send)","Rubah pesan harian (tidak dikirim)"}. {"Update message of the day on all hosts (don't send)","Rubah pesan harian pada semua host (tidak dikirim)"}. -{"Update plan","Rencana Perubahan"}. -{"Update script","Perbarui naskah"}. -{"Uptime:","Sampai saat:"}. -{"Use of STARTTLS required","Penggunaan STARTTLS diperlukan"}. +{"User already exists","Pengguna sudah ada"}. +{"User (jid)","Pengguna (jid)"}. +{"User JID","Pengguna JID"}. {"User Management","Manajemen Pengguna"}. +{"User removed","Pengguna dipindahkan"}. +{"User session not found","Sesi pengguna tidak ditemukan"}. +{"User session terminated","Sesi pengguna dihentikan"}. +{"User ~ts","Pengguna ~ts"}. {"Username:","Nama Pengguna:"}. {"User","Pengguna"}. {"Users are not allowed to register accounts so quickly","Pengguna tidak diperkenankan untuk mendaftar akun begitu cepat"}. {"Users Last Activity","Aktifitas terakhir para pengguna"}. {"Users","Pengguna"}. -{"Validate","Mengesahkan"}. +{"Value 'get' of 'type' attribute is not allowed","Nilai 'get' dari 'type' atribut tidak diperbolehkan"}. +{"Value of '~s' should be boolean","Nilai '~ s' harus boolean"}. +{"Value of '~s' should be datetime string","Nilai '~s' harus string datetime"}. +{"Value of '~s' should be integer","Nilai '~ s' harus integer"}. +{"Value 'set' of 'type' attribute is not allowed","Nilai 'set' dari 'type' atribut tidak diperbolehkan"}. {"vCard User Search","vCard Pencarian Pengguna"}. -{"Virtual Hosts","Virtual Hosts"}. -{"Visitors are not allowed to change their nicknames in this room","Visitor tidak diperbolehkan untuk mengubah nama julukan di ruangan ini"}. -{"Visitors are not allowed to send messages to all occupants","Visitor tidak diperbolehkan untuk mengirim pesan ke semua penghuni"}. +{"Virtual Hosts","Host Virtual"}. +{"Visitors are not allowed to change their nicknames in this room","Tamu tidak diperbolehkan untuk mengubah nama panggilan di ruangan ini"}. +{"Visitors are not allowed to send messages to all occupants","Tamu tidak diperbolehkan untuk mengirim pesan ke semua penghuni"}. +{"Visitor","Tamu"}. +{"Voice request","Permintaan suara"}. +{"Voice requests are disabled in this conference","Permintaan suara dinonaktifkan dalam konferensi ini"}. {"Wednesday","Rabu"}. +{"When a new subscription is processed and whenever a subscriber comes online","Saat langganan baru diproses dan tiap kali pelanggan online"}. +{"When a new subscription is processed","Saat langganan baru diproses"}. {"When to send the last published item","Ketika untuk mengirim item terakhir yang dipublikasikan"}. +{"Whether an entity wants to receive an XMPP message body in addition to the payload format","Apakah entitas ingin menerima isi pesan XMPP selain format payload"}. +{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","Apakah entitas ingin menerima ringkasan(agregasi) pemberitahuan atau semua pemberitahuan satu per satu"}. +{"Whether an entity wants to receive or disable notifications","Apakah entitas ingin menerima atau menonaktifkan pemberitahuan"}. +{"Whether owners or publisher should receive replies to items","Apakah pemilik atau penerbit harus menerima balasan dari item"}. +{"Whether the node is a leaf (default) or a collection","Apakah node adalah leaf (default) atau koleksi"}. {"Whether to allow subscriptions","Apakah diperbolehkan untuk berlangganan"}. -{"You can later change your password using a Jabber client.","Anda dapat mengubah kata sandi anda dilain waktu dengan menggunakan klien Jabber."}. +{"Whether to make all subscriptions temporary, based on subscriber presence","Apakah akan menjadikan semua langganan sementara, berdasarkan keberadaan pelanggan"}. +{"Whether to notify owners about new subscribers and unsubscribes","Apakah akan memberi tahu pemilik tentang pelanggan baru dan berhenti berlangganan"}. +{"Who may associate leaf nodes with a collection","Siapa yang dapat mengaitkan leaf node dengan koleksi"}. +{"Wrong parameters in the web formulary","Parameter yang salah di formula web"}. +{"Wrong xmlns","xmlns salah"}. +{"XMPP Account Registration","Pendaftaran Akun XMPP"}. +{"XMPP Domains","Domain XMPP"}. +{"XMPP Show Value of Away","XMPP menunjukkan status Away"}. +{"XMPP Show Value of Chat","XMPP menunjukkan status Chat"}. +{"XMPP Show Value of DND (Do Not Disturb)","XMPP menunjukkan status DND (Do Not Disturb)"}. +{"XMPP Show Value of XA (Extended Away)","XMPP menunjukkan status XA (Extended Away)"}. +{"XMPP URI of Associated Publish-Subscribe Node","XMPP URI dari node Associated Publish-Subscribe"}. +{"You are being removed from the room because of a system shutdown","Anda sedang dikeluarkan dari kamar karena sistem shutdown"}. +{"You are not joined to the channel","Anda tidak bergabung ke channel"}. +{"You can later change your password using an XMPP client.","Anda dapat mengubah kata sandi menggunakan aplikasi XMPP."}. {"You have been banned from this room","Anda telah diblokir dari ruangan ini"}. -{"You must fill in field \"Nickname\" in the form","Anda harus mengisi kolom \"Julukan\" dalam formulir"}. +{"You have joined too many conferences","Anda telah mengikuti terlalu banyak grup"}. +{"You must fill in field \"Nickname\" in the form","Anda harus mengisi kolom \"Panggilan\" dalam formulir"}. {"You need a client that supports x:data and CAPTCHA to register","Anda memerlukan klien yang mendukung x:data dan CAPTCHA untuk mendaftar"}. {"You need a client that supports x:data to register the nickname","Anda memerlukan klien yang mendukung x:data untuk mendaftar julukan"}. -{"You need an x:data capable client to configure mod_irc settings","Anda memerlukan x:data klien untuk mampu mengkonfigurasi pengaturan mod_irc"}. -{"You need an x:data capable client to configure room","Anda memerlukan x:data klien untuk dapat mengkonfigurasi ruangan"}. {"You need an x:data capable client to search","Anda memerlukan x:data klien untuk melakukan pencarian"}. -{"Your active privacy list has denied the routing of this stanza.","Daftar privasi aktif Anda telah menolak routing ztanza ini"}. +{"Your active privacy list has denied the routing of this stanza.","Daftar privasi aktif Anda telah menolak routing stanza ini."}. {"Your contact offline message queue is full. The message has been discarded.","Kontak offline Anda pada antrian pesan sudah penuh. Pesan telah dibuang."}. -{"Your Jabber account was successfully created.","Jabber akun Anda telah sukses dibuat"}. -{"Your Jabber account was successfully deleted.","Jabber akun Anda berhasil dihapus."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","Pesan Anda untuk ~s sedang diblokir. Untuk membuka blokir tersebut, kunjungi ~s"}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Pesan Anda untuk ~s sedang diblokir. Untuk membuka blokir tersebut, kunjungi ~s"}. +{"Your XMPP account was successfully registered.","Akun XMPP Anda berhasil didaftarkan."}. +{"Your XMPP account was successfully unregistered.","Akun XMPP Anda berhasil dihapus."}. +{"You're not allowed to create nodes","Anda tidak diizinkan membuat node"}. diff --git a/priv/msgs/id.po b/priv/msgs/id.po deleted file mode 100644 index 5ec254663..000000000 --- a/priv/msgs/id.po +++ /dev/null @@ -1,1946 +0,0 @@ -# , 2010. -msgid "" -msgstr "" -"Project-Id-Version: 2.1.0-alpha\n" -"PO-Revision-Date: 2011-02-15 07:09+0800\n" -"Last-Translator: Irfan Mahfudz Guntur \n" -"Language-Team: SmartCommunity \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Indonesian (Bahasa Indonesia)\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "Penggunaan STARTTLS diperlukan" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Tidak ada sumber daya yang disediakan" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Diganti dengan koneksi baru" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "telah dikick" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "Daftar privasi aktif Anda telah menolak routing ztanza ini" - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Masukkan teks yang Anda lihat" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "" -"Pesan Anda untuk ~s sedang diblokir. Untuk membuka blokir tersebut, kunjungi " -"~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "" -"Jika Anda tidak melihat gambar CAPTCHA disini, silahkan kunjungi halaman web." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "CAPTCHA laman web" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "Captcha ini benar." - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Pengguna" - -#: ejabberd_oauth.erl:256 -#, fuzzy -msgid "Server" -msgstr "Layanan:" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Sandi" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "Ditolak" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "Admin Web ejabberd" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Administrasi" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Akses Daftar Pengendalian" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Ulangi masukan" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Format yang buruk" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Serahkan" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "mentah" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Hapus Yang Terpilih" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Aturan Akses" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "~s aturan akses konfigurasi" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "Virtual Hosts" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Pengguna" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "Pengguna Yang Online" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Aktifitas terakhir para pengguna" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Periode:" - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "Akhir bulan" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "Akhir tahun" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "Semua aktifitas" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Tampilkan Tabel Normal" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Tampilkan Tabel Terpisah" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Statistik" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "Tidak Ditemukan" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Node tidak ditemukan" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Tambah Baru" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Host" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Pengguna Terdaftar" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Tambah Pengguna" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Pesan Offline" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Aktifitas Terakhir" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Tidak Pernah" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Online" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Pengguna Terdaftar:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Pengguna Online:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Koneksi s2s yang keluar:" - -#: ejabberd_web_admin.erl:1559 -#, fuzzy -msgid "Incoming s2s Connections:" -msgstr "Koneksi s2s yang keluar:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Tak satupun" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Ubah Kata Sandi" - -#: ejabberd_web_admin.erl:1673 -#, fuzzy -msgid "User ~s" -msgstr "Pengguna" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Sumber Daya Terhubung:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Kata Sandi:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Hapus Pengguna" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Tidak Ada Data" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Node-node" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Menjalankan Node" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Menghentikan node" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -#, fuzzy -msgid "Node ~p" -msgstr "Node" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "Database" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Backup" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Port Terdeteksi" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Memperbarui" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Jalankan Ulang" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Hentikan" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Modul" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "Panggilan Kesalahan RPC" - -#: ejabberd_web_admin.erl:1917 -#, fuzzy -msgid "Database Tables at ~p" -msgstr "Tabel Database pada" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Nama" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Jenis Penyimpanan" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Elemen-elemen" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Memori" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Kesalahan" - -#: ejabberd_web_admin.erl:1955 -#, fuzzy -msgid "Backup of ~p" -msgstr "Cadangan dari" - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Harap dicatat bahwa pilihan ini hanya akan membuat cadangan builtin Mnesia " -"database. Jika Anda menggunakan modul ODBC, anda juga perlu untuk membuat " -"cadangan database SQL Anda secara terpisah." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Penyimpanan cadangan yang berpasangan:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "YA" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Segera mengembalikan cadangan yang berpasangan:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"Mengembalikan cadangan yang berpasanagn setelah ejabberd berikutnya " -"dijalankan ulang (memerlukan memori lebih sedikit):" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Simpan cadangan teks biasa:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Segera mengembalikan cadangan teks biasa:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "impor data-data pengguna dari sebuah PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" -"Ekspor data dari semua pengguna pada layanan ke berkas PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "Ekspor data pengguna pada sebuah host ke berkas PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Impor data pengguna dari sekumpulan berkas jabberd14:" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Импорт пользовательских данных из буферной директории jabberd14:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Mendeteksi Port-port di" - -#: ejabberd_web_admin.erl:2144 -#, fuzzy -msgid "Modules at ~p" -msgstr "modul-modul di" - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "statistik dari ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Sampai saat:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "Waktu CPU:" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Transaksi yang dilakukan:" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Transaksi yang dibatalkan:" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Transaksi yang dijalankan ulang:" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Transaksi yang ditempuh:" - -#: ejabberd_web_admin.erl:2243 -#, fuzzy -msgid "Update ~p" -msgstr "Memperbarui " - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "Rencana Perubahan" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Modifikasi modul-modul" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Perbarui naskah" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Perbaruan naskah tingkat rendah" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Periksa naskah" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Port" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Protocol" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Modul" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Pilihan-pilihan" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Mulai" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Perintah" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "Benar-benar ingin menghapus pesan harian?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Subyek" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Isi Pesan" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "Tidak ada isi pesan yang disediakan untuk mengirimkan pesan" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Pengumuman" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Kirim pengumuman untuk semua pengguna" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Kirim pengumuman untuk semua pengguna pada semua host" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Kirim pengumuman untuk semua pengguna yang online" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "Kirim pengumuman untuk semua pengguna yang online pada semua host" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Mengatur pesan harian dan mengirimkan ke pengguna yang online" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" -"Mengatur pesan harian pada semua host dan kirimkan ke pengguna yang online" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Rubah pesan harian (tidak dikirim)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "Rubah pesan harian pada semua host (tidak dikirim)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Hapus pesan harian" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Hapus pesan harian pada semua host" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Pengaturan" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Memulai Modul" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Hentikan Modul" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Mengembalikan" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Dump menjadi File Teks" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Impor File" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Impor Direktori" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Restart Layanan" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Shut Down Layanan" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Hapus Pengguna" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Akhir Sesi Pengguna" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Dapatkan User Password" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Ubah User Password" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "Dapatkan Waktu Login Terakhir Pengguna " - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Dapatkan Statistik Pengguna" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Dapatkan Jumlah Pengguna Yang Terdaftar" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Dapatkan Jumlah User Yang Online" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Manajemen Pengguna" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Semua Pengguna" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Koneksi Keluar s2s" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Manajemen Backup" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Impor Pengguna Dari jabberd14 Spool File" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "Kepada ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "Dari ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Database Tabel Konfigurasi pada" - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Pilih jenis penyimpanan tabel" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Hanya salinan dari disc" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "RAM dan disc salinan" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "Salinan RAM" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "Salinan Remote" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Hentikan Modul pada" - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Pilih Modul untuk berhenti" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Mulai Modul pada" - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Masukkan daftar {Modul, [Options]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Daftar modul untuk memulai" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Backup ke File pada" - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Masukkan path untuk file cadangan" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Jalur ke File" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Kembalikan Backup dari File pada" - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Dump Backup ke File Teks di" - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Masukkan path ke file teks" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Impor Pengguna dari File pada" - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Masukkan path ke file jabberd14 spool" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Impor Pengguna dari Dir di" - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Masukkan path ke direktori spool jabberd14" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Jalur ke Dir" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "Waktu tunda" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Konfigurasi Daftar Akses Pengendalian" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Daftar Pengendalian Akses" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Akses Konfigurasi" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Akses peraturan" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Verifikasi Sandi" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Jumlah pengguna terdaftar" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Jumlah pengguna online" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Terakhir Login" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "Ukuran Daftar Kontak" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "Alamat IP" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Sumber daya" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Administrasi" - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Tindakan pada pengguna" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Ganti Properti" - -#: mod_fail2ban.erl:95 -msgid "" -"Too many (~p) failed authentications from this IP address (~s). The address " -"will be unblocked at ~s UTC" -msgstr "" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "" - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "" - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "Akses ditolak oleh kebijakan layanan" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "IRC Transport" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "ejabberd IRC modul" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "" -"Anda memerlukan x:data klien untuk mampu mengkonfigurasi pengaturan mod_irc" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Pendaftaran di mod_irc untuk" - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Masukkan username, pengkodean, port dan sandi yang ingin Anda gunakan untuk " -"menghubungkan ke layanan IRC" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "Nama Pengguna IRC" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Jika Anda ingin menentukan port yang berbeda, sandi, pengkodean untuk " -"layanan IRC, isi daftar ini dengan nilai-nilai dalam format '{\"server irc " -"\", \"encoding \", port, \"sandi \"}'. Secara default ini menggunakan " -"layanan \"~s \" pengkodean, port ~p, kata sandi kosong." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Contoh: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Parameter Koneksi" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "Gabung channel IRC" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "Channel IRC (tidak perlu menempatkan # sebelumnya)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "Layanan IRC" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Gabung ke channel IRC disini" - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Gabung ke channel IRC dengan Jabber ID: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "Pengaturan IRC" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Masukkan username dan pengkodean yang ingin Anda gunakan untuk menghubungkan " -"ke layanan IRC. Tekan 'Selanjutnya' untuk mendapatkan lagi formulir kemudian " -"Tekan 'Lengkap' untuk menyimpan pengaturan." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "Nama Pengguna IRC" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Kata Sandi ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Port ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Pengkodean untuk layanan ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Layanan ~b" - -#: mod_mam.erl:541 -#, fuzzy -msgid "Only members may query archives of this room" -msgstr "" -"Hanya moderator yang diperbolehkan untuk mengubah topik dalam ruangan ini" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "" -"Layanan hanya diperuntukan kepada administrator yang diizinkan untuk " -"mengirim layanan pesan" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "Pembuatan Ruangan ditolak oleh kebijakan layanan" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "Ruang Konferensi tidak ada" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Ruangan Chat" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "Anda memerlukan klien yang mendukung x:data untuk mendaftar julukan" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Pendaftaran Julukan pada" - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Masukkan nama julukan Anda jika ingin mendaftar" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Nama Julukan" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "Julukan tersebut telah didaftarkan oleh orang lain" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Anda harus mengisi kolom \"Julukan\" dalam formulir" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "ejabberd MUC Module" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "" - -#: mod_muc_admin.erl:249 -#, fuzzy -msgid "Total rooms" -msgstr "Ruangan Chat" - -#: mod_muc_admin.erl:250 -#, fuzzy -msgid "Permanent rooms" -msgstr "meninggalkan ruangan" - -#: mod_muc_admin.erl:251 -#, fuzzy -msgid "Registered nicknames" -msgstr "Pengguna Terdaftar" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "Konfigurasi ruang chat diubah" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "bergabung ke ruangan" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "meninggalkan ruangan" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "telah dibanned" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "telah dikick karena perubahan afiliasi" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "telah dikick karena ruangan telah diubah menjadi hanya untuk member" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "telah dikick karena sistem shutdown" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "sekarang dikenal sebagai" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr "telah menetapkan topik yaitu:" - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "Ruang chat telah dibuat" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "Ruang chat dilenyapkan" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "Ruang chat dimulai" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "Ruang chat dihentikan" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "Senin" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "Selasa" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "Rabu" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "Kamis" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "Jumat" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "Sabtu" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "Minggu" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "Januari" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "Februari" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "Maret" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "April" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "Mei" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "Juni" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "Juli" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "Agustus" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "September" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "Oktober" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "Nopember" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "Desember" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Konfigurasi Ruangan" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "Penghuni Ruangan" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "Lalu lintas melebihi batas" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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 "" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "Hal ini tidak diperbolehkan untuk mengirim pesan pribadi ke konferensi" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "" - -#: mod_muc_room.erl:377 -#, fuzzy -msgid "Only moderators can approve voice requests" -msgstr "Perbolehkan pengguna mengirimkan undangan" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Jenis pesan yang tidak benar" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "" -"Hal ini tidak diperbolehkan untuk mengirim pesan pribadi jenis \"groupchat \"" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "Penerima tidak berada di ruangan konferensi" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "Hal ini tidak diperbolehkan untuk mengirim pesan pribadi" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Hanya penghuni yang diizinkan untuk mengirim pesan ke konferensi" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "Hanya penghuni diizinkan untuk mengirim permintaan ke konferensi" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "" -"Permintaan untuk para anggota konferensi tidak diperbolehkan di ruangan ini" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "" -"Hanya moderator dan peserta yang diizinkan untuk mengganti topik pembicaraan " -"di ruangan ini" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "" -"Hanya moderator yang diperbolehkan untuk mengubah topik dalam ruangan ini" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "Visitor tidak diperbolehkan untuk mengirim pesan ke semua penghuni" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "Visitor tidak diperbolehkan untuk mengubah nama julukan di ruangan ini" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "Julukan itu sudah digunakan oleh penghuni lain" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Anda telah diblokir dari ruangan ini" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "Hanya Member yang dapat masuk ruangan ini" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "Diperlukan kata sandi untuk masuk ruangan ini" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "Tidak dapat menghasilkan CAPTCHA" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Kata sandi salah" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "Hak istimewa Administrator dibutuhkan" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "Hak istimewa moderator dibutuhkan" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "Jabber ID ~s tidak valid" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "Nama Julukan ~s tidak berada di dalam ruangan" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Afiliasi tidak valid: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Peran tidak valid: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "Hak istimewa owner dibutuhkan" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "Pengaturan ruangan ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Nama Ruangan" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "Keterangan ruangan" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Buat ruangan menjadi permanent" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Buat ruangan dapat dicari" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "Buat daftar participant diketahui oleh public" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Buat ruangan yang dilindungi dengan kata sandi" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Maksimum Jumlah Penghuni" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Tidak terbatas" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Tampilkan Jabber ID secara lengkap" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "Hanya moderator" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "Siapapun" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "" - -#: mod_muc_room.erl:3486 -#, fuzzy -msgid "Moderator" -msgstr "Hanya moderator" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Buat ruangan hanya untuk member saja" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Buat ruangan hanya untuk moderator saja" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "pengguna pertama kali masuk sebagai participant" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Perbolehkan pengguna untuk mengganti topik" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "perbolehkan pengguna mengirimkan pesan ke pengguna lain secara pribadi" - -#: mod_muc_room.erl:3533 -#, fuzzy -msgid "Allow visitors to send private messages to" -msgstr "perbolehkan pengguna mengirimkan pesan ke pengguna lain secara pribadi" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Perbolehkan pengguna untuk mengetahui pengguna lain" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Perbolehkan pengguna mengirimkan undangan" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "Izinkan pengunjung untuk mengirim teks status terbaru" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Perbolehkan visitor mengganti nama julukan" - -#: mod_muc_room.erl:3589 -#, fuzzy -msgid "Allow visitors to send voice requests" -msgstr "Perbolehkan pengguna mengirimkan undangan" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "Buat ruangan dilindungi dengan CAPTCHA" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Aktifkan catatan" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "Anda memerlukan x:data klien untuk dapat mengkonfigurasi ruangan" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Jumlah Penghuni" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "pribadi, " - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "" - -#: mod_muc_room.erl:4351 -#, fuzzy -msgid "User JID" -msgstr "Pengguna" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s mengundang anda ke ruangan ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "kata sandi yaitu:" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "" -"Kontak offline Anda pada antrian pesan sudah penuh. Pesan telah dibuang." - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "Antrian Pesan Offline ~s" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Waktu" - -#: mod_offline.erl:812 -msgid "From" -msgstr "Dari" - -#: mod_offline.erl:813 -msgid "To" -msgstr "Kepada" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Paket" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Pesan Offline:" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "Hapus Semua Pesan Offline" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "modul ejabberd SOCKS5 Bytestreams" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Setujui-Pertemanan" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "Modul ejabberd Setujui-Pertemanan" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "Permintaan pertemanan PubSub" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Pilih apakah akan menyetujui hubungan pertemanan ini." - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "ID Node" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Alamat Pertemanan" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "Izinkan ID Jabber ini untuk berlangganan pada node pubsub ini?" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Memberikan muatan dengan pemberitahuan acara" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Memberikan pemberitahuan acara" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Beritahu pelanggan ketika ada perubahan konfigurasi node" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Beritahu pelanggan ketika node dihapus" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Beritahu pelanggan ketika item tersebut dikeluarkan dari node" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Pertahankan item ke penyimpanan" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "Nama yang dikenal untuk node" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Max item untuk bertahan" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Apakah diperbolehkan untuk berlangganan" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Tentukan model akses" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Kelompok kontak yang diizinkan untuk berlangganan" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Tentukan model penerbitan" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "Bersihkan semua item ketika penerbit yang relevan telah offline" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "Tentukan jenis acara pesan" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Max kapasitas ukuran dalam bytes" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "Ketika untuk mengirim item terakhir yang dipublikasikan" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Hanya mengirimkan pemberitahuan kepada pengguna yang tersedia" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "Koleksi dengan yang berafiliasi dengan sebuah node" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "Verifikasi CAPTCHA telah gagal" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "" -"Anda memerlukan klien yang mendukung x:data dan CAPTCHA untuk mendaftar" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "Pilih nama pengguna dan kata sandi untuk mendaftar dengan layanan ini" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "Kata sandi terlalu lemah" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "Pengguna tidak diperkenankan untuk mendaftar akun begitu cepat" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "Jabber akun Anda telah sukses dibuat" - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "Ada kesalahan saat membuat akun:" - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "Jabber akun Anda berhasil dihapus." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "Ada kesalahan saat menghapus akun:" - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "Kata sandi pada akun Jabber Anda telah berhasil diubah." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "Ada kesalahan dalam mengubah password:" - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Pendaftaran Akun Jabber" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Daftarkan sebuah akun jabber" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Nonaktifkan akun jabber" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"Halaman ini memungkinkan untuk membuat akun Jabber di layanan Jabber ini. " -"JID Anda (Jabber Pengenal) akan berbentuk: namapengguna@layanan. Harap baca " -"dengan seksama petunjuk-petunjuk untuk mengisi kolom dengan benar." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "Nama Pengguna:" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" -"Pada bagian ini huruf besar dan kecil tidak dibedakan: Misalnya macbeth " -"adalah sama dengan MacBeth juga Macbeth." - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "Karakter tidak diperbolehkan:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "Layanan:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" -"Jangan memberitahukan kata sandi Anda ke siapapun, bahkan para administrator " -"dari layanan Jabber." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "" -"Anda dapat mengubah kata sandi anda dilain waktu dengan menggunakan klien " -"Jabber." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Beberapa klien Jabber dapat menyimpan password di komputer Anda. Gunakan " -"fitur itu hanya jika Anda mempercayai komputer Anda aman." - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"Hafalkan kata sandi Anda, atau dicatat dan letakkan di tempat yang aman. " -"Didalam Jabber tidak ada cara otomatis untuk mendapatkan kembali password " -"Anda jika Anda lupa." - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "Verifikasi Kata Sandi:" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "Mendaftar" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "Password Lama:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "Password Baru:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "" -"Pada bagian ini memungkinkan Anda untuk membatalkan pendaftaran akun Jabber " -"pada layanan Jabber ini." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "Nonaktifkan" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Berlangganan" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Tertunda" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Grup" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Mengesahkan" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Menghapus" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Kontak dari" - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Tambah Jabber ID" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Kontak" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Berbagi grup kontak" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Nama:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Keterangan:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Anggota:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Tampilkan Grup:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Grup" - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Layanan Erlang Jabber" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Hari Lahir" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Kota" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "Negara" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "Email" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Nama Keluarga (marga)" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Isi formulir untuk pencarian pengguna Jabber yang cocok (Tambahkan * ke " -"mengakhiri pengisian untuk menyamakan kata)" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Nama Lengkap" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Nama Tengah" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Nama Organisasi" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Unit Organisasi" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Pencarian pengguna dalam" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "Anda memerlukan x:data klien untuk melakukan pencarian" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "vCard Pencarian Pengguna" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "Modul ejabberd vCard" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Hasil Pencarian untuk" - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "Isi kolom untuk mencari pengguna Jabber yang sama" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Layanan s2s yang keluar:" - -#~ msgid "Delete" -#~ msgstr "Hapus" - -#~ msgid "This room is not anonymous" -#~ msgstr "Ruangan ini tidak dikenal" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "Peserta ini dikick dari ruangan karena dia mengirim pesan kesalahan" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Participant ini dikick dari ruangan karena ia mengirim pesan kesalahan ke " -#~ "participant lain" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "" -#~ "Participant ini dikick dari ruangan karena ia mengirim kehadiran kesalahan" - -#, fuzzy -#~ msgid "Captcha test failed" -#~ msgstr "Проверка капчи прошла успешно." diff --git a/priv/msgs/it.msg b/priv/msgs/it.msg index 3dc7d8336..926d16687 100644 --- a/priv/msgs/it.msg +++ b/priv/msgs/it.msg @@ -1,43 +1,79 @@ -%% -*- coding: latin-1 -*- -{"Access Configuration","Configurazione dell'accesso"}. -{"Access Control List Configuration","Configurazione dei diritti di accesso (ACL)"}. -{"Access control lists","Diritti di accesso (ACL)"}. -{"Access Control Lists","Diritti di accesso (ACL)"}. -{"Access denied by service policy","Accesso impedito dalle politiche del servizio"}. -{"Access rules","Regole di accesso"}. -{"Access Rules","Regole di accesso"}. -{"Action on user","Azione sull'utente"}. -{"Add Jabber ID","Aggiungere un Jabber ID (Jabber ID)"}. -{"Add New","Aggiungere nuovo"}. -{"Add User","Aggiungere un utente"}. -{"Administration","Amministrazione"}. -{"Administration of ","Amministrazione di "}. -{"Administrator privileges required","Necessari i privilegi di amministratore"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" (Add * to the end of field to match substring)"," (Aggiungere * alla fine del campo per far corrispondere la sottostringa)"}. +{" has set the subject to: "," ha modificato l'oggetto in: "}. +{"# participants","# partecipanti"}. +{"A description of the node","Una descrizione del nodo"}. {"A friendly name for the node","Un nome comodo per il nodo"}. +{"A password is required to enter this room","Per entrare in questa stanza è necessaria una password"}. +{"A Web Page","Una pagina web"}. +{"Accept","Accettare"}. +{"Access denied by service policy","Accesso negato dalle politiche del servizio"}. +{"Access model","Modello di accesso"}. +{"Account doesn't exist","L'account non esiste"}. +{"Action on user","Azione sull'utente"}. +{"Add a hat to a user","Aggiungere un cappello a un utente"}. +{"Add User","Aggiungere un utente"}. +{"Administration of ","Amministrazione di "}. +{"Administration","Amministrazione"}. +{"Administrator privileges required","Sono richiesti privilegi di amministratore"}. {"All activity","Tutta l'attività"}. -{"Allow this Jabber ID to subscribe to this pubsub node?","Consentire a questo Jabber ID l'iscrizione a questo nodo pubsub?"}. +{"All Users","Tutti gli utenti"}. +{"Allow subscription","Consenti iscrizione"}. +{"Allow this Jabber ID to subscribe to this pubsub node?","Consentire a questo ID Jabber di iscriversi a questo nodo pubsub?"}. +{"Allow this person to register with the room?","Permettere a questa persona di registrarsi con la stanza?"}. {"Allow users to change the subject","Consentire agli utenti di cambiare l'oggetto"}. {"Allow users to query other users","Consentire agli utenti query verso altri utenti"}. -{"Allow users to send invites","Consentire agli utenti l'invio di inviti"}. -{"Allow users to send private messages","Consentire agli utenti l'invio di messaggi privati"}. +{"Allow users to send invites","Consenti agli utenti di inviare inviti"}. +{"Allow users to send private messages","Consenti agli utenti di inviare messaggi privati"}. {"Allow visitors to change nickname","Consentire ai visitatori di cambiare il nickname"}. {"Allow visitors to send private messages to","Consentire agli ospiti l'invio di messaggi privati a"}. {"Allow visitors to send status text in presence updates","Consentire ai visitatori l'invio di testo sullo stato in aggiornamenti sulla presenza"}. {"Allow visitors to send voice requests","Consentire agli ospiti l'invio di richieste di parola"}. -{"All Users","Tutti gli utenti"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","Un gruppo LDAP associato che definisce l'appartenenza alla sala; deve trattarsi di un nome distinto LDAP in base a una definizione di gruppo specifica dell'implementazione o della distribuzione."}. {"Announcements","Annunci"}. -{"anyone","tutti"}. -{"A password is required to enter this room","Per entrare in questa stanza è prevista una password"}. +{"Answer associated with a picture","Risposta associata ad un'immagine"}. +{"Answer associated with a video","Risposta associata a un video"}. +{"Answer associated with speech","Risposta associata al discorso"}. +{"Answer to a question","Risposta a una domanda"}. +{"Anyone in the specified roster group(s) may subscribe and retrieve items","Chiunque nei gruppi di elenco specificati può iscriversi e recuperare elementi"}. +{"Anyone may associate leaf nodes with the collection","Chiunque può associare i nodi foglia alla raccolta"}. +{"Anyone may publish","Chiunque può pubblicare"}. +{"Anyone may subscribe and retrieve items","Chiunque può iscriversi e recuperare elementi"}. +{"Anyone with a presence subscription of both or from may subscribe and retrieve items","Chiunque abbia un abbonamento di presenza di entrambi o di può sottoscrivere e recuperare elementi"}. +{"Anyone with Voice","Chiunque abbia la Voce"}. +{"Anyone","Chiunque"}. {"April","Aprile"}. +{"Attribute 'channel' is required for this request","Per questa richiesta è richiesto l'attributo 'canale'"}. +{"Attribute 'id' is mandatory for MIX messages","L'attributo 'id' è obbligatorio per i messaggi MIX"}. +{"Attribute 'jid' is not allowed here","L'attributo \"jid\" non è consentito qui"}. +{"Attribute 'node' is not allowed here","L'attributo \"nodo\" non è consentito qui"}. +{"Attribute 'to' of stanza that triggered challenge","Attributo \"a\" della strofa che ha innescato la sfida"}. {"August","Agosto"}. -{"Backup Management","Gestione dei salvataggi"}. -{"Backup","Salvare"}. -{"Backup to File at ","Salvataggio sul file "}. +{"Automatic node creation is not enabled","La creazione automatica del nodo non è abilitata"}. +{"Backup Management","Gestione dei Backup"}. +{"Backup of ~p","Backup di ~p"}. +{"Backup to File at ","Backup su file in "}. +{"Backup","Backup"}. {"Bad format","Formato non valido"}. {"Birthday","Compleanno"}. +{"Both the username and the resource are required","Sono richiesti sia il nome utente che la risorsa"}. +{"Bytestream already activated","Bytestream già attivato"}. +{"Cannot remove active list","Impossibile rimuovere l'elenco attivo"}. +{"Cannot remove default list","Impossibile rimuovere l'elenco predefinito"}. {"CAPTCHA web page","Pagina web CAPTCHA"}. -{"Change Password","Modificare la password"}. +{"Challenge ID","ID Sfida"}. +{"Change Password","Cambiare la password"}. {"Change User Password","Cambiare la password dell'utente"}. +{"Changing password is not allowed","Non è consentito modificare la password"}. +{"Changing role/affiliation is not allowed","Non è consentito cambiare ruolo/affiliazione"}. +{"Channel already exists","Canale già esistente"}. +{"Channel does not exist","Il canale non esiste"}. +{"Channel JID","Canale JID"}. +{"Channels","Canali"}. {"Characters not allowed:","Caratteri non consentiti:"}. {"Chatroom configuration modified","Configurazione della stanza modificata"}. {"Chatroom is created","La stanza è creata"}. @@ -46,88 +82,100 @@ {"Chatroom is stopped","La stanza è arrestata"}. {"Chatrooms","Stanze"}. {"Choose a username and password to register with this server","Scegliere un nome utente e una password per la registrazione con questo server"}. -{"Choose modules to stop","Selezionare i moduli da arrestare"}. {"Choose storage type of tables","Selezionare una modalità di conservazione delle tabelle"}. -{"Choose whether to approve this entity's subscription.","Scegliere se approvare l'iscrizione per questa entità"}. +{"Choose whether to approve this entity's subscription.","Scegliere se approvare l'iscrizione per questa entità."}. {"City","Città"}. +{"Client acknowledged more stanzas than sent by server","Il client ha riconosciuto più stanze di quelle inviate dal server"}. {"Commands","Comandi"}. {"Conference room does not exist","La stanza per conferenze non esiste"}. -{"Configuration","Configurazione"}. {"Configuration of room ~s","Configurazione per la stanza ~s"}. -{"Connected Resources:","Risorse connesse:"}. -{"Connections parameters","Parametri delle connessioni"}. +{"Configuration","Configurazione"}. +{"Contact Addresses (normally, room owner or owners)","Indirizzi di contatto (normalmente, proprietario o proprietari della stanza)"}. {"Country","Paese"}. -{"CPU Time:","Tempo CPU:"}. -{"Database","Database"}. +{"Current Discussion Topic","Argomento di discussione attuale"}. +{"Database failure","Errore del database"}. {"Database Tables Configuration at ","Configurazione delle tabelle del database su "}. +{"Database","Database"}. {"December","Dicembre"}. {"Default users as participants","Definire per default gli utenti come partecipanti"}. -{"Delete message of the day","Eliminare il messaggio del giorno (MOTD)"}. {"Delete message of the day on all hosts","Eliminare il messaggio del giorno (MOTD) su tutti gli host"}. -{"Delete Selected","Eliminare gli elementi selezionati"}. +{"Delete message of the day","Eliminare il messaggio del giorno (MOTD)"}. {"Delete User","Eliminare l'utente"}. {"Deliver event notifications","Inviare notifiche degli eventi"}. {"Deliver payloads with event notifications","Inviare il contenuto del messaggio con la notifica dell'evento"}. -{"Description:","Descrizione:"}. -{"Disc only copy","Copia su disco soltanto"}. -{"Displayed Groups:","Gruppi visualizzati:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","Non comunicare la tua password a nessuno, neppure agli amministratori del server Jabber."}. +{"Disc only copy","Copia solo su disco"}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","Non rivelare la tua password a nessuno, nemmeno agli amministratori del server XMPP."}. {"Dump Backup to Text File at ","Trascrivere il salvataggio sul file di testo "}. {"Dump to Text File","Trascrivere su file di testo"}. +{"Duplicated groups are not allowed by RFC6121","I gruppi duplicati non sono consentiti da RFC6121"}. +{"Dynamically specify a replyto of the item publisher","Specifica dinamicamente una risposta dell'editore dell'elemento"}. {"Edit Properties","Modificare le proprietà"}. {"Either approve or decline the voice request.","Approva oppure respingi la richiesta di parola."}. -{"ejabberd IRC module","Modulo IRC per ejabberd"}. +{"ejabberd HTTP Upload service","Servizio di Caricamento HTTP di ejabberd"}. {"ejabberd MUC module","Modulo MUC per ejabberd"}. +{"ejabberd Multicast service","Servizio Multicast ejabberd"}. {"ejabberd Publish-Subscribe module","Modulo Pubblicazione/Iscrizione (PubSub) per ejabberd"}. {"ejabberd SOCKS5 Bytestreams module","Modulo SOCKS5 Bytestreams per ejabberd"}. {"ejabberd vCard module","Modulo vCard per ejabberd"}. {"ejabberd Web Admin","Amministrazione web ejabberd"}. -{"Elements","Elementi"}. +{"ejabberd","ejabberd"}. +{"Email Address","Indirizzo di Posta Elettronica"}. {"Email","E-mail"}. -{"Enable logging","Abilitare i log"}. -{"Encoding for server ~b","Codifica per il server ~b"}. +{"Enable hats","Abilitare i cappelli"}. +{"Enable logging","Abilitare la registrazione"}. +{"Enable message archiving","Abilita l'archiviazione dei messaggi"}. +{"Enabling push without 'node' attribute is not supported","L'abilitazione del push senza l'attributo 'nodo' non è supportata"}. {"End User Session","Terminare la sessione dell'utente"}. -{"Enter list of {Module, [Options]}","Immettere un elenco di {Modulo, [Opzioni]}"}. {"Enter nickname you want to register","Immettere il nickname che si vuole registrare"}. {"Enter path to backup file","Immettere il percorso del file di salvataggio"}. {"Enter path to jabberd14 spool dir","Immettere il percorso della directory di spool di jabberd14"}. {"Enter path to jabberd14 spool file","Immettere il percorso del file di spool di jabberd14"}. {"Enter path to text file","Immettere il percorso del file di testo"}. {"Enter the text you see","Immettere il testo visibile"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Immettere il nome utente e le codifiche che si desidera utilizzare per la connessione ai server IRC. Premere \"Avanti\" per vedere i successivi campi da compilare. Premere \"Fatto\" per salvare le impostazioni."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Immettere il nome utente, le codifiche, le porte e le password che si desidera utilizzare per la connessione ai server IRC"}. -{"Erlang Jabber Server","Erlang Jabber Server"}. -{"Error","Errore"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Esempio: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"segreto\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.serverdiprova.net\", \"utf-8\"}]."}. +{"Erlang XMPP Server","Server XMPP Erlang"}. {"Exclude Jabber IDs from CAPTCHA challenge","Escludi degli ID Jabber dal passaggio CAPTCHA"}. +{"Export all tables as SQL queries to a file:","Esporta tutte le tabelle come query SQL in un file:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Esportare i dati di tutti gli utenti nel server in file PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Esportare i dati degli utenti di un host in file PIEFXIS (XEP-0227):"}. +{"External component failure","Guasto del componente esterno"}. +{"External component timeout","Timeout del componente esterno"}. +{"Failed to activate bytestream","Impossibile attivare il flusso di byte"}. {"Failed to extract JID from your voice request approval","Impossibile estrarre il JID dall'approvazione della richiesta di parola"}. +{"Failed to map delegated namespace to external component","Impossibile mappare lo spazio dei nomi delegato al componente esterno"}. +{"Failed to parse HTTP response","Impossibile analizzare la risposta HTTP"}. +{"Failed to process option '~s'","Impossibile elaborare l'opzione '~s'"}. {"Family Name","Cognome"}. +{"FAQ Entry","Inserimento delle domande frequenti"}. {"February","Febbraio"}. -{"Fill in fields to search for any matching Jabber User","Riempire i campi per la ricerca di utenti Jabber corrispondenti ai criteri"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Riempire il modulo per la ricerca di utenti Jabber corrispondenti ai criteri (Aggiungere * alla fine del campo per la ricerca di una sottostringa"}. +{"File larger than ~w bytes","File più grande di ~w byte"}. +{"Fill in the form to search for any matching XMPP User","Compila il modulo per cercare qualsiasi utente XMPP corrispondente"}. {"Friday","Venerdì"}. -{"From","Da"}. -{"From ~s","Da ~s"}. +{"From ~ts","Da ~ts"}. +{"Full List of Room Admins","Elenco Completo degli Amministratori delle Stanze"}. +{"Full List of Room Owners","Elenco Completo dei Proprietari delle Stanze"}. {"Full Name","Nome completo"}. +{"Get List of Online Users","Ottieni L'elenco degli Utenti Online"}. +{"Get List of Registered Users","Ottieni L'elenco degli Utenti Registrati"}. {"Get Number of Online Users","Ottenere il numero di utenti online"}. {"Get Number of Registered Users","Ottenere il numero di utenti registrati"}. +{"Get Pending","Ottieni in sospeso"}. {"Get User Last Login Time","Ottenere la data di ultimo accesso dell'utente"}. -{"Get User Password","Ottenere la password dell'utente"}. {"Get User Statistics","Ottenere le statistiche dell'utente"}. +{"Given Name","Nome di battesimo"}. {"Grant voice to this person?","Dare parola a questa persona?"}. -{"Group ","Gruppo "}. -{"Groups","Gruppi"}. {"has been banned","è stata/o bandita/o"}. -{"has been kicked because of an affiliation change","è stato espulso a causa di un cambiamento di appartenenza"}. {"has been kicked because of a system shutdown","è stato espulso a causa dello spegnimento del sistema"}. +{"has been kicked because of an affiliation change","è stato espulso a causa di un cambiamento di appartenenza"}. {"has been kicked because the room has been changed to members-only","è stato espulso per la limitazione della stanza ai soli membri"}. {"has been kicked","è stata/o espulsa/o"}. -{" has set the subject to: "," ha modificato l'oggetto in: "}. -{"Host","Host"}. +{"Hash of the vCard-temp avatar of this room","Hash dell'avatar vCard-temp di questa stanza"}. +{"Hat title","Titolo del Cappello"}. +{"Hat URI","URI Cappello"}. +{"Hats limit exceeded","Limite di cappelli superato"}. +{"Host unknown","Host sconosciuto"}. +{"HTTP File Upload","Caricamento file HTTP"}. +{"Idle connection","Connessione inattiva"}. {"If you don't see the CAPTCHA image here, visit the web page.","Se qui non vedi l'immagine CAPTCHA, visita la pagina web."}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Se si vogliono specificare differenti porte, password, codifiche per i server IRC, si riempia questo elenco con valori nel formato '{\"server IRC\", \"codifica\", porta, \"password\"}'. Per default questo servizio utilizza la codifica \"~s\", la porta ~p, la password vuota."}. {"Import Directory","Importare una directory"}. {"Import File","Importare un file"}. {"Import user data from jabberd14 spool file:","Importare i dati utente da file di spool di jabberd14:"}. @@ -136,41 +184,48 @@ {"Import users data from jabberd14 spool directory:","Importare i dati utenti da directory di spool di jabberd14:"}. {"Import Users from Dir at ","Importare utenti dalla directory "}. {"Import Users From jabberd14 Spool Files","Importare utenti da file di spool di jabberd14"}. +{"Improper domain part of 'from' attribute","Parte del dominio non corretta dell'attributo 'da'"}. {"Improper message type","Tipo di messaggio non corretto"}. +{"Incorrect CAPTCHA submit","Invio CAPTCHA errato"}. +{"Incorrect data form","Modulo dati errato"}. {"Incorrect password","Password non esatta"}. -{"Invalid affiliation: ~s","Affiliazione non valida: ~s"}. -{"Invalid role: ~s","Ruolo non valido: ~s"}. +{"Incorrect value of 'action' attribute","Valore errato dell'attributo 'azione'"}. +{"Incorrect value of 'action' in data form","Valore errato di 'azione' nel modulo dati"}. +{"Incorrect value of 'path' in data form","Valore errato di 'percorso' nel modulo dati"}. +{"Installed Modules:","Moduli installati:"}. +{"Install","Installare"}. +{"Insufficient privilege","Privilegio insufficiente"}. +{"Internal server error","Errore interno del server"}. +{"Invalid 'from' attribute in forwarded message","Attributo 'da' non valido nel messaggio inoltrato"}. +{"Invalid node name","Nome del nodo non valido"}. +{"Invalid 'previd' value","Valore 'previd' non valido"}. +{"Invitations are not allowed in this conference","Non sono ammessi inviti a questa conferenza"}. {"IP addresses","Indirizzi IP"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","Canale IRC (senza il # iniziale)"}. -{"IRC server","Server IRC"}. -{"IRC settings","Impostazioni IRC"}. -{"IRC Transport","Transport IRC"}. -{"IRC username","Nome utente IRC"}. -{"IRC Username","Nome utente IRC"}. {"is now known as","è ora conosciuta/o come"}. -{"It is not allowed to send private messages","Non è consentito l'invio di messaggi privati"}. +{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Non è consentito inviare messaggi di errore alla stanza. Il partecipante (~s) ha inviato un messaggio di errore (~s) ed è stato espulso dalla stanza"}. {"It is not allowed to send private messages of type \"groupchat\"","Non è consentito l'invio di messaggi privati di tipo \"groupchat\""}. {"It is not allowed to send private messages to the conference","Non è consentito l'invio di messaggi privati alla conferenza"}. -{"Jabber Account Registration","Registrazione account Jabber"}. {"Jabber ID","Jabber ID (Jabber ID)"}. -{"Jabber ID ~s is invalid","Il Jabber ID ~s non è valido"}. {"January","Gennaio"}. -{"Join IRC channel","Entra nel canale IRC"}. +{"JID normalization denied by service policy","Normalizzazione JID negata dalla politica del servizio"}. +{"JID normalization failed","La normalizzazione JID non è riuscita"}. +{"Joined MIX channels of ~ts","Canali MIX uniti di ~ts"}. +{"Joined MIX channels:","Canali MIX uniti:"}. {"joins the room","entra nella stanza"}. -{"Join the IRC channel here.","Entra nel canale IRC qui."}. -{"Join the IRC channel in this Jabber ID: ~s","Entra nel canale IRC in questo ID Jabber: ~s"}. {"July","Luglio"}. {"June","Giugno"}. +{"Just created","Appena creato"}. {"Last Activity","Ultima attività"}. {"Last login","Ultimo accesso"}. +{"Last message","Ultimo messaggio"}. {"Last month","Ultimo mese"}. {"Last year","Ultimo anno"}. +{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","I bit meno significativi dell'hash di testo SHA-256 devono corrispondere all'etichetta esadecimale"}. {"leaves the room","esce dalla stanza"}. -{"Listened Ports at ","Porte in ascolto su "}. -{"Listened Ports","Porte in ascolto"}. -{"List of modules to start","Elenco dei moduli da avviare"}. -{"Low level update script","Script di aggiornamento di basso livello"}. +{"List of users with hats","Elenco degli utenti con cappelli"}. +{"List users with hats","Elenca gli utenti con cappelli"}. +{"Logged Out","Disconnesso"}. +{"Logging","Registrazione"}. {"Make participants list public","Rendere pubblica la lista dei partecipanti"}. {"Make room CAPTCHA protected","Rendere la stanza protetta da CAPTCHA"}. {"Make room members-only","Rendere la stanza riservata ai membri"}. @@ -178,233 +233,393 @@ {"Make room password protected","Rendere la stanza protetta da password"}. {"Make room persistent","Rendere la stanza persistente"}. {"Make room public searchable","Rendere la sala visibile al pubblico"}. +{"Malformed username","Nome utente malformato"}. +{"MAM preference modification denied by service policy","Modifica delle preferenze MAM negata dalla policy del servizio"}. {"March","Marzo"}. -{"Maximum Number of Occupants","Numero massimo di occupanti"}. -{"Max # of items to persist","Numero massimo di elementi da conservare persistentemente"}. +{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","Numero massimo di elementi da persistere o `max` per nessun limite specifico diverso da quello massimo imposto dal server"}. {"Max payload size in bytes","Dimensione massima del contenuto del messaggio in byte"}. +{"Maximum file size","Dimensione massima del file"}. +{"Maximum Number of History Messages Returned by Room","Numero Massimo di Messaggi di Cronologia Restituiti dalla Stanza"}. +{"Maximum number of items to persist","Numero massimo di elementi da persistere"}. +{"Maximum Number of Occupants","Numero massimo di occupanti"}. {"May","Maggio"}. {"Membership is required to enter this room","Per entrare in questa stanza è necessario essere membro"}. -{"Members:","Membri:"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Memorizza la password, o scrivila su un foglio di carta da conservare in un luogo sicuro. Jabber non prevede una modalità automatica per il recupero di una password dimenticata."}. -{"Memory","Memoria"}. +{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Memorizza la tua password, oppure scrivila su un foglio riposto in un luogo sicuro. In XMPP non esiste un modo automatico per recuperare la password se la dimentichi."}. +{"Mere Availability in XMPP (No Show Value)","Mera disponibilità in XMPP (Nessun Valore Mostrato)"}. {"Message body","Corpo del messaggio"}. +{"Message not found in forwarded payload","Messaggio non trovato nel payload inoltrato"}. +{"Messages from strangers are rejected","I messaggi provenienti da sconosciuti vengono rifiutati"}. +{"Messages of type headline","Messaggi di tipo headline"}. +{"Messages of type normal","Messaggi di tipo normale"}. {"Middle Name","Altro nome"}. {"Minimum interval between voice requests (in seconds)","Intervallo minimo fra due richieste di parola (in secondi)"}. {"Moderator privileges required","Necessari i privilegi di moderatore"}. -{"moderators only","moderatori soltanto"}. -{"Modified modules","Moduli modificati"}. -{"Module","Modulo"}. -{"Modules","Moduli"}. +{"Moderator","Moderatore/Moderatrice"}. +{"Moderators Only","Solo i Moderatori"}. +{"Module failed to handle the query","Il modulo non è riuscito a gestire la query"}. {"Monday","Lunedì"}. -{"Name:","Nome:"}. +{"Multicast","Multicast"}. +{"Multiple elements are not allowed by RFC6121","Più elementi non sono consentiti da RFC6121"}. +{"Multi-User Chat","Chat Multiutente"}. {"Name","Nome"}. +{"Natural Language for Room Discussions","Linguaggio Naturale per le Discussioni in Sala"}. +{"Natural-Language Room Name","Nome della Stanza in Linguaggio Naturale"}. +{"Neither 'jid' nor 'nick' attribute found","Né l'attributo 'jid' né quello 'nick' sono stati trovati"}. +{"Neither 'role' nor 'affiliation' attribute found","Non sono stati trovati né gli attributi 'ruolo' né 'affiliazione'"}. {"Never","Mai"}. {"New Password:","Nuova password:"}. -{"Nickname","Nickname"}. +{"Nickname can't be empty","Il soprannome non può essere vuoto"}. {"Nickname Registration at ","Registrazione di un nickname su "}. {"Nickname ~s does not exist in the room","Il nickname ~s non esiste nella stanza"}. -{"nobody","nessuno"}. +{"Nickname","Soprannome"}. +{"No address elements found","Nessun elemento dell'indirizzo trovato"}. +{"No addresses element found","Nessun elemento degli indirizzi trovato"}. +{"No 'affiliation' attribute found","Nessun attributo 'affiliazione' trovato"}. +{"No available resource found","Nessuna risorsa disponibile trovata"}. {"No body provided for announce message","Nessun corpo fornito per il messaggio di annuncio"}. +{"No child elements found","Non sono stati trovati elementi figlio"}. +{"No data form found","Nessun modulo dati trovato"}. {"No Data","Nessuna informazione"}. -{"Node ID","ID del nodo"}. -{"Node not found","Nodo non trovato"}. -{"Nodes","Nodi"}. +{"No features available","Nessuna funzionalità disponibile"}. +{"No element found","Nessun elemento trovato"}. +{"No hook has processed this command","Nessun hook ha elaborato questo comando"}. +{"No info about last activity found","Nessuna informazione sull'ultima attività trovata"}. +{"No 'item' element found","Nessun elemento 'item' trovato"}. +{"No items found in this query","Nessun elemento trovato in questa query"}. {"No limit","Nessun limite"}. -{"None","Nessuno"}. -{"No resource provided","Nessuna risorsa fornita"}. +{"No module is handling this query","Nessun modulo gestisce questa query"}. +{"No node specified","Nessun nodo specificato"}. +{"No 'password' found in data form","Nessuna 'password' trovata nel modulo dati"}. +{"No 'password' found in this query","Nessuna \"password\" trovata in questa query"}. +{"No 'path' found in data form","Nessun 'percorso' trovato nel modulo dati"}. +{"No pending subscriptions found","Nessuna sottoscrizione in attesa trovata"}. +{"No privacy list with this name found","Nessun elenco di privacy con questo nome trovato"}. +{"No private data found in this query","Non sono stati trovati dati privati in questa query"}. +{"No running node found","Nessun nodo in esecuzione trovato"}. +{"No services available","Nessun servizio disponibile"}. +{"No statistics found for this item","Nessuna statistica trovata per questa voce"}. +{"No 'to' attribute found in the invitation","Nessun attributo 'a' trovato nell'invito"}. +{"Nobody","Nessuno"}. +{"Node already exists","Il nodo esiste già"}. +{"Node ID","ID del Nodo"}. +{"Node index not found","Indice del nodo non trovato"}. +{"Node not found","Nodo non trovato"}. +{"Node ~p","Nodo ~p"}. +{"Node","Nodo"}. +{"Nodeprep has failed","Nodeprep non è riuscito"}. +{"Nodes","Nodi"}. +{"None","Niente"}. +{"Not allowed","Non consentito"}. {"Not Found","Non trovato"}. +{"Not subscribed","Non sottoscritto"}. {"Notify subscribers when items are removed from the node","Notificare gli iscritti quando sono eliminati degli elementi dal nodo"}. {"Notify subscribers when the node configuration changes","Notificare gli iscritti quando la configurazione del nodo cambia"}. {"Notify subscribers when the node is deleted","Notificare gli iscritti quando il nodo è cancellato"}. {"November","Novembre"}. +{"Number of answers required","Numero di risposte richieste"}. {"Number of occupants","Numero di presenti"}. +{"Number of Offline Messages","Numero di messaggi offline"}. {"Number of online users","Numero di utenti online"}. {"Number of registered users","Numero di utenti registrati"}. +{"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","Numero di secondi dopo i quali eliminare automaticamente gli elementi o `max` per nessun limite specifico diverso da quello massimo imposto dal server"}. +{"Occupants are allowed to invite others","Gli occupanti possono invitare altri"}. +{"Occupants are allowed to query others","Gli occupanti possono interrogare gli altri"}. +{"Occupants May Change the Subject","Gli Occupanti Possono Cambiare il Soggetto"}. {"October","Ottobre"}. -{"Offline Messages:","Messaggi offline:"}. -{"Offline Messages","Messaggi offline"}. {"OK","OK"}. {"Old Password:","Vecchia password:"}. -{"Online","Online"}. -{"Online Users:","Utenti connessi:"}. {"Online Users","Utenti online"}. +{"Online","Online"}. +{"Only collection node owners may associate leaf nodes with the collection","Solo i proprietari dei nodi di raccolta possono associare i nodi foglia alla collezione"}. {"Only deliver notifications to available users","Inviare le notifiche solamente agli utenti disponibili"}. +{"Only or tags are allowed","Sono consentiti solo i tag o "}. +{"Only element is allowed in this query","In questa query è consentito solo l'elemento "}. +{"Only members may query archives of this room","Solo i membri possono interrogare gli archivi di questa stanza"}. {"Only moderators and participants are allowed to change the subject in this room","La modifica dell'oggetto di questa stanza è consentita soltanto ai moderatori e ai partecipanti"}. {"Only moderators are allowed to change the subject in this room","La modifica dell'oggetto di questa stanza è consentita soltanto ai moderatori"}. +{"Only moderators are allowed to retract messages","Solo i moderatori possono ritirare i messaggi"}. {"Only moderators can approve voice requests","Soltanto i moderatori possono approvare richieste di parola"}. {"Only occupants are allowed to send messages to the conference","L'invio di messaggi alla conferenza è consentito soltanto ai presenti"}. {"Only occupants are allowed to send queries to the conference","L'invio di query alla conferenza è consentito ai soli presenti"}. +{"Only publishers may publish","Solo gli editori possono pubblicare"}. {"Only service administrators are allowed to send service messages","L'invio di messaggi di servizio è consentito solamente agli amministratori del servizio"}. -{"Options","Opzioni"}. +{"Only those on a whitelist may associate leaf nodes with the collection","Solo chi fa parte di una whitelist può associare i nodi foglia alla collezione"}. +{"Only those on a whitelist may subscribe and retrieve items","Solo chi fa parte di una whitelist può sottoscrivere e recuperare le voci"}. {"Organization Name","Nome dell'organizzazione"}. {"Organization Unit","Unità dell'organizzazione"}. -{"Outgoing s2s Connections:","Connessioni s2s in uscita:"}. +{"Other Modules Available:","Altri Moduli Disponibili:"}. {"Outgoing s2s Connections","Connessioni s2s in uscita"}. {"Owner privileges required","Necessari i privilegi di proprietario"}. -{"Packet","Pacchetto"}. -{"Password ~b","Password ~b"}. -{"Password:","Password:"}. -{"Password","Password"}. -{"Password Verification:","Verifica della password:"}. +{"Packet relay is denied by service policy","Il relay dei pacchetti è negato dalla politica di servizio"}. +{"Participant ID","ID Partecipante"}. +{"Participant","Partecipante"}. {"Password Verification","Verifica della password"}. +{"Password Verification:","Verifica della password:"}. +{"Password","Password"}. +{"Password:","Password:"}. {"Path to Dir","Percorso della directory"}. {"Path to File","Percorso del file"}. -{"Pending","Pendente"}. -{"Period: ","Periodo:"}. +{"Payload semantic type information","Informazioni sul tipo semantico del payload"}. +{"Period: ","Periodo: "}. {"Persist items to storage","Conservazione persistente degli elementi"}. +{"Persistent","Persistente"}. +{"Ping query is incorrect","La query ping non è corretta"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","N.B.: Queste opzioni comportano il salvataggio solamente del database interno Mnesia. Se si sta utilizzando il modulo ODBC, è necessario salvare anche il proprio database SQL separatamente."}. {"Please, wait for a while before sending new voice request","Attendi qualche istante prima di inviare una nuova richiesta di parola"}. {"Pong","Pong"}. -{"Port ~b","Porta ~b"}. -{"Port","Porta"}. +{"Possessing 'ask' attribute is not allowed by RFC6121","Il possesso dell'attributo 'chiedi' non è consentito da RFC6121"}. {"Present real Jabber IDs to","Rendere visibile il Jabber ID reale a"}. +{"Previous session not found","Sessione precedente non trovata"}. +{"Previous session PID has been killed","Il PID della sessione precedente è stato ucciso"}. +{"Previous session PID has exited","Il PID della sessione precedente è terminato"}. +{"Previous session PID is dead","Il PID della sessione precedente è morto"}. +{"Previous session timed out","La sessione precedente è scaduta"}. {"private, ","privato, "}. -{"Protocol","Protocollo"}. +{"Public","Pubblico"}. +{"Publish model","Pubblica modello"}. {"Publish-Subscribe","Pubblicazione-Iscrizione"}. {"PubSub subscriber request","Richiesta di iscrizione per PubSub"}. {"Purge all items when the relevant publisher goes offline","Cancella tutti gli elementi quando chi li ha pubblicati non è più online"}. +{"Push record not found","Record push non trovato"}. {"Queries to the conference members are not allowed in this room","In questa stanza non sono consentite query ai membri della conferenza"}. +{"Query to another users is forbidden","La richiesta ad altri utenti è vietata"}. {"RAM and disc copy","Copia in memoria (RAM) e su disco"}. {"RAM copy","Copia in memoria (RAM)"}. -{"Raw","Grezzo"}. {"Really delete message of the day?","Si conferma l'eliminazione del messaggio del giorno (MOTD)?"}. +{"Receive notification from all descendent nodes","Ricevere una notifica da tutti i nodi discendenti"}. +{"Receive notification from direct child nodes only","Ricevere notifiche solo dai nodi figli diretti"}. +{"Receive notification of new items only","Ricevere una notifica solo per le nuove voce"}. +{"Receive notification of new nodes only","Ricevere solo la notifica di nuovi nodi"}. {"Recipient is not in the conference room","Il destinatario non è nella stanza per conferenze"}. -{"Register a Jabber account","Registra un account Jabber"}. -{"Registered Users:","Utenti registrati:"}. -{"Registered Users","Utenti registrati"}. +{"Register an XMPP account","Registra un account XMPP"}. {"Register","Registra"}. -{"Registration in mod_irc for ","Registrazione in mod_irc per "}. {"Remote copy","Copia remota"}. -{"Remove All Offline Messages","Eliminare tutti i messaggi offline"}. -{"Remove","Eliminare"}. -{"Remove User","Eliminare l'utente"}. +{"Remove a hat from a user","Rimuovere un cappello da un utente"}. +{"Remove User","Rimuovere l'utente"}. {"Replaced by new connection","Sostituito da una nuova connessione"}. +{"Request has timed out","La richiesta è scaduta"}. +{"Request is ignored","La richiesta viene ignorata"}. +{"Requested role","Ruolo richiesto"}. {"Resources","Risorse"}. -{"Restart","Riavviare"}. {"Restart Service","Riavviare il servizio"}. {"Restore Backup from File at ","Recuperare il salvataggio dal file "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Recuperare un salvataggio binario dopo il prossimo riavvio di ejabberd (necessita di meno memoria):"}. {"Restore binary backup immediately:","Recuperare un salvataggio binario adesso:"}. {"Restore plain text backup immediately:","Recuperare un salvataggio come semplice testo adesso:"}. {"Restore","Recuperare"}. +{"Roles and Affiliations that May Retrieve Member List","Ruoli e Affiliazioni che Possono Recuperare L'elenco dei Membri"}. +{"Roles for which Presence is Broadcasted","Ruoli per i quali viene Trasmessa la Presenza"}. +{"Roles that May Send Private Messages","Ruoli che possono inviare messaggi privati"}. {"Room Configuration","Configurazione della stanza"}. {"Room creation is denied by service policy","La creazione di stanze è impedita dalle politiche del servizio"}. {"Room description","Descrizione della stanza"}. {"Room Occupants","Presenti nella stanza"}. +{"Room terminates","La stanza termina"}. {"Room title","Titolo della stanza"}. {"Roster groups allowed to subscribe","Gruppi roster abilitati alla registrazione"}. -{"Roster","Lista dei contatti"}. -{"Roster of ","Lista dei contatti di "}. {"Roster size","Dimensione della lista dei contatti"}. -{"RPC Call Error","Errore di chiamata RPC"}. {"Running Nodes","Nodi attivi"}. -{"~s access rule configuration","Configurazione delle regole di accesso per ~s"}. +{"~s invites you to the room ~s","~s ti invita nella stanza ~s"}. {"Saturday","Sabato"}. -{"Script check","Verifica dello script"}. +{"Search from the date","Cerca dalla data"}. {"Search Results for ","Risultati della ricerca per "}. +{"Search the text","Cerca nel testo"}. +{"Search until the date","Cerca fino alla data"}. {"Search users in ","Cercare utenti in "}. -{"Send announcement to all online users","Inviare l'annuncio a tutti gli utenti online"}. {"Send announcement to all online users on all hosts","Inviare l'annuncio a tutti gli utenti online su tutti gli host"}. -{"Send announcement to all users","Inviare l'annuncio a tutti gli utenti"}. +{"Send announcement to all online users","Inviare l'annuncio a tutti gli utenti online"}. {"Send announcement to all users on all hosts","Inviare l'annuncio a tutti gli utenti su tutti gli host"}. +{"Send announcement to all users","Inviare l'annuncio a tutti gli utenti"}. {"September","Settembre"}. -{"Server ~b","Server ~b"}. {"Server:","Server:"}. +{"Service list retrieval timed out","Il recupero dell'elenco dei servizi è scaduto"}. +{"Session state copying timed out","La copia dello stato della sessione è scaduta"}. {"Set message of the day and send to online users","Impostare il messaggio del giorno (MOTD) ed inviarlo agli utenti online"}. {"Set message of the day on all hosts and send to online users","Impostare il messaggio del giorno (MOTD) su tutti gli host e inviarlo agli utenti online"}. {"Shared Roster Groups","Gruppi di liste di contatti comuni"}. {"Show Integral Table","Mostrare la tabella integrale"}. +{"Show Occupants Join/Leave","Mostra gli occupanti che si uniscono/escono"}. {"Show Ordinary Table","Mostrare la tabella normale"}. {"Shut Down Service","Terminare il servizio"}. -{"~s invites you to the room ~s","~s ti invita nella stanza ~s"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Alcuni client Jabber possono conservare la password nel tuo computer. Utilizza tale funzione soltanto se ritieni che il tuo computer sia sicuro."}. +{"SOCKS5 Bytestreams","SOCKS5 flussi di byte"}. +{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Alcuni client XMPP possono memorizzare la tua password nel computer, ma dovresti farlo solo sul tuo computer personale per motivi di sicurezza."}. +{"Sources Specs:","Sorgenti Specifiche:"}. {"Specify the access model","Specificare il modello di accesso"}. {"Specify the event message type","Specificare il tipo di messaggio di evento"}. {"Specify the publisher model","Definire il modello di pubblicazione"}. -{"~s's Offline Messages Queue","Coda di ~s messaggi offline"}. -{"Start","Avviare"}. -{"Start Modules at ","Avviare moduli su "}. -{"Start Modules","Avviare moduli"}. -{"Statistics of ~p","Statistiche di ~p"}. -{"Statistics","Statistiche"}. -{"Stop","Arrestare"}. -{"Stop Modules","Arrestare moduli"}. -{"Stop Modules at ","Arrestare moduli su "}. +{"Stanza id is not valid","L'id della stanza non è valido"}. +{"Stanza ID","Stanza ID"}. +{"Statically specify a replyto of the node owner(s)","Specificare staticamente una risposta del proprietario(i) del nodo"}. {"Stopped Nodes","Nodi arrestati"}. -{"Storage Type","Tipo di conservazione"}. {"Store binary backup:","Conservare un salvataggio binario:"}. {"Store plain text backup:","Conservare un salvataggio come semplice testo:"}. +{"Stream management is already enabled","La gestione del flusso è già abilitata"}. +{"Stream management is not enabled","La gestione del flusso non è abilitata"}. {"Subject","Oggetto"}. -{"Submit","Inviare"}. {"Submitted","Inviato"}. {"Subscriber Address","Indirizzo dell'iscritta/o"}. -{"Subscription","Iscrizione"}. +{"Subscribers may publish","I sottoscrittori possono pubblicare"}. +{"Subscription requests must be approved and only subscribers may retrieve items","Le richieste di sottoscrizione devono essere approvate e solo i sottoscrittori possono recuperare le voci"}. +{"Subscriptions are not allowed","Le sottoscrizioni non sono consentite"}. {"Sunday","Domenica"}. +{"Text associated with a picture","Testo associato a un'immagine"}. +{"Text associated with a sound","Testo associato a un suono"}. +{"Text associated with a video","Testo associato a un video"}. +{"Text associated with speech","Testo associato al parlato"}. {"That nickname is already in use by another occupant","Il nickname è già in uso all'interno della conferenza"}. {"That nickname is registered by another person","Questo nickname è registrato da un'altra persona"}. +{"The account already exists","L'account esiste già"}. +{"The account was not unregistered","L'account non era non registrato"}. +{"The body text of the last received message","Il corpo del testo dell'ultimo messaggio ricevuto"}. {"The CAPTCHA is valid.","Il CAPTCHA è valido."}. {"The CAPTCHA verification has failed","La verifica del CAPTCHA ha avuto esito negativo"}. +{"The captcha you entered is wrong","Il captcha che hai inserito è sbagliato"}. +{"The child nodes (leaf or collection) associated with a collection","I nodi figlio (foglia o raccolta) associati a una raccolta"}. {"The collections with which a node is affiliated","Le collezioni a cui è affiliato un nodo"}. -{"the password is","la password è"}. +{"The DateTime at which a leased subscription will end or has ended","Il DateTime in cui un abbonamento in leasing terminerà o è terminato"}. +{"The datetime when the node was created","La dataora in cui è stato creato il nodo"}. +{"The default language of the node","La lingua predefinita del nodo"}. +{"The feature requested is not supported by the conference","La funzionalità richiesta non è supportata dalla conferenza"}. +{"The JID of the node creator","Il JID del creatore del nodo"}. +{"The JIDs of those to contact with questions","I JID di coloro da contattare per domande"}. +{"The JIDs of those with an affiliation of owner","I JID di coloro che hanno un'affiliazione di proprietario"}. +{"The JIDs of those with an affiliation of publisher","I JID di coloro che hanno un'affiliazione di editore"}. +{"The list of all online users","L'elenco di tutti gli utenti online"}. +{"The list of all users","L'elenco di tutti gli utenti"}. +{"The list of JIDs that may associate leaf nodes with a collection","L'elenco dei JID che possono associare i nodi foglia a una collezione"}. +{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","Il numero massimo di nodi figli che possono essere associati a una collezione, o `max` per non avere un limite specifico, se non quello imposto dal server"}. +{"The minimum number of milliseconds between sending any two notification digests","Il numero minimo di millisecondi tra l'invio di due digest di notifica qualsiasi"}. +{"The name of the node","Il nome del nodo"}. +{"The node is a collection node","Il nodo è un nodo di raccolta"}. +{"The node is a leaf node (default)","Il nodo è un nodo foglia (predefinito)"}. +{"The NodeID of the relevant node","Il NodeID del nodo rilevante"}. +{"The number of pending incoming presence subscription requests","Il numero di richieste di sottoscrizione di presenza in entrata in sospeso"}. +{"The number of subscribers to the node","Il numero di abbonati al nodo"}. +{"The number of unread or undelivered messages","Il numero di messaggi non letti o non consegnati"}. +{"The password contains unacceptable characters","La password contiene caratteri non accettabili"}. {"The password is too weak","La password è troppo debole"}. -{"The password of your Jabber account was successfully changed.","Il cambio di password del tuo account Jabber è andato a buon fine."}. -{"There was an error changing the password: ","Si è verificato un errore nel cambio di password: "}. +{"the password is","la password è"}. +{"The password of your XMPP account was successfully changed.","La password del tuo account XMPP è stata modificata con successo."}. +{"The password was not changed","La password non è stata modificata"}. +{"The passwords are different","Le password sono diverse"}. +{"The presence states for which an entity wants to receive notifications","Gli stati di presenza per i quali un'entità desidera ricevere le notifiche"}. +{"The query is only allowed from local users","La query è consentita solo da utenti locali"}. +{"The query must not contain elements","La query non deve contenere elementi "}. +{"The room subject can be modified by participants","L'oggetto della stanza potrà essere modificato dai partecipanti"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","Le informazioni sul tipo semantico dei dati nel nodo, solitamente specificate dallo spazio dei nomi del payload (se presente)"}. +{"The sender of the last received message","Il mittente dell'ultimo messaggio ricevuto"}. +{"The stanza MUST contain only one element, one element, or one element","La stanza DEVE contenere solo un elemento , un elemento o un elemento "}. +{"The subscription identifier associated with the subscription request","L'identificatore di sottoscrizione associato alla richiesta di sottoscrizione"}. +{"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","L'URL di una trasformazione XSL che può essere applicata ai payload per generare un elemento del corpo del messaggio appropriato."}. +{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","L'URL di una trasformazione XSL che può essere applicata al formato del payload per generare un risultato valido di Data Forms che il client può visualizzare utilizzando un motore di rendering Data Forms generico"}. +{"There was an error changing the password: ","Si è verificato un errore durante la modifica della password: "}. {"There was an error creating the account: ","Si è verificato un errore nella creazione dell'account: "}. {"There was an error deleting the account: ","Si è verificato un errore nella cancellazione dell'account: "}. -{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Non fa differenza fra minuscolo e maiuscolo: macbeth, MacBeth e Macbeth si equivalgono."}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Questa pagina consente di creare un account Jabber in questo server Jabber. Il tuo JID (Jabber IDentifier) avrà la forma: nome_utente@server. Leggi attentamente le istruzioni per compilare i campi correttamente."}. -{"This page allows to unregister a Jabber account in this Jabber server.","Questa pagina consente di eliminare un account Jabber da questo server Jabber."}. +{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Questo è insensibile alle maiuscole: macbeth è lo stesso che MacBeth e Macbeth."}. +{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Questa pagina consente di registrare un account XMPP in questo server XMPP. Il tuo JID (Jabber ID) sarà nel formato: nomeutente@server. Si prega di leggere attentamente le istruzioni per compilare correttamente i campi."}. +{"This page allows to unregister an XMPP account in this XMPP server.","Questa pagina consente di annullare la registrazione di un account XMPP in questo server XMPP."}. +{"This room is not anonymous","Questa stanza non è anonima"}. +{"This service can not process the address: ~s","Questo servizio non può elaborare l'indirizzo: ~s"}. {"Thursday","Giovedì"}. {"Time delay","Ritardo"}. -{"Time","Ora"}. -{"To","A"}. +{"Timed out waiting for stream resumption","Timed out in attesa della ripresa dello stream"}. +{"To register, visit ~s","Per registrarsi, visita ~s"}. +{"To ~ts","A ~ts"}. +{"Token TTL","Gettone TTL"}. +{"Too many active bytestreams","Troppi bytestream attivi"}. {"Too many CAPTCHA requests","Troppe richieste CAPTCHA"}. -{"To ~s","A ~s"}. +{"Too many child elements","Troppi elementi figlio"}. +{"Too many elements","Troppi elementi "}. +{"Too many elements","Troppi elementi "}. +{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Troppe (~p) autenticazioni non riuscite da questo indirizzo IP (~s). L'indirizzo verrà sbloccato alle ~s UTC"}. +{"Too many receiver fields were specified","Sono stati specificati troppi campi del ricevitore"}. +{"Too many unacked stanzas","Troppe stanze non riconosciute"}. +{"Too many users in this conference","Troppi utenti in questa conferenza"}. {"Traffic rate limit is exceeded","Limite di traffico superato"}. -{"Transactions Aborted:","Transazioni abortite:"}. -{"Transactions Committed:","Transazioni avvenute:"}. -{"Transactions Logged:","Transazioni con log:"}. -{"Transactions Restarted:","Transazioni riavviate:"}. +{"~ts's MAM Archive","Archivio MAM di ~ts"}. +{"~ts's Offline Messages Queue","La Coda dei Messaggi Offline di ~ts"}. {"Tuesday","Martedì"}. {"Unable to generate a CAPTCHA","Impossibile generare un CAPTCHA"}. +{"Unable to register route on existing local domain","Impossibile registrare il percorso sul dominio locale esistente"}. {"Unauthorized","Non autorizzato"}. -{"Unregister a Jabber account","Elimina un account Jabber"}. +{"Unexpected action","Azione inaspettata"}. +{"Unexpected error condition: ~p","Condizione di errore imprevisto: ~p"}. +{"Uninstall","Disinstallare"}. +{"Unregister an XMPP account","Disregistrare un account XMPP"}. {"Unregister","Elimina"}. -{"Update","Aggiornare"}. +{"Unsupported element","Elemento non supportato"}. +{"Unsupported version","Versione non supportata"}. {"Update message of the day (don't send)","Aggiornare il messaggio del giorno (MOTD) (non inviarlo)"}. {"Update message of the day on all hosts (don't send)","Aggiornare il messaggio del giorno (MOTD) su tutti gli host (non inviarlo)"}. -{"Update plan","Piano di aggiornamento"}. -{"Update script","Script di aggiornamento"}. -{"Uptime:","Tempo dall'avvio:"}. -{"Use of STARTTLS required","Utilizzo di STARTTLS obbligatorio"}. +{"Update specs to get modules source, then install desired ones.","Aggiorna le specifiche per ottenere il sorgente dei moduli, quindi installa quelli desiderati."}. +{"Update Specs","Aggiorna Specifiche"}. +{"Updating the vCard is not supported by the vCard storage backend","L'aggiornamento della vCard non è supportato dal backend di archiviazione vCard"}. +{"Upgrade","Aggiornamento"}. +{"URL for Archived Discussion Logs","URL per i Registri delle Discussioni Archiviati"}. +{"User already exists","L'utente esiste già"}. {"User JID","JID utente"}. +{"User (jid)","Utente (jid)"}. {"User Management","Gestione degli utenti"}. +{"User not allowed to perform an IQ set on another user's vCard.","L'utente non è autorizzato a eseguire un set IQ sulla vCard di un altro utente."}. +{"User removed","Utente rimosso"}. +{"User session not found","Sessione utente non trovata"}. +{"User session terminated","Sessione utente terminata"}. +{"User ~ts","Utente ~ts"}. {"Username:","Nome utente:"}. {"Users are not allowed to register accounts so quickly","Non è consentito agli utenti registrare account così rapidamente"}. {"Users Last Activity","Ultima attività degli utenti"}. {"Users","Utenti"}. {"User","Utente"}. -{"Validate","Validare"}. +{"Value 'get' of 'type' attribute is not allowed","Il valore 'get' dell'attributo 'type' non è consentito"}. +{"Value of '~s' should be boolean","Il valore di '~s' dovrebbe essere booleano"}. +{"Value of '~s' should be datetime string","Il valore di '~s' deve essere una stringa dataora"}. +{"Value of '~s' should be integer","Il valore di '~s' dovrebbe essere un intero"}. +{"Value 'set' of 'type' attribute is not allowed","Il valore 'set' dell'attributo 'type' non è consentito"}. {"vCard User Search","Ricerca di utenti per vCard"}. +{"View joined MIX channels","Visualizza i canali MIX uniti"}. {"Virtual Hosts","Host Virtuali"}. {"Visitors are not allowed to change their nicknames in this room","Non è consentito ai visitatori cambiare il nickname in questa stanza"}. {"Visitors are not allowed to send messages to all occupants","Non è consentito ai visitatori l'invio di messaggi a tutti i presenti"}. +{"Visitor","Visitatore"}. {"Voice request","Richiesta di parola"}. {"Voice requests are disabled in this conference","In questa conferenza le richieste di parola sono escluse"}. {"Wednesday","Mercoledì"}. +{"When a new subscription is processed and whenever a subscriber comes online","Quando viene elaborato una nuova sottoscrizione e ogni volta che una sottoscrizione entra in linea"}. +{"When a new subscription is processed","Quando viene elaborata una nuova sottoscrizione"}. {"When to send the last published item","Quando inviare l'ultimo elemento pubblicato"}. -{"Whether to allow subscriptions","Consentire iscrizioni?"}. -{"You can later change your password using a Jabber client.","Potrai in seguito cambiare la password utilizzando un client Jabber."}. +{"Whether an entity wants to receive an XMPP message body in addition to the payload format","Se un'entità vuole ricevere un corpo del messaggio XMPP inoltre al formato del payload"}. +{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","Se un'entità desidera ricevere i digest (aggregazioni) di notifiche o tutte le notifiche individualmente"}. +{"Whether an entity wants to receive or disable notifications","Se un'entità desidera ricevere o disabilitare le notifiche"}. +{"Whether owners or publisher should receive replies to items","Se i proprietari o l'editore dovrebbero ricevere le risposte alle voci"}. +{"Whether the node is a leaf (default) or a collection","Se il nodo è una foglia (impostazione predefinita) o una collezione"}. +{"Whether to allow subscriptions","Se consentire le iscrizioni"}. +{"Whether to make all subscriptions temporary, based on subscriber presence","Se rendere temporanee tutte le sottoscrizioni, in base alla presenza della sottoscrizione"}. +{"Whether to notify owners about new subscribers and unsubscribes","Se notificare ai proprietari le nuove sottoscrizioni e le cancellazioni"}. +{"Who can send private messages","Chi può inviare messaggi privati"}. +{"Who may associate leaf nodes with a collection","Chi può associare i nodi foglia a una collezione"}. +{"Wrong parameters in the web formulary","Parametri errati nel formulario web"}. +{"Wrong xmlns","xmlns errati"}. +{"XMPP Account Registration","Registrazione dell'account XMPP"}. +{"XMPP Domains","Domini XMPP"}. +{"XMPP Show Value of Away","XMPP Mostra Valore di Assenza"}. +{"XMPP Show Value of Chat","XMPP Mostra il Valore della Chat"}. +{"XMPP Show Value of DND (Do Not Disturb)","XMPP Mostra Valore di DND (Non Disturbare)"}. +{"XMPP Show Value of XA (Extended Away)","XMPP Mostra Valore di XA (Extended Away)"}. +{"XMPP URI of Associated Publish-Subscribe Node","URI XMPP del Nodo di Pubblicazione-Sottoscrizione Associato"}. +{"You are being removed from the room because of a system shutdown","Stai per essere rimosso dalla stanza a causa di un arresto del sistema"}. +{"You are not allowed to send private messages","Non ti è consentito inviare messaggi privati"}. +{"You are not joined to the channel","Non si è connessi al canale"}. +{"You can later change your password using an XMPP client.","È possibile modificare successivamente la tua password utilizzando un client XMPP."}. {"You have been banned from this room","Sei stata/o bandita/o da questa stanza"}. +{"You have joined too many conferences","Hai partecipato a troppe conferenze"}. {"You must fill in field \"Nickname\" in the form","Si deve riempire il campo \"Nickname\" nel modulo"}. {"You need a client that supports x:data and CAPTCHA to register","La registrazione richiede un client che supporti x:data e CAPTCHA"}. {"You need a client that supports x:data to register the nickname","Per registrare il nickname è necessario un client che supporti x:data"}. -{"You need an x:data capable client to configure mod_irc settings","Per la configurazione del modulo IRC è necessario un client che supporti x:data"}. -{"You need an x:data capable client to configure room","Per la configurazione della stanza è necessario un client che supporti x:data"}. {"You need an x:data capable client to search","Per effettuare ricerche è necessario un client che supporti x:data"}. {"Your active privacy list has denied the routing of this stanza.","In base alla tua attuale lista privacy questa stanza è stata esclusa dalla navigazione."}. -{"Your contact offline message queue is full. The message has been discarded.","La coda dei messaggi offline del contatto è piena. Il messaggio è stato scartato"}. -{"Your Jabber account was successfully created.","La creazione del tuo account Jabber è andata a buon fine."}. -{"Your Jabber account was successfully deleted.","La cancellazione del tuo account Jabber è andata a buon fine."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","I messaggi verso ~s sono bloccati. Per sbloccarli, visitare ~s"}. +{"Your contact offline message queue is full. The message has been discarded.","La coda dei messaggi offline del contatto è piena. Il messaggio è stato scartato."}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","I messaggi verso ~s sono bloccati. Per sbloccarli, visitare ~s"}. +{"Your XMPP account was successfully registered.","Il tuo account XMPP è stato registrato con successo."}. +{"Your XMPP account was successfully unregistered.","L'account XMPP è stato disregistrato con successo."}. +{"You're not allowed to create nodes","Non ti è consentito creare nodi"}. diff --git a/priv/msgs/it.po b/priv/msgs/it.po deleted file mode 100644 index d47248c69..000000000 --- a/priv/msgs/it.po +++ /dev/null @@ -1,1956 +0,0 @@ -# Luca Brivio , 2012. -msgid "" -msgstr "" -"Project-Id-Version: 2.1.0-alpha\n" -"PO-Revision-Date: 2012-04-24 16:48+0200\n" -"Last-Translator: Luca Brivio \n" -"Language-Team: Italian \n" -"Language: it\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Italian (italiano)\n" -"X-Additional-Translator: Gabriele Stilli \n" -"X-Additional-Translator: Smart2128\n" -"X-Generator: Lokalize 1.2\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "Utilizzo di STARTTLS obbligatorio" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Nessuna risorsa fornita" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Sostituito da una nuova connessione" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "è stata/o espulsa/o" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "" -"In base alla tua attuale lista privacy questa stanza è stata esclusa dalla " -"navigazione." - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Immettere il testo visibile" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "I messaggi verso ~s sono bloccati. Per sbloccarli, visitare ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "Se qui non vedi l'immagine CAPTCHA, visita la pagina web." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "Pagina web CAPTCHA" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "Il CAPTCHA è valido." - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Utente" - -#: ejabberd_oauth.erl:256 -#, fuzzy -msgid "Server" -msgstr "Server:" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Password" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "Non autorizzato" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "Amministrazione web ejabberd" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Amministrazione" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Diritti di accesso (ACL)" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Inviato" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Formato non valido" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Inviare" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "Grezzo" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Eliminare gli elementi selezionati" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Regole di accesso" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "Configurazione delle regole di accesso per ~s" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "Host Virtuali" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Utenti" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "Utenti online" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Ultima attività degli utenti" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Periodo:" - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "Ultimo mese" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "Ultimo anno" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "Tutta l'attività" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Mostrare la tabella normale" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Mostrare la tabella integrale" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Statistiche" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "Non trovato" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Nodo non trovato" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Aggiungere nuovo" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Host" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Utenti registrati" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Aggiungere un utente" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Messaggi offline" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Ultima attività" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Mai" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Online" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Utenti registrati:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Utenti connessi:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Connessioni s2s in uscita:" - -#: ejabberd_web_admin.erl:1559 -#, fuzzy -msgid "Incoming s2s Connections:" -msgstr "Connessioni s2s in uscita:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Nessuno" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Modificare la password" - -#: ejabberd_web_admin.erl:1673 -#, fuzzy -msgid "User ~s" -msgstr "Utente " - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Risorse connesse:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Password:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Eliminare l'utente" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Nessuna informazione" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Nodi" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Nodi attivi" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Nodi arrestati" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -#, fuzzy -msgid "Node ~p" -msgstr "Nodo " - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "Database" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Salvare" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Porte in ascolto" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Aggiornare" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Riavviare" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Arrestare" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Moduli" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "Errore di chiamata RPC" - -#: ejabberd_web_admin.erl:1917 -#, fuzzy -msgid "Database Tables at ~p" -msgstr "Tabelle del database su " - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Nome" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Tipo di conservazione" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Elementi" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Memoria" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Errore" - -#: ejabberd_web_admin.erl:1955 -#, fuzzy -msgid "Backup of ~p" -msgstr "Salvataggio di " - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"N.B.: Queste opzioni comportano il salvataggio solamente del database " -"interno Mnesia. Se si sta utilizzando il modulo ODBC, è necessario salvare " -"anche il proprio database SQL separatamente." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Conservare un salvataggio binario:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "OK" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Recuperare un salvataggio binario adesso:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"Recuperare un salvataggio binario dopo il prossimo riavvio di ejabberd " -"(necessita di meno memoria):" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Conservare un salvataggio come semplice testo:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Recuperare un salvataggio come semplice testo adesso:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "Importare i dati utenti da un file PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" -"Esportare i dati di tutti gli utenti nel server in file PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "Esportare i dati degli utenti di un host in file PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Importare i dati utente da file di spool di jabberd14:" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Importare i dati utenti da directory di spool di jabberd14:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Porte in ascolto su " - -#: ejabberd_web_admin.erl:2144 -#, fuzzy -msgid "Modules at ~p" -msgstr "Moduli su " - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "Statistiche di ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Tempo dall'avvio:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "Tempo CPU:" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Transazioni avvenute:" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Transazioni abortite:" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Transazioni riavviate:" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Transazioni con log:" - -#: ejabberd_web_admin.erl:2243 -#, fuzzy -msgid "Update ~p" -msgstr "Aggiornare " - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "Piano di aggiornamento" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Moduli modificati" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Script di aggiornamento" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Script di aggiornamento di basso livello" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Verifica dello script" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Porta" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Protocollo" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Modulo" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Opzioni" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Avviare" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Comandi" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "Si conferma l'eliminazione del messaggio del giorno (MOTD)?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Oggetto" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Corpo del messaggio" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "Nessun corpo fornito per il messaggio di annuncio" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Annunci" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Inviare l'annuncio a tutti gli utenti" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Inviare l'annuncio a tutti gli utenti su tutti gli host" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Inviare l'annuncio a tutti gli utenti online" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "Inviare l'annuncio a tutti gli utenti online su tutti gli host" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "" -"Impostare il messaggio del giorno (MOTD) ed inviarlo agli utenti online" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" -"Impostare il messaggio del giorno (MOTD) su tutti gli host e inviarlo agli " -"utenti online" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Aggiornare il messaggio del giorno (MOTD) (non inviarlo)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "" -"Aggiornare il messaggio del giorno (MOTD) su tutti gli host (non inviarlo)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Eliminare il messaggio del giorno (MOTD)" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Eliminare il messaggio del giorno (MOTD) su tutti gli host" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Configurazione" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Avviare moduli" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Arrestare moduli" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Recuperare" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Trascrivere su file di testo" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Importare un file" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Importare una directory" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Riavviare il servizio" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Terminare il servizio" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Eliminare l'utente" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Terminare la sessione dell'utente" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Ottenere la password dell'utente" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Cambiare la password dell'utente" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "Ottenere la data di ultimo accesso dell'utente" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Ottenere le statistiche dell'utente" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Ottenere il numero di utenti registrati" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Ottenere il numero di utenti online" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Gestione degli utenti" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Tutti gli utenti" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Connessioni s2s in uscita" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Gestione dei salvataggi" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Importare utenti da file di spool di jabberd14" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "A ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "Da ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Configurazione delle tabelle del database su " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Selezionare una modalità di conservazione delle tabelle" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Copia su disco soltanto" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "Copia in memoria (RAM) e su disco" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "Copia in memoria (RAM)" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "Copia remota" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Arrestare moduli su " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Selezionare i moduli da arrestare" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Avviare moduli su " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Immettere un elenco di {Modulo, [Opzioni]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Elenco dei moduli da avviare" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Salvataggio sul file " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Immettere il percorso del file di salvataggio" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Percorso del file" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Recuperare il salvataggio dal file " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Trascrivere il salvataggio sul file di testo " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Immettere il percorso del file di testo" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Importare un utente dal file " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Immettere il percorso del file di spool di jabberd14" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Importare utenti dalla directory " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Immettere il percorso della directory di spool di jabberd14" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Percorso della directory" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "Ritardo" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Configurazione dei diritti di accesso (ACL)" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Diritti di accesso (ACL)" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Configurazione dell'accesso" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Regole di accesso" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID (Jabber ID)" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Verifica della password" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Numero di utenti registrati" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Numero di utenti online" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Ultimo accesso" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "Dimensione della lista dei contatti" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "Indirizzi IP" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Risorse" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Amministrazione di " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Azione sull'utente" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Modificare le proprietà" - -#: mod_fail2ban.erl:95 -msgid "" -"Too many (~p) failed authentications from this IP address (~s). The address " -"will be unblocked at ~s UTC" -msgstr "" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "" - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "" - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "Accesso impedito dalle politiche del servizio" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "Transport IRC" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "Modulo IRC per ejabberd" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "" -"Per la configurazione del modulo IRC è necessario un client che supporti x:" -"data" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Registrazione in mod_irc per " - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Immettere il nome utente, le codifiche, le porte e le password che si " -"desidera utilizzare per la connessione ai server IRC" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "Nome utente IRC" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Se si vogliono specificare differenti porte, password, codifiche per i " -"server IRC, si riempia questo elenco con valori nel formato '{\"server IRC" -"\", \"codifica\", porta, \"password\"}'. Per default questo servizio " -"utilizza la codifica \"~s\", la porta ~p, la password vuota." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Esempio: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"segreto\"}, {\"vendetta." -"fef.net\", \"iso8859-1\", 7000}, {\"irc.serverdiprova.net\", \"utf-8\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Parametri delle connessioni" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "Entra nel canale IRC" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "Canale IRC (senza il # iniziale)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "Server IRC" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Entra nel canale IRC qui." - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Entra nel canale IRC in questo ID Jabber: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "Impostazioni IRC" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Immettere il nome utente e le codifiche che si desidera utilizzare per la " -"connessione ai server IRC. Premere \"Avanti\" per vedere i successivi campi " -"da compilare. Premere \"Fatto\" per salvare le impostazioni." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "Nome utente IRC" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Password ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Porta ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Codifica per il server ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Server ~b" - -#: mod_mam.erl:541 -#, fuzzy -msgid "Only members may query archives of this room" -msgstr "" -"La modifica dell'oggetto di questa stanza è consentita soltanto ai moderatori" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "" -"L'invio di messaggi di servizio è consentito solamente agli amministratori " -"del servizio" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "La creazione di stanze è impedita dalle politiche del servizio" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "La stanza per conferenze non esiste" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Stanze" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "Per registrare il nickname è necessario un client che supporti x:data" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Registrazione di un nickname su " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Immettere il nickname che si vuole registrare" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Nickname" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "Questo nickname è registrato da un'altra persona" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Si deve riempire il campo \"Nickname\" nel modulo" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "Modulo MUC per ejabberd" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "" - -#: mod_muc_admin.erl:249 -#, fuzzy -msgid "Total rooms" -msgstr "Stanze" - -#: mod_muc_admin.erl:250 -#, fuzzy -msgid "Permanent rooms" -msgstr "esce dalla stanza" - -#: mod_muc_admin.erl:251 -#, fuzzy -msgid "Registered nicknames" -msgstr "Utenti registrati" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "Configurazione della stanza modificata" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "entra nella stanza" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "esce dalla stanza" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "è stata/o bandita/o" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "è stato espulso a causa di un cambiamento di appartenenza" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "è stato espulso per la limitazione della stanza ai soli membri" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "è stato espulso a causa dello spegnimento del sistema" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "è ora conosciuta/o come" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " ha modificato l'oggetto in: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "La stanza è creata" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "La stanza è eliminata" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "La stanza è avviata" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "La stanza è arrestata" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "Lunedì" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "Martedì" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "Mercoledì" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "Giovedì" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "Venerdì" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "Sabato" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "Domenica" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "Gennaio" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "Febbraio" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "Marzo" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "Aprile" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "Maggio" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "Giugno" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "Luglio" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "Agosto" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "Settembre" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "Ottobre" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "Novembre" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "Dicembre" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Configurazione della stanza" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "Presenti nella stanza" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "Limite di traffico superato" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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 "" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "Non è consentito l'invio di messaggi privati alla conferenza" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "Attendi qualche istante prima di inviare una nuova richiesta di parola" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "In questa conferenza le richieste di parola sono escluse" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "" -"Impossibile estrarre il JID dall'approvazione della richiesta di parola" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "Soltanto i moderatori possono approvare richieste di parola" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Tipo di messaggio non corretto" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "Non è consentito l'invio di messaggi privati di tipo \"groupchat\"" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "Il destinatario non è nella stanza per conferenze" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "Non è consentito l'invio di messaggi privati" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "L'invio di messaggi alla conferenza è consentito soltanto ai presenti" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "L'invio di query alla conferenza è consentito ai soli presenti" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "In questa stanza non sono consentite query ai membri della conferenza" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "" -"La modifica dell'oggetto di questa stanza è consentita soltanto ai " -"moderatori e ai partecipanti" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "" -"La modifica dell'oggetto di questa stanza è consentita soltanto ai moderatori" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "Non è consentito ai visitatori l'invio di messaggi a tutti i presenti" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "Non è consentito ai visitatori cambiare il nickname in questa stanza" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "Il nickname è già in uso all'interno della conferenza" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Sei stata/o bandita/o da questa stanza" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "Per entrare in questa stanza è necessario essere membro" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "Per entrare in questa stanza è prevista una password" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "Troppe richieste CAPTCHA" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "Impossibile generare un CAPTCHA" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Password non esatta" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "Necessari i privilegi di amministratore" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "Necessari i privilegi di moderatore" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "Il Jabber ID ~s non è valido" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "Il nickname ~s non esiste nella stanza" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Affiliazione non valida: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Ruolo non valido: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "Necessari i privilegi di proprietario" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "Configurazione per la stanza ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Titolo della stanza" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "Descrizione della stanza" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Rendere la stanza persistente" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Rendere la sala visibile al pubblico" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "Rendere pubblica la lista dei partecipanti" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Rendere la stanza protetta da password" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Numero massimo di occupanti" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Nessun limite" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Rendere visibile il Jabber ID reale a" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "moderatori soltanto" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "tutti" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "" - -#: mod_muc_room.erl:3486 -#, fuzzy -msgid "Moderator" -msgstr "moderatori soltanto" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Rendere la stanza riservata ai membri" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Rendere la stanza moderata" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Definire per default gli utenti come partecipanti" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Consentire agli utenti di cambiare l'oggetto" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Consentire agli utenti l'invio di messaggi privati" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "Consentire agli ospiti l'invio di messaggi privati a" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "nessuno" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Consentire agli utenti query verso altri utenti" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Consentire agli utenti l'invio di inviti" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "" -"Consentire ai visitatori l'invio di testo sullo stato in aggiornamenti sulla " -"presenza" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Consentire ai visitatori di cambiare il nickname" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "Consentire agli ospiti l'invio di richieste di parola" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "Intervallo minimo fra due richieste di parola (in secondi)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "Rendere la stanza protetta da CAPTCHA" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "Escludi degli ID Jabber dal passaggio CAPTCHA" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Abilitare i log" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "" -"Per la configurazione della stanza è necessario un client che supporti x:data" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Numero di presenti" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "privato, " - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "Richiesta di parola" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "Approva oppure respingi la richiesta di parola." - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "JID utente" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "Dare parola a questa persona?" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s ti invita nella stanza ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "la password è" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "" -"La coda dei messaggi offline del contatto è piena. Il messaggio è stato " -"scartato" - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "Coda di ~s messaggi offline" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Ora" - -#: mod_offline.erl:812 -msgid "From" -msgstr "Da" - -#: mod_offline.erl:813 -msgid "To" -msgstr "A" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Pacchetto" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Messaggi offline:" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "Eliminare tutti i messaggi offline" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "Modulo SOCKS5 Bytestreams per ejabberd" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Pubblicazione-Iscrizione" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "Modulo Pubblicazione/Iscrizione (PubSub) per ejabberd" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "Richiesta di iscrizione per PubSub" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Scegliere se approvare l'iscrizione per questa entità" - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "ID del nodo" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Indirizzo dell'iscritta/o" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "Consentire a questo Jabber ID l'iscrizione a questo nodo pubsub?" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Inviare il contenuto del messaggio con la notifica dell'evento" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Inviare notifiche degli eventi" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Notificare gli iscritti quando la configurazione del nodo cambia" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Notificare gli iscritti quando il nodo è cancellato" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Notificare gli iscritti quando sono eliminati degli elementi dal nodo" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Conservazione persistente degli elementi" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "Un nome comodo per il nodo" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Numero massimo di elementi da conservare persistentemente" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Consentire iscrizioni?" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Specificare il modello di accesso" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Gruppi roster abilitati alla registrazione" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Definire il modello di pubblicazione" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "" -"Cancella tutti gli elementi quando chi li ha pubblicati non è più online" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "Specificare il tipo di messaggio di evento" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Dimensione massima del contenuto del messaggio in byte" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "Quando inviare l'ultimo elemento pubblicato" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Inviare le notifiche solamente agli utenti disponibili" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "Le collezioni a cui è affiliato un nodo" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "La verifica del CAPTCHA ha avuto esito negativo" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "La registrazione richiede un client che supporti x:data e CAPTCHA" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "" -"Scegliere un nome utente e una password per la registrazione con questo " -"server" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "La password è troppo debole" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "Non è consentito agli utenti registrare account così rapidamente" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "La creazione del tuo account Jabber è andata a buon fine." - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "Si è verificato un errore nella creazione dell'account: " - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "La cancellazione del tuo account Jabber è andata a buon fine." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "Si è verificato un errore nella cancellazione dell'account: " - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "Il cambio di password del tuo account Jabber è andato a buon fine." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "Si è verificato un errore nel cambio di password: " - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Registrazione account Jabber" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Registra un account Jabber" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Elimina un account Jabber" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"Questa pagina consente di creare un account Jabber in questo server Jabber. " -"Il tuo JID (Jabber IDentifier) avrà la forma: nome_utente@server. Leggi " -"attentamente le istruzioni per compilare i campi correttamente." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "Nome utente:" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" -"Non fa differenza fra minuscolo e maiuscolo: macbeth, MacBeth e Macbeth si " -"equivalgono." - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "Caratteri non consentiti:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "Server:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" -"Non comunicare la tua password a nessuno, neppure agli amministratori del " -"server Jabber." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "Potrai in seguito cambiare la password utilizzando un client Jabber." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Alcuni client Jabber possono conservare la password nel tuo computer. " -"Utilizza tale funzione soltanto se ritieni che il tuo computer sia sicuro." - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"Memorizza la password, o scrivila su un foglio di carta da conservare in un " -"luogo sicuro. Jabber non prevede una modalità automatica per il recupero di " -"una password dimenticata." - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "Verifica della password:" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "Registra" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "Vecchia password:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "Nuova password:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "" -"Questa pagina consente di eliminare un account Jabber da questo server " -"Jabber." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "Elimina" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Iscrizione" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Pendente" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Gruppi" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Validare" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Eliminare" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Lista dei contatti di " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Aggiungere un Jabber ID (Jabber ID)" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Lista dei contatti" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Gruppi di liste di contatti comuni" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Nome:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Descrizione:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Membri:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Gruppi visualizzati:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Gruppo " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Erlang Jabber Server" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Compleanno" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Città" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "Paese" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "E-mail" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Cognome" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Riempire il modulo per la ricerca di utenti Jabber corrispondenti ai criteri " -"(Aggiungere * alla fine del campo per la ricerca di una sottostringa" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Nome completo" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Altro nome" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Nome dell'organizzazione" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Unità dell'organizzazione" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Cercare utenti in " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "Per effettuare ricerche è necessario un client che supporti x:data" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "Ricerca di utenti per vCard" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "Modulo vCard per ejabberd" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Risultati della ricerca per " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "" -"Riempire i campi per la ricerca di utenti Jabber corrispondenti ai criteri" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Server s2s in uscita" - -#~ msgid "Delete" -#~ msgstr "Eliminare" - -#~ msgid "This room is not anonymous" -#~ msgstr "Questa stanza non è anonima" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "" -#~ "Partecipante espulso dalla stanza perché ha inviato un messaggio non " -#~ "valido" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Partecipante espulso dalla stanza perché ha inviato un messaggio non " -#~ "valido a un altro partecipante" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "" -#~ "Partecipante espulso dalla stanza perché ha inviato una presenza non " -#~ "valido" - -#, fuzzy -#~ msgid "CAPTCHA test failed" -#~ msgstr "Il CAPTCHA è valido." diff --git a/priv/msgs/ja.msg b/priv/msgs/ja.msg index 404a1620d..bf1401f54 100644 --- a/priv/msgs/ja.msg +++ b/priv/msgs/ja.msg @@ -1,21 +1,26 @@ -%% -*- coding: latin-1 -*- +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" (Add * to the end of field to match substring)"," (* を最後に付けると部分文字列にマッチします)"}. +{" has set the subject to: "," は題を設定しました: "}. +{"A description of the node","ノードの説明"}. +{"A friendly name for the node","ノードのフレンドリネーム"}. +{"A password is required to enter this room","このチャットルームに入るにはパスワードが必要です"}. +{"A Web Page","ウェブページ"}. {"Accept","許可"}. -{"Access Configuration","アクセス設定"}. -{"Access Control List Configuration","アクセスコントロールリスト設定"}. -{"Access control lists","アクセスコントロールリスト"}. -{"Access Control Lists","アクセスコントロールリスト"}. {"Access denied by service policy","サービスポリシーによってアクセスが禁止されました"}. -{"Access rules","アクセスルール"}. -{"Access Rules","アクセスルール"}. +{"Access model","アクセスモデル"}. +{"Account doesn't exist","アカウントは存在しません"}. {"Action on user","ユーザー操作"}. -{"Add Jabber ID","Jabber ID を追加"}. -{"Add New","新規追加"}. {"Add User","ユーザーを追加"}. {"Administration of ","管理: "}. {"Administration","管理"}. {"Administrator privileges required","管理者権限が必要です"}. -{"A friendly name for the node","ノードのフレンドリネーム"}. {"All activity","すべて"}. +{"All Users","全ユーザー"}. +{"Allow subscription","購読を認可"}. {"Allow this Jabber ID to subscribe to this pubsub node?","この Jabber ID に、この pubsubノードの購読を許可しますか ?"}. {"Allow users to change the subject","ユーザーによる件名の変更を許可"}. {"Allow users to query other users","ユーザーによる他のユーザーへのクエリーを許可"}. @@ -25,21 +30,27 @@ {"Allow visitors to send private messages to","傍聴者によるプライベートメッセージの送信を次の相手に許可"}. {"Allow visitors to send status text in presence updates","傍聴者によるプレゼンス更新のステータス文の送信を許可"}. {"Allow visitors to send voice requests","傍聴者による発言権の要求を許可"}. -{"All Users","全ユーザー"}. {"Announcements","アナウンス"}. -{"anyone","誰にでも"}. -{"A password is required to enter this room","このチャットルームに入るにはパスワードが必要です"}. +{"Anyone","誰にでも"}. {"April","4月"}. {"August","8月"}. -{"Backup","バックアップ"}. +{"Automatic node creation is not enabled","ノードの自動作成は有効になっていません"}. {"Backup Management","バックアップ管理"}. {"Backup of ~p","バックアップ: ~p"}. {"Backup to File at ","ファイルにバックアップ: "}. +{"Backup","バックアップ"}. {"Bad format","不正なフォーマット"}. {"Birthday","誕生日"}. +{"Both the username and the resource are required","ユーザー名とリソースの両方が必要"}. {"CAPTCHA web page","CAPTCHA ウェブページ"}. +{"Challenge ID","チャレンジ ID"}. {"Change Password","パスワードを変更"}. {"Change User Password","パスワードを変更"}. +{"Changing password is not allowed","パスワード変更の権限がありません"}. +{"Channel already exists","チャンネルは既に存在します"}. +{"Channel does not exist","チャンネルは存在しません"}. +{"Channel JID","チャンネル ID"}. +{"Channels","チャンネル"}. {"Characters not allowed:","使用できない文字:"}. {"Chatroom configuration modified","チャットルームの設定が変更されました"}. {"Chatroom is created","チャットルームを作りました"}. @@ -48,7 +59,6 @@ {"Chatroom is stopped","チャットルームを停止しました"}. {"Chatrooms","チャットルーム"}. {"Choose a username and password to register with this server","サーバーに登録するユーザー名とパスワードを選択してください"}. -{"Choose modules to stop","停止するモジュールを選択"}. {"Choose storage type of tables","テーブルのストレージタイプを選択"}. {"Choose whether to approve this entity's subscription.","このエントリを承認するかどうかを選択してください"}. {"City","都道府県"}. @@ -56,85 +66,80 @@ {"Conference room does not exist","会議室は存在しません"}. {"Configuration of room ~s","チャットルーム ~s の設定"}. {"Configuration","設定"}. -{"Connected Resources:","接続リソース:"}. -{"Connections parameters","接続パラメーター"}. +{"Contact Addresses (normally, room owner or owners)","連絡先 (通常は会議室の主宰者またはその複数)"}. {"Country","国"}. -{"CPU Time:","CPU時間:"}. -{"Database","データーベース"}. -{"Database Tables at ~p","データーベーステーブル: ~p"}. +{"Current Discussion Topic","現在の話題"}. +{"Database failure","データーベース障害"}. {"Database Tables Configuration at ","データーベーステーブル設定 "}. +{"Database","データーベース"}. {"December","12月"}. {"Default users as participants","デフォルトのユーザーは参加者"}. {"Delete message of the day on all hosts","全ホストのお知らせメッセージを削除"}. {"Delete message of the day","お知らせメッセージを削除"}. -{"Delete Selected","選択した項目を削除"}. {"Delete User","ユーザーを削除"}. {"Deliver event notifications","イベント通知を配送する"}. {"Deliver payloads with event notifications","イベント通知と同時にペイロードを配送する"}. -{"Description:","説明:"}. {"Disc only copy","ディスクだけのコピー"}. -{"Displayed Groups:","表示グループ:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","パスワードは誰にも教えないようにしてください。Jabber サーバーの管理者があなたにパスワードを尋ねることはありません。"}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","パスワードは誰にも(たとえ XMPP サーバーの管理者でも)教えないようにしてください。"}. {"Dump Backup to Text File at ","テキストファイルにバックアップ: "}. {"Dump to Text File","テキストファイルに出力"}. +{"Duplicated groups are not allowed by RFC6121","RFC6121 によりグループの重複は許されません"}. {"Edit Properties","プロパティを編集"}. {"Either approve or decline the voice request.","発言権の要求を承認または却下します。"}. -{"ejabberd IRC module","ejabberd IRC module"}. +{"ejabberd HTTP Upload service","ejabberd HTTP ファイルアップロード"}. {"ejabberd MUC module","ejabberd MUCモジュール"}. {"ejabberd Multicast service","ejabberdマルチキャストサービス"}. {"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe モジュール"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams モジュール"}. {"ejabberd vCard module","ejabberd vCard モジュール"}. {"ejabberd Web Admin","ejabberd ウェブ管理"}. -{"Elements","要素"}. -{"Email","メールアドレス"}. -{"Empty Rooms","空のルーム"}. +{"ejabberd","ejabberd"}. +{"Email Address","メールアドレス"}. +{"Email","メール"}. {"Enable logging","ロギングを有効"}. {"Enable message archiving","メッセージアーカイブを有効化"}. -{"Encoding for server ~b","サーバーのエンコーディング ~b"}. {"End User Session","エンドユーザーセッション"}. -{"Enter list of {Module, [Options]}","{モジュール, [オプション]}のリストを入力してください"}. {"Enter nickname you want to register","登録するニックネームを入力してください"}. {"Enter path to backup file","バックアップファイルのパスを入力してください"}. {"Enter path to jabberd14 spool dir","jabberd14 spool ディレクトリのディレクトリを入力してください"}. {"Enter path to jabberd14 spool file","jabberd14 spool ファイルのパスを入力してください"}. {"Enter path to text file","テキストファイルのパスを入力してください"}. {"Enter the text you see","見えているテキストを入力してください"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","IRC サーバーに接続先するためのユーザー名と文字エンコーディングを入力してください。'Next' を押して次の項目に進みます。'Complete' を押すと設定が保存されます。"}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","IRC サーバーに接続先するために使用するユーザー名、文字エンコーディング、ポート、パスワードを入力してください"}. -{"Erlang Jabber Server","Erlang Jabber Server"}. -{"Error","エラー"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","例: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. +{"Erlang XMPP Server","Erlang XMPP サーバー"}. {"Exclude Jabber IDs from CAPTCHA challenge","CAPTCHA 入力を免除する Jabber ID"}. {"Export all tables as SQL queries to a file:","すべてのテーブルをSQL形式でファイルにエクスポート: "}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","サーバーにあるすべてのユーザーデータを PIEFXIS ファイルにエクスポート (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","ホストのユーザーデータを PIEFXIS ファイルにエクスポート (XEP-0227):"}. +{"External component failure","外部コンポーネントの障害"}. +{"External component timeout","外部コンポーネントのタイムアウト"}. {"Failed to extract JID from your voice request approval","発言権要求の承認から JID を取り出すことに失敗しました"}. +{"Failed to parse HTTP response","HTTP 応答のパースに失敗しました"}. +{"Failed to process option '~s'","オプション '~s' の処理に失敗しました"}. {"Family Name","姓"}. {"February","2月"}. -{"Fill in fields to search for any matching Jabber User","項目を入力してユーザーを検索してください"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","項目を入力してユーザーを検索を行えます (* を使用すると部分文字列にマッチします)"}. +{"Fill in the form to search for any matching XMPP User","XMPP ユーザーを検索するには欄に入力してください"}. {"Friday","金曜日"}. -{"From ~s","From ~s"}. -{"From","差出人"}. +{"From ~ts","From ~ts"}. +{"Full List of Room Admins","チャットルーム管理者の一覧"}. +{"Full List of Room Owners","チャットルーム主宰者の一覧"}. {"Full Name","氏名"}. +{"Get List of Online Users","オンラインユーザーの一覧を取得"}. +{"Get List of Registered Users","登録ユーザーの一覧を取得"}. {"Get Number of Online Users","オンラインユーザー数を取得"}. {"Get Number of Registered Users","登録ユーザー数を取得"}. +{"Get Pending","保留中の取得"}. {"Get User Last Login Time","最終ログイン時間を取得"}. -{"Get User Password","パスワードを取得"}. {"Get User Statistics","ユーザー統計を取得"}. +{"Given Name","名"}. {"Grant voice to this person?","この人に発言権を与えますか ?"}. -{"Group ","グループ"}. -{"Groups","グループ"}. {"has been banned","はバンされました"}. -{"has been kicked","はキックされました"}. -{"has been kicked because of an affiliation change","は分掌が変更されたためキックされました"}. {"has been kicked because of a system shutdown","はシステムシャットダウンのためキックされました"}. +{"has been kicked because of an affiliation change","は分掌が変更されたためキックされました"}. {"has been kicked because the room has been changed to members-only","はチャットルームがメンバー制に変更されたためキックされました"}. -{" has set the subject to: "," は件名を設定しました: "}. -{"Host","ホスト"}. +{"has been kicked","はキックされました"}. +{"Host unknown","不明なホスト"}. +{"HTTP File Upload","HTTP ファイルアップロード"}. {"If you don't see the CAPTCHA image here, visit the web page.","ここに CAPTCHA 画像が表示されない場合、ウェブページを参照してください。"}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","別のポートやパスワード、文字エンコーディングを使用したい場合、'{\"irc server\", \"encoding\", port, \"password\"}' という形式のリストを入力してください。デフォルトでエンコーディングは \"~s\" を使用し、ポートは ~p、パスワードは空になっています。"}. {"Import Directory","ディレクトリインポート"}. {"Import File","ファイルからインポート"}. {"Import user data from jabberd14 spool file:","ユーザーデータを jabberd14 Spool ファイルからインポート:"}. @@ -144,43 +149,33 @@ {"Import Users from Dir at ","ディレクトリからユーザーをインポート: "}. {"Import Users From jabberd14 Spool Files","jabberd14 Spool ファイルからユーザーをインポート"}. {"Improper message type","誤ったメッセージタイプです"}. -{"Incoming s2s Connections:","内向き s2s コネクション:"}. +{"Incorrect data form","データ形式が違います"}. {"Incorrect password","パスワードが違います"}. -{"Invalid affiliation: ~s","無効な分掌です: ~s"}. -{"Invalid role: ~s","無効な役です: ~s"}. +{"Installed Modules:","インストールされているモジュール:"}. +{"Install","インストール"}. +{"Insufficient privilege","権限が不十分です"}. +{"Internal server error","内部サーバーエラー"}. +{"Invalid node name","無効なノード名です"}. +{"Invalid 'previd' value","無効な 'previd' の値です"}. +{"Invitations are not allowed in this conference","この会議では、招待はできません"}. {"IP addresses","IP アドレス"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","IRC チャンネル (先頭に#は不要)"}. -{"IRC server","IRC サーバー"}. -{"IRC settings","IRC 設定"}. -{"IRC Transport","IRCトランスポート"}. -{"IRC username","IRC ユーザー名"}. -{"IRC Username","IRC ユーザー名"}. {"is now known as","は名前を変更しました: "}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","このルームにエラーメッセージを送ることは許可されていません。参加者(~s)はエラーメッセージを(~s)を送信してルームからキックされました。"}. {"It is not allowed to send private messages of type \"groupchat\"","種別が\"groupchat\" であるプライベートメッセージを送信することはできません"}. {"It is not allowed to send private messages to the conference","この会議にプライベートメッセージを送信することはできません"}. -{"It is not allowed to send private messages","プライベートメッセージを送信することはできません"}. -{"Jabber Account Registration","Jabber アカウント登録"}. {"Jabber ID","Jabber ID"}. -{"Jabber ID ~s is invalid","Jabber ID ~s は無効です"}. {"January","1月"}. -{"Join IRC channel","IRC チャンネルに参加"}. +{"JID normalization failed","JID の正規化に失敗しました"}. {"joins the room","がチャットルームに参加しました"}. -{"Join the IRC channel here.","この IRC チャンネルに参加します。"}. -{"Join the IRC channel in this Jabber ID: ~s","Jabber ID: ~s でこの IRC チャンネルに参加"}. {"July","7月"}. {"June","6月"}. +{"Just created","作成しました"}. {"Last Activity","活動履歴"}. {"Last login","最終ログイン"}. +{"Last message","最終メッセージ"}. {"Last month","先月"}. {"Last year","去年"}. {"leaves the room","がチャットルームから退出しました"}. -{"Listened Ports at ","Listen ポート "}. -{"Listened Ports","Listen ポート"}. -{"List of modules to start","起動モジュールの一覧"}. -{"List of rooms","チャットルームの一覧"}. -{"Low level update script","低レベル更新スクリプト"}. {"Make participants list public","参加者一覧を公開"}. {"Make room CAPTCHA protected","チャットルームを CAPTCHA で保護"}. {"Make room members-only","チャットルームをメンバーのみに制限"}. @@ -188,62 +183,66 @@ {"Make room password protected","チャットルームをパスワードで保護"}. {"Make room persistent","チャットルームを永続化"}. {"Make room public searchable","チャットルームを検索可"}. +{"Malformed username","不正な形式のユーザー名"}. {"March","3月"}. -{"Maximum Number of Occupants","最大在室者数"}. -{"Max # of items to persist","アイテムの最大保存数"}. {"Max payload size in bytes","最大ぺイロードサイズ (byte)"}. +{"Maximum file size","最大ファイルサイズ"}. +{"Maximum Number of Occupants","最大在室者数"}. {"May","5月"}. -{"Members:","メンバー:"}. {"Membership is required to enter this room","このチャットルームに入るにはメンバーでなければなりません"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","パスワードは記憶するか、紙に書いて安全な場所に保管してください。もしあなたがパスワードを忘れてしまった場合、Jabber ではパスワードのリカバリを自動的に行うことはできません。"}. -{"Memory","メモリ"}. +{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","パスワードは記憶するか、紙に書いて安全な場所に保管してください。もしあなたがパスワードを忘れてしまった場合、XMPP ではパスワードのリカバリを自動的に行うことはできません。"}. {"Message body","本文"}. {"Middle Name","ミドルネーム"}. {"Minimum interval between voice requests (in seconds)","発言権の要求の最小時間間隔 (秒)"}. -{"Moderator","モデレーター"}. {"Moderator privileges required","モデレーター権限が必要です"}. -{"moderators only","モデレーターにのみ"}. -{"Modified modules","更新されたモジュール"}. -{"Module","モジュール"}. -{"Modules","モジュール"}. -{"Modules at ~p","モジュール ~p"}. +{"Moderator","モデレーター"}. {"Monday","月曜日"}. {"Multicast","マルチキャスト"}. {"Multi-User Chat","マルチユーザーチャット"}. {"Name","名"}. -{"Name:","名前:"}. +{"Natural-Language Room Name","自然言語での会議室名"}. {"Never","なし"}. {"New Password:","新しいパスワード:"}. -{"Nickname","ニックネーム"}. +{"Nickname can't be empty","ニックネームは空にできません"}. {"Nickname Registration at ","ニックネーム登録: "}. {"Nickname ~s does not exist in the room","ニックネーム ~s はこのチャットルームにいません"}. +{"Nickname","ニックネーム"}. +{"No address elements found","アドレス要素が見つかりません"}. +{"No addresses element found","アドレス要素が見つかりません"}. {"No body provided for announce message","アナウンスメッセージはありませんでした"}. -{"nobody","誰にも許可しない"}. +{"No child elements found","子要素が見つかりません"}. {"No Data","データなし"}. +{"No element found"," 要素が見つかりません"}. +{"No 'item' element found","'item' 要素が見つかりません"}. +{"No limit","制限なし"}. +{"No 'password' found in this query","このクエリに 'password' が見つかりません"}. +{"No services available","利用できるサービスはありません"}. +{"Node already exists","ノードは既に存在しています"}. {"Node ID","ノードID"}. {"Node not found","ノードが見つかりません"}. {"Node ~p","ノード ~p"}. {"Nodes","ノード"}. -{"No limit","制限なし"}. +{"Node","ノード"}. {"None","なし"}. -{"No resource provided","リソースが指定されていません"}. {"Not Found","見つかりません"}. {"Notify subscribers when items are removed from the node","アイテムがノードから消された時に購読者へ通知する"}. {"Notify subscribers when the node configuration changes","ノード設定に変更があった時に購読者へ通知する"}. {"Notify subscribers when the node is deleted","ノードが削除された時に購読者へ通知する"}. {"November","11月"}. {"Number of occupants","在室者の数"}. +{"Number of Offline Messages","オフラインメッセージ数"}. {"Number of online users","オンラインユーザー数"}. {"Number of registered users","登録ユーザー数"}. +{"Occupants are allowed to invite others","在室者は誰かを招待することができます"}. +{"Occupants May Change the Subject","ユーザーによる件名の変更を許可"}. {"October","10月"}. -{"Offline Messages:","オフラインメッセージ:"}. -{"Offline Messages","オフラインメッセージ"}. {"OK","OK"}. {"Old Password:","古いパスワード:"}. -{"Online","オンライン"}. -{"Online Users:","オンラインユーザー:"}. {"Online Users","オンラインユーザー"}. +{"Online","オンライン"}. {"Only deliver notifications to available users","有効なユーザーにのみ告知を送信する"}. +{"Only or tags are allowed"," タグまたは タグのみが許可されます"}. +{"Only element is allowed in this query","このクエリでは 要素のみが許可されます"}. {"Only members may query archives of this room","メンバーのみがこのルームのアーカイブを取得できます"}. {"Only moderators and participants are allowed to change the subject in this room","モデレーターと参加者のみがチャットルームの件名を変更できます"}. {"Only moderators are allowed to change the subject in this room","モデレーターのみがチャットルームの件名を変更できます"}. @@ -251,64 +250,51 @@ {"Only occupants are allowed to send messages to the conference","在室者のみがこの会議にメッセージを送ることができます"}. {"Only occupants are allowed to send queries to the conference","在室者のみが会議にクエリーを送信することができます"}. {"Only service administrators are allowed to send service messages","サービス管理者のみがサービスメッセージを送信できます"}. -{"Options","オプション"}. {"Organization Name","会社名"}. {"Organization Unit","部署名"}. -{"Outgoing s2s Connections:","外向き s2s コネクション:"}. {"Outgoing s2s Connections","外向き s2s コネクション"}. {"Owner privileges required","主宰者の権限が必要です"}. -{"Packet","パケット"}. +{"Participant ID","参加者 ID"}. {"Participant","参加者"}. -{"Password:","パスワード:"}. -{"Password","パスワード"}. -{"Password ~b","パスワード ~b"}. -{"Password Verification:","パスワード (確認):"}. {"Password Verification","パスワード (確認)"}. +{"Password Verification:","パスワード (確認):"}. +{"Password","パスワード"}. +{"Password:","パスワード:"}. {"Path to Dir","ディレクトリのパス"}. {"Path to File","ファイルのパス"}. -{"Pending","保留"}. {"Period: ","期間: "}. -{"Permanent rooms","永続チャットルーム"}. {"Persist items to storage","アイテムをストレージに保存する"}. +{"Persistent","チャットルームを永続化"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","これらのオプションは組み込みの Mnesia データーベースのバックアップのみを行うことに注意してください。もし ODBC モジュールを使用している場合は、SQL データーベースのバックアップを別に行う必要があります。"}. -{"Please specify file name.","ファイル名を指定してください。"}. -{"Please specify file size.","ファイルサイズを指定してください。"}. {"Please, wait for a while before sending new voice request","新しい発言権の要求を送るまで少し間をおいてください"}. {"Pong","Pong"}. -{"Port","ポート"}. -{"Port ~b","ポート ~b"}. {"Present real Jabber IDs to","本当の Jabber ID を公開"}. +{"Previous session not found","前のセッションが見つかりません"}. {"private, ","プライベート、"}. -{"Protocol","プロトコル"}. +{"Public","公開"}. {"Publish-Subscribe","Publish-Subscribe"}. {"PubSub subscriber request","PubSub 購読者のリクエスト"}. {"Purge all items when the relevant publisher goes offline","公開者がオフラインになるときに、すべてのアイテムを削除"}. {"Queries to the conference members are not allowed in this room","このチャットルームでは、会議のメンバーへのクエリーは禁止されています"}. +{"Query to another users is forbidden","他のユーザーへのクエリは禁止されています"}. {"RAM and disc copy","RAM, ディスクコピー"}. {"RAM copy","RAM コピー"}. -{"Raw","Raw"}. {"Really delete message of the day?","本当にお知らせメッセージを削除しますか ?"}. {"Recipient is not in the conference room","受信者はこの会議室にいません"}. -{"Register a Jabber account","Jabber アカウントを登録"}. -{"Registered nicknames","登録ニックネーム"}. -{"Registered Users:","登録ユーザー:"}. -{"Registered Users","登録ユーザー"}. +{"Register an XMPP account","XMPP アカウントを登録"}. {"Register","登録"}. -{"Registration in mod_irc for ","mod_irc での登録: "}. {"Remote copy","リモートコピー"}. -{"Remove All Offline Messages","すべてのオフラインメッセージを削除"}. {"Remove User","ユーザーを削除"}. -{"Remove","削除"}. {"Replaced by new connection","新しいコネクションによって置き換えられました"}. +{"Request is ignored","リクエストは無視されます"}. {"Resources","リソース"}. {"Restart Service","サービスを再起動"}. -{"Restart","再起動"}. -{"Restore","リストア"}. {"Restore Backup from File at ","ファイルからバックアップをリストア: "}. {"Restore binary backup after next ejabberd restart (requires less memory):","ejabberd の再起動時にバイナリバックアップからリストア (メモリ少):"}. {"Restore binary backup immediately:","直ちにバイナリバックアップからリストア:"}. {"Restore plain text backup immediately:","直ちにプレーンテキストバックアップからリストア:"}. +{"Restore","リストア"}. {"Roles for which Presence is Broadcasted","プレゼンスをブロードキャストするロール"}. {"Room Configuration","チャットルームの設定"}. {"Room creation is denied by service policy","サービスポリシーによってチャットルームの作成が禁止されています"}. @@ -316,14 +302,10 @@ {"Room Occupants","在室者"}. {"Room title","チャットルームのタイトル"}. {"Roster groups allowed to subscribe","名簿グループは購読を許可しました"}. -{"Roster of ","名簿: "}. {"Roster size","名簿サイズ"}. -{"Roster","名簿"}. -{"RPC Call Error","RPC 呼び出しエラー"}. {"Running Nodes","起動ノード"}. -{"~s access rule configuration","~s アクセスルール設定"}. +{"~s invites you to the room ~s","~s はあなたをチャットルーム ~s に招待しています"}. {"Saturday","土曜日"}. -{"Script check","スクリプトチェック"}. {"Search Results for ","検索結果: "}. {"Search users in ","ユーザーの検索: "}. {"Send announcement to all online users on all hosts","全ホストのオンラインユーザーにアナウンスを送信"}. @@ -332,89 +314,83 @@ {"Send announcement to all users","すべてのユーザーにアナウンスを送信"}. {"September","9月"}. {"Server:","サーバー:"}. -{"Server","サーバー"}. -{"Server ~b","サーバー ~b"}. {"Set message of the day and send to online users","お知らせメッセージを設定し、オンラインユーザーに送信"}. {"Set message of the day on all hosts and send to online users","全ホストのお知らせメッセージを設定し、オンラインユーザーに送信"}. {"Shared Roster Groups","共有名簿グループ"}. {"Show Integral Table","累積の表を表示"}. {"Show Ordinary Table","通常の表を表示"}. {"Shut Down Service","サービスを停止"}. -{"~s invites you to the room ~s","~s はあなたをチャットルーム ~s に招待しています"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Jabber クライアントはコンピューターにパスワードを記憶できます。コンピューターが安全であると信頼できる場合にのみ、この機能を使用してください。"}. +{"SOCKS5 Bytestreams","SOCKS5 Bytestreams"}. +{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","XMPP クライアントはコンピューターにパスワードを記憶できます。コンピューターが安全であると信頼できる場合にのみ、この機能を使用してください。"}. {"Specify the access model","アクセスモデルを設定する"}. {"Specify the event message type","イベントメッセージ種別を設定"}. {"Specify the publisher model","公開モデルを指定する"}. -{"~s's Offline Messages Queue","~s' のオフラインメッセージキュー"}. -{"Start Modules at ","モジュールを開始: "}. -{"Start Modules","モジュールを起動"}. -{"Start","開始"}. -{"Statistics of ~p","~p の統計"}. -{"Statistics","統計"}. -{"Stop Modules at ","モジュールを停止: "}. -{"Stop Modules","モジュールを停止"}. +{"Stanza ID","スタンザ ID"}. {"Stopped Nodes","停止ノード"}. -{"Stop","停止"}. -{"Storage Type","ストレージタイプ"}. {"Store binary backup:","バイナリバックアップを保存:"}. {"Store plain text backup:","プレーンテキストバックアップを保存:"}. {"Subject","件名"}. {"Submitted","送信完了"}. -{"Submit","送信"}. {"Subscriber Address","購読者のアドレス"}. -{"Subscription","認可"}. {"Sunday","日曜日"}. {"That nickname is already in use by another occupant","そのニックネームは既にほかの在室者によって使用されています"}. {"That nickname is registered by another person","ニックネームはほかの人によって登録されています"}. +{"The account already exists","アカウントは既に存在しています"}. +{"The account was not unregistered","アカウントは削除されませんでした"}. {"The CAPTCHA is valid.","CAPTCHA は有効です。"}. {"The CAPTCHA verification has failed","CAPTCHA 検証は失敗しました"}. +{"The captcha you entered is wrong","入力した CAPTCHA は間違っています"}. {"The collections with which a node is affiliated","提携されたノードの集合です"}. -{"the password is","パスワードは"}. +{"The default language of the node","ノードのデフォルトの言語"}. +{"The JID of the node creator","ノード作成者の JID"}. +{"The name of the node","ノード名"}. +{"The password contains unacceptable characters","パスワードに使用できない文字が含まれています"}. {"The password is too weak","このパスワードは単純過ぎます"}. -{"The password of your Jabber account was successfully changed.","Jabber アカウントのパスワード変更に成功しました。"}. -{"There was an error changing the password: ","パスワードの変更中にエラーが発生しました: "}. +{"the password is","パスワードは"}. +{"The password of your XMPP account was successfully changed.","XMPP アカウントのパスワード変更に成功しました。"}. +{"The password was not changed","このパスワードは変更されませんでした"}. +{"The passwords are different","このパスワードが違います"}. {"There was an error creating the account: ","アカウントの作成中にエラーが発生しました: "}. {"There was an error deleting the account: ","アカウントの削除中にエラーが発生しました: "}. -{"This IP address is blacklisted in ~s","このIPアドレスはアクセスを禁止されています ~s"}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","大文字と小文字は区別しません: macbeth は MacBeth や Macbeth と同じです。"}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","ここはこの Jabber サーバーにアカウントを作成するページです。あなたの JID (JabberID) は username@server のような形式になります。注意事項どおり、正しく項目を記入してください。"}. -{"This page allows to unregister a Jabber account in this Jabber server.","このページはサーバー上のJabberアカウントを削除するページです。"}. +{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","ここはこの XMPP サーバーにアカウントを作成するページです。あなたの JID (JabberID) は username@server のような形式になります。注意事項どおり、正しく項目を記入してください。"}. +{"This page allows to unregister an XMPP account in this XMPP server.","このページはサーバー上のXMPPアカウントを削除するページです。"}. +{"This room is not anonymous","このチャットルームは非匿名です"}. {"Thursday","木曜日"}. {"Time delay","遅延時間"}. -{"Time","時間"}. {"Too many CAPTCHA requests","CAPTCHA 要求が多すぎます"}. {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","~p回の認証に失敗しました。このIPアドレス(~s)は~s UTCまでブロックされます。"}. {"Too many unacked stanzas","多くのスタンザが応答していません"}. -{"To ~s","宛先 ~s"}. -{"Total rooms","チャットルーム数"}. -{"To","To"}. +{"Too many users in this conference","この会議にはユーザーが多すぎます"}. {"Traffic rate limit is exceeded","トラフィックレートの制限を超えました"}. -{"Transactions Aborted:","トランザクションの失敗:"}. -{"Transactions Committed:","トランザクションのコミット:"}. -{"Transactions Logged:","トランザクションのログ: "}. -{"Transactions Restarted:","トランザクションの再起動:"}. +{"~ts's Offline Messages Queue","~ts のオフラインメッセージキュー"}. {"Tuesday","火曜日"}. {"Unable to generate a CAPTCHA","CAPTCHA を生成できません"}. {"Unauthorized","認証されていません"}. -{"Unregister a Jabber account","Jabber アカウントを削除"}. +{"Unexpected action","予期しないアクション"}. +{"Unexpected error condition: ~p","予期しないエラー状態: ~p"}. +{"Uninstall","アンインストール"}. +{"Unregister an XMPP account","XMPP アカウントを削除"}. {"Unregister","削除"}. +{"Unsupported version","対応していないバージョン"}. {"Update message of the day (don't send)","お知らせメッセージを更新 (送信しない)"}. {"Update message of the day on all hosts (don't send)","全ホストのお知らせメッセージを更新 (送信しない)"}. -{"Update plan","更新計画"}. -{"Update ~p","更新 ~p"}. -{"Update script","スクリプトの更新"}. -{"Update","更新"}. -{"Uptime:","起動時間:"}. -{"Use of STARTTLS required","STARTTLS の使用が必須です"}. -{"User","ユーザー"}. +{"Upgrade","アップグレード"}. +{"User already exists","ユーザーは既に存在しています"}. +{"User (jid)","ユーザー (JID)"}. {"User JID","ユーザー JID"}. {"User Management","ユーザー管理"}. +{"User removed","ユーザーを削除しました"}. +{"User session not found","ユーザーセッションが見つかりません"}. +{"User session terminated","ユーザーセッションが終了しました"}. +{"User ~ts","ユーザー ~ts"}. {"Username:","ユーザー名:"}. -{"Users","ユーザー"}. {"Users are not allowed to register accounts so quickly","それほど速くアカウントを登録することはできません"}. {"Users Last Activity","ユーザーの活動履歴"}. -{"User ~s","ユーザー ~s"}. -{"Validate","検証"}. +{"Users","ユーザー"}. +{"User","ユーザー"}. +{"Value of '~s' should be boolean","'~s' の値はブール値である必要があります"}. +{"Value of '~s' should be integer","'~s' の値は整数である必要があります"}. {"vCard User Search","vCard検索"}. {"Virtual Hosts","バーチャルホスト"}. {"Visitors are not allowed to change their nicknames in this room","傍聴者はこのチャットルームでニックネームを変更することはできません"}. @@ -423,18 +399,24 @@ {"Voice requests are disabled in this conference","この会議では、発言権の要求はできません"}. {"Voice request","発言権を要求"}. {"Wednesday","水曜日"}. +{"When a new subscription is processed","新しい購読が処理されるとき"}. {"When to send the last published item","最後の公開アイテムを送信するタイミングで"}. {"Whether to allow subscriptions","購読を許可するかどうか"}. -{"You can later change your password using a Jabber client.","あなたは後で Jabber クライアントを使用してパスワードを変更できます。"}. -{"You have been banned from this room","あなたはこのチャットルームからバンされています"}. +{"Who can send private messages","誰がプライベートメッセージを送れるか"}. +{"XMPP Account Registration","XMPP アカウント登録"}. +{"XMPP Domains","XMPP ドメイン"}. +{"You are being removed from the room because of a system shutdown","システムシャットダウンのためチャットルームから削除されました"}. +{"You are not allowed to send private messages","プライベートメッセージを送信する権限がありません"}. +{"You are not joined to the channel","チャンネルに参加していません"}. +{"You can later change your password using an XMPP client.","あなたは後で XMPP クライアントを使用してパスワードを変更できます。"}. +{"You have been banned from this room","あなたはこのチャットルームから締め出されています"}. {"You must fill in field \"Nickname\" in the form","フォームの\"ニックネーム\"欄を入力する必要があります"}. {"You need a client that supports x:data and CAPTCHA to register","登録を行うには x:data と CAPTCHA をサポートするクライアントが必要です"}. {"You need a client that supports x:data to register the nickname","ニックネームを登録するには x:data をサポートするクライアントが必要です"}. -{"You need an x:data capable client to configure mod_irc settings","mod_irc の設定には x:data をサポートするクライアントが必要です"}. -{"You need an x:data capable client to configure room","チャットルームを設定するには x:data をサポートするクライアントが必要です"}. {"You need an x:data capable client to search","検索を行うためには x:data をサポートするクライアントが必要です"}. {"Your active privacy list has denied the routing of this stanza.","あなたのプライバシーリストはこのスタンザのルーティングを拒否しました。"}. {"Your contact offline message queue is full. The message has been discarded.","相手先のオフラインメッセージキューが一杯です。このメッセージは破棄されます。"}. -{"Your Jabber account was successfully created.","Jabber アカウントの作成に成功しました。"}. -{"Your Jabber account was successfully deleted.","Jabber アカウントの削除に成功しました。"}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","~s 宛のメッセージはブロックされています。解除するにはこちらを見てください ~s"}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","~s 宛のメッセージはブロックされています。解除するにはこちらを見てください ~s"}. +{"Your XMPP account was successfully registered.","XMPP アカウントの登録に成功しました。"}. +{"Your XMPP account was successfully unregistered.","XMPP アカウントの削除に成功しました。"}. +{"You're not allowed to create nodes","ノードを作成する権限がありません"}. diff --git a/priv/msgs/ja.po b/priv/msgs/ja.po deleted file mode 100644 index 1cb959081..000000000 --- a/priv/msgs/ja.po +++ /dev/null @@ -1,1925 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: ejabberd 2.1.x\n" -"PO-Revision-Date: 2012-04-16 15:48+0900\n" -"Last-Translator: Tsukasa Hamano \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Japanese (日本語)\n" -"X-Additional-Translator: Tsukasa Hamano \n" -"X-Additional-Translator: Mako N \n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "STARTTLS の使用が必須です" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "リソースが指定されていません" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "新しいコネクションによって置き換えられました" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "はキックされました" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "あなたのプライバシーリストはこのスタンザのルーティングを拒否しました。" - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "多くのスタンザが応答していません" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "見えているテキストを入力してください" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "" -"~s 宛のメッセージはブロックされています。解除するにはこちらを見てください ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "" -"ここに CAPTCHA 画像が表示されない場合、ウェブページを参照してください。" - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "CAPTCHA ウェブページ" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "CAPTCHA は有効です。" - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "ユーザー" - -#: ejabberd_oauth.erl:256 -msgid "Server" -msgstr "サーバー" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "パスワード" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "許可" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "認証されていません" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "ejabberd ウェブ管理" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "管理" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "アクセスコントロールリスト" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "送信完了" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "不正なフォーマット" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "送信" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "Raw" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "選択した項目を削除" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "アクセスルール" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "~s アクセスルール設定" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "バーチャルホスト" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "ユーザー" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "オンラインユーザー" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "ユーザーの活動履歴" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "期間: " - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "先月" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "去年" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "すべて" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "通常の表を表示" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "累積の表を表示" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "統計" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "見つかりません" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "ノードが見つかりません" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "新規追加" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "ホスト" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "登録ユーザー" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "ユーザーを追加" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "オフラインメッセージ" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "活動履歴" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "なし" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "オンライン" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "登録ユーザー:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "オンラインユーザー:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "外向き s2s コネクション:" - -#: ejabberd_web_admin.erl:1559 -msgid "Incoming s2s Connections:" -msgstr "内向き s2s コネクション:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "なし" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "パスワードを変更" - -#: ejabberd_web_admin.erl:1673 -msgid "User ~s" -msgstr "ユーザー ~s" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "接続リソース:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "パスワード:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "ユーザーを削除" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "データなし" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "ノード" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "起動ノード" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "停止ノード" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -msgid "Node ~p" -msgstr "ノード ~p" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "データーベース" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "バックアップ" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Listen ポート" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "更新" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "再起動" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "停止" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "モジュール" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "RPC 呼び出しエラー" - -#: ejabberd_web_admin.erl:1917 -msgid "Database Tables at ~p" -msgstr "データーベーステーブル: ~p" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "名" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "ストレージタイプ" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "要素" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "メモリ" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "エラー" - -#: ejabberd_web_admin.erl:1955 -msgid "Backup of ~p" -msgstr "バックアップ: ~p" - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"これらのオプションは組み込みの Mnesia データーベースのバックアップのみを行う" -"ことに注意してください。もし ODBC モジュールを使用している場合は、SQL デー" -"ターベースのバックアップを別に行う必要があります。" - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "バイナリバックアップを保存:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "OK" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "直ちにバイナリバックアップからリストア:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "ejabberd の再起動時にバイナリバックアップからリストア (メモリ少):" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "プレーンテキストバックアップを保存:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "直ちにプレーンテキストバックアップからリストア:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "ユーザーデータを PIEFXIS ファイルからインポート (XEP-0227):" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" -"サーバーにあるすべてのユーザーデータを PIEFXIS ファイルにエクスポート " -"(XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "ホストのユーザーデータを PIEFXIS ファイルにエクスポート (XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "すべてのテーブルをSQL形式でファイルにエクスポート: " - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "ユーザーデータを jabberd14 Spool ファイルからインポート:" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "ユーザーデータを jabberd14 Spool ディレクトリからインポート:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Listen ポート " - -#: ejabberd_web_admin.erl:2144 -msgid "Modules at ~p" -msgstr "モジュール ~p" - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "~p の統計" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "起動時間:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "CPU時間:" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "トランザクションのコミット:" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "トランザクションの失敗:" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "トランザクションの再起動:" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "トランザクションのログ: " - -#: ejabberd_web_admin.erl:2243 -msgid "Update ~p" -msgstr "更新 ~p" - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "更新計画" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "更新されたモジュール" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "スクリプトの更新" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "低レベル更新スクリプト" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "スクリプトチェック" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "ポート" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "プロトコル" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "モジュール" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "オプション" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "開始" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "コマンド" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "本当にお知らせメッセージを削除しますか ?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "件名" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "本文" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "アナウンスメッセージはありませんでした" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "アナウンス" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "すべてのユーザーにアナウンスを送信" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "全ホストのユーザーにアナウンスを送信" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "すべてのオンラインユーザーにアナウンスを送信" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "全ホストのオンラインユーザーにアナウンスを送信" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "お知らせメッセージを設定し、オンラインユーザーに送信" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "全ホストのお知らせメッセージを設定し、オンラインユーザーに送信" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "お知らせメッセージを更新 (送信しない)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "全ホストのお知らせメッセージを更新 (送信しない)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "お知らせメッセージを削除" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "全ホストのお知らせメッセージを削除" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "設定" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "モジュールを起動" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "モジュールを停止" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "リストア" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "テキストファイルに出力" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "ファイルからインポート" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "ディレクトリインポート" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "サービスを再起動" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "サービスを停止" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "ユーザーを削除" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "エンドユーザーセッション" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "パスワードを取得" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "パスワードを変更" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "最終ログイン時間を取得" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "ユーザー統計を取得" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "登録ユーザー数を取得" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "オンラインユーザー数を取得" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "ユーザー管理" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "全ユーザー" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "外向き s2s コネクション" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "バックアップ管理" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "jabberd14 Spool ファイルからユーザーをインポート" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "宛先 ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "From ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "データーベーステーブル設定 " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "テーブルのストレージタイプを選択" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "ディスクだけのコピー" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "RAM, ディスクコピー" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "RAM コピー" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "リモートコピー" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "モジュールを停止: " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "停止するモジュールを選択" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "モジュールを開始: " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "{モジュール, [オプション]}のリストを入力してください" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "起動モジュールの一覧" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "ファイルにバックアップ: " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "バックアップファイルのパスを入力してください" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "ファイルのパス" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "ファイルからバックアップをリストア: " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "テキストファイルにバックアップ: " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "テキストファイルのパスを入力してください" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "ファイルからユーザーをインポート: " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "jabberd14 spool ファイルのパスを入力してください" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "ディレクトリからユーザーをインポート: " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "jabberd14 spool ディレクトリのディレクトリを入力してください" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "ディレクトリのパス" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "遅延時間" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "アクセスコントロールリスト設定" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "アクセスコントロールリスト" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "アクセス設定" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "アクセスルール" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "パスワード (確認)" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "登録ユーザー数" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "オンラインユーザー数" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "最終ログイン" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "名簿サイズ" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "IP アドレス" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "リソース" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "管理: " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "ユーザー操作" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "プロパティを編集" - -#: mod_fail2ban.erl:95 -msgid "" -"Too many (~p) failed authentications from this IP address (~s). The address " -"will be unblocked at ~s UTC" -msgstr "" -"~p回の認証に失敗しました。このIPアドレス(~s)は~s UTCまでブロックされます。" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "ファイルサイズを指定してください。" - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "ファイル名を指定してください。" - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "このIPアドレスはアクセスを禁止されています ~s" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "サービスポリシーによってアクセスが禁止されました" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "IRCトランスポート" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "ejabberd IRC module" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "mod_irc の設定には x:data をサポートするクライアントが必要です" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "mod_irc での登録: " - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"IRC サーバーに接続先するために使用するユーザー名、文字エンコーディング、ポー" -"ト、パスワードを入力してください" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "IRC ユーザー名" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"別のポートやパスワード、文字エンコーディングを使用したい場合、'{\"irc server" -"\", \"encoding\", port, \"password\"}' という形式のリストを入力してください。" -"デフォルトでエンコーディングは \"~s\" を使用し、ポートは ~p、パスワードは空に" -"なっています。" - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"例: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net" -"\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "接続パラメーター" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "IRC チャンネルに参加" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "IRC チャンネル (先頭に#は不要)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "IRC サーバー" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "この IRC チャンネルに参加します。" - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Jabber ID: ~s でこの IRC チャンネルに参加" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "IRC 設定" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"IRC サーバーに接続先するためのユーザー名と文字エンコーディングを入力してくだ" -"さい。'Next' を押して次の項目に進みます。'Complete' を押すと設定が保存されま" -"す。" - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "IRC ユーザー名" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "パスワード ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "ポート ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "サーバーのエンコーディング ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "サーバー ~b" - -#: mod_mam.erl:541 -msgid "Only members may query archives of this room" -msgstr "メンバーのみがこのルームのアーカイブを取得できます" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "サービス管理者のみがサービスメッセージを送信できます" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "サービスポリシーによってチャットルームの作成が禁止されています" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "会議室は存在しません" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "チャットルーム" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "空のルーム" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "ニックネームを登録するには x:data をサポートするクライアントが必要です" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "ニックネーム登録: " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "登録するニックネームを入力してください" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "ニックネーム" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "ニックネームはほかの人によって登録されています" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "フォームの\"ニックネーム\"欄を入力する必要があります" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "ejabberd MUCモジュール" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "マルチユーザーチャット" - -#: mod_muc_admin.erl:249 -msgid "Total rooms" -msgstr "チャットルーム数" - -#: mod_muc_admin.erl:250 -msgid "Permanent rooms" -msgstr "永続チャットルーム" - -#: mod_muc_admin.erl:251 -msgid "Registered nicknames" -msgstr "登録ニックネーム" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "チャットルームの一覧" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "チャットルームの設定が変更されました" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "がチャットルームに参加しました" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "がチャットルームから退出しました" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "はバンされました" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "は分掌が変更されたためキックされました" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "はチャットルームがメンバー制に変更されたためキックされました" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "はシステムシャットダウンのためキックされました" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "は名前を変更しました: " - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " は件名を設定しました: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "チャットルームを作りました" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "チャットルームを終了しました" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "チャットルームを開始しました" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "チャットルームを停止しました" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "月曜日" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "火曜日" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "水曜日" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "木曜日" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "金曜日" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "土曜日" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "日曜日" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "1月" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "2月" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "3月" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "4月" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "5月" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "6月" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "7月" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "8月" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "9月" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "10月" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "11月" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "12月" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "チャットルームの設定" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "在室者" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "トラフィックレートの制限を超えました" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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)を送信してルームからキックされました。" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "この会議にプライベートメッセージを送信することはできません" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "新しい発言権の要求を送るまで少し間をおいてください" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "この会議では、発言権の要求はできません" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "発言権要求の承認から JID を取り出すことに失敗しました" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "モデレーターだけが発言権の要求を承認できます" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "誤ったメッセージタイプです" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "" -"種別が\"groupchat\" であるプライベートメッセージを送信することはできません" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "受信者はこの会議室にいません" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "プライベートメッセージを送信することはできません" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "在室者のみがこの会議にメッセージを送ることができます" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "在室者のみが会議にクエリーを送信することができます" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "このチャットルームでは、会議のメンバーへのクエリーは禁止されています" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "モデレーターと参加者のみがチャットルームの件名を変更できます" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "モデレーターのみがチャットルームの件名を変更できます" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "傍聴者はすべての在室者にメッセージを送信することはできません" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "傍聴者はこのチャットルームでニックネームを変更することはできません" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "そのニックネームは既にほかの在室者によって使用されています" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "あなたはこのチャットルームからバンされています" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "このチャットルームに入るにはメンバーでなければなりません" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "このチャットルームに入るにはパスワードが必要です" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "CAPTCHA 要求が多すぎます" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "CAPTCHA を生成できません" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "パスワードが違います" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "管理者権限が必要です" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "モデレーター権限が必要です" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "Jabber ID ~s は無効です" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "ニックネーム ~s はこのチャットルームにいません" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "無効な分掌です: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "無効な役です: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "主宰者の権限が必要です" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "チャットルーム ~s の設定" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "チャットルームのタイトル" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "チャットルームの説明" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "チャットルームを永続化" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "チャットルームを検索可" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "参加者一覧を公開" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "チャットルームをパスワードで保護" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "最大在室者数" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "制限なし" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "本当の Jabber ID を公開" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "モデレーターにのみ" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "誰にでも" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "プレゼンスをブロードキャストするロール" - -#: mod_muc_room.erl:3486 -msgid "Moderator" -msgstr "モデレーター" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "参加者" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "傍聴者" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "チャットルームをメンバーのみに制限" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "チャットルームをモデレート化" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "デフォルトのユーザーは参加者" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "ユーザーによる件名の変更を許可" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "ユーザーによるプライベートメッセージの送信を許可" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "傍聴者によるプライベートメッセージの送信を次の相手に許可" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "誰にも許可しない" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "ユーザーによる他のユーザーへのクエリーを許可" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "ユーザーによる招待を許可" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "傍聴者によるプレゼンス更新のステータス文の送信を許可" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "傍聴者のニックネームの変更を許可" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "傍聴者による発言権の要求を許可" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "発言権の要求の最小時間間隔 (秒)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "チャットルームを CAPTCHA で保護" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "メッセージアーカイブを有効化" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "CAPTCHA 入力を免除する Jabber ID" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "ロギングを有効" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "" -"チャットルームを設定するには x:data をサポートするクライアントが必要です" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "在室者の数" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "プライベート、" - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "発言権を要求" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "発言権の要求を承認または却下します。" - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "ユーザー JID" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "この人に発言権を与えますか ?" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s はあなたをチャットルーム ~s に招待しています" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "パスワードは" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "マルチキャスト" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "ejabberdマルチキャストサービス" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "" -"相手先のオフラインメッセージキューが一杯です。このメッセージは破棄されます。" - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "~s' のオフラインメッセージキュー" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "時間" - -#: mod_offline.erl:812 -msgid "From" -msgstr "差出人" - -#: mod_offline.erl:813 -msgid "To" -msgstr "To" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "パケット" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "オフラインメッセージ:" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "すべてのオフラインメッセージを削除" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "ejabberd SOCKS5 Bytestreams モジュール" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Publish-Subscribe" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "ejabberd Publish-Subscribe モジュール" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "PubSub 購読者のリクエスト" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "このエントリを承認するかどうかを選択してください" - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "ノードID" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "購読者のアドレス" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "この Jabber ID に、この pubsubノードの購読を許可しますか ?" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "イベント通知と同時にペイロードを配送する" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "イベント通知を配送する" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "ノード設定に変更があった時に購読者へ通知する" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "ノードが削除された時に購読者へ通知する" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "アイテムがノードから消された時に購読者へ通知する" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "アイテムをストレージに保存する" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "ノードのフレンドリネーム" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "アイテムの最大保存数" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "購読を許可するかどうか" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "アクセスモデルを設定する" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "名簿グループは購読を許可しました" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "公開モデルを指定する" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "公開者がオフラインになるときに、すべてのアイテムを削除" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "イベントメッセージ種別を設定" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "最大ぺイロードサイズ (byte)" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "最後の公開アイテムを送信するタイミングで" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "有効なユーザーにのみ告知を送信する" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "提携されたノードの集合です" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "CAPTCHA 検証は失敗しました" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "登録を行うには x:data と CAPTCHA をサポートするクライアントが必要です" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "サーバーに登録するユーザー名とパスワードを選択してください" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "このパスワードは単純過ぎます" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "それほど速くアカウントを登録することはできません" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "Jabber アカウントの作成に成功しました。" - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "アカウントの作成中にエラーが発生しました: " - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "Jabber アカウントの削除に成功しました。" - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "アカウントの削除中にエラーが発生しました: " - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "Jabber アカウントのパスワード変更に成功しました。" - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "パスワードの変更中にエラーが発生しました: " - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Jabber アカウント登録" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Jabber アカウントを登録" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Jabber アカウントを削除" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"ここはこの Jabber サーバーにアカウントを作成するページです。あなたの JID " -"(JabberID) は username@server のような形式になります。注意事項どおり、正しく" -"項目を記入してください。" - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "ユーザー名:" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" -"大文字と小文字は区別しません: macbeth は MacBeth や Macbeth と同じです。" - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "使用できない文字:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "サーバー:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" -"パスワードは誰にも教えないようにしてください。Jabber サーバーの管理者があなた" -"にパスワードを尋ねることはありません。" - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "あなたは後で Jabber クライアントを使用してパスワードを変更できます。" - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Jabber クライアントはコンピューターにパスワードを記憶できます。コンピューター" -"が安全であると信頼できる場合にのみ、この機能を使用してください。" - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"パスワードは記憶するか、紙に書いて安全な場所に保管してください。もしあなたが" -"パスワードを忘れてしまった場合、Jabber ではパスワードのリカバリを自動的に行う" -"ことはできません。" - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "パスワード (確認):" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "登録" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "古いパスワード:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "新しいパスワード:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "このページはサーバー上のJabberアカウントを削除するページです。" - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "削除" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "認可" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "保留" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "グループ" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "検証" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "削除" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "名簿: " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Jabber ID を追加" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "名簿" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "共有名簿グループ" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "名前:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "説明:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "メンバー:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "表示グループ:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "グループ" - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Erlang Jabber Server" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "誕生日" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "都道府県" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "国" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "メールアドレス" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "姓" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"項目を入力してユーザーを検索を行えます (* を使用すると部分文字列にマッチしま" -"す)" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "氏名" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "ミドルネーム" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "会社名" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "部署名" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "ユーザーの検索: " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "検索を行うためには x:data をサポートするクライアントが必要です" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "vCard検索" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "ejabberd vCard モジュール" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "検索結果: " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "項目を入力してユーザーを検索してください" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "外向き s2s サービス:" - -#~ msgid "Delete" -#~ msgstr "削除" - -#~ msgid "This room is not anonymous" -#~ msgstr "このチャットルームは非匿名です" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "エラーメッセージを送信したため、この参加者はキックされました" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "他の参加者にエラーメッセージを送信したため、この参加者はキックされました" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "エラープレゼンスを送信したため、この参加者はキックされました" - -#~ msgid "ejabberd virtual hosts" -#~ msgstr "ejabberd バーチャルホスト" - -#~ msgid "Captcha test failed" -#~ msgstr "キャプチャのテストに失敗しました" - -#~ msgid "Encodings" -#~ msgstr "エンコーディング" - -#~ msgid "(Raw)" -#~ msgstr "(Raw)" - -#~ msgid "Specified nickname is already registered" -#~ msgstr "指定されたニックネームは既に登録されています" - -#~ msgid "Size" -#~ msgstr "サイズ" diff --git a/priv/msgs/nl.msg b/priv/msgs/nl.msg index 8bb1c0ebf..d7799f9c5 100644 --- a/priv/msgs/nl.msg +++ b/priv/msgs/nl.msg @@ -1,20 +1,20 @@ -%% -*- coding: latin-1 -*- -{"Access Configuration","Toegangsinstellingen"}. -{"Access Control List Configuration","Instellingen van access control lists"}. -{"Access control lists","Access control lists"}. -{"Access Control Lists","Access control lists"}. -{"Access denied by service policy","De toegang werd geweigerd door het beleid van deze dienst"}. -{"Access rules","Access rules"}. -{"Access Rules","Access rules"}. -{"Action on user","Actie op gebruiker"}. -{"Add Jabber ID","Jabber ID toevoegen"}. -{"Add New","Toevoegen"}. -{"Add User","Gebruiker toevoegen"}. -{"Administration","Beheer"}. -{"Administration of ","Beheer van "}. -{"Administrator privileges required","U hebt beheerdersprivileges nodig"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" (Add * to the end of field to match substring)"," Gebruik de velden om te zoeken (Voeg achteraan het teken * toe om te zoeken naar alles wat met het eerste deel begint.)."}. +{" has set the subject to: "," veranderde het onderwerp in: "}. {"A friendly name for the node","Bijnaam voor deze knoop"}. +{"A password is required to enter this room","U hebt een wachtwoord nodig om deze chatruimte te kunnen betreden"}. +{"Access denied by service policy","De toegang werd geweigerd door het beleid van deze dienst"}. +{"Action on user","Actie op gebruiker"}. +{"Add User","Gebruiker toevoegen"}. +{"Administration of ","Beheer van "}. +{"Administration","Beheer"}. +{"Administrator privileges required","U hebt beheerdersprivileges nodig"}. {"All activity","Alle activiteit"}. +{"All Users","Alle gebruikers"}. {"Allow this Jabber ID to subscribe to this pubsub node?","Deze gebruiker toestaan te abonneren op deze pubsub node?"}. {"Allow users to change the subject","Sta gebruikers toe het onderwerp te veranderen"}. {"Allow users to query other users","Gebruikers mogen naar andere gebruikers verzoeken verzenden"}. @@ -24,16 +24,13 @@ {"Allow visitors to send private messages to","Gebruikers mogen privéberichten verzenden aan"}. {"Allow visitors to send status text in presence updates","Sta bezoekers toe hun statusbericht in te stellen"}. {"Allow visitors to send voice requests","Gebruikers mogen stemaanvragen verzenden"}. -{"All Users","Alle gebruikers"}. {"Announcements","Mededelingen"}. -{"anyone","iedereen"}. -{"A password is required to enter this room","U hebt een wachtwoord nodig om deze chatruimte te kunnen betreden"}. {"April","April"}. {"August","Augustus"}. -{"Backup","Backup"}. {"Backup Management","Backup"}. {"Backup of ~p","Backup maken van ~p"}. {"Backup to File at ","Binaire backup maken op "}. +{"Backup","Backup"}. {"Bad format","Verkeerd formaat"}. {"Birthday","Geboortedatum"}. {"CAPTCHA web page","CAPTCHA webpagina."}. @@ -47,62 +44,44 @@ {"Chatroom is stopped","Gespreksruimte gestopt"}. {"Chatrooms","Groepsgesprekken"}. {"Choose a username and password to register with this server","Kies een gebruikersnaam en een wachtwoord om u te registreren op deze server"}. -{"Choose modules to stop","Selecteer de modules die u wilt stoppen"}. {"Choose storage type of tables","Opslagmethode voor tabellen kiezen"}. {"Choose whether to approve this entity's subscription.","Beslis of dit verzoek tot abonneren zal worden goedgekeurd"}. {"City","Plaats"}. {"Commands","Commando's"}. {"Conference room does not exist","De chatruimte bestaat niet"}. -{"Configuration","Instellingen"}. {"Configuration of room ~s","Instellingen van chatruimte ~s"}. -{"Connected Resources:","Verbonden bronnen:"}. -{"Connections parameters","Verbindingsparameters"}. +{"Configuration","Instellingen"}. {"Country","Land"}. -{"CPU Time:","Processortijd:"}. -{"Database","Database"}. -{"Database Tables at ~p","Databasetabellen van ~p"}. {"Database Tables Configuration at ","Instellingen van databasetabellen op "}. +{"Database","Database"}. {"December","December"}. {"Default users as participants","Gebruikers standaard instellen als deelnemers"}. -{"Delete message of the day","Bericht van de dag verwijderen"}. {"Delete message of the day on all hosts","Verwijder bericht-van-de-dag op alle hosts"}. -{"Delete Selected","Geselecteerde verwijderen"}. +{"Delete message of the day","Bericht van de dag verwijderen"}. {"Delete User","Verwijder Gebruiker"}. {"Deliver event notifications","Gebeurtenisbevestigingen Sturen"}. {"Deliver payloads with event notifications","Berichten bezorgen samen met gebeurtenisnotificaties"}. -{"Description:","Beschrijving:"}. {"Disc only copy","Harde schijf"}. -{"Displayed Groups:","Weergegeven groepen:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","Geef Uw wachtwoord aan niemand, zelfs niet aan de beheerders van deze Jabber-server."}. {"Dump Backup to Text File at ","Backup naar een tekstbestand schrijven op "}. {"Dump to Text File","Backup naar een tekstbestand schrijven"}. {"Edit Properties","Eigenschappen bewerken"}. {"Either approve or decline the voice request.","Keur stemaanvraag goed of af."}. -{"ejabberd IRC module","ejabberd's IRC-module"}. {"ejabberd MUC module","ejabberd's MUC module"}. {"ejabberd Multicast service","ejabberd Multicast service"}. {"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe module"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams module"}. {"ejabberd vCard module","ejabberd's vCard-module"}. {"ejabberd Web Admin","ejabberd Webbeheer"}. -{"Elements","Elementen"}. {"Email","E-mail"}. {"Enable logging","Logs aanzetten"}. {"Enable message archiving","Zet bericht-archivering aan"}. -{"Encoding for server ~b","Karakterset voor server ~b"}. {"End User Session","Verwijder Gebruikers-sessie"}. -{"Enter list of {Module, [Options]}","Voer lijst met op te starten modules als volgt in: {Module, [Opties]}"}. {"Enter nickname you want to register","Voer de bijnaam in die u wilt registreren"}. {"Enter path to backup file","Voer pad naar backupbestand in"}. {"Enter path to jabberd14 spool dir","Voer pad naar jabberd14-spool-directory in"}. {"Enter path to jabberd14 spool file","Voer pad naar jabberd14-spool-bestand in"}. {"Enter path to text file","Voer pad naar backupbestand in"}. {"Enter the text you see","Voer de getoonde tekst in"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Voer de gebruikersnaam en de coderingen in die u wilt gebruiken voor verbindingen met IRC-servers. Klik op 'Volgende' om meer velden aan te maken. Klik op \"Voltooi' om de instellingen op te slaan."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Voer de gebruikersnaam, coderingen, poorten en wachtwoorden in die U wilt gebruiken voor het verbinden met IRC-servers"}. -{"Erlang Jabber Server","Erlang Jabber Server"}. -{"Error","Fout"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Voorbeeld: [{\"irc.example.org\", \"koi8-r\", 6667, \"geheim\"}, {\"vendetta.example.net\", \"iso8859-1\", 7000}, {irc,testserver.nl\", \"utf-8\"}]."}. {"Exclude Jabber IDs from CAPTCHA challenge","Geen CAPTCHA test voor Jabber IDs"}. {"Export all tables as SQL queries to a file:","Exporteer alle tabellen als SQL-queries naar een bestand:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exporteer data van alle gebruikers in de server naar PIEFXIS-bestanden (XEP-0227):"}. @@ -110,29 +89,19 @@ {"Failed to extract JID from your voice request approval","Er kon geen JID worden ontleend uit deze stemaanvraag"}. {"Family Name","Achternaam"}. {"February","Februari"}. -{"Fill in fields to search for any matching Jabber User","Vul de velden in om te zoeken naar Jabber-gebruikers op deze server"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Gebruik de velden om te zoeken (Voeg achteraan het teken * toe om te zoeken naar alles wat met het eerste deel begint.)."}. {"Friday","Vrijdag"}. -{"From ~s","Van ~s"}. -{"From","Van"}. {"Full Name","Volledige naam"}. {"Get Number of Online Users","Aantal Aanwezige Gebruikers Opvragen"}. {"Get Number of Registered Users","Aantal Geregistreerde Gebruikers Opvragen"}. {"Get User Last Login Time","Tijd van Laatste Aanmelding Opvragen"}. -{"Get User Password","Gebruikerswachtwoord Opvragen"}. {"Get User Statistics","Gebruikers-statistieken Opvragen"}. {"Grant voice to this person?","Stemaanvraag honoreren voor deze persoon?"}. -{"Group ","Groep "}. -{"Groups","Groepen"}. {"has been banned","is verbannen"}. -{"has been kicked because of an affiliation change","is weggestuurd vanwege een affiliatieverandering"}. {"has been kicked because of a system shutdown","is weggestuurd omdat het systeem gestopt wordt"}. +{"has been kicked because of an affiliation change","is weggestuurd vanwege een affiliatieverandering"}. {"has been kicked because the room has been changed to members-only","is weggestuurd omdat de chatruimte vanaf heden alleen toegankelijk is voor leden"}. {"has been kicked","is weggestuurd"}. -{" has set the subject to: "," veranderde het onderwerp in: "}. -{"Host","Host"}. {"If you don't see the CAPTCHA image here, visit the web page.","Als U het CAPTCHA-plaatje niet ziet, bezoek dan de webpagina."}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Als u verschillende poorten, wachtwoorden en coderingen wilt opgeven voor elke IRC-server, vul dan deze lijst met het volgende formaat: '{\"IRC-server\", \"codering\", poort, \"wachtwoord\"}'. Standaard gebruikt deze service de codering \"~s\", poort ~p, leeg wachtwoord."}. {"Import Directory","Directory importeren"}. {"Import File","Bestand importeren"}. {"Import user data from jabberd14 spool file:","Importeer gebruikersdata via spool-bestanden van jabberd14"}. @@ -143,28 +112,13 @@ {"Import Users From jabberd14 Spool Files","Importeer gebruikers via spool-bestanden van jabberd14"}. {"Improper message type","Onjuist berichttype"}. {"Incorrect password","Foutief wachtwoord"}. -{"Invalid affiliation: ~s","Ongeldige affiliatie: ~s"}. -{"Invalid role: ~s","Ongeldige rol: ~s"}. {"IP addresses","IP-adres"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","IRC kanaal (zonder eerste #)"}. -{"IRC server","IRC-server"}. -{"IRC settings","IRC instellingen"}. -{"IRC Transport","IRC-transport"}. -{"IRC username","Gebruikersnaam voor IRC"}. -{"IRC Username","Gebruikersnaam voor IRC:"}. {"is now known as","heet nu"}. -{"It is not allowed to send private messages","Het is niet toegestaan priveberichten te sturen"}. {"It is not allowed to send private messages of type \"groupchat\"","Er mogen geen privéberichten van het type \"groupchat\" worden verzonden"}. {"It is not allowed to send private messages to the conference","Er mogen geen privéberichten naar de chatruimte worden verzonden"}. -{"Jabber Account Registration","Jabber-account registratie"}. {"Jabber ID","Jabber ID"}. -{"Jabber ID ~s is invalid","De Jabber ID ~s is ongeldig"}. {"January","Januari"}. -{"Join IRC channel","Ga IRC kanaal binnen"}. {"joins the room","betrad de chatruimte"}. -{"Join the IRC channel here.","Ga het IRC kanaal binnen"}. -{"Join the IRC channel in this Jabber ID: ~s","Ga het IRC kanaal van deze Jabber ID binnen: ~s"}. {"July","Juli"}. {"June","Juni"}. {"Last Activity","Laatste activiteit"}. @@ -172,11 +126,6 @@ {"Last month","Afgelopen maand"}. {"Last year","Afgelopen jaar"}. {"leaves the room","verliet de chatruimte"}. -{"Listened Ports at ","Openstaande poorten op "}. -{"Listened Ports","Openstaande poorten"}. -{"List of modules to start","Lijst met op te starten modules"}. -{"List of rooms","Lijst van groepsgesprekken"}. -{"Low level update script","Lowlevel script voor de opwaardering"}. {"Make participants list public","Deelnemerslijst publiek maken"}. {"Make room CAPTCHA protected","Chatruimte beveiligen met een geautomatiseerde Turing test"}. {"Make room members-only","Chatruimte enkel toegankelijk maken voor leden"}. @@ -185,43 +134,31 @@ {"Make room persistent","Chatruimte blijvend maken"}. {"Make room public searchable","Chatruimte doorzoekbaar maken"}. {"March","Maart"}. -{"Maximum Number of Occupants","Maximum aantal aanwezigen"}. -{"Max # of items to persist","Maximum aantal in het geheugen te bewaren items"}. {"Max payload size in bytes","Maximumgrootte van bericht in bytes"}. +{"Maximum Number of Occupants","Maximum aantal aanwezigen"}. {"May","Mei"}. -{"Members:","Groepsleden:"}. {"Membership is required to enter this room","U moet lid zijn om deze chatruimte te kunnen betreden"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Onthou het wachtwoord, of schrijf het op en bewaar het op een veilige plaats. Met Jabber is er geen geautomatiseerde manier om het wachtwoord terug te halen als U het vergeet."}. -{"Memory","Geheugen"}. {"Message body","Bericht"}. {"Middle Name","Tussennaam"}. {"Minimum interval between voice requests (in seconds)","Minimale interval tussen stemaanvragen (in seconden)"}. {"Moderator privileges required","U hebt moderatorprivileges nodig"}. -{"moderators only","moderators"}. -{"Modified modules","Gewijzigde modules"}. -{"Module","Module"}. -{"Modules at ~p","Modules op ~p"}. -{"Modules","Modules"}. {"Monday","Maandag"}. {"Multicast","Multicast"}. {"Multi-User Chat","Groepschat"}. -{"Name:","Naam:"}. {"Name","Naam"}. {"Never","Nooit"}. {"New Password:","Nieuw Wachtwoord:"}. -{"Nickname","Bijnaam"}. {"Nickname Registration at ","Registratie van een bijnaam op "}. {"Nickname ~s does not exist in the room","De bijnaam ~s bestaat niet in deze chatruimte"}. -{"nobody","niemand"}. +{"Nickname","Bijnaam"}. {"No body provided for announce message","De mededeling bevat geen bericht"}. {"No Data","Geen gegevens"}. +{"No limit","Geen limiet"}. {"Node ID","Node ID"}. {"Node not found","Node niet gevonden"}. {"Node ~p","Node ~p"}. {"Nodes","Nodes"}. -{"No limit","Geen limiet"}. {"None","Geen"}. -{"No resource provided","Geen bron opgegeven"}. {"Not Found","Niet gevonden"}. {"Notify subscribers when items are removed from the node","Abonnees informeren wanneer items verwijderd worden uit de node"}. {"Notify subscribers when the node configuration changes","Abonnees informeren wanneer de instellingen van de node veranderen"}. @@ -231,13 +168,10 @@ {"Number of online users","Aantal Aanwezige Gebruikers"}. {"Number of registered users","Aantal Geregistreerde Gebruikers"}. {"October","Oktober"}. -{"Offline Messages:","Offline berichten:"}. -{"Offline Messages","Offline berichten"}. {"OK","OK"}. {"Old Password:","Oud Wachtwoord:"}. -{"Online","Online"}. -{"Online Users:","Online gebruikers:"}. {"Online Users","Online gebruikers"}. +{"Online","Online"}. {"Only deliver notifications to available users","Notificaties alleen verzenden naar online gebruikers"}. {"Only moderators and participants are allowed to change the subject in this room","Alleen moderators en deelnemers mogen het onderwerp van deze chatruimte veranderen"}. {"Only moderators are allowed to change the subject in this room","Alleen moderators mogen het onderwerp van deze chatruimte veranderen"}. @@ -245,83 +179,59 @@ {"Only occupants are allowed to send messages to the conference","Alleen aanwezigen mogen berichten naar de chatruimte verzenden"}. {"Only occupants are allowed to send queries to the conference","Alleen aanwezigen mogen verzoeken verzenden naar de chatruimte"}. {"Only service administrators are allowed to send service messages","Alleen beheerders van deze dienst mogen mededelingen verzenden naar alle chatruimtes"}. -{"Options","Opties"}. {"Organization Name","Organisatie"}. {"Organization Unit","Afdeling"}. -{"Outgoing s2s Connections:","Uitgaande s2s-verbindingen:"}. {"Outgoing s2s Connections","Uitgaande s2s-verbindingen"}. {"Owner privileges required","U hebt eigenaarsprivileges nodig"}. -{"Packet","Pakket"}. -{"Password ~b","Wachtwoord ~b"}. -{"Password Verification:","Wachtwoord Bevestiging:"}. {"Password Verification","Wachtwoord Bevestiging"}. -{"Password:","Wachtwoord:"}. +{"Password Verification:","Wachtwoord Bevestiging:"}. {"Password","Wachtwoord"}. +{"Password:","Wachtwoord:"}. {"Path to Dir","Pad naar directory"}. {"Path to File","Pad naar bestand"}. -{"Pending","Bezig"}. {"Period: ","Periode: "}. -{"Permanent rooms","Permanente groepsgesprekken"}. {"Persist items to storage","Items in het geheugen bewaren"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Merk op dat volgende opties enkel backups maken van de ingebouwde database Mnesia. Als U de ODBC module gebruikt dan moeten daarvan afzonderlijke backups gemaakt worden."}. {"Please, wait for a while before sending new voice request","Wacht s.v.p. met het maken van een nieuwe stemaanvraag."}. {"Pong","Pong"}. -{"Port ~b","Poort ~b"}. -{"Port","Poort"}. {"Present real Jabber IDs to","Jabber ID's kunnen achterhaald worden door"}. {"private, ","privé, "}. -{"Protocol","Protocol"}. {"Publish-Subscribe","Publish-Subscribe"}. {"PubSub subscriber request","PubSub abonnee verzoek"}. {"Purge all items when the relevant publisher goes offline","Verwijder alle items wanneer de gerelateerde publiceerder offline gaat"}. {"Queries to the conference members are not allowed in this room","Er mogen geen verzoeken verzenden worden naar deelnemers in deze chatruimte"}. {"RAM and disc copy","RAM en harde schijf"}. {"RAM copy","RAM"}. -{"Raw","Ruw"}. {"Really delete message of the day?","Wilt u het bericht van de dag verwijderen?"}. {"Recipient is not in the conference room","De ontvanger is niet in de chatruimte"}. -{"Register a Jabber account","Registreer een Jabber-account"}. -{"Registered nicknames","Geregistreerde gebruikersnamen"}. -{"Registered Users:","Geregistreerde gebruikers:"}. -{"Registered Users","Geregistreerde gebruikers"}. {"Register","Registreer"}. -{"Registration in mod_irc for ","Registratie van "}. {"Remote copy","Op andere nodes in de cluster"}. -{"Remove All Offline Messages","Verwijder alle offline berichten"}. {"Remove User","Gebruiker verwijderen"}. -{"Remove","Verwijderen"}. {"Replaced by new connection","Vervangen door een nieuwe verbinding"}. {"Resources","Bronnen"}. -{"Restart","Herstarten"}. {"Restart Service","Herstart Service"}. {"Restore Backup from File at ","Binaire backup direct herstellen op "}. -{"Restore","Binaire backup direct herstellen"}. {"Restore binary backup after next ejabberd restart (requires less memory):","Binaire backup herstellen na herstart van ejabberd (vereist minder geheugen):"}. {"Restore binary backup immediately:","Binaire backup direct herstellen:"}. {"Restore plain text backup immediately:","Backup in een tekstbestand direct herstellen:"}. +{"Restore","Binaire backup direct herstellen"}. {"Room Configuration","Instellingen van de chatruimte"}. {"Room creation is denied by service policy","De aanmaak van de chatruimte is verhinderd door de instellingen van deze server"}. {"Room description","Beschrijving"}. {"Room Occupants","Aantal aanwezigen"}. {"Room title","Naam van de chatruimte"}. {"Roster groups allowed to subscribe","Contactlijst-groepen die mogen abonneren"}. -{"Roster of ","Roster van "}. -{"Roster","Roster"}. {"Roster size","Contactlijst Groote"}. -{"RPC Call Error","RPC-oproepfout"}. {"Running Nodes","Draaiende nodes"}. -{"~s access rule configuration","Access rules op ~s"}. {"Saturday","Zaterdag"}. -{"Script check","Controle van script"}. {"Search Results for ","Zoekresultaten voor "}. {"Search users in ","Gebruikers zoeken in "}. -{"Send announcement to all online users","Mededeling verzenden naar alle online gebruikers"}. {"Send announcement to all online users on all hosts","Mededeling verzenden naar alle online gebruikers op alle virtuele hosts"}. -{"Send announcement to all users","Mededeling verzenden naar alle gebruikers"}. +{"Send announcement to all online users","Mededeling verzenden naar alle online gebruikers"}. {"Send announcement to all users on all hosts","Stuur aankondiging aan alle gebruikers op alle hosts"}. +{"Send announcement to all users","Mededeling verzenden naar alle gebruikers"}. {"September","September"}. -{"Server ~b","Server ~b"}. {"Server:","Server:"}. {"Set message of the day and send to online users","Bericht van de dag instellen en verzenden naar online gebruikers"}. {"Set message of the day on all hosts and send to online users","Stel bericht-van-de-dag in op alle hosts en stuur naar aanwezige gebruikers"}. @@ -329,81 +239,45 @@ {"Show Integral Table","Volledige tabel laten zien"}. {"Show Ordinary Table","Deel van tabel laten zien"}. {"Shut Down Service","Stop Service"}. -{"~s invites you to the room ~s","~s nodigt je uit voor het groepsgesprek ~s"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Sommige Jabber-clienten kunnen het wachtwoord opslaan op Uw computer. Gebruik deze mogelijkheid alleen als U vertrouwd dat Uw computer afdoende beveiligd is."}. {"Specify the access model","Geef toegangsmodel"}. {"Specify the event message type","Geef type van eventbericht"}. {"Specify the publisher model","Publicatietype opgeven"}. -{"~s's Offline Messages Queue","offline berichten van ~s"}. -{"Start Modules at ","Modules starten op "}. -{"Start Modules","Modules starten"}. -{"Start","Starten"}. -{"Statistics of ~p","Statistieken van ~p"}. -{"Statistics","Statistieken"}. -{"Stop Modules at ","Modules stoppen op "}. -{"Stop Modules","Modules stoppen"}. {"Stopped Nodes","Gestopte nodes"}. -{"Stop","Stoppen"}. -{"Storage Type","Opslagmethode"}. {"Store binary backup:","Binaire backup maken:"}. {"Store plain text backup:","Backup naar een tekstbestand schrijven:"}. {"Subject","Onderwerp"}. {"Submitted","Verzonden"}. -{"Submit","Verzenden"}. {"Subscriber Address","Abonnee Adres"}. -{"Subscription","Inschrijving"}. {"Sunday","Zondag"}. {"That nickname is already in use by another occupant","Deze bijnaam is al in gebruik door een andere aanwezige"}. {"That nickname is registered by another person","Deze bijnaam is al geregistreerd door iemand anders"}. {"The CAPTCHA is valid.","De geautomatiseerde Turing-test is geslaagd."}. {"The CAPTCHA verification has failed","De CAPTCHA-verificatie is mislukt"}. {"The collections with which a node is affiliated","De collecties waar een node mee is gerelateerd"}. -{"the password is","het wachtwoord is"}. {"The password is too weak","Het wachtwoord is te zwak"}. -{"The password of your Jabber account was successfully changed.","Het wachtwoord van Uw Jabber-account is succesvol veranderd."}. -{"There was an error changing the password: ","Er was een fout bij het veranderen van het wachtwoord:"}. +{"the password is","het wachtwoord is"}. {"There was an error creating the account: ","Er was een fout bij het creeern van de account:"}. {"There was an error deleting the account: ","Er was een fout bij het verwijderen van de account."}. -{"This IP address is blacklisted in ~s","Dit IP-adres is geblokkeerd in ~s"}. -{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Dit is niet hoofdlettergevoelig: macbeth is hetzelfde als MacBeth en Macbeth."}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Deze pagina maakt het mogelijk een Jabber-account te registreren op deze server. Uw JID (Jabber IDentiteit) zal er als volg uit zien: gebruikersnaam@server. Lees de instructies zorgvuldig teneinde de velden correct in te vullen."}. -{"This page allows to unregister a Jabber account in this Jabber server.","Deze pagina maakt het mogelijk een Jabber-account op deze server op te heffen."}. +{"This room is not anonymous","Deze chatruimte is niet anoniem"}. {"Thursday","Donderdag"}. {"Time delay","Vertraging"}. -{"Time","Tijd"}. -{"To","Aan"}. {"Too many CAPTCHA requests","Te veel CAPTCHA-aanvragen"}. {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Te veel (~p) mislukte authenticatie-pogingen van dit IP-adres (~s). Dit adres zal worden gedeblokkeerd om ~s UTC"}. {"Too many unacked stanzas","Te veel niet-bevestigde stanzas"}. -{"To ~s","Naar ~s"}. -{"Total rooms","Aantal groepsgesprekken"}. {"Traffic rate limit is exceeded","Dataverkeerslimiet overschreden"}. -{"Transactions Aborted:","Afgebroken transacties:"}. -{"Transactions Committed:","Bevestigde transacties:"}. -{"Transactions Logged:","Gelogde transacties:"}. -{"Transactions Restarted:","Herstarte transacties:"}. {"Tuesday","Dinsdag"}. {"Unable to generate a CAPTCHA","Het generen van een CAPTCHA is mislukt"}. {"Unauthorized","Niet geautoriseerd"}. -{"Unregister a Jabber account","Opheffen van Jabber-account"}. {"Unregister","Opheffen"}. -{"Update","Bijwerken"}. {"Update message of the day (don't send)","Bericht van de dag bijwerken (niet verzenden)"}. {"Update message of the day on all hosts (don't send)","Verander bericht-van-de-dag op alle hosts (niet versturen)"}. -{"Update plan","Plan voor de opwaardering"}. -{"Update ~p","Opwaarderen van ~p"}. -{"Update script","Script voor de opwaardering"}. -{"Uptime:","Uptime:"}. -{"Use of STARTTLS required","Gebruik van STARTTLS is vereist"}. -{"User","Gebruiker"}. {"User JID","JID Gebruiker"}. {"User Management","Gebruikersbeheer"}. +{"User","Gebruiker"}. {"Username:","Gebruikersnaam:"}. {"Users are not allowed to register accounts so quickly","Het is gebruikers niet toegestaan zo snel achter elkaar te registreren"}. -{"User ~s","Gebruiker ~s"}. -{"Users","Gebruikers"}. {"Users Last Activity","Laatste activiteit van gebruikers"}. -{"Validate","Bevestigen"}. +{"Users","Gebruikers"}. {"vCard User Search","Gebruikers zoeken"}. {"Virtual Hosts","Virtuele hosts"}. {"Visitors are not allowed to change their nicknames in this room","Het is bezoekers niet toegestaan hun naam te veranderen in dit kanaal"}. @@ -413,16 +287,11 @@ {"Wednesday","Woensdag"}. {"When to send the last published item","Wanneer het laatst gepubliceerde item verzonden moet worden"}. {"Whether to allow subscriptions","Abonnementsaanvraag toestaan"}. -{"You can later change your password using a Jabber client.","U can het wachtwoord later veranderen met een Jabber-client."}. {"You have been banned from this room","U werd verbannen uit deze chatruimte"}. {"You must fill in field \"Nickname\" in the form","U moet het veld \"bijnaam\" invullen"}. {"You need a client that supports x:data and CAPTCHA to register","U hebt een client nodig die x:data en CAPTCHA ondersteunt om een bijnaam te registreren"}. {"You need a client that supports x:data to register the nickname","U hebt een client nodig die x:data ondersteunt om een bijnaam te registreren"}. -{"You need an x:data capable client to configure mod_irc settings","U hebt een client nodig die x:data ondersteunt om dit IRC-transport in te stellen"}. -{"You need an x:data capable client to configure room","U hebt een client nodig die x:data ondersteunt om deze chatruimte in te stellen"}. {"You need an x:data capable client to search","U hebt een client nodig die x:data ondersteunt om te zoeken"}. {"Your active privacy list has denied the routing of this stanza.","Uw actieve privacy-lijst verbied het routeren van dit stanza."}. {"Your contact offline message queue is full. The message has been discarded.","Te veel offline berichten voor dit contactpersoon. Het bericht is niet opgeslagen."}. -{"Your Jabber account was successfully created.","Uw Jabber-account is succesvol gecreeerd."}. -{"Your Jabber account was successfully deleted.","Uw Jabber-account is succesvol verwijderd."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","Uw berichten aan ~s worden geblokkeerd. Om ze te deblokkeren, ga naar ~s"}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Uw berichten aan ~s worden geblokkeerd. Om ze te deblokkeren, ga naar ~s"}. diff --git a/priv/msgs/nl.po b/priv/msgs/nl.po deleted file mode 100644 index 54a467d1e..000000000 --- a/priv/msgs/nl.po +++ /dev/null @@ -1,1949 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: 2.1.0-alpha\n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: Andreas van Cranenburgh \n" -"Language-Team: \n" -"Language: nl\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Dutch (nederlands)\n" -"X-Additional-Translator: Sander Devrieze\n" -"X-Generator: Poedit 1.6.10\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "Gebruik van STARTTLS is vereist" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Geen bron opgegeven" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Vervangen door een nieuwe verbinding" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "is weggestuurd" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "Uw actieve privacy-lijst verbied het routeren van dit stanza." - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "Te veel niet-bevestigde stanzas" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Voer de getoonde tekst in" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "" -"Uw berichten aan ~s worden geblokkeerd. Om ze te deblokkeren, ga naar ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "Als U het CAPTCHA-plaatje niet ziet, bezoek dan de webpagina." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "CAPTCHA webpagina." - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "De geautomatiseerde Turing-test is geslaagd." - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Gebruiker" - -#: ejabberd_oauth.erl:256 -#, fuzzy -msgid "Server" -msgstr "Server:" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Wachtwoord" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "Niet geautoriseerd" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "ejabberd Webbeheer" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Beheer" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Access control lists" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Verzonden" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Verkeerd formaat" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Verzenden" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "Ruw" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Geselecteerde verwijderen" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Access rules" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "Access rules op ~s" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "Virtuele hosts" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Gebruikers" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "Online gebruikers" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Laatste activiteit van gebruikers" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Periode: " - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "Afgelopen maand" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "Afgelopen jaar" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "Alle activiteit" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Deel van tabel laten zien" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Volledige tabel laten zien" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Statistieken" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "Niet gevonden" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Node niet gevonden" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Toevoegen" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Host" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Geregistreerde gebruikers" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Gebruiker toevoegen" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Offline berichten" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Laatste activiteit" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Nooit" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Online" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Geregistreerde gebruikers:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Online gebruikers:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Uitgaande s2s-verbindingen:" - -#: ejabberd_web_admin.erl:1559 -#, fuzzy -msgid "Incoming s2s Connections:" -msgstr "Uitgaande s2s-verbindingen:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Geen" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Wachtwoord wijzigen" - -#: ejabberd_web_admin.erl:1673 -msgid "User ~s" -msgstr "Gebruiker ~s" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Verbonden bronnen:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Wachtwoord:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Gebruiker verwijderen" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Geen gegevens" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Nodes" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Draaiende nodes" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Gestopte nodes" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -msgid "Node ~p" -msgstr "Node ~p" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "Database" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Backup" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Openstaande poorten" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Bijwerken" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Herstarten" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Stoppen" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Modules" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "RPC-oproepfout" - -#: ejabberd_web_admin.erl:1917 -msgid "Database Tables at ~p" -msgstr "Databasetabellen van ~p" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Naam" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Opslagmethode" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Elementen" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Geheugen" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Fout" - -#: ejabberd_web_admin.erl:1955 -msgid "Backup of ~p" -msgstr "Backup maken van ~p" - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Merk op dat volgende opties enkel backups maken van de ingebouwde database " -"Mnesia. Als U de ODBC module gebruikt dan moeten daarvan afzonderlijke " -"backups gemaakt worden." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Binaire backup maken:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "OK" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Binaire backup direct herstellen:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"Binaire backup herstellen na herstart van ejabberd (vereist minder geheugen):" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Backup naar een tekstbestand schrijven:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Backup in een tekstbestand direct herstellen:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "Importeer gebruikersdata van een PIEFXIS-bestand (XEP-0227):" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" -"Exporteer data van alle gebruikers in de server naar PIEFXIS-bestanden " -"(XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "" -"Exporteer data van alle gebruikers van een host naar PIEXFIS-bestanden " -"(XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "Exporteer alle tabellen als SQL-queries naar een bestand:" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Importeer gebruikersdata via spool-bestanden van jabberd14" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Importeer gebruikersdata via spool-bestanden van jabberd14" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Openstaande poorten op " - -#: ejabberd_web_admin.erl:2144 -msgid "Modules at ~p" -msgstr "Modules op ~p" - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "Statistieken van ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Uptime:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "Processortijd:" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Bevestigde transacties:" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Afgebroken transacties:" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Herstarte transacties:" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Gelogde transacties:" - -#: ejabberd_web_admin.erl:2243 -msgid "Update ~p" -msgstr "Opwaarderen van ~p" - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "Plan voor de opwaardering" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Gewijzigde modules" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Script voor de opwaardering" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Lowlevel script voor de opwaardering" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Controle van script" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Poort" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Protocol" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Module" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Opties" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Starten" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Commando's" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "Wilt u het bericht van de dag verwijderen?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Onderwerp" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Bericht" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "De mededeling bevat geen bericht" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Mededelingen" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Mededeling verzenden naar alle gebruikers" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Stuur aankondiging aan alle gebruikers op alle hosts" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Mededeling verzenden naar alle online gebruikers" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "" -"Mededeling verzenden naar alle online gebruikers op alle virtuele hosts" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Bericht van de dag instellen en verzenden naar online gebruikers" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" -"Stel bericht-van-de-dag in op alle hosts en stuur naar aanwezige gebruikers" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Bericht van de dag bijwerken (niet verzenden)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "Verander bericht-van-de-dag op alle hosts (niet versturen)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Bericht van de dag verwijderen" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Verwijder bericht-van-de-dag op alle hosts" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Instellingen" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Modules starten" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Modules stoppen" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Binaire backup direct herstellen" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Backup naar een tekstbestand schrijven" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Bestand importeren" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Directory importeren" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Herstart Service" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Stop Service" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Verwijder Gebruiker" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Verwijder Gebruikers-sessie" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Gebruikerswachtwoord Opvragen" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Verander Gebruikerswachtwoord" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "Tijd van Laatste Aanmelding Opvragen" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Gebruikers-statistieken Opvragen" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Aantal Geregistreerde Gebruikers Opvragen" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Aantal Aanwezige Gebruikers Opvragen" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Gebruikersbeheer" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Alle gebruikers" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Uitgaande s2s-verbindingen" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Backup" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Importeer gebruikers via spool-bestanden van jabberd14" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "Naar ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "Van ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Instellingen van databasetabellen op " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Opslagmethode voor tabellen kiezen" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Harde schijf" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "RAM en harde schijf" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "RAM" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "Op andere nodes in de cluster" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Modules stoppen op " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Selecteer de modules die u wilt stoppen" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Modules starten op " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Voer lijst met op te starten modules als volgt in: {Module, [Opties]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Lijst met op te starten modules" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Binaire backup maken op " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Voer pad naar backupbestand in" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Pad naar bestand" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Binaire backup direct herstellen op " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Backup naar een tekstbestand schrijven op " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Voer pad naar backupbestand in" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Importeer gebruiker via bestand op " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Voer pad naar jabberd14-spool-bestand in" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Gebruikers importeren vanaf directory op " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Voer pad naar jabberd14-spool-directory in" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Pad naar directory" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "Vertraging" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Instellingen van access control lists" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Access control lists" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Toegangsinstellingen" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Access rules" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Wachtwoord Bevestiging" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Aantal Geregistreerde Gebruikers" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Aantal Aanwezige Gebruikers" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Laatste Aanmelding" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "Contactlijst Groote" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "IP-adres" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Bronnen" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Beheer van " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Actie op gebruiker" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Eigenschappen bewerken" - -#: mod_fail2ban.erl:95 -msgid "" -"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" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "" - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "" - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "Dit IP-adres is geblokkeerd in ~s" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "De toegang werd geweigerd door het beleid van deze dienst" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "IRC-transport" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "ejabberd's IRC-module" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "" -"U hebt een client nodig die x:data ondersteunt om dit IRC-transport in te " -"stellen" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Registratie van " - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Voer de gebruikersnaam, coderingen, poorten en wachtwoorden in die U wilt " -"gebruiken voor het verbinden met IRC-servers" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "Gebruikersnaam voor IRC:" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Als u verschillende poorten, wachtwoorden en coderingen wilt opgeven voor " -"elke IRC-server, vul dan deze lijst met het volgende formaat: '{\"IRC-server" -"\", \"codering\", poort, \"wachtwoord\"}'. Standaard gebruikt deze service " -"de codering \"~s\", poort ~p, leeg wachtwoord." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Voorbeeld: [{\"irc.example.org\", \"koi8-r\", 6667, \"geheim\"}, {\"vendetta." -"example.net\", \"iso8859-1\", 7000}, {irc,testserver.nl\", \"utf-8\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Verbindingsparameters" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "Ga IRC kanaal binnen" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "IRC kanaal (zonder eerste #)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "IRC-server" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Ga het IRC kanaal binnen" - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Ga het IRC kanaal van deze Jabber ID binnen: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "IRC instellingen" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Voer de gebruikersnaam en de coderingen in die u wilt gebruiken voor " -"verbindingen met IRC-servers. Klik op 'Volgende' om meer velden aan te " -"maken. Klik op \"Voltooi' om de instellingen op te slaan." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "Gebruikersnaam voor IRC" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Wachtwoord ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Poort ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Karakterset voor server ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Server ~b" - -#: mod_mam.erl:541 -#, fuzzy -msgid "Only members may query archives of this room" -msgstr "Alleen moderators mogen het onderwerp van deze chatruimte veranderen" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "" -"Alleen beheerders van deze dienst mogen mededelingen verzenden naar alle " -"chatruimtes" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "" -"De aanmaak van de chatruimte is verhinderd door de instellingen van deze " -"server" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "De chatruimte bestaat niet" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Groepsgesprekken" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "" -"U hebt een client nodig die x:data ondersteunt om een bijnaam te registreren" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Registratie van een bijnaam op " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Voer de bijnaam in die u wilt registreren" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Bijnaam" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "Deze bijnaam is al geregistreerd door iemand anders" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "U moet het veld \"bijnaam\" invullen" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "ejabberd's MUC module" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "Groepschat" - -#: mod_muc_admin.erl:249 -msgid "Total rooms" -msgstr "Aantal groepsgesprekken" - -#: mod_muc_admin.erl:250 -msgid "Permanent rooms" -msgstr "Permanente groepsgesprekken" - -#: mod_muc_admin.erl:251 -msgid "Registered nicknames" -msgstr "Geregistreerde gebruikersnamen" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "Lijst van groepsgesprekken" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "De instellingen van de chatruimte werden veranderd" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "betrad de chatruimte" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "verliet de chatruimte" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "is verbannen" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "is weggestuurd vanwege een affiliatieverandering" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "" -"is weggestuurd omdat de chatruimte vanaf heden alleen toegankelijk is voor " -"leden" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "is weggestuurd omdat het systeem gestopt wordt" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "heet nu" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " veranderde het onderwerp in: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "Gespreksruimte gecreëerd" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "Gespreksruimte vernietigd" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "Gespreksruimte gestart" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "Gespreksruimte gestopt" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "Maandag" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "Dinsdag" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "Woensdag" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "Donderdag" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "Vrijdag" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "Zaterdag" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "Zondag" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "Januari" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "Februari" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "Maart" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "April" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "Mei" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "Juni" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "Juli" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "Augustus" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "September" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "Oktober" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "November" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "December" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Instellingen van de chatruimte" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "Aantal aanwezigen" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "Dataverkeerslimiet overschreden" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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 "" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "Er mogen geen privéberichten naar de chatruimte worden verzonden" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "Wacht s.v.p. met het maken van een nieuwe stemaanvraag." - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "Stemaanvragen zijn uitgeschakeld voor deze chatruimte" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "Er kon geen JID worden ontleend uit deze stemaanvraag" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "Alleen moderators kunnen stemaanvragen goedkeuren" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Onjuist berichttype" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "" -"Er mogen geen privéberichten van het type \"groupchat\" worden verzonden" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "De ontvanger is niet in de chatruimte" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "Het is niet toegestaan priveberichten te sturen" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Alleen aanwezigen mogen berichten naar de chatruimte verzenden" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "Alleen aanwezigen mogen verzoeken verzenden naar de chatruimte" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "" -"Er mogen geen verzoeken verzenden worden naar deelnemers in deze chatruimte" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "" -"Alleen moderators en deelnemers mogen het onderwerp van deze chatruimte " -"veranderen" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Alleen moderators mogen het onderwerp van deze chatruimte veranderen" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "Bezoekers mogen geen berichten verzenden naar alle aanwezigen" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "Het is bezoekers niet toegestaan hun naam te veranderen in dit kanaal" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "Deze bijnaam is al in gebruik door een andere aanwezige" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "U werd verbannen uit deze chatruimte" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "U moet lid zijn om deze chatruimte te kunnen betreden" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "U hebt een wachtwoord nodig om deze chatruimte te kunnen betreden" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "Te veel CAPTCHA-aanvragen" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "Het generen van een CAPTCHA is mislukt" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Foutief wachtwoord" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "U hebt beheerdersprivileges nodig" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "U hebt moderatorprivileges nodig" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "De Jabber ID ~s is ongeldig" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "De bijnaam ~s bestaat niet in deze chatruimte" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Ongeldige affiliatie: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Ongeldige rol: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "U hebt eigenaarsprivileges nodig" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "Instellingen van chatruimte ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Naam van de chatruimte" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "Beschrijving" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Chatruimte blijvend maken" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Chatruimte doorzoekbaar maken" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "Deelnemerslijst publiek maken" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Chatruimte beveiligen met een wachtwoord" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Maximum aantal aanwezigen" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Geen limiet" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Jabber ID's kunnen achterhaald worden door" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "moderators" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "iedereen" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "" - -#: mod_muc_room.erl:3486 -#, fuzzy -msgid "Moderator" -msgstr "moderators" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Chatruimte enkel toegankelijk maken voor leden" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Chatruimte gemodereerd maken" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Gebruikers standaard instellen als deelnemers" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Sta gebruikers toe het onderwerp te veranderen" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Gebruikers mogen privéberichten verzenden" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "Gebruikers mogen privéberichten verzenden aan" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "niemand" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Gebruikers mogen naar andere gebruikers verzoeken verzenden" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Gebruikers mogen uitnodigingen verzenden" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "Sta bezoekers toe hun statusbericht in te stellen" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Sta bezoekers toe hun naam te veranderen" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "Gebruikers mogen stemaanvragen verzenden" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "Minimale interval tussen stemaanvragen (in seconden)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "Chatruimte beveiligen met een geautomatiseerde Turing test" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "Zet bericht-archivering aan" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "Geen CAPTCHA test voor Jabber IDs" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Logs aanzetten" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "" -"U hebt een client nodig die x:data ondersteunt om deze chatruimte in te " -"stellen" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Aantal aanwezigen" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "privé, " - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "Stemaanvraag" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "Keur stemaanvraag goed of af." - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "JID Gebruiker" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "Stemaanvraag honoreren voor deze persoon?" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s nodigt je uit voor het groepsgesprek ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "het wachtwoord is" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "Multicast" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "ejabberd Multicast service" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "" -"Te veel offline berichten voor dit contactpersoon. Het bericht is niet " -"opgeslagen." - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "offline berichten van ~s" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Tijd" - -#: mod_offline.erl:812 -msgid "From" -msgstr "Van" - -#: mod_offline.erl:813 -msgid "To" -msgstr "Aan" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Pakket" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Offline berichten:" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "Verwijder alle offline berichten" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "ejabberd SOCKS5 Bytestreams module" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Publish-Subscribe" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "ejabberd Publish-Subscribe module" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "PubSub abonnee verzoek" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Beslis of dit verzoek tot abonneren zal worden goedgekeurd" - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "Node ID" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Abonnee Adres" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "Deze gebruiker toestaan te abonneren op deze pubsub node?" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Berichten bezorgen samen met gebeurtenisnotificaties" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Gebeurtenisbevestigingen Sturen" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Abonnees informeren wanneer de instellingen van de node veranderen" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Abonnees informeren wanneer de node verwijderd word" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Abonnees informeren wanneer items verwijderd worden uit de node" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Items in het geheugen bewaren" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "Bijnaam voor deze knoop" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Maximum aantal in het geheugen te bewaren items" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Abonnementsaanvraag toestaan" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Geef toegangsmodel" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Contactlijst-groepen die mogen abonneren" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Publicatietype opgeven" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "Verwijder alle items wanneer de gerelateerde publiceerder offline gaat" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "Geef type van eventbericht" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Maximumgrootte van bericht in bytes" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "Wanneer het laatst gepubliceerde item verzonden moet worden" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Notificaties alleen verzenden naar online gebruikers" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "De collecties waar een node mee is gerelateerd" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "De CAPTCHA-verificatie is mislukt" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "" -"U hebt een client nodig die x:data en CAPTCHA ondersteunt om een bijnaam te " -"registreren" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "" -"Kies een gebruikersnaam en een wachtwoord om u te registreren op deze server" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "Het wachtwoord is te zwak" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "Het is gebruikers niet toegestaan zo snel achter elkaar te registreren" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "Uw Jabber-account is succesvol gecreeerd." - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "Er was een fout bij het creeern van de account:" - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "Uw Jabber-account is succesvol verwijderd." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "Er was een fout bij het verwijderen van de account." - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "Het wachtwoord van Uw Jabber-account is succesvol veranderd." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "Er was een fout bij het veranderen van het wachtwoord:" - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Jabber-account registratie" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Registreer een Jabber-account" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Opheffen van Jabber-account" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"Deze pagina maakt het mogelijk een Jabber-account te registreren op deze " -"server. Uw JID (Jabber IDentiteit) zal er als volg uit zien: " -"gebruikersnaam@server. Lees de instructies zorgvuldig teneinde de velden " -"correct in te vullen." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "Gebruikersnaam:" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" -"Dit is niet hoofdlettergevoelig: macbeth is hetzelfde als MacBeth en Macbeth." - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "Niet-toegestane karakters:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "Server:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" -"Geef Uw wachtwoord aan niemand, zelfs niet aan de beheerders van deze Jabber-" -"server." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "U can het wachtwoord later veranderen met een Jabber-client." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Sommige Jabber-clienten kunnen het wachtwoord opslaan op Uw computer. " -"Gebruik deze mogelijkheid alleen als U vertrouwd dat Uw computer afdoende " -"beveiligd is." - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"Onthou het wachtwoord, of schrijf het op en bewaar het op een veilige " -"plaats. Met Jabber is er geen geautomatiseerde manier om het wachtwoord " -"terug te halen als U het vergeet." - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "Wachtwoord Bevestiging:" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "Registreer" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "Oud Wachtwoord:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "Nieuw Wachtwoord:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "" -"Deze pagina maakt het mogelijk een Jabber-account op deze server op te " -"heffen." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "Opheffen" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Inschrijving" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Bezig" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Groepen" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Bevestigen" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Verwijderen" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Roster van " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Jabber ID toevoegen" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Roster" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Gedeelde rostergroepen" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Naam:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Beschrijving:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Groepsleden:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Weergegeven groepen:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Groep " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Erlang Jabber Server" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Geboortedatum" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Plaats" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "Land" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "E-mail" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Achternaam" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Gebruik de velden om te zoeken (Voeg achteraan het teken * toe om te zoeken " -"naar alles wat met het eerste deel begint.)." - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Volledige naam" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Tussennaam" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Organisatie" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Afdeling" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Gebruikers zoeken in " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "U hebt een client nodig die x:data ondersteunt om te zoeken" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "Gebruikers zoeken" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "ejabberd's vCard-module" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Zoekresultaten voor " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "Vul de velden in om te zoeken naar Jabber-gebruikers op deze server" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Uitgaande s2s-verbindingen:" - -#~ msgid "Delete" -#~ msgstr "Verwijderen" - -#~ msgid "This room is not anonymous" -#~ msgstr "Deze chatruimte is niet anoniem" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "" -#~ "Deze deelnemer wordt weggestuurd vanwege het sturen van een " -#~ "foutmeldingsbericht" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Deze deelnemer wordt weggestuurd vanwege het sturen van een " -#~ "foutmeldingsbericht aan een andere deelnemer" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "" -#~ "Deze deelnemer wordt weggestuurd vanwege het sturen van een foutmelding-" -#~ "aanwezigheid" - -#, fuzzy -#~ msgid "Captcha test failed" -#~ msgstr "De geautomatiseerde Turing-test is geslaagd." diff --git a/priv/msgs/no.msg b/priv/msgs/no.msg index e8ae741b3..b24b9eca9 100644 --- a/priv/msgs/no.msg +++ b/priv/msgs/no.msg @@ -1,410 +1,337 @@ -%% -*- coding: latin-1 -*- -{"Access Configuration","Tilgangskonfigurasjon"}. -{"Access Control List Configuration","Konfigurasjon for Tilgangskontroll lister"}. -{"Access Control Lists","Tilgangskontrollister"}. -{"Access control lists","Tilgangskontroll lister"}. -{"Access denied by service policy","Tilgang nektes på grunn av en tjeneste regel"}. -{"Access rules","Tilgangsregler"}. -{"Access Rules","Tilgangsregler"}. -{"Action on user","Handling på bruker"}. -{"Add Jabber ID","Legg til Jabber ID"}. -{"Add New","Legg til ny"}. -{"Add User","Legg til Bruker"}. -{"Administration","Administrasjon"}. -{"Administration of ","Administrasjon av "}. -{"Administrator privileges required","Administratorprivilegier kreves"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" has set the subject to: "," har satt emnet til: "}. {"A friendly name for the node","Et vennlig navn for noden"}. +{"A password is required to enter this room","Et passord kreves for tilgang til samtalerommet"}. +{"Accept","Godta"}. +{"Access denied by service policy","Tilgang nektes på grunn av en tjenesteregel"}. +{"Action on user","Handling på bruker"}. +{"Add User","Legg til bruker"}. +{"Administration of ","Administrasjon av "}. +{"Administration","Administrasjon"}. +{"Administrator privileges required","Administratorprivilegier kreves"}. {"All activity","All aktivitet"}. -{"Allow this Jabber ID to subscribe to this pubsub node?","Tillat denne Jabber ID å abonnere på denne pubsub "}. -{"Allow users to change the subject","Tillat brukere å endre emne"}. -{"Allow users to query other users","Tillat brukere å sende forespørsel til andre brukere"}. +{"All Users","Alle brukere"}. +{"Allow this Jabber ID to subscribe to this pubsub node?","Tillat denne Jabber-ID-en å abonnere på denne pubsub -noden?"}. +{"Allow users to change the subject","Tillat brukere å endre emnet"}. +{"Allow users to query other users","Tillat brukere å sende forespørsler til andre brukere"}. {"Allow users to send invites","Tillat brukere å sende invitasjoner"}. {"Allow users to send private messages","Tillat brukere å sende private meldinger"}. {"Allow visitors to change nickname","Tillat besøkende å endre kallenavn"}. {"Allow visitors to send private messages to","Tillat brukere å sende private meldinger til"}. -{"Allow visitors to send status text in presence updates","Tillat besøkende å sende status tekst i "}. -{"Allow visitors to send voice requests","Tillat brukere å sende lyd forespørsler"}. -{"All Users","Alle Brukere"}. +{"Allow visitors to send status text in presence updates","Tillat besøkende å sende statustekst i tilstedeværelsesoppdateringer"}. +{"Allow visitors to send voice requests","Tillat brukere å sende lydforespørsler"}. {"Announcements","Kunngjøringer"}. -{"anyone","hvem som helst"}. -{"A password is required to enter this room","Et passord kreves for tilgang til samtalerommet"}. +{"Answer to a question","Svar på spørsmål"}. {"April","april"}. {"August","august"}. -{"Backup Management","Håndtere Sikkerehetskopiering"}. -{"Backup","Sikkerhetskopier"}. -{"Backup to File at ","Sikkerhetskopiere til Fil på "}. -{"Bad format","Feil format"}. -{"Birthday","Fødselsdag"}. -{"CAPTCHA web page","CAPTCHA web side"}. -{"Change Password","Endre Passord"}. -{"Change User Password","Endre Brukers Passord"}. +{"Backup Management","Håndtering av sikkerehetskopiering"}. +{"Backup of ~p","Sikkerhetskopi av ~p"}. +{"Backup to File at ","Sikkerhetskopier til fil på "}. +{"Backup","Sikkerhetskopiering"}. +{"Bad format","Feilaktig format"}. +{"Birthday","Geburtsdag"}. +{"Both the username and the resource are required","Både brukernavn og ressurs kreves"}. +{"Cannot remove active list","Kan ikke fjerne aktiv liste"}. +{"Cannot remove default list","Kan ikke fjerne forvalgt liste"}. +{"CAPTCHA web page","CAPTCHA-nettside"}. +{"Challenge ID","Utfordrings-ID"}. +{"Change Password","Endre passord"}. +{"Change User Password","Endre brukerpassord"}. +{"Channel already exists","Kanalen finnes allerede"}. +{"Channels","Kanaler"}. {"Characters not allowed:","Ikke godtatte tegn:"}. -{"Chatroom configuration modified","Samtalerommets konfigurasjon er endret"}. -{"Chatroom is created","Samtalerom er opprettet"}. +{"Chatroom configuration modified","Samtalerommets oppsett er endret"}. +{"Chatroom is created","Samtalerom opprettet"}. {"Chatroom is destroyed","Samtalerom er fjernet"}. -{"Chatroom is started","Samtalerom er startet"}. -{"Chatroom is stopped","Samtalerom er stoppet"}. +{"Chatroom is started","Samtalerom startet"}. +{"Chatroom is stopped","Samtalerom stoppet"}. {"Chatrooms","Samtalerom"}. -{"Choose a username and password to register with this server","Velg et brukernavn og passord for å registrere på "}. -{"Choose modules to stop","Velg hvilke moduler som skal stoppes"}. +{"Choose a username and password to register with this server","Velg et brukernavn og passord for å registrere deg på denne tjeneren"}. {"Choose storage type of tables","Velg lagringstype for tabeller"}. -{"Choose whether to approve this entity's subscription.","Velg om du vil godkjenne denne eksistensens abonement"}. {"City","By"}. {"Commands","Kommandoer"}. {"Conference room does not exist","Konferanserommet finnes ikke"}. -{"Configuration","Konfigurasjon"}. -{"Configuration of room ~s","Konfigurasjon for rom ~s"}. -{"Connected Resources:","Tilkoblede Ressurser:"}. -{"Connections parameters","Tilkoblings parametere"}. +{"Configuration of room ~s","Oppsett for rom ~s"}. +{"Configuration","Oppsett"}. {"Country","Land"}. -{"CPU Time:","CPU Tid:"}. +{"Current Discussion Topic","Nåværende diskusjonstema"}. +{"Database Tables Configuration at ","Database-tabelloppsett på "}. {"Database","Database"}. -{"Database Tables Configuration at ","Database Tabell Konfigurasjon på "}. {"December","desember"}. {"Default users as participants","Standard brukere som deltakere"}. -{"Delete message of the day on all hosts","Slett melding for dagen på alle maskiner"}. {"Delete message of the day","Slett melding for dagen"}. -{"Delete Selected","Slett valgte"}. -{"Delete User","Slett Bruker"}. -{"Deliver event notifications","Lever begivenhets kunngjøringer"}. -{"Deliver payloads with event notifications","Send innhold sammen med kunngjøringer"}. -{"Description:","Beskrivelse:"}. +{"Delete User","Slett bruker"}. +{"Deliver event notifications","Lever begivenhetskunngjøringer"}. +{"Deliver payloads with event notifications","Send innhold sammen med hendelsesmerknader"}. {"Disc only copy","Kun diskkopi"}. -{"Displayed Groups:","Viste grupper:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","Ikke fortell passordet til noen, ikke en gang til administratoren av Jabber serveren."}. -{"Dump Backup to Text File at ","Dump Sikkerhetskopi til Tekstfil på "}. -{"Dump to Text File","Dump til Tekstfil"}. -{"Edit Properties","Redigere Egenskaper"}. -{"Either approve or decline the voice request.","Enten godkjenn eller forby lyd forespørselen"}. -{"ejabberd IRC module","ejabberd IRC modul"}. -{"ejabberd MUC module","ejabberd MUC modul"}. -{"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe modul"}. -{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams modul"}. -{"ejabberd vCard module","ejabberd vCard modul"}. -{"ejabberd Web Admin","ejabberd Web Admin"}. -{"Elements","Elementer"}. -{"Email","Epost"}. -{"Enable logging","Slå på logging"}. -{"Encoding for server ~b","Tekstkoding for server ~b"}. -{"End User Session","Avslutt Bruker Sesjon"}. -{"Enter list of {Module, [Options]}","Skriv inn en liste av {Module, [Options]}"}. +{"Dump Backup to Text File at ","Dump sikkerhetskopi til tekstfil på "}. +{"Dump to Text File","Dump til tekstfil"}. +{"Edit Properties","Rediger egenskaper"}. +{"Either approve or decline the voice request.","Enten godkjenn eller forby lydforespørselen."}. +{"ejabberd HTTP Upload service","ejabberd-HTTP-opplastingstjeneste"}. +{"ejabberd MUC module","ejabberd-MUC-modul"}. +{"ejabberd Multicast service","ejabberd-multikastingstjeneste"}. +{"ejabberd Publish-Subscribe module","ejabberd-Publish-Subscribe-modul"}. +{"Email","E-post"}. +{"Enable logging","Skru på loggføring"}. +{"Enable message archiving","Skru på meldingsarkivering"}. +{"Enabling push without 'node' attribute is not supported","Å skru på dytting uten «node»-attributt støttes ikke"}. +{"End User Session","Avslutt brukerøkt"}. {"Enter nickname you want to register","Skriv inn kallenavnet du ønsker å registrere"}. -{"Enter path to backup file","Skriv inn sti til sikkerhetskopi filen"}. +{"Enter path to backup file","Skriv inn sti til sikkerhetskopifilen"}. {"Enter path to jabberd14 spool dir","Skriv inn sti til jabberd14 spoolkatalog"}. -{"Enter path to jabberd14 spool file","Skriv inn sti til jabberd14 spoolfil"}. {"Enter path to text file","Skriv inn sti til tekstfil"}. {"Enter the text you see","Skriv inn teksten du ser"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Angi brukernavn og kodinger du ønsker å bruke for å koble til IRC servere. Trykk 'Neste' for å få flere felt for å fylle i. Trykk 'Fullfør' for å lagre innstillingene."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Skriv brukernavn, tekstkoding, porter og passord du ønsker å bruke for tilkobling til IRC servere"}. -{"Erlang Jabber Server","Erlang Jabber Server"}. -{"Error","Feil"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Eksempel: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. -{"Exclude Jabber IDs from CAPTCHA challenge","Ekskluder Jabber IDer fra CAPTCHA utfordring"}. -{"Export data of all users in the server to PIEFXIS files (XEP-0227):","Eksporter data om alle brukere i en server til PIEFXIS filer"}. -{"Export data of users in a host to PIEFXIS files (XEP-0227):","Eksporter data om alle brukere i en host til PIEFXIS filer (XEP-0227):"}. -{"Failed to extract JID from your voice request approval","Feilet i forsøk på å hente JID fra din lyd forespørsel godkjenning"}. +{"Exclude Jabber IDs from CAPTCHA challenge","Ekskluder Jabber-ID-er fra CAPTCHA-utfordring"}. +{"Export all tables as SQL queries to a file:","Eksporter alle tabeller som SQL-spørringer til en fil:"}. +{"Export data of all users in the server to PIEFXIS files (XEP-0227):","Eksporter data om alle brukere på en tjener til PIEFXIS-filer (XEP-0227):"}. +{"Export data of users in a host to PIEFXIS files (XEP-0227):","Eksporter data om alle brukere på en vert til PIEFXIS-filer (XEP-0227):"}. +{"External component failure","Feil med ekstern komponent"}. +{"External component timeout","Tidsavbrudd for ekstern komponent"}. {"Family Name","Etternavn"}. +{"FAQ Entry","O-S-S -oppføring"}. {"February","februar"}. -{"Fill in fields to search for any matching Jabber User","Fyll inn felt for å søke etter Jabber brukere"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Fyll inn skjemaet for å søke etter Jabber bruker (Legg til * på slutten av feltet for å treffe alle som starter slik)"}. +{"File larger than ~w bytes","Fil større enn ~w byte"}. {"Friday","fredag"}. -{"From","Fra"}. -{"From ~s","Fra ~s"}. -{"Full Name","Fullstendig Navn"}. -{"Get Number of Online Users","Vis Antall Tilkoblede Brukere"}. -{"Get Number of Registered Users","Vis Antall Registrerte Brukere"}. -{"Get User Last Login Time","Vis Brukers Siste Påloggings Tidspunkt"}. -{"Get User Password","Hent Brukers Passord"}. -{"Get User Statistics","Vis Bruker Statistikk"}. -{"Grant voice to this person?","Gi lyd til denne personen?"}. -{"Group ","Gruppe "}. -{"Groups","Grupper"}. +{"Full List of Room Admins","Full liste over romadministratorer"}. +{"Full List of Room Owners","Full liste over romeiere"}. +{"Full Name","Fullt navn"}. +{"Get Number of Online Users","Vis antall tilkoblede brukere"}. +{"Get Number of Registered Users","Vis antall registrerte brukere"}. +{"Get User Last Login Time","Vis brukers siste innloggingstidspunkt"}. +{"Get User Statistics","Vis brukerstatistikk"}. {"has been banned","har blitt bannlyst"}. -{"has been kicked because of an affiliation change","har blitt kastet ut på grunn av en tilknytnings endring"}. -{"has been kicked because of a system shutdown","har blitt kastet ut på grunn av at systemet avslutter"}. -{"has been kicked because the room has been changed to members-only","har blitt kastet ut på grunn av at rommet er endret til kun-for-medlemmer"}. +{"has been kicked because of a system shutdown","har blitt kastet ut på grunn av systemavstenging"}. +{"has been kicked because of an affiliation change","har blitt kastet ut på grunn av en tilknytningsendring"}. {"has been kicked","har blitt kastet ut"}. -{" has set the subject to: "," har satt emnet til: "}. -{"Host","Maskin"}. -{"If you don't see the CAPTCHA image here, visit the web page.","Dersom du ikke ser CAPTCHA bilde her, besøk web siden. "}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Om du ønsker å spesifisere tekstkoding for IRC tjenere, fyller du ut en liste med verdier i formatet '{\"irc server\", \"encoding\", port, \"password\"}'. Denne tjenesten bruker \"~s\" som standard, port ~p, empty password."}. -{"Import Directory","Importer Katalog"}. -{"Import File","Importer File"}. -{"Import user data from jabberd14 spool file:","Importer bruker data fra jabberd14 spoolfiler:"}. -{"Import User from File at ","Importer Bruker fra Fil på "}. -{"Import users data from a PIEFXIS file (XEP-0227):","Importer brukeres data fra en PIEFXIS fil (XEP-0227):"}. -{"Import users data from jabberd14 spool directory:","Importer brukeres data fra jabberd14 spoolfil katalog:"}. -{"Import Users from Dir at ","Importer Brukere fra Katalog på "}. -{"Import Users From jabberd14 Spool Files","Importer Brukere Fra jabberd14 Spoolfiler"}. +{"Host unknown","Ukjent vert"}. +{"HTTP File Upload","HTTP-filopplasting"}. +{"If you don't see the CAPTCHA image here, visit the web page.","Dersom du ikke ser et CAPTCHA-bilde her, besøk nettsiden."}. +{"Import Directory","Importer mappe"}. +{"Import File","Importer file"}. +{"Import User from File at ","Importer bruker fra fil på "}. +{"Import users data from a PIEFXIS file (XEP-0227):","Importer data fra bruker fra en PIEFXIS-fil (XEP-0227):"}. +{"Import Users from Dir at ","Importer brukere fra mappe på "}. {"Improper message type","Feilaktig meldingstype"}. {"Incorrect password","Feil passord"}. -{"Invalid affiliation: ~s","Ugyldig rang: ~s"}. -{"Invalid role: ~s","Ugyldig rolle: ~s"}. -{"IP addresses","IP adresser"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","IRC kanal (ikke skriv den første #)"}. -{"IRC server","IRC server"}. -{"IRC settings","IRC instillinger"}. -{"IRC Transport","IRC Transport"}. -{"IRC username","IRC brukernavn"}. -{"IRC Username","IRC Brukernavn"}. +{"Insufficient privilege","Mangler tilstrekkelige rettigheter"}. +{"Internal server error","Intern tjenerfeil"}. +{"Invalid 'from' attribute in forwarded message","Ugyldig «fra»-attributt i videresendt melding"}. +{"IP addresses","IP-adresser"}. {"is now known as","er nå kjent som"}. -{"It is not allowed to send private messages","Det er ikke tillatt å sende private meldinger"}. -{"It is not allowed to send private messages of type \"groupchat\"","Det er ikke tillatt å sende private meldinger med typen "}. -{"It is not allowed to send private messages to the conference","Det er ikke tillatt å sende private meldinger til "}. -{"Jabber Account Registration","Jabber Konto Registrering"}. -{"Jabber ID","Jabber ID"}. -{"Jabber ID ~s is invalid","Ugyldig Jabber ID ~s"}. +{"It is not allowed to send private messages to the conference","Det er ikke tillatt å sende private meldinger til konferansen"}. +{"Jabber ID","Jabber-ID"}. {"January","januar"}. -{"Join IRC channel","Bli med i IRC kanal"}. -{"joins the room","kommer inn i rommet"}. -{"Join the IRC channel here.","Bli med i IRC kanalen her. "}. -{"Join the IRC channel in this Jabber ID: ~s","Bli med i IRC kanalen med denne Jabber ID: ~s"}. +{"JID normalization failed","JID-normalisering mislyktes"}. +{"joins the room","tar del i rommet"}. {"July","juli"}. {"June","juni"}. -{"Last Activity","Siste Aktivitet"}. -{"Last login","Siste pålogging"}. +{"Just created","Akkurat opprettet"}. +{"Last Activity","Siste aktivitet"}. +{"Last login","Siste innlogging"}. {"Last month","Siste måned"}. -{"Last year","Siste året"}. +{"Last year","Siste år"}. +{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","De minst viktige bit-ene av SHA-256-sjekksummen for tekst skal tilsvare heksadesimal etikett"}. {"leaves the room","forlater rommet"}. -{"Listened Ports at ","Lyttende Porter på "}. -{"Listened Ports","Lyttende Porter"}. -{"List of modules to start","Liste over moduler som skal startes"}. -{"Low level update script","Lavnivå oppdaterings skript"}. +{"Logging","Loggføring"}. {"Make participants list public","Gjør deltakerlisten offentlig"}. -{"Make room CAPTCHA protected","Gjør rommet CAPTCHA beskyttet"}. {"Make room members-only","Gjør rommet tilgjengelig kun for medlemmer"}. -{"Make room moderated","Gjør rommet redaktørstyrt"}. {"Make room password protected","Passordbeskytt rommet"}. -{"Make room persistent","Gjør rommet permanent"}. +{"Make room persistent","Gjør rommet vedvarende"}. {"Make room public searchable","Gjør rommet offentlig søkbart"}. {"March","mars"}. -{"Maximum Number of Occupants","Maksimum Antall Deltakere"}. -{"Max # of items to persist","Høyeste # elementer som skal lagres"}. -{"Max payload size in bytes","Største innholdsstørrelse i byte"}. +{"Maximum file size","Maksimal filstørrelse"}. +{"Maximum Number of History Messages Returned by Room","Maksimalt antall historikkmeldinger tilbudt av rommet"}. {"May","mai"}. -{"Membership is required to enter this room","Medlemskap kreves for tilgang til samtalerommet"}. -{"Members:","Medlemmer:"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Husk passordet, eller skriv det ned på et papir lagret på et trygt sted. I Jabber er det ingen automatisert måte å gjenskape passordet om du glemmer det. "}. -{"Memory","Minne"}. +{"Membership is required to enter this room","Medlemskap kreves for tilgang til dette rommet"}. {"Message body","Meldingskropp"}. +{"Message not found in forwarded payload","Fant ikke melding i videresendt nyttelast"}. +{"Messages from strangers are rejected","Meldinger fra ukjente avvises"}. +{"Messages of type headline","Meldinger av typen overskrift"}. +{"Messages of type normal","Meldinger av normal type"}. {"Middle Name","Mellomnavn"}. -{"Minimum interval between voice requests (in seconds)","Minimums interval mellom lyd forespørsler (i sekunder)"}. -{"Moderator privileges required","Redaktørprivilegier kreves"}. -{"moderators only","kun for redaktører"}. -{"Modified modules","Endrede moduler"}. -{"Module","Modul"}. -{"Modules","Moduler"}. +{"Minimum interval between voice requests (in seconds)","Minimumsintervall mellom lydforespørsler (i sekunder)"}. {"Monday","mandag"}. -{"Name:","Navn:"}. +{"Multicast","Multikasting"}. +{"Multi-User Chat","Multibrukersludring"}. {"Name","Navn"}. {"Never","Aldri"}. -{"New Password:","Nytt Passord:"}. +{"New Password:","Nytt passord:"}. +{"Nickname can't be empty","Kallenavn kan ikke stå tomt"}. +{"Nickname Registration at ","Registrer kallenavn på "}. {"Nickname","Kallenavn"}. -{"Nickname Registration at ","Registrer Kallenavn på "}. -{"Nickname ~s does not exist in the room","Kallenavn ~s eksisterer ikke i dette rommet"}. -{"nobody","ingen"}. -{"No body provided for announce message","Ingen meldingskropp gitt for kunngjørings melding"}. -{"No Data","Ingen Data"}. -{"Node ID","Node ID"}. +{"No available resource found","Fant ingen tilgjengelig ressurs"}. +{"No body provided for announce message","Ingen meldingskropp angitt for kunngjøringsmelding"}. +{"No Data","Ingen data"}. +{"No features available","Ingen tilgjengelige funksjoner"}. +{"No element found","Fant ikke noe -element"}. +{"No limit","Ingen grense"}. +{"Node already exists","Node finnes allerede"}. +{"Node ID","Node-ID"}. {"Node not found","Noden finnes ikke"}. {"Nodes","Noder"}. -{"No limit","Ingen grense"}. {"None","Ingen"}. -{"No resource provided","Ingen ressurs angitt"}. -{"Not Found","Finnes Ikke"}. -{"Notify subscribers when items are removed from the node","Informer abonnenter når elementer fjernes fra noden"}. -{"Notify subscribers when the node configuration changes","Informer abonnenter når node konfigurasjonen endres"}. +{"Not Found","Finnes ikke"}. +{"Notify subscribers when the node configuration changes","Informer abonnenter når nodeoppsettet endres"}. {"Notify subscribers when the node is deleted","Informer abonnenter når noden slettes"}. {"November","november"}. {"Number of occupants","Antall deltakere"}. {"Number of online users","Antall tilkoblede brukere"}. {"Number of registered users","Antall registrerte brukere"}. {"October","oktober"}. -{"Offline Messages:","Frakoblede Meldinger:"}. -{"Offline Messages","Frakoblede Meldinger"}. {"OK","OK"}. -{"Old Password:","Gammelt Passord:"}. +{"Old Password:","Gammelt passord:"}. +{"Online Users","Tilkoblede brukere"}. {"Online","Tilkoblet"}. -{"Online Users:","Tilkoblede Brukere:"}. -{"Online Users","Tilkoblede Brukere"}. -{"Only deliver notifications to available users","Send kunngjøringer bare til tilgjengelige brukere"}. -{"Only moderators and participants are allowed to change the subject in this room","Bare redaktører og deltakere kan endre emnet i dette rommet"}. -{"Only moderators are allowed to change the subject in this room","Bare ordstyrer tillates å endre emnet i dette rommet"}. -{"Only moderators can approve voice requests","Bare ordstyrer kan godkjenne lyd forespørsler"}. +{"Only deliver notifications to available users","Kun send kunngjøringer til tilgjengelige brukere"}. {"Only occupants are allowed to send messages to the conference","Bare deltakere får sende normale meldinger til konferansen"}. -{"Only occupants are allowed to send queries to the conference","Bare deltakere er tillatt å sende forespørsler til "}. -{"Only service administrators are allowed to send service messages","Bare tjeneste administratorer er tilatt å sende tjeneste "}. -{"Options","Alternativer"}. +{"Only occupants are allowed to send queries to the conference","Kun deltakere tillates å sende forespørsler til konferansen"}. +{"Only publishers may publish","Kun publiserere kan publisere"}. +{"Only service administrators are allowed to send service messages","Bare tjenesteadministratorer tillates å sende tjenestemeldinger"}. {"Organization Name","Organisasjonsnavn"}. {"Organization Unit","Organisasjonsenhet"}. -{"Outgoing s2s Connections:","Utgående s2s Koblinger"}. -{"Outgoing s2s Connections","Utgående s2s Koblinger"}. +{"Outgoing s2s Connections","Utgående s2s-koblinger"}. {"Owner privileges required","Eierprivilegier kreves"}. -{"Packet","Pakke"}. -{"Password ~b","Passord ~b"}. -{"Password:","Passord:"}. +{"Participant","Deltager"}. +{"Password Verification","Passordbekreftelse"}. +{"Password Verification:","Passordbekreftelse:"}. {"Password","Passord"}. -{"Password Verification:","Passord Bekreftelse:"}. -{"Password Verification","Passord Bekreftelse"}. -{"Path to Dir","Sti til Katalog"}. -{"Path to File","Sti til Fil"}. -{"Pending","Ventende"}. +{"Password:","Passord:"}. +{"Path to Dir","Sti til mappe"}. +{"Path to File","Sti til fil"}. {"Period: ","Periode: "}. {"Persist items to storage","Vedvarende elementer til lagring"}. -{"Ping","Ping"}. -{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Merk at disse valgene vil bare sikkerhetskopiere den innebygde Mnesia databasen. Dersom du bruker ODBC modulen må du også ta backup av din SQL database."}. -{"Please, wait for a while before sending new voice request","Vennligst vent en stund før du sender en ny lyd forespørsel"}. -{"Pong","Pong"}. -{"Port ~b","Port ~b"}. -{"Port","Port"}. -{"Present real Jabber IDs to","Presenter ekte Jabber IDer til"}. +{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Merk at disse valgene kun vil sikkerhetskopiere den innebygde Mnesia-databasen. Dersom du bruker ODBC-modulen må du også ta sikkerhetskopi av din SQL-database."}. +{"Previous session PID has exited","Forrige økt-ID har avsluttet"}. +{"Previous session PID is dead","Forrige økt-PID er død"}. +{"Previous session timed out","Tidsavbrudd for forrige økt"}. {"private, ","privat, "}. -{"Protocol","Protokoll"}. -{"Publish-Subscribe","Publish-Subscribe"}. -{"PubSub subscriber request","PubSub abonements forespørsel"}. +{"Public","Offentlig"}. +{"PubSub subscriber request","PubSub-abonementsforespørsel"}. {"Purge all items when the relevant publisher goes offline","Rydd alle elementer når den aktuelle utgiveren logger av"}. -{"Queries to the conference members are not allowed in this room","Forespørsler til konferanse medlemmene er ikke tillat i dette rommet"}. -{"RAM and disc copy","RAM og diskkopi"}. -{"RAM copy","RAM kopi"}. -{"Raw","Rå"}. -{"Really delete message of the day?","Virkelig slette melding for dagen?"}. +{"Queries to the conference members are not allowed in this room","Forespørsler til konferansemedlemmerer tillates ikke i dette rommet"}. +{"Query to another users is forbidden","Spørringer til andre brukere er forbudt"}. +{"RAM and disc copy","Minne og diskkopi"}. +{"RAM copy","Minnekopi"}. +{"Really delete message of the day?","Vil du virkelig slette melding for dagen?"}. {"Recipient is not in the conference room","Mottakeren er ikke i konferanserommet"}. -{"Register a Jabber account","Registrer en Jabber konto"}. -{"Registered Users:","Registrerte Brukere:"}. -{"Registered Users","Registrerte Brukere"}. {"Register","Registrer"}. -{"Registration in mod_irc for ","Registrering i mod_irc for "}. -{"Remote copy","Lagres ikke lokalt"}. -{"Remove All Offline Messages","Fjern Alle Frakoblede Meldinger"}. -{"Remove","Fjern"}. -{"Remove User","Fjern Bruker"}. +{"Remove User","Fjern bruker"}. {"Replaced by new connection","Erstattet av en ny tilkobling"}. +{"Request has timed out","Tidsavbrudd for forespørsel"}. +{"Request is ignored","Forespørsel ignorert"}. +{"Requested role","Forespurt rolle"}. {"Resources","Ressurser"}. -{"Restart Service","Start Tjeneste på Nytt"}. -{"Restart","Starte på nytt"}. -{"Restore Backup from File at ","Gjenopprett fra Sikkerhetsopifil på "}. -{"Restore binary backup after next ejabberd restart (requires less memory):","Gjenopprette binær backup etter neste ejabberd omstart (krever mindre minne):"}. -{"Restore binary backup immediately:","Gjenopprette binær backup umiddelbart:"}. -{"Restore","Gjenopprett"}. +{"Restart Service","Omstart av tjeneste"}. +{"Restore Backup from File at ","Gjenopprett fra sikkerhetskopifil på "}. +{"Restore binary backup after next ejabberd restart (requires less memory):","Gjenopprett binær sikkerhetskopi etter neste ejabberd-omstart (krever mindre minne):"}. +{"Restore binary backup immediately:","Gjenopprett binær sikkerhetskopi umiddelbart:"}. {"Restore plain text backup immediately:","Gjenopprette rentekst sikkerhetskopi umiddelbart:"}. -{"Room Configuration","Rom Konfigurasjon"}. -{"Room creation is denied by service policy","Oppretting av rom nektes av en tjenste regel"}. -{"Room description","Rom beskrivelse"}. -{"Room Occupants","Samtalerom Deltakere"}. -{"Room title","Romtittel"}. -{"Roster groups allowed to subscribe","Kontaktliste grupper som tillates å abonnere"}. -{"Roster","Kontaktliste"}. -{"Roster of ","Kontaktliste for "}. -{"Roster size","Kontaktliste størrelse"}. -{"RPC Call Error","RPC Kall Feil"}. -{"Running Nodes","Kjørende Noder"}. -{"~s access rule configuration","tilgangsregel konfigurasjon for ~s"}. +{"Restore","Gjenopprett"}. +{"Room Configuration","Rom-oppsett"}. +{"Room creation is denied by service policy","Oppretting av rom nektes av en tjensteregel"}. +{"Room description","Rom-beskrivelse"}. +{"Room Occupants","Samtaleromsdeltagere"}. +{"Room title","Rom-tittel"}. +{"Roster groups allowed to subscribe","Kontaktlistegrupper som tillates å abonnere"}. +{"Roster size","Kontaktlistestørrelse"}. +{"Running Nodes","Kjørende noder"}. {"Saturday","lørdag"}. -{"Script check","Skript sjekk"}. -{"Search Results for ","Søke Resultater for "}. +{"Search Results for ","Søkeresultater for "}. {"Search users in ","Søk etter brukere i "}. -{"Send announcement to all online users on all hosts","Send kunngjøring til alle tilkoblede brukere på alle "}. +{"Send announcement to all online users on all hosts","Send kunngjøring til alle tilkoblede brukere på alle verter"}. {"Send announcement to all online users","Send kunngjøring alle tilkoblede brukere"}. -{"Send announcement to all users on all hosts","Send kunngjøring til alle brukere på alle maskiner"}. +{"Send announcement to all users on all hosts","Send kunngjøring til alle brukere på alle verter"}. {"Send announcement to all users","Send kunngjøring til alle brukere"}. {"September","september"}. -{"Server ~b","Server ~b"}. -{"Server:","Server:"}. +{"Server:","Tjener:"}. {"Set message of the day and send to online users","Angi melding for dagen og send til tilkoblede brukere"}. -{"Set message of the day on all hosts and send to online users","Angi melding for dagen på alle maskiner og send til "}. -{"Shared Roster Groups","Delte Kontaktgrupper"}. -{"Show Integral Table","Vis Integral Tabell"}. -{"Show Ordinary Table","Vis Ordinær Tabell"}. -{"Shut Down Service","Avslutt Tjeneste"}. -{"~s invites you to the room ~s","~s inviterer deg til rommet ~s"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Noen Jabber klienter kan lagre passordet på datamaskinen. Bruk bare den funksjonen dersom du er sikker på at maskinen er trygg."}. -{"Specify the access model","Spesifiser aksess modellen"}. -{"Specify the event message type","Spesifiser hendelsesbeskjed type"}. -{"Specify the publisher model","Angi publiserings modell"}. -{"~s's Offline Messages Queue","~ss kø for Frakoblede Meldinger"}. -{"Start Modules at ","Start Moduler på "}. -{"Start Modules","Start Moduler"}. -{"Start","Start"}. -{"Statistics of ~p","Statistikk for ~p"}. -{"Statistics","Statistikk"}. -{"Stop Modules at ","Stopp Moduler på "}. -{"Stop Modules","Stop Moduler"}. -{"Stopped Nodes","Stoppede Noder"}. -{"Stop","Stoppe"}. -{"Storage Type","Lagringstype"}. +{"Shared Roster Groups","Delte kontaktgrupper"}. +{"Show Ordinary Table","Vis ordinær tabell"}. +{"Shut Down Service","Avslutt tjeneste"}. +{"Specify the access model","Spesifiser tilgangsmodellen"}. +{"Specify the event message type","Spesifiser hendelsesbeskjedtypen"}. +{"Specify the publisher model","Angi publiseringsmodell"}. +{"Stopped Nodes","Stoppede noder"}. {"Store binary backup:","Lagre binær sikkerhetskopi:"}. -{"Store plain text backup:","Lagre rentekst sikkerhetskopi:"}. -{"Subject","Tittel"}. -{"Submit","Send"}. +{"Store plain text backup:","Lagre klartekst-sikkerhetskopi:"}. +{"Subject","Emne"}. {"Submitted","Innsendt"}. -{"Subscriber Address","Abonnements Adresse"}. -{"Subscription","Abonnement"}. +{"Subscriber Address","Abonnementsadresse"}. +{"Subscribers may publish","Abonnenter kan publisere"}. +{"Subscriptions are not allowed","Abonnementer tillates ikke"}. {"Sunday","søndag"}. +{"Text associated with a picture","Tekst tilknyttet et bilde"}. +{"Text associated with a sound","Tekst tilknyttet en lyd"}. +{"Text associated with a video","Tekst tilknyttet en video"}. +{"Text associated with speech","Tekst tilknyttet tale"}. {"That nickname is already in use by another occupant","Det kallenavnet er allerede i bruk av en annen deltaker"}. {"That nickname is registered by another person","Det kallenavnet er registrert av en annen person"}. -{"The CAPTCHA is valid.","Captchaen er ikke gyldig"}. -{"The CAPTCHA verification has failed","CAPTCHA godkjenningen har feilet"}. -{"The collections with which a node is affiliated","Samlingene som en node er assosiert med"}. -{"the password is","passordet er"}. +{"The account already exists","Kontoen finnes allerede"}. +{"The account was not unregistered","Kontoen ble ikke avregistrert"}. +{"The body text of the last received message","Brødteksten i sist mottatte melding"}. +{"The CAPTCHA is valid.","CAPTCHA-en er gyldig."}. +{"The CAPTCHA verification has failed","CAPTCHA-godkjenning mislyktes"}. +{"The captcha you entered is wrong","CAPTCHA-en du skrev inn er feil"}. +{"The number of unread or undelivered messages","Antallet uleste eller uleverte meldinger"}. +{"The password contains unacceptable characters","Passordet inneholder ulovlige tegn"}. {"The password is too weak","Passordet er for svakt"}. -{"The password of your Jabber account was successfully changed.","Passordet for din Jabber konto ble endret."}. -{"There was an error changing the password: ","En feil skjedde under endring av passordet:"}. -{"There was an error creating the account: ","En feil skjedde under oppretting av kontoen:"}. -{"There was an error deleting the account: ","En feil skjedde under sletting av kontoen: "}. -{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Denne er ufølsom for små og store bokstaver: macbeth er det samme som MacBeth og Macbeth. "}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Denne siden lar deg lage en Jabber konto på denne Jabber serveren. Din JID (Jabber ID) vil være i formatet: brukernavn@server. Vennligst les instruksjonene nøye slik at du fyller ut skjemaet riktig."}. -{"This page allows to unregister a Jabber account in this Jabber server.","Denne siden lar deg avregistrere en Jabber konto på denne Jabber serveren."}. +{"the password is","passordet er"}. +{"The presence states for which an entity wants to receive notifications","Tilstedeværelsestilstandene en eksistens ønsker å motta merknader for"}. +{"The query is only allowed from local users","Spørringen tillates kun fra lokale brukere"}. +{"The query must not contain elements","Spørringen kan ikke inneholde -elementer"}. +{"The room subject can be modified by participants","Romemnet kan endres av dets deltagere"}. +{"The sender of the last received message","Avsender for sist mottatte melding"}. +{"There was an error creating the account: ","En feil inntraff under oppretting av kontoen: "}. +{"There was an error deleting the account: ","En feil inntraff under sletting av kontoen: "}. +{"This room is not anonymous","Dette rommet er ikke anonymt"}. {"Thursday","torsdag"}. -{"Time delay","Tids forsinkelse"}. -{"Time","Tid"}. -{"Too many CAPTCHA requests","For mange CAPTCHA forespørsler"}. -{"To ~s","Til ~s"}. -{"To","Til"}. -{"Traffic rate limit is exceeded","Trafikkmengde grense overskredet"}. -{"Transactions Aborted:","Avbrutte Transasksjoner:"}. -{"Transactions Committed:","Sendte Transaksjoner:"}. -{"Transactions Logged:","Loggede Transasksjoner:"}. -{"Transactions Restarted:","Omstartede Transasksjoner:"}. +{"Time delay","Tidsforsinkelse"}. +{"To register, visit ~s","Besøk ~s for registrering"}. +{"Too many CAPTCHA requests","For mange CAPTCHA-forespørsler"}. +{"Too many elements","For mange -elementer"}. +{"Too many elements","For mange -elementer"}. +{"Traffic rate limit is exceeded","Grense for tillatt trafikkmengde overskredet"}. {"Tuesday","tirsdag"}. -{"Unable to generate a CAPTCHA","Umulig å generere en CAPTCHA"}. +{"Unable to generate a CAPTCHA","Kunne ikke generere CAPTCHA"}. {"Unauthorized","Uautorisert"}. -{"Unregister a Jabber account","Avregistrer en Jabber konto"}. {"Unregister","Avregistrer"}. +{"Unsupported element","Ustøttet -element"}. +{"Unsupported version","Ustøttet versjon"}. {"Update message of the day (don't send)","Oppdater melding for dagen (ikke send)"}. -{"Update message of the day on all hosts (don't send)","Oppdater melding for dagen på alle maskiner (ikke send)"}. -{"Update","Oppdatere"}. -{"Update plan","Oppdaterings plan"}. -{"Update script","Oppdaterings skript"}. -{"Uptime:","Oppetid:"}. -{"Use of STARTTLS required","Bruk av STARTTLS kreves"}. +{"Update message of the day on all hosts (don't send)","Oppdater melding for dagen på alle verter (ikke send)"}. +{"User already exists","Brukeren finnes allerede"}. +{"User Management","Brukerhåndtering"}. +{"User removed","Fjernet bruker"}. +{"User ~ts","Bruker ~ts"}. {"User","Bruker"}. -{"User JID","Bruker JID"}. -{"User Management","Bruker Behandling"}. {"Username:","Brukernavn:"}. -{"Users are not allowed to register accounts so quickly","Brukere får ikke lov til registrere kontoer så fort"}. +{"Users are not allowed to register accounts so quickly","Brukere har ikke lov til registrere kontoer så fort"}. +{"Users Last Activity","Brukers siste aktivitet"}. {"Users","Brukere"}. -{"Users Last Activity","Brukers Siste Aktivitet"}. -{"Validate","Bekrefte gyldighet"}. -{"vCard User Search","vCard Bruker Søk"}. -{"Virtual Hosts","Virtuella Maskiner"}. -{"Visitors are not allowed to change their nicknames in this room","Besøkende får ikke lov å endre kallenavn i dette "}. +{"vCard User Search","vCard-brukersøk"}. +{"Virtual Hosts","Virtuelle maskiner"}. +{"Visitor","Besøker"}. +{"Visitors are not allowed to change their nicknames in this room","Besøkende får ikke lov å endre kallenavn i dette rommet"}. {"Visitors are not allowed to send messages to all occupants","Besøkende får ikke sende meldinger til alle deltakere"}. -{"Voice request","Lyd forespørsel"}. -{"Voice requests are disabled in this conference","Lyd forespørsler er blokkert i denne konferansen"}. +{"Voice requests are disabled in this conference","Stemmeforespørsler er blokkert i denne konferansen"}. +{"Voice request","Stemmeforespørsel"}. {"Wednesday","onsdag"}. {"When to send the last published item","Når skal siste publiserte artikkel sendes"}. -{"Whether to allow subscriptions","Om man skal tillate abonnenter"}. -{"You can later change your password using a Jabber client.","Du kan når som helst endre passordet via en Jabber klient."}. -{"You have been banned from this room","Du har blitt bannlyst i dette rommet."}. -{"You must fill in field \"Nickname\" in the form","Du må fylle inn feltet \"Nickname\" i skjemaet"}. -{"You need a client that supports x:data and CAPTCHA to register","Du trenger en klient som støtter x:data og CAPTCHA for registrering "}. +{"Whether an entity wants to receive an XMPP message body in addition to the payload format","Hvorvidt en eksistens ønsker å motta en XMPP-meldingsbrødtekst i tillegg til nyttedata-formatet"}. +{"Whether an entity wants to receive or disable notifications","Hvorvidt en eksistens ønsker å motta eller skru av merknader"}. +{"Whether owners or publisher should receive replies to items","Hvorvidt elere eller publisererer skal motta svar på elementer"}. +{"Whether to allow subscriptions","Hvorvidt abonnementer skal tillates"}. +{"XMPP Domains","XMPP-domener"}. +{"You have been banned from this room","Du har blitt bannlyst fra dette rommet."}. +{"You have joined too many conferences","Du har tilknyttet deg for mange konferanser"}. +{"You must fill in field \"Nickname\" in the form","Du må fylle inn feltet «Kallenavn» i skjemaet"}. +{"You need a client that supports x:data and CAPTCHA to register","Du trenger en klient som støtter x:data og CAPTCHA for registrering"}. {"You need a client that supports x:data to register the nickname","Du trenger en klient som støtter x:data for å registrere kallenavnet"}. -{"You need an x:data capable client to configure mod_irc settings","Du trenger en x:data kompatibel klient for å konfigurere mod_irc instillinger"}. -{"You need an x:data capable client to configure room","Du trenger en klient som støtter x:data for å "}. -{"You need an x:data capable client to search","Du tregner en klient som støtter x:data for å kunne "}. -{"Your active privacy list has denied the routing of this stanza.","Din aktive privat liste har blokkert rutingen av denne strofen."}. +{"You need an x:data capable client to search","Du trenger en klient som støtter x:data for å søke"}. {"Your contact offline message queue is full. The message has been discarded.","Kontaktens frakoblede meldingskø er full. Meldingen har blitt kassert."}. -{"Your Jabber account was successfully created.","Din Jabber konto ble opprettet"}. -{"Your Jabber account was successfully deleted.","Dni Jabber konto er blitt sltettet."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","Dine meldinger til ~s blir blokkert. For å åpne igjen, besøk ~s"}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Din abonnementsforespørsel og/eller meldinger til ~s har blitt blokkert. For å avblokkere din abonnementsforespørsel, besøk ~s"}. diff --git a/priv/msgs/no.po b/priv/msgs/no.po deleted file mode 100644 index a90e02909..000000000 --- a/priv/msgs/no.po +++ /dev/null @@ -1,1925 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: 2.1.0-alpha\n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: Stian B. Barmen \n" -"Language-Team: \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Norwegian (bokmål)\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "Bruk av STARTTLS kreves" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Ingen ressurs angitt" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Erstattet av en ny tilkobling" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "har blitt kastet ut" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "Din aktive privat liste har blokkert rutingen av denne strofen." - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Skriv inn teksten du ser" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "Dine meldinger til ~s blir blokkert. For å åpne igjen, besøk ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "Dersom du ikke ser CAPTCHA bilde her, besøk web siden. " - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "CAPTCHA web side" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "Captchaen er ikke gyldig" - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Bruker" - -#: ejabberd_oauth.erl:256 -#, fuzzy -msgid "Server" -msgstr "Server:" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Passord" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "Uautorisert" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "ejabberd Web Admin" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Administrasjon" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Tilgangskontrollister" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Innsendt" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Feil format" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Send" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "Rå" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Slett valgte" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Tilgangsregler" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "tilgangsregel konfigurasjon for ~s" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "Virtuella Maskiner" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Brukere" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "Tilkoblede Brukere" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Brukers Siste Aktivitet" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Periode: " - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "Siste måned" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "Siste året" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "All aktivitet" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Vis Ordinær Tabell" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Vis Integral Tabell" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Statistikk" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "Finnes Ikke" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Noden finnes ikke" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Legg til ny" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Maskin" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Registrerte Brukere" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Legg til Bruker" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Frakoblede Meldinger" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Siste Aktivitet" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Aldri" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Tilkoblet" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Registrerte Brukere:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Tilkoblede Brukere:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Utgående s2s Koblinger" - -#: ejabberd_web_admin.erl:1559 -#, fuzzy -msgid "Incoming s2s Connections:" -msgstr "Utgående s2s Koblinger" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Ingen" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Endre Passord" - -#: ejabberd_web_admin.erl:1673 -#, fuzzy -msgid "User ~s" -msgstr "Bruker " - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Tilkoblede Ressurser:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Passord:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Fjern Bruker" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Ingen Data" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Noder" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Kjørende Noder" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Stoppede Noder" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -#, fuzzy -msgid "Node ~p" -msgstr "Node " - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "Database" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Sikkerhetskopier" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Lyttende Porter" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Oppdatere" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Starte på nytt" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Stoppe" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Moduler" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "RPC Kall Feil" - -#: ejabberd_web_admin.erl:1917 -#, fuzzy -msgid "Database Tables at ~p" -msgstr "Database Tabeller på " - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Navn" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Lagringstype" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Elementer" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Minne" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Feil" - -#: ejabberd_web_admin.erl:1955 -#, fuzzy -msgid "Backup of ~p" -msgstr "Sikkerhetskopi av " - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Merk at disse valgene vil bare sikkerhetskopiere den innebygde Mnesia " -"databasen. Dersom du bruker ODBC modulen må du også ta backup av din SQL " -"database." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Lagre binær sikkerhetskopi:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "OK" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Gjenopprette binær backup umiddelbart:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"Gjenopprette binær backup etter neste ejabberd omstart (krever mindre minne):" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Lagre rentekst sikkerhetskopi:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Gjenopprette rentekst sikkerhetskopi umiddelbart:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "Importer brukeres data fra en PIEFXIS fil (XEP-0227):" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "Eksporter data om alle brukere i en server til PIEFXIS filer" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "Eksporter data om alle brukere i en host til PIEFXIS filer (XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Importer bruker data fra jabberd14 spoolfiler:" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Importer brukeres data fra jabberd14 spoolfil katalog:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Lyttende Porter på " - -#: ejabberd_web_admin.erl:2144 -#, fuzzy -msgid "Modules at ~p" -msgstr "Moduler på " - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "Statistikk for ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Oppetid:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "CPU Tid:" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Sendte Transaksjoner:" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Avbrutte Transasksjoner:" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Omstartede Transasksjoner:" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Loggede Transasksjoner:" - -#: ejabberd_web_admin.erl:2243 -#, fuzzy -msgid "Update ~p" -msgstr "Oppdater " - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "Oppdaterings plan" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Endrede moduler" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Oppdaterings skript" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Lavnivå oppdaterings skript" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Skript sjekk" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Port" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Protokoll" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Modul" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Alternativer" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Start" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Kommandoer" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "Virkelig slette melding for dagen?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Tittel" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Meldingskropp" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "Ingen meldingskropp gitt for kunngjørings melding" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Kunngjøringer" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Send kunngjøring til alle brukere" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Send kunngjøring til alle brukere på alle maskiner" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Send kunngjøring alle tilkoblede brukere" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "Send kunngjøring til alle tilkoblede brukere på alle " - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Angi melding for dagen og send til tilkoblede brukere" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "Angi melding for dagen på alle maskiner og send til " - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Oppdater melding for dagen (ikke send)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "Oppdater melding for dagen på alle maskiner (ikke send)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Slett melding for dagen" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Slett melding for dagen på alle maskiner" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Konfigurasjon" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Start Moduler" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Stop Moduler" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Gjenopprett" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Dump til Tekstfil" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Importer File" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Importer Katalog" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Start Tjeneste på Nytt" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Avslutt Tjeneste" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Slett Bruker" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Avslutt Bruker Sesjon" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Hent Brukers Passord" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Endre Brukers Passord" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "Vis Brukers Siste Påloggings Tidspunkt" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Vis Bruker Statistikk" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Vis Antall Registrerte Brukere" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Vis Antall Tilkoblede Brukere" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Bruker Behandling" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Alle Brukere" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Utgående s2s Koblinger" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Håndtere Sikkerehetskopiering" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Importer Brukere Fra jabberd14 Spoolfiler" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "Til ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "Fra ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Database Tabell Konfigurasjon på " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Velg lagringstype for tabeller" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Kun diskkopi" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "RAM og diskkopi" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "RAM kopi" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "Lagres ikke lokalt" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Stopp Moduler på " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Velg hvilke moduler som skal stoppes" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Start Moduler på " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Skriv inn en liste av {Module, [Options]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Liste over moduler som skal startes" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Sikkerhetskopiere til Fil på " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Skriv inn sti til sikkerhetskopi filen" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Sti til Fil" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Gjenopprett fra Sikkerhetsopifil på " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Dump Sikkerhetskopi til Tekstfil på " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Skriv inn sti til tekstfil" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Importer Bruker fra Fil på " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Skriv inn sti til jabberd14 spoolfil" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Importer Brukere fra Katalog på " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Skriv inn sti til jabberd14 spoolkatalog" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Sti til Katalog" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "Tids forsinkelse" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Konfigurasjon for Tilgangskontroll lister" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Tilgangskontroll lister" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Tilgangskonfigurasjon" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Tilgangsregler" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Passord Bekreftelse" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Antall registrerte brukere" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Antall tilkoblede brukere" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Siste pålogging" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "Kontaktliste størrelse" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "IP adresser" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Ressurser" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Administrasjon av " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Handling på bruker" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Redigere Egenskaper" - -#: mod_fail2ban.erl:95 -msgid "" -"Too many (~p) failed authentications from this IP address (~s). The address " -"will be unblocked at ~s UTC" -msgstr "" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "" - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "" - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "Tilgang nektes på grunn av en tjeneste regel" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "IRC Transport" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "ejabberd IRC modul" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "" -"Du trenger en x:data kompatibel klient for å konfigurere mod_irc instillinger" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Registrering i mod_irc for " - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Skriv brukernavn, tekstkoding, porter og passord du ønsker å bruke for " -"tilkobling til IRC servere" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "IRC Brukernavn" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Om du ønsker å spesifisere tekstkoding for IRC tjenere, fyller du ut en " -"liste med verdier i formatet '{\"irc server\", \"encoding\", port, \"password" -"\"}'. Denne tjenesten bruker \"~s\" som standard, port ~p, empty password." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Eksempel: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta." -"fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Tilkoblings parametere" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "Bli med i IRC kanal" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "IRC kanal (ikke skriv den første #)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "IRC server" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Bli med i IRC kanalen her. " - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Bli med i IRC kanalen med denne Jabber ID: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "IRC instillinger" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Angi brukernavn og kodinger du ønsker å bruke for å koble til IRC servere. " -"Trykk 'Neste' for å få flere felt for å fylle i. Trykk 'Fullfør' for å lagre " -"innstillingene." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "IRC brukernavn" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Passord ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Port ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Tekstkoding for server ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Server ~b" - -#: mod_mam.erl:541 -#, fuzzy -msgid "Only members may query archives of this room" -msgstr "Bare ordstyrer tillates å endre emnet i dette rommet" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "Bare tjeneste administratorer er tilatt å sende tjeneste " - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "Oppretting av rom nektes av en tjenste regel" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "Konferanserommet finnes ikke" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Samtalerom" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "Du trenger en klient som støtter x:data for å registrere kallenavnet" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Registrer Kallenavn på " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Skriv inn kallenavnet du ønsker å registrere" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Kallenavn" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "Det kallenavnet er registrert av en annen person" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Du må fylle inn feltet \"Nickname\" i skjemaet" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "ejabberd MUC modul" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "" - -#: mod_muc_admin.erl:249 -#, fuzzy -msgid "Total rooms" -msgstr "Samtalerom" - -#: mod_muc_admin.erl:250 -#, fuzzy -msgid "Permanent rooms" -msgstr "forlater rommet" - -#: mod_muc_admin.erl:251 -#, fuzzy -msgid "Registered nicknames" -msgstr "Registrerte Brukere" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "Samtalerommets konfigurasjon er endret" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "kommer inn i rommet" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "forlater rommet" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "har blitt bannlyst" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "har blitt kastet ut på grunn av en tilknytnings endring" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "" -"har blitt kastet ut på grunn av at rommet er endret til kun-for-medlemmer" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "har blitt kastet ut på grunn av at systemet avslutter" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "er nå kjent som" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " har satt emnet til: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "Samtalerom er opprettet" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "Samtalerom er fjernet" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "Samtalerom er startet" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "Samtalerom er stoppet" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "mandag" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "tirsdag" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "onsdag" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "torsdag" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "fredag" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "lørdag" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "søndag" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "januar" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "februar" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "mars" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "april" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "mai" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "juni" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "juli" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "august" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "september" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "oktober" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "november" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "desember" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Rom Konfigurasjon" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "Samtalerom Deltakere" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "Trafikkmengde grense overskredet" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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 "" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "Det er ikke tillatt å sende private meldinger til " - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "Vennligst vent en stund før du sender en ny lyd forespørsel" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "Lyd forespørsler er blokkert i denne konferansen" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "Feilet i forsøk på å hente JID fra din lyd forespørsel godkjenning" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "Bare ordstyrer kan godkjenne lyd forespørsler" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Feilaktig meldingstype" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "Det er ikke tillatt å sende private meldinger med typen " - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "Mottakeren er ikke i konferanserommet" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "Det er ikke tillatt å sende private meldinger" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Bare deltakere får sende normale meldinger til konferansen" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "Bare deltakere er tillatt å sende forespørsler til " - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "Forespørsler til konferanse medlemmene er ikke tillat i dette rommet" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "Bare redaktører og deltakere kan endre emnet i dette rommet" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Bare ordstyrer tillates å endre emnet i dette rommet" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "Besøkende får ikke sende meldinger til alle deltakere" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "Besøkende får ikke lov å endre kallenavn i dette " - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "Det kallenavnet er allerede i bruk av en annen deltaker" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Du har blitt bannlyst i dette rommet." - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "Medlemskap kreves for tilgang til samtalerommet" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "Et passord kreves for tilgang til samtalerommet" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "For mange CAPTCHA forespørsler" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "Umulig å generere en CAPTCHA" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Feil passord" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "Administratorprivilegier kreves" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "Redaktørprivilegier kreves" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "Ugyldig Jabber ID ~s" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "Kallenavn ~s eksisterer ikke i dette rommet" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Ugyldig rang: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Ugyldig rolle: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "Eierprivilegier kreves" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "Konfigurasjon for rom ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Romtittel" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "Rom beskrivelse" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Gjør rommet permanent" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Gjør rommet offentlig søkbart" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "Gjør deltakerlisten offentlig" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Passordbeskytt rommet" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Maksimum Antall Deltakere" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Ingen grense" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Presenter ekte Jabber IDer til" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "kun for redaktører" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "hvem som helst" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "" - -#: mod_muc_room.erl:3486 -#, fuzzy -msgid "Moderator" -msgstr "kun for redaktører" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Gjør rommet tilgjengelig kun for medlemmer" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Gjør rommet redaktørstyrt" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Standard brukere som deltakere" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Tillat brukere å endre emne" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Tillat brukere å sende private meldinger" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "Tillat brukere å sende private meldinger til" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "ingen" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Tillat brukere å sende forespørsel til andre brukere" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Tillat brukere å sende invitasjoner" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "Tillat besøkende å sende status tekst i " - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Tillat besøkende å endre kallenavn" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "Tillat brukere å sende lyd forespørsler" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "Minimums interval mellom lyd forespørsler (i sekunder)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "Gjør rommet CAPTCHA beskyttet" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "Ekskluder Jabber IDer fra CAPTCHA utfordring" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Slå på logging" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "Du trenger en klient som støtter x:data for å " - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Antall deltakere" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "privat, " - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "Lyd forespørsel" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "Enten godkjenn eller forby lyd forespørselen" - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "Bruker JID" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "Gi lyd til denne personen?" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s inviterer deg til rommet ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "passordet er" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "Kontaktens frakoblede meldingskø er full. Meldingen har blitt kassert." - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "~ss kø for Frakoblede Meldinger" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Tid" - -#: mod_offline.erl:812 -msgid "From" -msgstr "Fra" - -#: mod_offline.erl:813 -msgid "To" -msgstr "Til" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Pakke" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Frakoblede Meldinger:" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "Fjern Alle Frakoblede Meldinger" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "ejabberd SOCKS5 Bytestreams modul" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Publish-Subscribe" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "ejabberd Publish-Subscribe modul" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "PubSub abonements forespørsel" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Velg om du vil godkjenne denne eksistensens abonement" - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "Node ID" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Abonnements Adresse" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "Tillat denne Jabber ID å abonnere på denne pubsub " - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Send innhold sammen med kunngjøringer" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Lever begivenhets kunngjøringer" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Informer abonnenter når node konfigurasjonen endres" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Informer abonnenter når noden slettes" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Informer abonnenter når elementer fjernes fra noden" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Vedvarende elementer til lagring" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "Et vennlig navn for noden" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Høyeste # elementer som skal lagres" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Om man skal tillate abonnenter" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Spesifiser aksess modellen" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Kontaktliste grupper som tillates å abonnere" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Angi publiserings modell" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "Rydd alle elementer når den aktuelle utgiveren logger av" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "Spesifiser hendelsesbeskjed type" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Største innholdsstørrelse i byte" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "Når skal siste publiserte artikkel sendes" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Send kunngjøringer bare til tilgjengelige brukere" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "Samlingene som en node er assosiert med" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "CAPTCHA godkjenningen har feilet" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "Du trenger en klient som støtter x:data og CAPTCHA for registrering " - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "Velg et brukernavn og passord for å registrere på " - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "Passordet er for svakt" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "Brukere får ikke lov til registrere kontoer så fort" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "Din Jabber konto ble opprettet" - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "En feil skjedde under oppretting av kontoen:" - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "Dni Jabber konto er blitt sltettet." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "En feil skjedde under sletting av kontoen: " - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "Passordet for din Jabber konto ble endret." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "En feil skjedde under endring av passordet:" - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Jabber Konto Registrering" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Registrer en Jabber konto" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Avregistrer en Jabber konto" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"Denne siden lar deg lage en Jabber konto på denne Jabber serveren. Din JID " -"(Jabber ID) vil være i formatet: brukernavn@server. Vennligst les " -"instruksjonene nøye slik at du fyller ut skjemaet riktig." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "Brukernavn:" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" -"Denne er ufølsom for små og store bokstaver: macbeth er det samme som " -"MacBeth og Macbeth. " - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "Ikke godtatte tegn:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "Server:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" -"Ikke fortell passordet til noen, ikke en gang til administratoren av Jabber " -"serveren." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "Du kan når som helst endre passordet via en Jabber klient." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Noen Jabber klienter kan lagre passordet på datamaskinen. Bruk bare den " -"funksjonen dersom du er sikker på at maskinen er trygg." - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"Husk passordet, eller skriv det ned på et papir lagret på et trygt sted. I " -"Jabber er det ingen automatisert måte å gjenskape passordet om du glemmer " -"det. " - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "Passord Bekreftelse:" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "Registrer" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "Gammelt Passord:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "Nytt Passord:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "" -"Denne siden lar deg avregistrere en Jabber konto på denne Jabber serveren." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "Avregistrer" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Abonnement" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Ventende" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Grupper" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Bekrefte gyldighet" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Fjern" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Kontaktliste for " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Legg til Jabber ID" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Kontaktliste" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Delte Kontaktgrupper" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Navn:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Beskrivelse:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Medlemmer:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Viste grupper:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Gruppe " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Erlang Jabber Server" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Fødselsdag" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "By" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "Land" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "Epost" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Etternavn" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Fyll inn skjemaet for å søke etter Jabber bruker (Legg til * på slutten av " -"feltet for å treffe alle som starter slik)" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Fullstendig Navn" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Mellomnavn" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Organisasjonsnavn" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Organisasjonsenhet" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Søk etter brukere i " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "Du tregner en klient som støtter x:data for å kunne " - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "vCard Bruker Søk" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "ejabberd vCard modul" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Søke Resultater for " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "Fyll inn felt for å søke etter Jabber brukere" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Utgående s2s Tjenere" - -#~ msgid "Delete" -#~ msgstr "Slett" - -#~ msgid "This room is not anonymous" -#~ msgstr "Dette rommet er ikke anonymt" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "" -#~ "Denne deltakeren er kastet ut av rommet fordi han sendte en feilmelding" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Denne deltakeren er kastet ut av rommet fordi han sendte en feilmelding " -#~ "til en annen deltaker" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "" -#~ "Denne deltakeren er kastet ut av rommet fordi han sendte feil " -#~ "tilstederværelse" - -#, fuzzy -#~ msgid "Captcha test failed" -#~ msgstr "Captchaen er ikke gyldig" diff --git a/priv/msgs/pl.msg b/priv/msgs/pl.msg index 03fbd3d00..e4015091c 100644 --- a/priv/msgs/pl.msg +++ b/priv/msgs/pl.msg @@ -1,22 +1,24 @@ -%% -*- coding: latin-1 -*- -{"Accept","Zaakceptuj"}. -{"Access Configuration","Konfiguracja dostępu"}. -{"Access Control List Configuration","Konfiguracja listy dostępowej"}. -{"Access Control Lists","Lista dostępowa"}. -{"Access control lists","Listy dostępowe"}. -{"Access denied by service policy","Dostęp zabroniony zgodnie z zasadami usługi"}. -{"Access rules","Reguły dostępu"}. -{"Access Rules","Zasady dostępu"}. -{"Action on user","Wykonaj na użytkowniku"}. -{"Add Jabber ID","Dodaj Jabber ID"}. -{"Add New","Dodaj nowe"}. -{"Add User","Dodaj użytkownika"}. -{"Administration","Administracja"}. -{"Administration of ","Zarządzanie "}. -{"Administrator privileges required","Wymagane uprawnienia administratora"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" has set the subject to: "," zmienił temat na: "}. {"A friendly name for the node","Przyjazna nazwa węzła"}. +{"A password is required to enter this room","Aby wejść do pokoju wymagane jest hasło"}. +{"A Web Page","Strona sieci Web"}. +{"Accept","Zaakceptuj"}. +{"Access denied by service policy","Dostęp zabroniony zgodnie z zasadami usługi"}. +{"Action on user","Wykonaj na użytkowniku"}. +{"Add a hat to a user","Dodaj kapelusz do użytkownika"}. +{"Add User","Dodaj użytkownika"}. +{"Administration of ","Zarządzanie "}. +{"Administration","Administracja"}. +{"Administrator privileges required","Wymagane uprawnienia administratora"}. {"All activity","Cała aktywność"}. +{"All Users","Wszyscy użytkownicy"}. {"Allow this Jabber ID to subscribe to this pubsub node?","Pozwól temu Jabber ID na zapisanie się do tego węzła PubSub"}. +{"Allow this person to register with the room?","Pozwolić tej osobie zarejestrować się w tym pokoju?"}. {"Allow users to change the subject","Pozwól użytkownikom zmieniać temat"}. {"Allow users to query other users","Pozwól użytkownikom pobierać informacje o innych użytkownikach"}. {"Allow users to send invites","Pozwól użytkownikom wysyłać zaproszenia"}. @@ -25,21 +27,25 @@ {"Allow visitors to send private messages to","Pozwól użytkownikom wysyłać prywatne wiadomości"}. {"Allow visitors to send status text in presence updates","Pozwól uczestnikom na wysyłanie statusów opisowych"}. {"Allow visitors to send voice requests","Pozwól użytkownikom wysyłać zaproszenia"}. -{"All Users","Wszyscy użytkownicy"}. {"Announcements","Powiadomienia"}. -{"anyone","wszystkich"}. -{"A password is required to enter this room","Aby wejść do pokoju wymagane jest hasło"}. {"April","Kwiecień"}. {"August","Sierpień"}. +{"Automatic node creation is not enabled","Automatyczne tworzenie węzłów nie zostało włączone"}. {"Backup Management","Zarządzanie kopiami zapasowymi"}. {"Backup of ~p","Kopia zapasowa ~p"}. {"Backup to File at ","Zapisz kopię w pliku na "}. -{"Backup","Wykonaj kopie"}. +{"Backup","Kopia zapasowa"}. {"Bad format","Błędny format"}. {"Birthday","Data urodzenia"}. +{"Both the username and the resource are required","Wymagana jest zarówno nazwa użytkownika jak i zasób"}. +{"Bytestream already activated","Strumień danych został już aktywowany"}. +{"Cannot remove active list","Nie można usunąć aktywnej listy"}. +{"Cannot remove default list","Nie można usunąć domyślnej listy"}. {"CAPTCHA web page","Strona internetowa CAPTCHA"}. {"Change Password","Zmień hasło"}. {"Change User Password","Zmień hasło użytkownika"}. +{"Changing password is not allowed","Zmiana hasła jest niedopuszczalna"}. +{"Changing role/affiliation is not allowed","Zmiana roli jest niedopuszczalna"}. {"Characters not allowed:","Te znaki są niedozwolone:"}. {"Chatroom configuration modified","Konfiguracja pokoju zmodyfikowana"}. {"Chatroom is created","Pokój został stworzony"}. @@ -48,127 +54,102 @@ {"Chatroom is stopped","Pokój został zatrzymany"}. {"Chatrooms","Pokoje rozmów"}. {"Choose a username and password to register with this server","Wybierz nazwę użytkownika i hasło aby zarejestrować się na tym serwerze"}. -{"Choose modules to stop","Wybierz moduły do zatrzymania"}. {"Choose storage type of tables","Wybierz typ bazy dla tablel"}. {"Choose whether to approve this entity's subscription.","Wybierz, czy akceptować subskrypcję tej jednostki"}. {"City","Miasto"}. {"Commands","Polecenia"}. {"Conference room does not exist","Pokój konferencyjny nie istnieje"}. -{"Configuration","Konfiguracja"}. {"Configuration of room ~s","Konfiguracja pokoju ~s"}. -{"Connected Resources:","Zasoby zalogowane:"}. -{"Connections parameters","Parametry połączeń"}. +{"Configuration","Konfiguracja"}. {"Country","Państwo"}. -{"CPU Time:","Czas CPU:"}. -{"Database","Baza danych"}. -{"Database Tables at ~p","Tabele bazy na ~p"}. +{"Database failure","Błąd bazy danych"}. {"Database Tables Configuration at ","Konfiguracja tabel bazy na "}. +{"Database","Baza danych"}. {"December","Grudzień"}. {"Default users as participants","Domyślni użytkownicy jako uczestnicy"}. {"Delete message of the day on all hosts","Usuń wiadomość dnia ze wszystkich hostów"}. {"Delete message of the day","Usuń wiadomość dnia"}. -{"Delete Selected","Usuń zaznaczone"}. {"Delete User","Usuń użytkownika"}. {"Deliver event notifications","Dostarczaj powiadomienia o zdarzeniach"}. {"Deliver payloads with event notifications","Dostarczaj zawartość publikacji wraz z powiadomieniami o zdarzeniach"}. -{"Description:","Opis:"}. {"Disc only copy","Kopia tylko na dysku"}. -{"Displayed Groups:","Wyświetlane grupy:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","Nie podawaj swojego hasła nikomu, nawet administratorowi serwera Jabber."}. {"Dump Backup to Text File at ","Zapisz kopię zapasową w pliku tekstowym na "}. {"Dump to Text File","Wykonaj kopie do pliku tekstowego"}. {"Edit Properties","Edytuj właściwości"}. -{"Either approve or decline the voice request.","Zatwierdź lub odrzuć żądanie głosowe"}. -{"ejabberd IRC module","Moduł IRC ejabberd"}. +{"Either approve or decline the voice request.","Zatwierdź lub odrzuć żądanie głosowe."}. {"ejabberd MUC module","Moduł MUC"}. {"ejabberd Multicast service","Serwis multicast ejabbera"}. {"ejabberd Publish-Subscribe module","Moduł Publish-Subscribe"}. {"ejabberd SOCKS5 Bytestreams module","Moduł SOCKS5 Bytestreams"}. {"ejabberd vCard module","Moduł vCard ejabberd"}. {"ejabberd Web Admin","ejabberd: Panel Administracyjny"}. -{"Elements","Elementy"}. {"Email","Email"}. -{"Empty Rooms","Puste pokoje"}. {"Enable logging","Włącz logowanie"}. {"Enable message archiving","Włącz archiwizowanie rozmów"}. -{"Encoding for server ~b","Kodowanie znaków dla serwera ~b"}. +{"Enabling push without 'node' attribute is not supported","Aktywacja 'push' bez węzła jest nie dostępna"}. {"End User Session","Zakończ sesję uzytkownika"}. -{"Enter list of {Module, [Options]}","Wprowadź listę {Moduł, [Opcje]}"}. {"Enter nickname you want to register","Wprowadz nazwę użytkownika którego chcesz zarejestrować"}. {"Enter path to backup file","Wprowadź scieżkę do pliku kopii zapasowej"}. {"Enter path to jabberd14 spool dir","Wprowadź ścieżkę do roboczego katalogu serwera jabberd14"}. {"Enter path to jabberd14 spool file","Wprowadź ścieżkę do roboczego pliku serwera jabberd14"}. {"Enter path to text file","Wprowadź scieżkę do pliku tekstowego"}. {"Enter the text you see","Przepisz tekst z obrazka"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Wprowadź nazwę użytkownika i kodowania których chcesz używać do łączenia z serwerami IRC. Wciśnij \"Dalej\" aby ustawić więcej parametrów połączenia. Wciśnij \"Zakończ\" aby zapisać ustawienia."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Wprowadź nazwę użytkownika, port i kodowanie, których chcesz używać do łączenia z serwerami IRC"}. -{"Erlang Jabber Server","Erlang Jabber Server"}. -{"Error","Błąd"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Przykład: [{\"wroclaw.irc.pl\",\"utf-8\"}, {\"warszawa.irc.pl\", \"iso8859-2\"}]."}. {"Exclude Jabber IDs from CAPTCHA challenge","Pomiń Jabber ID z żądania CAPTCHA"}. {"Export all tables as SQL queries to a file:","Wyeksportuj wszystkie tabele jako zapytania SQL do pliku:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Eksportuj dane wszystkich użytkowników serwera do plików w formacie PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Eksportuj dane użytkowników z hosta do plików w formacie PIEFXIS (XEP-0227):"}. +{"External component failure","Błąd zewnętrznego komponentu"}. +{"External component timeout","Upłynął limit czasu zewnętrznego komponentu"}. +{"Failed to activate bytestream","Nie udało się aktywować strumienia danych"}. {"Failed to extract JID from your voice request approval","Nie udało się wydobyć JID-u z twojego żądania"}. +{"Failed to map delegated namespace to external component","Nie udało się znaleźć zewnętrznego komponentu na podstawie nazwy"}. +{"Failed to parse HTTP response","Nie udało się zanalizować odpowiedzi HTTP"}. +{"Failed to process option '~s'","Nie udało się przetworzyć opcji '~s'"}. {"Family Name","Nazwisko"}. {"February","Luty"}. -{"Fill in fields to search for any matching Jabber User","Wypełnij pola aby znaleźć pasujących użytkowników Jabbera"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Wypełnij formularz aby wyszukać użytkowników Jabbera (dodaj * na koniec zapytania aby wyszukać po fragmencie)"}. +{"File larger than ~w bytes","Plik jest większy niż ~w bajtów"}. {"Friday","Piątek"}. -{"From","Od"}. -{"From ~s","Od ~s"}. {"Full Name","Pełna nazwa"}. {"Get Number of Online Users","Pokaż liczbę zalogowanych użytkowników"}. {"Get Number of Registered Users","Pokaż liczbę zarejestrowanych użytkowników"}. {"Get User Last Login Time","Pokaż czas ostatniego zalogowania uzytkownika"}. -{"Get User Password","Pobierz hasło użytkownika"}. {"Get User Statistics","Pobierz statystyki użytkownika"}. +{"Given Name","Imię"}. {"Grant voice to this person?","Udzielić głosu tej osobie?"}. -{"Group ","Grupa "}. -{"Groups","Grupy"}. {"has been banned","został wykluczony"}. -{"has been kicked because of an affiliation change","został wyrzucony z powodu zmiany przynależności"}. {"has been kicked because of a system shutdown","został wyrzucony z powodu wyłączenia systemu"}. +{"has been kicked because of an affiliation change","został wyrzucony z powodu zmiany przynależności"}. {"has been kicked because the room has been changed to members-only","został wyrzucony z powodu zmiany pokoju na \"Tylko dla Członków\""}. {"has been kicked","został wyrzucony"}. -{" has set the subject to: "," zmienił temat na: "}. -{"Host","Host"}. +{"Host unknown","Nieznany host"}. {"If you don't see the CAPTCHA image here, visit the web page.","Jeśli nie widzisz obrazka CAPTCHA, odwiedź stronę internetową."}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Jeśli chcesz ustawić inne hasła, porty lub kodowania dla poszczególnych serwerów IRC, wypełnij tą listę wartościami w formacie '{\"irc server\",\"encoding\", port, \"password\"}'. Domyślne ta usługa używa kodowania \"~s\", portu ~p, bez hasła."}. {"Import Directory","Importuj katalog"}. {"Import File","Importuj plik"}. {"Import user data from jabberd14 spool file:","Importuj dane użytkownika z pliku roboczego serwera jabberd14:"}. {"Import User from File at ","Importuj użytkownika z pliku na "}. {"Import users data from a PIEFXIS file (XEP-0227):","Importuj dane użytkowników z pliku w formacie PIEFXIS (XEP-0227):"}. -{"Import users data from jabberd14 spool directory:","Importuj użytkowników z katalogu roboczego serwera jabberd14"}. +{"Import users data from jabberd14 spool directory:","Importuj użytkowników z katalogu roboczego serwera jabberd14:"}. {"Import Users from Dir at ","Importuj użytkowników z katalogu na "}. {"Import Users From jabberd14 Spool Files","Importuj użytkowników z plików roboczych serwera jabberd14"}. +{"Improper domain part of 'from' attribute","Nieprawidłowa domena atrybutu 'from'"}. {"Improper message type","Nieprawidłowy typ wiadomości"}. -{"Incoming s2s Connections:","Przychodzące połączenia s2s:"}. +{"Incorrect CAPTCHA submit","Nieprawidłowa odpowiedz dla CAPTCHA"}. +{"Incorrect data form","Nieprawidłowe dane w formatce"}. {"Incorrect password","Nieprawidłowe hasło"}. -{"Invalid affiliation: ~s","Nieprawidłowa przynależność: ~s"}. -{"Invalid role: ~s","Nieprawidłowa rola: ~s"}. +{"Incorrect value of 'action' attribute","Nieprawidłowe dane atrybutu 'action'"}. +{"Incorrect value of 'action' in data form","Nieprawidłowe dane atrybutu 'action'"}. +{"Incorrect value of 'path' in data form","Nieprawidłowe dane atrybutu 'path'"}. +{"Insufficient privilege","Niewystarczające uprawnienia"}. +{"Invalid 'from' attribute in forwarded message","Nieprawidłowy atrybut 'from' w przesyłanej dalej wiadomości"}. +{"Invitations are not allowed in this conference","Zaproszenia są wyłączone w tym pokoju"}. {"IP addresses","Adresy IP"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","Kanał IRC (nie używaj #)"}. -{"IRC server","Serwer IRC"}. -{"IRC settings","Ustawienia IRC"}. -{"IRC Transport","Transport IRC"}. -{"IRC username","Nazwa użytkownika IRC"}. -{"IRC Username","Nazwa użytkownika IRC"}. {"is now known as","jest teraz znany jako"}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Użytkownik nie może wysyłać wiadomości o błędach do pokoju. Użytkownik (~s) wysłał błąd (~s) i został wyrzucony z pokoju"}. {"It is not allowed to send private messages of type \"groupchat\"","Nie można wysyłać prywatnych wiadomości typu \"groupchat\""}. {"It is not allowed to send private messages to the conference","Nie wolno wysyłac prywatnych wiadomości na konferencję"}. -{"It is not allowed to send private messages","Wysyłanie prywatnych wiadomości jest zabronione"}. -{"Jabber Account Registration","Zakładanie konta Jabber"}. {"Jabber ID","Jabber ID"}. -{"Jabber ID ~s is invalid","Jabber ID ~s jest niepoprawny"}. {"January","Styczeń"}. -{"Join IRC channel","Dołącz do kanału IRC"}. {"joins the room","dołącza do pokoju"}. -{"Join the IRC channel here.","Dołącz do kanału IRC."}. -{"Join the IRC channel in this Jabber ID: ~s","Dołącz do kanału IRC pod tym Jabber ID: ~s"}. {"July","Lipiec"}. {"June","Czerwiec"}. {"Last Activity","Ostatnia aktywność"}. @@ -176,11 +157,6 @@ {"Last month","Miniony miesiąc"}. {"Last year","Miniony rok"}. {"leaves the room","opuszcza pokój"}. -{"Listened Ports at ","Porty nasłuchujące na "}. -{"Listened Ports","Porty nasłuchujące"}. -{"List of modules to start","Lista modułów do uruchomienia"}. -{"List of rooms","Lista pokoi"}. -{"Low level update script","Skrypt aktualizacji niskiego poziomu"}. {"Make participants list public","Upublicznij listę uczestników"}. {"Make room CAPTCHA protected","Pokój zabezpieczony captchą"}. {"Make room members-only","Pokój tylko dla członków"}. @@ -188,46 +164,62 @@ {"Make room password protected","Pokój zabezpieczony hasłem"}. {"Make room persistent","Utwórz pokój na stałe"}. {"Make room public searchable","Pozwól wyszukiwać pokój"}. +{"Malformed username","Nieprawidłowa nazwa użytkownika"}. {"March","Marzec"}. -{"Maximum Number of Occupants","Maksymalna liczba uczestników"}. -{"Max # of items to persist","Maksymalna liczba przechowywanych przedmiotów"}. {"Max payload size in bytes","Maksymalna wielkość powiadomienia w bajtach"}. +{"Maximum Number of Occupants","Maksymalna liczba uczestników"}. {"May","Maj"}. -{"Members:","Członkowie:"}. {"Membership is required to enter this room","Musisz być na liście członków tego pokoju aby do niego wejść"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Zapamiętaj swoje hasło lub zapisz je na kartce i zachowaj w bezpiecznym miejscu. Na Jabberze nie ma zautomatyzowanego systemu odzyskiwania haseł."}. -{"Memory","Pamięć"}. {"Message body","Treść wiadomości"}. +{"Message not found in forwarded payload","Nie znaleziona wiadomości w przesyłanych dalej danych"}. {"Middle Name","Drugie imię"}. {"Minimum interval between voice requests (in seconds)","Minimalny odstęp między żądaniami głosowymi (w sekundach)"}. -{"Moderator","Moderatorzy"}. {"Moderator privileges required","Wymagane uprawnienia moderatora"}. -{"moderators only","tylko moderatorzy"}. -{"Modified modules","Zmodyfikowane moduły"}. -{"Module","Moduł"}. -{"Modules at ~p","Moduły na ~p"}. -{"Modules","Moduły"}. +{"Moderator","Moderatorzy"}. +{"Module failed to handle the query","Moduł nie był wstanie przetworzyć zapytania"}. {"Monday","Poniedziałek"}. {"Multicast","Multicast"}. {"Multi-User Chat","Wieloosobowa rozmowa"}. {"Name","Imię"}. -{"Name:","Nazwa:"}. +{"Neither 'jid' nor 'nick' attribute found","Brak zarówno atrybutu 'jid' jak i 'nick'"}. +{"Neither 'role' nor 'affiliation' attribute found","Brak zarówno atrybutu 'role' jak i 'affiliation'"}. {"Never","Nigdy"}. {"New Password:","Nowe hasło:"}. -{"Nickname","Nazwa użytkownika"}. {"Nickname Registration at ","Rejestracja nazwy użytkownika na "}. -{"Nickname ~s does not exist in the room","Nie ma nicka ~s w tym pokoju"}. -{"nobody","nikt"}. +{"Nickname","Nazwa użytkownika"}. +{"No 'affiliation' attribute found","Brak wartości dla 'access'"}. +{"No available resource found","Brak dostępnych zasobów"}. {"No body provided for announce message","Brak treści powiadomienia"}. +{"No data form found","Brak danych dla formatki"}. {"No Data","Brak danych"}. +{"No features available","Brak dostępnych funkcji"}. +{"No hook has processed this command","Żadna funkcja nie przetworzyła tej komendy"}. +{"No info about last activity found","Nie znaleziono informacji o ostatniej aktywności"}. +{"No 'item' element found","Brak wartości dla 'item'"}. +{"No items found in this query","Nie znaleziono żadnych pozycji w tym zapytaniu"}. +{"No limit","Bez limitu"}. +{"No module is handling this query","Żaden moduł nie obsługuje tego zapytania"}. +{"No node specified","Nie podano węzła"}. +{"No 'password' found in data form","Brak wartości dla 'password'"}. +{"No 'password' found in this query","Brak wartości dla 'password'"}. +{"No 'path' found in data form","Brak wartości dla 'path'"}. +{"No pending subscriptions found","Nie ma żadnych oczekujących subskrypcji"}. +{"No privacy list with this name found","Nie znaleziona żadnych list prywatności z tą nazwą"}. +{"No private data found in this query","Nie znaleziono danych prywatnych w tym zapytaniu"}. +{"No running node found","Brak uruchomionych węzłów"}. +{"No services available","Usługa nie jest dostępna"}. +{"No statistics found for this item","Nie znaleziono statystyk dla tego elementu"}. +{"No 'to' attribute found in the invitation","Brak wartości dla 'to' w zaproszeniu"}. +{"Node already exists","Węzeł już istnieje"}. {"Node ID","ID węzła"}. +{"Node index not found","Indeks węzła już istnieje"}. {"Node not found","Węzeł nie został znaleziony"}. {"Node ~p","Węzeł ~p"}. +{"Nodeprep has failed","Weryfikacja nazwy nie powiodła się"}. {"Nodes","Węzły"}. -{"No limit","Bez limitu"}. {"None","Brak"}. -{"No resource provided","Nie podano zasobu"}. {"Not Found","Nie znaleziono"}. +{"Not subscribed","Nie zasubskrybowano"}. {"Notify subscribers when items are removed from the node","Informuj subskrybentów o usunięciu elementów węzła"}. {"Notify subscribers when the node configuration changes","Informuj subskrybentów o zmianach konfiguracji węzła"}. {"Notify subscribers when the node is deleted","Informuj subskrybentów o usunięciu węzła"}. @@ -236,14 +228,13 @@ {"Number of online users","Liczba zalogowanych użytkowników"}. {"Number of registered users","Liczba zarejestrowanych użytkowników"}. {"October","Październik"}. -{"Offline Messages:","Wiadomości offline:"}. -{"Offline Messages","Wiadomości offline"}. {"OK","OK"}. {"Old Password:","Stare hasło:"}. -{"Online","Dostępny"}. -{"Online Users:","Użytkownicy zalogowani:"}. {"Online Users","Użytkownicy zalogowani"}. +{"Online","Dostępny"}. {"Only deliver notifications to available users","Dostarczaj powiadomienia tylko dostępnym użytkownikom"}. +{"Only or tags are allowed","Dozwolone są wyłącznie elementy lub "}. +{"Only element is allowed in this query","Wyłącznie elementy są dozwolone w tym zapytaniu"}. {"Only members may query archives of this room","Tylko moderatorzy mogą przeglądać archiwa tego pokoju"}. {"Only moderators and participants are allowed to change the subject in this room","Tylko moderatorzy i uczestnicy mogą zmienić temat tego pokoju"}. {"Only moderators are allowed to change the subject in this room","Tylko moderatorzy mogą zmienić temat tego pokoju"}. @@ -251,59 +242,41 @@ {"Only occupants are allowed to send messages to the conference","Tylko uczestnicy mogą wysyłać wiadomości na konferencję"}. {"Only occupants are allowed to send queries to the conference","Tylko uczestnicy mogą wysyłać zapytania do konferencji"}. {"Only service administrators are allowed to send service messages","Tylko administratorzy mogą wysyłać wiadomości"}. -{"Options","Opcje"}. {"Organization Name","Nazwa organizacji"}. {"Organization Unit","Dział"}. -{"Outgoing s2s Connections:","Wychodzące połączenia s2s:"}. {"Outgoing s2s Connections","Wychodzące połączenia s2s"}. {"Owner privileges required","Wymagane uprawnienia właściciela"}. -{"Packet","Pakiet"}. {"Participant","Uczestnicy"}. -{"Password ~b","Hasło ~b"}. -{"Password:","Hasło:"}. -{"Password","Hasło"}. -{"Password Verification:","Weryfikacja hasła:"}. {"Password Verification","Weryfikacja hasła"}. +{"Password Verification:","Weryfikacja hasła:"}. +{"Password","Hasło"}. +{"Password:","Hasło:"}. {"Path to Dir","Ścieżka do katalogu"}. {"Path to File","Scieżka do pliku"}. -{"Pending","Oczekuje"}. {"Period: ","Przedział czasu: "}. -{"Permanent rooms","Stałych pokoi"}. {"Persist items to storage","Przechowuj na stałe dane PubSub"}. +{"Ping query is incorrect","Żądanie 'ping' nie jest prawidłowe"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Te opcje kopii zapasowych dotyczą tylko wbudowanej bazy danych typu Mnesia. Jeśli korzystasz z modułu ODBC, musisz wykonać kopie bazy we własnym zakresie."}. -{"Please specify file name.","Proszę podać nazwę pliku."}. -{"Please specify file size.","Proszę podać rozmiar pliku."}. {"Please, wait for a while before sending new voice request","Proszę poczekać chwile, zanim wyślesz nowe żądanie głosowe"}. {"Pong","Pong"}. -{"Port ~b","Port ~b"}. -{"Port","Port"}. {"Present real Jabber IDs to","Prawdziwe Jabber ID widoczne dla"}. {"private, ","prywatny, "}. -{"Protocol","Protokół"}. {"Publish-Subscribe","PubSub"}. {"PubSub subscriber request","Żądanie subskrybcji PubSub"}. {"Purge all items when the relevant publisher goes offline","Usuń wszystkie elementy w momencie kiedy publikujący rozłączy się"}. {"Queries to the conference members are not allowed in this room","Informacje o członkach konferencji nie są dostępne w tym pokoju"}. +{"Query to another users is forbidden","Zapytanie do innych użytkowników nie są dozwolone"}. {"RAM and disc copy","Kopia na dysku i w pamięci RAM"}. {"RAM copy","Kopia w pamięci RAM"}. -{"Raw","Żródło"}. {"Really delete message of the day?","Na pewno usunąć wiadomość dnia?"}. {"Recipient is not in the conference room","Odbiorcy nie ma w pokoju"}. -{"Register a Jabber account","Załóż konto Jabber"}. -{"Registered nicknames","Zarejestrowanych nicków"}. -{"Registered Users:","Użytkownicy zarejestrowani:"}. -{"Registered Users","Użytkownicy zarejestrowani"}. {"Register","Zarejestruj"}. -{"Registration in mod_irc for ","Rejestracja w mod_irc dla "}. {"Remote copy","Kopia zdalna"}. -{"Remove All Offline Messages","Usuń wszystkie wiadomości typu 'Offline'"}. {"Remove User","Usuń użytkownika"}. -{"Remove","Usuń"}. {"Replaced by new connection","Połączenie zostało zastąpione"}. {"Resources","Zasoby"}. {"Restart Service","Restart usługi"}. -{"Restart","Uruchom ponownie"}. {"Restore Backup from File at ","Odtwórz bazę danych z kopii zapasowej na "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Odtwórz kopię binarną podczas następnego uruchomienia ejabberd (wymaga mniej zasobów):"}. {"Restore binary backup immediately:","Natychmiast odtwórz kopię binarną:"}. @@ -316,14 +289,9 @@ {"Room Occupants","Lista uczestników"}. {"Room title","Tytuł pokoju"}. {"Roster groups allowed to subscribe","Grupy kontaktów uprawnione do subskrypcji"}. -{"Roster","Lista kontaktów"}. -{"Roster of ","Lista kontaktów "}. {"Roster size","Rozmiar listy kontaktów"}. -{"RPC Call Error","Błąd żądania RPC"}. {"Running Nodes","Uruchomione węzły"}. -{"~s access rule configuration","~s konfiguracja zasad dostępu"}. {"Saturday","Sobota"}. -{"Script check","Sprawdź skrypt"}. {"Search Results for ","Wyniki wyszukiwania dla "}. {"Search users in ","Wyszukaj użytkowników w "}. {"Send announcement to all online users on all hosts","Wyślij powiadomienie do wszystkich zalogowanych użytkowników na wszystkich hostach"}. @@ -331,90 +299,76 @@ {"Send announcement to all users on all hosts","Wyślij powiadomienie do wszystkich użytkowników na wszystkich hostach"}. {"Send announcement to all users","Wyślij powiadomienie do wszystkich użytkowników"}. {"September","Wrzesień"}. -{"Server ~b","Serwer ~b"}. {"Server:","Serwer:"}. -{"Server","Serwer"}. {"Set message of the day and send to online users","Wyślij wiadomość dnia do wszystkich zalogowanych użytkowników"}. {"Set message of the day on all hosts and send to online users","Ustaw wiadomość dnia dla wszystkich hostów i wyślij do zalogowanych uzytkowników"}. {"Shared Roster Groups","Wspólne grupy kontaktów"}. {"Show Integral Table","Pokaż tabelę całkowitą"}. {"Show Ordinary Table","Pokaż zwykłą tabelę"}. {"Shut Down Service","Wyłącz usługę"}. -{"~s invites you to the room ~s","~s zaprasza Cię do pokoju ~s"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Niektóre klienty Jabber mogą zapisywać Twoje hasło na komputerze. Używaj tej opcji tylko jeśli ufasz komputerowi na którym pracujesz."}. {"Specify the access model","Określ model dostępu"}. {"Specify the event message type","Określ typ wiadomości"}. {"Specify the publisher model","Określ model publikującego"}. -{"~s's Offline Messages Queue","Kolejka wiadomości offline użytkownika ~s"}. -{"Start Modules at ","Uruchom moduły na "}. -{"Start Modules","Uruchom moduły"}. -{"Start","Uruchom"}. -{"Statistics of ~p","Statystyki ~p"}. -{"Statistics","Statystyki"}. -{"Stop Modules at ","Zatrzymaj moduły na "}. -{"Stop Modules","Zatrzymaj moduły"}. {"Stopped Nodes","Zatrzymane węzły"}. -{"Stop","Zatrzymaj"}. -{"Storage Type","Typ bazy"}. {"Store binary backup:","Zachowaj kopię binarną:"}. {"Store plain text backup:","Zachowaj kopię w postaci tekstowej:"}. {"Subject","Temat"}. {"Submitted","Wprowadzone"}. -{"Submit","Wyślij"}. {"Subscriber Address","Adres subskrybenta"}. -{"Subscription","Subskrypcja"}. +{"Subscriptions are not allowed","Subskrypcje nie są dozwolone"}. {"Sunday","Niedziela"}. {"That nickname is already in use by another occupant","Ta nazwa użytkownika jest używana przez kogoś innego"}. {"That nickname is registered by another person","Ta nazwa użytkownika jest już zarejestrowana przez inną osobę"}. {"The CAPTCHA is valid.","Captcha jest poprawna."}. -{"The CAPTCHA verification has failed","Weryfikacja CAPTCHA nie powiodła się."}. +{"The CAPTCHA verification has failed","Weryfikacja CAPTCHA nie powiodła się"}. {"The collections with which a node is affiliated","Grupy, do których należy węzeł"}. -{"the password is","hasło to:"}. +{"The feature requested is not supported by the conference","Żądana czynność nie jest obsługiwana przez konferencje"}. +{"The password contains unacceptable characters","Hasło zawiera niedopuszczalne znaki"}. {"The password is too weak","Hasło nie jest wystarczająco trudne"}. -{"The password of your Jabber account was successfully changed.","Hasło do Twojego konta zostało zmienione."}. -{"There was an error changing the password: ","Podczas próby zmiany hasła wystąpił błąd:"}. -{"There was an error creating the account: ","Wystąpił błąd podczas tworzenia konta:"}. -{"There was an error deleting the account: ","Podczas usuwania konta wystąpił błąd:"}. -{"This IP address is blacklisted in ~s","Ten adres IP został zablokowany w ~s"}. -{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Pole nie rozróżnia wielkości liter: słowo Hanna jest takie samo jak hAnna lub haNNa."}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Niniejsza strona pozwala na założenie konta Jabber na tym serwerze. Twój JID (Jabber IDentyfikator) będzie miał postać: nazwa_użytkownika@serwer. Przeczytaj dokładnie instrukcję i wypełnij pola."}. -{"This page allows to unregister a Jabber account in this Jabber server.","Ta strona pozwala usunąć konto Jabber z tego serwera."}. +{"the password is","hasło to:"}. +{"The query is only allowed from local users","To żądanie jest dopuszczalne wyłącznie dla lokalnych użytkowników"}. +{"The query must not contain elements","Żądanie nie może zawierać elementów "}. +{"The stanza MUST contain only one element, one element, or one element","Żądanie może zawierać wyłącznie jeden z elementów , lub "}. +{"There was an error creating the account: ","Wystąpił błąd podczas tworzenia konta: "}. +{"There was an error deleting the account: ","Podczas usuwania konta wystąpił błąd: "}. +{"This room is not anonymous","Ten pokój nie jest anonimowy"}. {"Thursday","Czwartek"}. -{"Time","Czas"}. {"Time delay","Opóźnienie"}. -{"To","Do"}. +{"To register, visit ~s","Żeby się zarejestrować odwiedź ~s"}. +{"Token TTL","Limit czasu tokenu"}. +{"Too many active bytestreams","Zbyt wiele strumieni danych"}. {"Too many CAPTCHA requests","Za dużo żądań CAPTCHA"}. +{"Too many elements","Zbyt wiele elementów "}. +{"Too many elements","Zbyt wiele elementów "}. {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Zbyt wiele (~p) nieudanych prób logowanie z tego adresu IP (~s). Ten adres zostanie odblokowany o ~s UTC"}. {"Too many unacked stanzas","Zbyt wiele niepotwierdzonych pakietów"}. -{"To ~s","Do ~s"}. -{"Total rooms","Wszystkich pokoi"}. +{"Too many users in this conference","Zbyt wielu użytkowników konferencji"}. {"Traffic rate limit is exceeded","Limit transferu przekroczony"}. -{"Transactions Aborted:","Transakcje anulowane:"}. -{"Transactions Committed:","Transakcje zakończone:"}. -{"Transactions Logged:","Transakcje zalogowane:"}. -{"Transactions Restarted:","Transakcje uruchomione ponownie:"}. {"Tuesday","Wtorek"}. {"Unable to generate a CAPTCHA","Nie można wygenerować CAPTCHA"}. +{"Unable to register route on existing local domain","Nie można zarejestrować trasy dla lokalnej domeny"}. {"Unauthorized","Nie autoryzowano"}. -{"Unregister a Jabber account","Usuń konto Jabber"}. +{"Unexpected action","Nieoczekiwana akcja"}. {"Unregister","Wyrejestruj"}. -{"Update","Aktualizuj"}. +{"Unsupported element","Nieobsługiwany element "}. {"Update message of the day (don't send)","Aktualizuj wiadomość dnia (bez wysyłania)"}. {"Update message of the day on all hosts (don't send)","Aktualizuj wiadomość dnia na wszystkich hostach (bez wysyłania)"}. -{"Update plan","Plan aktualizacji"}. -{"Update ~p","Uaktualnij ~p"}. -{"Update script","Skrypt aktualizacji"}. -{"Uptime:","Czas pracy:"}. -{"Use of STARTTLS required","Wymagane jest użycie STARTTLS"}. +{"User already exists","Użytkownik już istnieje"}. {"User JID","Użytkownik "}. +{"User (jid)","Użytkownik (jid)"}. {"User Management","Zarządzanie użytkownikami"}. +{"User session not found","Sesja użytkownika nie została znaleziona"}. +{"User session terminated","Sesja użytkownika została zakończona"}. {"Username:","Nazwa użytkownika:"}. {"Users are not allowed to register accounts so quickly","Użytkowncy nie mogą tak szybko rejestrować nowych kont"}. {"Users Last Activity","Ostatnia aktywność użytkowników"}. {"Users","Użytkownicy"}. -{"User ~s","Użytkownik ~s"}. {"User","Użytkownik"}. -{"Validate","Potwierdź"}. +{"Value 'get' of 'type' attribute is not allowed","Wartość 'get' dla atrybutu 'type' jest niedozwolona"}. +{"Value of '~s' should be boolean","Wartość '~s' powinna być typu logicznego"}. +{"Value of '~s' should be datetime string","Wartość '~s' powinna być typu daty"}. +{"Value of '~s' should be integer","Wartość '~s' powinna być liczbą"}. +{"Value 'set' of 'type' attribute is not allowed","Wartość 'set' dla atrybutu 'type' jest niedozwolona"}. {"vCard User Search","Wyszukiwanie vCard użytkowników"}. {"Virtual Hosts","Wirtualne Hosty"}. {"Visitor","Odwiedzający"}. @@ -425,16 +379,13 @@ {"Wednesday","Środa"}. {"When to send the last published item","Kiedy wysłać ostatnio opublikowaną rzecz"}. {"Whether to allow subscriptions","Czy pozwolić na subskrypcje"}. -{"You can later change your password using a Jabber client.","Możesz później zmienić swoje hasło za pomocą dowolnego klienta Jabber."}. {"You have been banned from this room","Zostałeś wykluczony z tego pokoju"}. +{"You have joined too many conferences","Dołączyłeś do zbyt wielu konferencji"}. {"You must fill in field \"Nickname\" in the form","Musisz wypełnić pole \"Nazwa użytkownika\" w formularzu"}. {"You need a client that supports x:data and CAPTCHA to register","Potrzebujesz klienta obsługującego x:data aby zarejestrować nick"}. {"You need a client that supports x:data to register the nickname","Potrzebujesz klienta obsługującego x:data aby zarejestrować nick"}. -{"You need an x:data capable client to configure mod_irc settings","Potrzebujesz klienta obsługującego x:data aby skonfigurować mod_irc"}. -{"You need an x:data capable client to configure room","Potrzebujesz klienta obsługującego x:data aby skonfigurować pokój"}. {"You need an x:data capable client to search","Potrzebujesz klienta obsługującego x:data aby wyszukiwać"}. -{"Your active privacy list has denied the routing of this stanza.","Aktualna lista prywatności zabrania przesyłania tej stanzy"}. +{"Your active privacy list has denied the routing of this stanza.","Aktualna lista prywatności zabrania przesyłania tej stanzy."}. {"Your contact offline message queue is full. The message has been discarded.","Kolejka wiadomości offline adresata jest pełna. Wiadomość została odrzucona."}. -{"Your Jabber account was successfully created.","Twoje konto zostało stworzone."}. -{"Your Jabber account was successfully deleted.","Twoje konto zostało usunięte."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","Twoje wiadomości do ~s są blokowane. Aby je odblokować, odwiedź ~s"}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Twoje wiadomości do ~s są blokowane. Aby je odblokować, odwiedź ~s"}. +{"You're not allowed to create nodes","Nie masz uprawnień do tworzenia węzłów"}. diff --git a/priv/msgs/pl.po b/priv/msgs/pl.po deleted file mode 100644 index 08bf0a03b..000000000 --- a/priv/msgs/pl.po +++ /dev/null @@ -1,1926 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: 2.1.0-alpha\n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: Paweł Chmielowski \n" -"Language-Team: \n" -"Language: pl\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Polish (polski)\n" -"X-Additional-Translator: Janusz B. Wiśniewski\n" -"X-Additional-Translator: Marcin Owsiany\n" -"X-Additional-Translator: Andrzej Smyk\n" -"X-Additional-Translator: Mateusz Gajewski\n" -"X-Generator: Poedit 1.8.6\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "Wymagane jest użycie STARTTLS" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Nie podano zasobu" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Połączenie zostało zastąpione" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "został wyrzucony" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "Aktualna lista prywatności zabrania przesyłania tej stanzy" - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "Zbyt wiele niepotwierdzonych pakietów" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Przepisz tekst z obrazka" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "Twoje wiadomości do ~s są blokowane. Aby je odblokować, odwiedź ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "Jeśli nie widzisz obrazka CAPTCHA, odwiedź stronę internetową." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "Strona internetowa CAPTCHA" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "Captcha jest poprawna." - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Użytkownik" - -#: ejabberd_oauth.erl:256 -msgid "Server" -msgstr "Serwer" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Hasło" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "Zaakceptuj" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "Nie autoryzowano" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "ejabberd: Panel Administracyjny" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Administracja" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Lista dostępowa" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Wprowadzone" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Błędny format" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Wyślij" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "Żródło" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Usuń zaznaczone" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Zasady dostępu" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "~s konfiguracja zasad dostępu" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "Wirtualne Hosty" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Użytkownicy" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 mod_configure.erl:524 -msgid "Online Users" -msgstr "Użytkownicy zalogowani" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Ostatnia aktywność użytkowników" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Przedział czasu: " - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "Miniony miesiąc" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "Miniony rok" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "Cała aktywność" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Pokaż zwykłą tabelę" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Pokaż tabelę całkowitą" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Statystyki" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "Nie znaleziono" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Węzeł nie został znaleziony" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Dodaj nowe" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Host" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Użytkownicy zarejestrowani" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Dodaj użytkownika" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Wiadomości offline" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Ostatnia aktywność" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Nigdy" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Dostępny" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Użytkownicy zarejestrowani:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Użytkownicy zalogowani:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Wychodzące połączenia s2s:" - -#: ejabberd_web_admin.erl:1559 -msgid "Incoming s2s Connections:" -msgstr "Przychodzące połączenia s2s:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Brak" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Zmień hasło" - -#: ejabberd_web_admin.erl:1673 -msgid "User ~s" -msgstr "Użytkownik ~s" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Zasoby zalogowane:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Hasło:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Usuń użytkownika" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Brak danych" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Węzły" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Uruchomione węzły" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Zatrzymane węzły" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -msgid "Node ~p" -msgstr "Węzeł ~p" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "Baza danych" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Wykonaj kopie" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Porty nasłuchujące" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Aktualizuj" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Uruchom ponownie" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Zatrzymaj" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Moduły" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "Błąd żądania RPC" - -#: ejabberd_web_admin.erl:1917 -msgid "Database Tables at ~p" -msgstr "Tabele bazy na ~p" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Imię" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Typ bazy" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Elementy" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Pamięć" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Błąd" - -#: ejabberd_web_admin.erl:1955 -msgid "Backup of ~p" -msgstr "Kopia zapasowa ~p" - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Te opcje kopii zapasowych dotyczą tylko wbudowanej bazy danych typu Mnesia. " -"Jeśli korzystasz z modułu ODBC, musisz wykonać kopie bazy we własnym " -"zakresie." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Zachowaj kopię binarną:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "OK" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Natychmiast odtwórz kopię binarną:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"Odtwórz kopię binarną podczas następnego uruchomienia ejabberd (wymaga mniej " -"zasobów):" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Zachowaj kopię w postaci tekstowej:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Natychmiast odtwórz kopię z postaci tekstowej:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "Importuj dane użytkowników z pliku w formacie PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" -"Eksportuj dane wszystkich użytkowników serwera do plików w formacie PIEFXIS " -"(XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "" -"Eksportuj dane użytkowników z hosta do plików w formacie PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "Wyeksportuj wszystkie tabele jako zapytania SQL do pliku:" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Importuj dane użytkownika z pliku roboczego serwera jabberd14:" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Importuj użytkowników z katalogu roboczego serwera jabberd14" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Porty nasłuchujące na " - -#: ejabberd_web_admin.erl:2144 -msgid "Modules at ~p" -msgstr "Moduły na ~p" - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "Statystyki ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Czas pracy:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "Czas CPU:" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Transakcje zakończone:" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Transakcje anulowane:" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Transakcje uruchomione ponownie:" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Transakcje zalogowane:" - -#: ejabberd_web_admin.erl:2243 -msgid "Update ~p" -msgstr "Uaktualnij ~p" - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "Plan aktualizacji" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Zmodyfikowane moduły" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Skrypt aktualizacji" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Skrypt aktualizacji niskiego poziomu" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Sprawdź skrypt" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Port" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Protokół" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Moduł" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Opcje" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Uruchom" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Polecenia" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "Na pewno usunąć wiadomość dnia?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Temat" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Treść wiadomości" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "Brak treści powiadomienia" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Powiadomienia" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Wyślij powiadomienie do wszystkich użytkowników" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Wyślij powiadomienie do wszystkich użytkowników na wszystkich hostach" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Wyślij powiadomienie do wszystkich zalogowanych użytkowników" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "" -"Wyślij powiadomienie do wszystkich zalogowanych użytkowników na wszystkich " -"hostach" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Wyślij wiadomość dnia do wszystkich zalogowanych użytkowników" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" -"Ustaw wiadomość dnia dla wszystkich hostów i wyślij do zalogowanych " -"uzytkowników" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Aktualizuj wiadomość dnia (bez wysyłania)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "Aktualizuj wiadomość dnia na wszystkich hostach (bez wysyłania)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Usuń wiadomość dnia" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Usuń wiadomość dnia ze wszystkich hostów" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Konfiguracja" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Uruchom moduły" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Zatrzymaj moduły" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Przywróć z kopii" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Wykonaj kopie do pliku tekstowego" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Importuj plik" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Importuj katalog" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Restart usługi" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Wyłącz usługę" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Usuń użytkownika" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Zakończ sesję uzytkownika" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Pobierz hasło użytkownika" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Zmień hasło użytkownika" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "Pokaż czas ostatniego zalogowania uzytkownika" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Pobierz statystyki użytkownika" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Pokaż liczbę zarejestrowanych użytkowników" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Pokaż liczbę zalogowanych użytkowników" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Zarządzanie użytkownikami" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Wszyscy użytkownicy" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Wychodzące połączenia s2s" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Zarządzanie kopiami zapasowymi" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Importuj użytkowników z plików roboczych serwera jabberd14" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "Do ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "Od ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Konfiguracja tabel bazy na " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Wybierz typ bazy dla tablel" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Kopia tylko na dysku" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "Kopia na dysku i w pamięci RAM" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "Kopia w pamięci RAM" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "Kopia zdalna" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Zatrzymaj moduły na " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Wybierz moduły do zatrzymania" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Uruchom moduły na " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Wprowadź listę {Moduł, [Opcje]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Lista modułów do uruchomienia" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Zapisz kopię w pliku na " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Wprowadź scieżkę do pliku kopii zapasowej" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Scieżka do pliku" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Odtwórz bazę danych z kopii zapasowej na " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Zapisz kopię zapasową w pliku tekstowym na " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Wprowadź scieżkę do pliku tekstowego" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Importuj użytkownika z pliku na " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Wprowadź ścieżkę do roboczego pliku serwera jabberd14" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Importuj użytkowników z katalogu na " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Wprowadź ścieżkę do roboczego katalogu serwera jabberd14" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Ścieżka do katalogu" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "Opóźnienie" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Konfiguracja listy dostępowej" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Listy dostępowe" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Konfiguracja dostępu" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Reguły dostępu" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Weryfikacja hasła" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Liczba zarejestrowanych użytkowników" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Liczba zalogowanych użytkowników" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Ostatnie logowanie" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "Rozmiar listy kontaktów" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "Adresy IP" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Zasoby" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Zarządzanie " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Wykonaj na użytkowniku" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Edytuj właściwości" - -#: mod_fail2ban.erl:95 -msgid "" -"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" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "Proszę podać rozmiar pliku." - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "Proszę podać nazwę pliku." - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "Ten adres IP został zablokowany w ~s" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "Dostęp zabroniony zgodnie z zasadami usługi" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "Transport IRC" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "Moduł IRC ejabberd" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "Potrzebujesz klienta obsługującego x:data aby skonfigurować mod_irc" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Rejestracja w mod_irc dla " - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Wprowadź nazwę użytkownika, port i kodowanie, których chcesz używać do " -"łączenia z serwerami IRC" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "Nazwa użytkownika IRC" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Jeśli chcesz ustawić inne hasła, porty lub kodowania dla poszczególnych " -"serwerów IRC, wypełnij tą listę wartościami w formacie '{\"irc server\"," -"\"encoding\", port, \"password\"}'. Domyślne ta usługa używa kodowania \"~s" -"\", portu ~p, bez hasła." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Przykład: [{\"wroclaw.irc.pl\",\"utf-8\"}, {\"warszawa.irc.pl\", " -"\"iso8859-2\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Parametry połączeń" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "Dołącz do kanału IRC" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "Kanał IRC (nie używaj #)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "Serwer IRC" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Dołącz do kanału IRC." - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Dołącz do kanału IRC pod tym Jabber ID: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "Ustawienia IRC" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Wprowadź nazwę użytkownika i kodowania których chcesz używać do łączenia z " -"serwerami IRC. Wciśnij \"Dalej\" aby ustawić więcej parametrów połączenia. " -"Wciśnij \"Zakończ\" aby zapisać ustawienia." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "Nazwa użytkownika IRC" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Hasło ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Port ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Kodowanie znaków dla serwera ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Serwer ~b" - -#: mod_mam.erl:541 -msgid "Only members may query archives of this room" -msgstr "Tylko moderatorzy mogą przeglądać archiwa tego pokoju" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "Tylko administratorzy mogą wysyłać wiadomości" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "Zasady serwera zabraniają tworzyć nowe pokoje" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "Pokój konferencyjny nie istnieje" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Pokoje rozmów" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "Puste pokoje" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "Potrzebujesz klienta obsługującego x:data aby zarejestrować nick" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Rejestracja nazwy użytkownika na " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Wprowadz nazwę użytkownika którego chcesz zarejestrować" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Nazwa użytkownika" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "Ta nazwa użytkownika jest już zarejestrowana przez inną osobę" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Musisz wypełnić pole \"Nazwa użytkownika\" w formularzu" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "Moduł MUC" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "Wieloosobowa rozmowa" - -#: mod_muc_admin.erl:249 -msgid "Total rooms" -msgstr "Wszystkich pokoi" - -#: mod_muc_admin.erl:250 -msgid "Permanent rooms" -msgstr "Stałych pokoi" - -#: mod_muc_admin.erl:251 -msgid "Registered nicknames" -msgstr "Zarejestrowanych nicków" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "Lista pokoi" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "Konfiguracja pokoju zmodyfikowana" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "dołącza do pokoju" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "opuszcza pokój" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "został wykluczony" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "został wyrzucony z powodu zmiany przynależności" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "został wyrzucony z powodu zmiany pokoju na \"Tylko dla Członków\"" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "został wyrzucony z powodu wyłączenia systemu" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "jest teraz znany jako" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " zmienił temat na: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "Pokój został stworzony" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "Pokój został usunięty" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "Pokój został uruchomiony" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "Pokój został zatrzymany" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "Poniedziałek" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "Wtorek" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "Środa" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "Czwartek" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "Piątek" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "Sobota" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "Niedziela" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "Styczeń" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "Luty" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "Marzec" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "Kwiecień" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "Maj" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "Czerwiec" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "Lipiec" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "Sierpień" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "Wrzesień" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "Październik" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "Listopad" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "Grudzień" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Konfiguracja pokoju" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "Lista uczestników" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "Limit transferu przekroczony" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "Nie wolno wysyłac prywatnych wiadomości na konferencję" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "Proszę poczekać chwile, zanim wyślesz nowe żądanie głosowe" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "Głosowe żądania są wyłączone w tym pokoju" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "Nie udało się wydobyć JID-u z twojego żądania" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "Tylko moderatorzy mogą zatwierdzać żądania głosowe" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Nieprawidłowy typ wiadomości" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "Nie można wysyłać prywatnych wiadomości typu \"groupchat\"" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "Odbiorcy nie ma w pokoju" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "Wysyłanie prywatnych wiadomości jest zabronione" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Tylko uczestnicy mogą wysyłać wiadomości na konferencję" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "Tylko uczestnicy mogą wysyłać zapytania do konferencji" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "Informacje o członkach konferencji nie są dostępne w tym pokoju" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "Tylko moderatorzy i uczestnicy mogą zmienić temat tego pokoju" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Tylko moderatorzy mogą zmienić temat tego pokoju" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "Odwiedzający nie mogą wysyłać wiadomości do wszystkich obecnych" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "Uczestnicy tego pokoju nie mogą zmieniać swoich nicków" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "Ta nazwa użytkownika jest używana przez kogoś innego" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Zostałeś wykluczony z tego pokoju" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "Musisz być na liście członków tego pokoju aby do niego wejść" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "Aby wejść do pokoju wymagane jest hasło" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "Za dużo żądań CAPTCHA" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "Nie można wygenerować CAPTCHA" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Nieprawidłowe hasło" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "Wymagane uprawnienia administratora" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "Wymagane uprawnienia moderatora" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "Jabber ID ~s jest niepoprawny" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "Nie ma nicka ~s w tym pokoju" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Nieprawidłowa przynależność: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Nieprawidłowa rola: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "Wymagane uprawnienia właściciela" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "Konfiguracja pokoju ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Tytuł pokoju" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "Opis pokoju" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Utwórz pokój na stałe" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Pozwól wyszukiwać pokój" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "Upublicznij listę uczestników" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Pokój zabezpieczony hasłem" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Maksymalna liczba uczestników" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Bez limitu" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Prawdziwe Jabber ID widoczne dla" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "tylko moderatorzy" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "wszystkich" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "Role dla których wysyłane są statusy" - -#: mod_muc_room.erl:3486 -msgid "Moderator" -msgstr "Moderatorzy" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "Uczestnicy" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "Odwiedzający" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Pokój tylko dla członków" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Pokój moderowany" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Domyślni użytkownicy jako uczestnicy" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Pozwól użytkownikom zmieniać temat" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Pozwól użytkownikom wysyłać prywatne wiadomości" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "Pozwól użytkownikom wysyłać prywatne wiadomości" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "nikt" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Pozwól użytkownikom pobierać informacje o innych użytkownikach" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Pozwól użytkownikom wysyłać zaproszenia" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "Pozwól uczestnikom na wysyłanie statusów opisowych" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Pozwól uczestnikom na zmianę nicka" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "Pozwól użytkownikom wysyłać zaproszenia" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "Minimalny odstęp między żądaniami głosowymi (w sekundach)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "Pokój zabezpieczony captchą" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "Włącz archiwizowanie rozmów" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "Pomiń Jabber ID z żądania CAPTCHA" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Włącz logowanie" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "Potrzebujesz klienta obsługującego x:data aby skonfigurować pokój" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Liczba uczestników" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "prywatny, " - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "Żądanie głosowe" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "Zatwierdź lub odrzuć żądanie głosowe" - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "Użytkownik " - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "Udzielić głosu tej osobie?" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s zaprasza Cię do pokoju ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "hasło to:" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "Multicast" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "Serwis multicast ejabbera" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "" -"Kolejka wiadomości offline adresata jest pełna. Wiadomość została odrzucona." - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "Kolejka wiadomości offline użytkownika ~s" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Czas" - -#: mod_offline.erl:812 -msgid "From" -msgstr "Od" - -#: mod_offline.erl:813 -msgid "To" -msgstr "Do" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Pakiet" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Wiadomości offline:" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "Usuń wszystkie wiadomości typu 'Offline'" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "Moduł SOCKS5 Bytestreams" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "PubSub" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "Moduł Publish-Subscribe" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "Żądanie subskrybcji PubSub" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Wybierz, czy akceptować subskrypcję tej jednostki" - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "ID węzła" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Adres subskrybenta" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "Pozwól temu Jabber ID na zapisanie się do tego węzła PubSub" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Dostarczaj zawartość publikacji wraz z powiadomieniami o zdarzeniach" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Dostarczaj powiadomienia o zdarzeniach" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Informuj subskrybentów o zmianach konfiguracji węzła" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Informuj subskrybentów o usunięciu węzła" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Informuj subskrybentów o usunięciu elementów węzła" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Przechowuj na stałe dane PubSub" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "Przyjazna nazwa węzła" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Maksymalna liczba przechowywanych przedmiotów" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Czy pozwolić na subskrypcje" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Określ model dostępu" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Grupy kontaktów uprawnione do subskrypcji" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Określ model publikującego" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "Usuń wszystkie elementy w momencie kiedy publikujący rozłączy się" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "Określ typ wiadomości" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Maksymalna wielkość powiadomienia w bajtach" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "Kiedy wysłać ostatnio opublikowaną rzecz" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Dostarczaj powiadomienia tylko dostępnym użytkownikom" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "Grupy, do których należy węzeł" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "Weryfikacja CAPTCHA nie powiodła się." - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "Potrzebujesz klienta obsługującego x:data aby zarejestrować nick" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "" -"Wybierz nazwę użytkownika i hasło aby zarejestrować się na tym serwerze" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "Hasło nie jest wystarczająco trudne" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "Użytkowncy nie mogą tak szybko rejestrować nowych kont" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "Twoje konto zostało stworzone." - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "Wystąpił błąd podczas tworzenia konta:" - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "Twoje konto zostało usunięte." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "Podczas usuwania konta wystąpił błąd:" - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "Hasło do Twojego konta zostało zmienione." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "Podczas próby zmiany hasła wystąpił błąd:" - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Zakładanie konta Jabber" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Załóż konto Jabber" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Usuń konto Jabber" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"Niniejsza strona pozwala na założenie konta Jabber na tym serwerze. Twój JID " -"(Jabber IDentyfikator) będzie miał postać: nazwa_użytkownika@serwer. " -"Przeczytaj dokładnie instrukcję i wypełnij pola." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "Nazwa użytkownika:" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" -"Pole nie rozróżnia wielkości liter: słowo Hanna jest takie samo jak hAnna " -"lub haNNa." - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "Te znaki są niedozwolone:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "Serwer:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" -"Nie podawaj swojego hasła nikomu, nawet administratorowi serwera Jabber." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "Możesz później zmienić swoje hasło za pomocą dowolnego klienta Jabber." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Niektóre klienty Jabber mogą zapisywać Twoje hasło na komputerze. Używaj tej " -"opcji tylko jeśli ufasz komputerowi na którym pracujesz." - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"Zapamiętaj swoje hasło lub zapisz je na kartce i zachowaj w bezpiecznym " -"miejscu. Na Jabberze nie ma zautomatyzowanego systemu odzyskiwania haseł." - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "Weryfikacja hasła:" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "Zarejestruj" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "Stare hasło:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "Nowe hasło:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "Ta strona pozwala usunąć konto Jabber z tego serwera." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "Wyrejestruj" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Subskrypcja" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Oczekuje" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Grupy" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Potwierdź" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Usuń" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Lista kontaktów " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Dodaj Jabber ID" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Lista kontaktów" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Wspólne grupy kontaktów" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Nazwa:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Opis:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Członkowie:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Wyświetlane grupy:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Grupa " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Erlang Jabber Server" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Data urodzenia" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Miasto" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "Państwo" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "Email" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Nazwisko" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Wypełnij formularz aby wyszukać użytkowników Jabbera (dodaj * na koniec " -"zapytania aby wyszukać po fragmencie)" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Pełna nazwa" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Drugie imię" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Nazwa organizacji" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Dział" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Wyszukaj użytkowników w " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "Potrzebujesz klienta obsługującego x:data aby wyszukiwać" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "Wyszukiwanie vCard użytkowników" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "Moduł vCard ejabberd" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Wyniki wyszukiwania dla " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "Wypełnij pola aby znaleźć pasujących użytkowników Jabbera" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Serwery zewnętrzne s2s:" - -#~ msgid "Delete" -#~ msgstr "Usuń" - -#~ msgid "This room is not anonymous" -#~ msgstr "Ten pokój nie jest anonimowy" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "" -#~ "Ten uczestnik został wyrzucony z pokoju ponieważ wysłał komunikat błędu" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Ten uczestnik został wyrzucony z pokoju ponieważ wysłał komunikat błędu " -#~ "do innego uczestnika" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "" -#~ "Ten uczestnik został wyrzucony z pokoju ponieważ jego informacja o " -#~ "statusie zawierała błędy" - -#, fuzzy -#~ msgid "Captcha test failed" -#~ msgstr "Captcha jest poprawna." diff --git a/priv/msgs/pt-br.msg b/priv/msgs/pt-br.msg index 3e7b28ee9..9fbbb0096 100644 --- a/priv/msgs/pt-br.msg +++ b/priv/msgs/pt-br.msg @@ -1,22 +1,30 @@ -%% -*- coding: latin-1 -*- -{"Accept","Aceito"}. -{"Access Configuration","Configuração de Acesso"}. -{"Access Control List Configuration","Configuração da Lista de Controle de Acesso"}. -{"Access control lists","Listas de Controle de Acesso"}. -{"Access Control Lists","Listas de Controle de Acesso"}. -{"Access denied by service policy","Acesso negado pela política do serviço"}. -{"Access rules","Regras de acesso"}. -{"Access Rules","Regras de Acesso"}. -{"Action on user","Ação no usuário"}. -{"Add Jabber ID","Adicionar ID jabber"}. -{"Add New","Adicionar novo"}. -{"Add User","Adicionar usuário"}. -{"Administration","Administração"}. -{"Administration of ","Administração de "}. -{"Administrator privileges required","Se necessita privilégios de administrador"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" (Add * to the end of field to match substring)"," (Adicione * no final do campo para combinar com a substring)"}. +{" has set the subject to: "," mudou o assunto para: "}. +{"# participants","# participantes"}. +{"A description of the node","Uma descrição do nó"}. {"A friendly name for the node","Um nome familiar para o nó"}. +{"A password is required to enter this room","Se necessita senha para entrar nesta sala"}. +{"A Web Page","Uma página da web"}. +{"Accept","Aceito"}. +{"Access denied by service policy","Acesso negado pela política do serviço"}. +{"Access model","Modelo de acesso"}. +{"Account doesn't exist","A conta não existe"}. +{"Action on user","Ação no usuário"}. +{"Add a hat to a user","Adiciona um chapéu num usuário"}. +{"Add User","Adicionar usuário"}. +{"Administration of ","Administração de "}. +{"Administration","Administração"}. +{"Administrator privileges required","Se necessita privilégios de administrador"}. {"All activity","Todas atividades"}. +{"All Users","Todos os usuários"}. +{"Allow subscription","Permitir a assinatura"}. {"Allow this Jabber ID to subscribe to this pubsub node?","Autorizar este Jabber ID para a inscrição neste tópico pubsub?"}. +{"Allow this person to register with the room?","Permita que esta pessoa se registe na sala?"}. {"Allow users to change the subject","Permitir a usuários modificar o assunto"}. {"Allow users to query other users","Permitir a usuários pesquisar informações sobre os demais"}. {"Allow users to send invites","Permitir a usuários envio de convites"}. @@ -25,21 +33,49 @@ {"Allow visitors to send private messages to","Permitir visitantes enviar mensagem privada para"}. {"Allow visitors to send status text in presence updates","Permitir atualizações de status aos visitantes"}. {"Allow visitors to send voice requests","Permitir aos visitantes o envio de requisições de voz"}. -{"All Users","Todos os usuários"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","Um grupo LDAP associado que define a adesão à sala; este deve ser um Nome Distinto LDAP de acordo com uma definição específica da implementação ou da implantação específica de um grupo."}. {"Announcements","Anúncios"}. -{"anyone","qualquer um"}. -{"A password is required to enter this room","Se necessita senha para entrar nesta sala"}. +{"Answer associated with a picture","Resposta associada com uma foto"}. +{"Answer associated with a video","Resposta associada com um vídeo"}. +{"Answer associated with speech","Resposta associada com a fala"}. +{"Answer to a question","Resposta para uma pergunta"}. +{"Anyone in the specified roster group(s) may subscribe and retrieve items","Qualquer pessoa do(s) grupo(s) informado(s) podem se inscrever e recuperar itens"}. +{"Anyone may associate leaf nodes with the collection","Qualquer pessoa pode associar nós das páginas à coleção"}. +{"Anyone may publish","Qualquer pessoa pode publicar"}. +{"Anyone may subscribe and retrieve items","Qualquer pessoa pode se inscrever e recuperar os itens"}. +{"Anyone with a presence subscription of both or from may subscribe and retrieve items","Qualquer pessoa com uma assinatura presente dos dois ou de ambos pode se inscrever e recuperar os itens"}. +{"Anyone with Voice","Qualquer pessoa com voz"}. +{"Anyone","Qualquer pessoa"}. +{"API Commands","Comandos API"}. {"April","Abril"}. +{"Arguments","Argumentos"}. +{"Attribute 'channel' is required for this request","O atributo 'canal' é necessário para esta solicitação"}. +{"Attribute 'id' is mandatory for MIX messages","O atributo 'id' é obrigatório para mensagens MIX"}. +{"Attribute 'jid' is not allowed here","O atributo 'jid' não é permitido aqui"}. +{"Attribute 'node' is not allowed here","O Atributo 'nó' não é permitido aqui"}. +{"Attribute 'to' of stanza that triggered challenge","O atributo 'para' da estrofe que desencadeou o desafio"}. {"August","Agosto"}. +{"Automatic node creation is not enabled","Criação automatizada de nós está desabilitada"}. {"Backup Management","Gestão de Backup"}. {"Backup of ~p","Backup de ~p"}. -{"Backup","Salvar cópia de segurança"}. {"Backup to File at ","Salvar backup para arquivo em "}. +{"Backup","Salvar cópia de segurança"}. {"Bad format","Formato incorreto"}. {"Birthday","Aniversário"}. +{"Both the username and the resource are required","Nome de usuário e recurso são necessários"}. +{"Bytestream already activated","Bytestream já foi ativado"}. +{"Cannot remove active list","Não é possível remover uma lista ativa"}. +{"Cannot remove default list","Não é possível remover uma lista padrão"}. {"CAPTCHA web page","CAPTCHA web page"}. +{"Challenge ID","ID do desafio"}. {"Change Password","Mudar senha"}. {"Change User Password","Alterar Senha do Usuário"}. +{"Changing password is not allowed","Não é permitida a alteração da senha"}. +{"Changing role/affiliation is not allowed","Não é permitida a alteração da função/afiliação"}. +{"Channel already exists","O canal já existe"}. +{"Channel does not exist","O canal não existe"}. +{"Channel JID","Canal JID"}. +{"Channels","Canais"}. {"Characters not allowed:","Caracteres não aceitos:"}. {"Chatroom configuration modified","Configuração da sala de bate-papo modificada"}. {"Chatroom is created","A sala de chat está criada"}. @@ -48,139 +84,151 @@ {"Chatroom is stopped","A sala de chat está parada"}. {"Chatrooms","Salas de Chat"}. {"Choose a username and password to register with this server","Escolha um nome de usuário e senha para registrar-se neste servidor"}. -{"Choose modules to stop","Selecione módulos a parar"}. {"Choose storage type of tables","Selecione o tipo de armazenamento das tabelas"}. {"Choose whether to approve this entity's subscription.","Aprovar esta assinatura."}. {"City","Cidade"}. +{"Client acknowledged more stanzas than sent by server","O cliente reconheceu mais estrofes do que as enviadas pelo servidor"}. +{"Clustering","Agrupamento"}. {"Commands","Comandos"}. {"Conference room does not exist","A sala de conferência não existe"}. -{"Configuration","Configuração"}. {"Configuration of room ~s","Configuração para ~s"}. -{"Connected Resources:","Recursos conectados:"}. -{"Connections parameters","Parâmetros para as Conexões"}. +{"Configuration","Configuração"}. +{"Contact Addresses (normally, room owner or owners)","Endereços de contato (normalmente, o proprietário ou os proprietários da sala)"}. {"Country","País"}. -{"CPU Time:","Tempo de CPU"}. -{"Database","Base de dados"}. -{"Database Tables at ~p","Tabelas da Base de dados em ~p"}. +{"Current Discussion Topic","Assunto em discussão"}. +{"Database failure","Falha no banco de dados"}. {"Database Tables Configuration at ","Configuração de Tabelas de Base de dados em "}. +{"Database","Base de dados"}. {"December","Dezembro"}. {"Default users as participants","Usuários padrões como participantes"}. -{"Delete message of the day","Apagar mensagem do dia"}. {"Delete message of the day on all hosts","Apagar a mensagem do dia em todos os hosts"}. -{"Delete Selected","Remover os selecionados"}. +{"Delete message of the day","Apagar mensagem do dia"}. {"Delete User","Deletar Usuário"}. {"Deliver event notifications","Entregar as notificações de evento"}. {"Deliver payloads with event notifications","Enviar payloads junto com as notificações de eventos"}. -{"Description:","Descrição:"}. {"Disc only copy","Somente cópia em disco"}. -{"Displayed Groups:","Grupos Exibidos:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","Não revele a sua senha a ninguém, nem mesmo para o administrador deste servidor Jabber."}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","Não revele a sua senha para ninguém, nem mesmo para o administrador deste servidor XMPP."}. {"Dump Backup to Text File at ","Exportar backup para texto em "}. {"Dump to Text File","Exportar para arquivo texto"}. +{"Duplicated groups are not allowed by RFC6121","Os grupos duplicados não são permitidos pela RFC6121"}. +{"Dynamically specify a replyto of the item publisher","Definir de forma dinâmica uma resposta da editora do item"}. {"Edit Properties","Editar propriedades"}. {"Either approve or decline the voice request.","Você deve aprovar/desaprovar a requisição de voz."}. -{"ejabberd IRC module","Módulo de IRC para ejabberd"}. +{"ejabberd HTTP Upload service","serviço HTTP de upload ejabberd"}. {"ejabberd MUC module","Módulo de MUC para ejabberd"}. -{"ejabberd Multicast service","ejabberd Multicast service"}. +{"ejabberd Multicast service","Serviço multicast ejabberd"}. {"ejabberd Publish-Subscribe module","Módulo para Publicar Tópicos do ejabberd"}. {"ejabberd SOCKS5 Bytestreams module","Modulo ejabberd SOCKS5 Bytestreams"}. {"ejabberd vCard module","Módulo vCard para ejabberd"}. {"ejabberd Web Admin","ejabberd Web Admin"}. -{"Elements","Elementos"}. +{"ejabberd","ejabberd"}. +{"Email Address","Endereço de e-mail"}. {"Email","Email"}. -{"Empty Rooms","Salas vazias"}. +{"Enable hats","Ativa chapéus"}. {"Enable logging","Permitir criação de logs"}. {"Enable message archiving","Habilitar arquivamento de mensagens"}. -{"Encoding for server ~b","Codificação para o servidor ~b"}. +{"Enabling push without 'node' attribute is not supported","Abilitar push sem o atributo 'node' não é suportado"}. {"End User Session","Terminar Sessão do Usuário"}. -{"Enter list of {Module, [Options]}","Introduza lista de {módulo, [opções]}"}. {"Enter nickname you want to register","Introduza o apelido que quer registrar"}. {"Enter path to backup file","Introduza o caminho do arquivo de backup"}. {"Enter path to jabberd14 spool dir","Introduza o caminho para o diretório de fila do jabberd14"}. {"Enter path to jabberd14 spool file","Insira o caminho para a fila (arquivo) do jabberd14"}. {"Enter path to text file","Introduza caminho para o arquivo texto"}. {"Enter the text you see","Insira o texto que você vê"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Insira o nome de usuário e codificações que você deseja usar para conectar-se aos servidores de IRC. Depois, presione 'Next' ('Próximo') para exibir mais campos que devem ser preenchidos. Ao final, pressione 'Complete' ('Completar') para salvar a configuração."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Insira o nome de usuário, codificações, portas e senhas que você deseja para usar nos servidores IRC"}. -{"Erlang Jabber Server","Servidor Jabber em Erlang"}. -{"Error","Erro"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Exemplo: [{\"irc.teste.net\", \"koi8-r\"}, 6667, \"senha\"}, {\"dominio.foo.net\", \"iso8859-1\", 7000}, {\"irc.servidordeteste.net\", \"utf-8\"}]."}. +{"Erlang XMPP Server","Servidor XMPP Erlang"}. {"Exclude Jabber IDs from CAPTCHA challenge","Excluir IDs Jabber de serem submetidos ao CAPTCHA"}. {"Export all tables as SQL queries to a file:","Exportar todas as tabelas como SQL para um arquivo:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportar todos os dados de todos os usuários no servidor, para arquivos formato PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Exportar dados dos usuários em um host, para arquivos PIEFXIS (XEP-0227):"}. +{"External component failure","Falha de componente externo"}. +{"External component timeout","Tempo esgotado à espera de componente externo"}. +{"Failed to activate bytestream","Falha ao ativar bytestream"}. {"Failed to extract JID from your voice request approval","Não foi possível extrair o JID (Jabber ID) da requisição de voz"}. +{"Failed to map delegated namespace to external component","Falha ao mapear namespace delegado ao componente externo"}. +{"Failed to parse HTTP response","Falha ao analisar resposta HTTP"}. +{"Failed to process option '~s'","Falha ao processar opção '~s'"}. {"Family Name","Sobrenome"}. +{"FAQ Entry","Registro das perguntas frequentes"}. {"February","Fevereiro"}. -{"Fill in fields to search for any matching Jabber User","Preencha campos para buscar usuários Jabber que concordem"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Preencha o formulário para buscar usuários Jabber. Agrega * ao final de um campo para buscar sub-palavras."}. +{"File larger than ~w bytes","Arquivo maior que ~w bytes"}. +{"Fill in the form to search for any matching XMPP User","Preencha campos para procurar por quaisquer usuários XMPP"}. {"Friday","Sexta"}. -{"From","De"}. -{"From ~s","De ~s"}. +{"From ~ts","De ~s"}. +{"Full List of Room Admins","Lista completa dos administradores das salas"}. +{"Full List of Room Owners","Lista completa dos proprietários das salas"}. {"Full Name","Nome completo"}. +{"Get List of Online Users","Obter a lista de usuários online"}. +{"Get List of Registered Users","Obter a lista de usuários registrados"}. {"Get Number of Online Users","Obter Número de Usuários Online"}. {"Get Number of Registered Users","Obter Número de Usuários Registrados"}. +{"Get Pending","Obter os pendentes"}. {"Get User Last Login Time","Obter a Data do Último Login"}. -{"Get User Password","Obter Senha do Usuário"}. {"Get User Statistics","Obter Estatísticas do Usuário"}. +{"Given Name","Prenome"}. {"Grant voice to this person?","Dar voz a esta pessoa?"}. -{"Group ","Grupo "}. -{"Groups","Grupos"}. {"has been banned","foi banido"}. -{"has been kicked because of an affiliation change","foi desconectado porque por afiliação inválida"}. {"has been kicked because of a system shutdown","foi desconectado porque o sistema foi desligado"}. +{"has been kicked because of an affiliation change","foi desconectado porque por afiliação inválida"}. {"has been kicked because the room has been changed to members-only","foi desconectado porque a política da sala mudou, só membros são permitidos"}. {"has been kicked","foi removido"}. -{" has set the subject to: "," mudou o assunto para: "}. -{"Host","Máquina"}. +{"Hash of the vCard-temp avatar of this room","Hash do avatar do vCard-temp desta sala"}. +{"Hat title","Título do chapéu"}. +{"Hat URI","URI do chapéu"}. +{"Hats limit exceeded","O limite dos chapéus foi excedido"}. +{"Host unknown","Máquina desconhecida"}. +{"HTTP File Upload","Upload de arquivo HTTP"}. +{"Idle connection","Conexão inativa"}. {"If you don't see the CAPTCHA image here, visit the web page.","Se você não conseguir ver o CAPTCHA aqui, visite a web page."}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Se você deseja especificar portas diferentes, senhas ou codifações para servidores de IRC, complete esta lista com os valores no formato: '{\"servidor IRC\", \"codificação\", porta, \"senha\"}'. Por padrão, este serviço usa a codificação \"~s\", porta \"~p\", e senha em branco (vazia)"}. {"Import Directory","Importar diretório"}. {"Import File","Importar arquivo"}. {"Import user data from jabberd14 spool file:","Importar dados dos usuários de uma fila jabberd14:"}. {"Import User from File at ","Importar usuário a partir do arquivo em "}. -{"Import users data from a PIEFXIS file (XEP-0227):","Importar usuários de um arquivo PIEFXIS (XEP-0227): "}. +{"Import users data from a PIEFXIS file (XEP-0227):","Importe os usuários de um arquivo PIEFXIS (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Importar dados dos usuários de um diretório-fila jabberd14:"}. {"Import Users from Dir at ","Importar usuários a partir do diretório em "}. {"Import Users From jabberd14 Spool Files","Importar usuários de arquivos jabberd14 (spool files)"}. +{"Improper domain part of 'from' attribute","Atributo 'from' contém domínio incorreto"}. {"Improper message type","Tipo de mensagem incorreto"}. -{"Incoming s2s Connections:","Conexões que entram de s2s"}. +{"Incorrect CAPTCHA submit","CAPTCHA submetido incorretamente"}. +{"Incorrect data form","Formulário dos dados incorreto"}. {"Incorrect password","Senha incorreta"}. -{"Invalid affiliation: ~s","Afiliação não válida: ~s"}. -{"Invalid role: ~s","Cargo (role) é não válido: ~s"}. +{"Incorrect value of 'action' attribute","Valor incorreto do atributo 'action'"}. +{"Incorrect value of 'action' in data form","Valor incorreto de 'action' no formulário de dados"}. +{"Incorrect value of 'path' in data form","Valor incorreto de 'path' no formulário de dados"}. +{"Installed Modules:","Módulos instalados:"}. +{"Install","Instalar"}. +{"Insufficient privilege","Privilégio insuficiente"}. +{"Internal server error","Erro interno do servidor"}. +{"Invalid 'from' attribute in forwarded message","Atributo 'from' inválido na mensagem reenviada"}. +{"Invalid node name","Nome do nó inválido"}. +{"Invalid 'previd' value","Valor 'previd' inválido"}. +{"Invitations are not allowed in this conference","Os convites não são permitidos nesta conferência"}. {"IP addresses","Endereços IP"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","Canal IRC (não coloque o #)"}. -{"IRC server","Servidor IRC"}. -{"IRC settings","Configurações do IRC"}. -{"IRC Transport","Transporte IRC"}. -{"IRC username","Usuário IRC"}. -{"IRC Username","Usuário IRC"}. {"is now known as","é agora conhecido como"}. -{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Não é permitido o envio de mensagens de erro a esta sala. O membro (~s) enviou uma mensagem de erro (~s) e foi desconectado (\"kicked\")."}. -{"It is not allowed to send private messages","Não é permitido enviar mensagens privadas"}. +{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Não é permitido o envio de mensagens de erro para a sala. O membro (~s) enviou uma mensagem de erro (~s) e foi expulso da sala"}. {"It is not allowed to send private messages of type \"groupchat\"","Não é permitido enviar mensagens privadas do tipo \"groupchat\""}. -{"It is not allowed to send private messages to the conference","Impedir o envio de mensagens privadas para a sala"}. -{"Jabber Account Registration","Registros de Contas Jabber"}. +{"It is not allowed to send private messages to the conference","Não é permitido enviar mensagens privadas para a sala de conferência"}. {"Jabber ID","ID Jabber"}. -{"Jabber ID ~s is invalid","O Jabber ID ~s não es válido"}. {"January","Janeiro"}. -{"Join IRC channel","Juntar-se ao canal IRC"}. +{"JID normalization denied by service policy","Normalização JID negada por causa da política de serviços"}. +{"JID normalization failed","A normalização JID falhou"}. +{"Joined MIX channels of ~ts","Entrou no canais MIX do ~ts"}. +{"Joined MIX channels:","Uniu-se aos canais MIX:"}. {"joins the room","Entrar na sala"}. -{"Join the IRC channel here.","Aqui! Juntar-se ao canal IRC."}. -{"Join the IRC channel in this Jabber ID: ~s","Entrar no canal IRC, neste ID Jabber: ~s"}. {"July","Julho"}. {"June","Junho"}. +{"Just created","Acabou de ser criado"}. {"Last Activity","Última atividade"}. {"Last login","Último login"}. +{"Last message","Última mensagem"}. {"Last month","Último mês"}. {"Last year","Último ano"}. +{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Bits menos significativos do hash sha-256 do texto devem ser iguais ao rótulo hexadecimal"}. {"leaves the room","Sair da sala"}. -{"Listened Ports at ","Portas abertas em "}. -{"Listened Ports","Portas escutadas"}. -{"List of modules to start","Listas de módulos para inicializar"}. -{"List of rooms","Lista de salas"}. -{"Low level update script","Script de atualização low level"}. +{"List of users with hats","Lista dos usuários com chapéus"}. +{"List users with hats","Lista os usuários com chapéus"}. +{"Logged Out","Desconectado"}. +{"Logging","Registrando no log"}. {"Make participants list public","Tornar pública a lista de participantes"}. {"Make room CAPTCHA protected","Tornar protegida a senha da sala"}. {"Make room members-only","Tornar sala apenas para membros"}. @@ -188,253 +236,395 @@ {"Make room password protected","Tornar sala protegida à senha"}. {"Make room persistent","Tornar sala persistente"}. {"Make room public searchable","Tornar sala pública possível de ser encontrada"}. +{"Malformed username","Nome de usuário inválido"}. +{"MAM preference modification denied by service policy","Modificação de preferência MAM negada por causa da política de serviços"}. {"March","Março"}. -{"Maximum Number of Occupants","Número máximo de participantes"}. -{"Max # of items to persist","Máximo # de elementos que persistem"}. +{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","Máximo # de itens para persistir ou `max` para nenhum limite específico que não seja um servidor imposto como máximo"}. {"Max payload size in bytes","Máximo tamanho do payload em bytes"}. +{"Maximum file size","Tamanho máximo do arquivo"}. +{"Maximum Number of History Messages Returned by Room","Quantidade máxima das mensagens do histórico que foram devolvidas por sala"}. +{"Maximum number of items to persist","Quantidade máxima dos itens para manter"}. +{"Maximum Number of Occupants","Número máximo de participantes"}. {"May","Maio"}. -{"Membership is required to enter this room","Necessitas ser membro desta sala para poder entrar"}. -{"Members:","Membros:"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Memorize a sua senha, ou escreva-a em um papel e guarde-o em um lugar seguro. Jabber não é uma maneira automatizada para recuperar a sua senha, se você a esquecer eventualmente."}. -{"Memory","Memória"}. +{"Membership is required to enter this room","É necessário ser membro desta sala para poder entrar"}. +{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Memorize a sua senha ou anote-a em um papel guardado em um local seguro. No XMPP, não há uma maneira automatizada de recuperar a sua senha caso a esqueça."}. +{"Mere Availability in XMPP (No Show Value)","Mera disponibilidade no XMPP (Sem valor para ser exibido)"}. {"Message body","Corpo da mensagem"}. +{"Message not found in forwarded payload","Mensagem não encontrada em conteúdo encaminhado"}. +{"Messages from strangers are rejected","As mensagens vindas de estranhos são rejeitadas"}. +{"Messages of type headline","Mensagens do tipo do título"}. +{"Messages of type normal","Mensagens do tipo normal"}. {"Middle Name","Nome do meio"}. {"Minimum interval between voice requests (in seconds)","O intervalo mínimo entre requisições de voz (em segundos)"}. -{"Moderator","Moderador"}. {"Moderator privileges required","Se necessita privilégios de moderador"}. -{"moderators only","apenas moderadores"}. -{"Modified modules","Módulos atualizados"}. -{"Module","Módulo"}. -{"Modules at ~p","Módulos em ~p"}. -{"Modules","Módulos"}. +{"Moderator","Moderador"}. +{"Moderators Only","Somente moderadores"}. +{"Module failed to handle the query","Módulo falhou ao processar a consulta"}. {"Monday","Segunda"}. {"Multicast","Multicast"}. +{"Multiple elements are not allowed by RFC6121","Vários elementos não são permitidos pela RFC6121"}. {"Multi-User Chat","Chat multi-usuário"}. -{"Name:","Nome:"}. {"Name","Nome"}. +{"Natural Language for Room Discussions","Idioma nativo para as discussões na sala"}. +{"Natural-Language Room Name","Nome da sala no idioma nativo"}. +{"Neither 'jid' nor 'nick' attribute found","Nem o atributo 'jid' nem 'nick' foram encontrados"}. +{"Neither 'role' nor 'affiliation' attribute found","Nem o atributo 'role' nem 'affiliation' foram encontrados"}. {"Never","Nunca"}. {"New Password:","Nova Senha:"}. -{"Nickname","Apelido"}. +{"Nickname can't be empty","O apelido não pode ser vazio"}. {"Nickname Registration at ","Registro do apelido em "}. -{"Nickname ~s does not exist in the room","O nick ~s não existe na sala"}. -{"nobody","ninguém"}. +{"Nickname ~s does not exist in the room","O apelido ~s não existe na sala"}. +{"Nickname","Apelido"}. +{"No address elements found","Nenhum elemento endereço foi encontrado"}. +{"No addresses element found","Nenhum elemento endereços foi encontrado"}. +{"No 'affiliation' attribute found","Atributo 'affiliation' não foi encontrado"}. +{"No available resource found","Nenhum recurso disponível foi encontrado"}. {"No body provided for announce message","Nenhum corpo de texto fornecido para anunciar mensagem"}. +{"No child elements found","Nenhum elemento filho foi encontrado"}. +{"No data form found","Nenhum formulário de dados foi encontrado"}. {"No Data","Nenhum dado"}. +{"No features available","Nenhuma funcionalidade disponível"}. +{"No element found","Nenhum elemento foi encontrado"}. +{"No hook has processed this command","Nenhum hook processou este comando"}. +{"No info about last activity found","Não foi encontrada informação sobre última atividade"}. +{"No 'item' element found","O elemento 'item' não foi encontrado"}. +{"No items found in this query","Nenhum item encontrado nesta consulta"}. +{"No limit","Ilimitado"}. +{"No module is handling this query","Nenhum módulo está processando esta consulta"}. +{"No node specified","Nenhum nó especificado"}. +{"No 'password' found in data form","'password' não foi encontrado em formulário de dados"}. +{"No 'password' found in this query","'password' não foi encontrado nesta consulta"}. +{"No 'path' found in data form","'path' não foi encontrado em formulário de dados"}. +{"No pending subscriptions found","Não foram encontradas subscrições"}. +{"No privacy list with this name found","Nenhuma lista de privacidade encontrada com este nome"}. +{"No private data found in this query","Nenhum dado privado encontrado nesta consulta"}. +{"No running node found","Nenhum nó em execução foi encontrado"}. +{"No services available","Não há serviços disponíveis"}. +{"No statistics found for this item","Não foram encontradas estatísticas para este item"}. +{"No 'to' attribute found in the invitation","Atributo 'to' não foi encontrado no convite"}. +{"Nobody","Ninguém"}. +{"Node already exists","Nó já existe"}. {"Node ID","ID do Tópico"}. +{"Node index not found","O índice do nó não foi encontrado"}. {"Node not found","Nó não encontrado"}. {"Node ~p","Nó ~p"}. +{"Node","Nó"}. +{"Nodeprep has failed","Processo de identificação de nó falhou (nodeprep)"}. {"Nodes","Nós"}. -{"No limit","Ilimitado"}. {"None","Nenhum"}. -{"No resource provided","Nenhum recurso foi informado"}. +{"Not allowed","Não é permitido"}. {"Not Found","Não encontrado"}. -{"Notify subscribers when items are removed from the node","Notificar subscritores quando os elementos se eliminem do nodo"}. -{"Notify subscribers when the node configuration changes","Notificar subscritores quando cambia la configuração do nodo"}. -{"Notify subscribers when the node is deleted","Notificar subscritores quando o nodo se elimine"}. +{"Not subscribed","Não subscrito"}. +{"Notify subscribers when items are removed from the node","Notificar assinantes quando itens forem eliminados do nó"}. +{"Notify subscribers when the node configuration changes","Notificar assinantes a configuração do nó mudar"}. +{"Notify subscribers when the node is deleted","Notificar assinantes quando o nó for eliminado se elimine"}. {"November","Novembro"}. -{"Number of occupants","Número de participantes"}. +{"Number of answers required","Quantidade de respostas necessárias"}. +{"Number of occupants","Quantidade de ocupantes"}. +{"Number of Offline Messages","Quantidade das mensagens offline"}. {"Number of online users","Número de usuários online"}. {"Number of registered users","Número de usuários registrados"}. +{"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","Quantidade de segundos após limpar automaticamente os itens ou `max` para nenhum limite específico que não seja um servidor imposto máximo"}. +{"Occupants are allowed to invite others","As pessoas estão autorizadas a convidar outras pessoas"}. +{"Occupants are allowed to query others","Os ocupantes estão autorizados a consultar os outros"}. +{"Occupants May Change the Subject","As pessoas talvez possam alterar o assunto"}. {"October","Outubro"}. -{"Offline Messages:","Mensagens offline"}. -{"Offline Messages","Mensagens offline"}. {"OK","OK"}. {"Old Password:","Senha Antiga:"}. -{"Online","Conectado"}. {"Online Users","Usuários conectados"}. -{"Online Users:","Usuários online"}. +{"Online","Conectado"}. +{"Only collection node owners may associate leaf nodes with the collection","Apenas um grupo dos proprietários dos nós podem associar as páginas na coleção"}. {"Only deliver notifications to available users","Somente enviar notificações aos usuários disponíveis"}. +{"Only or tags are allowed","Apenas tags ou são permitidas"}. +{"Only element is allowed in this query","Apenas elemento é permitido nesta consulta"}. {"Only members may query archives of this room","Somente os membros podem procurar nos arquivos desta sala"}. {"Only moderators and participants are allowed to change the subject in this room","Somente os moderadores e os participamentes podem alterar o assunto desta sala"}. {"Only moderators are allowed to change the subject in this room","Somente os moderadores podem alterar o assunto desta sala"}. +{"Only moderators are allowed to retract messages","Apenas moderadores estão autorizados a retirar mensagens"}. {"Only moderators can approve voice requests","Somente moderadores podem aprovar requisições de voz"}. -{"Only occupants are allowed to send messages to the conference","Somente os ocupantes podem enviar mensagens à sala"}. -{"Only occupants are allowed to send queries to the conference","Somente os ocupantes podem enviar consultas à sala"}. +{"Only occupants are allowed to send messages to the conference","Somente os ocupantes podem enviar mensagens à sala de conferência"}. +{"Only occupants are allowed to send queries to the conference","Somente os ocupantes podem enviar consultas à sala de conferência"}. +{"Only publishers may publish","Apenas os editores podem publicar"}. {"Only service administrators are allowed to send service messages","Apenas administradores possuem permissão para enviar mensagens de serviço"}. -{"Options","Opções"}. +{"Only those on a whitelist may associate leaf nodes with the collection","Apenas aqueles presentes em uma lista branca podem associar páginas na coleção"}. +{"Only those on a whitelist may subscribe and retrieve items","Apenas aqueles presentes em uma lista branca podem se inscrever e recuperar os itens"}. {"Organization Name","Nome da organização"}. {"Organization Unit","Departamento/Unidade"}. -{"Outgoing s2s Connections","Conexões que partam de s2s"}. -{"Outgoing s2s Connections:","Conexões que partem de s2s"}. +{"Other Modules Available:","Outros módulos disponíveis:"}. +{"Outgoing s2s Connections","Conexões s2s de Saída"}. {"Owner privileges required","Se requer privilégios de proprietário da sala"}. -{"Packet","Pacote"}. +{"Packet relay is denied by service policy","A retransmissão de pacote é negada por causa da política de serviço"}. +{"Participant ID","ID do participante"}. {"Participant","Participante"}. -{"Password ~b","Senha ~b"}. -{"Password:","Senha:"}. -{"Password","Senha"}. -{"Password Verification:","Verificação de Senha"}. +{"Password Verification:","Verificação da Senha:"}. {"Password Verification","Verificação de Senha"}. +{"Password","Senha"}. +{"Password:","Senha:"}. {"Path to Dir","Caminho para o diretório"}. {"Path to File","Caminho do arquivo"}. -{"Pending","Pendente"}. +{"Payload semantic type information","Informações de tipo semântico de carga útil"}. {"Period: ","Período: "}. -{"Permanent rooms","Salas permanentes"}. {"Persist items to storage","Persistir elementos ao armazenar"}. +{"Persistent","Persistente"}. +{"Ping query is incorrect","A consulta ping está incorreta"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Observe que tais opções farão backup apenas da base de dados Mnesia. Caso você esteja utilizando o modulo ODBC, você precisará fazer backup de sua base de dados SQL separadamente."}. -{"Please specify file name.","Por favor informe o nome do arquivo."}. -{"Please specify file size.","Por favor informe o tamanho do arquivo."}. {"Please, wait for a while before sending new voice request","Por favor, espere antes de enviar uma nova requisição de voz"}. {"Pong","Pong"}. -{"Port ~b","Porta ~b"}. -{"Port","Porta"}. +{"Possessing 'ask' attribute is not allowed by RFC6121","Possuir o atributo 'ask' não é permitido pela RFC6121"}. {"Present real Jabber IDs to","Tornar o Jabber ID real visível por"}. +{"Previous session not found","A sessão anterior não foi encontrada"}. +{"Previous session PID has been killed","O PID da sessão anterior foi excluído"}. +{"Previous session PID has exited","O PID da sessão anterior foi encerrado"}. +{"Previous session PID is dead","O PID da sessão anterior está morto"}. +{"Previous session timed out","A sessão anterior expirou"}. {"private, ","privado, "}. -{"Protocol","Porta"}. +{"Public","Público"}. +{"Publish model","Publicar o modelo"}. {"Publish-Subscribe","Publicação de Tópico"}. {"PubSub subscriber request","PubSub requisição de assinante"}. {"Purge all items when the relevant publisher goes offline","Descartar todos os itens quando o publicante principal estiver offline"}. -{"Queries to the conference members are not allowed in this room","Nesta sala não se permite consultas aos membros da sala"}. +{"Push record not found","O registro push não foi encontrado"}. +{"Queries to the conference members are not allowed in this room","Nesta sala de conferência, consultas aos membros não são permitidas"}. +{"Query to another users is forbidden","Consultar a outro usuário é proibido"}. {"RAM and disc copy","Cópias na RAM e disco rígido"}. {"RAM copy","Cópia em RAM"}. -{"Raw","Intocado"}. {"Really delete message of the day?","Deletar realmente a mensagem do dia?"}. +{"Receive notification from all descendent nodes","Receba a notificação de todos os nós descendentes"}. +{"Receive notification from direct child nodes only","Receba apenas as notificações dos nós relacionados"}. +{"Receive notification of new items only","Receba apenas as notificações dos itens novos"}. +{"Receive notification of new nodes only","Receba apenas as notificações dos nós novos"}. {"Recipient is not in the conference room","O receptor não está na sala de conferência"}. -{"Register a Jabber account","Registrar uma conta Jabber"}. -{"Registered nicknames","Usuários registrados"}. -{"Registered Users:","Usuários registrados"}. -{"Registered Users","Usuários Registrados"}. +{"Register an XMPP account","Registre uma conta XMPP"}. {"Register","Registrar"}. -{"Registration in mod_irc for ","Registro em mod_irc para "}. {"Remote copy","Cópia remota"}. -{"Remove All Offline Messages","Remover Todas as Mensagens Offline"}. -{"Remove","Remover"}. +{"Remove a hat from a user","Remove um chapéu de um usuário"}. {"Remove User","Remover usuário"}. {"Replaced by new connection","Substituído por nova conexão"}. +{"Request has timed out","O pedido expirou"}. +{"Request is ignored","O pedido foi ignorado"}. +{"Requested role","Função solicitada"}. {"Resources","Recursos"}. -{"Restart","Reiniciar"}. {"Restart Service","Reiniciar Serviço"}. {"Restore Backup from File at ","Restaurar backup a partir do arquivo em "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Restaurar backup binário após reinicialização do ejabberd (requer menos memória):"}. -{"Restore binary backup immediately:","Restaurar backup binário imediatamente"}. +{"Restore binary backup immediately:","Restaurar imediatamente o backup binário:"}. {"Restore plain text backup immediately:","Restaurar backup formato texto imediatamente:"}. {"Restore","Restaurar"}. +{"Result","Resultado"}. +{"Roles and Affiliations that May Retrieve Member List","As funções e as afiliações que podem recuperar a lista dos membros"}. {"Roles for which Presence is Broadcasted","Para quem a presença será notificada"}. +{"Roles that May Send Private Messages","Atribuições que talvez possam enviar mensagens privadas"}. {"Room Configuration","Configuração de salas"}. {"Room creation is denied by service policy","Sala não pode ser criada devido à política do serviço"}. {"Room description","Descrição da Sala"}. -{"Room Occupants","Número de participantes"}. +{"Room Occupants","Ocupantes do quarto"}. +{"Room terminates","Terminação da sala"}. {"Room title","Título da sala"}. {"Roster groups allowed to subscribe","Listar grupos autorizados"}. -{"Roster","Lista de contatos"}. -{"Roster of ","Lista de contatos de "}. {"Roster size","Tamanho da Lista"}. -{"RPC Call Error","Erro de chamada RPC"}. {"Running Nodes","Nós em execução"}. -{"~s access rule configuration","Configuração da Regra de Acesso ~s"}. +{"~s invites you to the room ~s","~s convidaram você para a sala ~s"}. {"Saturday","Sábado"}. -{"Script check","Verificação de Script"}. +{"Search from the date","Pesquise a partir da data"}. {"Search Results for ","Resultados de pesquisa para "}. +{"Search the text","Pesquise o texto"}. +{"Search until the date","Pesquise até a data"}. {"Search users in ","Procurar usuários em "}. -{"Send announcement to all online users","Enviar anúncio a todos os usuárions online"}. {"Send announcement to all online users on all hosts","Enviar anúncio a todos usuários online em todas as máquinas"}. -{"Send announcement to all users","Enviar anúncio a todos os usuários"}. +{"Send announcement to all online users","Enviar anúncio a todos os usuárions online"}. {"Send announcement to all users on all hosts","Enviar aviso para todos os usuários em todos os hosts"}. +{"Send announcement to all users","Enviar anúncio a todos os usuários"}. {"September","Setembro"}. -{"Server ~b","Servidor ~b"}. {"Server:","Servidor:"}. -{"Server","Servidor"}. +{"Service list retrieval timed out","A recuperação da lista dos serviços expirou"}. +{"Session state copying timed out","A cópia do estado da sessão expirou"}. {"Set message of the day and send to online users","Definir mensagem do dia e enviar a todos usuários online"}. {"Set message of the day on all hosts and send to online users","Definir mensagem do dia em todos os hosts e enviar para os usuários online"}. {"Shared Roster Groups","Grupos Shared Roster"}. {"Show Integral Table","Mostrar Tabela Integral"}. +{"Show Occupants Join/Leave","Mostrar a entrada e a saída de ocupantes"}. {"Show Ordinary Table","Mostrar Tabela Ordinária"}. {"Shut Down Service","Parar Serviço"}. -{"~s invites you to the room ~s","~s convidou você para a sala ~s"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Alguns clientes jabber podem salvar a sua senha no seu computador. Use recurso somente se você considera este computador seguro o suficiente."}. +{"SOCKS5 Bytestreams","Bytestreams SOCKS5"}. +{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Alguns clientes XMPP podem armazenar a sua senha no seu computador, só faça isso no seu computador particular por questões de segurança."}. +{"Sources Specs:","Especificações das fontes:"}. {"Specify the access model","Especificar os modelos de acesso"}. {"Specify the event message type","Especificar o tipo de mensagem para o evento"}. {"Specify the publisher model","Especificar o modelo do publicante"}. -{"~s's Offline Messages Queue","~s's Fila de Mensagens Offline"}. -{"Start","Iniciar"}. -{"Start Modules at ","Iniciar módulos em "}. -{"Start Modules","Iniciar módulos"}. -{"Statistics","Estatísticas"}. -{"Statistics of ~p","Estatísticas de ~p"}. -{"Stop Modules at ","Parar módulos em "}. -{"Stop Modules","Parar módulos"}. -{"Stop","Parar"}. -{"Stopped Nodes","Nos parados"}. -{"Storage Type","Tipo de armazenamento"}. +{"Stanza id is not valid","A Stanza ID não é válido"}. +{"Stanza ID","ID da estrofe"}. +{"Statically specify a replyto of the node owner(s)","Defina uma resposta fixa do(s) proprietário(s) do nó"}. +{"Stopped Nodes","Nós parados"}. {"Store binary backup:","Armazenar backup binário:"}. {"Store plain text backup:","Armazenar backup em texto:"}. +{"Stream management is already enabled","A gestão do fluxo já está ativada"}. +{"Stream management is not enabled","O gerenciamento do fluxo não está ativado"}. {"Subject","Assunto"}. -{"Submit","Enviar"}. {"Submitted","Submetido"}. {"Subscriber Address","Endereço dos Assinantes"}. -{"Subscription","Subscrição"}. +{"Subscribers may publish","Os assinantes podem publicar"}. +{"Subscription requests must be approved and only subscribers may retrieve items","Os pedidos de assinatura devem ser aprovados e apenas os assinantes podem recuperar os itens"}. +{"Subscriptions are not allowed","Subscrições não estão permitidas"}. {"Sunday","Domingo"}. +{"Text associated with a picture","Um texto associado a uma imagem"}. +{"Text associated with a sound","Um texto associado a um som"}. +{"Text associated with a video","Um texto associado a um vídeo"}. +{"Text associated with speech","Um texto associado à fala"}. {"That nickname is already in use by another occupant","O apelido (nick) já está sendo utilizado"}. -{"That nickname is registered by another person","O nick já está registrado por outra pessoa"}. +{"That nickname is registered by another person","O apelido já está registrado por outra pessoa"}. +{"The account already exists","A conta já existe"}. +{"The account was not unregistered","A conta não estava não registrada"}. +{"The body text of the last received message","O corpo do texto da última mensagem que foi recebida"}. {"The CAPTCHA is valid.","O CAPTCHA é inválido."}. {"The CAPTCHA verification has failed","A verificação do CAPTCHA falhou"}. +{"The captcha you entered is wrong","O captcha que você digitou está errado"}. +{"The child nodes (leaf or collection) associated with a collection","Os nós relacionados (página ou coleção) associados com uma coleção"}. {"The collections with which a node is affiliated","As coleções com as quais o nó está relacionado"}. +{"The DateTime at which a leased subscription will end or has ended","A data e a hora que uma assinatura alugada terminará ou terá terminado"}. +{"The datetime when the node was created","A data em que o nó foi criado"}. +{"The default language of the node","O idioma padrão do nó"}. +{"The feature requested is not supported by the conference","A funcionalidade solicitada não é suportada pela sala de conferência"}. +{"The JID of the node creator","O JID do criador do nó"}. +{"The JIDs of those to contact with questions","Os JIDs daqueles para entrar em contato com perguntas"}. +{"The JIDs of those with an affiliation of owner","Os JIDs daqueles com uma afiliação de proprietário"}. +{"The JIDs of those with an affiliation of publisher","Os JIDs daqueles com uma afiliação de editor"}. +{"The list of all online users","A lista de todos os usuários online"}. +{"The list of all users","A lista de todos os usuários"}. +{"The list of JIDs that may associate leaf nodes with a collection","A lista dos JIDs que podem associar as páginas dos nós em uma coleção"}. +{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","A quantidade máxima de nós relacionados que podem ser associados a uma coleção ou `máximo` para nenhum limite específico que não seja um servidor imposto no máximo"}. +{"The minimum number of milliseconds between sending any two notification digests","O número mínimo de milissegundos entre o envio do resumo das duas notificações"}. +{"The name of the node","O nome do nó"}. +{"The node is a collection node","O nó é um nó da coleção"}. +{"The node is a leaf node (default)","O nó é uma página do nó (padrão)"}. +{"The NodeID of the relevant node","O NodeID do nó relevante"}. +{"The number of pending incoming presence subscription requests","A quantidade pendente dos pedidos da presença da assinatura"}. +{"The number of subscribers to the node","A quantidade dos assinantes para o nó"}. +{"The number of unread or undelivered messages","A quantidade das mensagens que não foram lidas ou não foram entregues"}. +{"The password contains unacceptable characters","A senha contém caracteres proibidos"}. +{"The password is too weak","Senha considerada muito fraca"}. {"the password is","a senha é"}. -{"The password is too weak","Senha considerada fraca'"}. -{"The password of your Jabber account was successfully changed.","A senha da sua conta Jabber foi mudada com sucesso."}. -{"There was an error changing the password: ","Houve um erro ao mudar a senha: "}. +{"The password of your XMPP account was successfully changed.","A senha da sua conta XMPP foi alterada com sucesso."}. +{"The password was not changed","A senha não foi alterada"}. +{"The passwords are different","As senhas não batem"}. +{"The presence states for which an entity wants to receive notifications","As condições da presença para os quais uma entidade queira receber as notificações"}. +{"The query is only allowed from local users","Esta consulta só é permitida a partir de usuários locais"}. +{"The query must not contain elements","A consulta não pode conter elementos "}. +{"The room subject can be modified by participants","O tema da sala pode ser alterada pelos próprios participantes"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","Informações de tipo semântico dos dados no nó, geralmente especificadas pelo espaço de nomes da carga útil (se houver)"}. +{"The sender of the last received message","O remetente da última mensagem que foi recebida"}. +{"The stanza MUST contain only one element, one element, or one element","A instância DEVE conter apenas um elemento , um elemento , ou um elemento "}. +{"The subscription identifier associated with the subscription request","O identificador da assinatura associado à solicitação da assinatura"}. +{"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","O URL da transformação XSL que pode ser aplicada nas cargas úteis para gerar um elemento apropriado no corpo da mensagem."}. +{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","A URL de uma transformação XSL que pode ser aplicada ao formato de carga útil para gerar um Formulário de Dados válido onde o cliente possa exibir usando um mecanismo genérico de renderização do Formulários de Dados"}. +{"There was an error changing the password: ","Houve um erro ao alterar a senha: "}. {"There was an error creating the account: ","Houve um erro ao criar esta conta: "}. {"There was an error deleting the account: ","Houve um erro ao deletar esta conta: "}. -{"This IP address is blacklisted in ~s","Este endereço IP está bloqueado em ~s"}. -{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Não é 'case insensitive': macbeth é o mesmo que MacBeth e ainda Macbeth. "}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Esta pagina aceita criações de novas contas Jabber neste servidor. A sua JID (Identificador Jabber) será da seguinte forma: 'usuário@servidor'. Por favor, leia cuidadosamente as instruções para preencher corretamente os campos."}. -{"This page allows to unregister a Jabber account in this Jabber server.","Esta página aceita para deletar uma conta Jabber neste servidor."}. +{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","O tamanho da caixa não importa: macbeth é o mesmo que MacBeth e Macbeth."}. +{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Esta pagina permite a criação de novas contas XMPP neste servidor. O seu JID (Identificador Jabber) será da seguinte maneira: usuário@servidor. Por favor, leia cuidadosamente as instruções para preencher todos os campos corretamente."}. +{"This page allows to unregister an XMPP account in this XMPP server.","Esta página permite a exclusão de uma conta XMPP neste servidor."}. +{"This room is not anonymous","Essa sala não é anônima"}. +{"This service can not process the address: ~s","Este serviço não pode processar o endereço: ~s"}. {"Thursday","Quinta"}. {"Time delay","Intervalo (Tempo)"}. -{"Time","Tempo"}. +{"Timed out waiting for stream resumption","Tempo limite expirou durante à espera da retomada da transmissão"}. +{"To register, visit ~s","Para registrar, visite ~s"}. +{"To ~ts","Para ~s"}. +{"Token TTL","Token TTL"}. +{"Too many active bytestreams","Quantidade excessiva de bytestreams ativos"}. {"Too many CAPTCHA requests","Número excessivo de requisições para o CAPTCHA"}. +{"Too many child elements","Quantidade excessiva de elementos filho"}. +{"Too many elements","Número excessivo de elementos "}. +{"Too many elements","Número excessivo de elementos "}. {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Número excessivo (~p) de tentativas falhas de autenticação (~s). O endereço será desbloqueado às ~s UTC"}. -{"Too many unacked stanzas","número excessivo de instâncias sem confirmação"}. -{"To","Para"}. -{"To ~s","Para ~s"}. -{"Total rooms","Salas no total"}. +{"Too many receiver fields were specified","Foram definidos receptores demais nos campos"}. +{"Too many unacked stanzas","Número excessivo de instâncias sem confirmação"}. +{"Too many users in this conference","Há uma quantidade excessiva de usuários nesta conferência"}. {"Traffic rate limit is exceeded","Limite de banda excedido"}. -{"Transactions Aborted:","Transações abortadas:"}. -{"Transactions Committed:","Transações salvas:"}. -{"Transactions Logged:","Transações de log:"}. -{"Transactions Restarted:","Transações reiniciadas:"}. +{"~ts's MAM Archive","Arquivo ~ts's MAM"}. +{"~ts's Offline Messages Queue","~s's Fila de Mensagens Offline"}. {"Tuesday","Terça"}. {"Unable to generate a CAPTCHA","Impossível gerar um CAPTCHA"}. +{"Unable to register route on existing local domain","Não foi possível registrar rota no domínio local existente"}. {"Unauthorized","Não Autorizado"}. -{"Unregister a Jabber account","Deletar conta Jabber"}. +{"Unexpected action","Ação inesperada"}. +{"Unexpected error condition: ~p","Condição de erro inesperada: ~p"}. +{"Uninstall","Desinstalar"}. +{"Unregister an XMPP account","Excluir uma conta XMPP"}. {"Unregister","Deletar registro"}. -{"Update","Atualizar"}. +{"Unsupported element","Elemento não suportado"}. +{"Unsupported version","Versão sem suporte"}. {"Update message of the day (don't send)","Atualizar mensagem do dia (não enviar)"}. {"Update message of the day on all hosts (don't send)","Atualizar a mensagem do dia em todos os host (não enviar)"}. -{"Update ~p","Atualizar ~p"}. -{"Update plan","Plano de Atualização"}. -{"Update script","Script de atualização"}. -{"Uptime:","Uptime:"}. -{"Use of STARTTLS required","É obrigatório uso de STARTTLS"}. +{"Update specs to get modules source, then install desired ones.","Atualize as especificações para obter a fonte dos módulos e instale os que desejar."}. +{"Update Specs","Atualizar as especificações"}. +{"Updating the vCard is not supported by the vCard storage backend","A atualização do vCard não é compatível com o back-end de armazenamento do vCard"}. +{"Upgrade","Atualização"}. +{"URL for Archived Discussion Logs","A URL para o arquivamento dos registros da discussão"}. +{"User already exists","Usuário já existe"}. +{"User (jid)","Usuário (jid)"}. {"User JID","Usuário JID"}. {"User Management","Gerenciamento de Usuários"}. +{"User not allowed to perform an IQ set on another user's vCard.","O usuário não tem permissão para executar um conjunto de QI no vCard de outro usuário."}. +{"User removed","O usuário foi removido"}. +{"User session not found","A sessão do usuário não foi encontrada"}. +{"User session terminated","Sessão de usuário terminada"}. +{"User ~ts","Usuário ~s"}. {"Username:","Usuário:"}. {"Users are not allowed to register accounts so quickly","Usuários não estão autorizados a registrar contas imediatamente"}. {"Users Last Activity","Últimas atividades dos usuários"}. -{"User ~s","Usuário ~s"}. {"Users","Usuários"}. {"User","Usuário"}. -{"Validate","Validar"}. +{"Value 'get' of 'type' attribute is not allowed","Valor 'get' não permitido para atributo 'type'"}. +{"Value of '~s' should be boolean","Value de '~s' deveria ser um booleano"}. +{"Value of '~s' should be datetime string","Valor de '~s' deveria ser data e hora"}. +{"Value of '~s' should be integer","Valor de '~s' deveria ser um inteiro"}. +{"Value 'set' of 'type' attribute is not allowed","Valor 'set' não permitido para atributo 'type'"}. {"vCard User Search","Busca de Usuário vCard"}. +{"View joined MIX channels","Exibir os canais MIX aderidos"}. {"Virtual Hosts","Hosts virtuais"}. {"Visitors are not allowed to change their nicknames in this room","Nesta sala, os visitantes não podem mudar seus apelidos"}. {"Visitors are not allowed to send messages to all occupants","Os visitantes não podem enviar mensagens a todos os ocupantes"}. {"Visitor","Visitante"}. {"Voice request","Requisição de voz"}. -{"Voice requests are disabled in this conference","Requisições de voz estão desabilitadas nesta conferência"}. +{"Voice requests are disabled in this conference","Requisições de voz estão desabilitadas nesta sala de conferência"}. +{"Web client which allows to join the room anonymously","Cliente da web que permite entrar na sala de forma anônima"}. {"Wednesday","Quarta"}. +{"When a new subscription is processed and whenever a subscriber comes online","Quando uma nova assinatura é processada e sempre que um assinante fica online"}. +{"When a new subscription is processed","Quando uma nova assinatura é processada"}. {"When to send the last published item","Quando enviar o último tópico publicado"}. +{"Whether an entity wants to receive an XMPP message body in addition to the payload format","Caso uma entidade queira receber o corpo de uma mensagem XMPP além do formato de carga útil"}. +{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","Caso uma entidade queira receber os resumos (as agregações) das notificações ou todas as notificações individualmente"}. +{"Whether an entity wants to receive or disable notifications","Caso uma entidade queira receber ou desativar as notificações"}. +{"Whether owners or publisher should receive replies to items","Caso os proprietários ou a editora devam receber as respostas nos itens"}. +{"Whether the node is a leaf (default) or a collection","Caso o nó seja uma folha (padrão) ou uma coleção"}. {"Whether to allow subscriptions","Permitir subscrições"}. -{"You can later change your password using a Jabber client.","Mais tarde você pode alterar a sua senha usando um cliente Jabber."}. +{"Whether to make all subscriptions temporary, based on subscriber presence","Caso todas as assinaturas devam ser temporárias, com base na presença do assinante"}. +{"Whether to notify owners about new subscribers and unsubscribes","Caso deva notificar os proprietários sobre os novos assinantes e aqueles que cancelaram a assinatura"}. +{"Who can send private messages","Quem pode enviar mensagens privadas"}. +{"Who may associate leaf nodes with a collection","Quem pode associar as folhas dos nós em uma coleção"}. +{"Wrong parameters in the web formulary","O formulário web está com os parâmetros errados"}. +{"Wrong xmlns","Xmlns errado"}. +{"XMPP Account Registration","Registo da Conta XMPP"}. +{"XMPP Domains","Domínios XMPP"}. +{"XMPP Show Value of Away","XMPP Exiba o valor da ausência"}. +{"XMPP Show Value of Chat","XMPP Exiba o valor do chat"}. +{"XMPP Show Value of DND (Do Not Disturb)","XMPP Exiba o valor do DND (Não Perturbe)"}. +{"XMPP Show Value of XA (Extended Away)","XMPP Exiba o valor do XA (Ausência Estendida)"}. +{"XMPP URI of Associated Publish-Subscribe Node","XMPP URI da publicação do nó associado da assinatura"}. +{"You are being removed from the room because of a system shutdown","Você está sendo removido da sala por causa do desligamento do sistema"}. +{"You are not allowed to send private messages","Você não tem permissão para enviar mensagens privadas"}. +{"You are not joined to the channel","Você não está inscrito no canal"}. +{"You can later change your password using an XMPP client.","Você pode alterar a sua senha mais tarde usando um cliente XMPP."}. {"You have been banned from this room","Você foi banido desta sala"}. +{"You have joined too many conferences","Você entrou em um número excessivo de salas de conferência"}. {"You must fill in field \"Nickname\" in the form","Você deve completar o campo \"Apelido\" no formulário"}. -{"You need a client that supports x:data and CAPTCHA to register","Você precisa de um cliente com suporte de x:data para poder registrar o nick"}. +{"You need a client that supports x:data and CAPTCHA to register","Você precisa de um cliente com suporte de x:data para poder registrar o apelido"}. {"You need a client that supports x:data to register the nickname","Você precisa de um cliente com suporte a x:data para registrar o seu apelido"}. -{"You need an x:data capable client to configure mod_irc settings","Necessitas um cliente com suporte de x:data para configurar as opções de mod_irc"}. -{"You need an x:data capable client to configure room","Necessitas um cliente com suporte de x:data para configurar a sala"}. {"You need an x:data capable client to search","Necessitas um cliente com suporte de x:data para poder buscar"}. -{"Your active privacy list has denied the routing of this stanza.","Sua lista de privacidade ativa negou o roteamento deste."}. -{"Your contact offline message queue is full. The message has been discarded.","Sua fila de mensagens offline esta cheia. Sua mensagem foi descartada"}. -{"Your Jabber account was successfully created.","Sua conta jabber foi criada com sucesso."}. -{"Your Jabber account was successfully deleted.","Sua conta Jabber foi deletada com sucesso."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","Suas mensagens para ~s estão bloqueadas. Para desbloqueá-las, visite: ~s"}. +{"Your active privacy list has denied the routing of this stanza.","Sua lista de privacidade ativa negou o roteamento desta instância."}. +{"Your contact offline message queue is full. The message has been discarded.","A fila de contatos offline esta cheia. A sua mensagem foi descartada."}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Suas mensagens para ~s estão bloqueadas. Para desbloqueá-las, visite: ~s"}. +{"Your XMPP account was successfully registered.","A sua conta XMPP foi registrada com sucesso."}. +{"Your XMPP account was successfully unregistered.","Sua conta XMPP foi excluída com sucesso."}. +{"You're not allowed to create nodes","Você não tem autorização para criar nós"}. diff --git a/priv/msgs/pt-br.po b/priv/msgs/pt-br.po deleted file mode 100644 index 6d2ae1c57..000000000 --- a/priv/msgs/pt-br.po +++ /dev/null @@ -1,1930 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: 2.1.0-alpha\n" -"Last-Translator: Victor Rodrigues\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Portuguese (Brazil)\n" -"X-Additional-Translator: Otávio Fernandes\n" -"X-Additional-Translator: Renato Botelho\n" -"X-Additional-Translator: Lucius Curado\n" -"X-Additional-Translator: Felipe Brito Vasconcellos\n" -"X-Additional-Translator: Victor Hugo dos Santos\n" - -#: ejabberd_c2s.erl:506 ejabberd_c2s.erl:854 -msgid "Use of STARTTLS required" -msgstr "É obrigatório uso de STARTTLS" - -#: ejabberd_c2s.erl:605 -msgid "No resource provided" -msgstr "Nenhum recurso foi informado" - -#: ejabberd_c2s.erl:1340 -msgid "Replaced by new connection" -msgstr "Substituído por nova conexão" - -#: ejabberd_c2s.erl:1344 mod_configure.erl:1851 mod_muc_log.erl:423 -#: mod_muc_log.erl:426 -msgid "has been kicked" -msgstr "foi removido" - -#: ejabberd_c2s.erl:2105 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "Sua lista de privacidade ativa negou o roteamento deste." - -#: ejabberd_c2s.erl:2420 -msgid "Too many unacked stanzas" -msgstr "número excessivo de instâncias sem confirmação" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Insira o texto que você vê" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "" -"Suas mensagens para ~s estão bloqueadas. Para desbloqueá-las, visite: ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "Se você não conseguir ver o CAPTCHA aqui, visite a web page." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "CAPTCHA web page" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "O CAPTCHA é inválido." - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Usuário" - -#: ejabberd_oauth.erl:256 -msgid "Server" -msgstr "Servidor" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1395 -#: mod_configure.erl:1482 mod_configure.erl:1886 mod_configure.erl:2120 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Senha" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "Aceito" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "Não Autorizado" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "ejabberd Web Admin" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Administração" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:193 -#: mod_configure.erl:529 -msgid "Access Control Lists" -msgstr "Listas de Controle de Acesso" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Submetido" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Formato incorreto" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Enviar" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "Intocado" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Remover os selecionados" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:195 -#: mod_configure.erl:530 -msgid "Access Rules" -msgstr "Regras de Acesso" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "Configuração da Regra de Acesso ~s" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "Hosts virtuais" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Usuários" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:521 -msgid "Online Users" -msgstr "Usuários conectados" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Últimas atividades dos usuários" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Período: " - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "Último mês" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "Último ano" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "Todas atividades" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Mostrar Tabela Ordinária" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Mostrar Tabela Integral" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:245 -msgid "Statistics" -msgstr "Estatísticas" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "Não encontrado" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Nó não encontrado" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Adicionar novo" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Máquina" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Usuários Registrados" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:174 mod_configure.erl:536 -#: mod_configure.erl:1383 -msgid "Add User" -msgstr "Adicionar usuário" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Mensagens offline" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Última atividade" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1913 -msgid "Never" -msgstr "Nunca" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1923 -msgid "Online" -msgstr "Conectado" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Usuários registrados" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Usuários online" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Conexões que partem de s2s" - -#: ejabberd_web_admin.erl:1559 -msgid "Incoming s2s Connections:" -msgstr "Conexões que entram de s2s" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Nenhum" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Mudar senha" - -#: ejabberd_web_admin.erl:1673 -msgid "User ~s" -msgstr "Usuário ~s" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Recursos conectados:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Senha:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2114 -msgid "Remove User" -msgstr "Remover usuário" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Nenhum dado" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Nós" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:525 -msgid "Running Nodes" -msgstr "Nós em execução" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:526 -msgid "Stopped Nodes" -msgstr "Nos parados" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -msgid "Node ~p" -msgstr "Nó ~p" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:147 mod_configure.erl:608 -msgid "Database" -msgstr "Base de dados" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:156 mod_configure.erl:645 -msgid "Backup" -msgstr "Salvar cópia de segurança" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Portas escutadas" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Atualizar" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Reiniciar" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Parar" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:610 mod_configure.erl:623 -msgid "Modules" -msgstr "Módulos" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "Erro de chamada RPC" - -#: ejabberd_web_admin.erl:1917 -msgid "Database Tables at ~p" -msgstr "Tabelas da Base de dados em ~p" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Nome" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Tipo de armazenamento" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Elementos" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Memória" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Erro" - -#: ejabberd_web_admin.erl:1955 -msgid "Backup of ~p" -msgstr "Backup de ~p" - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Observe que tais opções farão backup apenas da base de dados Mnesia. Caso " -"você esteja utilizando o modulo ODBC, você precisará fazer backup de sua " -"base de dados SQL separadamente." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Armazenar backup binário:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "OK" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Restaurar backup binário imediatamente" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"Restaurar backup binário após reinicialização do ejabberd (requer menos " -"memória):" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Armazenar backup em texto:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Restaurar backup formato texto imediatamente:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "Importar usuários de um arquivo PIEFXIS (XEP-0227): " - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" -"Exportar todos os dados de todos os usuários no servidor, para arquivos " -"formato PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "" -"Exportar dados dos usuários em um host, para arquivos PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "Exportar todas as tabelas como SQL para um arquivo:" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Importar dados dos usuários de uma fila jabberd14:" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Importar dados dos usuários de um diretório-fila jabberd14:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Portas abertas em " - -#: ejabberd_web_admin.erl:2144 -msgid "Modules at ~p" -msgstr "Módulos em ~p" - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "Estatísticas de ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Uptime:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "Tempo de CPU" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Transações salvas:" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Transações abortadas:" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Transações reiniciadas:" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Transações de log:" - -#: ejabberd_web_admin.erl:2243 -msgid "Update ~p" -msgstr "Atualizar ~p" - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "Plano de Atualização" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Módulos atualizados" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Script de atualização" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Script de atualização low level" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Verificação de Script" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Porta" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Porta" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Módulo" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Opções" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Iniciar" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Comandos" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "Deletar realmente a mensagem do dia?" - -#: mod_announce.erl:536 mod_configure.erl:1235 mod_configure.erl:1295 -msgid "Subject" -msgstr "Assunto" - -#: mod_announce.erl:544 mod_configure.erl:1241 mod_configure.erl:1301 -msgid "Message body" -msgstr "Corpo da mensagem" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "Nenhum corpo de texto fornecido para anunciar mensagem" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Anúncios" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Enviar anúncio a todos os usuários" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Enviar aviso para todos os usuários em todos os hosts" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Enviar anúncio a todos os usuárions online" - -#: mod_announce.erl:670 mod_configure.erl:1228 mod_configure.erl:1288 -msgid "Send announcement to all online users on all hosts" -msgstr "Enviar anúncio a todos usuários online em todas as máquinas" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Definir mensagem do dia e enviar a todos usuários online" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" -"Definir mensagem do dia em todos os hosts e enviar para os usuários online" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Atualizar mensagem do dia (não enviar)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "Atualizar a mensagem do dia em todos os host (não enviar)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Apagar mensagem do dia" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Apagar a mensagem do dia em todos os hosts" - -#: mod_configure.erl:137 mod_configure.erl:293 mod_configure.erl:315 -#: mod_configure.erl:519 -msgid "Configuration" -msgstr "Configuração" - -#: mod_configure.erl:150 mod_configure.erl:633 -msgid "Start Modules" -msgstr "Iniciar módulos" - -#: mod_configure.erl:153 mod_configure.erl:635 -msgid "Stop Modules" -msgstr "Parar módulos" - -#: mod_configure.erl:159 mod_configure.erl:647 -msgid "Restore" -msgstr "Restaurar" - -#: mod_configure.erl:162 mod_configure.erl:649 -msgid "Dump to Text File" -msgstr "Exportar para arquivo texto" - -#: mod_configure.erl:165 mod_configure.erl:660 -msgid "Import File" -msgstr "Importar arquivo" - -#: mod_configure.erl:168 mod_configure.erl:662 -msgid "Import Directory" -msgstr "Importar diretório" - -#: mod_configure.erl:170 mod_configure.erl:616 mod_configure.erl:1202 -msgid "Restart Service" -msgstr "Reiniciar Serviço" - -#: mod_configure.erl:172 mod_configure.erl:618 mod_configure.erl:1262 -msgid "Shut Down Service" -msgstr "Parar Serviço" - -#: mod_configure.erl:176 mod_configure.erl:537 mod_configure.erl:1416 -msgid "Delete User" -msgstr "Deletar Usuário" - -#: mod_configure.erl:178 mod_configure.erl:539 mod_configure.erl:1434 -msgid "End User Session" -msgstr "Terminar Sessão do Usuário" - -#: mod_configure.erl:180 mod_configure.erl:541 mod_configure.erl:1452 -#: mod_configure.erl:1470 -msgid "Get User Password" -msgstr "Obter Senha do Usuário" - -#: mod_configure.erl:182 mod_configure.erl:543 -msgid "Change User Password" -msgstr "Alterar Senha do Usuário" - -#: mod_configure.erl:184 mod_configure.erl:545 mod_configure.erl:1497 -msgid "Get User Last Login Time" -msgstr "Obter a Data do Último Login" - -#: mod_configure.erl:186 mod_configure.erl:547 mod_configure.erl:1514 -msgid "Get User Statistics" -msgstr "Obter Estatísticas do Usuário" - -#: mod_configure.erl:188 mod_configure.erl:549 -msgid "Get Number of Registered Users" -msgstr "Obter Número de Usuários Registrados" - -#: mod_configure.erl:191 mod_configure.erl:551 -msgid "Get Number of Online Users" -msgstr "Obter Número de Usuários Online" - -#: mod_configure.erl:317 mod_configure.erl:520 -msgid "User Management" -msgstr "Gerenciamento de Usuários" - -#: mod_configure.erl:522 -msgid "All Users" -msgstr "Todos os usuários" - -#: mod_configure.erl:523 -msgid "Outgoing s2s Connections" -msgstr "Conexões que partam de s2s" - -#: mod_configure.erl:612 -msgid "Backup Management" -msgstr "Gestão de Backup" - -#: mod_configure.erl:614 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Importar usuários de arquivos jabberd14 (spool files)" - -#: mod_configure.erl:759 -msgid "To ~s" -msgstr "Para ~s" - -#: mod_configure.erl:779 -msgid "From ~s" -msgstr "De ~s" - -#: mod_configure.erl:999 -msgid "Database Tables Configuration at " -msgstr "Configuração de Tabelas de Base de dados em " - -#: mod_configure.erl:1005 -msgid "Choose storage type of tables" -msgstr "Selecione o tipo de armazenamento das tabelas" - -#: mod_configure.erl:1014 mod_configure.erl:1016 -msgid "Disc only copy" -msgstr "Somente cópia em disco" - -#: mod_configure.erl:1014 mod_configure.erl:1016 -msgid "RAM and disc copy" -msgstr "Cópias na RAM e disco rígido" - -#: mod_configure.erl:1014 mod_configure.erl:1016 -msgid "RAM copy" -msgstr "Cópia em RAM" - -#: mod_configure.erl:1014 mod_configure.erl:1016 -msgid "Remote copy" -msgstr "Cópia remota" - -#: mod_configure.erl:1042 -msgid "Stop Modules at " -msgstr "Parar módulos em " - -#: mod_configure.erl:1048 -msgid "Choose modules to stop" -msgstr "Selecione módulos a parar" - -#: mod_configure.erl:1069 -msgid "Start Modules at " -msgstr "Iniciar módulos em " - -#: mod_configure.erl:1075 -msgid "Enter list of {Module, [Options]}" -msgstr "Introduza lista de {módulo, [opções]}" - -#: mod_configure.erl:1077 -msgid "List of modules to start" -msgstr "Listas de módulos para inicializar" - -#: mod_configure.erl:1091 -msgid "Backup to File at " -msgstr "Salvar backup para arquivo em " - -#: mod_configure.erl:1096 mod_configure.erl:1117 -msgid "Enter path to backup file" -msgstr "Introduza o caminho do arquivo de backup" - -#: mod_configure.erl:1097 mod_configure.erl:1118 mod_configure.erl:1139 -#: mod_configure.erl:1160 -msgid "Path to File" -msgstr "Caminho do arquivo" - -#: mod_configure.erl:1112 -msgid "Restore Backup from File at " -msgstr "Restaurar backup a partir do arquivo em " - -#: mod_configure.erl:1133 -msgid "Dump Backup to Text File at " -msgstr "Exportar backup para texto em " - -#: mod_configure.erl:1138 -msgid "Enter path to text file" -msgstr "Introduza caminho para o arquivo texto" - -#: mod_configure.erl:1153 -msgid "Import User from File at " -msgstr "Importar usuário a partir do arquivo em " - -#: mod_configure.erl:1159 -msgid "Enter path to jabberd14 spool file" -msgstr "Insira o caminho para a fila (arquivo) do jabberd14" - -#: mod_configure.erl:1174 -msgid "Import Users from Dir at " -msgstr "Importar usuários a partir do diretório em " - -#: mod_configure.erl:1180 -msgid "Enter path to jabberd14 spool dir" -msgstr "Introduza o caminho para o diretório de fila do jabberd14" - -#: mod_configure.erl:1181 -msgid "Path to Dir" -msgstr "Caminho para o diretório" - -#: mod_configure.erl:1206 mod_configure.erl:1266 -msgid "Time delay" -msgstr "Intervalo (Tempo)" - -#: mod_configure.erl:1313 -msgid "Access Control List Configuration" -msgstr "Configuração da Lista de Controle de Acesso" - -#: mod_configure.erl:1318 -msgid "Access control lists" -msgstr "Listas de Controle de Acesso" - -#: mod_configure.erl:1349 -msgid "Access Configuration" -msgstr "Configuração de Acesso" - -#: mod_configure.erl:1353 -msgid "Access rules" -msgstr "Regras de acesso" - -#: mod_configure.erl:1387 mod_configure.erl:1420 mod_configure.erl:1438 -#: mod_configure.erl:1456 mod_configure.erl:1474 mod_configure.erl:1501 -#: mod_configure.erl:1518 mod_configure.erl:1884 mod_configure.erl:1931 -#: mod_configure.erl:1958 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "ID Jabber" - -#: mod_configure.erl:1404 -msgid "Password Verification" -msgstr "Verificação de Senha" - -#: mod_configure.erl:1537 -msgid "Number of registered users" -msgstr "Número de usuários registrados" - -#: mod_configure.erl:1556 -msgid "Number of online users" -msgstr "Número de usuários online" - -#: mod_configure.erl:1933 -msgid "Last login" -msgstr "Último login" - -#: mod_configure.erl:1960 -msgid "Roster size" -msgstr "Tamanho da Lista" - -#: mod_configure.erl:1962 -msgid "IP addresses" -msgstr "Endereços IP" - -#: mod_configure.erl:1964 -msgid "Resources" -msgstr "Recursos" - -#: mod_configure.erl:2092 -msgid "Administration of " -msgstr "Administração de " - -#: mod_configure.erl:2097 -msgid "Action on user" -msgstr "Ação no usuário" - -#: mod_configure.erl:2105 -msgid "Edit Properties" -msgstr "Editar propriedades" - -#: mod_fail2ban.erl:95 -msgid "" -"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" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "Por favor informe o tamanho do arquivo." - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "Por favor informe o nome do arquivo." - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "Este endereço IP está bloqueado em ~s" - -#: mod_irc.erl:220 mod_muc.erl:455 -msgid "Access denied by service policy" -msgstr "Acesso negado pela política do serviço" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "Transporte IRC" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "Módulo de IRC para ejabberd" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "" -"Necessitas um cliente com suporte de x:data para configurar as opções de " -"mod_irc" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Registro em mod_irc para " - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Insira o nome de usuário, codificações, portas e senhas que você deseja para " -"usar nos servidores IRC" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "Usuário IRC" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Se você deseja especificar portas diferentes, senhas ou codifações para " -"servidores de IRC, complete esta lista com os valores no formato: " -"'{\"servidor IRC\", \"codificação\", porta, \"senha\"}'. Por padrão, este " -"serviço usa a codificação \"~s\", porta \"~p\", e senha em branco (vazia)" - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Exemplo: [{\"irc.teste.net\", \"koi8-r\"}, 6667, \"senha\"}, {\"dominio.foo." -"net\", \"iso8859-1\", 7000}, {\"irc.servidordeteste.net\", \"utf-8\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Parâmetros para as Conexões" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "Juntar-se ao canal IRC" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "Canal IRC (não coloque o #)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "Servidor IRC" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Aqui! Juntar-se ao canal IRC." - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Entrar no canal IRC, neste ID Jabber: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "Configurações do IRC" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Insira o nome de usuário e codificações que você deseja usar para conectar-" -"se aos servidores de IRC. Depois, presione 'Next' ('Próximo') para exibir " -"mais campos que devem ser preenchidos. Ao final, pressione " -"'Complete' ('Completar') para salvar a configuração." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "Usuário IRC" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Senha ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Porta ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Codificação para o servidor ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Servidor ~b" - -#: mod_mam.erl:542 -msgid "Only members may query archives of this room" -msgstr "Somente os membros podem procurar nos arquivos desta sala" - -#: mod_muc.erl:573 -msgid "Only service administrators are allowed to send service messages" -msgstr "" -"Apenas administradores possuem permissão para enviar mensagens de serviço" - -#: mod_muc.erl:610 -msgid "Room creation is denied by service policy" -msgstr "Sala não pode ser criada devido à política do serviço" - -#: mod_muc.erl:617 -msgid "Conference room does not exist" -msgstr "A sala de conferência não existe" - -#: mod_muc.erl:728 mod_muc_admin.erl:319 -msgid "Chatrooms" -msgstr "Salas de Chat" - -#: mod_muc.erl:769 -msgid "Empty Rooms" -msgstr "Salas vazias" - -#: mod_muc.erl:921 -msgid "You need a client that supports x:data to register the nickname" -msgstr "" -"Você precisa de um cliente com suporte a x:data para registrar o seu apelido" - -#: mod_muc.erl:931 -msgid "Nickname Registration at " -msgstr "Registro do apelido em " - -#: mod_muc.erl:937 -msgid "Enter nickname you want to register" -msgstr "Introduza o apelido que quer registrar" - -#: mod_muc.erl:938 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Apelido" - -#: mod_muc.erl:1050 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "O nick já está registrado por outra pessoa" - -#: mod_muc.erl:1078 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Você deve completar o campo \"Apelido\" no formulário" - -#: mod_muc.erl:1101 -msgid "ejabberd MUC module" -msgstr "Módulo de MUC para ejabberd" - -#: mod_muc_admin.erl:229 mod_muc_admin.erl:232 mod_muc_admin.erl:244 -#: mod_muc_admin.erl:318 -msgid "Multi-User Chat" -msgstr "Chat multi-usuário" - -#: mod_muc_admin.erl:247 -msgid "Total rooms" -msgstr "Salas no total" - -#: mod_muc_admin.erl:248 -msgid "Permanent rooms" -msgstr "Salas permanentes" - -#: mod_muc_admin.erl:249 -msgid "Registered nicknames" -msgstr "Usuários registrados" - -#: mod_muc_admin.erl:252 -msgid "List of rooms" -msgstr "Lista de salas" - -#: mod_muc_log.erl:394 mod_muc_log.erl:403 -msgid "Chatroom configuration modified" -msgstr "Configuração da sala de bate-papo modificada" - -#: mod_muc_log.erl:406 -msgid "joins the room" -msgstr "Entrar na sala" - -#: mod_muc_log.erl:409 mod_muc_log.erl:412 -msgid "leaves the room" -msgstr "Sair da sala" - -#: mod_muc_log.erl:416 mod_muc_log.erl:419 -msgid "has been banned" -msgstr "foi banido" - -#: mod_muc_log.erl:431 -msgid "has been kicked because of an affiliation change" -msgstr "foi desconectado porque por afiliação inválida" - -#: mod_muc_log.erl:436 -msgid "has been kicked because the room has been changed to members-only" -msgstr "" -"foi desconectado porque a política da sala mudou, só membros são permitidos" - -#: mod_muc_log.erl:441 -msgid "has been kicked because of a system shutdown" -msgstr "foi desconectado porque o sistema foi desligado" - -#: mod_muc_log.erl:446 -msgid "is now known as" -msgstr "é agora conhecido como" - -#: mod_muc_log.erl:449 mod_muc_log.erl:788 -msgid " has set the subject to: " -msgstr " mudou o assunto para: " - -#: mod_muc_log.erl:489 -msgid "Chatroom is created" -msgstr "A sala de chat está criada" - -#: mod_muc_log.erl:491 -msgid "Chatroom is destroyed" -msgstr "A sala de chat está destruída" - -#: mod_muc_log.erl:493 -msgid "Chatroom is started" -msgstr "A sala de chat está iniciada" - -#: mod_muc_log.erl:495 -msgid "Chatroom is stopped" -msgstr "A sala de chat está parada" - -#: mod_muc_log.erl:499 -msgid "Monday" -msgstr "Segunda" - -#: mod_muc_log.erl:500 -msgid "Tuesday" -msgstr "Terça" - -#: mod_muc_log.erl:501 -msgid "Wednesday" -msgstr "Quarta" - -#: mod_muc_log.erl:502 -msgid "Thursday" -msgstr "Quinta" - -#: mod_muc_log.erl:503 -msgid "Friday" -msgstr "Sexta" - -#: mod_muc_log.erl:504 -msgid "Saturday" -msgstr "Sábado" - -#: mod_muc_log.erl:505 -msgid "Sunday" -msgstr "Domingo" - -#: mod_muc_log.erl:509 -msgid "January" -msgstr "Janeiro" - -#: mod_muc_log.erl:510 -msgid "February" -msgstr "Fevereiro" - -#: mod_muc_log.erl:511 -msgid "March" -msgstr "Março" - -#: mod_muc_log.erl:512 -msgid "April" -msgstr "Abril" - -#: mod_muc_log.erl:513 -msgid "May" -msgstr "Maio" - -#: mod_muc_log.erl:514 -msgid "June" -msgstr "Junho" - -#: mod_muc_log.erl:515 -msgid "July" -msgstr "Julho" - -#: mod_muc_log.erl:516 -msgid "August" -msgstr "Agosto" - -#: mod_muc_log.erl:517 -msgid "September" -msgstr "Setembro" - -#: mod_muc_log.erl:518 -msgid "October" -msgstr "Outubro" - -#: mod_muc_log.erl:519 -msgid "November" -msgstr "Novembro" - -#: mod_muc_log.erl:520 -msgid "December" -msgstr "Dezembro" - -#: mod_muc_log.erl:908 -msgid "Room Configuration" -msgstr "Configuração de salas" - -#: mod_muc_log.erl:928 -msgid "Room Occupants" -msgstr "Número de participantes" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "Limite de banda excedido" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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\")." - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "Impedir o envio de mensagens privadas para a sala" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "Por favor, espere antes de enviar uma nova requisição de voz" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "Requisições de voz estão desabilitadas nesta conferência" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "Não foi possível extrair o JID (Jabber ID) da requisição de voz" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "Somente moderadores podem aprovar requisições de voz" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Tipo de mensagem incorreto" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "Não é permitido enviar mensagens privadas do tipo \"groupchat\"" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "O receptor não está na sala de conferência" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "Não é permitido enviar mensagens privadas" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Somente os ocupantes podem enviar mensagens à sala" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "Somente os ocupantes podem enviar consultas à sala" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "Nesta sala não se permite consultas aos membros da sala" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "" -"Somente os moderadores e os participamentes podem alterar o assunto desta " -"sala" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Somente os moderadores podem alterar o assunto desta sala" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "Os visitantes não podem enviar mensagens a todos os ocupantes" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "Nesta sala, os visitantes não podem mudar seus apelidos" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "O apelido (nick) já está sendo utilizado" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Você foi banido desta sala" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "Necessitas ser membro desta sala para poder entrar" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "Se necessita senha para entrar nesta sala" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "Número excessivo de requisições para o CAPTCHA" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "Impossível gerar um CAPTCHA" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Senha incorreta" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "Se necessita privilégios de administrador" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "Se necessita privilégios de moderador" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "O Jabber ID ~s não es válido" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "O nick ~s não existe na sala" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Afiliação não válida: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Cargo (role) é não válido: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "Se requer privilégios de proprietário da sala" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "Configuração para ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Título da sala" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "Descrição da Sala" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Tornar sala persistente" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Tornar sala pública possível de ser encontrada" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "Tornar pública a lista de participantes" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Tornar sala protegida à senha" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Número máximo de participantes" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Ilimitado" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Tornar o Jabber ID real visível por" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "apenas moderadores" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "qualquer um" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "Para quem a presença será notificada" - -#: mod_muc_room.erl:3486 -msgid "Moderator" -msgstr "Moderador" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "Participante" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "Visitante" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Tornar sala apenas para membros" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Tornar a sala moderada" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Usuários padrões como participantes" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Permitir a usuários modificar o assunto" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Permitir a usuários enviarem mensagens privadas" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "Permitir visitantes enviar mensagem privada para" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "ninguém" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Permitir a usuários pesquisar informações sobre os demais" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Permitir a usuários envio de convites" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "Permitir atualizações de status aos visitantes" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Permitir mudança de apelido aos visitantes" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "Permitir aos visitantes o envio de requisições de voz" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "O intervalo mínimo entre requisições de voz (em segundos)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "Tornar protegida a senha da sala" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "Habilitar arquivamento de mensagens" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "Excluir IDs Jabber de serem submetidos ao CAPTCHA" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Permitir criação de logs" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "Necessitas um cliente com suporte de x:data para configurar a sala" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Número de participantes" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "privado, " - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "Requisição de voz" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "Você deve aprovar/desaprovar a requisição de voz." - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "Usuário JID" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "Dar voz a esta pessoa?" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s convidou você para a sala ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "a senha é" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "Multicast" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "ejabberd Multicast service" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "Sua fila de mensagens offline esta cheia. Sua mensagem foi descartada" - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "~s's Fila de Mensagens Offline" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Tempo" - -#: mod_offline.erl:812 -msgid "From" -msgstr "De" - -#: mod_offline.erl:813 -msgid "To" -msgstr "Para" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Pacote" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Mensagens offline" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "Remover Todas as Mensagens Offline" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "Modulo ejabberd SOCKS5 Bytestreams" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Publicação de Tópico" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "Módulo para Publicar Tópicos do ejabberd" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "PubSub requisição de assinante" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Aprovar esta assinatura." - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "ID do Tópico" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Endereço dos Assinantes" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "Autorizar este Jabber ID para a inscrição neste tópico pubsub?" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Enviar payloads junto com as notificações de eventos" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Entregar as notificações de evento" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Notificar subscritores quando cambia la configuração do nodo" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Notificar subscritores quando o nodo se elimine" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Notificar subscritores quando os elementos se eliminem do nodo" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Persistir elementos ao armazenar" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "Um nome familiar para o nó" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Máximo # de elementos que persistem" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Permitir subscrições" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Especificar os modelos de acesso" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Listar grupos autorizados" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Especificar o modelo do publicante" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "Descartar todos os itens quando o publicante principal estiver offline" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "Especificar o tipo de mensagem para o evento" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Máximo tamanho do payload em bytes" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "Quando enviar o último tópico publicado" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Somente enviar notificações aos usuários disponíveis" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "As coleções com as quais o nó está relacionado" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "A verificação do CAPTCHA falhou" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "" -"Você precisa de um cliente com suporte de x:data para poder registrar o nick" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "Escolha um nome de usuário e senha para registrar-se neste servidor" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "Senha considerada fraca'" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "Usuários não estão autorizados a registrar contas imediatamente" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "Sua conta jabber foi criada com sucesso." - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "Houve um erro ao criar esta conta: " - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "Sua conta Jabber foi deletada com sucesso." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "Houve um erro ao deletar esta conta: " - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "A senha da sua conta Jabber foi mudada com sucesso." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "Houve um erro ao mudar a senha: " - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Registros de Contas Jabber" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Registrar uma conta Jabber" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Deletar conta Jabber" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"Esta pagina aceita criações de novas contas Jabber neste servidor. A sua JID " -"(Identificador Jabber) será da seguinte forma: 'usuário@servidor'. Por " -"favor, leia cuidadosamente as instruções para preencher corretamente os " -"campos." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "Usuário:" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" -"Não é 'case insensitive': macbeth é o mesmo que MacBeth e ainda Macbeth. " - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "Caracteres não aceitos:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "Servidor:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" -"Não revele a sua senha a ninguém, nem mesmo para o administrador deste " -"servidor Jabber." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "Mais tarde você pode alterar a sua senha usando um cliente Jabber." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Alguns clientes jabber podem salvar a sua senha no seu computador. Use " -"recurso somente se você considera este computador seguro o suficiente." - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"Memorize a sua senha, ou escreva-a em um papel e guarde-o em um lugar " -"seguro. Jabber não é uma maneira automatizada para recuperar a sua senha, se " -"você a esquecer eventualmente." - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "Verificação de Senha" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "Registrar" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "Senha Antiga:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "Nova Senha:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "Esta página aceita para deletar uma conta Jabber neste servidor." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "Deletar registro" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Subscrição" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Pendente" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Grupos" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Validar" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Remover" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Lista de contatos de " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Adicionar ID jabber" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Lista de contatos" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Grupos Shared Roster" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Nome:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Descrição:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Membros:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Grupos Exibidos:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Grupo " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Servidor Jabber em Erlang" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Aniversário" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Cidade" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "País" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "Email" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Sobrenome" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Preencha o formulário para buscar usuários Jabber. Agrega * ao final de um " -"campo para buscar sub-palavras." - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Nome completo" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Nome do meio" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Nome da organização" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Departamento/Unidade" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Procurar usuários em " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "Necessitas um cliente com suporte de x:data para poder buscar" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "Busca de Usuário vCard" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "Módulo vCard para ejabberd" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Resultados de pesquisa para " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "Preencha campos para buscar usuários Jabber que concordem" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Servidores que partem de s2s" - -#~ msgid "Delete" -#~ msgstr "Eliminar" - -#~ msgid "This room is not anonymous" -#~ msgstr "Essa sala não é anônima" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "" -#~ "Este participante foi desconectado da sala de chat por ter enviado uma " -#~ "mensagem de erro." - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Este participante foi desconectado da sala de chat por ter enviado uma " -#~ "mensagem de erro para outro usuário." - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "" -#~ "Este participante foi desconectado da sala de chat por ter enviado uma " -#~ "notificação errônea de presença." - -#~ msgid "Captcha test failed" -#~ msgstr "O CAPTCHA é inválido." diff --git a/priv/msgs/pt.msg b/priv/msgs/pt.msg index f8be5a5d9..358eb4858 100644 --- a/priv/msgs/pt.msg +++ b/priv/msgs/pt.msg @@ -1,135 +1,630 @@ -%% -*- coding: latin-1 -*- -{"Access Configuration","Configuração de acessos"}. -{"Access Control List Configuration","Configuração da Lista de Controlo de Acesso"}. -{"Access control lists","Listas de Controlo de Acesso"}. -{"Access Control Lists","Listas de Controlo de Acesso"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" (Add * to the end of field to match substring)"," (Adicione * no final do campo para combinar com a sub-cadeia)"}. +{" has set the subject to: "," colocou o tópico: "}. +{"# participants","# participantes"}. +{"A description of the node","Uma descrição do nó"}. +{"A friendly name for the node","Um nome familiar para o nó"}. +{"A password is required to enter this room","Se necessita palavra-passe para entrar nesta sala"}. +{"A Web Page","Uma página da web"}. +{"Accept","Aceito"}. {"Access denied by service policy","Acesso negado pela política de serviço"}. -{"Access rules","Regras de acesso"}. -{"Access Rules","Regras de Acesso"}. +{"Access model","Modelo de acesso"}. +{"Account doesn't exist","A conta não existe"}. {"Action on user","Acção no utilizador"}. -{"Add New","Adicionar novo"}. +{"Add a hat to a user","Adiciona um chapéu num utilizador"}. {"Add User","Adicionar utilizador"}. {"Administration of ","Administração de "}. +{"Administration","Administração"}. {"Administrator privileges required","São necessários privilégios de administrador"}. +{"All activity","Todas atividades"}. {"All Users","Todos os utilizadores"}. -{"Backup","Guardar cópia de segurança"}. +{"Allow subscription","Permitir a assinatura"}. +{"Allow this Jabber ID to subscribe to this pubsub node?","Autorizar este Jabber ID para a inscrição neste tópico pubsub?"}. +{"Allow this person to register with the room?","Permita que esta pessoa se registe na sala?"}. +{"Allow users to change the subject","Permitir a utilizadores modificar o assunto"}. +{"Allow users to query other users","Permitir a utilizadores pesquisar informações sobre os demais"}. +{"Allow users to send invites","Permitir a utilizadores envio de convites"}. +{"Allow users to send private messages","Permitir a utilizadores enviarem mensagens privadas"}. +{"Allow visitors to change nickname","Permitir mudança de apelido aos visitantes"}. +{"Allow visitors to send private messages to","Permitir visitantes enviar mensagem privada para"}. +{"Allow visitors to send status text in presence updates","Permitir atualizações de estado aos visitantes"}. +{"Allow visitors to send voice requests","Permitir aos visitantes o envio de requisições de voz"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","Um grupo LDAP associado que define a adesão à sala; este deve ser um Nome Distinto LDAP de acordo com uma definição específica da implementação ou da implantação específica de um grupo."}. +{"Announcements","Anúncios"}. +{"Answer associated with a picture","Resposta associada com uma foto"}. +{"Answer associated with a video","Resposta associada com um vídeo"}. +{"Answer associated with speech","Resposta associada com a fala"}. +{"Answer to a question","Resposta para uma pergunta"}. +{"Anyone in the specified roster group(s) may subscribe and retrieve items","Qualquer pessoa do(s) grupo(s) informado(s) podem se inscrever e recuperar itens"}. +{"Anyone may associate leaf nodes with the collection","Qualquer pessoa pode associar nós das páginas à coleção"}. +{"Anyone may publish","Qualquer pessoa pode publicar"}. +{"Anyone may subscribe and retrieve items","Qualquer pessoa pode se inscrever e recuperar os itens"}. +{"Anyone with a presence subscription of both or from may subscribe and retrieve items","Qualquer pessoa com uma assinatura presente dos dois ou de ambos pode se inscrever e recuperar os itens"}. +{"Anyone with Voice","Qualquer pessoa com voz"}. +{"Anyone","Qualquer pessoa"}. +{"API Commands","Comandos API"}. +{"April","Abril"}. +{"Arguments","Argumentos"}. +{"Attribute 'channel' is required for this request","O atributo 'canal' é necessário para esta solicitação"}. +{"Attribute 'id' is mandatory for MIX messages","O atributo 'id' é obrigatório para mensagens MIX"}. +{"Attribute 'jid' is not allowed here","O atributo 'jid' não é permitido aqui"}. +{"Attribute 'node' is not allowed here","O Atributo 'nó' não é permitido aqui"}. +{"Attribute 'to' of stanza that triggered challenge","O atributo 'para' da estrofe que desencadeou o desafio"}. +{"August","Agosto"}. +{"Automatic node creation is not enabled","Criação automatizada de nós está desativada"}. {"Backup Management","Gestão de cópias de segurança"}. +{"Backup of ~p","Backup de ~p"}. {"Backup to File at ","Guardar cópia de segurança para ficheiro em "}. +{"Backup","Guardar cópia de segurança"}. +{"Bad format","Formato incorreto"}. {"Birthday","Data de nascimento"}. +{"Both the username and the resource are required","Nome de utilizador e recurso são necessários"}. +{"Bytestream already activated","Bytestream já foi ativado"}. +{"Cannot remove active list","Não é possível remover uma lista ativa"}. +{"Cannot remove default list","Não é possível remover uma lista padrão"}. +{"CAPTCHA web page","CAPTCHA web page"}. +{"Challenge ID","ID do desafio"}. {"Change Password","Mudar palavra-chave"}. +{"Change User Password","Alterar Palavra-passe do Utilizador"}. +{"Changing password is not allowed","Não é permitida a alteração da palavra-passe"}. +{"Changing role/affiliation is not allowed","Não é permitida a alteração da função/afiliação"}. +{"Channel already exists","O canal já existe"}. +{"Channel does not exist","O canal não existe"}. +{"Channel JID","Canal JID"}. +{"Channels","Canais"}. +{"Characters not allowed:","Caracteres não aceitos:"}. +{"Chatroom configuration modified","Configuração da sala de bate-papo modificada"}. +{"Chatroom is created","A sala de chat está criada"}. +{"Chatroom is destroyed","A sala de chat está destruída"}. +{"Chatroom is started","A sala de chat está iniciada"}. +{"Chatroom is stopped","A sala de chat está parada"}. +{"Chatrooms","Salas de Chat"}. {"Choose a username and password to register with this server","Escolha um nome de utilizador e palavra-chave para se registar neste servidor"}. -{"Choose modules to stop","Seleccione os módulos a parar"}. {"Choose storage type of tables","Seleccione o tipo de armazenagem das tabelas"}. +{"Choose whether to approve this entity's subscription.","Aprovar esta assinatura."}. {"City","Cidade"}. +{"Client acknowledged more stanzas than sent by server","O cliente reconheceu mais estrofes do que as enviadas pelo servidor"}. +{"Clustering","Agrupamento"}. +{"Commands","Comandos"}. {"Conference room does not exist","A sala não existe"}. +{"Configuration of room ~s","Configuração para ~s"}. {"Configuration","Configuração"}. -{"Connected Resources:","Recursos conectados:"}. +{"Contact Addresses (normally, room owner or owners)","Endereços de contato (normalmente, o proprietário ou os proprietários da sala)"}. {"Country","País"}. -{"Delete Selected","Eliminar os seleccionados"}. +{"Current Discussion Topic","Assunto em discussão"}. +{"Database failure","Falha no banco de dados"}. +{"Database Tables Configuration at ","Configuração de Tabelas de Base de dados em "}. +{"Database","Base de dados"}. +{"December","Dezembro"}. +{"Default users as participants","Utilizadores padrões como participantes"}. +{"Delete message of the day on all hosts","Apagar a mensagem do dia em todos os hosts"}. +{"Delete message of the day","Apagar mensagem do dia"}. +{"Delete User","Deletar Utilizador"}. +{"Deliver event notifications","Entregar as notificações de evento"}. +{"Deliver payloads with event notifications","Enviar payloads junto com as notificações de eventos"}. {"Disc only copy","Cópia apenas em disco"}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","Não revele a sua palavra-passe a ninguém, nem mesmo para o administrador deste servidor XMPP."}. {"Dump Backup to Text File at ","Exporta cópia de segurança para ficheiro de texto em "}. {"Dump to Text File","Exportar para ficheiro de texto"}. +{"Duplicated groups are not allowed by RFC6121","Os grupos duplicados não são permitidos pela RFC6121"}. +{"Dynamically specify a replyto of the item publisher","Definir de forma dinâmica uma resposta da editora do item"}. {"Edit Properties","Editar propriedades"}. -{"ejabberd IRC module","Módulo de IRC ejabberd"}. +{"Either approve or decline the voice request.","Deve aprovar/desaprovar a requisição de voz."}. +{"ejabberd HTTP Upload service","serviço HTTP de upload ejabberd"}. {"ejabberd MUC module","Módulo MUC de ejabberd"}. +{"ejabberd Multicast service","Serviço multicast ejabberd"}. +{"ejabberd Publish-Subscribe module","Módulo para Publicar Tópicos do ejabberd"}. +{"ejabberd SOCKS5 Bytestreams module","Modulo ejabberd SOCKS5 Bytestreams"}. {"ejabberd vCard module","Módulo vCard de ejabberd"}. -{"Enter list of {Module, [Options]}","Introduza lista de {módulos, [opções]}"}. +{"ejabberd Web Admin","ejabberd Web Admin"}. +{"ejabberd","ejabberd"}. +{"Email Address","Endereço de e-mail"}. +{"Email","Email"}. +{"Enable hats","Ativa chapéus"}. +{"Enable logging","Permitir criação de logs"}. +{"Enable message archiving","Ativar arquivamento de mensagens"}. +{"Enabling push without 'node' attribute is not supported","Abilitar push sem o atributo 'node' não é suportado"}. +{"End User Session","Terminar Sessão do Utilizador"}. {"Enter nickname you want to register","Introduza a alcunha que quer registar"}. {"Enter path to backup file","Introduza o caminho do ficheiro de cópia de segurança"}. {"Enter path to jabberd14 spool dir","Introduza o caminho para o directório de spools do jabberd14"}. {"Enter path to jabberd14 spool file","Introduza o caminho para o ficheiro de spool do jabberd14"}. {"Enter path to text file","Introduza caminho para o ficheiro de texto"}. -{"Erlang Jabber Server","Servidor Jabber em Erlang"}. +{"Enter the text you see","Insira o texto que vê"}. +{"Erlang XMPP Server","Servidor XMPP Erlang"}. +{"Exclude Jabber IDs from CAPTCHA challenge","Excluir IDs Jabber de serem submetidos ao CAPTCHA"}. +{"Export all tables as SQL queries to a file:","Exportar todas as tabelas como SQL para um ficheiro:"}. +{"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportar todos os dados de todos os utilizadores no servidor, para ficheiros de formato PIEFXIS (XEP-0227):"}. +{"Export data of users in a host to PIEFXIS files (XEP-0227):","Exportar dados dos utilizadores num host, para ficheiros de PIEFXIS (XEP-0227):"}. +{"External component failure","Falha de componente externo"}. +{"External component timeout","Tempo esgotado à espera de componente externo"}. +{"Failed to activate bytestream","Falha ao ativar bytestream"}. +{"Failed to extract JID from your voice request approval","Não foi possível extrair o JID (Jabber ID) da requisição de voz"}. +{"Failed to map delegated namespace to external component","Falha ao mapear namespace delegado ao componente externo"}. +{"Failed to parse HTTP response","Falha ao analisar resposta HTTP"}. +{"Failed to process option '~s'","Falha ao processar opção '~s'"}. {"Family Name","Apelido"}. -{"Fill in fields to search for any matching Jabber User","Preencha os campos para procurar utilizadores Jabber coincidentes"}. -{"From","De"}. -{"From ~s","De ~s"}. +{"FAQ Entry","Registo das perguntas frequentes"}. +{"February","Fevereiro"}. +{"File larger than ~w bytes","Ficheiro é maior que ~w bytes"}. +{"Fill in the form to search for any matching XMPP User","Preencha campos para procurar por quaisquer utilizadores XMPP"}. +{"Friday","Sexta"}. +{"From ~ts","De ~s"}. +{"Full List of Room Admins","Lista completa dos administradores das salas"}. +{"Full List of Room Owners","Lista completa dos proprietários das salas"}. {"Full Name","Nome completo"}. -{"Groups","Grupos"}. -{" has set the subject to: "," colocou o tópico: "}. +{"Get List of Online Users","Obter a lista de utilizadores online"}. +{"Get List of Registered Users","Obter a lista de utilizadores registados"}. +{"Get Number of Online Users","Obter quantidade de utilizadores online"}. +{"Get Number of Registered Users","Obter quantidade de utilizadores registados"}. +{"Get Pending","Obter os pendentes"}. +{"Get User Last Login Time","Obter a data do último login"}. +{"Get User Statistics","Obter estatísticas do utilizador"}. +{"Given Name","Sobrenome"}. +{"Grant voice to this person?","Dar voz a esta pessoa?"}. +{"has been banned","foi banido"}. +{"has been kicked because of a system shutdown","foi desconectado porque o sistema foi desligado"}. +{"has been kicked because of an affiliation change","foi desconectado porque por afiliação inválida"}. +{"has been kicked because the room has been changed to members-only","foi desconectado porque a política da sala mudou, só membros são permitidos"}. +{"has been kicked","foi removido"}. +{"Hash of the vCard-temp avatar of this room","Hash do avatar do vCard-temp desta sala"}. +{"Hat title","Título do chapéu"}. +{"Hat URI","URI do chapéu"}. +{"Hats limit exceeded","O limite dos chapéus foi excedido"}. +{"Host unknown","Máquina desconhecida"}. +{"HTTP File Upload","Upload de ficheiros por HTTP"}. +{"Idle connection","Conexão inativa"}. +{"If you don't see the CAPTCHA image here, visit the web page.","Se não conseguir ver o CAPTCHA aqui, visite a web page."}. {"Import Directory","Importar directório"}. {"Import File","Importar ficheiro"}. +{"Import user data from jabberd14 spool file:","Importar dados dos utilizadores de uma fila jabberd14:"}. {"Import User from File at ","Importar utilizador a partir do ficheiro em "}. +{"Import users data from a PIEFXIS file (XEP-0227):","Importe os utilizadores de um ficheiro PIEFXIS (XEP-0227):"}. +{"Import users data from jabberd14 spool directory:","Importar dados dos utilizadores de um diretório-fila jabberd14:"}. {"Import Users from Dir at ","Importar utilizadores a partir do directório em "}. +{"Import Users From jabberd14 Spool Files","Importar utilizadores de ficheiros de jabberd14 (spool files)"}. +{"Improper domain part of 'from' attribute","Atributo 'from' contém domínio incorreto"}. {"Improper message type","Tipo de mensagem incorrecto"}. +{"Incorrect CAPTCHA submit","CAPTCHA submetido incorretamente"}. +{"Incorrect data form","Formulário dos dados incorreto"}. {"Incorrect password","Palavra-chave incorrecta"}. -{"Invalid affiliation: ~s","Afiliação inválida: ~s"}. -{"Invalid role: ~s","Papel inválido: ~s"}. -{"IRC Username","Nome do utilizador de IRC"}. +{"Incorrect value of 'action' attribute","Valor incorreto do atributo 'action'"}. +{"Incorrect value of 'action' in data form","Valor incorreto de 'action' no formulário de dados"}. +{"Incorrect value of 'path' in data form","Valor incorreto de 'path' no formulário de dados"}. +{"Installed Modules:","Módulos instalados:"}. +{"Install","Instalar"}. +{"Insufficient privilege","Privilégio insuficiente"}. +{"Internal server error","Erro interno do servidor"}. +{"Invalid 'from' attribute in forwarded message","Atributo 'from' inválido na mensagem reenviada"}. +{"Invalid node name","Nome do nó inválido"}. +{"Invalid 'previd' value","Valor 'previd' inválido"}. +{"Invitations are not allowed in this conference","Os convites não são permitidos nesta conferência"}. +{"IP addresses","Endereços IP"}. +{"is now known as","é agora conhecido como"}. +{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Não é permitido o envio de mensagens de erro para a sala. O membro (~s) enviou uma mensagem de erro (~s) e foi expulso da sala"}. {"It is not allowed to send private messages of type \"groupchat\"","Não é permitido enviar mensagens privadas do tipo \"groupchat\""}. {"It is not allowed to send private messages to the conference","Impedir o envio de mensagens privadas para a sala"}. -{"Jabber ID ~s is invalid","O Jabber ID ~s não é válido"}. +{"Jabber ID","ID Jabber"}. +{"January","Janeiro"}. +{"JID normalization denied by service policy","Normalização JID negada por causa da política de serviços"}. +{"JID normalization failed","A normalização JID falhou"}. +{"Joined MIX channels of ~ts","Entrou no canais MIX do ~ts"}. +{"Joined MIX channels:","Uniu-se aos canais MIX:"}. +{"joins the room","Entrar na sala"}. +{"July","Julho"}. +{"June","Junho"}. +{"Just created","Acabou de ser criado"}. {"Last Activity","Última actividade"}. -{"Listened Ports at ","Portas em escuta em "}. -{"List of modules to start","Lista de módulos a iniciar"}. +{"Last login","Último login"}. +{"Last message","Última mensagem"}. +{"Last month","Último mês"}. +{"Last year","Último ano"}. +{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Bits menos significativos do hash sha-256 do texto devem ser iguais ao rótulo hexadecimal"}. +{"leaves the room","Sair da sala"}. +{"List of users with hats","Lista os utilizadores com chapéus"}. +{"List users with hats","Lista os utilizadores com chapéus"}. +{"Logged Out","Desconectado"}. +{"Logging","Registando no log"}. +{"Make participants list public","Tornar pública a lista de participantes"}. +{"Make room CAPTCHA protected","Tornar protegida a palavra-passe da sala"}. +{"Make room members-only","Tornar sala apenas para membros"}. {"Make room moderated","Tornar a sala moderada"}. -{"Memory","Memória"}. +{"Make room password protected","Tornar sala protegida à palavra-passe"}. +{"Make room persistent","Tornar sala persistente"}. +{"Make room public searchable","Tornar sala pública possível de ser encontrada"}. +{"Malformed username","Nome de utilizador inválido"}. +{"MAM preference modification denied by service policy","Modificação de preferência MAM negada por causa da política de serviços"}. +{"March","Março"}. +{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","Máximo # de itens para persistir ou `max` para nenhum limite específico que não seja um servidor imposto como máximo"}. +{"Max payload size in bytes","Máximo tamanho do payload em bytes"}. +{"Maximum file size","Tamanho máximo do ficheiro"}. +{"Maximum Number of History Messages Returned by Room","Quantidade máxima das mensagens do histórico que foram devolvidas por sala"}. +{"Maximum number of items to persist","Quantidade máxima dos itens para manter"}. +{"Maximum Number of Occupants","Quantidate máxima de participantes"}. +{"May","Maio"}. +{"Membership is required to enter this room","É necessário ser membro desta sala para poder entrar"}. +{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Memorize a sua palavra-passe ou anote-a num papel guardado num local seguro. No XMPP, não há uma maneira automatizada de recuperar a sua palavra-passe caso a esqueça."}. +{"Mere Availability in XMPP (No Show Value)","Mera disponibilidade no XMPP (Sem valor para ser exibido)"}. +{"Message body","Corpo da mensagem"}. +{"Message not found in forwarded payload","Mensagem não encontrada em conteúdo encaminhado"}. +{"Messages from strangers are rejected","As mensagens vindas de estranhos são rejeitadas"}. +{"Messages of type headline","Mensagens do tipo do título"}. +{"Messages of type normal","Mensagens do tipo normal"}. {"Middle Name","Segundo nome"}. +{"Minimum interval between voice requests (in seconds)","O intervalo mínimo entre requisições de voz (em segundos)"}. {"Moderator privileges required","São necessários privilégios de moderador"}. -{"Module","Módulo"}. -{"Modules","Módulos"}. +{"Moderator","Moderador"}. +{"Moderators Only","Somente moderadores"}. +{"Module failed to handle the query","Módulo falhou ao processar a consulta"}. +{"Monday","Segunda"}. +{"Multicast","Multicast"}. +{"Multiple elements are not allowed by RFC6121","Vários elementos não são permitidos pela RFC6121"}. +{"Multi-User Chat","Chat multi-utilizador"}. {"Name","Nome"}. +{"Natural Language for Room Discussions","Idioma nativo para as discussões na sala"}. +{"Natural-Language Room Name","Nome da sala no idioma nativo"}. +{"Neither 'jid' nor 'nick' attribute found","Nem o atributo 'jid' nem 'nick' foram encontrados"}. +{"Neither 'role' nor 'affiliation' attribute found","Nem o atributo 'role' nem 'affiliation' foram encontrados"}. {"Never","Nunca"}. -{"Nickname","Alcunha"}. +{"New Password:","Nova Palavra-passe:"}. +{"Nickname can't be empty","O apelido não pode ser vazio"}. {"Nickname Registration at ","Registo da alcunha em "}. {"Nickname ~s does not exist in the room","A alcunha ~s não existe na sala"}. +{"Nickname","Alcunha"}. +{"No address elements found","Nenhum elemento endereço foi encontrado"}. +{"No addresses element found","Nenhum elemento endereços foi encontrado"}. +{"No 'affiliation' attribute found","Atributo 'affiliation' não foi encontrado"}. +{"No available resource found","Nenhum recurso disponível foi encontrado"}. +{"No body provided for announce message","Nenhum corpo de texto fornecido para anunciar mensagem"}. +{"No child elements found","Nenhum elemento filho foi encontrado"}. +{"No data form found","Nenhum formulário de dados foi encontrado"}. +{"No Data","Nenhum dado"}. +{"No features available","Nenhuma funcionalidade disponível"}. +{"No element found","Nenhum elemento foi encontrado"}. +{"No hook has processed this command","Nenhum hook processou este comando"}. +{"No info about last activity found","Não foi encontrada informação sobre última atividade"}. +{"No 'item' element found","O elemento 'item' não foi encontrado"}. +{"No items found in this query","Nenhum item encontrado nesta consulta"}. +{"No limit","Ilimitado"}. +{"No module is handling this query","Nenhum módulo está processando esta consulta"}. +{"No node specified","Nenhum nó especificado"}. +{"No 'password' found in data form","'password' não foi encontrado em formulário de dados"}. +{"No 'password' found in this query","Nenhuma 'palavra-passe' foi encontrado nesta consulta"}. +{"No 'path' found in data form","'path' não foi encontrado em formulário de dados"}. +{"No pending subscriptions found","Não foram encontradas subscrições"}. +{"No privacy list with this name found","Nenhuma lista de privacidade encontrada com este nome"}. +{"No private data found in this query","Nenhum dado privado encontrado nesta consulta"}. +{"No running node found","Nenhum nó em execução foi encontrado"}. +{"No services available","Não há serviços disponíveis"}. +{"No statistics found for this item","Não foram encontradas estatísticas para este item"}. +{"No 'to' attribute found in the invitation","Atributo 'to' não foi encontrado no convite"}. +{"Nobody","Ninguém"}. +{"Node already exists","Nó já existe"}. +{"Node ID","ID do Tópico"}. +{"Node index not found","O índice do nó não foi encontrado"}. {"Node not found","Nodo não encontrado"}. +{"Node ~p","Nó ~p"}. +{"Node","Nó"}. +{"Nodeprep has failed","Processo de identificação de nó falhou (nodeprep)"}. {"Nodes","Nodos"}. {"None","Nenhum"}. -{"No resource provided","Não foi passado nenhum recurso"}. +{"Not allowed","Não é permitido"}. +{"Not Found","Não encontrado"}. +{"Not subscribed","Não subscrito"}. +{"Notify subscribers when items are removed from the node","Notificar assinantes quando itens forem eliminados do nó"}. +{"Notify subscribers when the node configuration changes","Notificar assinantes a configuração do nó mudar"}. +{"Notify subscribers when the node is deleted","Notificar assinantes quando o nó for eliminado se elimine"}. +{"November","Novembro"}. +{"Number of answers required","Quantidade de respostas necessárias"}. +{"Number of occupants","Quantidade de participantes"}. +{"Number of Offline Messages","Quantidade das mensagens offline"}. +{"Number of online users","Quantidade de utilizadores online"}. +{"Number of registered users","Quantidade de utilizadores registados"}. +{"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","Quantidade de segundos após limpar automaticamente os itens ou `max` para nenhum limite específico que não seja um servidor imposto máximo"}. +{"Occupants are allowed to invite others","As pessoas estão autorizadas a convidar outras pessoas"}. +{"Occupants are allowed to query others","Os ocupantes estão autorizados a consultar os outros"}. +{"Occupants May Change the Subject","As pessoas talvez possam alterar o assunto"}. +{"October","Outubro"}. {"OK","OK"}. -{"Online","Ligado"}. +{"Old Password:","Palavra-passe Antiga:"}. {"Online Users","Utilizadores ligados"}. +{"Online","Ligado"}. +{"Only collection node owners may associate leaf nodes with the collection","Apenas um grupo dos proprietários dos nós podem associar as páginas na coleção"}. +{"Only deliver notifications to available users","Somente enviar notificações aos utilizadores disponíveis"}. +{"Only or tags are allowed","Apenas tags ou são permitidas"}. +{"Only element is allowed in this query","Apenas elemento é permitido nesta consulta"}. +{"Only members may query archives of this room","Somente os membros podem procurar nos arquivos desta sala"}. +{"Only moderators and participants are allowed to change the subject in this room","Somente os moderadores e os participamentes podem alterar o assunto desta sala"}. +{"Only moderators are allowed to change the subject in this room","Somente os moderadores podem alterar o assunto desta sala"}. +{"Only moderators are allowed to retract messages","Apenas moderadores estão autorizados de retirar mensagens"}. +{"Only moderators can approve voice requests","Somente moderadores podem aprovar requisições de voz"}. {"Only occupants are allowed to send messages to the conference","Só os ocupantes podem enviar mensagens para a sala"}. {"Only occupants are allowed to send queries to the conference","Só os ocupantes podem enviar consultas para a sala"}. +{"Only publishers may publish","Apenas os editores podem publicar"}. {"Only service administrators are allowed to send service messages","Só os administradores do serviço têm permissão para enviar mensagens de serviço"}. -{"Options","Opções"}. +{"Only those on a whitelist may associate leaf nodes with the collection","Apenas aqueles presentes numa lista branca podem associar páginas na coleção"}. +{"Only those on a whitelist may subscribe and retrieve items","Apenas aqueles presentes numa lista branca podem se inscrever e recuperar os itens"}. {"Organization Name","Nome da organização"}. {"Organization Unit","Unidade da organização"}. +{"Other Modules Available:","Outros módulos disponíveis:"}. +{"Outgoing s2s Connections","Conexões s2s de Saída"}. {"Owner privileges required","São necessários privilégios de dono"}. -{"Packet","Pacote"}. -{"Password:","Palavra-chave:"}. +{"Packet relay is denied by service policy","A retransmissão de pacote é negada por causa da política de serviço"}. +{"Participant ID","ID do participante"}. +{"Participant","Participante"}. +{"Password Verification:","Verificação da Palavra-passe:"}. +{"Password Verification","Verificação de Palavra-passe"}. {"Password","Palavra-chave"}. +{"Password:","Palavra-chave:"}. {"Path to Dir","Caminho para o directório"}. {"Path to File","Caminho do ficheiro"}. -{"Pending","Pendente"}. -{"Port","Porta"}. -{"private, ","privado"}. +{"Payload semantic type information","Informações de tipo semântico de carga útil"}. +{"Period: ","Período: "}. +{"Persist items to storage","Persistir elementos ao armazenar"}. +{"Persistent","Persistente"}. +{"Ping query is incorrect","A consulta ping está incorreta"}. +{"Ping","Ping"}. +{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Observe que tais opções farão backup apenas da base de dados Mnesia. Caso esteja a utilizar o modulo ODBC, precisará fazer backup da sua base de dados SQL separadamente."}. +{"Please, wait for a while before sending new voice request","Por favor, espere antes de enviar uma nova requisição de voz"}. +{"Pong","Pong"}. +{"Possessing 'ask' attribute is not allowed by RFC6121","Possuir o atributo 'ask' não é permitido pela RFC6121"}. +{"Present real Jabber IDs to","Tornar o Jabber ID real visível por"}. +{"Previous session not found","A sessão anterior não foi encontrada"}. +{"Previous session PID has been killed","O PID da sessão anterior foi excluído"}. +{"Previous session PID has exited","O PID da sessão anterior foi encerrado"}. +{"Previous session PID is dead","O PID da sessão anterior está morto"}. +{"Previous session timed out","A sessão anterior expirou"}. +{"private, ","privado, "}. +{"Public","Público"}. +{"Publish model","Publicar o modelo"}. +{"Publish-Subscribe","Publicação de Tópico"}. +{"PubSub subscriber request","PubSub requisição de assinante"}. +{"Purge all items when the relevant publisher goes offline","Descartar todos os itens quando o publicante principal estiver offline"}. +{"Push record not found","O registo push não foi encontrado"}. {"Queries to the conference members are not allowed in this room","Nesta sala não são permitidas consultas aos seus membros"}. +{"Query to another users is forbidden","Consultar a outro utilizador é proibido"}. {"RAM and disc copy","Cópia em RAM e em disco"}. {"RAM copy","Cópia em RAM"}. +{"Really delete message of the day?","Deletar realmente a mensagem do dia?"}. +{"Receive notification from all descendent nodes","Receba a notificação de todos os nós descendentes"}. +{"Receive notification from direct child nodes only","Receba apenas as notificações dos nós relacionados"}. +{"Receive notification of new items only","Receba apenas as notificações dos itens novos"}. +{"Receive notification of new nodes only","Receba apenas as notificações dos nós novos"}. {"Recipient is not in the conference room","O destinatário não está na sala"}. -{"Registration in mod_irc for ","Registo no mod_irc para"}. +{"Register an XMPP account","Registe uma conta XMPP"}. +{"Register","Registar"}. {"Remote copy","Cópia remota"}. -{"Remove","Remover"}. +{"Remove a hat from a user","Remove um chapéu de um utilizador"}. {"Remove User","Eliminar utilizador"}. -{"Restart","Reiniciar"}. +{"Replaced by new connection","Substituído por nova conexão"}. +{"Request has timed out","O pedido expirou"}. +{"Request is ignored","O pedido foi ignorado"}. +{"Requested role","Função solicitada"}. +{"Resources","Recursos"}. +{"Restart Service","Reiniciar Serviço"}. {"Restore Backup from File at ","Restaura cópia de segurança a partir do ficheiro em "}. +{"Restore binary backup after next ejabberd restart (requires less memory):","Restaurar backup binário após reinicialização do ejabberd (requer menos memória):"}. +{"Restore binary backup immediately:","Restaurar imediatamente o backup binário:"}. +{"Restore plain text backup immediately:","Restaurar backup formato texto imediatamente:"}. {"Restore","Restaurar"}. +{"Result","Resultado"}. +{"Roles and Affiliations that May Retrieve Member List","As funções e as afiliações que podem recuperar a lista dos membros"}. +{"Roles for which Presence is Broadcasted","Para quem a presença será notificada"}. +{"Roles that May Send Private Messages","Atribuições que talvez possam enviar mensagens privadas"}. +{"Room Configuration","Configuração de salas"}. +{"Room creation is denied by service policy","Sala não pode ser criada devido à política do serviço"}. +{"Room description","Descrição da Sala"}. +{"Room Occupants","Quantidade de participantes"}. +{"Room terminates","Terminação da sala"}. {"Room title","Título da sala"}. -{"Roster","Lista de contactos"}. -{"Roster of ","Lista de contactos de "}. +{"Roster groups allowed to subscribe","Listar grupos autorizados"}. +{"Roster size","Tamanho da Lista"}. {"Running Nodes","Nodos a correr"}. -{"~s access rule configuration","Configuração das Regra de Acesso ~s"}. +{"~s invites you to the room ~s","~s convidaram-o à sala ~s"}. +{"Saturday","Sábado"}. +{"Search from the date","Pesquise a partir da data"}. +{"Search Results for ","Resultados de pesquisa para "}. +{"Search the text","Pesquise o texto"}. +{"Search until the date","Pesquise até a data"}. {"Search users in ","Procurar utilizadores em "}. -{"Start Modules at ","Iniciar os módulos em "}. -{"Start Modules","Iniciar módulos"}. -{"Statistics","Estatísticas"}. -{"Stop Modules at ","Parar módulos em "}. -{"Stop Modules","Parar módulos"}. -{"Stop","Parar"}. +{"Send announcement to all online users on all hosts","Enviar anúncio a todos utilizadores online em todas as máquinas"}. +{"Send announcement to all online users","Enviar anúncio a todos os utilizadorns online"}. +{"Send announcement to all users on all hosts","Enviar aviso para todos os utilizadores em todos os hosts"}. +{"Send announcement to all users","Enviar anúncio a todos os utilizadores"}. +{"September","Setembro"}. +{"Server:","Servidor:"}. +{"Service list retrieval timed out","A recuperação da lista dos serviços expirou"}. +{"Session state copying timed out","A cópia do estado da sessão expirou"}. +{"Set message of the day and send to online users","Definir mensagem do dia e enviar a todos utilizadores online"}. +{"Set message of the day on all hosts and send to online users","Definir mensagem do dia em todos os hosts e enviar para os utilizadores online"}. +{"Shared Roster Groups","Grupos Shared Roster"}. +{"Show Integral Table","Mostrar Tabela Integral"}. +{"Show Occupants Join/Leave","Mostrar a entrada e a saída de ocupantes"}. +{"Show Ordinary Table","Mostrar Tabela Ordinária"}. +{"Shut Down Service","Parar Serviço"}. +{"SOCKS5 Bytestreams","Bytestreams SOCKS5"}. +{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Alguns clientes XMPP podem armazenar a sua palavra-passe no seu computador, só faça isso no seu computador particular por questões de segurança."}. +{"Sources Specs:","Especificações das fontes:"}. +{"Specify the access model","Especificar os modelos de acesso"}. +{"Specify the event message type","Especificar o tipo de mensagem para o evento"}. +{"Specify the publisher model","Especificar o modelo do publicante"}. +{"Stanza id is not valid","A Stanza ID é inválido"}. +{"Stanza ID","ID da estrofe"}. +{"Statically specify a replyto of the node owner(s)","Defina uma resposta fixa do(s) proprietário(s) do nó"}. {"Stopped Nodes","Nodos parados"}. -{"Storage Type","Tipo de armazenagem"}. -{"Submit","Enviar"}. -{"Subscription","Subscrição"}. -{"Time","Data"}. -{"To","Para"}. -{"To ~s","A ~s"}. -{"Update","Actualizar"}. +{"Store binary backup:","Armazenar backup binário:"}. +{"Store plain text backup:","Armazenar backup em texto:"}. +{"Stream management is already enabled","A gestão do fluxo já está ativada"}. +{"Stream management is not enabled","A gestão do fluxo não está ativada"}. +{"Subject","Assunto"}. +{"Submitted","Submetido"}. +{"Subscriber Address","Endereço dos Assinantes"}. +{"Subscribers may publish","Os assinantes podem publicar"}. +{"Subscription requests must be approved and only subscribers may retrieve items","Os pedidos de assinatura devem ser aprovados e apenas os assinantes podem recuperar os itens"}. +{"Subscriptions are not allowed","Subscrições não estão permitidas"}. +{"Sunday","Domingo"}. +{"Text associated with a picture","Um texto associado a uma imagem"}. +{"Text associated with a sound","Um texto associado a um som"}. +{"Text associated with a video","Um texto associado a um vídeo"}. +{"Text associated with speech","Um texto associado à fala"}. +{"That nickname is already in use by another occupant","O apelido (nick) já está a ser utilizado"}. +{"That nickname is registered by another person","O apelido já está registado por outra pessoa"}. +{"The account already exists","A conta já existe"}. +{"The account was not unregistered","A conta não estava não registada"}. +{"The body text of the last received message","O corpo do texto da última mensagem que foi recebida"}. +{"The CAPTCHA is valid.","O CAPTCHA é inválido."}. +{"The CAPTCHA verification has failed","A verificação do CAPTCHA falhou"}. +{"The captcha you entered is wrong","O captcha que digitou está errado"}. +{"The child nodes (leaf or collection) associated with a collection","Os nós relacionados (página ou coleção) associados com uma coleção"}. +{"The collections with which a node is affiliated","As coleções com as quais o nó está relacionado"}. +{"The DateTime at which a leased subscription will end or has ended","A data e a hora que uma assinatura alugada terminará ou terá terminado"}. +{"The datetime when the node was created","A data em que o nó foi criado"}. +{"The default language of the node","O idioma padrão do nó"}. +{"The feature requested is not supported by the conference","A funcionalidade solicitada não é suportada pela sala de conferência"}. +{"The JID of the node creator","O JID do criador do nó"}. +{"The JIDs of those to contact with questions","Os JIDs daqueles para entrar em contato com perguntas"}. +{"The JIDs of those with an affiliation of owner","Os JIDs daqueles com uma afiliação de proprietário"}. +{"The JIDs of those with an affiliation of publisher","Os JIDs daqueles com uma afiliação de editor"}. +{"The list of all online users","A lista de todos os utilizadores online"}. +{"The list of all users","A lista de todos os utilizadores"}. +{"The list of JIDs that may associate leaf nodes with a collection","A lista dos JIDs que podem associar as páginas dos nós numa coleção"}. +{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","A quantidade máxima de nós relacionados que podem ser associados a uma coleção ou `máximo` para nenhum limite específico que não seja um servidor imposto no máximo"}. +{"The minimum number of milliseconds between sending any two notification digests","A quantidade mínima de milissegundos entre o envio do resumo das duas notificações"}. +{"The name of the node","O nome do nó"}. +{"The node is a collection node","O nó é um nó da coleção"}. +{"The node is a leaf node (default)","O nó é uma página do nó (padrão)"}. +{"The NodeID of the relevant node","O NodeID do nó relevante"}. +{"The number of pending incoming presence subscription requests","A quantidade pendente dos pedidos da presença da assinatura"}. +{"The number of subscribers to the node","A quantidade dos assinantes para o nó"}. +{"The number of unread or undelivered messages","A quantidade das mensagens que não foram lidas ou não foram entregues"}. +{"The password contains unacceptable characters","A palavra-passe contém caracteres proibidos"}. +{"The password is too weak","Palavra-passe considerada muito fraca"}. +{"the password is","a palavra-passe é"}. +{"The password of your XMPP account was successfully changed.","A palavra-passe da sua conta XMPP foi alterada com sucesso."}. +{"The password was not changed","A palavra-passe não foi alterada"}. +{"The passwords are different","As palavras-passe não batem"}. +{"The presence states for which an entity wants to receive notifications","As condições da presença para os quais uma entidade queira receber as notificações"}. +{"The query is only allowed from local users","Esta consulta só é permitida a partir de utilizadores locais"}. +{"The query must not contain elements","A consulta não pode conter elementos "}. +{"The room subject can be modified by participants","O tema da sala pode ser alterada pelos próprios participantes"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","Informações de tipo semântico dos dados no nó, geralmente especificadas pelo espaço de nomes da carga útil (se houver)"}. +{"The sender of the last received message","O remetente da última mensagem que foi recebida"}. +{"The stanza MUST contain only one element, one element, or one element","A instância DEVE conter apenas um elemento , um elemento , ou um elemento "}. +{"The subscription identifier associated with the subscription request","O identificador da assinatura associado à solicitação da assinatura"}. +{"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","O URL da transformação XSL que pode ser aplicada nas cargas úteis para gerar um elemento apropriado no corpo da mensagem."}. +{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","A URL de uma transformação XSL que pode ser aplicada ao formato de carga útil para gerar um Formulário de Dados válido onde o cliente possa exibir usando um mecanismo genérico de renderização do Formulários de Dados"}. +{"There was an error changing the password: ","Houve um erro ao alterar a palavra-passe: "}. +{"There was an error creating the account: ","Houve um erro ao criar esta conta: "}. +{"There was an error deleting the account: ","Houve um erro ao deletar esta conta: "}. +{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","O tamanho da caixa não importa: macbeth é o mesmo que MacBeth e Macbeth."}. +{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Esta pagina permite a criação de novas contas XMPP neste servidor. O seu JID (Identificador Jabber) será da seguinte maneira: utilizador@servidor. Por favor, leia cuidadosamente as instruções para preencher todos os campos corretamente."}. +{"This page allows to unregister an XMPP account in this XMPP server.","Esta página permite a exclusão de uma conta XMPP neste servidor."}. +{"This room is not anonymous","Essa sala não é anônima"}. +{"This service can not process the address: ~s","Este serviço não pode processar o endereço: ~s"}. +{"Thursday","Quinta"}. +{"Time delay","Intervalo (Tempo)"}. +{"Timed out waiting for stream resumption","Tempo limite expirou durante à espera da retomada da transmissão"}. +{"To register, visit ~s","Para registar, visite ~s"}. +{"To ~ts","Para ~s"}. +{"Token TTL","Token TTL"}. +{"Too many active bytestreams","Quantidade excessiva de bytestreams ativos"}. +{"Too many CAPTCHA requests","Quantidade excessiva de requisições para o CAPTCHA"}. +{"Too many child elements","Quantidade excessiva de elementos filho"}. +{"Too many elements","Quantidade excessiva de elementos "}. +{"Too many elements","Quantidade excessiva de elementos "}. +{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Tentativas excessivas (~p) com falha de autenticação (~s). O endereço será desbloqueado às ~s UTC"}. +{"Too many receiver fields were specified","Foram definidos receptores demais nos campos"}. +{"Too many unacked stanzas","Quantidade excessiva de instâncias sem confirmação"}. +{"Too many users in this conference","Há uma quantidade excessiva de utilizadores nesta conferência"}. +{"Traffic rate limit is exceeded","Limite de banda excedido"}. +{"~ts's MAM Archive","Arquivo ~ts's MAM"}. +{"~ts's Offline Messages Queue","~s's Fila de Mensagens Offline"}. +{"Tuesday","Terça"}. +{"Unable to generate a CAPTCHA","Impossível gerar um CAPTCHA"}. +{"Unable to register route on existing local domain","Não foi possível registar rota no domínio local existente"}. +{"Unauthorized","Não Autorizado"}. +{"Unexpected action","Ação inesperada"}. +{"Unexpected error condition: ~p","Condição de erro inesperada: ~p"}. +{"Uninstall","Desinstalar"}. +{"Unregister an XMPP account","Excluir uma conta XMPP"}. +{"Unregister","Deletar registo"}. +{"Unsupported element","Elemento não suportado"}. +{"Unsupported version","Versão sem suporte"}. +{"Update message of the day (don't send)","Atualizar mensagem do dia (não enviar)"}. +{"Update message of the day on all hosts (don't send)","Atualizar a mensagem do dia em todos os host (não enviar)"}. +{"Update specs to get modules source, then install desired ones.","Atualize as especificações para obter a fonte dos módulos e instale os que desejar."}. +{"Update Specs","Atualizar as especificações"}. +{"Updating the vCard is not supported by the vCard storage backend","A atualização do vCard não é compatível com o back-end de armazenamento do vCard"}. +{"Upgrade","Atualização"}. +{"URL for Archived Discussion Logs","A URL para o arquivamento dos registos da discussão"}. +{"User already exists","Utilizador já existe"}. +{"User (jid)","Utilizador (jid)"}. +{"User JID","Utilizador JID"}. +{"User Management","Gestão de utilizadores"}. +{"User not allowed to perform an IQ set on another user's vCard.","O utilizador não tem permissão para executar um conjunto de QI no vCard de outro utilizador."}. +{"User removed","O utilizador foi removido"}. +{"User session not found","A sessão do utilizador não foi encontrada"}. +{"User session terminated","Sessão de utilizador terminada"}. +{"User ~ts","Utilizador ~s"}. +{"Username:","Utilizador:"}. +{"Users are not allowed to register accounts so quickly","Utilizadores não estão autorizados a registar contas imediatamente"}. +{"Users Last Activity","Últimas atividades dos utilizadores"}. {"Users","Utilizadores"}. {"User","Utilizador"}. +{"Value 'get' of 'type' attribute is not allowed","Valor 'get' não permitido para atributo 'type'"}. +{"Value of '~s' should be boolean","Value de '~s' deveria ser um booleano"}. +{"Value of '~s' should be datetime string","Valor de '~s' deveria ser data e hora"}. +{"Value of '~s' should be integer","Valor de '~s' deveria ser um inteiro"}. +{"Value 'set' of 'type' attribute is not allowed","Valor 'set' não permitido para atributo 'type'"}. +{"vCard User Search","Busca de Utilizador vCard"}. +{"View joined MIX channels","Exibir os canais MIX aderidos"}. +{"Virtual Hosts","Hosts virtuais"}. +{"Visitors are not allowed to change their nicknames in this room","Nesta sala, os visitantes não podem mudar os apelidos deles"}. {"Visitors are not allowed to send messages to all occupants","Os visitantes não podem enviar mensagens para todos os ocupantes"}. +{"Visitor","Visitante"}. +{"Voice request","Requisição de voz"}. +{"Voice requests are disabled in this conference","Requisições de voz estão desativadas nesta sala de conferência"}. +{"Web client which allows to join the room anonymously","Cliente da web que permite entrar na sala de forma anônima"}. +{"Wednesday","Quarta"}. +{"When a new subscription is processed and whenever a subscriber comes online","Quando uma nova assinatura é processada e sempre que um assinante fica online"}. +{"When a new subscription is processed","Quando uma nova assinatura é processada"}. +{"When to send the last published item","Quando enviar o último tópico publicado"}. +{"Whether an entity wants to receive an XMPP message body in addition to the payload format","Caso uma entidade queira receber o corpo de uma mensagem XMPP além do formato de carga útil"}. +{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","Caso uma entidade queira receber os resumos (as agregações) das notificações ou todas as notificações individualmente"}. +{"Whether an entity wants to receive or disable notifications","Caso uma entidade queira receber ou desativar as notificações"}. +{"Whether owners or publisher should receive replies to items","Caso os proprietários ou a editora devam receber as respostas nos itens"}. +{"Whether the node is a leaf (default) or a collection","Caso o nó seja uma folha (padrão) ou uma coleção"}. +{"Whether to allow subscriptions","Permitir subscrições"}. +{"Whether to make all subscriptions temporary, based on subscriber presence","Caso todas as assinaturas devam ser temporárias, com base na presença do assinante"}. +{"Whether to notify owners about new subscribers and unsubscribes","Caso deva notificar os proprietários sobre os novos assinantes e aqueles que cancelaram a assinatura"}. +{"Who can send private messages","Quem pode enviar mensagens privadas"}. +{"Who may associate leaf nodes with a collection","Quem pode associar as folhas dos nós numa coleção"}. +{"Wrong parameters in the web formulary","O formulário web está com os parâmetros errados"}. +{"Wrong xmlns","Xmlns errado"}. +{"XMPP Account Registration","Registo da Conta XMPP"}. +{"XMPP Domains","Domínios XMPP"}. +{"XMPP Show Value of Away","XMPP Exiba o valor da ausência"}. +{"XMPP Show Value of Chat","XMPP Exiba o valor do chat"}. +{"XMPP Show Value of DND (Do Not Disturb)","XMPP Exiba o valor do DND (Não Perturbe)"}. +{"XMPP Show Value of XA (Extended Away)","XMPP Exiba o valor do XA (Ausência Estendida)"}. +{"XMPP URI of Associated Publish-Subscribe Node","XMPP URI da publicação do nó associado da assinatura"}. +{"You are being removed from the room because of a system shutdown","Está a ser removido da sala devido ao desligamento do sistema"}. +{"You are not allowed to send private messages","Não tem permissão de enviar mensagens privadas"}. +{"You are not joined to the channel","Não está inscrito no canal"}. +{"You can later change your password using an XMPP client.","Pode alterar a sua palavra-passe mais tarde usando um cliente XMPP."}. {"You have been banned from this room","Foi banido desta sala"}. -{"You need an x:data capable client to configure mod_irc settings","É necessário um cliente com suporte de x:data para configurar as opções do mod_irc"}. -{"You need an x:data capable client to configure room","É necessário um cliente com suporte de x:data para configurar a sala"}. +{"You have joined too many conferences","Entrou em demais salas de conferência"}. +{"You must fill in field \"Nickname\" in the form","Deve completar o campo \"Apelido\" no formulário"}. +{"You need a client that supports x:data and CAPTCHA to register","Precisa de um cliente com suporte de x:data para poder registar o apelido"}. +{"You need a client that supports x:data to register the nickname","Precisa de um cliente com suporte a x:data para registar o seu apelido"}. {"You need an x:data capable client to search","É necessário um cliente com suporte de x:data para poder procurar"}. +{"Your active privacy list has denied the routing of this stanza.","A sua lista de privacidade ativa negou o roteamento desta instância."}. +{"Your contact offline message queue is full. The message has been discarded.","A fila de contatos offline esta cheia. A sua mensagem foi descartada."}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","As suas mensagens para ~s estão bloqueadas. Para desbloqueá-las, visite: ~s"}. +{"Your XMPP account was successfully registered.","A sua conta XMPP foi registada com sucesso."}. +{"Your XMPP account was successfully unregistered.","A sua conta XMPP foi excluída com sucesso."}. +{"You're not allowed to create nodes","Não tem autorização para criar nós"}. diff --git a/priv/msgs/pt.po b/priv/msgs/pt.po deleted file mode 100644 index ab49665ed..000000000 --- a/priv/msgs/pt.po +++ /dev/null @@ -1,2065 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: 2.1.0-alpha\n" -"Last-Translator: Iceburn\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Portuguese (português)\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Não foi passado nenhum recurso" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "" - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -#, fuzzy -msgid "Enter the text you see" -msgstr "Introduza caminho para o ficheiro de texto" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "" - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "" - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Utilizador" - -#: ejabberd_oauth.erl:256 -#, fuzzy -msgid "Server" -msgstr "Nunca" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Palavra-chave" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -#, fuzzy -msgid "ejabberd Web Admin" -msgstr "Administração do ejabberd" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -#, fuzzy -msgid "Administration" -msgstr "Administração de " - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Listas de Controlo de Acesso" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -#, fuzzy -msgid "Submitted" -msgstr "enviado" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -#, fuzzy -msgid "Bad format" -msgstr "formato inválido" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Enviar" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -#, fuzzy -msgid "Raw" -msgstr "modo texto" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Eliminar os seleccionados" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Regras de Acesso" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "Configuração das Regra de Acesso ~s" - -#: ejabberd_web_admin.erl:931 -#, fuzzy -msgid "Virtual Hosts" -msgstr "Servidores virtuales" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Utilizadores" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "Utilizadores ligados" - -#: ejabberd_web_admin.erl:971 -#, fuzzy -msgid "Users Last Activity" -msgstr "Última actividade" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "" - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "" - -#: ejabberd_web_admin.erl:991 -#, fuzzy -msgid "All activity" -msgstr "Última actividade" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Estatísticas" - -#: ejabberd_web_admin.erl:1014 -#, fuzzy -msgid "Not Found" -msgstr "Nodo não encontrado" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Nodo não encontrado" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Adicionar novo" - -#: ejabberd_web_admin.erl:1338 -#, fuzzy -msgid "Host" -msgstr "Nome do servidor" - -#: ejabberd_web_admin.erl:1339 -#, fuzzy -msgid "Registered Users" -msgstr "Utilizadores registados" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Adicionar utilizador" - -#: ejabberd_web_admin.erl:1459 -#, fuzzy -msgid "Offline Messages" -msgstr "Mensagens diferidas" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Última actividade" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Nunca" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Ligado" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -#, fuzzy -msgid "Registered Users:" -msgstr "Utilizadores registados" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -#, fuzzy -msgid "Online Users:" -msgstr "Utilizadores ligados" - -#: ejabberd_web_admin.erl:1556 -#, fuzzy -msgid "Outgoing s2s Connections:" -msgstr "Conexões S2S para fora" - -#: ejabberd_web_admin.erl:1559 -#, fuzzy -msgid "Incoming s2s Connections:" -msgstr "Conexões S2S para fora" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Nenhum" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Mudar palavra-chave" - -#: ejabberd_web_admin.erl:1673 -#, fuzzy -msgid "User ~s" -msgstr "Utilizador" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Recursos conectados:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Palavra-chave:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Eliminar utilizador" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Nodos" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Nodos a correr" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Nodos parados" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -#, fuzzy -msgid "Node ~p" -msgstr "Nodo" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Guardar cópia de segurança" - -#: ejabberd_web_admin.erl:1845 -#, fuzzy -msgid "Listened Ports" -msgstr "Portas em escuta em " - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Actualizar" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Reiniciar" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Parar" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Módulos" - -#: ejabberd_web_admin.erl:1866 -#, fuzzy -msgid "RPC Call Error" -msgstr "Erro na chamada RPC" - -#: ejabberd_web_admin.erl:1917 -#, fuzzy -msgid "Database Tables at ~p" -msgstr "Tabelas da BD em " - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Nome" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Tipo de armazenagem" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Memória" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "" - -#: ejabberd_web_admin.erl:1955 -#, fuzzy -msgid "Backup of ~p" -msgstr "Guardar cópia de segurança" - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" - -#: ejabberd_web_admin.erl:1969 -#, fuzzy -msgid "Store binary backup:" -msgstr "Armazenar uma cópia de segurança no ficheiro" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "OK" - -#: ejabberd_web_admin.erl:1979 -#, fuzzy -msgid "Restore binary backup immediately:" -msgstr "Recuperar uma cópia de segurança a partir de ficheiro" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "" - -#: ejabberd_web_admin.erl:2009 -#, fuzzy -msgid "Restore plain text backup immediately:" -msgstr "Recuperar uma cópia de segurança a partir de ficheiro" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "" - -#: ejabberd_web_admin.erl:2076 -#, fuzzy -msgid "Import user data from jabberd14 spool file:" -msgstr "Importar utilizadores a partir de ficheiros da spool do jabberd14" - -#: ejabberd_web_admin.erl:2087 -#, fuzzy -msgid "Import users data from jabberd14 spool directory:" -msgstr "Importar utilizadores a partir de ficheiros da spool do jabberd14" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Portas em escuta em " - -#: ejabberd_web_admin.erl:2144 -#, fuzzy -msgid "Modules at ~p" -msgstr "Parar módulos em " - -#: ejabberd_web_admin.erl:2175 -#, fuzzy -msgid "Statistics of ~p" -msgstr "Estatísticas" - -#: ejabberd_web_admin.erl:2179 -#, fuzzy -msgid "Uptime:" -msgstr "Tempo de funcionamento" - -#: ejabberd_web_admin.erl:2183 -#, fuzzy -msgid "CPU Time:" -msgstr "Tempo de processador consumido" - -#: ejabberd_web_admin.erl:2191 -#, fuzzy -msgid "Transactions Committed:" -msgstr "Transacções realizadas" - -#: ejabberd_web_admin.erl:2195 -#, fuzzy -msgid "Transactions Aborted:" -msgstr "Transacções abortadas" - -#: ejabberd_web_admin.erl:2199 -#, fuzzy -msgid "Transactions Restarted:" -msgstr "Transacções reiniciadas" - -#: ejabberd_web_admin.erl:2203 -#, fuzzy -msgid "Transactions Logged:" -msgstr "Transacções armazenadas" - -#: ejabberd_web_admin.erl:2243 -#, fuzzy -msgid "Update ~p" -msgstr "Actualizar" - -#: ejabberd_web_admin.erl:2254 -#, fuzzy -msgid "Update plan" -msgstr "Actualizar" - -#: ejabberd_web_admin.erl:2255 -#, fuzzy -msgid "Modified modules" -msgstr "Iniciar módulos" - -#: ejabberd_web_admin.erl:2256 -#, fuzzy -msgid "Update script" -msgstr "Actualizar" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Porta" - -#: ejabberd_web_admin.erl:2439 -#, fuzzy -msgid "Protocol" -msgstr "Porta" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Módulo" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Opções" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -#, fuzzy -msgid "Start" -msgstr "Reiniciar" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -#, fuzzy -msgid "Ping" -msgstr "Pendente" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -#, fuzzy -msgid "Subject" -msgstr "Enviar" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "" - -#: mod_announce.erl:680 -#, fuzzy -msgid "Delete message of the day" -msgstr "Eliminar os seleccionados" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Configuração" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Iniciar módulos" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Parar módulos" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Restaurar" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Exportar para ficheiro de texto" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Importar ficheiro" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Importar directório" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -#, fuzzy -msgid "Restart Service" -msgstr "Reiniciar" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -#, fuzzy -msgid "Delete User" -msgstr "Eliminar" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -#, fuzzy -msgid "Get User Password" -msgstr "Palavra-chave" - -#: mod_configure.erl:185 mod_configure.erl:546 -#, fuzzy -msgid "Change User Password" -msgstr "Mudar palavra-chave" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -#, fuzzy -msgid "Get User Statistics" -msgstr "Estatísticas" - -#: mod_configure.erl:191 mod_configure.erl:552 -#, fuzzy -msgid "Get Number of Registered Users" -msgstr "Utilizadores registados" - -#: mod_configure.erl:194 mod_configure.erl:554 -#, fuzzy -msgid "Get Number of Online Users" -msgstr "Utilizadores ligados" - -#: mod_configure.erl:320 mod_configure.erl:523 -#, fuzzy -msgid "User Management" -msgstr "Gestão da BD" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Todos os utilizadores" - -#: mod_configure.erl:526 -#, fuzzy -msgid "Outgoing s2s Connections" -msgstr "Conexões S2S para fora" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Gestão de cópias de segurança" - -#: mod_configure.erl:617 -#, fuzzy -msgid "Import Users From jabberd14 Spool Files" -msgstr "Importar utilizadores a partir de ficheiros da spool do jabberd14" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "A ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "De ~s" - -#: mod_configure.erl:1002 -#, fuzzy -msgid "Database Tables Configuration at " -msgstr "Configuração de tabelas da BD em " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Seleccione o tipo de armazenagem das tabelas" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Cópia apenas em disco" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "Cópia em RAM e em disco" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "Cópia em RAM" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "Cópia remota" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Parar módulos em " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Seleccione os módulos a parar" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Iniciar os módulos em " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Introduza lista de {módulos, [opções]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Lista de módulos a iniciar" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Guardar cópia de segurança para ficheiro em " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Introduza o caminho do ficheiro de cópia de segurança" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Caminho do ficheiro" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Restaura cópia de segurança a partir do ficheiro em " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Exporta cópia de segurança para ficheiro de texto em " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Introduza caminho para o ficheiro de texto" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Importar utilizador a partir do ficheiro em " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Introduza o caminho para o ficheiro de spool do jabberd14" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Importar utilizadores a partir do directório em " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Introduza o caminho para o directório de spools do jabberd14" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Caminho para o directório" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Configuração da Lista de Controlo de Acesso" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Listas de Controlo de Acesso" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Configuração de acessos" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Regras de acesso" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "" - -#: mod_configure.erl:1540 -#, fuzzy -msgid "Number of registered users" -msgstr "Utilizadores registados" - -#: mod_configure.erl:1559 -#, fuzzy -msgid "Number of online users" -msgstr "Utilizadores ligados" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "" - -#: mod_configure.erl:1963 -#, fuzzy -msgid "Roster size" -msgstr "Lista de contactos" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "" - -#: mod_configure.erl:1967 -#, fuzzy -msgid "Resources" -msgstr "Restaurar" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Administração de " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Acção no utilizador" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Editar propriedades" - -#: mod_fail2ban.erl:95 -msgid "" -"Too many (~p) failed authentications from this IP address (~s). The address " -"will be unblocked at ~s UTC" -msgstr "" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "" - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "" - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "Acesso negado pela política de serviço" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "Módulo de IRC ejabberd" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "" -"É necessário um cliente com suporte de x:data para configurar as opções do " -"mod_irc" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Registo no mod_irc para" - -#: mod_irc.erl:659 -#, fuzzy -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Introduza o nome de utilizador e codificações de caracteres que quer usar ao " -"conectar-se aos servidores de IRC" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "Nome do utilizador de IRC" - -#: mod_irc.erl:682 -#, fuzzy -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Se deseja especificar codificações de caracteres diferentes para cada " -"servidor IRC preencha esta lista con valores no formato '{\"servidor irc\", " -"\"codificação\"}'. Este serviço usa por omissão a codificação \"~s\"." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "" - -#: mod_irc.erl:903 -#, fuzzy -msgid "IRC server" -msgstr "Nome do utilizador de IRC" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "" - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "" - -#: mod_irc.erl:1051 -#, fuzzy -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Introduza o nome de utilizador e codificações de caracteres que quer usar ao " -"conectar-se aos servidores de IRC" - -#: mod_irc.erl:1060 -#, fuzzy -msgid "IRC username" -msgstr "Nome do utilizador de IRC" - -#: mod_irc.erl:1126 -#, fuzzy -msgid "Password ~b" -msgstr "Palavra-chave" - -#: mod_irc.erl:1137 -#, fuzzy -msgid "Port ~b" -msgstr "Porta" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "" - -#: mod_mam.erl:541 -#, fuzzy -msgid "Only members may query archives of this room" -msgstr "Só os moderadores podem mudar o tópico desta sala" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "" -"Só os administradores do serviço têm permissão para enviar mensagens de " -"serviço" - -#: mod_muc.erl:622 -#, fuzzy -msgid "Room creation is denied by service policy" -msgstr "Acesso negado pela política de serviço" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "A sala não existe" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "" - -#: mod_muc.erl:933 -#, fuzzy -msgid "You need a client that supports x:data to register the nickname" -msgstr "" -"É necessário um cliente com suporte de x:data para poder registar a alcunha" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Registo da alcunha em " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Introduza a alcunha que quer registar" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Alcunha" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -#, fuzzy -msgid "That nickname is registered by another person" -msgstr "A alcunha já está registada por outra pessoa" - -#: mod_muc.erl:1090 -#, fuzzy -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Deve preencher o campo \"alcunha\" no formulário" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "Módulo MUC de ejabberd" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "" - -#: mod_muc_admin.erl:249 -msgid "Total rooms" -msgstr "" - -#: mod_muc_admin.erl:250 -msgid "Permanent rooms" -msgstr "" - -#: mod_muc_admin.erl:251 -#, fuzzy -msgid "Registered nicknames" -msgstr "Utilizadores registados" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -#, fuzzy -msgid "Chatroom configuration modified" -msgstr "Configuração para " - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " colocou o tópico: " - -#: mod_muc_log.erl:493 -#, fuzzy -msgid "Chatroom is created" -msgstr "Configuração para " - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "" - -#: mod_muc_log.erl:523 -#, fuzzy -msgid "November" -msgstr "Nunca" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "" - -#: mod_muc_log.erl:912 -#, fuzzy -msgid "Room Configuration" -msgstr "Configuração" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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 "" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "Impedir o envio de mensagens privadas para a sala" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "" - -#: mod_muc_room.erl:377 -#, fuzzy -msgid "Only moderators can approve voice requests" -msgstr "Permitir que os utilizadores enviem convites?" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Tipo de mensagem incorrecto" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "Não é permitido enviar mensagens privadas do tipo \"groupchat\"" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "O destinatário não está na sala" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -#, fuzzy -msgid "It is not allowed to send private messages" -msgstr "Impedir o envio de mensagens privadas para a sala" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Só os ocupantes podem enviar mensagens para a sala" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "Só os ocupantes podem enviar consultas para a sala" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "Nesta sala não são permitidas consultas aos seus membros" - -#: mod_muc_room.erl:961 -#, fuzzy -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "Só os moderadores e os participantes podem mudar o tópico desta sala" - -#: mod_muc_room.erl:966 -#, fuzzy -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Só os moderadores podem mudar o tópico desta sala" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "Os visitantes não podem enviar mensagens para todos os ocupantes" - -#: mod_muc_room.erl:1080 -#, fuzzy -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "Só os moderadores podem mudar o tópico desta sala" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -#, fuzzy -msgid "That nickname is already in use by another occupant" -msgstr "A alcunha já está a ser usado por outro ocupante" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Foi banido desta sala" - -#: mod_muc_room.erl:1826 -#, fuzzy -msgid "Membership is required to enter this room" -msgstr "É necessário ser membro desta sala para poder entrar" - -#: mod_muc_room.erl:1872 -#, fuzzy -msgid "A password is required to enter this room" -msgstr "É necessária a palavra-chave para poder entrar nesta sala" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Palavra-chave incorrecta" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "São necessários privilégios de administrador" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "São necessários privilégios de moderador" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "O Jabber ID ~s não é válido" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "A alcunha ~s não existe na sala" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Afiliação inválida: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Papel inválido: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "São necessários privilégios de dono" - -#: mod_muc_room.erl:3348 -#, fuzzy -msgid "Configuration of room ~s" -msgstr "Configuração para " - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Título da sala" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -#, fuzzy -msgid "Room description" -msgstr "Subscrição" - -#: mod_muc_room.erl:3369 -#, fuzzy -msgid "Make room persistent" -msgstr "Tornar a sala permanente?" - -#: mod_muc_room.erl:3375 -#, fuzzy -msgid "Make room public searchable" -msgstr "Tornar a sala publicamente visível?" - -#: mod_muc_room.erl:3378 -#, fuzzy -msgid "Make participants list public" -msgstr "Tornar pública a lista de participantes?" - -#: mod_muc_room.erl:3380 -#, fuzzy -msgid "Make room password protected" -msgstr "Proteger a sala com palavra-chave?" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -#, fuzzy -msgid "anyone" -msgstr "Nenhum" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "" - -#: mod_muc_room.erl:3486 -msgid "Moderator" -msgstr "" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "" - -#: mod_muc_room.erl:3513 -#, fuzzy -msgid "Make room members-only" -msgstr "Tornar a sala exclusiva a membros?" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Tornar a sala moderada" - -#: mod_muc_room.erl:3519 -#, fuzzy -msgid "Default users as participants" -msgstr "Os utilizadores são membros por omissão?" - -#: mod_muc_room.erl:3522 -#, fuzzy -msgid "Allow users to change the subject" -msgstr "Permitir aos utilizadores mudar o tópico?" - -#: mod_muc_room.erl:3525 -#, fuzzy -msgid "Allow users to send private messages" -msgstr "Permitir que os utilizadores enviem mensagens privadas?" - -#: mod_muc_room.erl:3533 -#, fuzzy -msgid "Allow visitors to send private messages to" -msgstr "Permitir que os utilizadores enviem mensagens privadas?" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "" - -#: mod_muc_room.erl:3576 -#, fuzzy -msgid "Allow users to query other users" -msgstr "Permitir aos utilizadores consultar outros utilizadores?" - -#: mod_muc_room.erl:3579 -#, fuzzy -msgid "Allow users to send invites" -msgstr "Permitir que os utilizadores enviem convites?" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "" - -#: mod_muc_room.erl:3586 -#, fuzzy -msgid "Allow visitors to change nickname" -msgstr "Permitir aos utilizadores mudar o tópico?" - -#: mod_muc_room.erl:3589 -#, fuzzy -msgid "Allow visitors to send voice requests" -msgstr "Permitir que os utilizadores enviem convites?" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "" - -#: mod_muc_room.erl:3599 -#, fuzzy -msgid "Make room CAPTCHA protected" -msgstr "Proteger a sala com palavra-chave?" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "" - -#: mod_muc_room.erl:3621 -#, fuzzy -msgid "Enable logging" -msgstr "Guardar históricos?" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "É necessário um cliente com suporte de x:data para configurar a sala" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "privado" - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "" - -#: mod_muc_room.erl:4351 -#, fuzzy -msgid "User JID" -msgstr "Utilizador" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "" - -#: mod_muc_room.erl:4509 -#, fuzzy -msgid "the password is" -msgstr "Mudar palavra-chave" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "" - -#: mod_multicast.erl:306 -#, fuzzy -msgid "ejabberd Multicast service" -msgstr "Utilizadores do ejabberd" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "" - -#: mod_offline.erl:798 -#, fuzzy -msgid "~s's Offline Messages Queue" -msgstr "~s fila de mensagens diferidas" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Data" - -#: mod_offline.erl:812 -msgid "From" -msgstr "De" - -#: mod_offline.erl:813 -msgid "To" -msgstr "Para" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Pacote" - -#: mod_offline.erl:992 -#, fuzzy -msgid "Offline Messages:" -msgstr "Mensagens diferidas:" - -#: mod_offline.erl:996 -#, fuzzy -msgid "Remove All Offline Messages" -msgstr "Mensagens diferidas" - -#: mod_proxy65_service.erl:248 -#, fuzzy -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "Módulo vCard de ejabberd" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "" - -#: mod_pubsub.erl:1222 -#, fuzzy -msgid "ejabberd Publish-Subscribe module" -msgstr "Módulo pub/sub de ejabberd" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "" - -#: mod_pubsub.erl:1559 -#, fuzzy -msgid "Node ID" -msgstr "Nodo" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "" - -#: mod_register.erl:253 -#, fuzzy -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "" -"É necessário um cliente com suporte de x:data para poder registar a alcunha" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "" -"Escolha um nome de utilizador e palavra-chave para se registar neste servidor" - -#: mod_register.erl:373 mod_register.erl:421 -#, fuzzy -msgid "The password is too weak" -msgstr "Mudar palavra-chave" - -#: mod_register.erl:426 -#, fuzzy -msgid "Users are not allowed to register accounts so quickly" -msgstr "Os visitantes não podem enviar mensagens para todos os ocupantes" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "" - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "" - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "" - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "" - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "" - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "" - -#: mod_register_web.erl:175 mod_register_web.erl:183 -#, fuzzy -msgid "Jabber Account Registration" -msgstr "Configuração das Listas de Controlo de Acesso do ejabberd" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -#, fuzzy -msgid "Username:" -msgstr "Nome do utilizador de IRC" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -#, fuzzy -msgid "Server:" -msgstr "Nunca" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "" - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "" - -#: mod_register_web.erl:269 -#, fuzzy -msgid "Register" -msgstr "Lista de contactos" - -#: mod_register_web.erl:366 -#, fuzzy -msgid "Old Password:" -msgstr "Palavra-chave:" - -#: mod_register_web.erl:370 -#, fuzzy -msgid "New Password:" -msgstr "Palavra-chave:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "" - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Subscrição" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Pendente" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Grupos" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Remover" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Lista de contactos de " - -#: mod_roster.erl:1504 -#, fuzzy -msgid "Add Jabber ID" -msgstr "Adicionar Jabber ID" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Lista de contactos" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -#, fuzzy -msgid "Shared Roster Groups" -msgstr "Lista de contactos partilhada" - -#: mod_shared_roster.erl:1232 -#, fuzzy -msgid "Name:" -msgstr "Nome" - -#: mod_shared_roster.erl:1236 -#, fuzzy -msgid "Description:" -msgstr "Subscrição" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "" - -#: mod_shared_roster.erl:1259 -#, fuzzy -msgid "Group " -msgstr "Grupos" - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Servidor Jabber em Erlang" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Data de nascimento" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Cidade" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "País" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -#, fuzzy -msgid "Email" -msgstr "email" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Apelido" - -#: mod_vcard.erl:490 -#, fuzzy -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "Preencha os campos para procurar utilizadores Jabber coincidentes" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Nome completo" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Segundo nome" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Nome da organização" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Unidade da organização" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Procurar utilizadores em " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "É necessário um cliente com suporte de x:data para poder procurar" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "Módulo vCard de ejabberd" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -#, fuzzy -msgid "Search Results for " -msgstr "Procurar utilizadores em " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "Preencha os campos para procurar utilizadores Jabber coincidentes" - -#, fuzzy -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Servidores S2S de saída" - -#~ msgid "Delete" -#~ msgstr "Eliminar" - -#, fuzzy -#~ msgid "This room is not anonymous" -#~ msgstr "Tornar a sala anónima?" - -#~ msgid "Encodings" -#~ msgstr "Codificações" - -#, fuzzy -#~ msgid "(Raw)" -#~ msgstr "(modo texto)" - -#~ msgid "Specified nickname is already registered" -#~ msgstr "A alcunha especificada já está registada" - -#~ msgid "Size" -#~ msgstr "Tamanho" - -#~ msgid "Backup Management at " -#~ msgstr "Gestão da cópia de segurança em " - -#~ msgid "Choose host name" -#~ msgstr "Introduza o nome do servidor" - -#~ msgid "Choose users to remove" -#~ msgstr "Seleccione utilizadores a eliminar" - -#~ msgid "DB" -#~ msgstr "BD" - -#~ msgid "Dump a database in a text file" -#~ msgstr "Exportar uma Base de Dados para um ficheiro de texto" - -#~ msgid "Host name" -#~ msgstr "Nome do servidor" - -#~ msgid "Hostname Configuration" -#~ msgstr "Configuração do nome do servidor" - -#~ msgid "Install a database fallback from a file" -#~ msgstr "Instalar uma recuperação de BD desde um ficheiro" - -#~ msgid "It is not allowed to send normal messages to the conference" -#~ msgstr "Impedir o envio de mensagens normais para a sala" - -#~ msgid "Listened Ports Management" -#~ msgstr "Gestão das portas em escuta" - -#~ msgid "Make room moderated?" -#~ msgstr "Tornar a sala moderada?" - -#~ msgid "Remove Users" -#~ msgstr "Eliminar utilizadores" - -#~ msgid "Restore a database from a text file" -#~ msgstr "Restaurar uma Base de Dados a partir de ficheiro de texto" - -#~ msgid "Results of search in " -#~ msgstr "Resultados da procura em " - -#~ msgid "ejabberd (c) 2002-2005 Alexey Shchepin, 2005 Process One" -#~ msgstr "ejabberd (c) 2002-2005 Alexey Shchepin, 2005 Process One" - -#~ msgid "ejabberd access rules configuration" -#~ msgstr "Configuração das Regras de Acesso do ejabberd" - -#~ msgid "~p statistics" -#~ msgstr "Estatísticas de ~p" diff --git a/priv/msgs/ru.msg b/priv/msgs/ru.msg index 7acab78f9..ca23d2167 100644 --- a/priv/msgs/ru.msg +++ b/priv/msgs/ru.msg @@ -1,21 +1,25 @@ -%% -*- coding: latin-1 -*- -{"Access Configuration","Конфигурация доступа"}. -{"Access Control List Configuration","Конфигурация списков управления доступом"}. -{"Access control lists","Списки управления доступом"}. -{"Access Control Lists","Списки управления доступом"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" (Add * to the end of field to match substring)"," (Добавьте * в конец поля для поиска подстроки)"}. +{" has set the subject to: "," установил(а) тему: "}. +{"A friendly name for the node","Легко запоминаемое имя для узла"}. +{"A password is required to enter this room","Чтобы войти в эту конференцию, нужен пароль"}. +{"A Web Page","Веб-страница"}. +{"Accept","Принять"}. {"Access denied by service policy","Доступ запрещён политикой службы"}. -{"Access rules","Правила доступа"}. -{"Access Rules","Правила доступа"}. +{"Account doesn't exist","Учётная запись не существует"}. {"Action on user","Действие над пользователем"}. -{"Add Jabber ID","Добавить Jabber ID"}. -{"Add New","Добавить"}. {"Add User","Добавить пользователя"}. {"Administration of ","Администрирование "}. {"Administration","Администрирование"}. {"Administrator privileges required","Требуются права администратора"}. -{"A friendly name for the node","Легко запоминаемое имя для узла"}. {"All activity","Вся статистика"}. +{"All Users","Все пользователи"}. {"Allow this Jabber ID to subscribe to this pubsub node?","Разрешить этому Jabber ID подписаться на данный узел?"}. +{"Allow this person to register with the room?","Разрешить пользователю зарегистрироваться в комнате?"}. {"Allow users to change the subject","Разрешить пользователям изменять тему"}. {"Allow users to query other users","Разрешить iq-запросы к пользователям"}. {"Allow users to send invites","Разрешить пользователям посылать приглашения"}. @@ -24,21 +28,32 @@ {"Allow visitors to send private messages to","Разрешить посетителям посылать приватные сообщения"}. {"Allow visitors to send status text in presence updates","Разрешить посетителям вставлять текcт статуса в сообщения о присутствии"}. {"Allow visitors to send voice requests","Разрешить посетителям запрашивать право голоса"}. -{"All Users","Все пользователи"}. {"Announcements","Объявления"}. -{"anyone","всем участникам"}. -{"A password is required to enter this room","Чтобы войти в эту конференцию, нужен пароль"}. {"April","апреля"}. +{"Attribute 'channel' is required for this request","Атрибут 'channel' является обязательным для этого запроса"}. +{"Attribute 'id' is mandatory for MIX messages","Атрибут 'id' является обязательным для MIX сообщений"}. +{"Attribute 'jid' is not allowed here","Атрибут 'jid' здесь недопустим"}. +{"Attribute 'node' is not allowed here","Атрибут 'node' здесь недопустим"}. {"August","августа"}. +{"Automatic node creation is not enabled","Автоматическое создание узлов недоступно"}. {"Backup Management","Управление резервным копированием"}. {"Backup of ~p","Резервное копирование ~p"}. {"Backup to File at ","Резервное копирование в файл на "}. {"Backup","Резервное копирование"}. {"Bad format","Неправильный формат"}. {"Birthday","День рождения"}. +{"Both the username and the resource are required","Требуются имя пользователя и ресурс"}. +{"Bytestream already activated","Поток данных уже активирован"}. +{"Cannot remove active list","Невозможно удалить активный список"}. +{"Cannot remove default list","Невозможно удалить список по умолчанию"}. {"CAPTCHA web page","Ссылка на капчу"}. {"Change Password","Сменить пароль"}. {"Change User Password","Изменить пароль пользователя"}. +{"Changing password is not allowed","Изменение пароля не разрешено"}. +{"Changing role/affiliation is not allowed","Изменение роли/ранга не разрешено"}. +{"Channel already exists","Канал уже существует"}. +{"Channel does not exist","Канал не существует"}. +{"Channels","Каналы"}. {"Characters not allowed:","Недопустимые символы:"}. {"Chatroom configuration modified","Конфигурация комнаты изменилась"}. {"Chatroom is created","Комната создана"}. @@ -47,92 +62,80 @@ {"Chatroom is stopped","Комната остановлена"}. {"Chatrooms","Комнаты"}. {"Choose a username and password to register with this server","Выберите имя пользователя и пароль для регистрации на этом сервере"}. -{"Choose modules to stop","Выберите модули, которые следует остановить"}. {"Choose storage type of tables","Выберите тип хранения таблиц"}. {"Choose whether to approve this entity's subscription.","Решите: предоставить ли подписку этому объекту."}. {"City","Город"}. +{"Client acknowledged more stanzas than sent by server","Клиент подтвердил больше сообщений чем было отправлено сервером"}. {"Commands","Команды"}. {"Conference room does not exist","Конференция не существует"}. {"Configuration of room ~s","Конфигурация комнаты ~s"}. {"Configuration","Конфигурация"}. -{"Connected Resources:","Подключённые ресурсы:"}. -{"Connections parameters","Параметры соединения"}. {"Country","Страна"}. -{"CPU Time:","Процессорное время:"}. -{"Database Tables at ~p","Таблицы базы данных на ~p"}. +{"Database failure","Ошибка базы данных"}. {"Database Tables Configuration at ","Конфигурация таблиц базы данных на "}. {"Database","База данных"}. {"December","декабря"}. {"Default users as participants","Сделать пользователей участниками по умолчанию"}. {"Delete message of the day on all hosts","Удалить сообщение дня со всех виртуальных серверов"}. {"Delete message of the day","Удалить сообщение дня"}. -{"Delete Selected","Удалить выделенные"}. {"Delete User","Удалить пользователя"}. {"Deliver event notifications","Доставлять уведомления о событиях"}. {"Deliver payloads with event notifications","Доставлять вместе с уведомлениями o публикациях сами публикации"}. -{"Description:","Описание:"}. {"Disc only copy","только диск"}. -{"Displayed Groups:","Видимые группы:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","Не говорите никому свой пароль, даже администраторам сервера."}. {"Dump Backup to Text File at ","Копирование в текстовый файл на "}. {"Dump to Text File","Копирование в текстовый файл"}. {"Edit Properties","Изменить параметры"}. {"Either approve or decline the voice request.","Подтвердите или отклоните право голоса."}. -{"ejabberd IRC module","ejabberd IRC модуль"}. {"ejabberd MUC module","ejabberd MUC модуль"}. {"ejabberd Multicast service","ejabberd Multicast сервис"}. {"ejabberd Publish-Subscribe module","Модуль ejabberd Публикации-Подписки"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams модуль"}. {"ejabberd vCard module","ejabberd vCard модуль"}. {"ejabberd Web Admin","Web-интерфейс администрирования ejabberd"}. -{"Elements","Элементы"}. +{"ejabberd","ejabberd"}. {"Email","Электронная почта"}. {"Enable logging","Включить журналирование"}. {"Enable message archiving","Включить хранение сообщений"}. -{"Encoding for server ~b","Кодировка сервера ~b"}. +{"Enabling push without 'node' attribute is not supported","Включение push-режима без атрибута 'node' не поддерживается"}. {"End User Session","Завершить сеанс пользователя"}. -{"Enter list of {Module, [Options]}","Введите список вида {Module, [Options]}"}. {"Enter nickname you want to register","Введите псевдоним, который Вы хотели бы зарегистрировать"}. {"Enter path to backup file","Введите путь к резервному файлу"}. {"Enter path to jabberd14 spool dir","Введите путь к директории спула jabberd14"}. {"Enter path to jabberd14 spool file","Введите путь к файлу из спула jabberd14"}. {"Enter path to text file","Введите путь к текстовому файлу"}. {"Enter the text you see","Введите увиденный текст"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Введите имя пользователя и кодировки, которые будут использоваться при подключении к IRC-серверам. Нажмите 'Далее' для получения дополнительных полей для заполнения. Нажмите 'Завершить' для сохранения настроек."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Введите имя пользователя, кодировки, порты и пароли, которые будут использоваться при подключении к IRC-серверам"}. -{"Erlang Jabber Server","Erlang Jabber Server"}. -{"Error","Ошибка"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Пример: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Exclude Jabber IDs from CAPTCHA challenge","Исключить показ капчи для списка Jabber ID"}. {"Export all tables as SQL queries to a file:","Экспортировать все таблицы в виде SQL запросов в файл:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Экспорт данных всех пользователей сервера в файлы формата PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Экспорт пользовательских данных домена в файлы формата PIEFXIS (XEP-0227):"}. +{"External component failure","Ошибка внешнего сервиса"}. +{"External component timeout","Таймаут внешнего сервиса"}. +{"Failed to activate bytestream","Ошибка при активировании потока данных"}. {"Failed to extract JID from your voice request approval","Ошибка обработки JID из вашего запроса на право голоса"}. +{"Failed to map delegated namespace to external component","Не получилось найти внешний сервис, делегирующий это пространство имён"}. +{"Failed to parse HTTP response","Ошибка разбора HTTP ответа"}. +{"Failed to process option '~s'","Ошибка обработки опции '~s'"}. {"Family Name","Фамилия"}. {"February","февраля"}. -{"Fill in fields to search for any matching Jabber User","Заполните форму для поиска пользователя Jabber"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Заполните форму для поиска пользователя Jabber (Если добавить * в конец поля, то происходит поиск подстроки)"}. +{"File larger than ~w bytes","Файл больше ~w байт"}. {"Friday","Пятница"}. -{"From ~s","От ~s"}. -{"From","От кого"}. {"Full Name","Полное имя"}. {"Get Number of Online Users","Получить количество подключённых пользователей"}. {"Get Number of Registered Users","Получить количество зарегистрированных пользователей"}. +{"Get Pending","Получить отложенные"}. {"Get User Last Login Time","Получить время последнего подключения пользователя"}. -{"Get User Password","Получить пароль пользователя"}. {"Get User Statistics","Получить статистику по пользователю"}. +{"Given Name","Имя"}. {"Grant voice to this person?","Предоставить голос?"}. -{"Groups","Группы"}. -{"Group ","Группа "}. {"has been banned","запретили входить в комнату"}. -{"has been kicked because of an affiliation change","выгнали из комнаты вследствие смены ранга"}. {"has been kicked because of a system shutdown","выгнали из комнаты из-за останова системы"}. +{"has been kicked because of an affiliation change","выгнали из комнаты вследствие смены ранга"}. {"has been kicked because the room has been changed to members-only","выгнали из комнаты потому что она стала только для членов"}. {"has been kicked","выгнали из комнаты"}. -{" has set the subject to: "," установил(а) тему: "}. -{"Host","Хост"}. +{"Host unknown","Неизвестное имя сервера"}. +{"HTTP File Upload","Передача файлов по HTTP"}. +{"Idle connection","Неиспользуемое соединение"}. {"If you don't see the CAPTCHA image here, visit the web page.","Если вы не видите изображение капчи, перейдите по ссылке."}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Чтобы указать различные порты, пароли, кодировки для разных серверов IRC, заполните список значениями в формате '{\"сервер IRC\", \"кодировка\", порт, \"пароль\"}'. По умолчанию сервис использует кодировку \"~s\", порт ~p, пустой пароль."}. {"Import Directory","Импорт из директории"}. {"Import File","Импорт из файла"}. {"Import user data from jabberd14 spool file:","Импорт пользовательских данных из буферного файла jabberd14:"}. @@ -141,30 +144,28 @@ {"Import users data from jabberd14 spool directory:","Импорт пользовательских данных из буферной директории jabberd14:"}. {"Import Users from Dir at ","Импорт пользователей из директории на "}. {"Import Users From jabberd14 Spool Files","Импорт пользователей из спула jabberd14"}. +{"Improper domain part of 'from' attribute","Неправильная доменная часть атрибута 'from'"}. {"Improper message type","Неправильный тип сообщения"}. +{"Incorrect CAPTCHA submit","Неверный ввод капчи"}. +{"Incorrect data form","Некорректная форма данных"}. {"Incorrect password","Неправильный пароль"}. -{"Invalid affiliation: ~s","Недопустимый ранг: ~s"}. -{"Invalid role: ~s","Недопустимая роль: ~s"}. +{"Incorrect value of 'action' attribute","Некорректное значение атрибута 'action'"}. +{"Incorrect value of 'action' in data form","Некорректное значение 'action' в форме данных"}. +{"Incorrect value of 'path' in data form","Некорректное значение 'path' в форме данных"}. +{"Insufficient privilege","Недостаточно прав"}. +{"Internal server error","Внутренняя ошибка сервера"}. +{"Invalid 'from' attribute in forwarded message","Некорректный атрибут 'from' в пересланном сообщении"}. +{"Invalid node name","Недопустимое имя узла"}. +{"Invalid 'previd' value","Недопустимое значение 'previd'"}. +{"Invitations are not allowed in this conference","Рассылка приглашений отключена в этой конференции"}. {"IP addresses","IP адреса"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","Канал IRC (без символа #)"}. -{"IRC server","Сервер IRC"}. -{"IRC settings","Настройки IRC"}. -{"IRC Transport","IRC Транспорт"}. -{"IRC username","Имя пользователя IRC"}. -{"IRC Username","Имя пользователя IRC"}. {"is now known as","изменил(а) имя на"}. +{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Запрещено посылать сообщения об ошибках в эту комнату. Участник (~s) послал сообщение об ошибке (~s) и был выкинут из комнаты"}. {"It is not allowed to send private messages of type \"groupchat\"","Нельзя посылать частные сообщения типа \"groupchat\""}. {"It is not allowed to send private messages to the conference","Не разрешается посылать частные сообщения прямо в конференцию"}. -{"It is not allowed to send private messages","Запрещено посылать приватные сообщения"}. -{"Jabber Account Registration","Регистрация Jabber-аккаунта"}. {"Jabber ID","Jabber ID"}. -{"Jabber ID ~s is invalid","Jabber ID ~s недопустимый"}. {"January","января"}. -{"Join IRC channel","Присоединиться к каналу IRC"}. {"joins the room","вошёл(а) в комнату"}. -{"Join the IRC channel here.","Присоединяйтесь к каналу IRC"}. -{"Join the IRC channel in this Jabber ID: ~s","Присоединиться к каналу IRC с Jabber ID: ~s"}. {"July","июля"}. {"June","июня"}. {"Last Activity","Последнее подключение"}. @@ -172,11 +173,6 @@ {"Last month","За последний месяц"}. {"Last year","За последний год"}. {"leaves the room","вышел(а) из комнаты"}. -{"Listened Ports at ","Прослушиваемые порты на "}. -{"Listened Ports","Прослушиваемые порты"}. -{"List of modules to start","Список запускаемых модулей"}. -{"List of rooms","Список комнат"}. -{"Low level update script","Низкоуровневый сценарий обновления"}. {"Make participants list public","Сделать список участников видимым всем"}. {"Make room CAPTCHA protected","Сделать комнату защищённой капчей"}. {"Make room members-only","Комната только для зарегистрированных участников"}. @@ -184,45 +180,70 @@ {"Make room password protected","Сделать комнату защищённой паролем"}. {"Make room persistent","Сделать комнату постоянной"}. {"Make room public searchable","Сделать комнату видимой всем"}. +{"Malformed username","Недопустимое имя пользователя"}. +{"MAM preference modification denied by service policy","Изменение настроек архива сообщений запрещено политикой службы"}. {"March","марта"}. -{"Maximum Number of Occupants","Максимальное количество участников"}. -{"Max # of items to persist","Максимальное число сохраняемых публикаций"}. {"Max payload size in bytes","Максимальный размер полезной нагрузки в байтах"}. +{"Maximum Number of Occupants","Максимальное количество участников"}. {"May","мая"}. {"Membership is required to enter this room","В эту конференцию могут входить только её члены"}. -{"Members:","Члены:"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Запомните пароль или запишите его на бумаге, которую сохраните в безопасном месте. В Jabber'е нет автоматизированного средства восстановления пароля в том случае, если Вы его забудете."}. -{"Memory","Память"}. {"Message body","Тело сообщения"}. +{"Message not found in forwarded payload","Сообщение не найдено в пересылаемом вложении"}. +{"Messages from strangers are rejected","Сообщения от незнакомцев запрещены"}. {"Middle Name","Отчество"}. {"Minimum interval between voice requests (in seconds)","Минимальный интервал между запросами на право голоса"}. {"Moderator privileges required","Требуются права модератора"}. -{"moderators only","только модераторам"}. -{"Modified modules","Изменённые модули"}. -{"Modules at ~p","Модули на ~p"}. -{"Modules","Модули"}. -{"Module","Модуль"}. +{"Module failed to handle the query","Ошибка модуля при обработке запроса"}. {"Monday","Понедельник"}. {"Multicast","Мультикаст"}. {"Multi-User Chat","Конференция"}. -{"Name:","Название:"}. {"Name","Название"}. +{"Neither 'jid' nor 'nick' attribute found","Не найден атрибут 'jid' или 'nick'"}. +{"Neither 'role' nor 'affiliation' attribute found","Не найден атрибут 'role' или 'affiliation'"}. {"Never","Никогда"}. {"New Password:","Новый пароль:"}. +{"Nickname can't be empty","Псевдоним не может быть пустым значением"}. {"Nickname Registration at ","Регистрация псевдонима на "}. {"Nickname ~s does not exist in the room","Псевдоним ~s в комнате отсутствует"}. {"Nickname","Псевдоним"}. +{"No address elements found","Не найден элемент

"}. +{"No addresses element found","Не найден элемент "}. +{"No 'affiliation' attribute found","Не найден атрибут 'affiliation'"}. +{"No available resource found","Нет доступных ресурсов"}. {"No body provided for announce message","Тело объявления не должно быть пустым"}. -{"nobody","никто"}. +{"No child elements found","Нет дочерних элементов"}. +{"No data form found","Форма данных не найдена"}. {"No Data","Нет данных"}. +{"No features available","Свойства недоступны"}. +{"No element found","Не найден элемент "}. +{"No hook has processed this command","Ни один из хуков не выполнил эту команду"}. +{"No info about last activity found","Не найдено информации о последней активности"}. +{"No 'item' element found","Элемент 'item' не найден"}. +{"No items found in this query","Не найдены элементы в этом запросе"}. +{"No limit","Не ограничено"}. +{"No module is handling this query","Нет модуля который бы обработал этот запрос"}. +{"No node specified","Узел не указан"}. +{"No 'password' found in data form","Не найден 'password' в форме данных"}. +{"No 'password' found in this query","Не найден 'password' в этом запросе"}. +{"No 'path' found in data form","Не найден 'path' в форме данных"}. +{"No pending subscriptions found","Не найдено ожидающих решения подписок"}. +{"No privacy list with this name found","Списка приватности с таким именем не найдено"}. +{"No private data found in this query","Приватные данные не найдены в этом запросе"}. +{"No running node found","Нет работающих узлов"}. +{"No services available","Нет доступных сервисов"}. +{"No statistics found for this item","Не найдено статистики для этого элемента"}. +{"No 'to' attribute found in the invitation","Не найден атрибут 'to' в этом приглашении"}. +{"Node already exists","Узел уже существует"}. {"Node ID","ID узла"}. +{"Node index not found","Индекс узла не найден"}. {"Node not found","Узел не найден"}. {"Node ~p","Узел ~p"}. +{"Nodeprep has failed","Ошибка применения профиля Nodeprep"}. {"Nodes","Узлы"}. -{"No limit","Не ограничено"}. {"None","Нет"}. -{"No resource provided","Не указан ресурс"}. +{"Not allowed","Недопустимо"}. {"Not Found","Не Найдено"}. +{"Not subscribed","Нет подписки"}. {"Notify subscribers when items are removed from the node","Уведомлять подписчиков об удалении публикаций из сборника"}. {"Notify subscribers when the node configuration changes","Уведомлять подписчиков об изменении конфигурации сборника"}. {"Notify subscribers when the node is deleted","Уведомлять подписчиков об удалении сборника"}. @@ -231,70 +252,62 @@ {"Number of online users","Количество подключённых пользователей"}. {"Number of registered users","Количество зарегистрированных пользователей"}. {"October","октября"}. -{"Offline Messages:","Офлайновые сообщения:"}. -{"Offline Messages","Офлайновые сообщения"}. {"OK","Продолжить"}. {"Old Password:","Старый пароль:"}. -{"Online Users:","Подключённые пользователи:"}. {"Online Users","Подключённые пользователи"}. {"Online","Подключён"}. {"Only deliver notifications to available users","Доставлять уведомления только доступным пользователям"}. +{"Only or tags are allowed","Допустимы только тэги или "}. +{"Only element is allowed in this query","Только элемент допустим в этом запросе"}. +{"Only members may query archives of this room","Только члены могут запрашивать архивы этой комнаты"}. {"Only moderators and participants are allowed to change the subject in this room","Только модераторы и участники могут изменять тему в этой комнате"}. {"Only moderators are allowed to change the subject in this room","Только модераторы могут изменять тему в этой комнате"}. {"Only moderators can approve voice requests","Только модераторы могут утверждать запросы на право голоса"}. {"Only occupants are allowed to send messages to the conference","Только присутствующим разрешается посылать сообщения в конференцию"}. {"Only occupants are allowed to send queries to the conference","Только присутствующим разрешается посылать запросы в конференцию"}. {"Only service administrators are allowed to send service messages","Только администратор службы может посылать служебные сообщения"}. -{"Options","Параметры"}. {"Organization Name","Название организации"}. {"Organization Unit","Отдел организации"}. -{"Outgoing s2s Connections:","Исходящие s2s-серверы:"}. {"Outgoing s2s Connections","Исходящие s2s-соединения"}. {"Owner privileges required","Требуются права владельца"}. -{"Packet","Пакет"}. -{"Password ~b","Пароль ~b"}. -{"Password Verification:","Проверка пароля:"}. +{"Packet relay is denied by service policy","Пересылка пакетов запрещена политикой службы"}. {"Password Verification","Проверка пароля"}. -{"Password:","Пароль:"}. +{"Password Verification:","Проверка пароля:"}. {"Password","Пароль"}. +{"Password:","Пароль:"}. {"Path to Dir","Путь к директории"}. {"Path to File","Путь к файлу"}. -{"Pending","Ожидание"}. {"Period: ","Период"}. -{"Permanent rooms","Постоянные комнаты"}. {"Persist items to storage","Сохранять публикации в хранилище"}. +{"Ping query is incorrect","Некорректный пинг-запрос"}. {"Ping","Пинг"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Заметьте, что здесь производится резервное копирование только встроенной базы данных Mnesia. Если Вы также используете другое хранилище данных (например с помощью модуля ODBC), то его резервное копирование следует осуществлять отдельно."}. {"Please, wait for a while before sending new voice request","Пожалуйста, подождите перед тем как подать новый запрос на право голоса"}. {"Pong","Понг"}. -{"Port ~b","Порт ~b"}. -{"Port","Порт"}. {"Present real Jabber IDs to","Сделать реальные Jabber ID участников видимыми"}. +{"Previous session not found","Предыдущая сессия не найдена"}. +{"Previous session PID has been killed","Предыдущая сессия была убита"}. +{"Previous session PID has exited","Процесс предыдущей сессии завершён"}. +{"Previous session PID is dead","Предыдущая сессия мертва"}. +{"Previous session timed out","Предыдущая сессия не отвечает"}. {"private, ","приватная, "}. -{"Protocol","Протокол"}. {"Publish-Subscribe","Публикация-Подписка"}. {"PubSub subscriber request","Запрос подписчика PubSub"}. {"Purge all items when the relevant publisher goes offline","Очищать все записи автора публикации когда он отключается"}. +{"Push record not found","Push-запись не найдена"}. {"Queries to the conference members are not allowed in this room","Запросы к пользователям в этой конференции запрещены"}. +{"Query to another users is forbidden","Запрос к другим пользователям запрещён"}. {"RAM and disc copy","ОЗУ и диск"}. {"RAM copy","ОЗУ"}. -{"Raw","Необработанный формат"}. {"Really delete message of the day?","Действительно удалить сообщение дня?"}. {"Recipient is not in the conference room","Адресата нет в конференции"}. -{"Register a Jabber account","Зарегистрировать Jabber-аккаунт"}. -{"Registered nicknames","Зарегистрированные псевдонимы"}. -{"Registered Users:","Зарегистрированные пользователи:"}. -{"Registered Users","Зарегистрированные пользователи"}. {"Register","Зарегистрировать"}. -{"Registration in mod_irc for ","Регистрация в mod_irc для "}. {"Remote copy","не хранится локально"}. -{"Remove All Offline Messages","Удалить все офлайновые сообщения"}. {"Remove User","Удалить пользователя"}. -{"Remove","Удалить"}. {"Replaced by new connection","Заменено новым соединением"}. +{"Request has timed out","Истекло время ожидания запроса"}. {"Resources","Ресурсы"}. {"Restart Service","Перезапустить службу"}. -{"Restart","Перезапустить"}. {"Restore Backup from File at ","Восстановление из резервной копии на "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Восстановить из бинарной резервной копии при следующем запуске (требует меньше памяти):"}. {"Restore binary backup immediately:","Восстановить из бинарной резервной копии немедленно:"}. @@ -304,16 +317,12 @@ {"Room creation is denied by service policy","Cоздавать конференцию запрещено политикой службы"}. {"Room description","Описание комнаты"}. {"Room Occupants","Участники комнаты"}. +{"Room terminates","Комната остановлена"}. {"Room title","Название комнаты"}. {"Roster groups allowed to subscribe","Группы списка контактов, которым разрешена подписка"}. -{"Roster of ","Ростер пользователя "}. {"Roster size","Размер списка контактов"}. -{"Roster","Ростер"}. -{"RPC Call Error","Ошибка вызова RPC"}. {"Running Nodes","Работающие узлы"}. -{"~s access rule configuration","Конфигурация правила доступа ~s"}. {"Saturday","Суббота"}. -{"Script check","Проверка сценария"}. {"Search Results for ","Результаты поиска в "}. {"Search users in ","Поиск пользователей в "}. {"Send announcement to all online users on all hosts","Разослать объявление всем подключённым пользователям на всех виртуальных серверах"}. @@ -321,89 +330,91 @@ {"Send announcement to all users on all hosts","Разослать объявление всем пользователям на всех виртуальных серверах"}. {"Send announcement to all users","Разослать объявление всем пользователям"}. {"September","сентября"}. -{"Server ~b","Сервер ~b"}. {"Server:","Сервер:"}. +{"Session state copying timed out","Таймаут копирования состояния сессии"}. {"Set message of the day and send to online users","Установить сообщение дня и разослать его подключённым пользователям"}. {"Set message of the day on all hosts and send to online users","Установить сообщение дня на всех виртуальных серверах и разослать его подключённым пользователям"}. {"Shared Roster Groups","Группы общих контактов"}. {"Show Integral Table","Показать интегральную таблицу"}. {"Show Ordinary Table","Показать обычную таблицу"}. {"Shut Down Service","Остановить службу"}. -{"~s invites you to the room ~s","~s приглашает вас в комнату ~s"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Некоторые Jabber-клиенты могут сохранять пароль на Вашем компьютере. Используйте эту функцию только в том случае, если считаете это безопасным."}. +{"SOCKS5 Bytestreams","Передача файлов через SOCKS5"}. {"Specify the access model","Укажите механизм управления доступом"}. {"Specify the event message type","Укажите тип сообщения о событии"}. {"Specify the publisher model","Условия публикации"}. -{"~s's Offline Messages Queue","Oчередь офлайновых сообщений ~s"}. -{"Start Modules at ","Запуск модулей на "}. -{"Start Modules","Запуск модулей"}. -{"Start","Запустить"}. -{"Statistics of ~p","статистика узла ~p"}. -{"Statistics","Статистика"}. -{"Stop Modules at ","Остановка модулей на "}. -{"Stop Modules","Остановка модулей"}. {"Stopped Nodes","Остановленные узлы"}. -{"Stop","Остановить"}. -{"Storage Type","Тип таблицы"}. {"Store binary backup:","Сохранить бинарную резервную копию:"}. {"Store plain text backup:","Сохранить текстовую резервную копию:"}. +{"Stream management is already enabled","Управление потоком уже активировано"}. +{"Stream management is not enabled","Управление потоком не активировано"}. {"Subject","Тема"}. {"Submitted","Отправлено"}. -{"Submit","Отправить"}. {"Subscriber Address","Адрес подписчика"}. -{"Subscription","Подписка"}. +{"Subscriptions are not allowed","Подписки недопустимы"}. {"Sunday","Воскресенье"}. {"That nickname is already in use by another occupant","Этот псевдоним уже занят другим участником"}. {"That nickname is registered by another person","Этот псевдоним зарегистрирован кем-то другим"}. +{"The account already exists","Учётная запись уже существует"}. {"The CAPTCHA is valid.","Проверка капчи прошла успешно."}. {"The CAPTCHA verification has failed","Проверка капчи не пройдена"}. +{"The captcha you entered is wrong","Неправильно введённое значение капчи"}. {"The collections with which a node is affiliated","Имя коллекции, в которую входит узел"}. +{"The feature requested is not supported by the conference","Запрашиваемое свойство не поддерживается этой конференцией"}. +{"The password contains unacceptable characters","Пароль содержит недопустимые символы"}. {"The password is too weak","Слишком слабый пароль"}. {"the password is","пароль:"}. -{"The password of your Jabber account was successfully changed.","Пароль Вашего Jabber-аккаунта был успешно изменен."}. -{"There was an error changing the password: ","Ошибка при смене пароля:"}. +{"The password was not changed","Пароль не был изменён"}. +{"The passwords are different","Пароли не совпадают"}. +{"The query is only allowed from local users","Запрос доступен только для локальных пользователей"}. +{"The query must not contain elements","Запрос не должен содержать элементов "}. +{"The stanza MUST contain only one element, one element, or one element","Строфа может содержать только один элемент , один элемент или один элемент "}. {"There was an error creating the account: ","Ошибка при создании аккаунта:"}. {"There was an error deleting the account: ","Ошибка при удалении аккаунта:"}. -{"This IP address is blacklisted in ~s","Этот IP адрес находится в чёрном списке ~s"}. -{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Регистр не имеет значения: \"маша\" и \"МАША\" будет считаться одним и тем же именем."}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Здесь Вы можете создать Jabber-аккаунт на этом Jabber-сервере. Ваш JID (Jabber-идентификатор) будет в виде: \"пользователь@сервер\". Пожалуйста, внимательно читайте инструкции для правильного заполнения полей."}. -{"This page allows to unregister a Jabber account in this Jabber server.","Здесь Вы можете удалить Jabber-аккаунт с этого сервера."}. +{"This room is not anonymous","Эта комната не анонимная"}. +{"This service can not process the address: ~s","Сервер не может обработать адрес: ~s"}. {"Thursday","Четверг"}. {"Time delay","По истечение"}. -{"Time","Время"}. +{"Timed out waiting for stream resumption","Истекло время ожидания возобновления потока"}. +{"To register, visit ~s","Для регистрации посетите ~s"}. +{"Token TTL","Токен TTL"}. +{"Too many active bytestreams","Слишком много активных потоков данных"}. {"Too many CAPTCHA requests","Слишком много запросов капчи"}. +{"Too many child elements","Слишком много дочерних элементов"}. +{"Too many elements","Слишком много элементов "}. +{"Too many elements","Слишком много элементов "}. {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Слишком много (~p) неудачных попыток аутентификации с этого IP-адреса (~s). Адрес будет разблокирован в ~s UTC"}. +{"Too many receiver fields were specified","Указано слишком много получателей"}. {"Too many unacked stanzas","Слишком много неподтверждённых пакетов"}. -{"To ~s","К ~s"}. -{"Total rooms","Все комнаты"}. -{"To","Кому"}. +{"Too many users in this conference","Слишком много пользователей в этой конференции"}. {"Traffic rate limit is exceeded","Превышен лимит скорости посылки информации"}. -{"Transactions Aborted:","Транзакции отмененные:"}. -{"Transactions Committed:","Транзакции завершенные:"}. -{"Transactions Logged:","Транзакции запротоколированные:"}. -{"Transactions Restarted:","Транзакции перезапущенные:"}. {"Tuesday","Вторник"}. {"Unable to generate a CAPTCHA","Не получилось создать капчу"}. +{"Unable to register route on existing local domain","Нельзя регистрировать маршруты на существующие локальные домены"}. {"Unauthorized","Не авторизован"}. -{"Unregister a Jabber account","Удалить Jabber-аккаунт"}. +{"Unexpected action","Неожиданное действие"}. +{"Unexpected error condition: ~p","Неожиданная ошибка: ~p"}. {"Unregister","Удалить"}. +{"Unsupported element","Элемент не поддерживается"}. +{"Unsupported version","Неподдерживаемая версия"}. {"Update message of the day (don't send)","Обновить сообщение дня (не рассылать)"}. {"Update message of the day on all hosts (don't send)","Обновить сообщение дня на всех виртуальных серверах (не рассылать)"}. -{"Update plan","План обновления"}. -{"Update ~p","Обновление ~p"}. -{"Update script","Сценарий обновления"}. -{"Update","Обновить"}. -{"Uptime:","Время работы:"}. -{"Use of STARTTLS required","Вы обязаны использовать STARTTLS"}. +{"User already exists","Пользователь уже существует"}. {"User JID","JID пользователя"}. +{"User (jid)","Пользователь (XMPP адрес)"}. {"User Management","Управление пользователями"}. +{"User removed","Пользователь удалён"}. +{"User session not found","Сессия пользователя не найдена"}. +{"User session terminated","Сессия пользователя завершена"}. {"Username:","Имя пользователя:"}. {"Users are not allowed to register accounts so quickly","Пользователи не могут регистрировать учётные записи так быстро"}. {"Users Last Activity","Статистика последнего подключения пользователей"}. {"Users","Пользователи"}. -{"User ~s","Пользователь ~s"}. {"User","Пользователь"}. -{"Validate","Утвердить"}. +{"Value 'get' of 'type' attribute is not allowed","Значение 'get' атрибута 'type' недопустимо"}. +{"Value of '~s' should be boolean","Значение '~s' должно быть булевым"}. +{"Value of '~s' should be datetime string","Значение '~s' должно быть датой"}. +{"Value of '~s' should be integer","Значение '~s' должно быть целочисленным"}. +{"Value 'set' of 'type' attribute is not allowed","Значение 'set' атрибута 'type' недопустимо"}. {"vCard User Search","Поиск пользователей по vCard"}. {"Virtual Hosts","Виртуальные хосты"}. {"Visitors are not allowed to change their nicknames in this room","Посетителям запрещено изменять свои псевдонимы в этой комнате"}. @@ -413,16 +424,17 @@ {"Wednesday","Среда"}. {"When to send the last published item","Когда посылать последний опубликованный элемент"}. {"Whether to allow subscriptions","Разрешить подписку"}. -{"You can later change your password using a Jabber client.","Позже Вы можете изменить пароль через Jabber-клиент."}. +{"Wrong parameters in the web formulary","Недопустимые параметры веб-формы"}. +{"Wrong xmlns","Неправильный xmlns"}. +{"You are being removed from the room because of a system shutdown","Вы покинули комнату из-за останова системы"}. +{"You are not joined to the channel","Вы не присоединены к каналу"}. {"You have been banned from this room","Вам запрещено входить в эту конференцию"}. +{"You have joined too many conferences","Вы присоединены к слишком большому количеству конференций"}. {"You must fill in field \"Nickname\" in the form","Вы должны заполнить поле \"Псевдоним\" в форме"}. {"You need a client that supports x:data and CAPTCHA to register","Чтобы зарегистрироваться, требуется x:data-совместимый клиент"}. {"You need a client that supports x:data to register the nickname","Чтобы зарегистрировать псевдоним, требуется x:data-совместимый клиент"}. -{"You need an x:data capable client to configure mod_irc settings","Чтобы настроить параметры mod_irc, требуется x:data-совместимый клиент"}. -{"You need an x:data capable client to configure room","Чтобы сконфигурировать комнату, требуется x:data-совместимый клиент"}. {"You need an x:data capable client to search","Чтобы воспользоваться поиском, требуется x:data-совместимый клиент"}. {"Your active privacy list has denied the routing of this stanza.","Маршрутизация этой строфы запрещена вашим активным списком приватности."}. {"Your contact offline message queue is full. The message has been discarded.","Очередь недоставленных сообщений Вашего адресата переполнена. Сообщение не было сохранено."}. -{"Your Jabber account was successfully created.","Ваш Jabber-аккаунт был успешно создан."}. -{"Your Jabber account was successfully deleted.","Ваш Jabber-аккаунт был успешно удален."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","Ваши сообщения к ~s блокируются. Для снятия блокировки перейдите по ссылке ~s"}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Ваши запросы на добавление в контакт-лист, а также сообщения к ~s блокируются. Для снятия блокировки перейдите по ссылке ~s"}. +{"You're not allowed to create nodes","Вам не разрешается создавать узлы"}. diff --git a/priv/msgs/ru.po b/priv/msgs/ru.po deleted file mode 100644 index fe8687460..000000000 --- a/priv/msgs/ru.po +++ /dev/null @@ -1,1935 +0,0 @@ -# , 2010. -msgid "" -msgstr "" -"Project-Id-Version: 2.1.0-alpha\n" -"POT-Creation-Date: \n" -"PO-Revision-Date: 2015-09-07 15:04+0300\n" -"Last-Translator: Evgeniy Khramtsov \n" -"Language-Team: Russian \n" -"Language: ru\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Russian (русский)\n" -"X-Additional-Translator: Konstantin Khomoutov\n" -"X-Additional-Translator: Sergei Golovan\n" -"X-Generator: Lokalize 1.0\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" -"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "Вы обязаны использовать STARTTLS" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Не указан ресурс" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Заменено новым соединением" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "выгнали из комнаты" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "" -"Маршрутизация этой строфы запрещена вашим активным списком приватности." - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "Слишком много неподтверждённых пакетов" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Введите увиденный текст" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "" -"Ваши сообщения к ~s блокируются. Для снятия блокировки перейдите по ссылке ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "Если вы не видите изображение капчи, перейдите по ссылке." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "Ссылка на капчу" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "Проверка капчи прошла успешно." - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Пользователь" - -#: ejabberd_oauth.erl:256 -#, fuzzy -msgid "Server" -msgstr "Сервер:" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Пароль" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "Не авторизован" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "Web-интерфейс администрирования ejabberd" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Администрирование" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Списки управления доступом" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Отправлено" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Неправильный формат" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Отправить" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "Необработанный формат" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Удалить выделенные" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Правила доступа" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "Конфигурация правила доступа ~s" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "Виртуальные хосты" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Пользователи" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "Подключённые пользователи" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Статистика последнего подключения пользователей" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Период" - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "За последний месяц" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "За последний год" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "Вся статистика" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Показать обычную таблицу" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Показать интегральную таблицу" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Статистика" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "Не Найдено" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Узел не найден" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Добавить" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Хост" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Зарегистрированные пользователи" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Добавить пользователя" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Офлайновые сообщения" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Последнее подключение" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Никогда" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Подключён" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Зарегистрированные пользователи:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Подключённые пользователи:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Исходящие s2s-серверы:" - -#: ejabberd_web_admin.erl:1559 -#, fuzzy -msgid "Incoming s2s Connections:" -msgstr "Исходящие s2s-серверы:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Нет" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Сменить пароль" - -#: ejabberd_web_admin.erl:1673 -msgid "User ~s" -msgstr "Пользователь ~s" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Подключённые ресурсы:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Пароль:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Удалить пользователя" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Нет данных" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Узлы" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Работающие узлы" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Остановленные узлы" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -msgid "Node ~p" -msgstr "Узел ~p" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "База данных" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Резервное копирование" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Прослушиваемые порты" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Обновить" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Перезапустить" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Остановить" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Модули" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "Ошибка вызова RPC" - -#: ejabberd_web_admin.erl:1917 -msgid "Database Tables at ~p" -msgstr "Таблицы базы данных на ~p" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Название" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Тип таблицы" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Элементы" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Память" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Ошибка" - -#: ejabberd_web_admin.erl:1955 -msgid "Backup of ~p" -msgstr "Резервное копирование ~p" - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Заметьте, что здесь производится резервное копирование только встроенной " -"базы данных Mnesia. Если Вы также используете другое хранилище данных " -"(например с помощью модуля ODBC), то его резервное копирование следует " -"осуществлять отдельно." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Сохранить бинарную резервную копию:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "Продолжить" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Восстановить из бинарной резервной копии немедленно:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"Восстановить из бинарной резервной копии при следующем запуске (требует " -"меньше памяти):" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Сохранить текстовую резервную копию:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Восстановить из текстовой резервной копии немедленно:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "Импорт пользовательских данных из файла формата PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" -"Экспорт данных всех пользователей сервера в файлы формата PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "" -"Экспорт пользовательских данных домена в файлы формата PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "Экспортировать все таблицы в виде SQL запросов в файл:" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Импорт пользовательских данных из буферного файла jabberd14:" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Импорт пользовательских данных из буферной директории jabberd14:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Прослушиваемые порты на " - -#: ejabberd_web_admin.erl:2144 -msgid "Modules at ~p" -msgstr "Модули на ~p" - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "статистика узла ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Время работы:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "Процессорное время:" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Транзакции завершенные:" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Транзакции отмененные:" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Транзакции перезапущенные:" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Транзакции запротоколированные:" - -#: ejabberd_web_admin.erl:2243 -msgid "Update ~p" -msgstr "Обновление ~p" - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "План обновления" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Изменённые модули" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Сценарий обновления" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Низкоуровневый сценарий обновления" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Проверка сценария" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Порт" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Протокол" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Модуль" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Параметры" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Запустить" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Команды" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Пинг" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Понг" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "Действительно удалить сообщение дня?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Тема" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Тело сообщения" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "Тело объявления не должно быть пустым" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Объявления" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Разослать объявление всем пользователям" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Разослать объявление всем пользователям на всех виртуальных серверах" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Разослать объявление всем подключённым пользователям" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "" -"Разослать объявление всем подключённым пользователям на всех виртуальных " -"серверах" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Установить сообщение дня и разослать его подключённым пользователям" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" -"Установить сообщение дня на всех виртуальных серверах и разослать его " -"подключённым пользователям" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Обновить сообщение дня (не рассылать)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "Обновить сообщение дня на всех виртуальных серверах (не рассылать)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Удалить сообщение дня" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Удалить сообщение дня со всех виртуальных серверов" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Конфигурация" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Запуск модулей" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Остановка модулей" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Восстановление из резервной копии" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Копирование в текстовый файл" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Импорт из файла" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Импорт из директории" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Перезапустить службу" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Остановить службу" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Удалить пользователя" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Завершить сеанс пользователя" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Получить пароль пользователя" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Изменить пароль пользователя" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "Получить время последнего подключения пользователя" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Получить статистику по пользователю" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Получить количество зарегистрированных пользователей" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Получить количество подключённых пользователей" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Управление пользователями" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Все пользователи" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Исходящие s2s-соединения" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Управление резервным копированием" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Импорт пользователей из спула jabberd14" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "К ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "От ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Конфигурация таблиц базы данных на " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Выберите тип хранения таблиц" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "только диск" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "ОЗУ и диск" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "ОЗУ" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "не хранится локально" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Остановка модулей на " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Выберите модули, которые следует остановить" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Запуск модулей на " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Введите список вида {Module, [Options]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Список запускаемых модулей" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Резервное копирование в файл на " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Введите путь к резервному файлу" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Путь к файлу" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Восстановление из резервной копии на " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Копирование в текстовый файл на " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Введите путь к текстовому файлу" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Импорт пользователя из файла на " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Введите путь к файлу из спула jabberd14" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Импорт пользователей из директории на " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Введите путь к директории спула jabberd14" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Путь к директории" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "По истечение" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Конфигурация списков управления доступом" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Списки управления доступом" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Конфигурация доступа" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Правила доступа" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Проверка пароля" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Количество зарегистрированных пользователей" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Количество подключённых пользователей" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Время последнего подключения" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "Размер списка контактов" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "IP адреса" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Ресурсы" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Администрирование " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Действие над пользователем" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Изменить параметры" - -#: mod_fail2ban.erl:95 -msgid "" -"Too many (~p) failed authentications from this IP address (~s). The address " -"will be unblocked at ~s UTC" -msgstr "" -"Слишком много (~p) неудачных попыток аутентификации с этого IP-адреса (~s). " -"Адрес будет разблокирован в ~s UTC" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "" - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "" - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "Этот IP адрес находится в чёрном списке ~s" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "Доступ запрещён политикой службы" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "IRC Транспорт" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "ejabberd IRC модуль" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "Чтобы настроить параметры mod_irc, требуется x:data-совместимый клиент" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Регистрация в mod_irc для " - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Введите имя пользователя, кодировки, порты и пароли, которые будут " -"использоваться при подключении к IRC-серверам" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "Имя пользователя IRC" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Чтобы указать различные порты, пароли, кодировки для разных серверов IRC, " -"заполните список значениями в формате '{\"сервер IRC\", \"кодировка\", порт, " -"\"пароль\"}'. По умолчанию сервис использует кодировку \"~s\", порт ~p, " -"пустой пароль." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Пример: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Параметры соединения" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "Присоединиться к каналу IRC" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "Канал IRC (без символа #)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "Сервер IRC" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Присоединяйтесь к каналу IRC" - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Присоединиться к каналу IRC с Jabber ID: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "Настройки IRC" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Введите имя пользователя и кодировки, которые будут использоваться при " -"подключении к IRC-серверам. Нажмите 'Далее' для получения дополнительных " -"полей для заполнения. Нажмите 'Завершить' для сохранения настроек." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "Имя пользователя IRC" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Пароль ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Порт ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Кодировка сервера ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Сервер ~b" - -#: mod_mam.erl:541 -#, fuzzy -msgid "Only members may query archives of this room" -msgstr "Только модераторы могут изменять тему в этой комнате" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "Только администратор службы может посылать служебные сообщения" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "Cоздавать конференцию запрещено политикой службы" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "Конференция не существует" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Комнаты" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "Чтобы зарегистрировать псевдоним, требуется x:data-совместимый клиент" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Регистрация псевдонима на " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Введите псевдоним, который Вы хотели бы зарегистрировать" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Псевдоним" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "Этот псевдоним зарегистрирован кем-то другим" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Вы должны заполнить поле \"Псевдоним\" в форме" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "ejabberd MUC модуль" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "Конференция" - -#: mod_muc_admin.erl:249 -msgid "Total rooms" -msgstr "Все комнаты" - -#: mod_muc_admin.erl:250 -msgid "Permanent rooms" -msgstr "Постоянные комнаты" - -#: mod_muc_admin.erl:251 -msgid "Registered nicknames" -msgstr "Зарегистрированные псевдонимы" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "Список комнат" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "Конфигурация комнаты изменилась" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "вошёл(а) в комнату" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "вышел(а) из комнаты" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "запретили входить в комнату" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "выгнали из комнаты вследствие смены ранга" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "выгнали из комнаты потому что она стала только для членов" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "выгнали из комнаты из-за останова системы" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "изменил(а) имя на" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " установил(а) тему: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "Комната создана" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "Комната уничтожена" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "Комната запущена" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "Комната остановлена" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "Понедельник" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "Вторник" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "Среда" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "Четверг" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "Пятница" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "Суббота" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "Воскресенье" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "января" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "февраля" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "марта" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "апреля" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "мая" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "июня" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "июля" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "августа" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "сентября" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "октября" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "ноября" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "декабря" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Конфигурация комнаты" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "Участники комнаты" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "Превышен лимит скорости посылки информации" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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 "" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "Не разрешается посылать частные сообщения прямо в конференцию" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "" -"Пожалуйста, подождите перед тем как подать новый запрос на право голоса" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "Запросы на право голоса отключены в этой конференции" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "Ошибка обработки JID из вашего запроса на право голоса" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "Только модераторы могут утверждать запросы на право голоса" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Неправильный тип сообщения" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "Нельзя посылать частные сообщения типа \"groupchat\"" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "Адресата нет в конференции" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "Запрещено посылать приватные сообщения" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Только присутствующим разрешается посылать сообщения в конференцию" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "Только присутствующим разрешается посылать запросы в конференцию" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "Запросы к пользователям в этой конференции запрещены" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "Только модераторы и участники могут изменять тему в этой комнате" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Только модераторы могут изменять тему в этой комнате" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "Посетителям не разрешается посылать сообщения всем присутствующим" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "Посетителям запрещено изменять свои псевдонимы в этой комнате" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "Этот псевдоним уже занят другим участником" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Вам запрещено входить в эту конференцию" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "В эту конференцию могут входить только её члены" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "Чтобы войти в эту конференцию, нужен пароль" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "Слишком много запросов капчи" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "Не получилось создать капчу" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Неправильный пароль" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "Требуются права администратора" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "Требуются права модератора" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "Jabber ID ~s недопустимый" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "Псевдоним ~s в комнате отсутствует" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Недопустимый ранг: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Недопустимая роль: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "Требуются права владельца" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "Конфигурация комнаты ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Название комнаты" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "Описание комнаты" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Сделать комнату постоянной" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Сделать комнату видимой всем" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "Сделать список участников видимым всем" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Сделать комнату защищённой паролем" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Максимальное количество участников" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Не ограничено" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Сделать реальные Jabber ID участников видимыми" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "только модераторам" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "всем участникам" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "" - -#: mod_muc_room.erl:3486 -#, fuzzy -msgid "Moderator" -msgstr "только модераторам" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Комната только для зарегистрированных участников" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Сделать комнату модерируемой" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Сделать пользователей участниками по умолчанию" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Разрешить пользователям изменять тему" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Разрешить приватные сообщения" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "Разрешить посетителям посылать приватные сообщения" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "никто" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Разрешить iq-запросы к пользователям" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Разрешить пользователям посылать приглашения" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "" -"Разрешить посетителям вставлять текcт статуса в сообщения о присутствии" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Разрешить посетителям изменять псевдоним" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "Разрешить посетителям запрашивать право голоса" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "Минимальный интервал между запросами на право голоса" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "Сделать комнату защищённой капчей" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "Включить хранение сообщений" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "Исключить показ капчи для списка Jabber ID" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Включить журналирование" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "Чтобы сконфигурировать комнату, требуется x:data-совместимый клиент" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Число присутствующих" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "приватная, " - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "Запрос на право голоса" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "Подтвердите или отклоните право голоса." - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "JID пользователя" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "Предоставить голос?" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s приглашает вас в комнату ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "пароль:" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "Мультикаст" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "ejabberd Multicast сервис" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "" -"Очередь недоставленных сообщений Вашего адресата переполнена. Сообщение не " -"было сохранено." - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "Oчередь офлайновых сообщений ~s" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Время" - -#: mod_offline.erl:812 -msgid "From" -msgstr "От кого" - -#: mod_offline.erl:813 -msgid "To" -msgstr "Кому" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Пакет" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Офлайновые сообщения:" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "Удалить все офлайновые сообщения" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "ejabberd SOCKS5 Bytestreams модуль" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Публикация-Подписка" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "Модуль ejabberd Публикации-Подписки" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "Запрос подписчика PubSub" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Решите: предоставить ли подписку этому объекту." - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "ID узла" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Адрес подписчика" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "Разрешить этому Jabber ID подписаться на данный узел?" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Доставлять вместе с уведомлениями o публикациях сами публикации" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Доставлять уведомления о событиях" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Уведомлять подписчиков об изменении конфигурации сборника" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Уведомлять подписчиков об удалении сборника" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Уведомлять подписчиков об удалении публикаций из сборника" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Сохранять публикации в хранилище" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "Легко запоминаемое имя для узла" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Максимальное число сохраняемых публикаций" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Разрешить подписку" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Укажите механизм управления доступом" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Группы списка контактов, которым разрешена подписка" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Условия публикации" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "Очищать все записи автора публикации когда он отключается" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "Укажите тип сообщения о событии" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Максимальный размер полезной нагрузки в байтах" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "Когда посылать последний опубликованный элемент" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Доставлять уведомления только доступным пользователям" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "Имя коллекции, в которую входит узел" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "Проверка капчи не пройдена" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "Чтобы зарегистрироваться, требуется x:data-совместимый клиент" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "Выберите имя пользователя и пароль для регистрации на этом сервере" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "Слишком слабый пароль" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "Пользователи не могут регистрировать учётные записи так быстро" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "Ваш Jabber-аккаунт был успешно создан." - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "Ошибка при создании аккаунта:" - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "Ваш Jabber-аккаунт был успешно удален." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "Ошибка при удалении аккаунта:" - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "Пароль Вашего Jabber-аккаунта был успешно изменен." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "Ошибка при смене пароля:" - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Регистрация Jabber-аккаунта" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Зарегистрировать Jabber-аккаунт" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Удалить Jabber-аккаунт" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"Здесь Вы можете создать Jabber-аккаунт на этом Jabber-сервере. Ваш JID " -"(Jabber-идентификатор) будет в виде: \"пользователь@сервер\". Пожалуйста, " -"внимательно читайте инструкции для правильного заполнения полей." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "Имя пользователя:" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" -"Регистр не имеет значения: \"маша\" и \"МАША\" будет считаться одним и тем " -"же именем." - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "Недопустимые символы:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "Сервер:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "Не говорите никому свой пароль, даже администраторам сервера." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "Позже Вы можете изменить пароль через Jabber-клиент." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Некоторые Jabber-клиенты могут сохранять пароль на Вашем компьютере. " -"Используйте эту функцию только в том случае, если считаете это безопасным." - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"Запомните пароль или запишите его на бумаге, которую сохраните в безопасном " -"месте. В Jabber'е нет автоматизированного средства восстановления пароля в " -"том случае, если Вы его забудете." - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "Проверка пароля:" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "Зарегистрировать" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "Старый пароль:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "Новый пароль:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "Здесь Вы можете удалить Jabber-аккаунт с этого сервера." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "Удалить" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Подписка" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Ожидание" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Группы" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Утвердить" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Удалить" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Ростер пользователя " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Добавить Jabber ID" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Ростер" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Группы общих контактов" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Название:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Описание:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Члены:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Видимые группы:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Группа " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Erlang Jabber Server" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "День рождения" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Город" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "Страна" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "Электронная почта" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Фамилия" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Заполните форму для поиска пользователя Jabber (Если добавить * в конец " -"поля, то происходит поиск подстроки)" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Полное имя" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Отчество" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Название организации" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Отдел организации" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Поиск пользователей в " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "Чтобы воспользоваться поиском, требуется x:data-совместимый клиент" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "Поиск пользователей по vCard" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "ejabberd vCard модуль" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Результаты поиска в " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "Заполните форму для поиска пользователя Jabber" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Исходящие s2s-серверы:" - -#~ msgid "Delete" -#~ msgstr "Удалить" - -#~ msgid "This room is not anonymous" -#~ msgstr "Эта комната не анонимная" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "" -#~ "Этого участника выгнали из комнаты за то, что он послал сообщение об " -#~ "ошибке" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Этого участника выгнали из комнаты за то, что он послал сообщение об " -#~ "ошибке другому участнику" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "" -#~ "Этого участника выгнали из комнаты за то, что он послал присутствие с " -#~ "ошибкой" - -#, fuzzy -#~ msgid "Captcha test failed" -#~ msgstr "Проверка капчи прошла успешно." diff --git a/priv/msgs/sk.msg b/priv/msgs/sk.msg index 1be739281..54f711610 100644 --- a/priv/msgs/sk.msg +++ b/priv/msgs/sk.msg @@ -1,20 +1,19 @@ -%% -*- coding: latin-1 -*- -{"Access Configuration","Konfigurácia prístupu"}. -{"Access Control List Configuration","Konfigurácia zoznamu prístupových oprávnení (ACL)"}. -{"Access control lists","Zoznamy prístupových oprávnení (ACL)"}. -{"Access Control Lists","Zoznamy prístupových oprávnení (ACL)"}. -{"Access denied by service policy","Prístup bol zamietnutý nastavením služby"}. -{"Access rules","Prístupové pravidlá"}. -{"Access Rules","Prístupové pravidlá"}. -{"Action on user","Operácia aplikovaná na užívateľa"}. -{"Add Jabber ID","Pridať Jabber ID"}. -{"Add New","Pridať nový"}. -{"Add User","Pridať používateľa"}. -{"Administration","Administrácia"}. -{"Administration of ","Administrácia "}. -{"Administrator privileges required","Sú potrebné práva administrátora"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" has set the subject to: ","zmenil(a) tému na: "}. {"A friendly name for the node","Prístupný názov pre uzol"}. +{"A password is required to enter this room","Pre vstup do miestnosti je potrebné heslo"}. +{"Access denied by service policy","Prístup bol zamietnutý nastavením služby"}. +{"Action on user","Operácia aplikovaná na užívateľa"}. +{"Add User","Pridať používateľa"}. +{"Administration of ","Administrácia "}. +{"Administration","Administrácia"}. +{"Administrator privileges required","Sú potrebné práva administrátora"}. {"All activity","Všetky aktivity"}. +{"All Users","Všetci užívatelia"}. {"Allow this Jabber ID to subscribe to this pubsub node?","Dovoliť tomuto Jabber ID odoberať PubSub uzol?"}. {"Allow users to change the subject","Povoliť užívateľom meniť tému"}. {"Allow users to query other users","Povoliť užívateľom dotazovať sa informácie o iných užívateľoch"}. @@ -24,10 +23,7 @@ {"Allow visitors to send private messages to","Povoliť užívateľom odosielať súkromné správy"}. {"Allow visitors to send status text in presence updates","Návštevníci môžu posielať textové informácie v stavových správach"}. {"Allow visitors to send voice requests","Povoliť používateľom posielanie pozvánok"}. -{"All Users","Všetci užívatelia"}. {"Announcements","Oznámenia"}. -{"anyone","všetkým"}. -{"A password is required to enter this room","Pre vstup do miestnosti je potrebné heslo"}. {"April","Apríl"}. {"August","August"}. {"Backup Management","Správa zálohovania"}. @@ -46,88 +42,61 @@ {"Chatroom is stopped","Diskusná miestnosť je pozastavená"}. {"Chatrooms","Diskusné miestnosti"}. {"Choose a username and password to register with this server","Zvolte meno užívateľa a heslo pre registráciu na tomto servere"}. -{"Choose modules to stop","Vyberte moduly, ktoré majú byť zastavené"}. {"Choose storage type of tables","Vyberte typ úložiska pre tabuľky"}. {"Choose whether to approve this entity's subscription.","Zvolte, či chcete povoliť toto odoberanie"}. {"City","Mesto"}. {"Commands","Príkazy"}. {"Conference room does not exist","Diskusná miestnosť neexistuje"}. -{"Configuration","Konfigurácia"}. {"Configuration of room ~s","Konfigurácia miestnosti ~s"}. -{"Connected Resources:","Pripojené zdroje:"}. -{"Connections parameters","Parametre spojenia"}. +{"Configuration","Konfigurácia"}. {"Country","Krajina"}. -{"CPU Time:","Čas procesoru"}. -{"Database","Databáza"}. {"Database Tables Configuration at ","Konfigurácia databázových tabuliek "}. +{"Database","Databáza"}. {"December","December"}. {"Default users as participants","Užívatelia sú implicitne členmi"}. {"Delete message of the day on all hosts","Zmazať správu dňa na všetkých serveroch"}. {"Delete message of the day","Zmazať správu dňa"}. -{"Delete Selected","Zmazať vybrané"}. {"Delete User","Vymazať užívateľa"}. {"Deliver event notifications","Doručiť oznamy o udalosti"}. {"Deliver payloads with event notifications","Doručiť náklad s upozornením na udalosť"}. -{"Description:","Popis:"}. {"Disc only copy","Len kópia disku"}. -{"Displayed Groups:","Zobrazené skupiny:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","Nevyzrádzajte heslo nikomu, ani administrátorom tohoto Jabber servera."}. {"Dump Backup to Text File at ","Uložiť zálohu do textového súboru na "}. {"Dump to Text File","Uložiť do textového súboru"}. {"Edit Properties","Editovať vlastnosti"}. {"Either approve or decline the voice request.","Povolte alebo zamietnite žiadosť o Voice."}. -{"ejabberd IRC module","ejabberd IRC modul"}. {"ejabberd MUC module","ejabberd MUC modul"}. {"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe modul"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams modul"}. {"ejabberd vCard module","ejabberd vCard modul"}. {"ejabberd Web Admin","ejabberd Web Admin"}. -{"Elements","Prvky"}. {"Email","E-mail"}. {"Enable logging","Zapnúť zaznamenávanie histórie"}. -{"Encoding for server ~b","Kódovanie pre server ~b"}. {"End User Session","Ukončiť reláciu užívateľa"}. -{"Enter list of {Module, [Options]}","Vložte zoznam modulov {Modul, [Parametre]}"}. {"Enter nickname you want to register","Zadajte prezývku, ktorú chcete registrovať"}. {"Enter path to backup file","Zadajte cestu k súboru so zálohou"}. {"Enter path to jabberd14 spool dir","Zadajte cestu k jabberd14 spool adresáru"}. {"Enter path to jabberd14 spool file","Zadajte cestu k spool súboru jabberd14"}. {"Enter path to text file","Zadajte cestu k textovému súboru"}. {"Enter the text you see","Zadajte zobrazený text"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Vložte meno používateľa a kódovanie, ktoré chcete používať pri pripojení na IRC servery. Kliknutím na tlačítko 'Ďalej' môžete zadať niektoré ďalšie hodnoty. Pomocou 'Ukončiť ' uložíte nastavenia."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Vložte meno používateľa, kódovanie, porty a heslo ktoré chcete používať pri pripojení na IRC server"}. -{"Erlang Jabber Server","Erlang Jabber Server"}. -{"Error","Chyba"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Príklad: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Exclude Jabber IDs from CAPTCHA challenge","Nepoužívať CAPTCHA pre nasledujúce Jabber ID"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportovať dáta všetkých uživateľov na serveri do súborov PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Exportovať dáta uživateľov na hostitelovi do súborov PIEFXIS (XEP-0227):"}. {"Failed to extract JID from your voice request approval","Nepodarilo sa nájsť JID v súhlase o Voice."}. {"Family Name","Priezvisko"}. {"February","Február"}. -{"Fill in fields to search for any matching Jabber User","Vyplnte políčka pre vyhľadávanie Jabber užívateľa"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Pre vyhľadanie Jabber používateľa vyplňte formulár (pridajte znak * na koniec, pre vyhľadanie podreťazca)"}. {"Friday","Piatok"}. -{"From","Od"}. -{"From ~s","Od ~s"}. {"Full Name","Celé meno: "}. {"Get Number of Online Users","Zobraziť počet pripojených užívateľov"}. {"Get Number of Registered Users","Zobraziť počet registrovaných užívateľov"}. {"Get User Last Login Time","Zobraziť čas posledného prihlásenia"}. -{"Get User Password","Zobraziť heslo užívateľa"}. {"Get User Statistics","Zobraziť štatistiku užívateľa"}. {"Grant voice to this person?","Prideltiť Voice tejto osobe?"}. -{"Group ","Skupina "}. -{"Groups","Skupiny"}. {"has been banned","bol(a) zablokovaný(á)"}. -{"has been kicked because of an affiliation change","bol vyhodený(á) kvôli zmene priradenia"}. {"has been kicked because of a system shutdown","bol vyhodený(á) kvôli reštartu systému"}. +{"has been kicked because of an affiliation change","bol vyhodený(á) kvôli zmene priradenia"}. {"has been kicked because the room has been changed to members-only","bol vyhodený(á), pretože miestnosť bola vyhradená len pre členov"}. {"has been kicked","bol(a) vyhodený(á) z miestnosti"}. -{" has set the subject to: ","zmenil(a) tému na: "}. -{"Host","Server"}. {"If you don't see the CAPTCHA image here, visit the web page.","Pokiaľ nevidíte obrázok CAPTCHA, navštívte webovú stránku."}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Ak chcete zadať iné porty, heslá a kódovania pre IRC servery, vyplnte zoznam s hodnotami vo formáte '{\"irc server\",\"kódovanie\", \"port\", \"heslo\"}'. Predvolenéi hodnoty pre túto službu sú: kódovanie \"~s\", port ~p a žiadne heslo."}. {"Import Directory","Import adresára"}. {"Import File","Import súboru"}. {"Import user data from jabberd14 spool file:","Importovať dáta užívateľov z jabberd14 spool súboru:"}. @@ -138,28 +107,13 @@ {"Import Users From jabberd14 Spool Files","Importovať užívateľov z jabberd14 spool súborov"}. {"Improper message type","Nesprávny typ správy"}. {"Incorrect password","Nesprávne heslo"}. -{"Invalid affiliation: ~s","Neplatné priradenie: ~s"}. -{"Invalid role: ~s","Neplatná rola: ~s"}. {"IP addresses","IP adresa"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","IRC kanál (bez počiatočnej #)"}. -{"IRC server","IRC server"}. -{"IRC settings","Nastavania IRC"}. -{"IRC Transport","IRC Transport"}. -{"IRC username","IRC prezývka"}. -{"IRC Username","IRC prezývka"}. {"is now known as","sa premenoval(a) na"}. -{"It is not allowed to send private messages","Nieje povolené posielať súkromné správy"}. {"It is not allowed to send private messages of type \"groupchat\"","Nie je dovolené odoslanie súkromnej správy typu \"Skupinová správa\" "}. {"It is not allowed to send private messages to the conference","Nie je povolené odosielať súkromné správy do konferencie"}. -{"Jabber Account Registration","Registrácia jabber účtu"}. {"Jabber ID","Jabber ID"}. -{"Jabber ID ~s is invalid","Jabber ID ~s je neplatné"}. {"January","Január"}. -{"Join IRC channel","Pripojit IRC kanál"}. {"joins the room","vstúpil(a) do miestnosti"}. -{"Join the IRC channel here.","Propojiť IRC kanál sem."}. -{"Join the IRC channel in this Jabber ID: ~s","Pripojit IRC kanál k tomuto Jabber ID: ~s"}. {"July","Júl"}. {"June","Jún"}. {"Last Activity","Posledná aktivita"}. @@ -167,10 +121,6 @@ {"Last month","Posledný mesiac"}. {"Last year","Posledný rok"}. {"leaves the room","odišiel(a) z miestnosti"}. -{"Listened Ports at ","Otvorené porty na "}. -{"Listened Ports","Otvorené portov"}. -{"List of modules to start","Zoznam modulov, ktoré majú byť spustené"}. -{"Low level update script","Nízkoúrovňový aktualizačný skript"}. {"Make participants list public","Nastaviť zoznam zúčastnených ako verejný"}. {"Make room CAPTCHA protected","Chrániť miestnosť systémom CAPTCHA"}. {"Make room members-only","Nastaviť miestnosť len pre členov"}. @@ -179,39 +129,28 @@ {"Make room persistent","Nastaviť miestnosť ako trvalú"}. {"Make room public searchable","Nastaviť miestnosť ako verejne prehľadávateľnú"}. {"March","Marec"}. -{"Maximum Number of Occupants","Počet účastníkov"}. -{"Max # of items to persist","Maximálny počet položiek, ktoré je možné natrvalo uložiť"}. {"Max payload size in bytes","Maximálny náklad v bajtoch"}. +{"Maximum Number of Occupants","Počet účastníkov"}. {"May","Máj"}. -{"Members:","Členovia:"}. {"Membership is required to enter this room","Pre vstup do miestnosti je potrebné byť členom"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Zapamätajte si heslo alebo si ho zapíšte na papier. Jabber neposkytuje automatickú funkciu ako zistiť zabudnuté heslo. "}. -{"Memory","Pamäť"}. {"Message body","Telo správy"}. {"Middle Name","Prostredné meno: "}. {"Minimum interval between voice requests (in seconds)","Minimum interval between voice requests (in seconds)"}. {"Moderator privileges required","Sú potrebné práva moderátora"}. -{"moderators only","moderátorom"}. -{"Modified modules","Modifikované moduly"}. -{"Module","Modul"}. -{"Modules","Moduly"}. {"Monday","Pondelok"}. -{"Name:","Meno:"}. {"Name","Meno"}. {"Never","Nikdy"}. {"New Password:","Nové heslo:"}. -{"Nickname","Prezývka"}. {"Nickname Registration at ","Registrácia prezývky na "}. {"Nickname ~s does not exist in the room","Prezývka ~s v miestnosti neexistuje"}. -{"nobody","nikto"}. +{"Nickname","Prezývka"}. {"No body provided for announce message","Správa neobsahuje text"}. {"No Data","Žiadne dáta"}. +{"No limit","Bez limitu"}. {"Node ID","ID uzlu"}. {"Node not found","Uzol nenájdený"}. {"Nodes","Uzly"}. -{"No limit","Bez limitu"}. {"None","Nič"}. -{"No resource provided","Nebol poskytnutý žiadny zdroj"}. {"Not Found","Nebol nájdený"}. {"Notify subscribers when items are removed from the node","Upozorniť prihlásených používateľov na odstránenie položiek z uzlu"}. {"Notify subscribers when the node configuration changes","Upozorniť prihlásených používateľov na zmenu nastavenia uzlu"}. @@ -221,13 +160,10 @@ {"Number of online users","Počet online užívateľov"}. {"Number of registered users","Počet registrovaných užívateľov"}. {"October","Október"}. -{"Offline Messages:","Offline správy"}. -{"Offline Messages","Offline správy"}. {"OK","OK"}. {"Old Password:","Staré heslo:"}. -{"Online","Online"}. -{"Online Users:","Online používatelia:"}. {"Online Users","Online užívatelia"}. +{"Online","Online"}. {"Only deliver notifications to available users","Doručovať upozornenia len aktuálne prihláseným používateľom"}. {"Only moderators and participants are allowed to change the subject in this room","Len moderátori a zúčastnený majú povolené meniť tému tejto miestnosti"}. {"Only moderators are allowed to change the subject in this room","Len moderátori majú povolené meniť tému miestnosti"}. @@ -235,148 +171,95 @@ {"Only occupants are allowed to send messages to the conference","Len členovia majú povolené zasielať správy do konferencie"}. {"Only occupants are allowed to send queries to the conference","Len členovia majú povolené dotazovať sa o konferencii"}. {"Only service administrators are allowed to send service messages","Iba správcovia služby majú povolené odosielanie servisných správ"}. -{"Options","Nastavenia"}. {"Organization Name","Meno organizácie: "}. {"Organization Unit","Organizačná jednotka: "}. -{"Outgoing s2s Connections:","Odchádzajúce s2s spojenia:"}. {"Outgoing s2s Connections","Odchádzajúce s2s spojenia"}. {"Owner privileges required","Sú vyžadované práva vlastníka"}. -{"Packet","Paket"}. -{"Password ~b","Heslo ~b"}. -{"Password:","Heslo:"}. -{"Password","Heslo"}. -{"Password Verification:","Overenie hesla"}. {"Password Verification","Overenie hesla"}. +{"Password Verification:","Overenie hesla"}. +{"Password","Heslo"}. +{"Password:","Heslo:"}. {"Path to Dir","Cesta k adresáru"}. {"Path to File","Cesta k súboru"}. -{"Pending","Čakajúce"}. {"Period: ","Čas:"}. {"Persist items to storage","Uložiť položky natrvalo do úložiska"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Prosím, berte na vedomie, že tieto nastavenia zázálohujú iba zabudovnú Mnesia databázu. Ak používate ODBC modul, musíte zálohovať vašu SQL databázu separátne."}. {"Please, wait for a while before sending new voice request","Prosím počkate, predtým než pošlete novú žiadosť o Voice"}. {"Pong","Pong"}. -{"Port ~b","Port ~b"}. -{"Port","Port"}. {"Present real Jabber IDs to","Zobrazovať skutočné Jabber ID"}. {"private, ","súkromná, "}. -{"Protocol","Protokol"}. {"Publish-Subscribe","Publish-Subscribe"}. {"PubSub subscriber request","Žiadosť odberateľa PubSub"}. {"Purge all items when the relevant publisher goes offline","Odstrániť všetky relevantné položky, keď užívateľ prejde do módu offline"}. {"Queries to the conference members are not allowed in this room","Dotazovať sa o členoch nie je v tejto miestnosti povolené"}. {"RAM and disc copy","Kópia RAM a disku"}. {"RAM copy","Kópia RAM"}. -{"Raw","Surové dáta"}. {"Really delete message of the day?","Skutočne zmazať správu dňa?"}. {"Recipient is not in the conference room","Príjemca sa nenachádza v konferenčnej miestnosti"}. -{"Register a Jabber account","Zaregistrovať Jabber účet"}. -{"Registered Users:","Registrovaní používatelia:"}. -{"Registered Users","Registrovaní používatelia"}. {"Register","Zoznam kontaktov"}. -{"Registration in mod_irc for ","Registrácia do mod_irc na"}. {"Remote copy","Vzdialená kópia"}. -{"Remove All Offline Messages","Odstrániť všetky offline správy"}. -{"Remove","Odstrániť"}. {"Remove User","Odstrániť užívateľa"}. {"Replaced by new connection","Nahradené novým spojením"}. {"Resources","Zdroje"}. -{"Restart","Reštart"}. {"Restart Service","Reštartovať službu"}. {"Restore Backup from File at ","Obnoviť zálohu zo súboru na "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Obnoviť binárnu zálohu pri nasledujúcom reštarte ejabberd (vyžaduje menej pamäte)"}. {"Restore binary backup immediately:","Okamžite obnoviť binárnu zálohu:"}. -{"Restore","Obnoviť"}. {"Restore plain text backup immediately:","Okamžite obnoviť zálohu z textového súboru:"}. +{"Restore","Obnoviť"}. {"Room Configuration","Nastavenia miestnosti"}. {"Room creation is denied by service policy","Vytváranie miestnosti nie je povolené"}. {"Room description","Popis miestnosti"}. {"Room Occupants","Ľudí v miestnosti"}. {"Room title","Názov miestnosti"}. {"Roster groups allowed to subscribe","Skupiny kontaktov, ktoré môžu odoberať"}. -{"Roster of ","Zoznam kontaktov "}. {"Roster size","Počet kontaktov v zozname"}. -{"Roster","Zoznam kontaktov"}. -{"RPC Call Error","Chyba RPC volania"}. {"Running Nodes","Bežiace uzly"}. -{"~s access rule configuration","~s konfigurácia prístupového pravidla"}. {"Saturday","Sobota"}. -{"Script check","Kontrola skriptu"}. {"Search Results for ","Hľadať výsledky pre "}. {"Search users in ","Hľadať užívateľov v "}. -{"Send announcement to all online users","Odoslať zoznam všetkým online používateľom"}. {"Send announcement to all online users on all hosts","Odoslať oznam všetkým online používateľom na všetkých serveroch"}. -{"Send announcement to all users","Odoslať oznam všetkým používateľom"}. +{"Send announcement to all online users","Odoslať zoznam všetkým online používateľom"}. {"Send announcement to all users on all hosts","Poslať oznámenie všetkým užívateľom na všetkých serveroch"}. +{"Send announcement to all users","Odoslať oznam všetkým používateľom"}. {"September","September"}. -{"Server ~b","Server ~b"}. {"Set message of the day and send to online users","Nastaviť správu dňa a odoslať ju online používateľom"}. {"Set message of the day on all hosts and send to online users","Nastaviť správu dňa na všetkých serveroch a poslať ju online užívateľom"}. {"Shared Roster Groups","Skupiny pre zdieľaný zoznam kontaktov"}. {"Show Integral Table","Zobraziť kompletnú tabuľku"}. {"Show Ordinary Table","Zobraziť bežnú tabuľku"}. {"Shut Down Service","Vypnúť službu"}. -{"~s invites you to the room ~s","~s Vás pozýva do miestnosti ~s"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Niektorí Jabber klenti môžu ukladať heslá v počítači. Používajte túto funkciu len ak veríte, že sú tam v bezpečí. "}. {"Specify the access model","Uveďte model prístupu"}. {"Specify the event message type","Uveďte typ pre správu o udalosti"}. {"Specify the publisher model","Špecifikovať model publikovania"}. -{"~s's Offline Messages Queue","~s Offline správy"}. -{"Start Modules at ","Spustiť moduly na "}. -{"Start Modules","Spustiť moduly"}. -{"Start","Štart"}. -{"Statistics of ~p","Štatistiky ~p"}. -{"Statistics","Štatistiky"}. -{"Stop Modules at ","Zastaviť moduly na "}. -{"Stop Modules","Zastaviť moduly"}. {"Stopped Nodes","Zastavené uzly"}. -{"Stop","Zastaviť"}. -{"Storage Type","Typ úložiska"}. {"Store binary backup:","Uložiť binárnu zálohu:"}. {"Store plain text backup:","Uložiť zálohu do textového súboru:"}. {"Subject","Predmet"}. -{"Submit","Odoslať"}. {"Submitted","Odoslané"}. {"Subscriber Address","Adresa odberateľa"}. -{"Subscription","Prihlásenie"}. {"Sunday","Nedeľa"}. {"That nickname is already in use by another occupant","Prezývka je už používaná iným členom"}. {"That nickname is registered by another person","Prezývka je už zaregistrovaná inou osobou"}. {"The CAPTCHA is valid.","Platná CAPTCHA."}. {"The CAPTCHA verification has failed","Overenie pomocou CAPTCHA zlihalo"}. {"The collections with which a node is affiliated","Kolekcie asociované s uzlom"}. -{"the password is","heslo je"}. {"The password is too weak","heslo je"}. -{"The password of your Jabber account was successfully changed.","Heslo k Jabber účtu bolo úspešne zmenené."}. -{"There was an error changing the password: ","Pri zmene hesla nastala chyba: "}. +{"the password is","heslo je"}. {"There was an error creating the account: ","Pri vytváraní účtu nastala chyba: "}. {"There was an error deleting the account: ","Pri rušení účtu nastala chyba:"}. -{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Veľké a malé písmená sa nerozlišujú: macbeth je to isté ako MacBeth a Macbeth."}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Táto stránka umožňuje refistrovať Jabber účet na tomto serveri. Vaše JID (Jabber IDentifikátor) bude vo formáte: užívateľ@server. Pozorne sledujte inštrukcie, aby ste údaje vypnili správne."}. -{"This page allows to unregister a Jabber account in this Jabber server.","Na tejto stránke si môžete zrušiť Jabber účet registrovaný na tomto serveri."}. +{"This room is not anonymous","Táto miestnosť nie je anonymná"}. {"Thursday","Štvrtok"}. -{"Time","Čas"}. {"Time delay","Časový posun"}. {"Too many CAPTCHA requests","Príliš veľa žiadostí o CAPTCHA"}. -{"To","Pre"}. -{"To ~s","Pre ~s"}. {"Traffic rate limit is exceeded","Bol prekročený prenosový limit"}. -{"Transactions Aborted:","Transakcie zrušená"}. -{"Transactions Committed:","Transakcie potvrdená"}. -{"Transactions Logged:","Transakcie zaznamenaná"}. -{"Transactions Restarted:","Transakcie reštartovaná"}. {"Tuesday","Utorok"}. {"Unable to generate a CAPTCHA","Nepodarilo sa vygenerovat CAPTCHA"}. {"Unauthorized","Neautorizovaný"}. -{"Unregister a Jabber account","Zrušiť Jabber účet"}. {"Unregister","Zrušiť účet"}. -{"Update","Aktualizovať"}. {"Update message of the day (don't send)","Aktualizovať správu dňa (neodosielať)"}. {"Update message of the day on all hosts (don't send)","Upraviť správu dňa na všetkých serveroch"}. -{"Update plan","Aktualizovať plán"}. -{"Update script","Aktualizované skripty"}. -{"Uptime:","Uptime:"}. -{"Use of STARTTLS required","Je vyžadované použitie STARTTLS "}. {"User JID","Používateľ "}. {"User Management","Správa užívateľov"}. {"Username:","IRC prezývka"}. @@ -384,7 +267,6 @@ {"Users Last Activity","Posledná aktivita používateľa"}. {"Users","Používatelia"}. {"User","Užívateľ"}. -{"Validate","Overiť"}. {"vCard User Search","Hľadať užívateľov vo vCard"}. {"Virtual Hosts","Virtuálne servery"}. {"Visitors are not allowed to change their nicknames in this room","V tejto miestnosti nieje povolené meniť prezývky"}. @@ -394,16 +276,11 @@ {"Wednesday","Streda"}. {"When to send the last published item","Kedy odoslať posledne publikovanú položku"}. {"Whether to allow subscriptions","Povoliť prihlasovanie"}. -{"You can later change your password using a Jabber client.","Neskôr si heslo môžete zmeniť pomocou Jabber klienta."}. {"You have been banned from this room","Boli ste vylúčený z tejto miestnosti"}. {"You must fill in field \"Nickname\" in the form","Musíte vyplniť políčko \"Prezývka\" vo formulári"}. {"You need a client that supports x:data and CAPTCHA to register","Na registráciu prezývky potrebujete klienta podporujúceho z x:data"}. {"You need a client that supports x:data to register the nickname","Na registráciu prezývky potrebujete klienta podporujúceho z x:data"}. -{"You need an x:data capable client to configure mod_irc settings","Pre konfiguráciu mod_irc potrebujete klienta podporujúceho x:data"}. -{"You need an x:data capable client to configure room","Na konfiguráciu miestnosti potrebujete klienta podporujúceho x:data"}. {"You need an x:data capable client to search","Na vyhľadávanie potrebujete klienta podporujúceho x:data"}. {"Your active privacy list has denied the routing of this stanza.","Aktívny list súkromia zbránil v smerovaní tejto stanzy."}. {"Your contact offline message queue is full. The message has been discarded.","Fronta offline správ tohoto kontaktu je plná. Správa bola zahodená."}. -{"Your Jabber account was successfully created.","Jabber účet bol úspešne vytvorený."}. -{"Your Jabber account was successfully deleted.","Váš Jabber účet bol úspešne odstránený."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","Správa určená pre ~s bola zablokovaná. Oblokovať ju môžete na ~s"}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Správa určená pre ~s bola zablokovaná. Oblokovať ju môžete na ~s"}. diff --git a/priv/msgs/sk.po b/priv/msgs/sk.po deleted file mode 100644 index 779a2ace3..000000000 --- a/priv/msgs/sk.po +++ /dev/null @@ -1,1942 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: 2.1.x\n" -"PO-Revision-Date: 2012-04-29 18:25+0000\n" -"Last-Translator: Marek Bečka \n" -"Language: sk\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Slovak (slovenčina)\n" -"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2\n" -"X-Additional-Translator: Juraj Michalek\n" -"X-Additional-Translator: SkLUG\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "Je vyžadované použitie STARTTLS " - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Nebol poskytnutý žiadny zdroj" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Nahradené novým spojením" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "bol(a) vyhodený(á) z miestnosti" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "Aktívny list súkromia zbránil v smerovaní tejto stanzy." - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Zadajte zobrazený text" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "Správa určená pre ~s bola zablokovaná. Oblokovať ju môžete na ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "Pokiaľ nevidíte obrázok CAPTCHA, navštívte webovú stránku." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "Webová stránka CAPTCHA" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "Platná CAPTCHA." - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Užívateľ" - -#: ejabberd_oauth.erl:256 -#, fuzzy -msgid "Server" -msgstr "Server ~b" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Heslo" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "Neautorizovaný" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "ejabberd Web Admin" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Administrácia" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Zoznamy prístupových oprávnení (ACL)" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Odoslané" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Zlý formát" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Odoslať" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "Surové dáta" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Zmazať vybrané" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Prístupové pravidlá" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "~s konfigurácia prístupového pravidla" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "Virtuálne servery" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Používatelia" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "Online užívatelia" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Posledná aktivita používateľa" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Čas:" - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "Posledný mesiac" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "Posledný rok" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "Všetky aktivity" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Zobraziť bežnú tabuľku" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Zobraziť kompletnú tabuľku" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Štatistiky" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "Nebol nájdený" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Uzol nenájdený" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Pridať nový" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Server" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Registrovaní používatelia" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Pridať používateľa" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Offline správy" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Posledná aktivita" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Nikdy" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Online" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Registrovaní používatelia:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Online používatelia:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Odchádzajúce s2s spojenia:" - -#: ejabberd_web_admin.erl:1559 -#, fuzzy -msgid "Incoming s2s Connections:" -msgstr "Odchádzajúce s2s spojenia:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Nič" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Zmeniť heslo" - -#: ejabberd_web_admin.erl:1673 -#, fuzzy -msgid "User ~s" -msgstr "Používateľ " - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Pripojené zdroje:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Heslo:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Odstrániť užívateľa" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Žiadne dáta" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Uzly" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Bežiace uzly" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Zastavené uzly" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -#, fuzzy -msgid "Node ~p" -msgstr "Uzol" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "Databáza" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Zálohovať" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Otvorené portov" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Aktualizovať" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Reštart" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Zastaviť" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Moduly" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "Chyba RPC volania" - -#: ejabberd_web_admin.erl:1917 -#, fuzzy -msgid "Database Tables at ~p" -msgstr "Databázové tabuľky na " - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Meno" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Typ úložiska" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Prvky" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Pamäť" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Chyba" - -#: ejabberd_web_admin.erl:1955 -#, fuzzy -msgid "Backup of ~p" -msgstr "Záloha " - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Prosím, berte na vedomie, že tieto nastavenia zázálohujú iba zabudovnú " -"Mnesia databázu. Ak používate ODBC modul, musíte zálohovať vašu SQL databázu " -"separátne." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Uložiť binárnu zálohu:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "OK" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Okamžite obnoviť binárnu zálohu:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"Obnoviť binárnu zálohu pri nasledujúcom reštarte ejabberd (vyžaduje menej " -"pamäte)" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Uložiť zálohu do textového súboru:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Okamžite obnoviť zálohu z textového súboru:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "Importovat dáta užívateľov zo súboru PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" -"Exportovať dáta všetkých uživateľov na serveri do súborov PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "" -"Exportovať dáta uživateľov na hostitelovi do súborov PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Importovať dáta užívateľov z jabberd14 spool súboru:" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Importovať dáta užívateľov z jabberd14 spool adresára:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Otvorené porty na " - -#: ejabberd_web_admin.erl:2144 -#, fuzzy -msgid "Modules at ~p" -msgstr "Moduly na " - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "Štatistiky ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Uptime:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "Čas procesoru" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Transakcie potvrdená" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Transakcie zrušená" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Transakcie reštartovaná" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Transakcie zaznamenaná" - -#: ejabberd_web_admin.erl:2243 -#, fuzzy -msgid "Update ~p" -msgstr "Aktualizovať " - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "Aktualizovať plán" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Modifikované moduly" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Aktualizované skripty" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Nízkoúrovňový aktualizačný skript" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Kontrola skriptu" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Port" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Protokol" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Modul" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Nastavenia" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Štart" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Príkazy" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "Skutočne zmazať správu dňa?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Predmet" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Telo správy" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "Správa neobsahuje text" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Oznámenia" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Odoslať oznam všetkým používateľom" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Poslať oznámenie všetkým užívateľom na všetkých serveroch" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Odoslať zoznam všetkým online používateľom" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "Odoslať oznam všetkým online používateľom na všetkých serveroch" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Nastaviť správu dňa a odoslať ju online používateľom" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" -"Nastaviť správu dňa na všetkých serveroch a poslať ju online užívateľom" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Aktualizovať správu dňa (neodosielať)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "Upraviť správu dňa na všetkých serveroch" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Zmazať správu dňa" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Zmazať správu dňa na všetkých serveroch" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Konfigurácia" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Spustiť moduly" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Zastaviť moduly" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Obnoviť" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Uložiť do textového súboru" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Import súboru" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Import adresára" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Reštartovať službu" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Vypnúť službu" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Vymazať užívateľa" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Ukončiť reláciu užívateľa" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Zobraziť heslo užívateľa" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Zmeniť heslo užívateľa" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "Zobraziť čas posledného prihlásenia" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Zobraziť štatistiku užívateľa" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Zobraziť počet registrovaných užívateľov" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Zobraziť počet pripojených užívateľov" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Správa užívateľov" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Všetci užívatelia" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Odchádzajúce s2s spojenia" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Správa zálohovania" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Importovať užívateľov z jabberd14 spool súborov" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "Pre ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "Od ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Konfigurácia databázových tabuliek " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Vyberte typ úložiska pre tabuľky" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Len kópia disku" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "Kópia RAM a disku" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "Kópia RAM" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "Vzdialená kópia" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Zastaviť moduly na " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Vyberte moduly, ktoré majú byť zastavené" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Spustiť moduly na " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Vložte zoznam modulov {Modul, [Parametre]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Zoznam modulov, ktoré majú byť spustené" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Záloha do súboru na " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Zadajte cestu k súboru so zálohou" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Cesta k súboru" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Obnoviť zálohu zo súboru na " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Uložiť zálohu do textového súboru na " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Zadajte cestu k textovému súboru" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Importovať užívateľa zo súboru na " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Zadajte cestu k spool súboru jabberd14" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Importovať užívateľov z adresára na " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Zadajte cestu k jabberd14 spool adresáru" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Cesta k adresáru" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "Časový posun" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Konfigurácia zoznamu prístupových oprávnení (ACL)" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Zoznamy prístupových oprávnení (ACL)" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Konfigurácia prístupu" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Prístupové pravidlá" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Overenie hesla" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Počet registrovaných užívateľov" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Počet online užívateľov" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Posledné prihlásenie" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "Počet kontaktov v zozname" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "IP adresa" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Zdroje" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Administrácia " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Operácia aplikovaná na užívateľa" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Editovať vlastnosti" - -#: mod_fail2ban.erl:95 -msgid "" -"Too many (~p) failed authentications from this IP address (~s). The address " -"will be unblocked at ~s UTC" -msgstr "" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "" - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "" - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "Prístup bol zamietnutý nastavením služby" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "IRC Transport" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "ejabberd IRC modul" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "Pre konfiguráciu mod_irc potrebujete klienta podporujúceho x:data" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Registrácia do mod_irc na" - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Vložte meno používateľa, kódovanie, porty a heslo ktoré chcete používať pri " -"pripojení na IRC server" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "IRC prezývka" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Ak chcete zadať iné porty, heslá a kódovania pre IRC servery, vyplnte zoznam " -"s hodnotami vo formáte '{\"irc server\",\"kódovanie\", \"port\", \"heslo" -"\"}'. Predvolenéi hodnoty pre túto službu sú: kódovanie \"~s\", port ~p a " -"žiadne heslo." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Príklad: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Parametre spojenia" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "Pripojit IRC kanál" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "IRC kanál (bez počiatočnej #)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "IRC server" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Propojiť IRC kanál sem." - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Pripojit IRC kanál k tomuto Jabber ID: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "Nastavania IRC" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Vložte meno používateľa a kódovanie, ktoré chcete používať pri pripojení na " -"IRC servery. Kliknutím na tlačítko 'Ďalej' môžete zadať niektoré ďalšie " -"hodnoty. Pomocou 'Ukončiť ' uložíte nastavenia." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "IRC prezývka" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Heslo ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Port ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Kódovanie pre server ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Server ~b" - -#: mod_mam.erl:541 -#, fuzzy -msgid "Only members may query archives of this room" -msgstr "Len moderátori majú povolené meniť tému miestnosti" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "Iba správcovia služby majú povolené odosielanie servisných správ" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "Vytváranie miestnosti nie je povolené" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "Diskusná miestnosť neexistuje" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Diskusné miestnosti" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "Na registráciu prezývky potrebujete klienta podporujúceho z x:data" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Registrácia prezývky na " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Zadajte prezývku, ktorú chcete registrovať" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Prezývka" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "Prezývka je už zaregistrovaná inou osobou" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Musíte vyplniť políčko \"Prezývka\" vo formulári" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "ejabberd MUC modul" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "" - -#: mod_muc_admin.erl:249 -#, fuzzy -msgid "Total rooms" -msgstr "Diskusné miestnosti" - -#: mod_muc_admin.erl:250 -#, fuzzy -msgid "Permanent rooms" -msgstr "odišiel(a) z miestnosti" - -#: mod_muc_admin.erl:251 -#, fuzzy -msgid "Registered nicknames" -msgstr "Registrovaní používatelia" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "Nastavenie diskusnej miestnosti bolo zmenené" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "vstúpil(a) do miestnosti" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "odišiel(a) z miestnosti" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "bol(a) zablokovaný(á)" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "bol vyhodený(á) kvôli zmene priradenia" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "bol vyhodený(á), pretože miestnosť bola vyhradená len pre členov" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "bol vyhodený(á) kvôli reštartu systému" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "sa premenoval(a) na" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr "zmenil(a) tému na: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "Diskusná miestnosť je vytvorená" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "Diskusná miestnosť je zrušená" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "Diskusná miestnosť je obnovená" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "Diskusná miestnosť je pozastavená" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "Pondelok" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "Utorok" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "Streda" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "Štvrtok" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "Piatok" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "Sobota" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "Nedeľa" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "Január" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "Február" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "Marec" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "Apríl" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "Máj" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "Jún" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "Júl" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "August" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "September" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "Október" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "November" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "December" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Nastavenia miestnosti" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "Ľudí v miestnosti" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "Bol prekročený prenosový limit" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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 "" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "Nie je povolené odosielať súkromné správy do konferencie" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "Prosím počkate, predtým než pošlete novú žiadosť o Voice" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "Žiadosti o Voice nie sú povolené v tejto konferencii" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "Nepodarilo sa nájsť JID v súhlase o Voice." - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "Len moderátori môžu schváliť žiadosť o Voice" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Nesprávny typ správy" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "Nie je dovolené odoslanie súkromnej správy typu \"Skupinová správa\" " - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "Príjemca sa nenachádza v konferenčnej miestnosti" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "Nieje povolené posielať súkromné správy" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Len členovia majú povolené zasielať správy do konferencie" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "Len členovia majú povolené dotazovať sa o konferencii" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "Dotazovať sa o členoch nie je v tejto miestnosti povolené" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "Len moderátori a zúčastnený majú povolené meniť tému tejto miestnosti" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Len moderátori majú povolené meniť tému miestnosti" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "" -"Návštevníci nemajú povolené zasielať správy všetkým prihláseným do " -"konferencie" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "V tejto miestnosti nieje povolené meniť prezývky" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "Prezývka je už používaná iným členom" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Boli ste vylúčený z tejto miestnosti" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "Pre vstup do miestnosti je potrebné byť členom" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "Pre vstup do miestnosti je potrebné heslo" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "Príliš veľa žiadostí o CAPTCHA" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "Nepodarilo sa vygenerovat CAPTCHA" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Nesprávne heslo" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "Sú potrebné práva administrátora" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "Sú potrebné práva moderátora" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "Jabber ID ~s je neplatné" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "Prezývka ~s v miestnosti neexistuje" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Neplatné priradenie: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Neplatná rola: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "Sú vyžadované práva vlastníka" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "Konfigurácia miestnosti ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Názov miestnosti" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "Popis miestnosti" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Nastaviť miestnosť ako trvalú" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Nastaviť miestnosť ako verejne prehľadávateľnú" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "Nastaviť zoznam zúčastnených ako verejný" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Chrániť miestnosť heslom" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Počet účastníkov" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Bez limitu" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Zobrazovať skutočné Jabber ID" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "moderátorom" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "všetkým" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "" - -#: mod_muc_room.erl:3486 -#, fuzzy -msgid "Moderator" -msgstr "moderátorom" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Nastaviť miestnosť len pre členov" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Nastaviť miestnosť ako moderovanú" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Užívatelia sú implicitne členmi" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Povoliť užívateľom meniť tému" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Povoliť užívateľom odosielať súkromné správy" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "Povoliť užívateľom odosielať súkromné správy" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "nikto" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Povoliť užívateľom dotazovať sa informácie o iných užívateľoch" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Povoliť používateľom posielanie pozvánok" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "Návštevníci môžu posielať textové informácie v stavových správach" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Návštevníci môžu meniť prezývky" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "Povoliť používateľom posielanie pozvánok" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "Minimum interval between voice requests (in seconds)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "Chrániť miestnosť systémom CAPTCHA" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "Nepoužívať CAPTCHA pre nasledujúce Jabber ID" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Zapnúť zaznamenávanie histórie" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "Na konfiguráciu miestnosti potrebujete klienta podporujúceho x:data" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Počet zúčastnených" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "súkromná, " - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "Žiadosť o Voice" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "Povolte alebo zamietnite žiadosť o Voice." - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "Používateľ " - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "Prideltiť Voice tejto osobe?" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s Vás pozýva do miestnosti ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "heslo je" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "Fronta offline správ tohoto kontaktu je plná. Správa bola zahodená." - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "~s Offline správy" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Čas" - -#: mod_offline.erl:812 -msgid "From" -msgstr "Od" - -#: mod_offline.erl:813 -msgid "To" -msgstr "Pre" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Paket" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Offline správy" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "Odstrániť všetky offline správy" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "ejabberd SOCKS5 Bytestreams modul" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Publish-Subscribe" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "ejabberd Publish-Subscribe modul" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "Žiadosť odberateľa PubSub" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Zvolte, či chcete povoliť toto odoberanie" - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "ID uzlu" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Adresa odberateľa" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "Dovoliť tomuto Jabber ID odoberať PubSub uzol?" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Doručiť náklad s upozornením na udalosť" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Doručiť oznamy o udalosti" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Upozorniť prihlásených používateľov na zmenu nastavenia uzlu" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Upozorniť prihlásených používateľov na zmazanie uzlu" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Upozorniť prihlásených používateľov na odstránenie položiek z uzlu" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Uložiť položky natrvalo do úložiska" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "Prístupný názov pre uzol" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Maximálny počet položiek, ktoré je možné natrvalo uložiť" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Povoliť prihlasovanie" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Uveďte model prístupu" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Skupiny kontaktov, ktoré môžu odoberať" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Špecifikovať model publikovania" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "" -"Odstrániť všetky relevantné položky, keď užívateľ prejde do módu offline" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "Uveďte typ pre správu o udalosti" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Maximálny náklad v bajtoch" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "Kedy odoslať posledne publikovanú položku" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Doručovať upozornenia len aktuálne prihláseným používateľom" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "Kolekcie asociované s uzlom" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "Overenie pomocou CAPTCHA zlihalo" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "Na registráciu prezývky potrebujete klienta podporujúceho z x:data" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "Zvolte meno užívateľa a heslo pre registráciu na tomto servere" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "heslo je" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "Nieje dovolené vytvárať účty tak rýchlo po sebe" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "Jabber účet bol úspešne vytvorený." - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "Pri vytváraní účtu nastala chyba: " - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "Váš Jabber účet bol úspešne odstránený." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "Pri rušení účtu nastala chyba:" - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "Heslo k Jabber účtu bolo úspešne zmenené." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "Pri zmene hesla nastala chyba: " - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Registrácia jabber účtu" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Zaregistrovať Jabber účet" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Zrušiť Jabber účet" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"Táto stránka umožňuje refistrovať Jabber účet na tomto serveri. Vaše JID " -"(Jabber IDentifikátor) bude vo formáte: užívateľ@server. Pozorne sledujte " -"inštrukcie, aby ste údaje vypnili správne." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "IRC prezývka" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" -"Veľké a malé písmená sa nerozlišujú: macbeth je to isté ako MacBeth a " -"Macbeth." - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "Nepovolené znaky:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -#, fuzzy -msgid "Server:" -msgstr "Server ~b" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "Nevyzrádzajte heslo nikomu, ani administrátorom tohoto Jabber servera." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "Neskôr si heslo môžete zmeniť pomocou Jabber klienta." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Niektorí Jabber klenti môžu ukladať heslá v počítači. Používajte túto " -"funkciu len ak veríte, že sú tam v bezpečí. " - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"Zapamätajte si heslo alebo si ho zapíšte na papier. Jabber neposkytuje " -"automatickú funkciu ako zistiť zabudnuté heslo. " - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "Overenie hesla" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "Zoznam kontaktov" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "Staré heslo:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "Nové heslo:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "" -"Na tejto stránke si môžete zrušiť Jabber účet registrovaný na tomto serveri." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "Zrušiť účet" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Prihlásenie" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Čakajúce" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Skupiny" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Overiť" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Odstrániť" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Zoznam kontaktov " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Pridať Jabber ID" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Zoznam kontaktov" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Skupiny pre zdieľaný zoznam kontaktov" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Meno:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Popis:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Členovia:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Zobrazené skupiny:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Skupina " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Erlang Jabber Server" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Dátum narodenia" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Mesto" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "Krajina" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "E-mail" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Priezvisko" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Pre vyhľadanie Jabber používateľa vyplňte formulár (pridajte znak * na " -"koniec, pre vyhľadanie podreťazca)" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Celé meno: " - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Prostredné meno: " - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Meno organizácie: " - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Organizačná jednotka: " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Hľadať užívateľov v " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "Na vyhľadávanie potrebujete klienta podporujúceho x:data" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "Hľadať užívateľov vo vCard" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "ejabberd vCard modul" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Hľadať výsledky pre " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "Vyplnte políčka pre vyhľadávanie Jabber užívateľa" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Odchádzajúce s2s servery:" - -#~ msgid "Delete" -#~ msgstr "Zmazať" - -#~ msgid "This room is not anonymous" -#~ msgstr "Táto miestnosť nie je anonymná" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "Účastník bol vyhodený z miestnosti, pretože poslal chybovú správu" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Účastník bol vyhodený z miestnosti, pretože poslal chybovú správu inému " -#~ "účastníkovi" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "" -#~ "Účastník bol vyhodený z miestnosti, pretože poslal chybovú správu o stave" - -#~ msgid "Captcha test failed" -#~ msgstr "Platná CAPTCHA." - -#~ msgid "Encodings" -#~ msgstr "Kódovania" - -#~ msgid "(Raw)" -#~ msgstr "(Raw)" - -#~ msgid "Specified nickname is already registered" -#~ msgstr "Zadaná prezývka je už registrovaná" - -#~ msgid "Size" -#~ msgstr "Veľkosť" - -#~ msgid "You must fill in field \"nick\" in the form" -#~ msgstr "Musíte vyplniť políčko \"prezývka\" vo formulári" diff --git a/priv/msgs/sq.msg b/priv/msgs/sq.msg new file mode 100644 index 000000000..de51a9614 --- /dev/null +++ b/priv/msgs/sq.msg @@ -0,0 +1,345 @@ +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" has set the subject to: "," ka caktuar si subjekt: "}. +{"# participants","# pjesëmarrës"}. +{"A description of the node","Përshkrim i nyjës"}. +{"A friendly name for the node","Emër miqësor për nyjën"}. +{"A password is required to enter this room","Lypset fjalëkalim për të hyrë në këtë dhomë"}. +{"A Web Page","Faqe Web"}. +{"Accept","Pranoje"}. +{"Account doesn't exist","Llogaria s’ekziston"}. +{"Add User","Shtoni Përdorues"}. +{"Administration of ","Administrim i "}. +{"Administration","Administrim"}. +{"Administrator privileges required","Lyp privilegje përgjegjësi"}. +{"All activity","Krejt veprimtaria"}. +{"All Users","Krejt Përdoruesit"}. +{"Allow subscription","Lejo pajtim"}. +{"Allow users to query other users","Lejojuni përdoruesve të kërkojnë për përdorues të tjerë"}. +{"Allow users to send invites","Lejojuni përdoruesve të dërgojnë ftesa"}. +{"Allow users to send private messages","Lejojuni përdoruesve të dërgojnë mesazhe private"}. +{"Allow visitors to change nickname","Lejojuni përdoruesve të ndryshojnë nofkë"}. +{"Allow visitors to send private messages to","Lejojuni përdoruesve të dërgojnë mesazhe private te"}. +{"Announcements","Lajmërime"}. +{"Answer to a question","Përgjigjuni një pyetje"}. +{"Anyone may publish","Gjithkush mund të publikojë"}. +{"Anyone with Voice","Cilido me Zë"}. +{"Anyone","Cilido"}. +{"API Commands","Urdhra API"}. +{"April","Prill"}. +{"Arguments","Argumente"}. +{"Attribute 'channel' is required for this request","Atributi 'channel' është i domosdoshëm për këtë kërkesë"}. +{"Attribute 'jid' is not allowed here","Atributi 'jid' s’lejohet këtu"}. +{"Attribute 'node' is not allowed here","Atributi 'node' s’lejohet këtu"}. +{"August","Gusht"}. +{"Automatic node creation is not enabled","S’është aktivizuar krijimi automatik i nyjes"}. +{"Backup Management","Administrim Kopjeruajtjesh"}. +{"Backup of ~p","Kopjeruajtje e ~p"}. +{"Backup to File at ","Kopjeruaje te Kartelë në "}. +{"Backup","Kopjeruajtje"}. +{"Bad format","Format i gabuar"}. +{"Birthday","Datëlindje"}. +{"Both the username and the resource are required","Janë të domosdoshëm të dy, emri i përdoruesit dhe burimi"}. +{"Cannot remove active list","S’hiqet dot lista aktive"}. +{"Cannot remove default list","S’hiqet dot lista parazgjedhje"}. +{"CAPTCHA web page","Faqe web e CAPTCHA-s"}. +{"Change Password","Ndryshoni Fjalëkalimin"}. +{"Change User Password","Ndryshoni Fjalëkalim Përdoruesi"}. +{"Changing password is not allowed","Nuk lejohet ndryshimi i fjalëkalimit"}. +{"Changing role/affiliation is not allowed","Nuk lejohet ndryshim roli/përkatësie"}. +{"Channel already exists","Kanali ekziston tashmë"}. +{"Channel does not exist","Kanali s’ekziston"}. +{"Channel JID","JID Kanali"}. +{"Channels","Kanale"}. +{"Characters not allowed:","Shenja të palejuara:"}. +{"Chatroom configuration modified","Ndryshoi formësimi i dhomës së fjalosjeve"}. +{"Chatroom is created","Dhoma e fjalosjes u krijua"}. +{"Chatroom is destroyed","Dhoma e fjalosjes u asgjësua"}. +{"Chatroom is started","Dhoma e fjalosjes u nis"}. +{"Chatroom is stopped","Dhoma e fjalosjes u ndal"}. +{"Chatrooms","Dhoma fjalosjeje"}. +{"Choose a username and password to register with this server","Zgjidhni një emër përdoruesi dhe fjalëkalim për ta regjistruar me këtë shërbyes"}. +{"Choose storage type of tables","Zgjidhni lloj depozitimi tableash"}. +{"City","Qytet"}. +{"Commands","Urdhra"}. +{"Conference room does not exist","Dhoma e konferencës s’ekziston"}. +{"Configuration of room ~s","Formësim i dhomë ~s"}. +{"Configuration","Formësim"}. +{"Country","Vend"}. +{"Current Discussion Topic","Tema e Tanishme e Diskutimit"}. +{"Database failure","Dështim baze të dhënash"}. +{"Database Tables Configuration at ","Formësim Tabelash Baze të Dhënash te "}. +{"Database","Bazë të dhënash"}. +{"December","Dhjetor"}. +{"Delete message of the day","Fshini mesazhin e ditës"}. +{"Delete User","Fshi Përdorues"}. +{"Deliver event notifications","Dërgo njoftime aktesh"}. +{"Disc only copy","Kopje vetëm në disk"}. +{"Duplicated groups are not allowed by RFC6121","Grupe të përsëdytur s’lejohen nga RFC6121"}. +{"Edit Properties","Përpunoni Veti"}. +{"ejabberd","ejabberd"}. +{"Email Address","Adresë Email"}. +{"Email","Email"}. +{"Enable logging","Aktivizo regjistrim"}. +{"Enable message archiving","Aktivizoni arkivim mesazhesh"}. +{"Enter path to backup file","Jepni shteg për te kartelë kopjeruajtje"}. +{"Enter path to text file","Jepni shteg për te kartelë tekst"}. +{"Enter the text you see","Jepni tekstin që shihni"}. +{"External component failure","Dështim përbërësi të jashtëm"}. +{"External component timeout","Mbarim kohe për përbërës të jashtëm"}. +{"Failed to parse HTTP response","S’u arrit të përtypet përgjigje HTTP"}. +{"Failed to process option '~s'","S’u arrit të përpunohej mundësia '~s'"}. +{"Family Name","Mbiemër"}. +{"FAQ Entry","Zë PBR-sh"}. +{"February","Shkurt"}. +{"File larger than ~w bytes","Kartelë më e madhe se ~w bajte"}. +{"Fill in the form to search for any matching XMPP User","Plotësoni formularin që të kërkohet për çfarëdo përdoruesi XMPP me përputhje"}. +{"Friday","E premte"}. +{"From ~ts","Nga ~ts"}. +{"Full List of Room Admins","Listë e Plotë Përgjegjësish Dhome"}. +{"Full List of Room Owners","Listë e Plotë të Zotësh Dhome"}. +{"Full Name","Emër i Plotë"}. +{"Get List of Online Users","Merr Listë Përdoruesish Në Linjë"}. +{"Get List of Registered Users","Merr Listë Përdoruesish të Regjistruar"}. +{"Get Number of Online Users","Merr Numër Përdoruesish Në Linjë"}. +{"Get Number of Registered Users","Merr Numër Përdoruesish të Regjistruar"}. +{"Get User Statistics","Merr Statistika Përdoruesi"}. +{"Given Name","Emër"}. +{"Grant voice to this person?","T’i akordohet zë këtij personi?"}. +{"has been banned","është dëbuar"}. +{"has been kicked","është përzënë"}. +{"Hat title","Titull kapeleje"}. +{"Host unknown","Strehë e panjohur"}. +{"HTTP File Upload","Ngarkim Kartelash HTTP"}. +{"Idle connection","Lidhje e plogësht"}. +{"Import Directory","Importoni Drejtori"}. +{"Import File","Importoni Kartelë"}. +{"Import User from File at ","Importo Përdorues prej Kartele te "}. +{"Import Users from Dir at ","Importo Përdorues nga Drejtori te "}. +{"Improper message type","Lloj i pasaktë mesazhesh"}. +{"Incorrect CAPTCHA submit","Parashtim Captcha-je të pasaktë"}. +{"Incorrect password","Fjalëkalim i pasaktë"}. +{"Incorrect value of 'action' attribute","Vlerë e pavlefshme atributi 'action'"}. +{"Insufficient privilege","Privilegj i pamjaftueshëm"}. +{"Internal server error","Gabim i brendshëm shërbyesi"}. +{"Invalid node name","Emër i pavlefshëm nyjeje"}. +{"Invalid 'previd' value","Vlerë e pavlefshme 'previd'"}. +{"Invitations are not allowed in this conference","Në këtë konferencë nuk lejohen ftesa"}. +{"IP addresses","Adresa IP"}. +{"is now known as","tani njihet si"}. +{"It is not allowed to send private messages to the conference","Nuk lejohet të dërgohen mesazhe private te konferenca"}. +{"Jabber ID","ID Jabber"}. +{"January","Janar"}. +{"JID normalization failed","Normalizimi JID dështoi"}. +{"joins the room","hyn te dhoma"}. +{"July","Korrik"}. +{"June","Qershor"}. +{"Just created","Të sapokrijuara"}. +{"Last Activity","Veprimtaria e Fundit"}. +{"Last login","Hyrja e fundit"}. +{"Last message","Mesazhi i fundit"}. +{"Last month","Muaji i fundit"}. +{"Last year","Viti i shkuar"}. +{"leaves the room","del nga dhoma"}. +{"Logging","Regjistrim"}. +{"Make participants list public","Bëje publike listën e pjesëmarrësve"}. +{"Make room CAPTCHA protected","Bëje dhomën të mbrojtur me CAPTCHA"}. +{"Make room members-only","Bëje dhomën vetëm për anëtarët"}. +{"Make room moderated","Bëje dhomën të moderuar"}. +{"Make room password protected","Bëje dhomën të mbrojtur me fjalëkalim"}. +{"Make room persistent","Bëje dhomën të qëndrueshme"}. +{"Make room public searchable","Bëje dhomën të kërkueshme publikisht"}. +{"Malformed username","Faqerojtës i keqformuar"}. +{"March","Mars"}. +{"Max payload size in bytes","Madhësi maksimum ngarkese në bajte"}. +{"Maximum file size","Madhësi maksimum kartelash"}. +{"Maximum Number of Occupants","Numër Maksimum të Pranishmish"}. +{"May","Maj"}. +{"Membership is required to enter this room","Lypset anëtarësim për të hyrë në këtë dhomë"}. +{"Message body","Lëndë mesazhi"}. +{"Messages from strangers are rejected","Mesazhet prej të panjohurish hidhen tej"}. +{"Messages of type headline","Mesazhe të llojit titull"}. +{"Messages of type normal","Mesazhe të llojit normal"}. +{"Middle Name","Emër i Dytë"}. +{"Moderator privileges required","Lypset privilegj moderatori"}. +{"Moderator","Moderator"}. +{"Moderators Only","Vetëm Moderatorët"}. +{"Module failed to handle the query","Moduli s’arrii të trajtonte kërkesën"}. +{"Monday","E hënë"}. +{"Multicast","Multikast"}. +{"Multi-User Chat","Fjalosje Me Shumë Përdorues Njëherësh"}. +{"Name","Emër"}. +{"Natural-Language Room Name","Emër Dhome Në Gjuhë Natyrale"}. +{"Never","Kurrë"}. +{"New Password:","Fjalëkalim i Ri:"}. +{"Nickname can't be empty","Nofka s’mund të jetë e zbrazët"}. +{"Nickname Registration at ","Regjistrim Nofke te "}. +{"Nickname ~s does not exist in the room","Në këtë dhomë s’ekziston nofka ~s"}. +{"Nickname","Nofkë"}. +{"No address elements found","S’u gjetën elementë adrese"}. +{"No addresses element found","S’u gjetën elementë adresash"}. +{"No child elements found","S’u gjetën elementë pjella"}. +{"No Data","S’ka të Dhëna"}. +{"No items found in this query","S’u gjetën objekte në këtë kërkesë"}. +{"No limit","Pa kufi"}. +{"No node specified","S’u përcaktua nyjë"}. +{"No pending subscriptions found","S’u gjetën pajtime pezull"}. +{"No privacy list with this name found","S’u gjet listë privatësie me atë emër"}. +{"No running node found","S’u gjet nyjë në funksionim"}. +{"No services available","S’ka shërbime të gatshme"}. +{"No statistics found for this item","S’u gjetën statistika për këtë objekt"}. +{"Nobody","Askush"}. +{"Node already exists","Nyja ekziston tashmë"}. +{"Node ID","ID Nyjeje"}. +{"Node index not found","S’u gjet tregues nyje"}. +{"Node not found","S’u gjet nyjë"}. +{"Node ~p","Nyjë ~p"}. +{"Node","Nyjë"}. +{"Nodes","Nyja"}. +{"None","Asnjë"}. +{"Not allowed","E palejuar"}. +{"Not Found","S’u Gjet"}. +{"Not subscribed","Jo i pajtuar"}. +{"November","Nëntor"}. +{"Number of answers required","Numër përgjigjesh të domosdoshme"}. +{"Number of occupants","Numër të pranishmish"}. +{"Number of Offline Messages","Numër Mesazhesh Jo Në Linjë"}. +{"Number of online users","Numër përdoruesish në linjë"}. +{"Number of registered users","Numër përdoruesish të regjistruar"}. +{"Occupants are allowed to invite others","Të pranishmëve u është lejuar të ftojnë të tjerë"}. +{"Occupants are allowed to query others","Të pranishmëve u është lejuar t’u bëjnë kërkim të tjerëve"}. +{"Occupants May Change the Subject","Të pranishmit Mund të Ndryshojnë Subjektin"}. +{"October","Tetor"}. +{"OK","OK"}. +{"Old Password:","Fjalëkalimi i Vjetër:"}. +{"Online Users","Përdorues Në Linjë"}. +{"Online","Në linjë"}. +{"Only deliver notifications to available users","Dorëzo njoftime vetëm te përdoruesit e pranishëm"}. +{"Only moderators are allowed to retract messages","Vetëm të moderatorëve u lejohet të tërheqin mbrapsht mesazhe"}. +{"Only occupants are allowed to send messages to the conference","Vetëm të pranishmëve u lejohet të dërgojnë mesazhe te konferenca"}. +{"Only publishers may publish","Vetëm botuesit mund të botojnë"}. +{"Organization Name","Emër Enti"}. +{"Organization Unit","Njësi Organizative"}. +{"Outgoing s2s Connections","Lidhje s2s Ikëse"}. +{"Owner privileges required","Lypset privilegje të zoti"}. +{"Participant ID","ID Pjesëmarrësi"}. +{"Participant","Pjesëmarrës"}. +{"Password Verification","Verifikim Fjalëkalimi"}. +{"Password Verification:","Verifikim Fjalëkalimi:"}. +{"Password","Fjalëkalim"}. +{"Password:","Fjalëkalim:"}. +{"Path to Dir","Shteg për te Drejtori"}. +{"Path to File","Shteg për te Kartelë"}. +{"Period: ","Periudhë: "}. +{"Ping","Ping"}. +{"Pong","Pong"}. +{"Previous session not found","S’u gjet sesion i mëparshëm"}. +{"Previous session PID is dead","PID e sesionit të mëparshëm është e asgjësuar"}. +{"Previous session timed out","Sesionit të mëparshëm i mbaroi koha"}. +{"private, ","private, "}. +{"RAM and disc copy","RAM dhe kopje në disk"}. +{"RAM copy","Kopje në RAM"}. +{"Really delete message of the day?","Të fshihet vërtet mesazhi i ditës?"}. +{"Recipient is not in the conference room","Pjesëmarrësi s’është në dhomën e konferencës"}. +{"Register an XMPP account","Regjistroni një llogari XMPP"}. +{"Register","Regjistrohuni"}. +{"Remote copy","Kopje e largët"}. +{"Remove User","Hiqeni Përdoruesin"}. +{"Replaced by new connection","Zëvendësuar nga lidhje e re"}. +{"Request has timed out","Kërkesës i mbaroi koha"}. +{"Request is ignored","Kërkesa u shpërfill"}. +{"Requested role","Rol i domosdoshëm"}. +{"Resources","Burime"}. +{"Restart Service","Rinise Shërbimin"}. +{"Restore","Riktheje"}. +{"Roles that May Send Private Messages","Role që Mund të Dërgojnë Mesazhe Private"}. +{"Room Configuration","Formësim Dhome"}. +{"Room description","Përshkrim i dhomës"}. +{"Room Occupants","Të pranishëm Në Dhomë"}. +{"Room title","Titull dhome"}. +{"Running Nodes","Nyje Në Punë"}. +{"Saturday","E shtunë"}. +{"Search from the date","Kërko nga data"}. +{"Search Results for ","Përfundime Kërkimi për "}. +{"Search the text","Kërkoni për tekst"}. +{"Search until the date","Kërko deri më datën"}. +{"Search users in ","Kërko përdorues te "}. +{"Send announcement to all users","Dërgo njoftim krejt përdoruesve"}. +{"September","Shtator"}. +{"Server:","Shërbyes:"}. +{"Show Integral Table","Shfaq Tabelë të Plotë"}. +{"Show Ordinary Table","Shfaq Tabelë të Rëndomtë"}. +{"Shut Down Service","Fike Shërbimin"}. +{"Specify the access model","Specifikoni model hyrjeje"}. +{"Specify the event message type","Përcaktoni llojin e mesazhit për aktin"}. +{"Specify the publisher model","Përcaktoni model botuesi"}. +{"Stanza id is not valid","ID Stanza s’është i vlefshëm"}. +{"Stopped Nodes","Nyja të Ndalura"}. +{"Subject","Subjekti"}. +{"Submitted","Parashtruar"}. +{"Subscriber Address","Adresë e Pajtimtarit"}. +{"Sunday","E diel"}. +{"The account already exists","Ka tashmë një llogari të tillë"}. +{"The account was not unregistered","Llogaria s’qe çregjistruar"}. +{"The CAPTCHA is valid.","Kaptça është e vlefshme."}. +{"The default language of the node","Gjuha parazgjedhje e nyjës"}. +{"The feature requested is not supported by the conference","Veçoria e kërkuar nuk mbulohen nga konferenca"}. +{"The JID of the node creator","JID i krijjuesit të nyjës"}. +{"The list of all online users","Lista e krejt përdoruesve në linjë"}. +{"The name of the node","Emri i nyjës"}. +{"The number of subscribers to the node","Numri i pajtimtarëve te nyja"}. +{"The number of unread or undelivered messages","Numri i mesazheve të palexuar ose të padorëzuar"}. +{"The password is too weak","Fjalëkalimi është shumë i dobët"}. +{"the password is","fjalëkalimi është"}. +{"The password of your XMPP account was successfully changed.","Fjalëkalimi i llogarisë tuaj XMPP u ndryshua me sukses."}. +{"The password was not changed","Fjalëkalimi s’u ndryshua"}. +{"The passwords are different","Fjalëkalimet janë të ndryshëm"}. +{"The sender of the last received message","Dërguesi i mesazhit të fundit të marrë"}. +{"There was an error changing the password: ","Pati një gabim në ndryshimin e fjalëkalimit: "}. +{"There was an error creating the account: ","Pati një gabim në krijimin e llogarisë: "}. +{"This room is not anonymous","Kjo dhomë s’është anonime"}. +{"Thursday","E enjte"}. +{"Time delay","Vonesë kohore"}. +{"Too many CAPTCHA requests","Shumë kërkesa ndaj CAPTCHA-s"}. +{"Too many child elements","Shumë elementë pjella"}. +{"Too many elements","Shumë elementë "}. +{"Too many elements","Shumë elementë "}. +{"Too many users in this conference","Shumë përdorues në këtë konferencë"}. +{"Tuesday","E martë"}. +{"Unable to generate a CAPTCHA","S’arrihet të prodhohet një CAPTCHA"}. +{"Unauthorized","E paautorizuar"}. +{"Unexpected action","Veprim i papritur"}. +{"Unregister an XMPP account","Çregjistroni një llogari XMPP"}. +{"Unregister","Çregjistrohuni"}. +{"Unsupported version","Version i pambuluar"}. +{"Updating the vCard is not supported by the vCard storage backend","Përditësimi i vCard-it nuk mbulohet nga mekanizmi i depozitimit të vCard-ve"}. +{"User already exists","Ka tashmë një përdorues të tillë"}. +{"User JID","JID përdoruesi"}. +{"User (jid)","Përdorues (jid)"}. +{"User Management","Administrim Përdoruesish"}. +{"User removed","Përdoruesi u hoq"}. +{"User session not found","S’u gjet sesion përdoruesi"}. +{"User session terminated","Sesioni i përdoruesit përfundoi"}. +{"Username:","Emër përdoruesi:"}. +{"User","Përdorues"}. +{"Users Last Activity","Veprimtaria e Fundit Nga Përdorues"}. +{"Users","Përdorues"}. +{"Virtual Hosts","Streha Virtuale"}. +{"Visitor","Vizitor"}. +{"Wednesday","E mërkurë"}. +{"When a new subscription is processed","Kur përpunohet një pajtim i ri"}. +{"Whether to allow subscriptions","Nëse duhen lejuar apo jo pajtime"}. +{"Who can send private messages","Cilët mund të dërgojnë mesazhe private"}. +{"Wrong parameters in the web formulary","Parametër i gabuar në formular web"}. +{"XMPP Account Registration","Regjistrim Llogarish XMPP"}. +{"XMPP Domains","Përkatësi XMPP"}. +{"You are not allowed to send private messages","S’keni leje të dërgoni mesazhe private"}. +{"You are not joined to the channel","S’keni hyrë te kanali"}. +{"You have been banned from this room","Jeni dëbuar prej kësaj dhome"}. +{"You have joined too many conferences","Keni hyrë në shumë konferenca"}. +{"Your XMPP account was successfully registered.","Llogaria juaj XMPP u regjistrua me sukses."}. +{"Your XMPP account was successfully unregistered.","Llogaria juaj XMPP u çregjistrua me sukses."}. +{"You're not allowed to create nodes","S’keni leje të krijoni nyja"}. diff --git a/priv/msgs/sv.msg b/priv/msgs/sv.msg index b992bce06..8e454464e 100644 --- a/priv/msgs/sv.msg +++ b/priv/msgs/sv.msg @@ -1,20 +1,18 @@ -%% -*- coding: latin-1 -*- -{"Access Configuration","Åtkomstkonfiguration"}. -{"Access Control List Configuration","Konfiguera ACL"}. -{"Access control lists","ACL"}. -{"Access Control Lists","ACL"}. -{"Access denied by service policy","Åtkomst nekad enligt lokal policy"}. -{"Access rules","Åtkomstregler"}. -{"Access Rules","Åtkomstregler"}. -{"Action on user","Handling mot användare"}. -{"Add Jabber ID","Lägg till Jabber ID"}. -{"Add New","Lägg till ny"}. -{"Add User","Lägg till användare"}. -{"Administration","Administration"}. -{"Administration of ","Administration av "}. -{"Administrator privileges required","Administrationsprivilegier krävs"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" has set the subject to: "," har satt ämnet till: "}. {"A friendly name for the node","Ett vänligt namn for noden"}. +{"Access denied by service policy","Åtkomst nekad enligt lokal policy"}. +{"Action on user","Handling mot användare"}. +{"Add User","Lägg till användare"}. +{"Administration of ","Administration av "}. +{"Administration","Administration"}. +{"Administrator privileges required","Administrationsprivilegier krävs"}. {"All activity","All aktivitet"}. +{"All Users","Alla användare"}. {"Allow this Jabber ID to subscribe to this pubsub node?","Tillåt denna Jabber ID att prenumerera på denna pubsub node"}. {"Allow users to change the subject","Tillåt användare att byta ämne"}. {"Allow users to query other users","Tillåt användare att söka efter andra användare"}. @@ -22,14 +20,12 @@ {"Allow users to send private messages","Tillåt användare att skicka privata meddelanden"}. {"Allow visitors to change nickname","Tillåt gäster att kunna ändra smeknamn"}. {"Allow visitors to send status text in presence updates","Tillåt gäster att skicka statustext som uppdatering"}. -{"All Users","Alla användare"}. {"Announcements","Meddelanden"}. -{"anyone","Vemsomhelst"}. {"April","April"}. {"August","Augusti"}. {"Backup Management","Hantera säkerhetskopior"}. -{"Backup","Säkerhetskopiera"}. {"Backup to File at ","Säkerhetskopiera till fil på "}. +{"Backup","Säkerhetskopiera"}. {"Bad format","Dåligt format"}. {"Birthday","Födelsedag"}. {"Change Password","Ändra lösenord"}. @@ -37,81 +33,55 @@ {"Chatroom configuration modified","Chattrum konfiguration modifierad"}. {"Chatrooms","Chattrum"}. {"Choose a username and password to register with this server","Välj ett användarnamn och lösenord för att registrera mot denna server"}. -{"Choose modules to stop","Välj vilka moduler som skall stoppas"}. {"Choose storage type of tables","Välj lagringstyp för tabeller"}. -{"Choose whether to approve this entity's subscription.","Välj om du vill godkänna hela denna prenumertion."}. {"City","Stad"}. {"Commands","Kommandon"}. {"Conference room does not exist","Rummet finns inte"}. -{"Configuration","Konfiguration"}. {"Configuration of room ~s","Konfiguration för ~s"}. -{"Connected Resources:","Anslutna resurser:"}. -{"Connections parameters","Uppkopplingsparametrar"}. +{"Configuration","Konfiguration"}. {"Country","Land"}. -{"CPU Time:","CPU tid"}. -{"Database","Databas"}. {"Database Tables Configuration at ","Databastabellers konfiguration"}. +{"Database","Databas"}. {"December","December"}. {"Default users as participants","Gör om användare till deltagare"}. {"Delete message of the day on all hosts","Ta bort dagens meddelande på alla värdar"}. {"Delete message of the day","Ta bort dagens meddelande"}. -{"Delete Selected","Tabort valda"}. {"Delete User","Ta bort användare"}. {"Deliver event notifications","Skicka eventnotifikation"}. {"Deliver payloads with event notifications","Skicka innehåll tillsammans med notifikationer"}. -{"Description:","Beskrivning:"}. {"Disc only copy","Endast diskkopia"}. -{"Displayed Groups:","Visade grupper:"}. {"Dump Backup to Text File at ","Dumpa säkerhetskopia till textfil på "}. {"Dump to Text File","Dumpa till textfil"}. {"Edit Properties","Redigera egenskaper"}. -{"ejabberd IRC module","ejabberd IRC-modul"}. {"ejabberd MUC module","ejabberd MUC modul"}. {"ejabberd Publish-Subscribe module","ejabberd publikprenumerations modul"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestrem modul"}. {"ejabberd vCard module","ejabberd vCard-modul"}. {"ejabberd Web Admin","ejabberd Web Admin"}. -{"Elements","Elements"}. {"Email","Email"}. {"Enable logging","Möjliggör login"}. -{"Encoding for server ~b","Encoding för server ~b"}. {"End User Session","Avsluta användarsession"}. -{"Enter list of {Module, [Options]}","Skriv in en lista av {Module, [Options]}"}. {"Enter nickname you want to register","Skriv in smeknamnet du vill registrera"}. {"Enter path to backup file","Skriv in sökväg till fil för säkerhetskopia"}. {"Enter path to jabberd14 spool dir","Skriv in sökväg till spoolkatalog från jabberd14"}. {"Enter path to jabberd14 spool file","Skriv in sökväg till spoolfil från jabberd14"}. {"Enter path to text file","Skriv in sökväg till textfil"}. {"Enter the text you see","Skriv in sökväg till textfil"}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Skriv in användarnamn och textkodning du vill använda för att ansluta till IRC-servrar"}. -{"Erlang Jabber Server","Erlang Jabber Server"}. -{"Error","Fel"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Exempel: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportera data av alla användare i servern till en PIEFXIS fil (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Exportera data av användare i en host till PIEFXIS fil (XEP-0227):"}. {"Family Name","Efternamn"}. {"February","Februari"}. -{"Fill in fields to search for any matching Jabber User","Fyll i fält för att söka efter jabberanvändare"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Fyll i formuläret för att söka efter en användare (lägg till * på slutet av fältet för att hitta alla som börjar så)"}. {"Friday","Fredag"}. -{"From","Från"}. -{"From ~s","Från ~s"}. {"Full Name","Fullständigt namn"}. {"Get Number of Online Users","Hämta antal inloggade användare"}. {"Get Number of Registered Users","Hämta antal registrerade användare"}. {"Get User Last Login Time","Hämta användarens senast inloggade tid"}. -{"Get User Password","Hämta användarlösenord"}. {"Get User Statistics","Hämta användarstatistik"}. -{"Group ","Grupp "}. -{"Groups","Grupper"}. {"has been banned","har blivit bannad"}. -{"has been kicked because of an affiliation change","har blivit kickad p.g.a en ändring av tillhörighet"}. {"has been kicked because of a system shutdown","har blivit kickad p.g.a en systemnerstängning"}. +{"has been kicked because of an affiliation change","har blivit kickad p.g.a en ändring av tillhörighet"}. {"has been kicked because the room has been changed to members-only","har blivit kickad p.g.a att rummet har ändrats till endast användare"}. {"has been kicked","har blivit kickad"}. -{" has set the subject to: "," har satt ämnet till: "}. -{"Host","Server"}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Om du vill specifiera textkodning för IRC-servrar, fyll i listan med värden i formatet '{\"irc server\", \"encoding\", port, \"password\"}'. Som standard används \"~s\", port ~p, no password."}. {"Import Directory","Importera katalog"}. {"Import File","Importera fil"}. {"Import user data from jabberd14 spool file:","Importera användare från jabberd14 Spool filer"}. @@ -122,27 +92,13 @@ {"Import Users From jabberd14 Spool Files","Importera användare från jabberd14 Spool filer"}. {"Improper message type","Felaktig medelandetyp"}. {"Incorrect password","Fel lösenord"}. -{"Invalid affiliation: ~s","Ogiltlig rang: ~s"}. -{"Invalid role: ~s","Ogiltlig roll: ~s"}. {"IP addresses","IP adresser"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","IRC kanal (skriv inte första #)"}. -{"IRC server","IRC-användarnamn"}. -{"IRC settings","IRC Inställningar"}. -{"IRC Transport","IRC transport"}. -{"IRC username","IRC-användarnamn"}. -{"IRC Username","IRC-användarnamn"}. {"is now known as","är känd som"}. -{"It is not allowed to send private messages","Det ar inte tillåtet att skicka privata meddelanden"}. {"It is not allowed to send private messages of type \"groupchat\"","Det är inte tillåtet att skicka privata medelanden med typen \"groupchat\""}. {"It is not allowed to send private messages to the conference","Det är inte tillåtet att skicka privata medelanden till den här konferensen"}. {"Jabber ID","Jabber ID"}. -{"Jabber ID ~s is invalid","Otillåtet Jabber ID ~s"}. {"January","Januari"}. -{"Join IRC channel","Lägg till IRC kanal"}. {"joins the room","joinar rummet"}. -{"Join the IRC channel here.","Lägg till IRC kanal här."}. -{"Join the IRC channel in this Jabber ID: ~s","Lägg till IRC kanal till detta Jabber ID: ~s"}. {"July","Juli"}. {"June","Juni"}. {"Last Activity","Senast aktivitet"}. @@ -150,10 +106,6 @@ {"Last month","Senaste månaden"}. {"Last year","Senaste året"}. {"leaves the room","lämnar rummet"}. -{"Listened Ports at ","Lyssnande portar på "}. -{"Listened Ports","Lyssnarport"}. -{"List of modules to start","Lista av moduler som skall startas"}. -{"Low level update script","Uppdaterade laglevel skript"}. {"Make participants list public","Gör deltagarlistan publik"}. {"Make room members-only","Gör om rummet till endast medlemmar"}. {"Make room moderated","Gör rummet modererat"}. @@ -161,22 +113,14 @@ {"Make room persistent","Gör rummet permanent"}. {"Make room public searchable","Gör rummet publikt sökbart"}. {"March","Mars"}. -{"Maximum Number of Occupants","Maximalt antal av användare"}. -{"Max # of items to persist","Högsta antal dataposter som sparas"}. {"Max payload size in bytes","Högsta innehållsstorlek i bytes"}. +{"Maximum Number of Occupants","Maximalt antal av användare"}. {"May","Maj"}. {"Membership is required to enter this room","Du måste vara medlem för att komma in i det här rummet"}. -{"Members:","Medlemmar:"}. -{"Memory","Minne"}. {"Message body","Meddelande kropp"}. {"Middle Name","Mellannamn"}. {"Moderator privileges required","Moderatorprivilegier krävs"}. -{"moderators only","endast moderatorer"}. -{"Modified modules","Uppdaterade moduler"}. -{"Module","Modul"}. -{"Modules","Moduler"}. {"Monday","Måndag"}. -{"Name:","Namn:"}. {"Name","Namn"}. {"Never","Aldrig"}. {"Nickname Registration at ","Registrera smeknamn på "}. @@ -184,12 +128,11 @@ {"Nickname","Smeknamn"}. {"No body provided for announce message","Ingen kropp behövs för dessa meddelanden"}. {"No Data","Ingen data"}. +{"No limit","Ingen gräns"}. {"Node ID","Node ID"}. {"Node not found","Noden finns inte"}. {"Nodes","Noder"}. -{"No limit","Ingen gräns"}. {"None","Inga"}. -{"No resource provided","Ingen resurs angiven"}. {"Not Found","Noden finns inte"}. {"Notify subscribers when items are removed from the node","Meddela prenumeranter när dataposter tas bort från noden"}. {"Notify subscribers when the node configuration changes","Meddela prenumeranter när nodens konfiguration ändras"}. @@ -199,141 +142,92 @@ {"Number of online users","Antal inloggade användare"}. {"Number of registered users","Antal registrerade användare"}. {"October","Oktober"}. -{"Offline Messages:","Offline meddelanden:"}. -{"Offline Messages","Offline meddelanden"}. {"OK","OK"}. -{"Online","Ansluten"}. {"Online Users","Anslutna användare"}. -{"Online Users:","Inloggade användare"}. +{"Online","Ansluten"}. {"Only deliver notifications to available users","Skicka notifikationer bara till uppkopplade användare"}. {"Only moderators and participants are allowed to change the subject in this room","Endast moderatorer och deltagare har tillåtelse att ändra ämnet i det här rummet"}. {"Only occupants are allowed to send messages to the conference","Utomstående får inte skicka medelanden till den här konferensen"}. {"Only occupants are allowed to send queries to the conference","Utomstående får inte skicka iq-queries till den här konferensen"}. {"Only service administrators are allowed to send service messages","Endast administratörer får skicka tjänstmeddelanden"}. -{"Options","Parametrar"}. {"Organization Name","Organisationsnamn"}. {"Organization Unit","Organisationsenhet"}. {"Outgoing s2s Connections","Utgaende s2s anslutning"}. -{"Outgoing s2s Connections:","Utgående s2s anslutning"}. {"Owner privileges required","Ägarprivilegier krävs"}. -{"Packet","Paket"}. -{"Password ~b","Lösenord ~b"}. -{"Password:","Lösenord:"}. -{"Password","Lösenord"}. {"Password Verification","Lösenordsverifikation"}. +{"Password","Lösenord"}. +{"Password:","Lösenord:"}. {"Path to Dir","Sökväg till katalog"}. {"Path to File","Sökväg till fil"}. -{"Pending","Ännu inte godkända"}. {"Period: ","Period: "}. {"Persist items to storage","Spara dataposter permanent"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Kom ihåg att dessa inställningar endast tar backup pa builtin Mnesias databas. Om du använder ODBC modul så måste du ta backup på SQLs databas enskilt"}. {"Pong","Pong"}. -{"Port ~b","Port ~b"}. -{"Port","Port"}. {"Present real Jabber IDs to","Nuvarande äkta Jabber IDs till"}. {"private, ","privat, "}. -{"Protocol","Protocol"}. {"Publish-Subscribe","Publikprenumeration"}. {"PubSub subscriber request","Pubsub prenumerationsforfrågan"}. {"Queries to the conference members are not allowed in this room","Det är förbjudet att skicka iq-queries till konferensdeltagare"}. {"RAM and disc copy","RAM- och diskkopia"}. {"RAM copy","RAM-kopia"}. -{"Raw","Ra"}. {"Really delete message of the day?","Verkligen ta bort dagens meddelanden?"}. {"Recipient is not in the conference room","Mottagaren finns inte i rummet"}. -{"Registered Users:","Registrerade användare"}. -{"Registered Users","Registrerade användare"}. -{"Registration in mod_irc for ","mod_irc-registrering för "}. {"Remote copy","Sparas inte lokalt"}. -{"Remove","Ta bort"}. {"Remove User","Ta bort användare"}. {"Replaced by new connection","Ersatt av ny anslutning"}. {"Resources","Resurser"}. -{"Restart","Omstart"}. {"Restart Service","Starta om servicen"}. -{"Restore","Återställ"}. {"Restore Backup from File at ","Återställ säkerhetskopia från fil på "}. {"Restore binary backup after next ejabberd restart (requires less memory):","återställ den binära backupen efter nästa ejabberd omstart"}. {"Restore binary backup immediately:","återställ den binära backupen omedelbart"}. {"Restore plain text backup immediately:","återställ textbackup omedelbart"}. +{"Restore","Återställ"}. {"Room Configuration","Rumkonfiguration"}. {"Room creation is denied by service policy","Skapandet av rum är förbjudet enligt lokal policy"}. {"Room Occupants","Antal besökare"}. {"Room title","Rumstitel"}. {"Roster groups allowed to subscribe","Rostergrupper tillåts att prenumerera"}. -{"Roster","Kontaktlista"}. -{"Roster of ","Kontaktlista för "}. {"Roster size","Roster storlek"}. -{"RPC Call Error","RPC Uppringningserror"}. {"Running Nodes","Körande noder"}. -{"~s access rule configuration","Åtkomstregelkonfiguration för ~s"}. {"Saturday","Lördag"}. -{"Script check","Skript kollat"}. {"Search Results for ","Sökresultat för"}. {"Search users in ","Sök efter användare på "}. {"Send announcement to all online users on all hosts","Sänd meddelanden till alla inloggade användare på alla värdar"}. {"Send announcement to all online users","Sänd meddelanden till alla inloggade användare"}. -{"Send announcement to all users on all hosts","Sänd meddelanden till alla användare på alla värdar"}. {"Send announcement to all users","Sänd meddelanden till alla användare"}. {"September","September"}. -{"Server ~b","Server ~b"}. {"Set message of the day and send to online users","Sätt dagens status meddelande och skicka till alla användare"}. {"Set message of the day on all hosts and send to online users","Sätt dagens status meddelande pa alla värdar och skicka till alla användare"}. {"Shared Roster Groups","Delade Rostergrupper"}. {"Show Integral Table","Visa kumulativ tabell"}. {"Show Ordinary Table","Visa normal tabell"}. {"Shut Down Service","Stäng ner servicen"}. -{"~s invites you to the room ~s","~s bjöd in dig till rummet ~s"}. {"Specify the access model","Specificera accessmodellen"}. {"Specify the publisher model","Ange publiceringsmodell"}. -{"~s's Offline Messages Queue","~s's offline meddelandekö"}. -{"Start Modules at ","Starta moduler på "}. -{"Start Modules","Starta moduler"}. -{"Start","Starta"}. -{"Statistics of ~p","Statistik på ~p"}. -{"Statistics","Statistik"}. -{"Stop Modules at ","Stoppa moduler på "}. -{"Stop Modules","Stanna moduler"}. {"Stopped Nodes","Stannade noder"}. -{"Stop","Stoppa"}. -{"Storage Type","Lagringstyp"}. {"Store binary backup:","Lagra den binära backupen"}. {"Store plain text backup:","Lagra textbackup"}. {"Subject","Ämne"}. -{"Submit","Skicka"}. {"Submitted","Skicka in"}. {"Subscriber Address","Prenumerationsadress"}. -{"Subscription","Prenumeration"}. {"Sunday","Söndag"}. {"That nickname is registered by another person","Smeknamnet är reserverat"}. {"The CAPTCHA is valid.","Din CAPTCHA är godkänd."}. {"the password is","Lösenordet är"}. +{"This room is not anonymous","Detta rum är inte anonymt"}. {"Thursday","Torsdag"}. {"Time delay","Tidsförsening"}. -{"Time","Tid"}. -{"To ~s","Till ~s"}. -{"To","Till"}. {"Traffic rate limit is exceeded","Trafikgränsen har överstigits"}. -{"Transactions Aborted:","Transaktioner borttagna"}. -{"Transactions Committed:","Transaktioner kommittade"}. -{"Transactions Logged:","Transaktioner loggade "}. -{"Transactions Restarted:","Transaktioner omstartade"}. {"Tuesday","Tisdag"}. {"Unauthorized","Ej auktoriserad"}. {"Update message of the day (don't send)","Uppdatera dagens status meddelande (skicka inte)"}. {"Update message of the day on all hosts (don't send)","Uppdatera dagens status meddelande på alla värdar (skicka inte)"}. -{"Update plan","Uppdateringsplan"}. -{"Update script","Uppdatera skript"}. -{"Update","Uppdatera"}. -{"Uptime:","Tid upp"}. -{"Use of STARTTLS required","Du måste använda STARTTLS"}. -{"User","Användarnamn"}. {"User Management","Användarmanagement"}. -{"Users","Användare"}. +{"User","Användarnamn"}. {"Users are not allowed to register accounts so quickly","Det är inte tillåtet för användare att skapa konton så fort"}. {"Users Last Activity","Användarens senaste aktivitet"}. -{"Validate","Validera"}. +{"Users","Användare"}. {"vCard User Search","vCard användare sök"}. {"Virtual Hosts","Virtuella servrar"}. {"Visitors are not allowed to change their nicknames in this room","Det är inte tillåtet for gäster att ändra sina smeknamn i detta rummet"}. @@ -343,8 +237,6 @@ {"Whether to allow subscriptions","Tillåta prenumerationer?"}. {"You have been banned from this room","Du har blivit bannlyst från det här rummet"}. {"You must fill in field \"Nickname\" in the form","Du måste fylla i fält \"smeknamn\" i formen"}. -{"You need an x:data capable client to configure mod_irc settings","Du behöer en klient som stöjer x:data för att konfigurera mod_irc"}. -{"You need an x:data capable client to configure room","Du behöver en klient som stödjer x:data för att konfiguera detta rum"}. {"You need an x:data capable client to search","Du behöver en klient som stödjer x:data, för att kunna söka"}. {"Your contact offline message queue is full. The message has been discarded.","Din kontaktkö for offlinekontakter ar full"}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","Dina meddelanden till ~s är blockerade. För att avblockera dem, gå till ~s"}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Dina meddelanden till ~s är blockerade. För att avblockera dem, gå till ~s"}. diff --git a/priv/msgs/sv.po b/priv/msgs/sv.po deleted file mode 100644 index eb391fe80..000000000 --- a/priv/msgs/sv.po +++ /dev/null @@ -1,1943 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: 2.1.0-alpha\n" -"Last-Translator: Gustaf Alströmer\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Swedish (svenska)\n" -"X-Additional-Translator: Thore Alstromer\n" -"X-Additional-Translator: Heysan\n" -"X-Additional-Translator: Magnus Henoch\n" -"X-Additional-Translator: Jonas Ådahl\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "Du måste använda STARTTLS" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Ingen resurs angiven" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Ersatt av ny anslutning" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "har blivit kickad" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "" - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Skriv in sökväg till textfil" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "" -"Dina meddelanden till ~s är blockerade. För att avblockera dem, gå till ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "" - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "Din CAPTCHA är godkänd." - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Användarnamn" - -#: ejabberd_oauth.erl:256 -#, fuzzy -msgid "Server" -msgstr "Server ~b" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Lösenord" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "Ej auktoriserad" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "ejabberd Web Admin" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Administration" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "ACL" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Skicka in" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Dåligt format" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Skicka" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "Ra" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Tabort valda" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Åtkomstregler" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "Åtkomstregelkonfiguration för ~s" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "Virtuella servrar" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Användare" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "Anslutna användare" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Användarens senaste aktivitet" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Period: " - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "Senaste månaden" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "Senaste året" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "All aktivitet" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Visa normal tabell" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Visa kumulativ tabell" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Statistik" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "Noden finns inte" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Noden finns inte" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Lägg till ny" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Server" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Registrerade användare" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Lägg till användare" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Offline meddelanden" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Senast aktivitet" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Aldrig" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Ansluten" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Registrerade användare" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Inloggade användare" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Utgående s2s anslutning" - -#: ejabberd_web_admin.erl:1559 -#, fuzzy -msgid "Incoming s2s Connections:" -msgstr "Utgående s2s anslutning" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Inga" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Ändra lösenord" - -#: ejabberd_web_admin.erl:1673 -#, fuzzy -msgid "User ~s" -msgstr "Användare " - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Anslutna resurser:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Lösenord:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Ta bort användare" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Ingen data" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Noder" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Körande noder" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Stannade noder" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -#, fuzzy -msgid "Node ~p" -msgstr "Nod " - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "Databas" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Säkerhetskopiera" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Lyssnarport" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Uppdatera" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Omstart" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Stoppa" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Moduler" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "RPC Uppringningserror" - -#: ejabberd_web_admin.erl:1917 -#, fuzzy -msgid "Database Tables at ~p" -msgstr "Databas tabell pa" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Namn" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Lagringstyp" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Elements" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Minne" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Fel" - -#: ejabberd_web_admin.erl:1955 -#, fuzzy -msgid "Backup of ~p" -msgstr "Backup av" - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Kom ihåg att dessa inställningar endast tar backup pa builtin Mnesias " -"databas. Om du använder ODBC modul så måste du ta backup på SQLs databas " -"enskilt" - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Lagra den binära backupen" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "OK" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "återställ den binära backupen omedelbart" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "återställ den binära backupen efter nästa ejabberd omstart" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Lagra textbackup" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "återställ textbackup omedelbart" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "Importera användardata från en PIEFXIS fil (XEP-0227):" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" -"Exportera data av alla användare i servern till en PIEFXIS fil (XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "Exportera data av användare i en host till PIEFXIS fil (XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Importera användare från jabberd14 Spool filer" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Importera användare från jabberd14 Spool directory:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Lyssnande portar på " - -#: ejabberd_web_admin.erl:2144 -#, fuzzy -msgid "Modules at ~p" -msgstr "Moduler på" - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "Statistik på ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Tid upp" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "CPU tid" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Transaktioner kommittade" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Transaktioner borttagna" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Transaktioner omstartade" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Transaktioner loggade " - -#: ejabberd_web_admin.erl:2243 -#, fuzzy -msgid "Update ~p" -msgstr "Uppdatera" - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "Uppdateringsplan" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Uppdaterade moduler" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Uppdatera skript" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Uppdaterade laglevel skript" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Skript kollat" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Port" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Protocol" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Modul" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Parametrar" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Starta" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Kommandon" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "Verkligen ta bort dagens meddelanden?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Ämne" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Meddelande kropp" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "Ingen kropp behövs för dessa meddelanden" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Meddelanden" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Sänd meddelanden till alla användare" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Sänd meddelanden till alla användare på alla värdar" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Sänd meddelanden till alla inloggade användare" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "Sänd meddelanden till alla inloggade användare på alla värdar" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Sätt dagens status meddelande och skicka till alla användare" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" -"Sätt dagens status meddelande pa alla värdar och skicka till alla användare" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Uppdatera dagens status meddelande (skicka inte)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "Uppdatera dagens status meddelande på alla värdar (skicka inte)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Ta bort dagens meddelande" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Ta bort dagens meddelande på alla värdar" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Konfiguration" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Starta moduler" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Stanna moduler" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Återställ" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Dumpa till textfil" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Importera fil" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Importera katalog" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Starta om servicen" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Stäng ner servicen" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Ta bort användare" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Avsluta användarsession" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Hämta användarlösenord" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Andra användarlösenord" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "Hämta användarens senast inloggade tid" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Hämta användarstatistik" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Hämta antal registrerade användare" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Hämta antal inloggade användare" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Användarmanagement" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Alla användare" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Utgaende s2s anslutning" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Hantera säkerhetskopior" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Importera användare från jabberd14 Spool filer" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "Till ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "Från ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Databastabellers konfiguration" - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Välj lagringstyp för tabeller" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Endast diskkopia" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "RAM- och diskkopia" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "RAM-kopia" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "Sparas inte lokalt" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Stoppa moduler på " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Välj vilka moduler som skall stoppas" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Starta moduler på " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Skriv in en lista av {Module, [Options]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Lista av moduler som skall startas" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Säkerhetskopiera till fil på " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Skriv in sökväg till fil för säkerhetskopia" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Sökväg till fil" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Återställ säkerhetskopia från fil på " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Dumpa säkerhetskopia till textfil på " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Skriv in sökväg till textfil" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Importera användare från fil på " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Skriv in sökväg till spoolfil från jabberd14" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Importera användare från katalog på " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Skriv in sökväg till spoolkatalog från jabberd14" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Sökväg till katalog" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "Tidsförsening" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Konfiguera ACL" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "ACL" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Åtkomstkonfiguration" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Åtkomstregler" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Lösenordsverifikation" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Antal registrerade användare" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Antal inloggade användare" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Senaste login" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "Roster storlek" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "IP adresser" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Resurser" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Administration av " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Handling mot användare" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Redigera egenskaper" - -#: mod_fail2ban.erl:95 -msgid "" -"Too many (~p) failed authentications from this IP address (~s). The address " -"will be unblocked at ~s UTC" -msgstr "" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "" - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "" - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "Åtkomst nekad enligt lokal policy" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "IRC transport" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "ejabberd IRC-modul" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "Du behöer en klient som stöjer x:data för att konfigurera mod_irc" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "mod_irc-registrering för " - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Skriv in användarnamn och textkodning du vill använda för att ansluta till " -"IRC-servrar" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "IRC-användarnamn" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Om du vill specifiera textkodning för IRC-servrar, fyll i listan med värden " -"i formatet '{\"irc server\", \"encoding\", port, \"password\"}'. Som " -"standard används \"~s\", port ~p, no password." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Exempel: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Uppkopplingsparametrar" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "Lägg till IRC kanal" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "IRC kanal (skriv inte första #)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "IRC-användarnamn" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Lägg till IRC kanal här." - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Lägg till IRC kanal till detta Jabber ID: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "IRC Inställningar" - -#: mod_irc.erl:1051 -#, fuzzy -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Skriv in användarnamn och textkodning du vill använda för att ansluta till " -"IRC-servrar" - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "IRC-användarnamn" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Lösenord ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Port ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Encoding för server ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Server ~b" - -#: mod_mam.erl:541 -#, fuzzy -msgid "Only members may query archives of this room" -msgstr "Endast moderatorer får ändra ämnet i det här rummet" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "Endast administratörer får skicka tjänstmeddelanden" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "Skapandet av rum är förbjudet enligt lokal policy" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "Rummet finns inte" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Chattrum" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "" - -#: mod_muc.erl:933 -#, fuzzy -msgid "You need a client that supports x:data to register the nickname" -msgstr "Du behöver en klient som stödjer x:data för att registrera smeknamn" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Registrera smeknamn på " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Skriv in smeknamnet du vill registrera" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Smeknamn" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "Smeknamnet är reserverat" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Du måste fylla i fält \"smeknamn\" i formen" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "ejabberd MUC modul" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "" - -#: mod_muc_admin.erl:249 -#, fuzzy -msgid "Total rooms" -msgstr "Chattrum" - -#: mod_muc_admin.erl:250 -#, fuzzy -msgid "Permanent rooms" -msgstr "lämnar rummet" - -#: mod_muc_admin.erl:251 -#, fuzzy -msgid "Registered nicknames" -msgstr "Registrerade användare" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "Chattrum konfiguration modifierad" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "joinar rummet" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "lämnar rummet" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "har blivit bannad" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "har blivit kickad p.g.a en ändring av tillhörighet" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "har blivit kickad p.g.a att rummet har ändrats till endast användare" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "har blivit kickad p.g.a en systemnerstängning" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "är känd som" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " har satt ämnet till: " - -#: mod_muc_log.erl:493 -#, fuzzy -msgid "Chatroom is created" -msgstr "Chattrum" - -#: mod_muc_log.erl:495 -#, fuzzy -msgid "Chatroom is destroyed" -msgstr "Chattrum" - -#: mod_muc_log.erl:497 -#, fuzzy -msgid "Chatroom is started" -msgstr "Chattrum" - -#: mod_muc_log.erl:499 -#, fuzzy -msgid "Chatroom is stopped" -msgstr "Chattrum" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "Måndag" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "Tisdag" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "Onsdag" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "Torsdag" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "Fredag" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "Lördag" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "Söndag" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "Januari" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "Februari" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "Mars" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "April" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "Maj" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "Juni" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "Juli" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "Augusti" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "September" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "Oktober" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "November" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "December" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Rumkonfiguration" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "Antal besökare" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "Trafikgränsen har överstigits" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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 "" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "" -"Det är inte tillåtet att skicka privata medelanden till den här konferensen" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "" - -#: mod_muc_room.erl:377 -#, fuzzy -msgid "Only moderators can approve voice requests" -msgstr "Tillåt användare att skicka inbjudningar" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Felaktig medelandetyp" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "" -"Det är inte tillåtet att skicka privata medelanden med typen \"groupchat\"" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "Mottagaren finns inte i rummet" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "Det ar inte tillåtet att skicka privata meddelanden" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Utomstående får inte skicka medelanden till den här konferensen" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "Utomstående får inte skicka iq-queries till den här konferensen" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "Det är förbjudet att skicka iq-queries till konferensdeltagare" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "" -"Endast moderatorer och deltagare har tillåtelse att ändra ämnet i det här " -"rummet" - -#: mod_muc_room.erl:966 -#, fuzzy -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Endast moderatorer får ändra ämnet i det här rummet" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "Besökare får inte skicka medelande till alla" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "Det är inte tillåtet for gäster att ändra sina smeknamn i detta rummet" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -#, fuzzy -msgid "That nickname is already in use by another occupant" -msgstr "Smeknamnet används redan" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Du har blivit bannlyst från det här rummet" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "Du måste vara medlem för att komma in i det här rummet" - -#: mod_muc_room.erl:1872 -#, fuzzy -msgid "A password is required to enter this room" -msgstr "Lösenord erfordras" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -#, fuzzy -msgid "Unable to generate a CAPTCHA" -msgstr "Kunde inte generera ett CAPTCHA" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Fel lösenord" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "Administrationsprivilegier krävs" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "Moderatorprivilegier krävs" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "Otillåtet Jabber ID ~s" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "Smeknamnet ~s existerar inte i det här rummet" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Ogiltlig rang: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Ogiltlig roll: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "Ägarprivilegier krävs" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "Konfiguration för ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Rumstitel" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -#, fuzzy -msgid "Room description" -msgstr "Beskrivning:" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Gör rummet permanent" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Gör rummet publikt sökbart" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "Gör deltagarlistan publik" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Gör losenorden i rummet publika" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Maximalt antal av användare" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Ingen gräns" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Nuvarande äkta Jabber IDs till" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "endast moderatorer" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "Vemsomhelst" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "" - -#: mod_muc_room.erl:3486 -#, fuzzy -msgid "Moderator" -msgstr "endast moderatorer" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Gör om rummet till endast medlemmar" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Gör rummet modererat" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Gör om användare till deltagare" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Tillåt användare att byta ämne" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Tillåt användare att skicka privata meddelanden" - -#: mod_muc_room.erl:3533 -#, fuzzy -msgid "Allow visitors to send private messages to" -msgstr "Tillåt användare att skicka privata meddelanden" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Tillåt användare att söka efter andra användare" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Tillåt användare att skicka inbjudningar" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "Tillåt gäster att skicka statustext som uppdatering" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Tillåt gäster att kunna ändra smeknamn" - -#: mod_muc_room.erl:3589 -#, fuzzy -msgid "Allow visitors to send voice requests" -msgstr "Tillåt användare att skicka inbjudningar" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "" - -#: mod_muc_room.erl:3599 -#, fuzzy -msgid "Make room CAPTCHA protected" -msgstr "Gör losenorden i rummet publika" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Möjliggör login" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "Du behöver en klient som stödjer x:data för att konfiguera detta rum" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Antal besökare" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "privat, " - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "" - -#: mod_muc_room.erl:4351 -#, fuzzy -msgid "User JID" -msgstr "Användare " - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s bjöd in dig till rummet ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "Lösenordet är" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "Din kontaktkö for offlinekontakter ar full" - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "~s's offline meddelandekö" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Tid" - -#: mod_offline.erl:812 -msgid "From" -msgstr "Från" - -#: mod_offline.erl:813 -msgid "To" -msgstr "Till" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Paket" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Offline meddelanden:" - -#: mod_offline.erl:996 -#, fuzzy -msgid "Remove All Offline Messages" -msgstr "Offline meddelanden" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "ejabberd SOCKS5 Bytestrem modul" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Publikprenumeration" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "ejabberd publikprenumerations modul" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "Pubsub prenumerationsforfrågan" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Välj om du vill godkänna hela denna prenumertion." - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "Node ID" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Prenumerationsadress" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "Tillåt denna Jabber ID att prenumerera på denna pubsub node" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Skicka innehåll tillsammans med notifikationer" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Skicka eventnotifikation" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Meddela prenumeranter när nodens konfiguration ändras" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Meddela prenumeranter när noden tas bort" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Meddela prenumeranter när dataposter tas bort från noden" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Spara dataposter permanent" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "Ett vänligt namn for noden" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Högsta antal dataposter som sparas" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Tillåta prenumerationer?" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Specificera accessmodellen" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Rostergrupper tillåts att prenumerera" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Ange publiceringsmodell" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "" - -#: mod_pubsub.erl:3771 -#, fuzzy -msgid "Specify the event message type" -msgstr "Specificera accessmodellen" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Högsta innehållsstorlek i bytes" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "När att skicka senast publicerade ämne" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Skicka notifikationer bara till uppkopplade användare" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "" - -#: mod_register.erl:209 -#, fuzzy -msgid "The CAPTCHA verification has failed" -msgstr "Din CAPTCHA är godkänd." - -#: mod_register.erl:253 -#, fuzzy -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "Du behöver en klient som stödjer x:data för att registrera smeknamn" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "Välj ett användarnamn och lösenord för att registrera mot denna server" - -#: mod_register.erl:373 mod_register.erl:421 -#, fuzzy -msgid "The password is too weak" -msgstr "Lösenordet är" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "Det är inte tillåtet för användare att skapa konton så fort" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "" - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "" - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "" - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "" - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "" - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "" - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -#, fuzzy -msgid "Username:" -msgstr "IRC-användarnamn" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -#, fuzzy -msgid "Server:" -msgstr "Server ~b" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "" - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" - -#: mod_register_web.erl:262 mod_register_web.erl:374 -#, fuzzy -msgid "Password Verification:" -msgstr "Lösenordsverifikation" - -#: mod_register_web.erl:269 -#, fuzzy -msgid "Register" -msgstr "Kontaktlista" - -#: mod_register_web.erl:366 -#, fuzzy -msgid "Old Password:" -msgstr "Lösenord:" - -#: mod_register_web.erl:370 -#, fuzzy -msgid "New Password:" -msgstr "Lösenord:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "" - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Prenumeration" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Ännu inte godkända" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Grupper" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Validera" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Ta bort" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Kontaktlista för " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Lägg till Jabber ID" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Kontaktlista" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Delade Rostergrupper" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Namn:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Beskrivning:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Medlemmar:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Visade grupper:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Grupp " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Erlang Jabber Server" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Födelsedag" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Stad" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "Land" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "Email" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Efternamn" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Fyll i formuläret för att söka efter en användare (lägg till * på slutet av " -"fältet för att hitta alla som börjar så)" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Fullständigt namn" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Mellannamn" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Organisationsnamn" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Organisationsenhet" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Sök efter användare på " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "Du behöver en klient som stödjer x:data, för att kunna söka" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "vCard användare sök" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "ejabberd vCard-modul" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Sökresultat för" - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "Fyll i fält för att söka efter jabberanvändare" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Utgående s2s server" - -#~ msgid "Delete" -#~ msgstr "Ta bort" - -#~ msgid "This room is not anonymous" -#~ msgstr "Detta rum är inte anonymt" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "" -#~ "Deltagaren har blivit kickad fran rummet p.g.a att han skickade ett " -#~ "errormeddelande" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Deltagaren har blivit kickad från rummet p.g.a att han skickade ett " -#~ "errormeddelande till en annan deltagare" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "" -#~ "Denna deltagaren är kickad från rummet p.g.a att han skickade en " -#~ "errorstatus" - -#, fuzzy -#~ msgid "Captcha test failed" -#~ msgstr "Din CAPTCHA är godkänd." diff --git a/priv/msgs/ta.msg b/priv/msgs/ta.msg new file mode 100644 index 000000000..d76425610 --- /dev/null +++ b/priv/msgs/ta.msg @@ -0,0 +1,630 @@ +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" (Add * to the end of field to match substring)"," (அடி மூலக்கூறுடன் பொருந்தக்கூடிய புலத்தின் முடிவில் * சேர்க்கவும்)"}. +{" has set the subject to: "," இதற்கு பொருள் அமைத்துள்ளது: "}. +{"# participants","# பங்கேற்பாளர்கள்"}. +{"A description of the node","முனையின் விளக்கம்"}. +{"A friendly name for the node","முனைக்கு ஒரு நட்பு பெயர்"}. +{"A password is required to enter this room","இந்த அறைக்குள் நுழைய கடவுச்சொல் தேவை"}. +{"A Web Page","ஒரு வலைப்பக்கம்"}. +{"Accept","ஏற்றுக்கொள்"}. +{"Access denied by service policy","பணி கொள்கையால் மறுக்கப்பட்டது"}. +{"Access model","அணுகல் மாதிரி"}. +{"Account doesn't exist","கணக்கு இல்லை"}. +{"Action on user","பயனரின் செயல்"}. +{"Add a hat to a user","ஒரு பயனருக்கு ஒரு தொப்பியைச் சேர்க்கவும்"}. +{"Add User","பயனரைச் சேர்க்கவும்"}. +{"Administration of ","நிர்வாகம் "}. +{"Administration","நிர்வாகம்"}. +{"Administrator privileges required","நிர்வாகி சலுகைகள் தேவை"}. +{"All activity","அனைத்து செயல்பாடுகளும்"}. +{"All Users","அனைத்து பயனர்களும்"}. +{"Allow subscription","சந்தாவை அனுமதிக்கவும்"}. +{"Allow this Jabber ID to subscribe to this pubsub node?","இந்த பப்சப் முனைக்கு குழுசேர இந்த சாபர் ஐடியை அனுமதிக்கவா?"}. +{"Allow this person to register with the room?","இந்த நபரை அறையில் பதிவு செய்ய அனுமதிக்கவா?"}. +{"Allow users to change the subject","இந்த விசயத்தை மாற்ற பயனர்களை அனுமதிக்கவும்"}. +{"Allow users to query other users","பயனர்களை மற்ற பயனர்களை வினவ அனுமதிக்கவும்"}. +{"Allow users to send invites","அழைப்புகளை அனுப்ப பயனர்களை அனுமதிக்கவும்"}. +{"Allow users to send private messages","தனிப்பட்ட செய்திகளை அனுப்ப பயனர்களை அனுமதிக்கவும்"}. +{"Allow visitors to change nickname","பார்வையாளர்களை புனைப்பெயரை மாற்ற அனுமதிக்கவும்"}. +{"Allow visitors to send private messages to","தனிப்பட்ட செய்திகளை அனுப்ப பார்வையாளர்களை அனுமதிக்கவும்"}. +{"Allow visitors to send status text in presence updates","முன்னிலையில் புதுப்பிப்புகளில் நிலை உரையை அனுப்ப பார்வையாளர்களை அனுமதிக்கவும்"}. +{"Allow visitors to send voice requests","குரல் கோரிக்கைகளை அனுப்ப பார்வையாளர்களை அனுமதிக்கவும்"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","அறை உறுப்பினர்களை வரையறுக்கும் தொடர்புடைய எல்.டி.ஏ.பி குழு; இது ஒரு குழுவின் செயல்படுத்தல்-குறிப்பிட்ட அல்லது வரிசைப்படுத்தல்-குறிப்பிட்ட வரையறையின்படி எல்.டி.ஏ.பி புகழ்பெற்ற பெயராக இருக்க வேண்டும்."}. +{"Announcements","அறிவிப்புகள்"}. +{"Answer associated with a picture","ஒரு படத்துடன் தொடர்புடைய பதில்"}. +{"Answer associated with a video","வீடியோவுடன் தொடர்புடைய பதில்"}. +{"Answer associated with speech","பேச்சுடன் தொடர்புடைய பதில்"}. +{"Answer to a question","ஒரு கேள்விக்கு பதில்"}. +{"Anyone in the specified roster group(s) may subscribe and retrieve items","குறிப்பிட்ட ரோச்டர் குழு (கள்) இல் உள்ள எவரும் உருப்படிகளை குழுசேரலாம் மற்றும் மீட்டெடுக்கலாம்"}. +{"Anyone may associate leaf nodes with the collection","எவரும் இலை முனைகளை சேகரிப்புடன் தொடர்புபடுத்தலாம்"}. +{"Anyone may publish","எவரும் வெளியிடலாம்"}. +{"Anyone may subscribe and retrieve items","எவரும் பொருட்களை குழுசேர் மற்றும் மீட்டெடுக்கலாம்"}. +{"Anyone with a presence subscription of both or from may subscribe and retrieve items","அல்லது இரண்டின் இருப்பு சந்தா உள்ள எவரும் உருப்படிகளை குழுசேர் மற்றும் மீட்டெடுக்கலாம்"}. +{"Anyone with Voice","குரல் உள்ள எவரும்"}. +{"Anyone","யாரும்"}. +{"API Commands","பநிஇ கட்டளைகள்"}. +{"April","ப-சித்திரை"}. +{"Arguments","வாதங்கள்"}. +{"Attribute 'channel' is required for this request","இந்த கோரிக்கைக்கு 'சேனல்' என்ற பண்புக்கூறு தேவை"}. +{"Attribute 'id' is mandatory for MIX messages","கலவை செய்திகளுக்கு 'ஐடி' கட்டாயமாகும்"}. +{"Attribute 'jid' is not allowed here","'சிட்' என்ற பண்புக்கூறு இங்கே அனுமதிக்கப்படவில்லை"}. +{"Attribute 'node' is not allowed here","'முனை' என்ற பண்புக்கூறு இங்கே அனுமதிக்கப்படவில்லை"}. +{"Attribute 'to' of stanza that triggered challenge","சவாலைத் தூண்டும் சரணத்தின் '' 'என்ற பண்புக்கூறு"}. +{"August","ஆ-ஆவணி"}. +{"Automatic node creation is not enabled","தானியங்கி முனை உருவாக்கம் இயக்கப்படவில்லை"}. +{"Backup Management","காப்பு மேலாண்மை"}. +{"Backup of ~p","~p இன் காப்புப்பிரதி"}. +{"Backup to File at ","தாக்கல் செய்ய காப்புப்பிரதி "}. +{"Backup","காப்புப்பிரதி"}. +{"Bad format","மோசமான வடிவம்"}. +{"Birthday","பிறந்த நாள்"}. +{"Both the username and the resource are required","பயனர்பெயர் மற்றும் சான்று இரண்டும் தேவை"}. +{"Bytestream already activated","பைட்டெச்ட்ரீம் ஏற்கனவே செயல்படுத்தப்பட்டது"}. +{"Cannot remove active list","செயலில் உள்ள பட்டியலை அகற்ற முடியாது"}. +{"Cannot remove default list","இயல்புநிலை பட்டியலை அகற்ற முடியாது"}. +{"CAPTCHA web page","கேப்ட்சா வலைப்பக்கம்"}. +{"Challenge ID","அறைகூவல் ஐடி"}. +{"Change Password","கடவுச்சொல்லை மாற்றவும்"}. +{"Change User Password","பயனர் கடவுச்சொல்லை மாற்றவும்"}. +{"Changing password is not allowed","கடவுச்சொல்லை மாற்ற அனுமதிக்கப்படவில்லை"}. +{"Changing role/affiliation is not allowed","பங்கு/இணைப்பை மாற்ற அனுமதிக்கப்படவில்லை"}. +{"Channel already exists","சேனல் ஏற்கனவே உள்ளது"}. +{"Channel does not exist","சேனல் இல்லை"}. +{"Channel JID","சேனல் சிட்"}. +{"Channels","சேனல்கள்"}. +{"Characters not allowed:","எழுத்துக்கள் அனுமதிக்கப்படவில்லை:"}. +{"Chatroom configuration modified","அரட்டை உள்ளமைவு மாற்றப்பட்டது"}. +{"Chatroom is created","அரட்டை அறை உருவாக்கப்பட்டது"}. +{"Chatroom is destroyed","அரட்டை அறை அழிக்கப்படுகிறது"}. +{"Chatroom is started","அரட்டை அறை தொடங்கப்பட்டது"}. +{"Chatroom is stopped","அரட்டை அறை நிறுத்தப்பட்டது"}. +{"Chatrooms","அரட்டை அறைகள்"}. +{"Choose a username and password to register with this server","இந்த சேவையகத்துடன் பதிவு செய்ய பயனர்பெயர் மற்றும் கடவுச்சொல்லைத் தேர்வுசெய்க"}. +{"Choose storage type of tables","அட்டவணைகளின் சேமிப்பக வகை என்பதைத் தேர்வுசெய்க"}. +{"Choose whether to approve this entity's subscription.","இந்த நிறுவனத்தின் சந்தாவை அங்கீகரிக்க வேண்டுமா என்பதைத் தேர்வுசெய்க."}. +{"City","நகரம்"}. +{"Client acknowledged more stanzas than sent by server","சேவையகத்தால் அனுப்பப்பட்டதை விட கிளையன்ட் அதிக சரணத்தை ஒப்புக் கொண்டார்"}. +{"Clustering","கிளச்டரிங்"}. +{"Commands","கட்டளைகள்"}. +{"Conference room does not exist","மாநாட்டு அறை இல்லை"}. +{"Configuration of room ~s","அறையின் உள்ளமைவு ~s"}. +{"Configuration","உள்ளமைவு"}. +{"Contact Addresses (normally, room owner or owners)","தொடர்பு முகவரிகள் (பொதுவாக, அறை உரிமையாளர் அல்லது உரிமையாளர்கள்)"}. +{"Country","நாடு"}. +{"Current Discussion Topic","தற்போதைய கலந்துரையாடல் தலைப்பு"}. +{"Database failure","தரவுத்தள தோல்வி"}. +{"Database Tables Configuration at ","தரவுத்தள அட்டவணைகள் உள்ளமைவு "}. +{"Database","தரவுத்தளம்"}. +{"December","கா-மார்கழி"}. +{"Default users as participants","பங்கேற்பாளர்களாக இயல்புநிலை பயனர்கள்"}. +{"Delete message of the day on all hosts","அனைத்து புரவலர்களிலும் அன்றைய செய்தியை நீக்கு"}. +{"Delete message of the day","அன்றைய செய்தியை நீக்கு"}. +{"Delete User","பயனரை நீக்கு"}. +{"Deliver event notifications","நிகழ்வு அறிவிப்புகளை வழங்கவும்"}. +{"Deliver payloads with event notifications","நிகழ்வு அறிவிப்புகளுடன் பேலோடுகளை வழங்கவும்"}. +{"Disc only copy","வட்டு மட்டுமே நகலெடுக்கவும்"}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","உங்கள் கடவுச்சொல்லை யாரிடமும் சொல்லாதீர்கள், எக்ச்எம்பி.பி சேவையகத்தின் நிர்வாகிகள் கூட இல்லை."}. +{"Dump Backup to Text File at ","உரை கோப்பில் காப்புப்பிரதியை டம்ப் செய்யுங்கள் "}. +{"Dump to Text File","உரை கோப்பில் டம்ப் செய்யுங்கள்"}. +{"Duplicated groups are not allowed by RFC6121","நகல் குழுக்கள் RFC6121 ஆல் அனுமதிக்கப்படவில்லை"}. +{"Dynamically specify a replyto of the item publisher","உருப்படி வெளியீட்டாளரின் பதிலை மாறும் வகையில் குறிப்பிடவும்"}. +{"Edit Properties","பண்புகளைத் திருத்து"}. +{"Either approve or decline the voice request.","குரல் கோரிக்கையை அங்கீகரிக்கவும் அல்லது நிராகரிக்கவும்."}. +{"ejabberd HTTP Upload service","EJABBERD HTTP பதிவேற்ற பணி"}. +{"ejabberd MUC module","EJABBERD MUC தொகுதி"}. +{"ejabberd Multicast service","எசாபர்ட் மல்டிகாச்ட் பணி"}. +{"ejabberd Publish-Subscribe module","EJABBERD வெளியீட்டு-சந்தா தொகுதி"}. +{"ejabberd SOCKS5 Bytestreams module","EJABBERD SOCKS5 BYTESTREAMS தொகுதி"}. +{"ejabberd vCard module","EJABBERD VCARD தொகுதி"}. +{"ejabberd Web Admin","எசாபர்ட் வலை நிர்வாகி"}. +{"ejabberd","எசாபர்ட்"}. +{"Email Address","மின்னஞ்சல் முகவரி"}. +{"Email","மின்னஞ்சல்"}. +{"Enable hats","தொப்பிகளை இயக்கவும்"}. +{"Enable logging","பதிவை இயக்கவும்"}. +{"Enable message archiving","செய்தி காப்பகத்தை இயக்கவும்"}. +{"Enabling push without 'node' attribute is not supported","'முனை' பண்புக்கூறு இல்லாமல் உந்துதலை இயக்குவது ஆதரிக்கப்படவில்லை"}. +{"End User Session","இறுதி பயனர் அமர்வு"}. +{"Enter nickname you want to register","நீங்கள் பதிவு செய்ய விரும்பும் புனைப்பெயரை உள்ளிடவும்"}. +{"Enter path to backup file","காப்புப்பிரதி கோப்பிற்கு பாதையை உள்ளிடவும்"}. +{"Enter path to jabberd14 spool dir","Jabberd14 Spool அடைவு க்கு பாதையை உள்ளிடவும்"}. +{"Enter path to jabberd14 spool file","சாபர்ட் 14 ச்பூல் கோப்புக்கு பாதையை உள்ளிடவும்"}. +{"Enter path to text file","உரை கோப்புக்கு பாதையை உள்ளிடவும்"}. +{"Enter the text you see","நீங்கள் பார்க்கும் உரையை உள்ளிடவும்"}. +{"Erlang XMPP Server","எர்லாங் எக்ச்எம்பிபி சேவையகம்"}. +{"Exclude Jabber IDs from CAPTCHA challenge","கேப்ட்சா சேலஞ்சில் இருந்து சாபர் ஐடிகளை விலக்குங்கள்"}. +{"Export all tables as SQL queries to a file:","அனைத்து அட்டவணைகளையும் கவிமொ வினவல்களாக ஒரு கோப்பில் ஏற்றுமதி செய்யுங்கள்:"}. +{"Export data of all users in the server to PIEFXIS files (XEP-0227):","சேவையகத்தில் உள்ள அனைத்து பயனர்களின் தரவை Piefxis கோப்புகளுக்கு (XEP-0227) ஏற்றுமதி செய்யுங்கள்:"}. +{"Export data of users in a host to PIEFXIS files (XEP-0227):","ஓச்டில் பயனர்களின் தரவை Piefxis கோப்புகளுக்கு ஏற்றுமதி செய்யுங்கள் (XEP-0227):"}. +{"External component failure","வெளிப்புற கூறு தோல்வி"}. +{"External component timeout","வெளிப்புற கூறு நேரம் முடிந்தது"}. +{"Failed to activate bytestream","பைட்டெச்ட்ரீமை செயல்படுத்தத் தவறிவிட்டது"}. +{"Failed to extract JID from your voice request approval","உங்கள் குரல் கோரிக்கை ஒப்புதலிலிருந்து சிட் பிரித்தெடுப்பதில் தோல்வி"}. +{"Failed to map delegated namespace to external component","வெளிப்புற கூறுகளுக்கு பிரதிநிதித்துவ பெயர்வெளியை வரைபடமாக்குவதில் தோல்வி"}. +{"Failed to parse HTTP response","HTTP பதிலை அலசத் தவறிவிட்டது"}. +{"Failed to process option '~s'","விருப்பத்தை '~s' செயலாக்குவதில் தோல்வி"}. +{"Family Name","குடும்ப பெயர்"}. +{"FAQ Entry","கேள்விகள் நுழைவு"}. +{"February","தை-மாசி"}. +{"File larger than ~w bytes","~w பைட்டுகளை விட பெரியது"}. +{"Fill in the form to search for any matching XMPP User","பொருந்தக்கூடிய எக்ச்எம்பிபி பயனரைத் தேட படிவத்தை நிரப்பவும்"}. +{"Friday","வெள்ளிக்கிழமை"}. +{"From ~ts","~ts இலிருந்து"}. +{"Full List of Room Admins","அறை நிர்வாகிகளின் முழு பட்டியல்"}. +{"Full List of Room Owners","அறை உரிமையாளர்களின் முழு பட்டியல்"}. +{"Full Name","முழு பெயர்"}. +{"Get List of Online Users","நிகழ்நிலை பயனர்களின் பட்டியலைப் பெறுங்கள்"}. +{"Get List of Registered Users","பதிவுசெய்யப்பட்ட பயனர்களின் பட்டியலைப் பெறுங்கள்"}. +{"Get Number of Online Users","நிகழ்நிலை பயனர்களின் எண்ணிக்கையைப் பெறுங்கள்"}. +{"Get Number of Registered Users","பதிவுசெய்யப்பட்ட பயனர்களின் எண்ணிக்கையைப் பெறுங்கள்"}. +{"Get Pending","நிலுவையில் செல்லுங்கள்"}. +{"Get User Last Login Time","பயனர் கடைசி உள்நுழைவு நேரத்தைப் பெறுங்கள்"}. +{"Get User Statistics","பயனர் புள்ளிவிவரங்களைப் பெறுங்கள்"}. +{"Given Name","கொடுக்கப்பட்ட பெயர்"}. +{"Grant voice to this person?","இந்த நபருக்கு குரல் கொடுக்கவா?"}. +{"has been banned","தடைசெய்யப்பட்டுள்ளது"}. +{"has been kicked because of a system shutdown","கணினி பணிநிறுத்தம் காரணமாக உதைக்கப்பட்டுள்ளது"}. +{"has been kicked because of an affiliation change","இணைப்பு மாற்றம் காரணமாக உதைக்கப்பட்டுள்ளது"}. +{"has been kicked because the room has been changed to members-only","அறை உறுப்பினர்களுக்கு மட்டுமே மாற்றப்பட்டதால் உதைக்கப்பட்டுள்ளது"}. +{"has been kicked","உதைக்கப்பட்டுள்ளது"}. +{"Hash of the vCard-temp avatar of this room","இந்த அறையின் vcard-temp அவதாரத்தின் ஆச்"}. +{"Hat title","தொப்பி தலைப்பு"}. +{"Hat URI","தொப்பி யூரி"}. +{"Hats limit exceeded","தொப்பிகள் வரம்பு மீறியது"}. +{"Host unknown","புரவலன் தெரியவில்லை"}. +{"HTTP File Upload","HTTP கோப்பு பதிவேற்றம்"}. +{"Idle connection","செயலற்ற இணைப்பு"}. +{"If you don't see the CAPTCHA image here, visit the web page.","நீங்கள் இங்கே கேப்ட்சா படத்தைக் காணவில்லை என்றால், வலைப்பக்கத்தைப் பார்வையிடவும்."}. +{"Import Directory","இறக்குமதி அடைவு"}. +{"Import File","கோப்பு இறக்குமதி"}. +{"Import user data from jabberd14 spool file:","சாபர்ட் 14 ச்பூல் கோப்பிலிருந்து பயனர் தரவை இறக்குமதி செய்யுங்கள்:"}. +{"Import User from File at ","கோப்பிலிருந்து பயனரை இறக்குமதி செய்யுங்கள் "}. +{"Import users data from a PIEFXIS file (XEP-0227):","PIEFXIS கோப்பிலிருந்து (XEP-0227) பயனர்களின் தரவை இறக்குமதி செய்யுங்கள்:"}. +{"Import users data from jabberd14 spool directory:","சாபர்ட் 14 ச்பூல் கோப்பகத்திலிருந்து பயனர்களின் தரவை இறக்குமதி செய்யுங்கள்:"}. +{"Import Users from Dir at ","அடைவு இலிருந்து பயனர்களை இறக்குமதி செய்யுங்கள் "}. +{"Import Users From jabberd14 Spool Files","சாபர்ட் 14 ச்பூல் கோப்புகளிலிருந்து பயனர்களை இறக்குமதி செய்யுங்கள்"}. +{"Improper domain part of 'from' attribute","'ஃப்ரம்' பண்புக்கூறின் முறையற்ற டொமைன் பகுதி"}. +{"Improper message type","முறையற்ற செய்தி வகை"}. +{"Incorrect CAPTCHA submit","தவறான கேப்ட்சா சமர்ப்பிக்கவும்"}. +{"Incorrect data form","தவறான தரவு வடிவம்"}. +{"Incorrect password","தவறான கடவுச்சொல்"}. +{"Incorrect value of 'action' attribute","'செயல்' பண்புக்கூறின் தவறான மதிப்பு"}. +{"Incorrect value of 'action' in data form","தரவு வடிவத்தில் 'செயல்' இன் தவறான மதிப்பு"}. +{"Incorrect value of 'path' in data form","தரவு வடிவத்தில் 'பாதை' இன் தவறான மதிப்பு"}. +{"Installed Modules:","நிறுவப்பட்ட தொகுதிகள்:"}. +{"Install","நிறுவவும்"}. +{"Insufficient privilege","போதிய சலுகை"}. +{"Internal server error","உள் சேவையக பிழை"}. +{"Invalid 'from' attribute in forwarded message","அனுப்பப்பட்ட செய்தியில் 'இலிருந்து' பண்புக்கூறு"}. +{"Invalid node name","தவறான முனை பெயர்"}. +{"Invalid 'previd' value","தவறான 'முந்தைய' மதிப்பு"}. +{"Invitations are not allowed in this conference","இந்த மாநாட்டில் அழைப்புகள் அனுமதிக்கப்படவில்லை"}. +{"IP addresses","ஐபி முகவரிகள்"}. +{"is now known as","இப்போது அழைக்கப்படுகிறது"}. +{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","பிழை செய்திகளை அறைக்கு அனுப்ப அனுமதிக்கப்படவில்லை. பங்கேற்பாளர் (~s) ஒரு பிழை செய்தியை (~s) அனுப்பி அறையிலிருந்து உதைத்துள்ளார்"}. +{"It is not allowed to send private messages of type \"groupchat\"","\"குரூப்சாட்\" என்ற வகை தனிப்பட்ட செய்திகளை அனுப்ப அனுமதிக்கப்படவில்லை"}. +{"It is not allowed to send private messages to the conference","தனிப்பட்ட செய்திகளை மாநாட்டிற்கு அனுப்ப அனுமதிக்கப்படவில்லை"}. +{"Jabber ID","சாபர் ஐடி"}. +{"January","மா-தை"}. +{"JID normalization denied by service policy","பணி கொள்கையால் மறுக்கப்பட்ட சிட் இயல்பாக்கம்"}. +{"JID normalization failed","சிட் இயல்பாக்கம் தோல்வியடைந்தது"}. +{"Joined MIX channels of ~ts","~ts இன் கலவை சேனல்களில் சேர்ந்தது"}. +{"Joined MIX channels:","இணைந்த கலவை சேனல்கள்:"}. +{"joins the room","அறையில் இணைகிறது"}. +{"July","ஆ-ஆடி"}. +{"June","வை-ஆனி"}. +{"Just created","இப்போது உருவாக்கப்பட்டது"}. +{"Last Activity","கடைசி செயல்பாடு"}. +{"Last login","கடைசி உள்நுழைவு"}. +{"Last message","கடைசி செய்தி"}. +{"Last month","கடந்த மாதம்"}. +{"Last year","கடந்த ஆண்டு"}. +{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","உரையின் SHA-256 ஆசின் குறைந்த குறிப்பிடத்தக்க பிட்கள் எக்சாடெசிமல் சிட்டை சமமாக இருக்க வேண்டும்"}. +{"leaves the room","அறையை விட்டு வெளியேறுகிறது"}. +{"List of users with hats","தொப்பிகளைக் கொண்ட பயனர்களின் பட்டியல்"}. +{"List users with hats","தொப்பிகளுடன் பயனர்களை பட்டியலிடுங்கள்"}. +{"Logged Out","வெளியேறியது"}. +{"Logging","பதிவு"}. +{"Make participants list public","பங்கேற்பாளர்கள் பட்டியலிடுங்கள்"}. +{"Make room CAPTCHA protected","அறை கேப்ட்சா பாதுகாக்கவும்"}. +{"Make room members-only","அறை உறுப்பினர்களை மட்டும் செய்யுங்கள்"}. +{"Make room moderated","அறை மிதமானதாக்குங்கள்"}. +{"Make room password protected","அறை கடவுச்சொல்லைப் பாதுகாக்கவும்"}. +{"Make room persistent","அறையை தொடர்ந்து செய்யுங்கள்"}. +{"Make room public searchable","அறையை பொதுவில் தேடலாம்"}. +{"Malformed username","தவறான பயனர்பெயர்"}. +{"MAM preference modification denied by service policy","பணி கொள்கையால் மறுக்கப்பட்ட மாம் விருப்பத்தேர்வு மாற்றம்"}. +{"March","மா-பங்குனி"}. +{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","அதிகபட்சம் # தொடர்ச்சியாக இருக்க வேண்டும், அல்லது அதிகபட்சமாக விதிக்கப்பட்ட சேவையகத்தைத் தவிர வேறு எந்த குறிப்பிட்ட வரம்பும் இல்லாமல் `அதிகபட்சம்"}. +{"Max payload size in bytes","பைட்டுகளில் அதிகபட்ச பேலோட் அளவு"}. +{"Maximum file size","அதிகபட்ச கோப்பு அளவு"}. +{"Maximum Number of History Messages Returned by Room","அறையால் திரும்பிய அதிகபட்ச வரலாற்று செய்திகளின் எண்ணிக்கை"}. +{"Maximum number of items to persist","தொடர்ந்து அதிகபட்ச உருப்படிகளின் எண்ணிக்கை"}. +{"Maximum Number of Occupants","அதிகபட்ச குடியிருப்பாளர்களின் எண்ணிக்கை"}. +{"May","சி-வைகாசி"}. +{"Membership is required to enter this room","இந்த அறைக்குள் நுழைய உறுப்பினர் தேவை"}. +{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","உங்கள் கடவுச்சொல்லை மனப்பாடம் செய்யுங்கள், அல்லது பாதுகாப்பான இடத்தில் வைக்கப்பட்டுள்ள ஒரு காகிதத்தில் எழுதுங்கள். உங்கள் கடவுச்சொல்லை மறந்துவிட்டால் அதை மீட்டெடுக்க எக்ச்எம்பிபியில் தானியங்கு வழி இல்லை."}. +{"Mere Availability in XMPP (No Show Value)","எக்ச்எம்பிபியில் வெறும் கிடைக்கும் (காட்சி மதிப்பு இல்லை)"}. +{"Message body","செய்தி உடல்"}. +{"Message not found in forwarded payload","அனுப்பப்பட்ட பேலோடில் செய்தி காணப்படவில்லை"}. +{"Messages from strangers are rejected","அந்நியர்களிடமிருந்து செய்திகள் நிராகரிக்கப்படுகின்றன"}. +{"Messages of type headline","வகை தலைப்பின் செய்திகள்"}. +{"Messages of type normal","வகை இயல்பான செய்திகள்"}. +{"Middle Name","நடுத்தர பெயர்"}. +{"Minimum interval between voice requests (in seconds)","குரல் கோரிக்கைகளுக்கு இடையில் குறைந்தபட்ச இடைவெளி (நொடிகளில்)"}. +{"Moderator privileges required","மதிப்பீட்டாளர் சலுகைகள் தேவை"}. +{"Moderators Only","மதிப்பீட்டாளர்கள் மட்டுமே"}. +{"Moderator","மதிப்பீட்டாளர்"}. +{"Module failed to handle the query","தொகுதி வினவலைக் கையாளத் தவறிவிட்டது"}. +{"Monday","திங்கள்"}. +{"Multicast","மல்டிகாச்ட்"}. +{"Multiple elements are not allowed by RFC6121","பல கூறுகள் RFC6121 ஆல் அனுமதிக்கப்படாது"}. +{"Multi-User Chat","பல பயனர் அரட்டை"}. +{"Name","பெயர்"}. +{"Natural Language for Room Discussions","அறை விவாதங்களுக்கு இயற்கை மொழி"}. +{"Natural-Language Room Name","இயற்கை மொழி அறை பெயர்"}. +{"Neither 'jid' nor 'nick' attribute found","'சிட்' அல்லது 'நிக்' பண்புக்கூறு இல்லை"}. +{"Neither 'role' nor 'affiliation' attribute found","'பங்கு' அல்லது 'இணைப்பு' பண்புக்கூறு இல்லை"}. +{"Never","ஒருபோதும்"}. +{"New Password:","புதிய கடவுச்சொல்:"}. +{"Nickname can't be empty","புனைப்பெயர் காலியாக இருக்க முடியாது"}. +{"Nickname Registration at ","இல் புனைப்பெயர் பதிவு "}. +{"Nickname ~s does not exist in the room","அறையில் ~s புனைப்பெயர் இல்லை"}. +{"Nickname","புனைப்பெயர்"}. +{"No address elements found","முகவரி கூறுகள் எதுவும் கிடைக்கவில்லை"}. +{"No addresses element found","முகவரிகள் உறுப்பு இல்லை"}. +{"No 'affiliation' attribute found","'இணைப்பு' பண்புக்கூறு இல்லை"}. +{"No available resource found","கிடைக்கக்கூடிய வளங்கள் எதுவும் கிடைக்கவில்லை"}. +{"No body provided for announce message","அறிவிப்பு செய்திக்கு எந்த உடலும் வழங்கப்படவில்லை"}. +{"No child elements found","குழந்தை கூறுகள் எதுவும் கிடைக்கவில்லை"}. +{"No data form found","தரவு படிவம் எதுவும் கிடைக்கவில்லை"}. +{"No Data","தரவு இல்லை"}. +{"No features available","நற்பொருத்தங்கள் எதுவும் கிடைக்கவில்லை"}. +{"No element found","இல்லை உறுப்பு காணப்பட்டது"}. +{"No hook has processed this command","இந்த கட்டளையை எந்த ஊக்கும் செயலாக்கவில்லை"}. +{"No info about last activity found","கடைசி செயல்பாட்டைப் பற்றிய எந்த தகவலும் கிடைக்கவில்லை"}. +{"No 'item' element found","'உருப்படி' உறுப்பு இல்லை"}. +{"No items found in this query","இந்த வினவலில் எந்த உருப்படிகளும் காணப்படவில்லை"}. +{"No limit","வரம்பு இல்லை"}. +{"No module is handling this query","இந்த வினவலை எந்த தொகுதியும் கையாளவில்லை"}. +{"No node specified","எந்த முனையும் குறிப்பிடப்படவில்லை"}. +{"No 'password' found in data form","தரவு வடிவத்தில் 'கடவுச்சொல்' காணப்படவில்லை"}. +{"No 'password' found in this query","இந்த வினவலில் 'கடவுச்சொல்' இல்லை"}. +{"No 'path' found in data form","தரவு வடிவத்தில் 'பாதை' இல்லை"}. +{"No pending subscriptions found","நிலுவையில் உள்ள சந்தாக்கள் எதுவும் கிடைக்கவில்லை"}. +{"No privacy list with this name found","இந்த பெயருடன் தனியுரிமை பட்டியல் எதுவும் இல்லை"}. +{"No private data found in this query","இந்த வினவலில் தனிப்பட்ட தரவு எதுவும் காணப்படவில்லை"}. +{"No running node found","இயங்கும் முனை எதுவும் கிடைக்கவில்லை"}. +{"No services available","சேவைகள் எதுவும் கிடைக்கவில்லை"}. +{"No statistics found for this item","இந்த உருப்படிக்கு புள்ளிவிவரங்கள் எதுவும் கிடைக்கவில்லை"}. +{"No 'to' attribute found in the invitation","அழைப்பில் காணப்படும் 'to' பண்புக்கூறு"}. +{"Nobody","யாரும்"}. +{"Node already exists","முனை ஏற்கனவே உள்ளது"}. +{"Node ID","முனை ஐடி"}. +{"Node index not found","முனை குறியீடு கிடைக்கவில்லை"}. +{"Node not found","முனை கிடைக்கவில்லை"}. +{"Node ~p","முனை ~p"}. +{"Nodeprep has failed","நோடெப்ரெப் தோல்வியுற்றது"}. +{"Nodes","முனைகள்"}. +{"Node","கணு"}. +{"None","எதுவுமில்லை"}. +{"Not allowed","அனுமதிக்கப்படவில்லை"}. +{"Not Found","கண்டுபிடிக்கப்படவில்லை"}. +{"Not subscribed","குழுசேரவில்லை"}. +{"Notify subscribers when items are removed from the node","முனையிலிருந்து உருப்படிகள் அகற்றப்படும்போது சந்தாதாரர்களுக்கு அறிவிக்கவும்"}. +{"Notify subscribers when the node configuration changes","முனை உள்ளமைவு மாறும்போது சந்தாதாரர்களுக்கு அறிவிக்கவும்"}. +{"Notify subscribers when the node is deleted","முனை நீக்கப்படும் போது சந்தாதாரர்களுக்கு அறிவிக்கவும்"}. +{"November","ஐ-கார்த்திகை"}. +{"Number of answers required","தேவையான பதில்களின் எண்ணிக்கை"}. +{"Number of occupants","குடியிருப்பாளர்களின் எண்ணிக்கை"}. +{"Number of Offline Messages","இணைப்பில்லாத செய்திகளின் எண்ணிக்கை"}. +{"Number of online users","நிகழ்நிலை பயனர்களின் எண்ணிக்கை"}. +{"Number of registered users","பதிவுசெய்யப்பட்ட பயனர்களின் எண்ணிக்கை"}. +{"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","உருப்படிகளை தானாக தூய்மைப்படுத்துவதற்கான விநாடிகளின் எண்ணிக்கை, அல்லது அதிகபட்சம் விதிக்கப்பட்ட சேவையகத்தைத் தவிர வேறு எந்த குறிப்பிட்ட வரம்பையும் `அதிகபட்சம்`"}. +{"Occupants are allowed to invite others","குடியிருப்பாளர்கள் மற்றவர்களை அழைக்க அனுமதிக்கப்படுகிறார்கள்"}. +{"Occupants are allowed to query others","குடியிருப்பாளர்கள் மற்றவர்களை வினவ அனுமதிக்கப்படுகிறார்கள்"}. +{"Occupants May Change the Subject","குடியிருப்பாளர்கள் இந்த விசயத்தை மாற்றலாம்"}. +{"October","பு-ஐப்பசி"}. +{"OK","சரி"}. +{"Old Password:","பழைய கடவுச்சொல்:"}. +{"Online Users","நிகழ்நிலை பயனர்கள்"}. +{"Online","ஆன்லைனில்"}. +{"Only collection node owners may associate leaf nodes with the collection","சேகரிப்பு முனை உரிமையாளர்கள் மட்டுமே இலை முனைகளை சேகரிப்புடன் தொடர்புபடுத்தலாம்"}. +{"Only deliver notifications to available users","கிடைக்கக்கூடிய பயனர்களுக்கு மட்டுமே அறிவிப்புகளை வழங்கவும்"}. +{"Only or tags are allowed","அல்லது குறிச்சொற்கள் மட்டுமே அனுமதிக்கப்படுகின்றன"}. +{"Only element is allowed in this query","இந்த வினவலில் உறுப்பு மட்டுமே அனுமதிக்கப்படுகிறது"}. +{"Only members may query archives of this room","உறுப்பினர்கள் மட்டுமே இந்த அறையின் காப்பகங்களை வினவலாம்"}. +{"Only moderators and participants are allowed to change the subject in this room","இந்த அறையில் உள்ள விசயத்தை மாற்ற மதிப்பீட்டாளர்கள் மற்றும் பங்கேற்பாளர்கள் மட்டுமே அனுமதிக்கப்படுகிறார்கள்"}. +{"Only moderators are allowed to change the subject in this room","இந்த அறையில் உள்ள விசயத்தை மாற்ற மதிப்பீட்டாளர்கள் மட்டுமே அனுமதிக்கப்படுகிறார்கள்"}. +{"Only moderators are allowed to retract messages","மதிப்பீட்டாளர்கள் மட்டுமே செய்திகளைத் திரும்பப் பெற அனுமதிக்கப்படுகிறார்கள்"}. +{"Only moderators can approve voice requests","மதிப்பீட்டாளர்கள் மட்டுமே குரல் கோரிக்கைகளை அங்கீகரிக்க முடியும்"}. +{"Only occupants are allowed to send messages to the conference","குடியிருப்பாளர்கள் மட்டுமே மாநாட்டிற்கு செய்திகளை அனுப்ப அனுமதிக்கப்படுகிறார்கள்"}. +{"Only occupants are allowed to send queries to the conference","மாநாட்டிற்கு வினவல்களை அனுப்ப குடியிருப்பாளர்கள் மட்டுமே அனுமதிக்கப்படுகிறார்கள்"}. +{"Only publishers may publish","வெளியீட்டாளர்கள் மட்டுமே வெளியிடலாம்"}. +{"Only service administrators are allowed to send service messages","பணி செய்திகளை அனுப்ப பணி நிர்வாகிகள் மட்டுமே அனுமதிக்கப்படுகிறார்கள்"}. +{"Only those on a whitelist may associate leaf nodes with the collection","அனுமதிப்பட்டியலில் இருப்பவர்கள் மட்டுமே இலை முனைகளை சேகரிப்புடன் தொடர்புபடுத்தலாம்"}. +{"Only those on a whitelist may subscribe and retrieve items","அனுமதிப்பட்டியலில் இருப்பவர்கள் மட்டுமே உருப்படிகளை குழுசேரலாம் மற்றும் மீட்டெடுக்கலாம்"}. +{"Organization Name","அமைப்பு பெயர்"}. +{"Organization Unit","அமைப்பு பிரிவு"}. +{"Other Modules Available:","பிற தொகுதிகள் கிடைக்கின்றன:"}. +{"Outgoing s2s Connections","வெளிச்செல்லும் எச் 2 எச் இணைப்புகள்"}. +{"Owner privileges required","உரிமையாளர் சலுகைகள் தேவை"}. +{"Packet relay is denied by service policy","பணி கொள்கையால் பாக்கெட் ரிலே மறுக்கப்படுகிறது"}. +{"Participant ID","பங்கேற்பாளர் ஐடி"}. +{"Participant","பங்கேற்பாளர்"}. +{"Password Verification","கடவுச்சொல் சரிபார்ப்பு"}. +{"Password Verification:","கடவுச்சொல் சரிபார்ப்பு:"}. +{"Password","கடவுச்சொல்"}. +{"Password:","கடவுச்சொல்:"}. +{"Path to Dir","அடைவு க்கு பாதை"}. +{"Path to File","தாக்கல் செய்வதற்கான பாதை"}. +{"Payload semantic type information","பேலோட் சொற்பொருள் வகை செய்தி"}. +{"Period: ","காலம்: "}. +{"Persist items to storage","சேமிப்பகத்திற்கு உருப்படிகளைத் தொடருங்கள்"}. +{"Persistent","விடாமுயற்சி"}. +{"Ping query is incorrect","பிங் வினவல் தவறானது"}. +{"Ping","பிங்"}. +{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","இந்த விருப்பங்கள் பில்டின் மென்சியா தரவுத்தளத்தை மட்டுமே காப்புப் பிரதி எடுக்கும் என்பதை நினைவில் கொள்க. நீங்கள் ODBC தொகுதியைப் பயன்படுத்துகிறீர்கள் என்றால், உங்கள் கவிமொ தரவுத்தளத்தையும் தனித்தனியாக காப்புப் பிரதி எடுக்க வேண்டும்."}. +{"Please, wait for a while before sending new voice request","தயவுசெய்து, புதிய குரல் கோரிக்கையை அனுப்புவதற்கு முன் சிறிது நேரம் காத்திருங்கள்"}. +{"Pong","பாங்"}. +{"Possessing 'ask' attribute is not allowed by RFC6121","'கேளுங்கள்' பண்புக்கூறு வைத்திருப்பது RFC6121 ஆல் அனுமதிக்கப்படவில்லை"}. +{"Present real Jabber IDs to","உண்மையான சாபர் ஐடிகளை வழங்கவும்"}. +{"Previous session not found","முந்தைய அமர்வு காணப்படவில்லை"}. +{"Previous session PID has been killed","முந்தைய அமர்வு பிஐடி கொல்லப்பட்டுள்ளது"}. +{"Previous session PID has exited","முந்தைய அமர்வு பிஐடி வெளியேறியது"}. +{"Previous session PID is dead","முந்தைய அமர்வு பிஐடி இறந்துவிட்டது"}. +{"Previous session timed out","முந்தைய அமர்வு நேரம் முடிந்தது"}. +{"private, ","தனிப்பட்ட, "}. +{"Public","பொது"}. +{"Publish model","மாதிரி வெளியிடு"}. +{"Publish-Subscribe","வெளியீட்டு-சந்தா"}. +{"PubSub subscriber request","பப்சப் சந்தாதாரர் கோரிக்கை"}. +{"Purge all items when the relevant publisher goes offline","தொடர்புடைய வெளியீட்டாளர் ஆஃப்லைனில் செல்லும்போது எல்லா பொருட்களையும் தூய்மைப்படுத்துங்கள்"}. +{"Push record not found","புச் பதிவு கிடைக்கவில்லை"}. +{"Queries to the conference members are not allowed in this room","இந்த அறையில் மாநாட்டு உறுப்பினர்களுக்கு வினவல்கள் அனுமதிக்கப்படவில்லை"}. +{"Query to another users is forbidden","மற்றொரு பயனர்களுக்கு வினவல் தடைசெய்யப்பட்டுள்ளது"}. +{"RAM and disc copy","ராம் மற்றும் வட்டு நகல்"}. +{"RAM copy","ரேம் நகல்"}. +{"Really delete message of the day?","அன்றைய செய்தியை உண்மையில் நீக்கவா?"}. +{"Receive notification from all descendent nodes","அனைத்து வழித்தோன்றல்களிலிருந்தும் அறிவிப்பைப் பெறுங்கள்"}. +{"Receive notification from direct child nodes only","நேரடி குழந்தை முனைகளிலிருந்து மட்டுமே அறிவிப்பைப் பெறுங்கள்"}. +{"Receive notification of new items only","புதிய பொருட்களின் அறிவிப்பைப் பெறுங்கள்"}. +{"Receive notification of new nodes only","புதிய முனைகளின் அறிவிப்பைப் பெறுங்கள்"}. +{"Recipient is not in the conference room","பெறுநர் மாநாட்டு அறையில் இல்லை"}. +{"Register an XMPP account","எக்ச்எம்பி.பி கணக்கை பதிவு செய்யுங்கள்"}. +{"Register","பதிவு செய்யுங்கள்"}. +{"Remote copy","தொலை நகல்"}. +{"Remove a hat from a user","ஒரு பயனரிடமிருந்து ஒரு தொப்பியை அகற்றவும்"}. +{"Remove User","பயனரை அகற்று"}. +{"Replaced by new connection","புதிய இணைப்பு மூலம் மாற்றப்பட்டது"}. +{"Request has timed out","கோரிக்கை நேரம் முடிந்துவிட்டது"}. +{"Request is ignored","கோரிக்கை புறக்கணிக்கப்படுகிறது"}. +{"Requested role","கோரப்பட்ட பங்கு"}. +{"Resources","வளங்கள்"}. +{"Restart Service","சேவையை மறுதொடக்கம் செய்யுங்கள்"}. +{"Restore Backup from File at ","கோப்பிலிருந்து காப்புப்பிரதியை மீட்டெடுக்கவும் "}. +{"Restore binary backup after next ejabberd restart (requires less memory):","அடுத்த EJABBERD மறுதொடக்கத்திற்குப் பிறகு பைனரி காப்புப்பிரதியை மீட்டமைக்கவும் (குறைவான நினைவகம் தேவை):"}. +{"Restore binary backup immediately:","பைனரி காப்புப்பிரதியை உடனடியாக மீட்டமைக்கவும்:"}. +{"Restore plain text backup immediately:","எளிய உரை காப்புப்பிரதியை உடனடியாக மீட்டெடுக்கவும்:"}. +{"Restore","மீட்டமை"}. +{"Result","விளைவு"}. +{"Roles and Affiliations that May Retrieve Member List","உறுப்பினர் பட்டியலை மீட்டெடுக்கக்கூடிய பாத்திரங்கள் மற்றும் இணைப்புகள்"}. +{"Roles for which Presence is Broadcasted","இருப்பு ஒளிபரப்பப்படும் பாத்திரங்கள்"}. +{"Roles that May Send Private Messages","தனிப்பட்ட செய்திகளை அனுப்பக்கூடிய பாத்திரங்கள்"}. +{"Room Configuration","அறை உள்ளமைவு"}. +{"Room creation is denied by service policy","பணி கொள்கையால் அறை உருவாக்கம் மறுக்கப்படுகிறது"}. +{"Room description","அறை விளக்கம்"}. +{"Room Occupants","அறை குடியிருப்பாளர்கள்"}. +{"Room terminates","அறை முடிவடைகிறது"}. +{"Room title","அறை தலைப்பு"}. +{"Roster groups allowed to subscribe","பட்டியல் குழுக்கள் குழுசேர அனுமதிக்கப்பட்டன"}. +{"Roster size","பட்டியல் அளவு"}. +{"Running Nodes","இயங்கும் முனைகள்"}. +{"~s invites you to the room ~s","~s எச் உங்களை அறைக்கு அழைக்கிறது ~s கள்"}. +{"Saturday","காரிக்கிழமை"}. +{"Search from the date","தேதியிலிருந்து தேடுங்கள்"}. +{"Search Results for ","தேடல் முடிவுகள் "}. +{"Search the text","உரையைத் தேடுங்கள்"}. +{"Search until the date","தேதி வரை தேடுங்கள்"}. +{"Search users in ","பயனர்களைத் தேடுங்கள் "}. +{"Send announcement to all online users on all hosts","அனைத்து ஓச்ட்களிலும் அனைத்து நிகழ்நிலை பயனர்களுக்கும் அறிவிப்பை அனுப்பவும்"}. +{"Send announcement to all online users","அனைத்து நிகழ்நிலை பயனர்களுக்கும் அறிவிப்பை அனுப்பவும்"}. +{"Send announcement to all users on all hosts","அனைத்து ஓச்ட்களிலும் உள்ள அனைத்து பயனர்களுக்கும் அறிவிப்பை அனுப்பவும்"}. +{"Send announcement to all users","அனைத்து பயனர்களுக்கும் அறிவிப்பை அனுப்பவும்"}. +{"September","ஆ-புரட்டாசி"}. +{"Server:","சேவையகம்:"}. +{"Service list retrieval timed out","பணி பட்டியல் மீட்டெடுப்பு நேரம் முடிந்தது"}. +{"Session state copying timed out","அமர்வு நிலை நகலெடுக்கும் நேரம் முடிந்தது"}. +{"Set message of the day and send to online users","அன்றைய செய்தியை அமைத்து நிகழ்நிலை பயனர்களுக்கு அனுப்பவும்"}. +{"Set message of the day on all hosts and send to online users","அனைத்து ஓச்ட்களிலும் அன்றைய செய்தியை அமைத்து நிகழ்நிலை பயனர்களுக்கு அனுப்பவும்"}. +{"Shared Roster Groups","பகிரப்பட்ட பட்டியல் குழுக்கள்"}. +{"Show Integral Table","ஒருங்கிணைந்த அட்டவணையைக் காட்டு"}. +{"Show Occupants Join/Leave","காட்டு குடியிருப்பாளர்கள் சேர/விடுகிறார்கள்"}. +{"Show Ordinary Table","சாதாரண அட்டவணையைக் காட்டு"}. +{"Shut Down Service","சேவையை மூடு"}. +{"SOCKS5 Bytestreams","SOCKS5 BYTESTREAMS"}. +{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","சில எக்ச்எம்பிபி வாடிக்கையாளர்கள் உங்கள் கடவுச்சொல்லை கணினியில் சேமிக்க முடியும், ஆனால் பாதுகாப்பு காரணங்களுக்காக இதை உங்கள் தனிப்பட்ட கணினியில் மட்டுமே செய்ய வேண்டும்."}. +{"Sources Specs:","ஆதார விவரக்குறிப்புகள்:"}. +{"Specify the access model","அணுகல் மாதிரியைக் குறிப்பிடவும்"}. +{"Specify the event message type","நிகழ்வு செய்தி வகையைக் குறிப்பிடவும்"}. +{"Specify the publisher model","வெளியீட்டாளர் மாதிரியைக் குறிப்பிடவும்"}. +{"Stanza id is not valid","முடிப்பு ஐடி செல்லுபடியாகாது"}. +{"Stanza ID","முடிப்பு"}. +{"Statically specify a replyto of the node owner(s)","முனை உரிமையாளரின் (கள்) ஒரு பதிலை நிலையான முறையில் குறிப்பிடவும்"}. +{"Stopped Nodes","நிறுத்தப்பட்ட முனைகள்"}. +{"Store binary backup:","பைனரி காப்புப்பிரதியை சேமிக்கவும்:"}. +{"Store plain text backup:","எளிய உரை காப்புப்பிரதியை சேமிக்கவும்:"}. +{"Stream management is already enabled","ச்ட்ரீம் மேலாண்மை ஏற்கனவே இயக்கப்பட்டது"}. +{"Stream management is not enabled","ச்ட்ரீம் மேலாண்மை இயக்கப்படவில்லை"}. +{"Subject","பொருள்"}. +{"Submitted","சமர்ப்பிக்கப்பட்டது"}. +{"Subscriber Address","சந்தாதாரர் முகவரி"}. +{"Subscribers may publish","சந்தாதாரர்கள் வெளியிடலாம்"}. +{"Subscription requests must be approved and only subscribers may retrieve items","சந்தா கோரிக்கைகள் அங்கீகரிக்கப்பட வேண்டும் மற்றும் சந்தாதாரர்கள் மட்டுமே உருப்படிகளை மீட்டெடுக்க முடியும்"}. +{"Subscriptions are not allowed","சந்தாக்கள் அனுமதிக்கப்படவில்லை"}. +{"Sunday","ஞாயிற்றுக்கிழமை"}. +{"Text associated with a picture","ஒரு படத்துடன் தொடர்புடைய உரை"}. +{"Text associated with a sound","ஒலி ஒரு ஒலியுடன் தொடர்புடையது"}. +{"Text associated with a video","வீடியோவுடன் தொடர்புடைய உரை"}. +{"Text associated with speech","பேச்சுடன் தொடர்புடைய உரை"}. +{"That nickname is already in use by another occupant","அந்த புனைப்பெயர் ஏற்கனவே மற்றொரு குடியிருப்பாளரால் பயன்பாட்டில் உள்ளது"}. +{"That nickname is registered by another person","அந்த புனைப்பெயர் மற்றொரு நபரால் பதிவு செய்யப்பட்டுள்ளது"}. +{"The account already exists","கணக்கு ஏற்கனவே உள்ளது"}. +{"The account was not unregistered","கணக்கு பதிவு செய்யப்படவில்லை"}. +{"The body text of the last received message","கடைசியாக பெறப்பட்ட செய்தியின் உடல் உரை"}. +{"The CAPTCHA is valid.","கேப்ட்சா செல்லுபடியாகும்."}. +{"The CAPTCHA verification has failed","கேப்ட்சா சரிபார்ப்பு தோல்வியுற்றது"}. +{"The captcha you entered is wrong","நீங்கள் உள்ளிட்ட கேப்ட்சா தவறு"}. +{"The child nodes (leaf or collection) associated with a collection","சேகரிப்புடன் தொடர்புடைய குழந்தை முனைகள் (இலை அல்லது சேகரிப்பு)"}. +{"The collections with which a node is affiliated","ஒரு முனை இணைக்கப்பட்ட தொகுப்புகள்"}. +{"The DateTime at which a leased subscription will end or has ended","குத்தகைக்கு விடப்பட்ட சந்தா முடிவடையும் அல்லது முடிவடைந்த தேதிநேரம்"}. +{"The datetime when the node was created","முனை உருவாக்கப்பட்ட தேதிநேரம்"}. +{"The default language of the node","முனையின் இயல்புநிலை மொழி"}. +{"The feature requested is not supported by the conference","கோரப்பட்ட நற்பொருத்தம் மாநாட்டால் ஆதரிக்கப்படவில்லை"}. +{"The JID of the node creator","முனை படைப்பாளரின் சிட்"}. +{"The JIDs of those to contact with questions","கேள்விகளுடன் தொடர்பு கொள்ளுபவர்களின் குழந்தைகள்"}. +{"The JIDs of those with an affiliation of owner","உரிமையாளரின் இணைப்பு உள்ளவர்களின் குழந்தைகள்"}. +{"The JIDs of those with an affiliation of publisher","வெளியீட்டாளரின் இணைப்பு உள்ளவர்களின் குழந்தைகள்"}. +{"The list of all online users","அனைத்து நிகழ்நிலை பயனர்களின் பட்டியல்"}. +{"The list of all users","அனைத்து பயனர்களின் பட்டியல்"}. +{"The list of JIDs that may associate leaf nodes with a collection","இலை முனைகளை ஒரு தொகுப்போடு தொடர்புபடுத்தக்கூடிய JIDS இன் பட்டியல்"}. +{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","ஒரு சேகரிப்புடன் தொடர்புடைய அதிகபட்ச குழந்தை முனைகளின் எண்ணிக்கை, அல்லது அதிகபட்சமாக விதிக்கப்பட்ட சேவையகத்தைத் தவிர வேறு எந்த குறிப்பிட்ட வரம்பும் இல்லாமல் `அதிகபட்சம் '"}. +{"The minimum number of milliseconds between sending any two notification digests","இரண்டு அறிவிப்பு செரிமானங்களையும் அனுப்புவதற்கு இடையில் குறைந்தபட்ச மில்லி விநாடிகளின் எண்ணிக்கை"}. +{"The name of the node","முனையின் பெயர்"}. +{"The node is a collection node","முனை ஒரு சேகரிப்பு முனை"}. +{"The node is a leaf node (default)","முனை ஒரு இலை முனை (இயல்புநிலை)"}. +{"The NodeID of the relevant node","தொடர்புடைய முனையின் நோடிட்"}. +{"The number of pending incoming presence subscription requests","நிலுவையில் உள்ள உள்வரும் இருப்பு சந்தா கோரிக்கைகளின் எண்ணிக்கை"}. +{"The number of subscribers to the node","முனைக்கு சந்தாதாரர்களின் எண்ணிக்கை"}. +{"The number of unread or undelivered messages","படிக்காத அல்லது வழங்கப்படாத செய்திகளின் எண்ணிக்கை"}. +{"The password contains unacceptable characters","கடவுச்சொல்லில் ஏற்றுக்கொள்ள முடியாத எழுத்துக்கள் உள்ளன"}. +{"The password is too weak","கடவுச்சொல் மிகவும் பலவீனமாக உள்ளது"}. +{"the password is","கடவுச்சொல்"}. +{"The password of your XMPP account was successfully changed.","உங்கள் எக்ச்எம்பிபி கணக்கின் கடவுச்சொல் வெற்றிகரமாக மாற்றப்பட்டது."}. +{"The password was not changed","கடவுச்சொல் மாற்றப்படவில்லை"}. +{"The passwords are different","கடவுச்சொற்கள் வேறுபட்டவை"}. +{"The presence states for which an entity wants to receive notifications","ஒரு நிறுவனம் அறிவிப்புகளைப் பெற விரும்பும் இருப்பு நிலைகள்"}. +{"The query is only allowed from local users","வினவல் உள்ளக பயனர்களிடமிருந்து மட்டுமே அனுமதிக்கப்படுகிறது"}. +{"The query must not contain elements","வினவலில் கூறுகள் இருக்கக் கூடாது"}. +{"The room subject can be modified by participants","அறை பொருள் பங்கேற்பாளர்களால் மாற்றப்படலாம்"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","முனையில் உள்ள தரவின் சொற்பொருள் வகை செய்தி, பொதுவாக பேலோடின் பெயர்வெளியால் குறிப்பிடப்படுகிறது (ஏதேனும் இருந்தால்)"}. +{"The sender of the last received message","கடைசியாக பெறப்பட்ட செய்தியை அனுப்பு"}. +{"The stanza MUST contain only one element, one element, or one element","ச்டான்சாவில் ஒரே உறுப்பு, ஒரு உறுப்பு அல்லது ஒரு உறுப்பு மட்டுமே இருக்க வேண்டும்"}. +{"The subscription identifier associated with the subscription request","சந்தா கோரிக்கையுடன் தொடர்புடைய சந்தா அடையாளங்காட்டி"}. +{"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","பொருத்தமான செய்தி உடல் உறுப்பை உருவாக்குவதற்காக பேலோடுகளுக்கு பயன்படுத்தக்கூடிய எக்ச்எச்எல் மாற்றத்தின் முகவரி."}. +{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","செல்லுபடியாகும் தரவு படிவங்களை உருவாக்குவதற்காக பேலோட் வடிவத்தில் பயன்படுத்தக்கூடிய ஒரு எக்ச்எச்எல் உருமாற்றத்தின் முகவரி, கிளையன்ட் பொதுவான தரவு படிவங்கள் வழங்குதல் எஞ்சின் பயன்படுத்தி காண்பிக்க முடியும்"}. +{"There was an error changing the password: ","கடவுச்சொல்லை மாற்றுவதில் பிழை ஏற்பட்டது: "}. +{"There was an error creating the account: ","கணக்கை உருவாக்கும் பிழை இருந்தது: "}. +{"There was an error deleting the account: ","கணக்கை நீக்குவதில் பிழை ஏற்பட்டது: "}. +{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","இது வழக்கு உணர்வற்றது: மாக்பெத் மற்றும் மாக்பெத் தான்."}. +{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","இந்த XMPP சேவையகத்தில் ஒரு XMPP கணக்கை பதிவு செய்ய இந்த பக்கம் அனுமதிக்கிறது. உங்கள் சிட் (சாபர் ஐடி) படிவமாக இருக்கும்: பயனர்பெயர்@சேவையகம். புலங்களை சரியாக நிரப்ப வழிமுறைகளை கவனமாகப் படியுங்கள்."}. +{"This page allows to unregister an XMPP account in this XMPP server.","இந்த எக்ச்எம்பி.பி சேவையகத்தில் எக்ச்எம்பி.பி கணக்கை பதிவு செய்ய இந்த பக்கம் அனுமதிக்கிறது."}. +{"This room is not anonymous","இந்த அறை அநாமதேயமானது அல்ல"}. +{"This service can not process the address: ~s","இந்த பணி முகவரியை செயலாக்க முடியாது: ~s"}. +{"Thursday","வியாழக்கிழமை"}. +{"Time delay","நேர நேரந்தவறுகை"}. +{"Timed out waiting for stream resumption","ச்ட்ரீம் மறுதொடக்கத்திற்காக காத்திருக்கும் நேரம்"}. +{"To register, visit ~s","பதிவு செய்ய, ~s பார்வையிடவும்"}. +{"To ~ts","~ts K"}. +{"Token TTL","கிள்ளாக்கு டி.டி.எல்"}. +{"Too many active bytestreams","பல செயலில் உள்ள பைட்டிரீம்கள்"}. +{"Too many CAPTCHA requests","பல கேப்ட்சா கோரிக்கைகள்"}. +{"Too many child elements","பல குழந்தை கூறுகள்"}. +{"Too many elements","பல கூறுகள்"}. +{"Too many elements","பல கூறுகள்"}. +{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","இந்த ஐபி முகவரியிலிருந்து (~p) பல (~s) தோல்வியுற்ற அங்கீகாரங்கள் ~s. முகவரி UTC இல் தடைசெய்யப்படும்"}. +{"Too many receiver fields were specified","அதிகமான ரிசீவர் புலங்கள் குறிப்பிடப்பட்டன"}. +{"Too many unacked stanzas","பல அறியப்படாத சரணங்கள்"}. +{"Too many users in this conference","இந்த மாநாட்டில் அதிகமான பயனர்கள்"}. +{"Traffic rate limit is exceeded","போக்குவரத்து வீத வரம்பு மீறப்பட்டது"}. +{"~ts's MAM Archive","~ts இன் மாம் காப்பகம்"}. +{"~ts's Offline Messages Queue","~ts இன் இணைப்பில்லாத செய்திகள் வரிசை"}. +{"Tuesday","செவ்வாய்க்கிழமை"}. +{"Unable to generate a CAPTCHA","கேப்ட்சாவை உருவாக்க முடியவில்லை"}. +{"Unable to register route on existing local domain","தற்போதுள்ள உள்ளக களத்தில் வழியை பதிவு செய்ய முடியவில்லை"}. +{"Unauthorized","அங்கீகரிக்கப்படாதது"}. +{"Unexpected action","எதிர்பாராத நடவடிக்கை"}. +{"Unexpected error condition: ~p","எதிர்பாராத பிழை நிலை: ~p"}. +{"Uninstall","நிறுவல் நீக்க"}. +{"Unregister an XMPP account","ஒரு எக்ச்எம்பிபி கணக்கை பதிவு செய்யவும்"}. +{"Unregister","பதிவு செய்யப்படாதது"}. +{"Unsupported element","ஆதரிக்கப்படாத உறுப்பு"}. +{"Unsupported version","ஆதரிக்கப்படாத பதிப்பு"}. +{"Update message of the day (don't send)","அன்றைய செய்தியைப் புதுப்பிக்கவும் (அனுப்ப வேண்டாம்)"}. +{"Update message of the day on all hosts (don't send)","எல்லா ஓச்ட்களிலும் அன்றைய செய்தியைப் புதுப்பிக்கவும் (அனுப்ப வேண்டாம்)"}. +{"Update specs to get modules source, then install desired ones.","தொகுதிகள் மூலத்தைப் பெற விவரக்குறிப்புகளைப் புதுப்பிக்கவும், பின்னர் விரும்பியவற்றை நிறுவவும்."}. +{"Update Specs","விவரக்குறிப்புகளைப் புதுப்பிக்கவும்"}. +{"Updating the vCard is not supported by the vCard storage backend","VCARD ஐப் புதுப்பிப்பது VCARD சேமிப்பக பின்தளத்தில் ஆதரிக்கப்படவில்லை"}. +{"Upgrade","மேம்படுத்தல்"}. +{"URL for Archived Discussion Logs","காப்பகப்படுத்தப்பட்ட கலந்துரையாடல் பதிவுகளுக்கான முகவரி"}. +{"User already exists","பயனர் ஏற்கனவே உள்ளது"}. +{"User (jid)","பயனர் (சிட்)"}. +{"User JID","பயனர் சிட்"}. +{"User Management","பயனர் மேலாண்மை"}. +{"User not allowed to perform an IQ set on another user's vCard.","மற்றொரு பயனரின் VCARD இல் IQ தொகுப்பை செய்ய பயனர் அனுமதிக்கப்படவில்லை."}. +{"User removed","பயனர் அகற்றப்பட்டார்"}. +{"User session not found","பயனர் அமர்வு காணப்படவில்லை"}. +{"User session terminated","பயனர் அமர்வு நிறுத்தப்பட்டது"}. +{"User ~ts","பயனர் ~ts"}. +{"Username:","பயனர்பெயர்:"}. +{"Users are not allowed to register accounts so quickly","பயனர்கள் கணக்குகளை விரைவாக பதிவு செய்ய அனுமதிக்கப்படுவதில்லை"}. +{"Users Last Activity","பயனர்கள் கடைசி செயல்பாடு"}. +{"Users","பயனர்கள்"}. +{"User","பயனர்"}. +{"Value 'get' of 'type' attribute is not allowed","'வகை' பண்புக்கூறின் மதிப்பு 'பெறுதல்' அனுமதிக்கப்படவில்லை"}. +{"Value of '~s' should be boolean","'~s' மதிப்பு பூலியனாக இருக்க வேண்டும்"}. +{"Value of '~s' should be datetime string","'~s' இன் மதிப்பு தேதிநேர சரமாக இருக்க வேண்டும்"}. +{"Value of '~s' should be integer","'~s' இன் மதிப்பு முழு எண்ணாக இருக்க வேண்டும்"}. +{"Value 'set' of 'type' attribute is not allowed","'வகை' பண்புக்கூறின் 'தொகுப்பு' மதிப்பு அனுமதிக்கப்படவில்லை"}. +{"vCard User Search","VCARD பயனர் தேடல்"}. +{"View joined MIX channels","கலப்பு சேனல்களில் சேர்ந்தார்"}. +{"Virtual Hosts","மெய்நிகர் ஓச்ட்கள்"}. +{"Visitors are not allowed to change their nicknames in this room","இந்த அறையில் பார்வையாளர்கள் தங்கள் புனைப்பெயர்களை மாற்ற அனுமதிக்கப்படுவதில்லை"}. +{"Visitors are not allowed to send messages to all occupants","பார்வையாளர்கள் அனைத்து குடியிருப்பாளர்களுக்கும் செய்திகளை அனுப்ப அனுமதிக்கப்படுவதில்லை"}. +{"Visitor","பார்வையாளர்"}. +{"Voice requests are disabled in this conference","இந்த மாநாட்டில் குரல் கோரிக்கைகள் முடக்கப்பட்டுள்ளன"}. +{"Voice request","குரல் கோரிக்கை"}. +{"Web client which allows to join the room anonymously","அநாமதேயமாக அறையில் சேர அனுமதிக்கும் வலை கிளையண்ட்"}. +{"Wednesday","புதன்கிழமை"}. +{"When a new subscription is processed and whenever a subscriber comes online","புதிய சந்தா செயலாக்கப்படும் போது, சந்தாதாரர் ஆன்லைனில் வரும்போதெல்லாம்"}. +{"When a new subscription is processed","புதிய சந்தா செயலாக்கப்படும் போது"}. +{"When to send the last published item","கடைசியாக வெளியிடப்பட்ட உருப்படியை எப்போது அனுப்ப வேண்டும்"}. +{"Whether an entity wants to receive an XMPP message body in addition to the payload format","ஒரு நிறுவனம் பேலோட் வடிவமைப்பிற்கு கூடுதலாக ஒரு எக்ச்எம்பிபி செய்தி உடலைப் பெற விரும்புகிறதா என்பதை"}. +{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","ஒரு நிறுவனம் அறிவிப்புகளின் செரிமானங்களை (திரட்டல்கள்) பெற விரும்புகிறதா அல்லது அனைத்து அறிவிப்புகளையும் தனித்தனியாகப் பெற விரும்புகிறதா"}. +{"Whether an entity wants to receive or disable notifications","ஒரு நிறுவனம் அறிவிப்புகளைப் பெற விரும்புகிறதா அல்லது முடக்க விரும்புகிறதா"}. +{"Whether owners or publisher should receive replies to items","உரிமையாளர்கள் அல்லது வெளியீட்டாளர் உருப்படிகளுக்கான பதில்களைப் பெற வேண்டுமா"}. +{"Whether the node is a leaf (default) or a collection","முனை ஒரு இலை (இயல்புநிலை) அல்லது தொகுப்பாக இருந்தாலும் சரி"}. +{"Whether to allow subscriptions","சந்தாக்களை அனுமதிக்க வேண்டுமா"}. +{"Whether to make all subscriptions temporary, based on subscriber presence","அனைத்து சந்தாக்களையும் தற்காலிகமாக மாற்றலாமா, சந்தாதாரர் இருப்பின் அடிப்படையில்"}. +{"Whether to notify owners about new subscribers and unsubscribes","புதிய சந்தாதாரர்கள் மற்றும் குழுவிலகங்களைப் பற்றி உரிமையாளர்களுக்கு அறிவிக்க வேண்டுமா"}. +{"Who can send private messages","தனிப்பட்ட செய்திகளை யார் அனுப்ப முடியும்"}. +{"Who may associate leaf nodes with a collection","இலை முனைகளை யார் சேகரிப்புடன் தொடர்புபடுத்தலாம்"}. +{"Wrong parameters in the web formulary","வலை சூத்திரத்தில் தவறான அளவுருக்கள்"}. +{"Wrong xmlns","தவறான xmlns"}. +{"XMPP Account Registration","எக்ச்எம்பிபி கணக்கு பதிவு"}. +{"XMPP Domains","எக்ச்எம்பிபி களங்கள்"}. +{"XMPP Show Value of Away","எக்ச்எம்பிபி சோ மதிப்பைக் காட்டுகிறது"}. +{"XMPP Show Value of Chat","எக்ச்எம்பிபி அரட்டையின் மதிப்பைக் காட்டுகிறது"}. +{"XMPP Show Value of DND (Do Not Disturb)","எக்ச்எம்பிபி டி.என்.டி யின் மதிப்பைக் காட்டுகிறது (தொந்தரவு செய்யாதீர்கள்)"}. +{"XMPP Show Value of XA (Extended Away)","XMPP XA இன் மதிப்பைக் காட்டுகிறது (நீட்டிக்கப்பட்டது)"}. +{"XMPP URI of Associated Publish-Subscribe Node","அசோசியேட்டட் பப்ளிச்-சப்ச்கிரிப்ட் முனையின் எக்ச்எம்பிபி யுஆர்ஐ"}. +{"You are being removed from the room because of a system shutdown","கணினி பணிநிறுத்தம் காரணமாக நீங்கள் அறையிலிருந்து அகற்றப்படுகிறீர்கள்"}. +{"You are not allowed to send private messages","தனிப்பட்ட செய்திகளை அனுப்ப உங்களுக்கு இசைவு இல்லை"}. +{"You are not joined to the channel","நீங்கள் சேனலுடன் சேரவில்லை"}. +{"You can later change your password using an XMPP client.","எக்ச்எம்பிபி கிளையண்டைப் பயன்படுத்தி உங்கள் கடவுச்சொல்லை பின்னர் மாற்றலாம்."}. +{"You have been banned from this room","இந்த அறையிலிருந்து நீங்கள் தடை செய்யப்பட்டுள்ளீர்கள்"}. +{"You have joined too many conferences","நீங்கள் பல மாநாடுகளில் சேர்ந்துள்ளீர்கள்"}. +{"You must fill in field \"Nickname\" in the form","நீங்கள் வடிவத்தில் புலம் \"புனைப்பெயரை\" நிரப்ப வேண்டும்"}. +{"You need a client that supports x:data and CAPTCHA to register","எக்ச்: தரவு மற்றும் கேப்ட்சா பதிவு செய்ய உங்களுக்கு ஒரு வாங்கி தேவை"}. +{"You need a client that supports x:data to register the nickname","புனைப்பெயரை பதிவு செய்ய எக்ச்: தரவை ஆதரிக்கும் வாங்கி உங்களுக்குத் தேவை"}. +{"You need an x:data capable client to search","தேட உங்களுக்கு ஒரு ஃச் தேவை: தேட தரவு திறன் கொண்ட வாங்கி"}. +{"Your active privacy list has denied the routing of this stanza.","உங்கள் செயலில் உள்ள தனியுரிமை பட்டியல் இந்த சரணத்தை வழிநடத்துவதை மறுத்துள்ளது."}. +{"Your contact offline message queue is full. The message has been discarded.","உங்கள் தொடர்பு இணைப்பில்லாத செய்தி வரிசை நிரம்பியுள்ளது. செய்தி நிராகரிக்கப்பட்டுள்ளது."}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","உங்கள் சந்தா கோரிக்கை மற்றும்/அல்லது செய்திகளுக்கான செய்திகள் தடுக்கப்பட்டுள்ளன ~s. உங்கள் சந்தா கோரிக்கையைத் தடுக்க, ~s கள் பார்வையிடவும்"}. +{"Your XMPP account was successfully registered.","உங்கள் எக்ச்எம்பிபி கணக்கு வெற்றிகரமாக பதிவு செய்யப்பட்டது."}. +{"Your XMPP account was successfully unregistered.","உங்கள் எக்ச்எம்பிபி கணக்கு வெற்றிகரமாக பதிவு செய்யப்படவில்லை."}. +{"You're not allowed to create nodes","முனைகளை உருவாக்க உங்களுக்கு இசைவு இல்லை"}. diff --git a/priv/msgs/th.msg b/priv/msgs/th.msg index 4730123e0..0def6490d 100644 --- a/priv/msgs/th.msg +++ b/priv/msgs/th.msg @@ -1,31 +1,27 @@ -%% -*- coding: latin-1 -*- -{"Access Configuration","การกำหนดค่าการเข้าถึง"}. -{"Access Control List Configuration","การกำหนดค่ารายการควบคุมการเข้าถึง"}. -{"Access control lists","รายการควบคุมการเข้าถึง"}. -{"Access Control Lists","รายการควบคุมการเข้าถึง"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" has set the subject to: "," ตั้งหัวข้อว่า: "}. {"Access denied by service policy","การเข้าถึงถูกปฏิเสธโดยนโยบายการบริการ"}. -{"Access rules","กฎการเข้าถึง"}. -{"Access Rules","กฎการเข้าถึง"}. {"Action on user","การดำเนินการกับผู้ใช้"}. -{"Add Jabber ID","เพิ่ม Jabber ID"}. -{"Add New","เพิ่มผู้ใช้ใหม่"}. {"Add User","เพิ่มผู้ใช้"}. -{"Administration","การดูแล"}. {"Administration of ","การดูแล "}. +{"Administration","การดูแล"}. {"Administrator privileges required","ต้องมีสิทธิพิเศษของผู้ดูแลระบบ"}. {"All activity","กิจกรรมทั้งหมด"}. +{"All Users","ผู้ใช้ทั้งหมด"}. {"Allow this Jabber ID to subscribe to this pubsub node?","อนุญาตให้ Jabber ID นี้เข้าร่วมเป็นสมาชิกของโหนด pubsub หรือไม่"}. {"Allow users to query other users","อนุญาตให้ผู้ใช้ถามคำถามกับผู้ใช้คนอื่นๆ ได้"}. {"Allow users to send invites","อนุญาตให้ผู้ใช้ส่งคำเชิญถึงกันได้"}. {"Allow users to send private messages","อนุญาตให้ผู้ใช้ส่งข้อความส่วนตัว"}. -{"All Users","ผู้ใช้ทั้งหมด"}. {"Announcements","ประกาศ"}. -{"anyone","ทุกคน"}. {"April","เมษายน"}. {"August","สิงหาคม"}. -{"Backup","การสำรองข้อมูล "}. {"Backup Management","การจัดการข้อมูลสำรอง"}. {"Backup to File at ","สำรองไฟล์ข้อมูลที่"}. +{"Backup","การสำรองข้อมูล "}. {"Bad format","รูปแบบที่ไม่ถูกต้อง"}. {"Birthday","วันเกิด"}. {"Change Password","เปลี่ยนรหัสผ่าน"}. @@ -33,33 +29,26 @@ {"Chatroom configuration modified","มีการปรับเปลี่ยนการกำหนดค่าของห้องสนทนา"}. {"Chatrooms","ห้องสนทนา"}. {"Choose a username and password to register with this server","เลือกชื่อผู้ใช้และรหัสผ่านเพื่อลงทะเบียนกับเซิร์ฟเวอร์นี้"}. -{"Choose modules to stop","เลือกโมดูลเพื่อหยุดการทำงาน"}. {"Choose storage type of tables","เลือกชนิดการจัดเก็บของตาราง"}. {"Choose whether to approve this entity's subscription.","เลือกว่าจะอนุมัติการสมัครเข้าใช้งานของเอนทิตี้นี้หรือไม่"}. {"City","เมือง"}. {"Commands","คำสั่ง"}. {"Conference room does not exist","ไม่มีห้องประชุม"}. {"Configuration","การกำหนดค่า"}. -{"Connected Resources:","ทรัพยากรที่เชื่อมต่อ:"}. {"Country","ประเทศ"}. -{"CPU Time:","เวลาการทำงานของ CPU:"}. -{"Database","ฐานข้อมูล"}. {"Database Tables Configuration at ","การกำหนดค่าตารางฐานข้อมูลที่"}. +{"Database","ฐานข้อมูล"}. {"December","ธันวาคม"}. {"Default users as participants","ผู้ใช้เริ่มต้นเป็นผู้เข้าร่วม"}. -{"Delete message of the day","ลบข้อความของวัน"}. {"Delete message of the day on all hosts","ลบข้อความของวันบนโฮสต์ทั้งหมด"}. -{"Delete Selected","ลบข้อความที่เลือก"}. +{"Delete message of the day","ลบข้อความของวัน"}. {"Delete User","ลบผู้ใช้"}. {"Deliver event notifications","ส่งการแจ้งเตือนเหตุการณ์"}. {"Deliver payloads with event notifications","ส่งส่วนของข้อมูล (payload) พร้อมกับการแจ้งเตือนเหตุการณ์"}. -{"Description:","รายละเอียด:"}. {"Disc only copy","คัดลอกเฉพาะดิสก์"}. -{"Displayed Groups:","กลุ่มที่แสดง:"}. {"Dump Backup to Text File at ","ถ่ายโอนการสำรองข้อมูลไปยังไฟล์ข้อความที่"}. {"Dump to Text File","ถ่ายโอนข้อมูลไปยังไฟล์ข้อความ"}. {"Edit Properties","แก้ไขคุณสมบัติ"}. -{"ejabberd IRC module","ejabberd IRC module"}. {"ejabberd MUC module","ejabberd MUC module"}. {"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe module"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams module"}. @@ -67,32 +56,21 @@ {"Email","อีเมล"}. {"Enable logging","เปิดใช้งานการบันทึก"}. {"End User Session","สิ้นสุดเซสชันของผู้ใช้"}. -{"Enter list of {Module, [Options]}","ป้อนรายการของ {โมดูล, [ตัวเลือก]}"}. {"Enter nickname you want to register","ป้อนชื่อเล่นที่คุณต้องการลงทะเบียน"}. {"Enter path to backup file","ป้อนพาธเพื่อสำรองไฟล์ข้อมูล"}. {"Enter path to jabberd14 spool dir","ป้อนพาธไปยัง jabberd14 spool dir"}. {"Enter path to jabberd14 spool file","ป้อนพาธไปยังไฟล์เก็บพักข้อมูล jabberd14"}. {"Enter path to text file","ป้อนพาธของไฟล์ข้อความ"}. -{"Erlang Jabber Server","Erlang Jabber Server"}. {"Family Name","นามสกุล"}. {"February","กุมภาพันธ์"}. -{"Fill in fields to search for any matching Jabber User","กรอกข้อมูลลงในฟิลด์เพื่อค้นหาผู้ใช้ Jabber ที่ตรงกัน"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","กรอกข้อมูลในแบบฟอร์มเพื่อค้นหาผู้ใช้ Jabber ที่ตรงกัน (ใส่เครื่องหมาย * ที่ท้ายสุดของฟิลด์เพื่อจับคู่กับสตริงย่อย)"}. {"Friday","วันศุกร์"}. -{"From","จาก"}. -{"From ~s","จาก ~s"}. {"Full Name","ชื่อเต็ม"}. {"Get Number of Online Users","แสดงจำนวนผู้ใช้ออนไลน์"}. {"Get Number of Registered Users","แสดงจำนวนผู้ใช้ที่ลงทะเบียน"}. {"Get User Last Login Time","แสดงเวลาเข้าสู่ระบบครั้งล่าสุดของผู้ใช้"}. -{"Get User Password","ขอรับรหัสผ่านของผู้ใช้"}. {"Get User Statistics","แสดงสถิติของผู้ใช้"}. -{"Group ","กลุ่ม"}. -{"Groups","กลุ่ม"}. {"has been banned","ถูกสั่งห้าม"}. {"has been kicked","ถูกไล่ออก"}. -{" has set the subject to: "," ตั้งหัวข้อว่า: "}. -{"Host","โฮสต์"}. {"Import Directory","อิมพอร์ตไดเร็กทอรี"}. {"Import File","อิมพอร์ตไฟล์"}. {"Import User from File at ","อิมพอร์ตผู้ใช้จากไฟล์ที่"}. @@ -100,16 +78,11 @@ {"Import Users From jabberd14 Spool Files","อิมพอร์ตผู้ใช้จากไฟล์เก็บพักข้อมูล jabberd14"}. {"Improper message type","ประเภทข้อความไม่เหมาะสม"}. {"Incorrect password","รหัสผ่านไม่ถูกต้อง"}. -{"Invalid affiliation: ~s","การเข้าร่วมที่ไม่ถูกต้อง: ~s"}. -{"Invalid role: ~s","บทบาทไม่ถูกต้อง: ~s"}. {"IP addresses","ที่อยู่ IP"}. -{"IRC Transport","การส่ง IRC"}. -{"IRC Username","ชื่อผู้ใช้ IRC"}. {"is now known as","ซึ่งรู้จักกันในชื่อ"}. {"It is not allowed to send private messages of type \"groupchat\"","ไม่อนุญาตให้ส่งข้อความส่วนตัวไปยัง \"กลุ่มสนทนา\""}. {"It is not allowed to send private messages to the conference","ไม่อนุญาตให้ส่งข้อความส่วนตัวไปยังห้องประชุม"}. {"Jabber ID","Jabber ID"}. -{"Jabber ID ~s is invalid","Jabber ID ~s ไม่ถูกต้อง"}. {"January","มกราคม"}. {"joins the room","เข้าห้องสนทนานี้"}. {"July","กรกฎาคม"}. @@ -119,43 +92,30 @@ {"Last month","เดือนที่แล้ว"}. {"Last year","ปีที่แล้ว"}. {"leaves the room","ออกจากห้อง"}. -{"Listened Ports","พอร์ทฟัง"}. -{"Listened Ports at ","พอร์ทฟังที่"}. -{"List of modules to start","รายการของโมดูลที่จะเริ่มการทำงาน"}. -{"Low level update script","อัพเดตสคริปต์ระดับต่ำ"}. {"Make participants list public","สร้างรายการผู้เข้าร่วมสำหรับใช้งานโดยบุคคลทั่วไป"}. {"Make room members-only","สร้างห้องสำหรับสมาชิกเท่านั้น"}. {"Make room password protected","สร้างห้องที่มีการป้องกันด้วยรหัสผ่าน"}. {"Make room persistent","สร้างเป็นห้องถาวร"}. {"Make room public searchable","สร้างเป็นห้องที่บุคคลทั่วไปสามารถค้นหาได้"}. {"March","มีนาคม"}. -{"Maximum Number of Occupants","จำนวนผู้ครอบครองห้องสูงสุด"}. -{"Max # of items to persist","จำนวนสูงสุดของรายการที่ยืนยัน"}. {"Max payload size in bytes","ขนาดสูงสุดของส่วนของข้อมูล (payload) มีหน่วยเป็นไบต์"}. +{"Maximum Number of Occupants","จำนวนผู้ครอบครองห้องสูงสุด"}. {"May","พฤษภาคม"}. -{"Members:","สมาชิก:"}. -{"Memory","หน่วยความจำ"}. {"Message body","เนื้อหาของข้อความ"}. {"Middle Name","ชื่อกลาง"}. {"Moderator privileges required","ต้องมีสิทธิพิเศษของผู้ดูแลการสนทนา"}. -{"moderators only","สำหรับผู้ดูแลการสนทนาเท่านั้น"}. -{"Module","โมดูล"}. -{"Modules","โมดูล"}. {"Monday","วันจันทร์"}. -{"Name:","ชื่อ:"}. {"Name","ชื่อ"}. {"Never","ไม่เคย"}. -{"Nickname","ชื่อเล่น"}. {"Nickname Registration at ","การลงทะเบียนชื่อเล่นที่ "}. -{"Nickname ~s does not exist in the room","ไม่มีชื่อเล่น ~s อยู่ในห้องนี้"}. +{"Nickname","ชื่อเล่น"}. {"No body provided for announce message","ไม่ได้ป้อนเนื้อหาสำหรับข้อความที่ประกาศ"}. {"No Data","ไม่มีข้อมูล"}. +{"No limit","ไม่จำกัด"}. {"Node ID","ID โหนด"}. {"Node not found","ไม่พบโหนด"}. {"Nodes","โหนด"}. -{"No limit","ไม่จำกัด"}. {"None","ไม่มี"}. -{"No resource provided","ไม่ได้ระบุข้อมูล"}. {"Notify subscribers when items are removed from the node","แจ้งเตือนผู้สมัครสมาชิกเมื่อรายการถูกลบออกจากโหนด"}. {"Notify subscribers when the node configuration changes","แจ้งเตือนผู้สมัครสมาชิกเมื่อการกำหนดค่าโหนดเปลี่ยนแปลง"}. {"Notify subscribers when the node is deleted","แจ้งเตือนผู้สมัครสมาชิกเมื่อโหนดถูกลบ"}. @@ -164,34 +124,26 @@ {"Number of online users","จำนวนผู้ใช้ออนไลน์"}. {"Number of registered users","จำนวนผู้ใช้ที่ลงทะเบียน"}. {"October","ตุลาคม"}. -{"Offline Messages:","ข้อความออฟไลน์:"}. -{"Offline Messages","ข้อความออฟไลน์"}. {"OK","ตกลง"}. -{"Online","ออนไลน์"}. -{"Online Users:","ผู้ใช้ออนไลน์:"}. {"Online Users","ผู้ใช้ออนไลน์"}. +{"Online","ออนไลน์"}. {"Only deliver notifications to available users","ส่งการแจ้งเตือนถึงผู้ใช้ที่สามารถติดต่อได้เท่านั้น"}. {"Only occupants are allowed to send messages to the conference","ผู้ครอบครองห้องเท่านั้นที่ได้รับอนุญาตให้ส่งข้อความไปยังห้องประชุม"}. {"Only occupants are allowed to send queries to the conference","ผู้ครอบครองห้องเท่านั้นที่ได้รับอนุญาตให้ส่งกระทู้ถามไปยังห้องประชุม"}. {"Only service administrators are allowed to send service messages","ผู้ดูแลด้านการบริการเท่านั้นที่ได้รับอนุญาตให้ส่งข้อความการบริการ"}. -{"Options","ตัวเลือก"}. {"Organization Name","ชื่อองค์กร"}. {"Organization Unit","หน่วยขององค์กร"}. -{"Outgoing s2s Connections:","การเชื่อมต่อ s2s ขาออก:"}. {"Outgoing s2s Connections","การเชื่อมต่อ s2s ขาออก"}. {"Owner privileges required","ต้องมีสิทธิพิเศษของเจ้าของ"}. -{"Packet","แพ็กเก็ต"}. -{"Password:","รหัสผ่าน:"}. -{"Password","รหัสผ่าน"}. {"Password Verification","การตรวจสอบรหัสผ่าน"}. +{"Password","รหัสผ่าน"}. +{"Password:","รหัสผ่าน:"}. {"Path to Dir","พาธไปยัง Dir"}. {"Path to File","พาธของไฟล์ข้อมูล"}. -{"Pending","ค้างอยู่"}. {"Period: ","ระยะเวลา:"}. {"Persist items to storage","ยืนยันรายการที่จะจัดเก็บ"}. {"Ping","Ping"}. {"Pong","Pong"}. -{"Port","พอร์ท"}. {"Present real Jabber IDs to","แสดง Jabber IDs ที่ถูกต้องแก่"}. {"private, ","ส่วนตัว, "}. {"Publish-Subscribe","เผยแพร่-สมัครเข้าใช้งาน"}. @@ -199,41 +151,30 @@ {"Queries to the conference members are not allowed in this room","ห้องนี้ไม่อนุญาตให้ส่งกระทู้ถามถึงสมาชิกในห้องประชุม"}. {"RAM and disc copy","คัดลอก RAM และดิสก์"}. {"RAM copy","คัดลอก RAM"}. -{"Raw","ข้อมูลดิบ"}. {"Really delete message of the day?","แน่ใจว่าต้องการลบข้อความของวันหรือไม่"}. {"Recipient is not in the conference room","ผู้รับไม่ได้อยู่ในห้องประชุม"}. -{"Registered Users:","ผู้ใช้ที่ลงทะเบียน:"}. -{"Registered Users","ผู้ใช้ที่ลงทะเบียน"}. -{"Registration in mod_irc for ","การลงทะเบียนใน mod_irc สำหรับ"}. {"Remote copy","คัดลอกระยะไกล"}. -{"Remove","ลบ"}. {"Remove User","ลบผู้ใช้"}. {"Replaced by new connection","แทนที่ด้วยการเชื่อมต่อใหม่"}. {"Resources","ทรัพยากร"}. -{"Restart","เริ่มต้นใหม่"}. {"Restart Service","เริ่มต้นการบริการใหม่อีกครั้ง"}. -{"Restore","การคืนค่า"}. {"Restore Backup from File at ","คืนค่าการสำรองข้อมูลจากไฟล์ที่"}. {"Restore binary backup after next ejabberd restart (requires less memory):","คืนค่าข้อมูลสำรองแบบไบนารีหลังจากที่ ejabberd ถัดไปเริ่มการทำงานใหม่ (ใช้หน่วยความจำน้อยลง):"}. {"Restore binary backup immediately:","คืนค่าข้อมูลสำรองแบบไบนารีโดยทันที:"}. {"Restore plain text backup immediately:","คืนค่าข้อมูลสำรองที่เป็นข้อความธรรมดาโดยทันที:"}. +{"Restore","การคืนค่า"}. {"Room Configuration","การกำหนดค่าห้องสนทนา"}. {"Room creation is denied by service policy","การสร้างห้องสนทนาถูกปฏิเสธโดยนโยบายการบริการ"}. {"Room title","ชื่อห้อง"}. -{"Roster","บัญชีรายชื่อ"}. -{"Roster of ","บัญชีรายชื่อของ "}. {"Roster size","ขนาดของบัญชีรายชื่อ"}. -{"RPC Call Error","ข้อผิดพลาดจากการเรียกใช้ RPC"}. {"Running Nodes","โหนดที่ทำงาน"}. -{"~s access rule configuration","~s การกำหนดค่ากฎการเข้าถึง"}. {"Saturday","วันเสาร์"}. -{"Script check","ตรวจสอบคริปต์"}. {"Search Results for ","ผลการค้นหาสำหรับ "}. {"Search users in ","ค้นหาผู้ใช้ใน "}. -{"Send announcement to all online users","ส่งประกาศถึงผู้ใช้ออนไลน์ทั้งหมด"}. {"Send announcement to all online users on all hosts","ส่งประกาศถึงผู้ใช้ออนไลน์ทั้งหมดบนโฮสต์ทั้งหมด"}. -{"Send announcement to all users","ส่งประกาศถึงผู้ใช้ทั้งหมด"}. +{"Send announcement to all online users","ส่งประกาศถึงผู้ใช้ออนไลน์ทั้งหมด"}. {"Send announcement to all users on all hosts","ส่งประกาศถึงผู้ใช้ทั้งหมดบนโฮสต์ทั้งหมด"}. +{"Send announcement to all users","ส่งประกาศถึงผู้ใช้ทั้งหมด"}. {"September","กันยายน"}. {"Set message of the day and send to online users","ตั้งค่าข้อความของวันและส่งถึงผู้ใช้ออนไลน์"}. {"Set message of the day on all hosts and send to online users","ตั้งค่าข้อความของวันบนโฮสต์ทั้งหมดและส่งถึงผู้ใช้ออนไลน์"}. @@ -241,52 +182,27 @@ {"Show Integral Table","แสดงตารางรวม"}. {"Show Ordinary Table","แสดงตารางทั่วไป"}. {"Shut Down Service","ปิดการบริการ"}. -{"~s invites you to the room ~s","~s เชิญคุณเข้าร่วมสนทนาในห้อง ~s"}. {"Specify the access model","ระบุโมเดลการเข้าถึง"}. {"Specify the publisher model","ระบุโมเดลผู้เผยแพร่"}. -{"~s's Offline Messages Queue","~s's ลำดับข้อความออฟไลน์"}. -{"Start","เริ่ม"}. -{"Start Modules","เริ่มโมดูล"}. -{"Start Modules at ","เริ่มโมดูลที่"}. -{"Statistics","สถิติ"}. -{"Statistics of ~p","สถิติของ ~p"}. -{"Stop","หยุด"}. -{"Stop Modules","หยุดโมดูล"}. -{"Stop Modules at ","หยุดโมดูลที่"}. {"Stopped Nodes","โหนดที่หยุด"}. -{"Storage Type","ชนิดที่เก็บข้อมูล"}. {"Store binary backup:","จัดเก็บข้อมูลสำรองแบบไบนารี:"}. {"Store plain text backup:","จัดเก็บข้อมูลสำรองที่เป็นข้อความธรรมดา:"}. {"Subject","หัวเรื่อง"}. -{"Submit","ส่ง"}. {"Submitted","ส่งแล้ว"}. {"Subscriber Address","ที่อยู่ของผู้สมัคร"}. -{"Subscription","การสมัครสมาชิก"}. {"Sunday","วันอาทิตย์"}. {"the password is","รหัสผ่านคือ"}. +{"This room is not anonymous","ห้องนี้ไม่ปิดบังชื่อ"}. {"Thursday","วันพฤหัสบดี"}. -{"Time","เวลา"}. {"Time delay","การหน่วงเวลา"}. -{"To","ถึง"}. -{"To ~s","ถึง ~s"}. {"Traffic rate limit is exceeded","อัตราของปริมาณการเข้าใช้เกินขีดจำกัด"}. -{"Transactions Aborted:","ทรานแซกชันที่ถูกยกเลิก:"}. -{"Transactions Committed:","ทรานแซกชันที่ได้รับมอบหมาย:"}. -{"Transactions Logged:","ทรานแซกชันที่บันทึก:"}. -{"Transactions Restarted:","ทรานแซกชันที่เริ่มทำงานใหม่อีกครั้ง:"}. {"Tuesday","วันอังคาร"}. -{"Update","อัพเดต"}. {"Update message of the day (don't send)","อัพเดตข้อความของวัน (ไม่ต้องส่ง)"}. {"Update message of the day on all hosts (don't send)","อัพเดตข้อความของวันบนโฮสต์ทั้งหมด (ไม่ต้องส่ง) "}. -{"Update plan","แผนการอัพเดต"}. -{"Update script","อัพเดตสคริปต์"}. -{"Uptime:","เวลาการทำงานต่อเนื่อง:"}. -{"Use of STARTTLS required","ต้องใช้ STARTTLS"}. -{"User","ผู้ใช้"}. {"User Management","การจัดการผู้ใช้"}. -{"Users","ผู้ใช้"}. {"Users Last Activity","กิจกรรมล่าสุดของผู้ใช้"}. -{"Validate","ตรวจสอบ"}. +{"Users","ผู้ใช้"}. +{"User","ผู้ใช้"}. {"vCard User Search","ค้นหาผู้ใช้ vCard "}. {"Virtual Hosts","โฮสต์เสมือน"}. {"Visitors are not allowed to send messages to all occupants","ผู้เยี่ยมเยือนไม่ได้รับอนุญาตให้ส่งข้อความถึงผู้ครอบครองห้องทั้งหมด"}. @@ -295,7 +211,5 @@ {"Whether to allow subscriptions","อนุญาตให้เข้าร่วมเป็นสมาชิกหรือไม่"}. {"You have been banned from this room","คุณถูกสั่งห้ามไมให้เข้าห้องนี้"}. {"You must fill in field \"Nickname\" in the form","คุณต้องกรอกฟิลด์ \"Nickname\" ในแบบฟอร์ม"}. -{"You need an x:data capable client to configure mod_irc settings","คุณต้องใช้ไคลเอ็นต์ที่รองรับ x:data เพื่อกำหนดการตั้งค่า mod_irc"}. -{"You need an x:data capable client to configure room","คุณต้องใช้ไคลเอ็นต์ที่รองรับ x:data เพื่อกำหนดค่าห้องสนทนา "}. {"You need an x:data capable client to search","คุณต้องใช้ไคลเอ็นต์ที่รองรับ x:data เพื่อค้นหา"}. {"Your contact offline message queue is full. The message has been discarded.","ลำดับข้อความออฟไลน์ของผู้ที่ติดต่อของคุณเต็มแล้ว ข้อความถูกลบทิ้งแล้ว"}. diff --git a/priv/msgs/th.po b/priv/msgs/th.po deleted file mode 100644 index fe3ccd182..000000000 --- a/priv/msgs/th.po +++ /dev/null @@ -1,1945 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: 2.1.0-alpha\n" -"Last-Translator: EQHO Communications (Thailand) Ltd. - http://www.eqho.com\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Thai (ภาษาไทย)\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "ต้องใช้ STARTTLS" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "ไม่ได้ระบุข้อมูล" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "แทนที่ด้วยการเชื่อมต่อใหม่" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "ถูกไล่ออก" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "" - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -#, fuzzy -msgid "Enter the text you see" -msgstr "ป้อนพาธของไฟล์ข้อความ" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "" - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "" - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "ผู้ใช้" - -#: ejabberd_oauth.erl:256 -#, fuzzy -msgid "Server" -msgstr "ไม่เคย" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "รหัสผ่าน" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -#, fuzzy -msgid "ejabberd Web Admin" -msgstr "เว็บอินเทอร์เฟซของ ejabberd" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "การดูแล" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "รายการควบคุมการเข้าถึง" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "ส่งแล้ว" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "รูปแบบที่ไม่ถูกต้อง" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "ส่ง" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "ข้อมูลดิบ" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "ลบข้อความที่เลือก" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "กฎการเข้าถึง" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "~s การกำหนดค่ากฎการเข้าถึง" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "โฮสต์เสมือน" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "ผู้ใช้" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "ผู้ใช้ออนไลน์" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "กิจกรรมล่าสุดของผู้ใช้" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "ระยะเวลา:" - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "เดือนที่แล้ว" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "ปีที่แล้ว" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "กิจกรรมทั้งหมด" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "แสดงตารางทั่วไป" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "แสดงตารางรวม" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "สถิติ" - -#: ejabberd_web_admin.erl:1014 -#, fuzzy -msgid "Not Found" -msgstr "ไม่พบโหนด" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "ไม่พบโหนด" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "เพิ่มผู้ใช้ใหม่" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "โฮสต์" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "ผู้ใช้ที่ลงทะเบียน" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "เพิ่มผู้ใช้" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "ข้อความออฟไลน์" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "กิจกรรมล่าสุด" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "ไม่เคย" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "ออนไลน์" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "ผู้ใช้ที่ลงทะเบียน:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "ผู้ใช้ออนไลน์:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "การเชื่อมต่อ s2s ขาออก:" - -#: ejabberd_web_admin.erl:1559 -#, fuzzy -msgid "Incoming s2s Connections:" -msgstr "การเชื่อมต่อ s2s ขาออก:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "ไม่มี" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "เปลี่ยนรหัสผ่าน" - -#: ejabberd_web_admin.erl:1673 -#, fuzzy -msgid "User ~s" -msgstr "ผู้ใช้" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "ทรัพยากรที่เชื่อมต่อ:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "รหัสผ่าน:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "ลบผู้ใช้" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "ไม่มีข้อมูล" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "โหนด" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "โหนดที่ทำงาน" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "โหนดที่หยุด" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -#, fuzzy -msgid "Node ~p" -msgstr "โหนด " - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "ฐานข้อมูล" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "การสำรองข้อมูล " - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "พอร์ทฟัง" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "อัพเดต" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "เริ่มต้นใหม่" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "หยุด" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "โมดูล" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "ข้อผิดพลาดจากการเรียกใช้ RPC" - -#: ejabberd_web_admin.erl:1917 -#, fuzzy -msgid "Database Tables at ~p" -msgstr "ตารางฐานข้อมูลที่" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "ชื่อ" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "ชนิดที่เก็บข้อมูล" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "หน่วยความจำ" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "" - -#: ejabberd_web_admin.erl:1955 -#, fuzzy -msgid "Backup of ~p" -msgstr "การสำรองข้อมูล" - -#: ejabberd_web_admin.erl:1959 -#, fuzzy -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"โปรดทราบว่าตัวเลือกเหล่านี้จะสำรองข้อมูลในฐานข้อมูล builtin Mnesia เท่านั้น หากคุณใช้โมดูล " -"ODBC คุณต้องสำรองข้อมูลของฐานข้อมูล SQL แยกต่างหากด้วย" - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "จัดเก็บข้อมูลสำรองแบบไบนารี:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "ตกลง" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "คืนค่าข้อมูลสำรองแบบไบนารีโดยทันที:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"คืนค่าข้อมูลสำรองแบบไบนารีหลังจากที่ ejabberd ถัดไปเริ่มการทำงานใหม่ (ใช้หน่วยความจำน้อยลง):" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "จัดเก็บข้อมูลสำรองที่เป็นข้อความธรรมดา:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "คืนค่าข้อมูลสำรองที่เป็นข้อความธรรมดาโดยทันที:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "" - -#: ejabberd_web_admin.erl:2076 -#, fuzzy -msgid "Import user data from jabberd14 spool file:" -msgstr "อิมพอร์ตผู้ใช้จากไฟล์เก็บพักข้อมูล jabberd14" - -#: ejabberd_web_admin.erl:2087 -#, fuzzy -msgid "Import users data from jabberd14 spool directory:" -msgstr "อิมพอร์ตผู้ใช้จากไฟล์เก็บพักข้อมูล jabberd14" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "พอร์ทฟังที่" - -#: ejabberd_web_admin.erl:2144 -#, fuzzy -msgid "Modules at ~p" -msgstr "โมดูลที่ " - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "สถิติของ ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "เวลาการทำงานต่อเนื่อง:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "เวลาการทำงานของ CPU:" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "ทรานแซกชันที่ได้รับมอบหมาย:" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "ทรานแซกชันที่ถูกยกเลิก:" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "ทรานแซกชันที่เริ่มทำงานใหม่อีกครั้ง:" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "ทรานแซกชันที่บันทึก:" - -#: ejabberd_web_admin.erl:2243 -#, fuzzy -msgid "Update ~p" -msgstr "อัพเดต " - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "แผนการอัพเดต" - -#: ejabberd_web_admin.erl:2255 -#, fuzzy -msgid "Modified modules" -msgstr "โมดูลที่อัพเดต" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "อัพเดตสคริปต์" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "อัพเดตสคริปต์ระดับต่ำ" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "ตรวจสอบคริปต์" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "พอร์ท" - -#: ejabberd_web_admin.erl:2439 -#, fuzzy -msgid "Protocol" -msgstr "พอร์ท" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "โมดูล" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "ตัวเลือก" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "เริ่ม" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "คำสั่ง" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "แน่ใจว่าต้องการลบข้อความของวันหรือไม่" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "หัวเรื่อง" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "เนื้อหาของข้อความ" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "ไม่ได้ป้อนเนื้อหาสำหรับข้อความที่ประกาศ" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "ประกาศ" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "ส่งประกาศถึงผู้ใช้ทั้งหมด" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "ส่งประกาศถึงผู้ใช้ทั้งหมดบนโฮสต์ทั้งหมด" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "ส่งประกาศถึงผู้ใช้ออนไลน์ทั้งหมด" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "ส่งประกาศถึงผู้ใช้ออนไลน์ทั้งหมดบนโฮสต์ทั้งหมด" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "ตั้งค่าข้อความของวันและส่งถึงผู้ใช้ออนไลน์" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "ตั้งค่าข้อความของวันบนโฮสต์ทั้งหมดและส่งถึงผู้ใช้ออนไลน์" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "อัพเดตข้อความของวัน (ไม่ต้องส่ง)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "อัพเดตข้อความของวันบนโฮสต์ทั้งหมด (ไม่ต้องส่ง) " - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "ลบข้อความของวัน" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "ลบข้อความของวันบนโฮสต์ทั้งหมด" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "การกำหนดค่า" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "เริ่มโมดูล" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "หยุดโมดูล" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "การคืนค่า" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "ถ่ายโอนข้อมูลไปยังไฟล์ข้อความ" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "อิมพอร์ตไฟล์" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "อิมพอร์ตไดเร็กทอรี" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "เริ่มต้นการบริการใหม่อีกครั้ง" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "ปิดการบริการ" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "ลบผู้ใช้" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "สิ้นสุดเซสชันของผู้ใช้" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "ขอรับรหัสผ่านของผู้ใช้" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "เปลี่ยนรหัสผ่านของผู้ใช้" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "แสดงเวลาเข้าสู่ระบบครั้งล่าสุดของผู้ใช้" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "แสดงสถิติของผู้ใช้" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "แสดงจำนวนผู้ใช้ที่ลงทะเบียน" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "แสดงจำนวนผู้ใช้ออนไลน์" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "การจัดการผู้ใช้" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "ผู้ใช้ทั้งหมด" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "การเชื่อมต่อ s2s ขาออก" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "การจัดการข้อมูลสำรอง" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "อิมพอร์ตผู้ใช้จากไฟล์เก็บพักข้อมูล jabberd14" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "ถึง ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "จาก ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "การกำหนดค่าตารางฐานข้อมูลที่" - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "เลือกชนิดการจัดเก็บของตาราง" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "คัดลอกเฉพาะดิสก์" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "คัดลอก RAM และดิสก์" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "คัดลอก RAM" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "คัดลอกระยะไกล" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "หยุดโมดูลที่" - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "เลือกโมดูลเพื่อหยุดการทำงาน" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "เริ่มโมดูลที่" - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "ป้อนรายการของ {โมดูล, [ตัวเลือก]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "รายการของโมดูลที่จะเริ่มการทำงาน" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "สำรองไฟล์ข้อมูลที่" - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "ป้อนพาธเพื่อสำรองไฟล์ข้อมูล" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "พาธของไฟล์ข้อมูล" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "คืนค่าการสำรองข้อมูลจากไฟล์ที่" - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "ถ่ายโอนการสำรองข้อมูลไปยังไฟล์ข้อความที่" - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "ป้อนพาธของไฟล์ข้อความ" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "อิมพอร์ตผู้ใช้จากไฟล์ที่" - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "ป้อนพาธไปยังไฟล์เก็บพักข้อมูล jabberd14" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "อิมพอร์ตผู้ใช้จาก Dir ที่" - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "ป้อนพาธไปยัง jabberd14 spool dir" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "พาธไปยัง Dir" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "การหน่วงเวลา" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "การกำหนดค่ารายการควบคุมการเข้าถึง" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "รายการควบคุมการเข้าถึง" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "การกำหนดค่าการเข้าถึง" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "กฎการเข้าถึง" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "การตรวจสอบรหัสผ่าน" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "จำนวนผู้ใช้ที่ลงทะเบียน" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "จำนวนผู้ใช้ออนไลน์" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "การเข้าสู่ระบบครั้งล่าสุด" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "ขนาดของบัญชีรายชื่อ" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "ที่อยู่ IP" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "ทรัพยากร" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "การดูแล " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "การดำเนินการกับผู้ใช้" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "แก้ไขคุณสมบัติ" - -#: mod_fail2ban.erl:95 -msgid "" -"Too many (~p) failed authentications from this IP address (~s). The address " -"will be unblocked at ~s UTC" -msgstr "" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "" - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "" - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "การเข้าถึงถูกปฏิเสธโดยนโยบายการบริการ" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "การส่ง IRC" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "ejabberd IRC module" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "คุณต้องใช้ไคลเอ็นต์ที่รองรับ x:data เพื่อกำหนดการตั้งค่า mod_irc" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "การลงทะเบียนใน mod_irc สำหรับ" - -#: mod_irc.erl:659 -#, fuzzy -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "ป้อนชื่อผู้ใช้และการเข้ารหัสที่คุณต้องการใช้สำหรับเชื่อมต่อกับเซิร์ฟเวอร์ IRC" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "ชื่อผู้ใช้ IRC" - -#: mod_irc.erl:682 -#, fuzzy -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"ถ้าคุณต้องการระบุการเข้ารหัสที่ต่างกันสำหรับเซิร์ฟเวอร์ IRC ให้กรอกค่าโดยใช้รูปแบบ '{\"irc " -"server\", \"encoding\"}' ลงในรายการ การบริการนี้ใช้การเข้ารหัสในรูปแบบ \"~s\" " -"โดยค่าดีฟอลต์ " - -#: mod_irc.erl:704 -#, fuzzy -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"ตัวอย่าง: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", " -"\"iso8859-1\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "" - -#: mod_irc.erl:903 -#, fuzzy -msgid "IRC server" -msgstr "ชื่อผู้ใช้ IRC" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "" - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "" - -#: mod_irc.erl:1051 -#, fuzzy -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "ป้อนชื่อผู้ใช้และการเข้ารหัสที่คุณต้องการใช้สำหรับเชื่อมต่อกับเซิร์ฟเวอร์ IRC" - -#: mod_irc.erl:1060 -#, fuzzy -msgid "IRC username" -msgstr "ชื่อผู้ใช้ IRC" - -#: mod_irc.erl:1126 -#, fuzzy -msgid "Password ~b" -msgstr "รหัสผ่าน" - -#: mod_irc.erl:1137 -#, fuzzy -msgid "Port ~b" -msgstr "พอร์ท" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "" - -#: mod_mam.erl:541 -#, fuzzy -msgid "Only members may query archives of this room" -msgstr "ผู้ดูแลการสนทนาเท่านั้นที่ได้รับอนุญาตให้เปลี่ยนหัวข้อในห้องนี้" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "ผู้ดูแลด้านการบริการเท่านั้นที่ได้รับอนุญาตให้ส่งข้อความการบริการ" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "การสร้างห้องสนทนาถูกปฏิเสธโดยนโยบายการบริการ" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "ไม่มีห้องประชุม" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "ห้องสนทนา" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "" - -#: mod_muc.erl:933 -#, fuzzy -msgid "You need a client that supports x:data to register the nickname" -msgstr "คุณต้องใช้ไคลเอ็นต์ที่รองรับ x:data เพื่อลงทะเบียนชื่อเล่น" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "การลงทะเบียนชื่อเล่นที่ " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "ป้อนชื่อเล่นที่คุณต้องการลงทะเบียน" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "ชื่อเล่น" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -#, fuzzy -msgid "That nickname is registered by another person" -msgstr "ชื่อเล่นถูกลงทะเบียนใช้งานโดยบุคคลอื่น" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "คุณต้องกรอกฟิลด์ \"Nickname\" ในแบบฟอร์ม" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "ejabberd MUC module" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "" - -#: mod_muc_admin.erl:249 -#, fuzzy -msgid "Total rooms" -msgstr "ห้องสนทนา" - -#: mod_muc_admin.erl:250 -#, fuzzy -msgid "Permanent rooms" -msgstr "ออกจากห้อง" - -#: mod_muc_admin.erl:251 -#, fuzzy -msgid "Registered nicknames" -msgstr "ผู้ใช้ที่ลงทะเบียน" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "มีการปรับเปลี่ยนการกำหนดค่าของห้องสนทนา" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "เข้าห้องสนทนานี้" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "ออกจากห้อง" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "ถูกสั่งห้าม" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "ซึ่งรู้จักกันในชื่อ" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " ตั้งหัวข้อว่า: " - -#: mod_muc_log.erl:493 -#, fuzzy -msgid "Chatroom is created" -msgstr "ห้องสนทนา" - -#: mod_muc_log.erl:495 -#, fuzzy -msgid "Chatroom is destroyed" -msgstr "ห้องสนทนา" - -#: mod_muc_log.erl:497 -#, fuzzy -msgid "Chatroom is started" -msgstr "ห้องสนทนา" - -#: mod_muc_log.erl:499 -#, fuzzy -msgid "Chatroom is stopped" -msgstr "ห้องสนทนา" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "วันจันทร์" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "วันอังคาร" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "วันพุธ" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "วันพฤหัสบดี" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "วันศุกร์" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "วันเสาร์" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "วันอาทิตย์" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "มกราคม" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "กุมภาพันธ์" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "มีนาคม" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "เมษายน" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "พฤษภาคม" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "มิถุนายน" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "กรกฎาคม" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "สิงหาคม" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "กันยายน" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "ตุลาคม" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "พฤศจิกายน" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "ธันวาคม" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "การกำหนดค่าห้องสนทนา" - -#: mod_muc_log.erl:932 -#, fuzzy -msgid "Room Occupants" -msgstr "จำนวนผู้ครอบครองห้อง" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "อัตราของปริมาณการเข้าใช้เกินขีดจำกัด" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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 "" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "ไม่อนุญาตให้ส่งข้อความส่วนตัวไปยังห้องประชุม" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "" - -#: mod_muc_room.erl:377 -#, fuzzy -msgid "Only moderators can approve voice requests" -msgstr "อนุญาตให้ผู้ใช้ส่งคำเชิญถึงกันได้" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "ประเภทข้อความไม่เหมาะสม" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "ไม่อนุญาตให้ส่งข้อความส่วนตัวไปยัง \"กลุ่มสนทนา\"" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "ผู้รับไม่ได้อยู่ในห้องประชุม" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -#, fuzzy -msgid "It is not allowed to send private messages" -msgstr "ไม่อนุญาตให้ส่งข้อความส่วนตัวไปยังห้องประชุม" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "ผู้ครอบครองห้องเท่านั้นที่ได้รับอนุญาตให้ส่งข้อความไปยังห้องประชุม" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "ผู้ครอบครองห้องเท่านั้นที่ได้รับอนุญาตให้ส่งกระทู้ถามไปยังห้องประชุม" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "ห้องนี้ไม่อนุญาตให้ส่งกระทู้ถามถึงสมาชิกในห้องประชุม" - -#: mod_muc_room.erl:961 -#, fuzzy -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "ผู้ดูแลการสนทนาและผู้เข้าร่วมเท่านั้นที่ได้รับอนุญาตให้เปลี่ยนหัวข้อในห้องนี้" - -#: mod_muc_room.erl:966 -#, fuzzy -msgid "Only moderators are allowed to change the subject in this room" -msgstr "ผู้ดูแลการสนทนาเท่านั้นที่ได้รับอนุญาตให้เปลี่ยนหัวข้อในห้องนี้" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "ผู้เยี่ยมเยือนไม่ได้รับอนุญาตให้ส่งข้อความถึงผู้ครอบครองห้องทั้งหมด" - -#: mod_muc_room.erl:1080 -#, fuzzy -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "ผู้ดูแลการสนทนาเท่านั้นที่ได้รับอนุญาตให้เปลี่ยนหัวข้อในห้องนี้" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -#, fuzzy -msgid "That nickname is already in use by another occupant" -msgstr "ชื่อเล่นถูกใช้งานอยู่โดยผู้ครอบครองห้อง" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "คุณถูกสั่งห้ามไมให้เข้าห้องนี้" - -#: mod_muc_room.erl:1826 -#, fuzzy -msgid "Membership is required to enter this room" -msgstr "ต้องเป็นสมาชิกจึงจะสามารถเข้าห้องนี้ได้" - -#: mod_muc_room.erl:1872 -#, fuzzy -msgid "A password is required to enter this room" -msgstr "ต้องใส่รหัสผ่านเพื่อเข้าห้องนี้" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "รหัสผ่านไม่ถูกต้อง" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "ต้องมีสิทธิพิเศษของผู้ดูแลระบบ" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "ต้องมีสิทธิพิเศษของผู้ดูแลการสนทนา" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "Jabber ID ~s ไม่ถูกต้อง" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "ไม่มีชื่อเล่น ~s อยู่ในห้องนี้" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "การเข้าร่วมที่ไม่ถูกต้อง: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "บทบาทไม่ถูกต้อง: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "ต้องมีสิทธิพิเศษของเจ้าของ" - -#: mod_muc_room.erl:3348 -#, fuzzy -msgid "Configuration of room ~s" -msgstr "การกำหนดค่าสำหรับ " - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "ชื่อห้อง" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -#, fuzzy -msgid "Room description" -msgstr "รายละเอียด:" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "สร้างเป็นห้องถาวร" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "สร้างเป็นห้องที่บุคคลทั่วไปสามารถค้นหาได้" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "สร้างรายการผู้เข้าร่วมสำหรับใช้งานโดยบุคคลทั่วไป" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "สร้างห้องที่มีการป้องกันด้วยรหัสผ่าน" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "จำนวนผู้ครอบครองห้องสูงสุด" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "ไม่จำกัด" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "แสดง Jabber IDs ที่ถูกต้องแก่" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "สำหรับผู้ดูแลการสนทนาเท่านั้น" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "ทุกคน" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "" - -#: mod_muc_room.erl:3486 -#, fuzzy -msgid "Moderator" -msgstr "สำหรับผู้ดูแลการสนทนาเท่านั้น" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "สร้างห้องสำหรับสมาชิกเท่านั้น" - -#: mod_muc_room.erl:3516 -#, fuzzy -msgid "Make room moderated" -msgstr "สร้างเป็นห้องถาวร" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "ผู้ใช้เริ่มต้นเป็นผู้เข้าร่วม" - -#: mod_muc_room.erl:3522 -#, fuzzy -msgid "Allow users to change the subject" -msgstr "อนุญาตให้ผู้ใช้เปลี่ยนหัวข้อได้" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "อนุญาตให้ผู้ใช้ส่งข้อความส่วนตัว" - -#: mod_muc_room.erl:3533 -#, fuzzy -msgid "Allow visitors to send private messages to" -msgstr "อนุญาตให้ผู้ใช้ส่งข้อความส่วนตัว" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "อนุญาตให้ผู้ใช้ถามคำถามกับผู้ใช้คนอื่นๆ ได้" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "อนุญาตให้ผู้ใช้ส่งคำเชิญถึงกันได้" - -#: mod_muc_room.erl:3582 -#, fuzzy -msgid "Allow visitors to send status text in presence updates" -msgstr "อนุญาตให้ผู้ใช้ส่งข้อความส่วนตัว" - -#: mod_muc_room.erl:3586 -#, fuzzy -msgid "Allow visitors to change nickname" -msgstr "อนุญาตให้ผู้ใช้เปลี่ยนหัวข้อได้" - -#: mod_muc_room.erl:3589 -#, fuzzy -msgid "Allow visitors to send voice requests" -msgstr "อนุญาตให้ผู้ใช้ส่งคำเชิญถึงกันได้" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "" - -#: mod_muc_room.erl:3599 -#, fuzzy -msgid "Make room CAPTCHA protected" -msgstr "สร้างห้องที่มีการป้องกันด้วยรหัสผ่าน" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "เปิดใช้งานการบันทึก" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "คุณต้องใช้ไคลเอ็นต์ที่รองรับ x:data เพื่อกำหนดค่าห้องสนทนา " - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "จำนวนผู้ครอบครองห้อง" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "ส่วนตัว, " - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "" - -#: mod_muc_room.erl:4351 -#, fuzzy -msgid "User JID" -msgstr "ผู้ใช้" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s เชิญคุณเข้าร่วมสนทนาในห้อง ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "รหัสผ่านคือ" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "ลำดับข้อความออฟไลน์ของผู้ที่ติดต่อของคุณเต็มแล้ว ข้อความถูกลบทิ้งแล้ว" - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "~s's ลำดับข้อความออฟไลน์" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "เวลา" - -#: mod_offline.erl:812 -msgid "From" -msgstr "จาก" - -#: mod_offline.erl:813 -msgid "To" -msgstr "ถึง" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "แพ็กเก็ต" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "ข้อความออฟไลน์:" - -#: mod_offline.erl:996 -#, fuzzy -msgid "Remove All Offline Messages" -msgstr "ข้อความออฟไลน์" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "ejabberd SOCKS5 Bytestreams module" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "เผยแพร่-สมัครเข้าใช้งาน" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "ejabberd Publish-Subscribe module" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "คำร้องขอของผู้สมัครเข้าใช้งาน PubSub" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "เลือกว่าจะอนุมัติการสมัครเข้าใช้งานของเอนทิตี้นี้หรือไม่" - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "ID โหนด" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "ที่อยู่ของผู้สมัคร" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "อนุญาตให้ Jabber ID นี้เข้าร่วมเป็นสมาชิกของโหนด pubsub หรือไม่" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "ส่งส่วนของข้อมูล (payload) พร้อมกับการแจ้งเตือนเหตุการณ์" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "ส่งการแจ้งเตือนเหตุการณ์" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "แจ้งเตือนผู้สมัครสมาชิกเมื่อการกำหนดค่าโหนดเปลี่ยนแปลง" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "แจ้งเตือนผู้สมัครสมาชิกเมื่อโหนดถูกลบ" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "แจ้งเตือนผู้สมัครสมาชิกเมื่อรายการถูกลบออกจากโหนด" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "ยืนยันรายการที่จะจัดเก็บ" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "จำนวนสูงสุดของรายการที่ยืนยัน" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "อนุญาตให้เข้าร่วมเป็นสมาชิกหรือไม่" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "ระบุโมเดลการเข้าถึง" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "ระบุโมเดลผู้เผยแพร่" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "" - -#: mod_pubsub.erl:3771 -#, fuzzy -msgid "Specify the event message type" -msgstr "ระบุโมเดลการเข้าถึง" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "ขนาดสูงสุดของส่วนของข้อมูล (payload) มีหน่วยเป็นไบต์" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "เวลาที่ส่งรายการที่เผยแพร่ครั้งล่าสุด" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "ส่งการแจ้งเตือนถึงผู้ใช้ที่สามารถติดต่อได้เท่านั้น" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "" - -#: mod_register.erl:253 -#, fuzzy -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "คุณต้องใช้ไคลเอ็นต์ที่รองรับ x:data เพื่อลงทะเบียนชื่อเล่น" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "เลือกชื่อผู้ใช้และรหัสผ่านเพื่อลงทะเบียนกับเซิร์ฟเวอร์นี้" - -#: mod_register.erl:373 mod_register.erl:421 -#, fuzzy -msgid "The password is too weak" -msgstr "รหัสผ่านคือ" - -#: mod_register.erl:426 -#, fuzzy -msgid "Users are not allowed to register accounts so quickly" -msgstr "ผู้เยี่ยมเยือนไม่ได้รับอนุญาตให้ส่งข้อความถึงผู้ครอบครองห้องทั้งหมด" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "" - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "" - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "" - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "" - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "" - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "" - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -#, fuzzy -msgid "Username:" -msgstr "ชื่อผู้ใช้ IRC" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -#, fuzzy -msgid "Server:" -msgstr "ไม่เคย" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "" - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" - -#: mod_register_web.erl:262 mod_register_web.erl:374 -#, fuzzy -msgid "Password Verification:" -msgstr "การตรวจสอบรหัสผ่าน" - -#: mod_register_web.erl:269 -#, fuzzy -msgid "Register" -msgstr "บัญชีรายชื่อ" - -#: mod_register_web.erl:366 -#, fuzzy -msgid "Old Password:" -msgstr "รหัสผ่าน:" - -#: mod_register_web.erl:370 -#, fuzzy -msgid "New Password:" -msgstr "รหัสผ่าน:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "" - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "การสมัครสมาชิก" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "ค้างอยู่" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "กลุ่ม" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "ตรวจสอบ" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "ลบ" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "บัญชีรายชื่อของ " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "เพิ่ม Jabber ID" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "บัญชีรายชื่อ" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "กลุ่มบัญชีรายชื่อที่ใช้งานร่วมกัน" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "ชื่อ:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "รายละเอียด:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "สมาชิก:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "กลุ่มที่แสดง:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "กลุ่ม" - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Erlang Jabber Server" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "วันเกิด" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "เมือง" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "ประเทศ" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "อีเมล" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "นามสกุล" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"กรอกข้อมูลในแบบฟอร์มเพื่อค้นหาผู้ใช้ Jabber ที่ตรงกัน (ใส่เครื่องหมาย * " -"ที่ท้ายสุดของฟิลด์เพื่อจับคู่กับสตริงย่อย)" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "ชื่อเต็ม" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "ชื่อกลาง" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "ชื่อองค์กร" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "หน่วยขององค์กร" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "ค้นหาผู้ใช้ใน " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "คุณต้องใช้ไคลเอ็นต์ที่รองรับ x:data เพื่อค้นหา" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "ค้นหาผู้ใช้ vCard " - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "ejabberd vCard module" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "ผลการค้นหาสำหรับ " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "กรอกข้อมูลลงในฟิลด์เพื่อค้นหาผู้ใช้ Jabber ที่ตรงกัน" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "เซิร์ฟเวอร์ s2s ขาออก:" - -#~ msgid "Delete" -#~ msgstr "ลบ" - -#~ msgid "This room is not anonymous" -#~ msgstr "ห้องนี้ไม่ปิดบังชื่อ" - -#~ msgid "Encodings" -#~ msgstr "การเข้ารหัส" - -#~ msgid "(Raw)" -#~ msgstr "(ข้อมูลดิบ)" - -#~ msgid "Specified nickname is already registered" -#~ msgstr "ชื่อเล่นที่ระบุได้รับการลงได้ทะเบียนแล้ว" - -#~ msgid "Size" -#~ msgstr "ขนาด" - -#~ msgid "Roster groups that may subscribe (if access model is roster)" -#~ msgstr "กลุ่มบัญชีรายชื่อที่อาจจะสมัครเป็นสมาชิก (ถ้าโมเดลการเข้าถึงคือบัญชีรายชื่อ)" diff --git a/priv/msgs/tr.msg b/priv/msgs/tr.msg index 356d245e3..af108d514 100644 --- a/priv/msgs/tr.msg +++ b/priv/msgs/tr.msg @@ -1,20 +1,19 @@ -%% -*- coding: latin-1 -*- -{"Access Configuration","Erişim Ayarları"}. -{"Access Control List Configuration","Erişim Kontrol Listelerinin Ayarlanması (ACL)"}. -{"Access control lists","Erişim kontrol listeleri (ACL)"}. -{"Access Control Lists","Erişim Kontrol Listeleri (ACL)"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" has set the subject to: "," konuyu değiştirdi: "}. +{"A friendly name for the node","Düğüm için dostane bir isim"}. +{"A password is required to enter this room","Bu odaya girmek için parola gerekiyor"}. {"Access denied by service policy","Servis politikası gereği erişim engellendi"}. -{"Access rules","Erişim kuralları"}. -{"Access Rules","Erişim Kuralları"}. {"Action on user","Kullanıcıya uygulanacak eylem"}. -{"Add Jabber ID","Jabber ID'si Ekle"}. -{"Add New","Yeni Ekle"}. {"Add User","Kullanıcı Ekle"}. {"Administration of ","Yönetim : "}. {"Administration","Yönetim"}. {"Administrator privileges required","Yönetim yetkileri gerekli"}. -{"A friendly name for the node","Düğüm için dostane bir isim"}. {"All activity","Tüm aktivite"}. +{"All Users","Tüm Kullanıcılar"}. {"Allow this Jabber ID to subscribe to this pubsub node?","Bu Jabber ID bu pubsub düğümüne üye olmasına izin verilsin mi?"}. {"Allow users to change the subject","Kullanıcıların konu değiştirmesine izin ver"}. {"Allow users to query other users","Kullanıcıların diğer kullanıcıları sorgulamalarına izin ver"}. @@ -24,10 +23,7 @@ {"Allow visitors to send private messages to","Ziyaretçilerin özel mesaj göndermelerine izin ver"}. {"Allow visitors to send status text in presence updates","Ziyaretçilerin varlık (presence) güncellemelerinde durum metni göndermelerine izin ver"}. {"Allow visitors to send voice requests","Ziyaretçilerin ses isteğine göndermelerine izin ver"}. -{"All Users","Tüm Kullanıcılar"}. {"Announcements","Duyurular"}. -{"anyone","herkes"}. -{"A password is required to enter this room","Bu odaya girmek için parola gerekiyor"}. {"April","Nisan"}. {"August","Ağustos"}. {"Backup Management","Yedek Yönetimi"}. @@ -46,88 +42,61 @@ {"Chatroom is stopped","Sohbet odası durduruldu"}. {"Chatrooms","Sohbet Odaları"}. {"Choose a username and password to register with this server","Bu sunucuya kayıt olmak için bir kullanıcı ismi ve parola seçiniz"}. -{"Choose modules to stop","Durdurulacak modülleri seçiniz"}. {"Choose storage type of tables","Tabloların veri depolama tipini seçiniz"}. {"Choose whether to approve this entity's subscription.","Bu varlığın üyeliğini onaylayıp onaylamamayı seçiniz."}. {"City","İl"}. {"Commands","Komutlar"}. {"Conference room does not exist","Konferans odası bulunamadı"}. -{"Configuration","Ayarlar"}. {"Configuration of room ~s","~s odasının ayarları"}. -{"Connected Resources:","Bağlı Kaynaklar:"}. -{"Connections parameters","Bağlantı parametreleri"}. +{"Configuration","Ayarlar"}. {"Country","Ülke"}. -{"CPU Time:","İşlemci Zamanı:"}. {"Database Tables Configuration at ","Veritabanı Tablo Ayarları : "}. {"Database","Veritabanı"}. {"December","Aralık"}. {"Default users as participants","Kullanıcılar öntanımlı olarak katılımcı olsun"}. -{"Delete message of the day","Günün mesajını sil"}. {"Delete message of the day on all hosts","Tüm sunuculardaki günün mesajını sil"}. -{"Delete Selected","Seçilenleri Sil"}. +{"Delete message of the day","Günün mesajını sil"}. {"Delete User","Kullanıcıyı Sil"}. {"Deliver event notifications","Olay uyarıları gönderilsin"}. {"Deliver payloads with event notifications","Yükleri (payload) olay uyarıları ile beraber gönder"}. -{"Description:","Tanım:"}. {"Disc only copy","Sadece disk kopyala"}. -{"Displayed Groups:","Gösterilen Gruplar:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","Parolanızı kimseye söylemeyin, Jabber sunucusunun yöneticilerine bile."}. {"Dump Backup to Text File at ","Metin Dosyasına Döküm Alarak Yedekle : "}. {"Dump to Text File","Metin Dosyasına Döküm Al"}. {"Edit Properties","Özellikleri Düzenle"}. {"Either approve or decline the voice request.","Ses isteğini kabul edin ya da reddedin"}. -{"ejabberd IRC module","ejabberd IRC modülü"}. {"ejabberd MUC module","ejabberd MUC modülü"}. {"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe modülü"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams modülü"}. {"ejabberd vCard module","ejabberd vCard modülü"}. {"ejabberd Web Admin","ejabberd Web Yöneticisi"}. -{"Elements","Elementler"}. {"Email","E-posta"}. {"Enable logging","Kayıt tutma özelliğini aç"}. -{"Encoding for server ~b","Sunucu için kodlama ~b"}. {"End User Session","Kullanıcı Oturumunu Kapat"}. -{"Enter list of {Module, [Options]}","{Module, [Options]} listesi giriniz"}. {"Enter nickname you want to register","Kaydettirmek istediğiniz takma ismi giriniz"}. {"Enter path to backup file","Yedek dosyasının yolunu giriniz"}. {"Enter path to jabberd14 spool dir","jabberd14 spool dosyası için yol giriniz"}. {"Enter path to jabberd14 spool file","jabberd14 spool dosyası için yol giriniz"}. {"Enter path to text file","Metin dosyasının yolunu giriniz"}. {"Enter the text you see","Gördüğünüz metni giriniz"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","IRC sunuculara bağlanmak için kullanmak istediğiniz kullanıcı isimleri ve kodlamaları giriniz. 'İleri' tuşuna basınca karşınıza dolduracak daha fazla alan çıkacak. 'Tamamla' tuşuna basarak ayarları kaydedin."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","IRC sunuculara bağlanmak için kullanmak istediğiniz kullanıcı ismi, kodlamalar, kapılar (portlar) ve parolaları giriniz"}. -{"Erlang Jabber Server","Erlang Jabber Sunucusu"}. -{"Error","Hata"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Örnek: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"gizli\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}], {\"irc.sometestserver.net\", \"utf-8\"}]"}. {"Exclude Jabber IDs from CAPTCHA challenge","CAPTCHA doğrulamasını şu Jabber ID'ler için yapma"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Sunucudaki tüm kullanıcıların verisini PIEFXIS dosyalarına (XEP-0227) dışa aktar:"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Bir sunucudaki kullanıcıların verisini PIEFXIS dosyalarına (XEP-0227) dışa aktar:"}. {"Failed to extract JID from your voice request approval","Ses isteği onayınızdan JID bilginize ulaşılamadı"}. {"Family Name","Soyisim"}. {"February","Şubat"}. -{"Fill in fields to search for any matching Jabber User","Eşleşen jabber kullanıcılarını aramak için alanları doldurunuz"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Eşleşen jabber kullanıcılarını aramak için formu doldurunuz (Alt dizgi eşlemek için alanın sonuna * ekleyin)"}. {"Friday","Cuma"}. -{"From","Kimden"}. -{"From ~s","Kimden ~s"}. {"Full Name","Tam İsim"}. {"Get Number of Online Users","Bağlı Kullanıcı Sayısını Al"}. {"Get Number of Registered Users","Kayıtlı Kullanıcı Sayısını Al"}. {"Get User Last Login Time","Kullanıcı Son Giriş Zamanınlarını Al"}. -{"Get User Password","Kullanıcı Parolasını Al"}. {"Get User Statistics","Kullanıcı İstatistiklerini Al"}. {"Grant voice to this person?","Bu kişiye ses verelim mi?"}. -{"Group ","Group "}. -{"Groups","Gruplar"}. {"has been banned","odaya girmesi yasaklandı"}. -{"has been kicked because of an affiliation change","ilişki değişikliğinden dolayı atıldı"}. {"has been kicked because of a system shutdown","sistem kapandığından dolayı atıldı"}. +{"has been kicked because of an affiliation change","ilişki değişikliğinden dolayı atıldı"}. {"has been kicked because the room has been changed to members-only","oda üyelere-özel hale getirildiğinden dolayı atıldı"}. {"has been kicked","odadan atıldı"}. -{" has set the subject to: "," konuyu değiştirdi: "}. -{"Host","Sunucu"}. {"If you don't see the CAPTCHA image here, visit the web page.","Eğer burada CAPTCHA resmini göremiyorsanız, web sayfasını ziyaret edin."}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","IRC sunucuları için farklı kapılar (portlar), parolalar, kodlamalar belirtmek istiyorsanız, '{\"irc sunucusu\", \"kodlama\",\"kapı\",\"parola\"}' biçeminde değerlerle bu listeyi doldurunuz. Öntanımlı olarak bu servis \"~s\" kodlamasını, ~p \"kapısını\", \"boş\" parolasını kullanıyor."}. {"Import Directory","Dizini İçe Aktar"}. {"Import File","Dosyayı İçe Aktar"}. {"Import user data from jabberd14 spool file:","Jabberd 1.4 Spool Dosyalarından Kullanıcıları İçe Aktar:"}. @@ -138,28 +107,13 @@ {"Import Users From jabberd14 Spool Files","Jabberd 1.4 Spool Dosyalarından Kullanıcıları İçeri Aktar"}. {"Improper message type","Uygunsuz mesaj tipi"}. {"Incorrect password","Yanlış parola"}. -{"Invalid affiliation: ~s","Geçersiz ilişki: ~s"}. -{"Invalid role: ~s","Geçersiz rol: ~s"}. {"IP addresses","IP adresleri"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","IRC kanalı (ilk # işaretini koymayın)"}. -{"IRC server","IRC sunucusu"}. -{"IRC settings","IRC ayarları"}. -{"IRC Transport","IRC Nakli (Transport)"}. -{"IRC username","IRC kullanıcı ismi"}. -{"IRC Username","IRC Kullanıcı İsmi"}. {"is now known as","isim değiştirdi :"}. {"It is not allowed to send private messages of type \"groupchat\"","\"groupchat\" tipinde özel mesajlar gönderilmesine izin verilmiyor"}. -{"It is not allowed to send private messages","Özel mesaj gönderilmesine izin verilmiyor"}. {"It is not allowed to send private messages to the conference","Konferansa özel mesajlar gönderilmesine izin verilmiyor"}. -{"Jabber Account Registration","Jabber Hesap Kaydı"}. {"Jabber ID","Jabber ID"}. -{"Jabber ID ~s is invalid","Jabber ID ~s geçersiz"}. {"January","Ocak"}. -{"Join IRC channel","IRC kanalına katıl"}. {"joins the room","odaya katıldı"}. -{"Join the IRC channel here.","Buradaki IRC kanalına katıl."}. -{"Join the IRC channel in this Jabber ID: ~s","IRC kanalına bu Jabber ID'si ile katıl: ~s"}. {"July","Temmuz"}. {"June","Haziran"}. {"Last Activity","Son Aktivite"}. @@ -167,10 +121,6 @@ {"Last month","Geçen ay"}. {"Last year","Geçen yıl"}. {"leaves the room","odadan ayrıldı"}. -{"Listened Ports at ","Dinlenen Kapılar (Portlar) : "}. -{"Listened Ports","Dinlenen Kapılar (Portlar)"}. -{"List of modules to start","Başlatılacak modüllerin listesi"}. -{"Low level update script","Düşük seviye güncelleme betiği"}. {"Make participants list public","Katılımcı listesini herkese açık hale getir"}. {"Make room CAPTCHA protected","Odayı insan doğrulaması (captcha) korumalı hale getir"}. {"Make room members-only","Odayı sadece üyelere açık hale getir"}. @@ -179,39 +129,28 @@ {"Make room persistent","Odayı kalıcı hale getir"}. {"Make room public searchable","Odayı herkes tarafından aranabilir hale getir"}. {"March","Mart"}. -{"Maximum Number of Occupants","Odada En Fazla Bulunabilecek Kişi Sayısı"}. -{"Max # of items to persist","Kalıcı hale getirilecek en fazla öğe sayısı"}. {"Max payload size in bytes","En fazla yük (payload) boyutu (bayt olarak)"}. +{"Maximum Number of Occupants","Odada En Fazla Bulunabilecek Kişi Sayısı"}. {"May","Mayıs"}. {"Membership is required to enter this room","Bu odaya girmek için üyelik gerekiyor"}. -{"Members:","Üyeler:"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Parolanızı ezberleyin ya da bir kağıda yazarak güvenli bir yerde saklayın. Jabber'da parolanızı unutursanız, otomatik kurtarmak için bir yöntem bulunmuyor."}. -{"Memory","Bellek"}. {"Message body","Mesajın gövdesi"}. {"Middle Name","Ortanca İsim"}. {"Minimum interval between voice requests (in seconds)","Ses istekleri arasında olabilecek en az aralık (saniye olarak)"}. {"Moderator privileges required","Moderatör yetkileri gerekli"}. -{"moderators only","sadece moderatörler"}. -{"Modified modules","Değişen modüller"}. -{"Module","Modül"}. -{"Modules","Modüller"}. {"Monday","Pazartesi"}. -{"Name:","İsim:"}. {"Name","İsim"}. {"Never","Asla"}. {"New Password:","Yeni Parola:"}. {"Nickname Registration at ","Takma İsim Kaydı : "}. {"Nickname ~s does not exist in the room","~s takma ismi odada yok"}. {"Nickname","Takma isim"}. -{"nobody","hiç kimse"}. {"No body provided for announce message","Duyuru mesajının gövdesi yok"}. {"No Data","Veri Yok"}. +{"No limit","Sınırsız"}. {"Node ID","Düğüm ID"}. {"Node not found","Düğüm bulunamadı"}. {"Nodes","Düğümler"}. -{"No limit","Sınırsız"}. {"None","Hiçbiri"}. -{"No resource provided","Hiç kaynak sağlanmadı"}. {"Not Found","Bulunamadı"}. {"Notify subscribers when items are removed from the node","Düğümden öğeler kaldırıldığında üyeleri uyar"}. {"Notify subscribers when the node configuration changes","Düğüm ayarları değiştiğinde üyeleri uyar"}. @@ -221,13 +160,10 @@ {"Number of online users","Bağlı kullanıcı sayısı"}. {"Number of registered users","Kayıtlı kullanıcı sayısı"}. {"October","Ekim"}. -{"Offline Messages:","Çevirim-dışı Mesajlar:"}. -{"Offline Messages","Çevirim-dışı Mesajlar"}. {"OK","Tamam"}. {"Old Password:","Eski Parola:"}. -{"Online","Bağlı"}. -{"Online Users:","Bağlı Kullanıcılar:"}. {"Online Users","Bağlı Kullanıcılar"}. +{"Online","Bağlı"}. {"Only deliver notifications to available users","Uyarıları sadece durumu uygun kullanıcılara ulaştır"}. {"Only moderators and participants are allowed to change the subject in this room","Sadece moderatörlerin ve katılımcıların bu odanın konusunu değiştirmesine izin veriliyor"}. {"Only moderators are allowed to change the subject in this room","Sadece moderatörlerin bu odanın konusunu değiştirmesine izin veriliyor"}. @@ -235,54 +171,38 @@ {"Only occupants are allowed to send messages to the conference","Sadece oda sakinlerinin konferansa mesaj göndermesine izin veriliyor"}. {"Only occupants are allowed to send queries to the conference","Sadece oda sakinlerinin konferansa sorgu göndermesine izin veriliyor"}. {"Only service administrators are allowed to send service messages","Sadece servis yöneticileri servis mesajı gönderebilirler"}. -{"Options","Seçenekler"}. {"Organization Name","Kurum İsmi"}. {"Organization Unit","Kurumun İlgili Birimi"}. -{"Outgoing s2s Connections:","Giden s2s Bağlantıları:"}. {"Outgoing s2s Connections","Giden s2s Bağlantıları"}. {"Owner privileges required","Sahip yetkileri gerekli"}. -{"Packet","Paket"}. -{"Password ~b","Parola ~b"}. -{"Password:","Parola:"}. -{"Password","Parola"}. -{"Password Verification:","Parola Doğrulaması:"}. {"Password Verification","Parola Doğrulaması"}. +{"Password Verification:","Parola Doğrulaması:"}. +{"Password","Parola"}. +{"Password:","Parola:"}. {"Path to Dir","Dizinin Yolu"}. {"Path to File","Dosyanın Yolu"}. -{"Pending","Sıra Bekleyen"}. {"Period: ","Periyot:"}. {"Persist items to storage","Öğeleri depoda kalıcı hale getir"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Bu seçeneklerin sadece gömülü Mnesia veritabanını yedekleyeceğine dikkat edin. Eğer ODBC modülünü kullanıyorsanız, SQL veritabanınızı da ayrıca yedeklemeniz gerekiyor."}. {"Please, wait for a while before sending new voice request","Lütfen yeni bir ses isteği göndermeden önce biraz bekleyin"}. {"Pong","Pong"}. -{"Port ~b","Kapı (Port) ~b"}. -{"Port","Kapı (Port)"}. {"Present real Jabber IDs to","Gerçek Jabber ID'lerini göster :"}. {"private, ","özel"}. -{"Protocol","Protokol"}. {"Publish-Subscribe","Yayınla-Üye Ol"}. {"PubSub subscriber request","PubSub üye isteği"}. {"Purge all items when the relevant publisher goes offline","İlgili yayıncı çevirimdışı olunca tüm onunla ilgili olanları sil"}. {"Queries to the conference members are not allowed in this room","Bu odada konferans üyelerine sorgu yapılmasına izin verilmiyor"}. {"RAM and disc copy","RAM ve disk kopyala"}. {"RAM copy","RAM kopyala"}. -{"Raw","Ham"}. {"Really delete message of the day?","Günün mesajını silmek istediğinize emin misiniz?"}. {"Recipient is not in the conference room","Alıcı konferans odasında değil"}. -{"Register a Jabber account","Bir Jabber hesabı kaydet"}. -{"Registered Users:","Kayıtlı Kullanıcılar:"}. -{"Registered Users","Kayıtlı Kullanıcılar"}. {"Register","Kayıt Ol"}. -{"Registration in mod_irc for ","mod_irc'ye kayıt : "}. {"Remote copy","Uzak kopyala"}. -{"Remove All Offline Messages","Tüm Çevirim-dışı Mesajları Kaldır"}. -{"Remove","Kaldır"}. {"Remove User","Kullanıcıyı Kaldır"}. {"Replaced by new connection","Eski bağlantı yenisi ile değiştirildi"}. {"Resources","Kaynaklar"}. {"Restart Service","Servisi Tekrar Başlat"}. -{"Restart","Tekrar Başlat"}. {"Restore Backup from File at ","Dosyadaki Yedekten Geri Al : "}. {"Restore binary backup after next ejabberd restart (requires less memory):","ejabberd'nin bir sonraki tekrar başlatılışında ikili yedekten geri al (daha az bellek gerektirir)"}. {"Restore binary backup immediately:","Hemen ikili yedekten geri al:"}. @@ -294,22 +214,16 @@ {"Room Occupants","Oda Sakini Sayısı"}. {"Room title","Oda başlığı"}. {"Roster groups allowed to subscribe","Üye olunmasına izin verilen kontak listesi grupları"}. -{"Roster","Kontak Listesi"}. -{"Roster of ","Kontak Listesi : "}. {"Roster size","İsim listesi boyutu"}. -{"RPC Call Error","RPC Çağrı Hatası"}. {"Running Nodes","Çalışan Düğümler"}. -{"~s access rule configuration","~s erişim kuralları ayarları"}. {"Saturday","Cumartesi"}. -{"Script check","Betik kontrolü"}. {"Search Results for ","Arama sonuçları : "}. {"Search users in ","Kullanıcılarda arama yap : "}. -{"Send announcement to all online users","Duyuruyu tüm bağlı kullanıcılara yolla"}. {"Send announcement to all online users on all hosts","Duyuruyu tüm sunuculardaki tüm bağlı kullanıcılara yolla"}. -{"Send announcement to all users","Duyuruyu tüm kullanıcılara yolla"}. +{"Send announcement to all online users","Duyuruyu tüm bağlı kullanıcılara yolla"}. {"Send announcement to all users on all hosts","Tüm sunuculardaki tüm kullanıcılara duyuru yolla"}. +{"Send announcement to all users","Duyuruyu tüm kullanıcılara yolla"}. {"September","Eylül"}. -{"Server ~b","Sunucu ~b"}. {"Server:","Sunucu:"}. {"Set message of the day and send to online users","Günün mesajını belirle"}. {"Set message of the day on all hosts and send to online users","Tüm sunucularda günün mesajını belirle ve bağlı tüm kullanıcılara gönder"}. @@ -317,75 +231,43 @@ {"Show Integral Table","Önemli Tabloyu Göster"}. {"Show Ordinary Table","Sıradan Tabloyu Göster"}. {"Shut Down Service","Servisi Kapat"}. -{"~s invites you to the room ~s","~s sizi ~s odasına davet ediyor"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Bazı Jabber istemcileri parolanızı bilgisayarınızda saklayabilir. Bu özelliği ancak bilgisayarın güvenli olduğuna güveniyorsanız kullanın."}. {"Specify the access model","Erişim modelini belirtiniz"}. {"Specify the event message type","Olay mesaj tipini belirtiniz"}. {"Specify the publisher model","Yayıncı modelini belirtiniz"}. -{"~s's Offline Messages Queue","~s Kullanıcısının Mesaj Kuyruğu"}. -{"Start","Başlat"}. -{"Start Modules at ","Modülleri Başlat : "}. -{"Start Modules","Modülleri Başlat"}. -{"Statistics","İstatistikler"}. -{"Statistics of ~p","~p istatistikleri"}. -{"Stop","Durdur"}. -{"Stop Modules at ","Modülleri Durdur : "}. -{"Stop Modules","Modülleri Durdur"}. {"Stopped Nodes","Durdurulmuş Düğümler"}. -{"Storage Type","Depolama Tipi"}. {"Store binary backup:","İkili yedeği sakla:"}. {"Store plain text backup:","Düz metin yedeği sakla:"}. {"Subject","Konu"}. -{"Submit","Gönder"}. {"Submitted","Gönderilenler"}. {"Subscriber Address","Üye Olanın Adresi"}. -{"Subscription","Üyelik"}. {"Sunday","Pazar"}. {"That nickname is already in use by another occupant","Takma isim odanın başka bir sakini tarafından halihazırda kullanımda"}. {"That nickname is registered by another person","O takma isim başka biri tarafından kaydettirilmiş"}. {"The CAPTCHA is valid.","İnsan doğrulaması (captcha) geçerli."}. {"The CAPTCHA verification has failed","CAPTCHA doğrulaması başarısız oldu"}. {"The collections with which a node is affiliated","Bir düğüm ile bağlantılı koleksiyonlar"}. -{"the password is","parola :"}. {"The password is too weak","Parola çok zayıf"}. -{"The password of your Jabber account was successfully changed.","Jabber hesabınızın parolası başarıyla değiştirildi."}. -{"There was an error changing the password: ","Parolanın değiştirilmesi sırasında bir hata oluştu:"}. +{"the password is","parola :"}. {"There was an error creating the account: ","Hesap oluşturulurken bir hata oluştu:"}. {"There was an error deleting the account: ","Hesabın silinmesi sırasında bir hata oluştu:"}. -{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Burada büyük küçük harfi yapılmaz: macbeth ile MacBeth ve Macbeth aynıdır."}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Bu sayfa bu Jabber sunucusunda bir Jabber hesabı oluşturulmasına olanak tanıyor. Sizin JID'iniz (Jabber Tanımlayıcısı) şu biçemde olacaktır: kullanici_adi@sunucu. Lütfen alanları doğru doldurabilmek için yönergeleri dikkatle okuyunuz."}. -{"This page allows to unregister a Jabber account in this Jabber server.","Bu sayfa bu Jabber sunucusundan bir Jabber hesabının kaydını silmeye olanak tanıyor."}. +{"This room is not anonymous","Bu oda anonim değil"}. {"Thursday","Perşembe"}. {"Time delay","Zaman gecikmesi"}. -{"Time","Zaman"}. -{"To","Kime"}. {"Too many CAPTCHA requests","Çok fazla CAPTCHA isteği"}. -{"To ~s","Kime ~s"}. {"Traffic rate limit is exceeded","Trafik oran sınırı aşıldı"}. -{"Transactions Aborted:","İptal Edilen Hareketler (Transactions):"}. -{"Transactions Committed:","Tamamlanan Hareketler (Transactions Committed):"}. -{"Transactions Logged:","Kaydı Tutulan Hareketler (Transactions):"}. -{"Transactions Restarted:","Tekrar Başlatılan Hareketler (Transactions):"}. {"Tuesday","Salı"}. {"Unable to generate a CAPTCHA","İnsan doğrulaması (CAPTCHA) oluşturulamadı"}. {"Unauthorized","Yetkisiz"}. -{"Unregister a Jabber account","Bir Jabber hesabı kaydı sil"}. {"Unregister","Kaydı Sil"}. -{"Update","GÜncelle"}. {"Update message of the day (don't send)","Günün mesajını güncelle (gönderme)"}. {"Update message of the day on all hosts (don't send)","Tüm sunuculardaki günün mesajını güncelle (gönderme)"}. -{"Update plan","Planı güncelle"}. -{"Update script","Betiği Güncelle"}. -{"Uptime:","Hizmet Süresi:"}. -{"Use of STARTTLS required","STARTTLS kullanımı gereklidir"}. {"User JID","Kullanıcı JID"}. -{"User","Kullanıcı"}. {"User Management","Kullanıcı Yönetimi"}. +{"User","Kullanıcı"}. {"Username:","Kullanıcı adı:"}. {"Users are not allowed to register accounts so quickly","Kullanıcıların bu kadar hızlı hesap açmalarına izin verilmiyor"}. -{"Users","Kullanıcılar"}. {"Users Last Activity","Kullanıcıların Son Aktiviteleri"}. -{"Validate","Geçerli"}. +{"Users","Kullanıcılar"}. {"vCard User Search","vCard Kullanıcı Araması"}. {"Virtual Hosts","Sanal Sunucuları"}. {"Visitors are not allowed to change their nicknames in this room","Bu odada ziyaretçilerin takma isimlerini değiştirmesine izin verilmiyor"}. @@ -395,16 +277,11 @@ {"Wednesday","Çarşamba"}. {"When to send the last published item","Son yayınlanan öğe ne zaman gönderilsin"}. {"Whether to allow subscriptions","Üyeliklere izin verilsin mi"}. -{"You can later change your password using a Jabber client.","Parolanızı daha sonra bir Jabber istemci kullanarak değiştirebilirsiniz."}. {"You have been banned from this room","Bu odaya girmeniz yasaklandı"}. {"You must fill in field \"Nickname\" in the form","Formda \"Takma isim\" alanını doldurmanız gerekiyor"}. {"You need a client that supports x:data and CAPTCHA to register","Takma isminizi kaydettirmek için x:data ve CAPTCHA destekleyen bir istemciye gereksinimiz var"}. {"You need a client that supports x:data to register the nickname","Takma isminizi kaydettirmek için x:data destekleyen bir istemciye gereksinimiz var"}. -{"You need an x:data capable client to configure mod_irc settings","mod_irc ayarlarını düzenlemek için x:data becerisine sahip bir istemciye gereksinimiz var"}. -{"You need an x:data capable client to configure room","Odayı ayarlamak için x:data becerisine sahip bir istemciye gereksinimiz var"}. {"You need an x:data capable client to search","Arama yapabilmek için x:data becerisine sahip bir istemciye gereksinimiz var"}. {"Your active privacy list has denied the routing of this stanza.","Etkin mahremiyet listeniz bu bölümün yönlendirilmesini engelledi."}. {"Your contact offline message queue is full. The message has been discarded.","Çevirim-dışı mesaj kuyruğunuz dolu. Mesajını dikkate alınmadı."}. -{"Your Jabber account was successfully created.","Jabber hesabınız başarıyla oluşturuldu."}. -{"Your Jabber account was successfully deleted.","Jabber hesabınız başarıyla silindi."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","~s kullanıcısına mesajlarınız engelleniyor. Durumu düzeltmek için ~s adresini ziyaret ediniz."}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","~s kullanıcısına mesajlarınız engelleniyor. Durumu düzeltmek için ~s adresini ziyaret ediniz."}. diff --git a/priv/msgs/tr.po b/priv/msgs/tr.po deleted file mode 100644 index 613b2260c..000000000 --- a/priv/msgs/tr.po +++ /dev/null @@ -1,1943 +0,0 @@ -# translation of tr.po to Turkish -# Doruk Fisek , 2009, 2012. -msgid "" -msgstr "" -"Project-Id-Version: tr\n" -"PO-Revision-Date: 2012-04-17 11:18+0300\n" -"Last-Translator: Doruk Fisek \n" -"Language-Team: Turkish \n" -"Language: tr\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Turkish (türkçe)\n" -"X-Generator: KBabel 1.11.4\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "STARTTLS kullanımı gereklidir" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Hiç kaynak sağlanmadı" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Eski bağlantı yenisi ile değiştirildi" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "odadan atıldı" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "Etkin mahremiyet listeniz bu bölümün yönlendirilmesini engelledi." - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Gördüğünüz metni giriniz" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "" -"~s kullanıcısına mesajlarınız engelleniyor. Durumu düzeltmek için ~s " -"adresini ziyaret ediniz." - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "" -"Eğer burada CAPTCHA resmini göremiyorsanız, web sayfasını ziyaret edin." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "CAPTCHA web sayfası" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "İnsan doğrulaması (captcha) geçerli." - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Kullanıcı" - -#: ejabberd_oauth.erl:256 -#, fuzzy -msgid "Server" -msgstr "Sunucu:" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Parola" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "Yetkisiz" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "ejabberd Web Yöneticisi" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Yönetim" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Erişim Kontrol Listeleri (ACL)" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Gönderilenler" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Kötü biçem" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Gönder" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "Ham" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Seçilenleri Sil" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Erişim Kuralları" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "~s erişim kuralları ayarları" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "Sanal Sunucuları" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Kullanıcılar" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "Bağlı Kullanıcılar" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Kullanıcıların Son Aktiviteleri" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Periyot:" - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "Geçen ay" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "Geçen yıl" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "Tüm aktivite" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Sıradan Tabloyu Göster" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Önemli Tabloyu Göster" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "İstatistikler" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "Bulunamadı" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Düğüm bulunamadı" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Yeni Ekle" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Sunucu" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Kayıtlı Kullanıcılar" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Kullanıcı Ekle" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Çevirim-dışı Mesajlar" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Son Aktivite" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Asla" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Bağlı" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Kayıtlı Kullanıcılar:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Bağlı Kullanıcılar:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Giden s2s Bağlantıları:" - -#: ejabberd_web_admin.erl:1559 -#, fuzzy -msgid "Incoming s2s Connections:" -msgstr "Giden s2s Bağlantıları:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Hiçbiri" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Parola Değiştir" - -#: ejabberd_web_admin.erl:1673 -#, fuzzy -msgid "User ~s" -msgstr "Kullanıcı " - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Bağlı Kaynaklar:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Parola:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Kullanıcıyı Kaldır" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Veri Yok" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Düğümler" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Çalışan Düğümler" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Durdurulmuş Düğümler" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -#, fuzzy -msgid "Node ~p" -msgstr "Düğüm " - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "Veritabanı" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Yedekle" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Dinlenen Kapılar (Portlar)" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "GÜncelle" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Tekrar Başlat" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Durdur" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Modüller" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "RPC Çağrı Hatası" - -#: ejabberd_web_admin.erl:1917 -#, fuzzy -msgid "Database Tables at ~p" -msgstr "Veritabanı Tabloları : " - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "İsim" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Depolama Tipi" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Elementler" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Bellek" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Hata" - -#: ejabberd_web_admin.erl:1955 -#, fuzzy -msgid "Backup of ~p" -msgstr "Yedek : " - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Bu seçeneklerin sadece gömülü Mnesia veritabanını yedekleyeceğine dikkat " -"edin. Eğer ODBC modülünü kullanıyorsanız, SQL veritabanınızı da ayrıca " -"yedeklemeniz gerekiyor." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "İkili yedeği sakla:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "Tamam" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Hemen ikili yedekten geri al:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"ejabberd'nin bir sonraki tekrar başlatılışında ikili yedekten geri al (daha " -"az bellek gerektirir)" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Düz metin yedeği sakla:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Hemen düz metin yedekten geri al" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "Kullanıcıları bir PIEFXIS dosyasından (XEP-0227) içe aktar:" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" -"Sunucudaki tüm kullanıcıların verisini PIEFXIS dosyalarına (XEP-0227) dışa " -"aktar:" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "" -"Bir sunucudaki kullanıcıların verisini PIEFXIS dosyalarına (XEP-0227) dışa " -"aktar:" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Jabberd 1.4 Spool Dosyalarından Kullanıcıları İçe Aktar:" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Jabberd 1.4 Spool Dizininden Kullanıcıları İçe Aktar:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Dinlenen Kapılar (Portlar) : " - -#: ejabberd_web_admin.erl:2144 -#, fuzzy -msgid "Modules at ~p" -msgstr "Modüller : " - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "~p istatistikleri" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Hizmet Süresi:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "İşlemci Zamanı:" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Tamamlanan Hareketler (Transactions Committed):" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "İptal Edilen Hareketler (Transactions):" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Tekrar Başlatılan Hareketler (Transactions):" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Kaydı Tutulan Hareketler (Transactions):" - -#: ejabberd_web_admin.erl:2243 -#, fuzzy -msgid "Update ~p" -msgstr "Güncelle " - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "Planı güncelle" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Değişen modüller" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Betiği Güncelle" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Düşük seviye güncelleme betiği" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Betik kontrolü" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Kapı (Port)" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Protokol" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Modül" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Seçenekler" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Başlat" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Komutlar" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "Günün mesajını silmek istediğinize emin misiniz?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Konu" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Mesajın gövdesi" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "Duyuru mesajının gövdesi yok" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Duyurular" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Duyuruyu tüm kullanıcılara yolla" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Tüm sunuculardaki tüm kullanıcılara duyuru yolla" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Duyuruyu tüm bağlı kullanıcılara yolla" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "Duyuruyu tüm sunuculardaki tüm bağlı kullanıcılara yolla" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Günün mesajını belirle" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" -"Tüm sunucularda günün mesajını belirle ve bağlı tüm kullanıcılara gönder" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Günün mesajını güncelle (gönderme)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "Tüm sunuculardaki günün mesajını güncelle (gönderme)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Günün mesajını sil" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Tüm sunuculardaki günün mesajını sil" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Ayarlar" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Modülleri Başlat" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Modülleri Durdur" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Yedekten Geri Al" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Metin Dosyasına Döküm Al" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Dosyayı İçe Aktar" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Dizini İçe Aktar" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Servisi Tekrar Başlat" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Servisi Kapat" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Kullanıcıyı Sil" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Kullanıcı Oturumunu Kapat" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Kullanıcı Parolasını Al" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Kullanıcı Parolasını Değiştir" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "Kullanıcı Son Giriş Zamanınlarını Al" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Kullanıcı İstatistiklerini Al" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Kayıtlı Kullanıcı Sayısını Al" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Bağlı Kullanıcı Sayısını Al" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Kullanıcı Yönetimi" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Tüm Kullanıcılar" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Giden s2s Bağlantıları" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Yedek Yönetimi" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Jabberd 1.4 Spool Dosyalarından Kullanıcıları İçeri Aktar" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "Kime ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "Kimden ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Veritabanı Tablo Ayarları : " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Tabloların veri depolama tipini seçiniz" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Sadece disk kopyala" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "RAM ve disk kopyala" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "RAM kopyala" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "Uzak kopyala" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Modülleri Durdur : " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Durdurulacak modülleri seçiniz" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Modülleri Başlat : " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "{Module, [Options]} listesi giriniz" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Başlatılacak modüllerin listesi" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Dosyaya Yedekle : " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Yedek dosyasının yolunu giriniz" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Dosyanın Yolu" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Dosyadaki Yedekten Geri Al : " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Metin Dosyasına Döküm Alarak Yedekle : " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Metin dosyasının yolunu giriniz" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Dosyadan Kullanıcıları İçe Aktar : " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "jabberd14 spool dosyası için yol giriniz" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Dizinden Kullanıcıları İçe Aktar : " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "jabberd14 spool dosyası için yol giriniz" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Dizinin Yolu" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "Zaman gecikmesi" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Erişim Kontrol Listelerinin Ayarlanması (ACL)" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Erişim kontrol listeleri (ACL)" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Erişim Ayarları" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Erişim kuralları" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Parola Doğrulaması" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Kayıtlı kullanıcı sayısı" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Bağlı kullanıcı sayısı" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Son giriş" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "İsim listesi boyutu" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "IP adresleri" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Kaynaklar" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Yönetim : " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Kullanıcıya uygulanacak eylem" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Özellikleri Düzenle" - -#: mod_fail2ban.erl:95 -msgid "" -"Too many (~p) failed authentications from this IP address (~s). The address " -"will be unblocked at ~s UTC" -msgstr "" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "" - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "" - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "Servis politikası gereği erişim engellendi" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "IRC Nakli (Transport)" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "ejabberd IRC modülü" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "" -"mod_irc ayarlarını düzenlemek için x:data becerisine sahip bir istemciye " -"gereksinimiz var" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "mod_irc'ye kayıt : " - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"IRC sunuculara bağlanmak için kullanmak istediğiniz kullanıcı ismi, " -"kodlamalar, kapılar (portlar) ve parolaları giriniz" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "IRC Kullanıcı İsmi" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"IRC sunucuları için farklı kapılar (portlar), parolalar, kodlamalar " -"belirtmek istiyorsanız, '{\"irc sunucusu\", \"kodlama\",\"kapı\",\"parola" -"\"}' biçeminde değerlerle bu listeyi doldurunuz. Öntanımlı olarak bu servis " -"\"~s\" kodlamasını, ~p \"kapısını\", \"boş\" parolasını kullanıyor." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Örnek: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"gizli\"}, {\"vendetta.fef.net" -"\", \"iso8859-1\", 7000}], {\"irc.sometestserver.net\", \"utf-8\"}]" - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Bağlantı parametreleri" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "IRC kanalına katıl" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "IRC kanalı (ilk # işaretini koymayın)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "IRC sunucusu" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Buradaki IRC kanalına katıl." - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "IRC kanalına bu Jabber ID'si ile katıl: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "IRC ayarları" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"IRC sunuculara bağlanmak için kullanmak istediğiniz kullanıcı isimleri ve " -"kodlamaları giriniz. 'İleri' tuşuna basınca karşınıza dolduracak daha fazla " -"alan çıkacak. 'Tamamla' tuşuna basarak ayarları kaydedin." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "IRC kullanıcı ismi" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Parola ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Kapı (Port) ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Sunucu için kodlama ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Sunucu ~b" - -#: mod_mam.erl:541 -#, fuzzy -msgid "Only members may query archives of this room" -msgstr "Sadece moderatörlerin bu odanın konusunu değiştirmesine izin veriliyor" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "Sadece servis yöneticileri servis mesajı gönderebilirler" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "Odanın oluşturulması servis politikası gereği reddedildi" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "Konferans odası bulunamadı" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Sohbet Odaları" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "" -"Takma isminizi kaydettirmek için x:data destekleyen bir istemciye " -"gereksinimiz var" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Takma İsim Kaydı : " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Kaydettirmek istediğiniz takma ismi giriniz" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Takma isim" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "O takma isim başka biri tarafından kaydettirilmiş" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Formda \"Takma isim\" alanını doldurmanız gerekiyor" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "ejabberd MUC modülü" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "" - -#: mod_muc_admin.erl:249 -#, fuzzy -msgid "Total rooms" -msgstr "Sohbet Odaları" - -#: mod_muc_admin.erl:250 -#, fuzzy -msgid "Permanent rooms" -msgstr "odadan ayrıldı" - -#: mod_muc_admin.erl:251 -#, fuzzy -msgid "Registered nicknames" -msgstr "Kayıtlı Kullanıcılar" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "Sohbet odası ayarı değiştirildi" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "odaya katıldı" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "odadan ayrıldı" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "odaya girmesi yasaklandı" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "ilişki değişikliğinden dolayı atıldı" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "oda üyelere-özel hale getirildiğinden dolayı atıldı" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "sistem kapandığından dolayı atıldı" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "isim değiştirdi :" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " konuyu değiştirdi: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "Sohbet odası oluşturuldu" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "Sohbet odası kaldırıldı" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "Sohbet odası başlatıldı" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "Sohbet odası durduruldu" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "Pazartesi" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "Salı" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "Çarşamba" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "Perşembe" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "Cuma" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "Cumartesi" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "Pazar" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "Ocak" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "Şubat" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "Mart" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "Nisan" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "Mayıs" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "Haziran" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "Temmuz" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "Ağustos" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "Eylül" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "Ekim" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "Kasım" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "Aralık" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Oda Ayarları" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "Oda Sakini Sayısı" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "Trafik oran sınırı aşıldı" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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 "" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "Konferansa özel mesajlar gönderilmesine izin verilmiyor" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "Lütfen yeni bir ses isteği göndermeden önce biraz bekleyin" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "Bu konferansta ses istekleri etkisizleştirilmiş durumda." - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "Ses isteği onayınızdan JID bilginize ulaşılamadı" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "Yalnız moderatörler ses isteklerini onaylayabilir" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Uygunsuz mesaj tipi" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "\"groupchat\" tipinde özel mesajlar gönderilmesine izin verilmiyor" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "Alıcı konferans odasında değil" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "Özel mesaj gönderilmesine izin verilmiyor" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Sadece oda sakinlerinin konferansa mesaj göndermesine izin veriliyor" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "Sadece oda sakinlerinin konferansa sorgu göndermesine izin veriliyor" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "Bu odada konferans üyelerine sorgu yapılmasına izin verilmiyor" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "" -"Sadece moderatörlerin ve katılımcıların bu odanın konusunu değiştirmesine " -"izin veriliyor" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Sadece moderatörlerin bu odanın konusunu değiştirmesine izin veriliyor" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "" -"Ziyaretçilerin odadaki tüm sakinlere mesaj göndermesine izin verilmiyor" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "" -"Bu odada ziyaretçilerin takma isimlerini değiştirmesine izin verilmiyor" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "Takma isim odanın başka bir sakini tarafından halihazırda kullanımda" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Bu odaya girmeniz yasaklandı" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "Bu odaya girmek için üyelik gerekiyor" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "Bu odaya girmek için parola gerekiyor" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "Çok fazla CAPTCHA isteği" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "İnsan doğrulaması (CAPTCHA) oluşturulamadı" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Yanlış parola" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "Yönetim yetkileri gerekli" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "Moderatör yetkileri gerekli" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "Jabber ID ~s geçersiz" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "~s takma ismi odada yok" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Geçersiz ilişki: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Geçersiz rol: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "Sahip yetkileri gerekli" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "~s odasının ayarları" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Oda başlığı" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "Oda tanımı" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Odayı kalıcı hale getir" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Odayı herkes tarafından aranabilir hale getir" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "Katılımcı listesini herkese açık hale getir" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Odayı parola korumalı hale getir" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Odada En Fazla Bulunabilecek Kişi Sayısı" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Sınırsız" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Gerçek Jabber ID'lerini göster :" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "sadece moderatörler" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "herkes" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "" - -#: mod_muc_room.erl:3486 -#, fuzzy -msgid "Moderator" -msgstr "sadece moderatörler" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Odayı sadece üyelere açık hale getir" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Odayı moderasyonlu hale getir" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Kullanıcılar öntanımlı olarak katılımcı olsun" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Kullanıcıların konu değiştirmesine izin ver" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Kullanıcıların özel mesaj göndermelerine izin ver" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "Ziyaretçilerin özel mesaj göndermelerine izin ver" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "hiç kimse" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Kullanıcıların diğer kullanıcıları sorgulamalarına izin ver" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Kullanıcıların davetiye göndermelerine izin ver" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "" -"Ziyaretçilerin varlık (presence) güncellemelerinde durum metni " -"göndermelerine izin ver" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Ziyaretçilerin takma isim değiştirmelerine izin ver" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "Ziyaretçilerin ses isteğine göndermelerine izin ver" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "Ses istekleri arasında olabilecek en az aralık (saniye olarak)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "Odayı insan doğrulaması (captcha) korumalı hale getir" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "CAPTCHA doğrulamasını şu Jabber ID'ler için yapma" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Kayıt tutma özelliğini aç" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "" -"Odayı ayarlamak için x:data becerisine sahip bir istemciye gereksinimiz var" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Oda sakini sayısı" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "özel" - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "Ses isteği" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "Ses isteğini kabul edin ya da reddedin" - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "Kullanıcı JID" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "Bu kişiye ses verelim mi?" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s sizi ~s odasına davet ediyor" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "parola :" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "Çevirim-dışı mesaj kuyruğunuz dolu. Mesajını dikkate alınmadı." - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "~s Kullanıcısının Mesaj Kuyruğu" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Zaman" - -#: mod_offline.erl:812 -msgid "From" -msgstr "Kimden" - -#: mod_offline.erl:813 -msgid "To" -msgstr "Kime" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Paket" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Çevirim-dışı Mesajlar:" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "Tüm Çevirim-dışı Mesajları Kaldır" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "ejabberd SOCKS5 Bytestreams modülü" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Yayınla-Üye Ol" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "ejabberd Publish-Subscribe modülü" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "PubSub üye isteği" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Bu varlığın üyeliğini onaylayıp onaylamamayı seçiniz." - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "Düğüm ID" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Üye Olanın Adresi" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "Bu Jabber ID bu pubsub düğümüne üye olmasına izin verilsin mi?" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Yükleri (payload) olay uyarıları ile beraber gönder" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Olay uyarıları gönderilsin" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Düğüm ayarları değiştiğinde üyeleri uyar" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Bir düğüm silindiğinde üyeleri uyar" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Düğümden öğeler kaldırıldığında üyeleri uyar" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Öğeleri depoda kalıcı hale getir" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "Düğüm için dostane bir isim" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Kalıcı hale getirilecek en fazla öğe sayısı" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Üyeliklere izin verilsin mi" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Erişim modelini belirtiniz" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Üye olunmasına izin verilen kontak listesi grupları" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Yayıncı modelini belirtiniz" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "İlgili yayıncı çevirimdışı olunca tüm onunla ilgili olanları sil" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "Olay mesaj tipini belirtiniz" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "En fazla yük (payload) boyutu (bayt olarak)" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "Son yayınlanan öğe ne zaman gönderilsin" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Uyarıları sadece durumu uygun kullanıcılara ulaştır" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "Bir düğüm ile bağlantılı koleksiyonlar" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "CAPTCHA doğrulaması başarısız oldu" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "" -"Takma isminizi kaydettirmek için x:data ve CAPTCHA destekleyen bir istemciye " -"gereksinimiz var" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "Bu sunucuya kayıt olmak için bir kullanıcı ismi ve parola seçiniz" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "Parola çok zayıf" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "Kullanıcıların bu kadar hızlı hesap açmalarına izin verilmiyor" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "Jabber hesabınız başarıyla oluşturuldu." - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "Hesap oluşturulurken bir hata oluştu:" - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "Jabber hesabınız başarıyla silindi." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "Hesabın silinmesi sırasında bir hata oluştu:" - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "Jabber hesabınızın parolası başarıyla değiştirildi." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "Parolanın değiştirilmesi sırasında bir hata oluştu:" - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Jabber Hesap Kaydı" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Bir Jabber hesabı kaydet" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Bir Jabber hesabı kaydı sil" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"Bu sayfa bu Jabber sunucusunda bir Jabber hesabı oluşturulmasına olanak " -"tanıyor. Sizin JID'iniz (Jabber Tanımlayıcısı) şu biçemde olacaktır: " -"kullanici_adi@sunucu. Lütfen alanları doğru doldurabilmek için yönergeleri " -"dikkatle okuyunuz." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "Kullanıcı adı:" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" -"Burada büyük küçük harfi yapılmaz: macbeth ile MacBeth ve Macbeth aynıdır." - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "İzin verilmeyen karakterler:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "Sunucu:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "Parolanızı kimseye söylemeyin, Jabber sunucusunun yöneticilerine bile." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "" -"Parolanızı daha sonra bir Jabber istemci kullanarak değiştirebilirsiniz." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Bazı Jabber istemcileri parolanızı bilgisayarınızda saklayabilir. Bu " -"özelliği ancak bilgisayarın güvenli olduğuna güveniyorsanız kullanın." - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"Parolanızı ezberleyin ya da bir kağıda yazarak güvenli bir yerde saklayın. " -"Jabber'da parolanızı unutursanız, otomatik kurtarmak için bir yöntem " -"bulunmuyor." - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "Parola Doğrulaması:" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "Kayıt Ol" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "Eski Parola:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "Yeni Parola:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "" -"Bu sayfa bu Jabber sunucusundan bir Jabber hesabının kaydını silmeye olanak " -"tanıyor." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "Kaydı Sil" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Üyelik" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Sıra Bekleyen" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Gruplar" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Geçerli" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Kaldır" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Kontak Listesi : " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Jabber ID'si Ekle" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Kontak Listesi" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Paylaşımlı Kontak Listesi Grupları" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "İsim:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Tanım:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Üyeler:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Gösterilen Gruplar:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Group " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Erlang Jabber Sunucusu" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Doğumgünü" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "İl" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "Ülke" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "E-posta" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Soyisim" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Eşleşen jabber kullanıcılarını aramak için formu doldurunuz (Alt dizgi " -"eşlemek için alanın sonuna * ekleyin)" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Tam İsim" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Ortanca İsim" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Kurum İsmi" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Kurumun İlgili Birimi" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Kullanıcılarda arama yap : " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "" -"Arama yapabilmek için x:data becerisine sahip bir istemciye gereksinimiz var" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "vCard Kullanıcı Araması" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "ejabberd vCard modülü" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Arama sonuçları : " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "Eşleşen jabber kullanıcılarını aramak için alanları doldurunuz" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Giden s2s Sunucuları" - -#~ msgid "Delete" -#~ msgstr "Sil" - -#~ msgid "This room is not anonymous" -#~ msgstr "Bu oda anonim değil" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "Bu katılımcı bir hata mesajı gönderdiği için odadan atıldı" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Bu katılımcı başka bir katılımcıya bir hata mesajı gönderdiği için odadan " -#~ "atıldı" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "" -#~ "Bu katılımcı bir hata varlığı (presence) gönderdiği için odadan atıldı" diff --git a/priv/msgs/uk.msg b/priv/msgs/uk.msg index 568ac0926..cf950ac73 100644 --- a/priv/msgs/uk.msg +++ b/priv/msgs/uk.msg @@ -1,22 +1,30 @@ -%% -*- coding: latin-1 -*- +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" (Add * to the end of field to match substring)"," Заповніть поля для пошуку користувача Jabber (Додайте * в кінець поля для пошуку підрядка)"}. +{" has set the subject to: "," встановив(ла) тему: "}. +{"# participants","# учасників"}. +{"A description of the node","Опис вузла"}. +{"A friendly name for the node","Псевдонім для вузла"}. +{"A password is required to enter this room","Щоб зайти в цю конференцію, необхідно ввести пароль"}. +{"A Web Page","Веб-сторінка"}. {"Accept","Прийняти"}. -{"Access Configuration","Конфігурація доступу"}. -{"Access Control List Configuration","Конфігурація списків керування доступом"}. -{"Access control lists","Списки керування доступом"}. -{"Access Control Lists","Списки керування доступом"}. {"Access denied by service policy","Доступ заборонений політикою служби"}. -{"Access rules","Правила доступу"}. -{"Access Rules","Правила доступу"}. +{"Access model","Права доступу"}. +{"Account doesn't exist","Обліковий запис не існує"}. {"Action on user","Дія над користувачем"}. -{"Add Jabber ID","Додати Jabber ID"}. -{"Add New","Додати"}. +{"Add a hat to a user","Додати капелюх користувачу"}. {"Add User","Додати користувача"}. {"Administration of ","Адміністрування "}. {"Administration","Адміністрування"}. {"Administrator privileges required","Необхідні права адміністратора"}. -{"A friendly name for the node","Псевдонім для вузла"}. {"All activity","Вся статистика"}. -{"Allow this Jabber ID to subscribe to this pubsub node?","Чи дозволити цьому Jabber ID підписатись новини наданого вузла"}. +{"All Users","Всі користувачі"}. +{"Allow subscription","Дозволити підписку"}. +{"Allow this Jabber ID to subscribe to this pubsub node?","Дозволити цьому Jabber ID підписатись на даний pubsub-вузол?"}. +{"Allow this person to register with the room?","Дозволити цій людині зареєструватися в кімнаті?"}. {"Allow users to change the subject","Дозволити користувачам змінювати тему"}. {"Allow users to query other users","Дозволити iq-запити до користувачів"}. {"Allow users to send invites","Дозволити користувачам надсилати запрошення"}. @@ -25,116 +33,152 @@ {"Allow visitors to send private messages to","Дозволити відвідувачам відсилати приватні повідомлення"}. {"Allow visitors to send status text in presence updates","Дозволити відвідувачам відсилати текст статусу в оновленнях присутності"}. {"Allow visitors to send voice requests","Дозволити відвідувачам надсилати голосові запрошення"}. -{"All Users","Всі користувачі"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","Асоційована група LDAP, яка визначає членство в кімнаті; це повинно бути відмінне ім'я LDAP відповідно до специфічного для реалізації або специфічного для розгортання визначення групи."}. {"Announcements","Сповіщення"}. -{"anyone","всім учасникам"}. -{"A password is required to enter this room","Щоб зайти в цю конференцію, необхідно ввести пароль"}. -{"April","квітня"}. -{"August","серпня"}. +{"Answer associated with a picture","Відповідь, пов’язана зі зображенням"}. +{"Answer associated with a video","Відповідь, пов'язана з відео"}. +{"Answer associated with speech","Відповідь, пов'язана з мовленням"}. +{"Answer to a question","Відповідь на запитання"}. +{"Anyone in the specified roster group(s) may subscribe and retrieve items","Будь-хто в зазначеній групі (групах) реєстру може підписатися та отримувати матеріали"}. +{"Anyone may associate leaf nodes with the collection","Будь-хто може асоціювати листові вузли з колекцією"}. +{"Anyone may publish","Будь-хто може публікувати"}. +{"Anyone may subscribe and retrieve items","Будь-хто може підписатися та отримувати публікації"}. +{"Anyone with a presence subscription of both or from may subscribe and retrieve items","Будь-хто, хто має підписку на отримання інформації про присутність в обох випадках або може підписуватись та отримувати матеріали"}. +{"Anyone with Voice","Будь-хто, хто має голос"}. +{"Anyone","Будь-хто"}. +{"API Commands","Команди API"}. +{"April","Квітень"}. +{"Arguments","Аргументи"}. +{"Attribute 'channel' is required for this request","Атрибут \"канал\" є обов'язковим для цього запиту"}. +{"Attribute 'id' is mandatory for MIX messages","Атрибут 'id' обов'язковий для MIX повідомлень"}. +{"Attribute 'jid' is not allowed here","Атрибут 'jid' заборонений"}. +{"Attribute 'node' is not allowed here","Атрибут \"вузол\" заборонений"}. +{"Attribute 'to' of stanza that triggered challenge","Атрибут \"до\" рядка, який спровокував виклик"}. +{"August","Серпень"}. +{"Automatic node creation is not enabled","Автоматичне створення вузлів не ввімкнено"}. {"Backup Management","Керування резервним копіюванням"}. {"Backup of ~p","Резервне копіювання ~p"}. {"Backup to File at ","Резервне копіювання в файл на "}. {"Backup","Резервне копіювання"}. -{"Bad format","Неправильний формат"}. +{"Bad format","Невірний формат"}. {"Birthday","День народження"}. -{"CAPTCHA web page","Адреса капчі"}. +{"Both the username and the resource are required","Обов'язково потрібне ім'я користувача та джерело"}. +{"Bytestream already activated","Потік байтів вже активовано"}. +{"Cannot remove active list","Неможливо видалити активний список"}. +{"Cannot remove default list","Неможливо видалити список за замовчуванням"}. +{"CAPTCHA web page","Веб-сторінка CAPTCHA"}. +{"Challenge ID","ID виклику"}. {"Change Password","Змінити пароль"}. -{"Change User Password","Змінити Пароль Користувача"}. +{"Change User Password","Змінити пароль користувача"}. +{"Changing password is not allowed","Зміна пароля заборонена"}. +{"Changing role/affiliation is not allowed","Зміна ролі/рангу заборонена"}. +{"Channel already exists","Канал вже існує"}. +{"Channel does not exist","Каналу не існує"}. +{"Channel JID","Канали JID"}. +{"Channels","Канали"}. {"Characters not allowed:","Заборонені символи:"}. -{"Chatroom configuration modified","Конфігурація кімнати змінилась"}. -{"Chatroom is created","Створено кімнату"}. -{"Chatroom is destroyed","Знищено кімнату"}. -{"Chatroom is started","Запущено кімнату"}. -{"Chatroom is stopped","Зупинено кімнату"}. +{"Chatroom configuration modified","Конфігурацію кімнати змінено"}. +{"Chatroom is created","Кімнату створено"}. +{"Chatroom is destroyed","Кімнату видалено"}. +{"Chatroom is started","Кімнату запущено"}. +{"Chatroom is stopped","Кімнату зупинено"}. {"Chatrooms","Кімнати"}. -{"Choose a username and password to register with this server","Виберіть назву користувача та пароль для реєстрації на цьому сервері"}. -{"Choose modules to stop","Виберіть модулі, які необхідно зупинити"}. +{"Choose a username and password to register with this server","Виберіть логін і пароль для реєстрації на цьому сервері"}. {"Choose storage type of tables","Оберіть тип збереження таблиць"}. -{"Choose whether to approve this entity's subscription.","Вирішіть, чи задовольнити запит цього об'єкту на підписку"}. +{"Choose whether to approve this entity's subscription.","Виберіть, чи підтверджувати підписку."}. {"City","Місто"}. +{"Client acknowledged more stanzas than sent by server","Клієнт підтвердив більше повідомлень, ніж було відправлено сервером"}. +{"Clustering","Кластеризація"}. {"Commands","Команди"}. -{"Conference room does not exist","Конференція не існує"}. +{"Conference room does not exist","Кімната для переговорів відсутня"}. {"Configuration of room ~s","Конфігурація кімнати ~s"}. {"Configuration","Конфігурація"}. -{"Connected Resources:","Підключені ресурси:"}. -{"Connections parameters","Параметри з'єднання"}. +{"Contact Addresses (normally, room owner or owners)","Контактні адреси (зазвичай, власника або власників кімнати)"}. {"Country","Країна"}. -{"CPU Time:","Процесорний час:"}. -{"Database Tables at ~p","Таблиці бази даних на ~p"}. +{"Current Discussion Topic","Поточна тема обговорення"}. +{"Database failure","Збій бази даних"}. {"Database Tables Configuration at ","Конфігурація таблиць бази даних на "}. {"Database","База даних"}. -{"December","грудня"}. -{"Default users as participants","Зробити користувачів учасниками за замовчуванням"}. +{"December","Грудень"}. +{"Default users as participants","Користувачі за замовчуванням як учасники"}. {"Delete message of the day on all hosts","Видалити повідомлення дня на усіх хостах"}. {"Delete message of the day","Видалити повідомлення дня"}. -{"Delete Selected","Видалити виділені"}. -{"Delete User","Видалити Користувача"}. +{"Delete User","Видалити користувача"}. {"Deliver event notifications","Доставляти сповіщення про події"}. {"Deliver payloads with event notifications","Доставляти разом з повідомленнями про публікації самі публікації"}. -{"Description:","Опис:"}. {"Disc only copy","Тільки диск"}. -{"Displayed Groups:","Видимі групи:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","Нікому не кажіть свій пароль, навіть адміністраторам сервера."}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","Нікому не кажіть свій пароль, навіть адміністраторам XMPP-сервера."}. {"Dump Backup to Text File at ","Копіювання в текстовий файл на "}. {"Dump to Text File","Копіювання в текстовий файл"}. +{"Duplicated groups are not allowed by RFC6121","RFC6121 забороняє дублювати групи"}. +{"Dynamically specify a replyto of the item publisher","Динамічно вказуйте відповідь видавця елемента"}. {"Edit Properties","Змінити параметри"}. -{"Either approve or decline the voice request.","Підтвердить або відхилите голосовий запит"}. -{"ejabberd IRC module","ejabberd IRC модуль"}. +{"Either approve or decline the voice request.","Підтвердіть або відхиліть голосовий запит."}. +{"ejabberd HTTP Upload service","Служба відвантаження по HTTP для ejabberd"}. {"ejabberd MUC module","ejabberd MUC модуль"}. {"ejabberd Multicast service","Мультікаст ejabberd сервіс"}. {"ejabberd Publish-Subscribe module","Модуль ejabberd Публікації-Підписки"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams модуль"}. {"ejabberd vCard module","ejabberd vCard модуль"}. {"ejabberd Web Admin","Веб-інтерфейс Адміністрування ejabberd"}. -{"Elements","Елементи"}. +{"ejabberd","ejabberd"}. +{"Email Address","Адреса ел. пошти"}. {"Email","Електронна пошта"}. -{"Empty Rooms","Порожні кімнати"}. -{"Enable logging","Включити журнал роботи"}. +{"Enable hats","Увімкнути капелюхи"}. +{"Enable logging","Увімкнути журнал роботи"}. {"Enable message archiving","Ввімкнути архівацію повідомлень"}. -{"Encoding for server ~b","Кодування для сервера ~b"}. +{"Enabling push without 'node' attribute is not supported","Увімкнення push без атрибута node не підтримується"}. {"End User Session","Закінчити Сеанс Користувача"}. -{"Enter list of {Module, [Options]}","Введіть перелік такого виду {Module, [Options]}"}. {"Enter nickname you want to register","Введіть псевдонім, який ви хочете зареєструвати"}. {"Enter path to backup file","Введіть шлях до резервного файла"}. {"Enter path to jabberd14 spool dir","Введіть шлях до директорії спула jabberd14"}. {"Enter path to jabberd14 spool file","Введіть шлях до файла зі спула jabberd14"}. {"Enter path to text file","Введіть шлях до текстового файла"}. {"Enter the text you see","Введіть текст, що ви бачите"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Введіть ім'я користувача та кодування, які будуть використовуватися при підключенні до IRC-серверів Натисніть 'Далі' для заповнення додаткових полів. Натисніть 'Завершити' для збереження параметрів."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Введіть ім'я користувача, кодування, порти та паролі, що будуть використовуватися при підключенні до IRC-серверів"}. -{"Erlang Jabber Server","Erlang Jabber Server"}. -{"Error","Помилка"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Приклад: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. +{"Erlang XMPP Server","Ерланґ XMPP Сервер"}. {"Exclude Jabber IDs from CAPTCHA challenge","Пропускати ці Jabber ID без CAPTCHA-запиту"}. -{"Export all tables as SQL queries to a file:","Експорт усіх таблиць, як SQL запити, у файл"}. +{"Export all tables as SQL queries to a file:","Експортувати всі таблиці у файл як SQL запити:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Експорт даних всіх користувачів сервера до файлу PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Експорт даних користувачів домена до файлу PIEFXIS (XEP-0227):"}. +{"External component failure","Помилка зовнішнього компонента"}. +{"External component timeout","Тайм-аут зовнішнього компонента"}. +{"Failed to activate bytestream","Не вдалося активувати потік байтів"}. {"Failed to extract JID from your voice request approval","Помилка витягнення JID з вашого схвалення голосового запиту"}. +{"Failed to map delegated namespace to external component","Не вдалося зіставити делегований простір імен із зовнішнім компонентом"}. +{"Failed to parse HTTP response","Не вдалося розібрати HTTP-відповідь"}. +{"Failed to process option '~s'","Не вдалося обробити параметр \"~s\""}. {"Family Name","Прізвище"}. -{"February","лютого"}. -{"Fill in fields to search for any matching Jabber User","Заповніть поля для пошуку користувача Jabber"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Заповніть поля для пошуку користувача Jabber (Додайте * в кінець поля для пошуку підрядка)"}. +{"FAQ Entry","Запис в ЧаПи"}. +{"February","Лютого"}. +{"File larger than ~w bytes","Файл більший, ніж ~w байт"}. +{"Fill in the form to search for any matching XMPP User","Заповніть форму для пошуку будь-якого відповідного користувача XMPP"}. {"Friday","П'ятниця"}. -{"From ~s","Від ~s"}. -{"From","Від кого"}. +{"From ~ts","Від ~ts"}. +{"Full List of Room Admins","Повний перелік адміністраторів кімнати"}. +{"Full List of Room Owners","Повний перелік власників кімнати"}. {"Full Name","Повне ім'я"}. +{"Get List of Online Users","Отримати кількість користувачів в мережі"}. +{"Get List of Registered Users","Отримати кількість зареєстрованих користувачів"}. {"Get Number of Online Users","Отримати Кількість Підключених Користувачів"}. {"Get Number of Registered Users","Отримати Кількість Зареєстрованих Користувачів"}. +{"Get Pending","Очікування"}. {"Get User Last Login Time","Отримати Час Останнього Підключення Користувача"}. -{"Get User Password","Отримати Пароль Користувача"}. {"Get User Statistics","Отримати Статистику по Користувачу"}. +{"Given Name","По-батькові"}. {"Grant voice to this person?","Надати голос персоні?"}. -{"Groups","Групи"}. -{"Group ","Група "}. {"has been banned","заборонили вхід в кімнату"}. -{"has been kicked because of an affiliation change","вигнано з кімнати внаслідок зміни рангу"}. {"has been kicked because of a system shutdown","вигнано з кімнати внаслідок зупинки системи"}. +{"has been kicked because of an affiliation change","вигнано з кімнати внаслідок зміни рангу"}. {"has been kicked because the room has been changed to members-only","вигнано з кімнати тому, що вона стала тільки для учасників"}. {"has been kicked","вигнали з кімнати"}. -{" has set the subject to: "," встановив(ла) тему: "}. -{"Host","Хост"}. -{"If you don't see the CAPTCHA image here, visit the web page.","Якщо ви не бачите зображення капчі, перейдіть за за цією адресою."}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Щоб вказати різні порти, паролі та кодування для різних серверів IRC, заповніть список значеннями в форматі '{\"irc server\", \"encoding\", port, \"password\"}'. За замовчуванням ця служба використовує \"~s\" кодування, порт ~p, пустий пароль."}. +{"Hash of the vCard-temp avatar of this room","Хеш тимчасового аватара vCard цієї кімнати"}. +{"Hat title","Назва кімнати"}. +{"Hat URI","Назва URI"}. +{"Hats limit exceeded","Перевищено швидкість передачі інформації"}. +{"Host unknown","Невідоме ім'я сервера"}. +{"HTTP File Upload","Відвантаження файлів по HTTP"}. +{"Idle connection","Неактивне підключення"}. +{"If you don't see the CAPTCHA image here, visit the web page.","Якщо ви не бачите зображення CAPTCHA, перейдіть за адресою."}. {"Import Directory","Імпорт з директорії"}. {"Import File","Імпорт з файла"}. {"Import user data from jabberd14 spool file:","Імпорт користувачів з файла спула jabberd14:"}. @@ -142,299 +186,445 @@ {"Import users data from a PIEFXIS file (XEP-0227):","Імпорт даних користовучів з файлу PIEFXIS (XEP-0227):"}. {"Import users data from jabberd14 spool directory:","Імпорт користувачів з діректорії спула jabberd14:"}. {"Import Users from Dir at ","Імпортування користувача з директорії на "}. -{"Import Users From jabberd14 Spool Files","Імпорт користувачів з jabberd14 файлів \"Spool\""}. +{"Import Users From jabberd14 Spool Files","Імпорт користувачів з jabberd14 файлів \"Spool\""}. +{"Improper domain part of 'from' attribute","Неправильна доменна частина атрибута \"from\""}. {"Improper message type","Неправильний тип повідомлення"}. -{"Incoming s2s Connections:","Вхідні s2s-з'єднання:"}. +{"Incorrect CAPTCHA submit","Неправильний ввід CAPTCHA"}. +{"Incorrect data form","Неправильна форма даних"}. {"Incorrect password","Неправильний пароль"}. -{"Invalid affiliation: ~s","Недопустимий ранг: ~s"}. -{"Invalid role: ~s","Недопустима роль: ~s"}. +{"Incorrect value of 'action' attribute","Неправильне значення атрибута \"action\""}. +{"Incorrect value of 'action' in data form","Неправильне значення \"action\" у формі даних"}. +{"Incorrect value of 'path' in data form","Неправильне значення \"path\" у формі даних"}. +{"Installed Modules:","Запуск модулів:"}. +{"Install","Встановлення"}. +{"Insufficient privilege","Недостатньо привілеїв"}. +{"Internal server error","Внутрішня помилка сервера"}. +{"Invalid 'from' attribute in forwarded message","Неприйнятний атрибут \"from\" у пересланому повідомленні"}. +{"Invalid node name","Неприйнятне ім'я вузла"}. +{"Invalid 'previd' value","Неприйнятне значення \"previd\""}. +{"Invitations are not allowed in this conference","Запрошення на цю конференцію не допускаються"}. {"IP addresses","IP адреси"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","Канал IRC (не включаючи #)"}. -{"IRC server","IRC-сервер"}. -{"IRC settings","Парметри IRC"}. -{"IRC Transport","IRC Транспорт"}. -{"IRC username","Ім'я користувача IRC"}. -{"IRC Username","Ім'я користувача IRC"}. {"is now known as","змінив(ла) псевдонім на"}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Не дозволяється відправляти помилкові повідомлення в кімнату. Учасник (~s) відправив помилкове повідомлення (~s), та був виганий з кімнати"}. {"It is not allowed to send private messages of type \"groupchat\"","Не дозволяється надсилати приватні повідомлення типу \"groupchat\""}. -{"It is not allowed to send private messages to the conference","Не дозволяється надсилати приватні повідомлення в конференцію"}. -{"It is not allowed to send private messages","Приватні повідомлення не дозволені"}. -{"Jabber Account Registration","Реєстрація Jabber-акаунту"}. +{"It is not allowed to send private messages to the conference","Не дозволяється надсилати приватні повідомлення в конференцію"}. {"Jabber ID","Jabber ID"}. -{"Jabber ID ~s is invalid","Jabber ID ~s недопустимий"}. -{"January","січня"}. -{"Join IRC channel","Приєднатися до каналу IRC"}. +{"January","Січня"}. +{"JID normalization denied by service policy","Створювати конференцію заборонено політикою служби"}. +{"JID normalization failed","Помилка нормалізації JID"}. +{"Joined MIX channels of ~ts","Приєднався до каналів MIX ~ts"}. +{"Joined MIX channels:","Приєднався до каналів MIX:"}. {"joins the room","увійшов(ла) в кімнату"}. -{"Join the IRC channel here.","Приєднатися до каналу IRC"}. -{"Join the IRC channel in this Jabber ID: ~s","Приєднатися до каналу IRC з Jabber ID: ~s"}. -{"July","липня"}. -{"June","червня"}. +{"July","Липня"}. +{"June","Червня"}. +{"Just created","Щойно створено"}. {"Last Activity","Останнє підключення"}. {"Last login","Останнє підключення"}. +{"Last message","Останнє повідомлення"}. {"Last month","За останній місяць"}. {"Last year","За останній рік"}. +{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Найменш значущі біти хеш-функції SHA-256 від тексту мають відповідати шістнадцятковій мітці."}. {"leaves the room","вийшов(ла) з кімнати"}. -{"Listened Ports at ","Відкриті порти на "}. -{"Listened Ports","Відкриті порти"}. -{"List of modules to start","Список завантажуваних модулів"}. -{"List of rooms","Перелік кімнат"}. -{"Low level update script","Низькорівневий сценарій поновлення"}. +{"List of users with hats","Список користувачів із капелюхами"}. +{"List users with hats","Список користувачів із капелюхами"}. +{"Logged Out","Вийшов із системи"}. +{"Logging","Журналювання"}. {"Make participants list public","Зробити список учасників видимим всім"}. {"Make room CAPTCHA protected","Зробити кімнату захищеною капчею"}. -{"Make room members-only","Кімната тільки для зареєтрованых учасників"}. +{"Make room members-only","Кімната тільки для зареєтрованих учасників"}. {"Make room moderated","Зробити кімнату модерованою"}. {"Make room password protected","Зробити кімнату захищеною паролем"}. {"Make room persistent","Зробити кімнату постійною"}. {"Make room public searchable","Зробити кімнату видимою всім"}. +{"Malformed username","Неправильне ім’я користувача"}. +{"MAM preference modification denied by service policy","Зміна налаштувань MAM відхилена через політики сервісу"}. {"March","березня"}. -{"Maximum Number of Occupants","Максимальна кількість учасників"}. -{"Max # of items to persist","Максимальне число збережених публікацій"}. +{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","Максимальна кількість елементів для зберігання, або max для встановлення не конкретизованого ліміту, окрім максимального, накладеного сервером"}. {"Max payload size in bytes","Максимальний розмір корисного навантаження в байтах"}. +{"Maximum file size","Макс. розмір файлу"}. +{"Maximum Number of History Messages Returned by Room","Максимальна кількість повідомлень історії на кімнату"}. +{"Maximum number of items to persist","Максимальна кількість елементів для збереження"}. +{"Maximum Number of Occupants","Максимальна кількість учасників"}. {"May","травня"}. {"Membership is required to enter this room","В цю конференцію можуть входити тільки її члени"}. -{"Members:","Члени:"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Запам'ятайте пароль, або запишіть його на папері, який треба зберегти у безпечному місці. У Jabber'і немає автоматизованих засобів відновлення пароля на той випадок, якщо ви його забудете."}. -{"Memory","Пам'ять"}. +{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Запам'ятайте свій пароль або запишіть його на папері та зберігайте у безпечному місці. У XMPP не існує автоматизованого способу відновлення паролю, якщо ви його забудете."}. +{"Mere Availability in XMPP (No Show Value)","Проста доступність у XMPP (без показу статусу)"}. {"Message body","Тіло повідомлення"}. +{"Message not found in forwarded payload","Повідомлення не знайдено в пересланому вмісті"}. +{"Messages from strangers are rejected","Повідомлення від незнайомців відхиляються"}. +{"Messages of type headline","Повідомлення типу \"заголовок\""}. +{"Messages of type normal","Повідомлення типу \"звичайні\""}. {"Middle Name","По-батькові"}. {"Minimum interval between voice requests (in seconds)","Мінімальний інтервал між голосовими запитами (в секундах)"}. {"Moderator privileges required","Необхідні права модератора"}. -{"moderators only","тільки модераторам"}. +{"Moderators Only","Тільки модераторам"}. {"Moderator","Модератор"}. -{"Modified modules","Змінені модулі"}. -{"Modules at ~p","Модулі на ~p"}. -{"Modules","Модулі"}. -{"Module","Модуль"}. +{"Module failed to handle the query","Модулю не вдалося обробити запит"}. {"Monday","Понеділок"}. {"Multicast","Мультікаст"}. +{"Multiple elements are not allowed by RFC6121","Кілька елементів не дозволені RFC6121"}. {"Multi-User Chat","Багато-користувальницький чат"}. -{"Name:","Назва:"}. {"Name","Назва"}. +{"Natural Language for Room Discussions","Розмовна мова для обговорень у кімнаті"}. +{"Natural-Language Room Name","Назва кімнати розмовною мовою"}. +{"Neither 'jid' nor 'nick' attribute found","Не знайдено ні атрибута \"jid\", ні \"nick\""}. +{"Neither 'role' nor 'affiliation' attribute found","Не знайдено ні атрибута \"role\", ні \"affiliation\""}. {"Never","Ніколи"}. {"New Password:","Новий Пароль:"}. +{"Nickname can't be empty","Псевдонім не може бути порожнім"}. {"Nickname Registration at ","Реєстрація псевдоніма на "}. {"Nickname ~s does not exist in the room","Псевдонім ~s в кімнаті відсутній"}. {"Nickname","Псевдонім"}. +{"No address elements found","Не знайдено елементів адреси"}. +{"No addresses element found","Не знайдено елемента адрес"}. +{"No 'affiliation' attribute found","Не знайдено атрибут \"affiliation\""}. +{"No available resource found","Не знайдено доступного ресурсу"}. {"No body provided for announce message","Тіло оголошення має бути непустим"}. -{"nobody","ніхто"}. +{"No child elements found","Не знайдено дочірніх елементів"}. +{"No data form found","Не знайдено форми даних"}. {"No Data","Немає даних"}. +{"No features available","Немає доступних функцій"}. +{"No element found","Вузол не знайдено"}. +{"No hook has processed this command","Жоден хук не обробив цю команду"}. +{"No info about last activity found","Не знайдено інформації про останню діяльність"}. +{"No 'item' element found","Елемент \"item\" не знайдено"}. +{"No items found in this query","У цьому запиті не знайдено жодного елемента"}. +{"No limit","Без обмежень"}. +{"No module is handling this query","Жоден модуль не може обробити цей запит"}. +{"No node specified","Вузол не вказано"}. +{"No 'password' found in data form","Не знайдено \"пароль\" у формі даних"}. +{"No 'password' found in this query","Не знайдено \"пароль\" у цьому запиті"}. +{"No 'path' found in data form","Не знайдено \"path\" у формі даних"}. +{"No pending subscriptions found","Не знайдено очікуваних рішення підписок"}. +{"No privacy list with this name found","Немає списку конфіденційності з такою назвою"}. +{"No private data found in this query","Приватних даних у цьому запиті не знайдено"}. +{"No running node found","Не знайдено запущеного вузла"}. +{"No services available","Немає доступних сервісів"}. +{"No statistics found for this item","Для цього елемента статистичні дані не знайдено"}. +{"No 'to' attribute found in the invitation","У запрошенні не знайдено атрибут \"до\""}. +{"Nobody","Ніхто"}. +{"Node already exists","Вузол уже існує"}. {"Node ID","ID вузла"}. +{"Node index not found","Індекс вузла не знайдено"}. {"Node not found","Вузол не знайдено"}. {"Node ~p","Вузол ~p"}. +{"Nodeprep has failed","Не вдалося виконати Nodeprep"}. {"Nodes","Вузли"}. -{"No limit","Без обмежень"}. +{"Node","Вузол"}. {"None","Немає"}. -{"No resource provided","Не вказаний ресурс"}. -{"Not Found","не знайдено"}. +{"Not allowed","Не дозволяється"}. +{"Not Found","Не знайдено"}. +{"Not subscribed","Не підписаний"}. {"Notify subscribers when items are removed from the node","Повідомляти абонентів про видалення публікацій із збірника"}. {"Notify subscribers when the node configuration changes","Повідомляти абонентів про зміни в конфігурації збірника"}. {"Notify subscribers when the node is deleted","Повідомляти абонентів про видалення збірника"}. -{"November","листопада"}. +{"November","Листопада"}. +{"Number of answers required","Кількість необхідних відповідей"}. {"Number of occupants","Кількість присутніх"}. +{"Number of Offline Messages","Кількість автономних повідомлень"}. {"Number of online users","Кількість підключених користувачів"}. {"Number of registered users","Кількість зареєстрованих користувачів"}. +{"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","Кількість секунд, після яких автоматично видаляти елементи, або `max` для встановлення невизначеного ліміту, окрім максимального, накладеного сервером."}. +{"Occupants are allowed to invite others","Учасникам дозволено запрошувати інших"}. +{"Occupants are allowed to query others","Учасникам дозволено ставити запитання іншим"}. +{"Occupants May Change the Subject","Учасникам дозволено змінювати тему"}. {"October","грудня"}. -{"Offline Messages:","Офлайнові повідомлення:"}. -{"Offline Messages","Офлайнові повідомлення"}. {"OK","Продовжити"}. {"Old Password:","Старий пароль:"}. -{"Online Users:","Підключені користувачі:"}. {"Online Users","Підключені користувачі"}. {"Online","Підключений"}. +{"Only collection node owners may associate leaf nodes with the collection","Лише власники вузлів колекції можуть асоціювати листові вузли з колекцією"}. {"Only deliver notifications to available users","Доставляти повідомленнями тільки доступним користувачам"}. +{"Only or tags are allowed","Дозволені лише теги або "}. +{"Only element is allowed in this query","У цьому запиті дозволено лише елемент "}. {"Only members may query archives of this room","Тільки модератори можуть запитувати архіви цієї кімнати"}. {"Only moderators and participants are allowed to change the subject in this room","Тільки модератори та учасники можуть змінювати тему в цій кімнаті"}. {"Only moderators are allowed to change the subject in this room","Тільки модератори можуть змінювати тему в цій кімнаті"}. +{"Only moderators are allowed to retract messages","Тільки модераторам дозволено відкликати повідомлення"}. {"Only moderators can approve voice requests","Тільки модератори можуть схвалювати голосові запити"}. {"Only occupants are allowed to send messages to the conference","Тільки присутнім дозволяється надсилати повідомленняя в конференцію"}. {"Only occupants are allowed to send queries to the conference","Тільки присутнім дозволяється відправляти запити в конференцію"}. +{"Only publishers may publish","Тільки видавці можуть публікувати"}. {"Only service administrators are allowed to send service messages","Тільки адміністратор сервісу може надсилати службові повідомлення"}. -{"Options","Параметри"}. +{"Only those on a whitelist may associate leaf nodes with the collection","Лише ті, хто входить до білого списку, можуть асоціювати листові вузли з колекцією"}. +{"Only those on a whitelist may subscribe and retrieve items","Тільки ті, хто є в білому списку, можуть підписуватися та отримувати елементи"}. {"Organization Name","Назва організації"}. {"Organization Unit","Відділ організації"}. -{"Outgoing s2s Connections:","Вихідні s2s-з'єднання:"}. +{"Other Modules Available:","Інші доступні модулі:"}. {"Outgoing s2s Connections","Вихідні s2s-з'єднання"}. {"Owner privileges required","Необхідні права власника"}. -{"Packet","Пакет"}. +{"Packet relay is denied by service policy","Пересилання пакетів заборонене політикою сервісу"}. +{"Participant ID","ID учасника"}. {"Participant","Учасник"}. -{"Password ~b","Пароль ~b"}. -{"Password Verification:","Перевірка Пароля:"}. {"Password Verification","Перевірка Пароля"}. -{"Password:","Пароль:"}. +{"Password Verification:","Перевірка Пароля:"}. {"Password","Пароль"}. +{"Password:","Пароль:"}. {"Path to Dir","Шлях до директорії"}. {"Path to File","Шлях до файла"}. -{"Pending","Очікування"}. -{"Period: ","Період"}. -{"Permanent rooms","Постійні кімнати"}. -{"Persist items to storage","Зберегати публікації до сховища"}. -{"Ping","Пінг"}. -{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Зауважте, що ця опція відповідає за резервне копіювання тільки вбудованної бази даних Mnesia. Якщо Ви також використовуєте інше сховище для даних (наприклад за допомогою модуля ODBC), то його резервне копіювання потрібно робити окремо."}. -{"Please specify file name.","Будь ласка вкажіть ім'я файлу."}. -{"Please specify file size.","Будь ласка вкажіть розмір файлу."}. -{"Please, wait for a while before sending new voice request","Будь ласка, почекайте деякий час перед тим, як знову відправляти голосовий запит"}. -{"Pong","Понг"}. -{"Port ~b","Порт ~b"}. -{"Port","Порт"}. -{"Present real Jabber IDs to","Зробити реальні Jabber ID учасників видимими"}. +{"Payload semantic type information","Інформація про тип вмісту даних"}. +{"Period: ","Період: "}. +{"Persist items to storage","Зберігати елементи в сховищі"}. +{"Persistent","Тривалий"}. +{"Ping query is incorrect","Запит ping некоректний"}. +{"Ping","Затримка (пінг)"}. +{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Зверніть увагу, що ці налаштування створюють резервну копію лише вбудованої бази даних Mnesia. Якщо ви використовуєте модуль ODBC, вам також потрібно окремо створити резервну копію вашої SQL-бази даних."}. +{"Please, wait for a while before sending new voice request","Будь ласка, зачекайте трохи перед тим, як відправляти новий голосовий запит"}. +{"Pong","Pong (відповідь на Ping, підтвердження взаємодії)"}. +{"Possessing 'ask' attribute is not allowed by RFC6121","Наявність атрибуту 'ask' не дозволена згідно з RFC6121"}. +{"Present real Jabber IDs to","Показувати реальні Jabber ID для"}. +{"Previous session not found","Попередню сесію не знайдено"}. +{"Previous session PID has been killed","PID попередньої сесії був зупинений"}. +{"Previous session PID has exited","PID попередньої сесії завершив роботу"}. +{"Previous session PID is dead","PID попередньої сесії більше не активний"}. +{"Previous session timed out","Час попередньої сесії вичерпано"}. {"private, ","приватна, "}. -{"Protocol","Протокол"}. +{"Public","Публічний"}. +{"Publish model","Модель публікації"}. {"Publish-Subscribe","Публікація-Підписка"}. -{"PubSub subscriber request","Запит на підписку PubSub"}. -{"Purge all items when the relevant publisher goes offline","Видалити всі елементи, коли особа, що їх опублікувала, вимикається від мережі"}. -{"Queries to the conference members are not allowed in this room","Запити до користувачів в цій конференції заборонені"}. -{"RAM and disc copy","ОЗП та диск"}. -{"RAM copy","ОЗП"}. -{"Raw","необроблений формат"}. -{"Really delete message of the day?","Насправді, видалити повідомлення дня?"}. +{"PubSub subscriber request","Запит підписника PubSub"}. +{"Purge all items when the relevant publisher goes offline","Видалити всі елементи, коли автор публікації виходить офлайн"}. +{"Push record not found","Push-запис не знайдено"}. +{"Queries to the conference members are not allowed in this room","Запити до учасників конференції не дозволені в цій кімнаті"}. +{"Query to another users is forbidden","Запит до інших користувачів заборонено"}. +{"RAM and disc copy","Копія оперативної пам'яті та диску"}. +{"RAM copy","Копія оперативної пам'яті"}. +{"Really delete message of the day?","Дійсно видалити повідомлення дня?"}. +{"Receive notification from all descendent nodes","Отримувати сповіщення від усіх підпорядкованих вузлів"}. +{"Receive notification from direct child nodes only","Отримувати сповіщення лише від прямих дочірніх вузлів"}. +{"Receive notification of new items only","Отримувати сповіщення лише про нові товари"}. +{"Receive notification of new nodes only","Отримувати сповіщення лише про нові вузли"}. {"Recipient is not in the conference room","Адресата немає в конференції"}. -{"Register a Jabber account","Зареєструвати Jabber-акаунт"}. -{"Registered nicknames","Зареєстровані імена"}. -{"Registered Users:","Зареєстровані користувачі:"}. -{"Registered Users","Зареєстровані користувачі"}. +{"Register an XMPP account","Зареєструвати XMPP-запис"}. {"Register","Реєстрація"}. -{"Registration in mod_irc for ","Реєстрація в mod_irc для "}. -{"Remote copy","не зберігаеться локально"}. -{"Remove All Offline Messages","Видалити всі офлайнові повідомлення"}. +{"Remote copy","Віддалене копіювання"}. +{"Remove a hat from a user","Зняти шапку з користувача"}. {"Remove User","Видалити користувача"}. -{"Remove","Видалити"}. {"Replaced by new connection","Замінено новим з'єднанням"}. +{"Request has timed out","Час очікування запиту минув"}. +{"Request is ignored","Запит ігнорується"}. +{"Requested role","Запитана роль"}. {"Resources","Ресурси"}. {"Restart Service","Перезапустити Сервіс"}. -{"Restart","Перезапустити"}. {"Restore Backup from File at ","Відновлення з резервної копії на "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Відновити з бінарної резервної копії при наступному запуску (потребує менше пам'яті):"}. {"Restore binary backup immediately:","Відновити з бінарної резервної копії негайно:"}. {"Restore plain text backup immediately:","Відновити з текстової резервної копії негайно:"}. {"Restore","Відновлення з резервної копії"}. +{"Result","Результат"}. +{"Roles and Affiliations that May Retrieve Member List","Ролі та зв’язки, які можуть отримати список учасників"}. {"Roles for which Presence is Broadcasted","Ролі для яких поширюється наявність"}. +{"Roles that May Send Private Messages","Ролі, що можуть надсилати приватні повідомлення"}. {"Room Configuration","Конфігурація кімнати"}. {"Room creation is denied by service policy","Створювати конференцію заборонено політикою служби"}. {"Room description","Опис кімнати"}. {"Room Occupants","Учасники кімнати"}. +{"Room terminates","Кімната припиняється"}. {"Room title","Назва кімнати"}. {"Roster groups allowed to subscribe","Дозволені для підписки групи ростера"}. -{"Roster of ","Ростер користувача "}. {"Roster size","Кількість контактів"}. -{"Roster","Ростер"}. -{"RPC Call Error","Помилка виклику RPC"}. {"Running Nodes","Працюючі вузли"}. -{"~s access rule configuration","Конфігурація правила доступу ~s"}. +{"~s invites you to the room ~s","~s запрошує вас до кімнати ~s"}. {"Saturday","Субота"}. -{"Script check","Перевірка сценарію"}. +{"Search from the date","Пошук від дати"}. {"Search Results for ","Результати пошуку в "}. +{"Search the text","Знайдіть текст"}. +{"Search until the date","Пошук до дати"}. {"Search users in ","Пошук користувачів в "}. {"Send announcement to all online users on all hosts","Надіслати сповіщення всім підключеним користувачам на всіх віртуальних серверах"}. {"Send announcement to all online users","Надіслати сповіщення всім підключеним користувачам"}. {"Send announcement to all users on all hosts","Надіслати сповіщення до усіх користувачів на усіх хостах"}. {"Send announcement to all users","Надіслати сповіщення всім користувачам"}. -{"September","вересня"}. -{"Server ~b","Сервер ~b"}. +{"September","Вересня"}. {"Server:","Сервер:"}. -{"Server","Сервер:"}. +{"Service list retrieval timed out","Час очікування отримання списку послуг минув"}. +{"Session state copying timed out","Час очікування копіювання стану сеансу минув"}. {"Set message of the day and send to online users","Встановити повідомлення дня та надіслати його підключеним користувачам"}. {"Set message of the day on all hosts and send to online users","Встановити повідомлення дня на всіх хостах та надійслати його підключеним користувачам"}. {"Shared Roster Groups","Спільні групи контактів"}. {"Show Integral Table","Показати інтегральну таблицю"}. +{"Show Occupants Join/Leave","Показати мешканцям приєднатися/вийти"}. {"Show Ordinary Table","Показати звичайну таблицю"}. {"Shut Down Service","Вимкнути Сервіс"}. -{"~s invites you to the room ~s","~s запрошує вас до кімнати ~s"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Деякі Jabber-клієнти можуть зберігати пароль на вашому комп'ютері. Користуйтесь цією функцією тільки у тому випадку, якщо вважаєте її безпечною."}. +{"SOCKS5 Bytestreams","Потоки байтів SOCKS5"}. +{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Деякі клієнти XMPP можуть зберігати ваш пароль на комп’ютері, але ви повинні робити це лише на своєму персональному комп’ютері з міркувань безпеки."}. +{"Sources Specs:","Специфікації джерел:"}. {"Specify the access model","Визначити модель доступу"}. {"Specify the event message type","Вкажіть тип повідомлень зі сповіщеннями про події"}. {"Specify the publisher model","Умови публікації"}. -{"~s's Offline Messages Queue","Черга офлайнових повідомлень ~s"}. -{"Start Modules at ","Запуск модулів на "}. -{"Start Modules","Запуск модулів"}. -{"Start","Запустити"}. -{"Statistics of ~p","Статистика вузла ~p"}. -{"Statistics","Статистика"}. -{"Stop Modules at ","Зупинка модулів на "}. -{"Stop Modules","Зупинка модулів"}. +{"Stanza id is not valid","Ідентифікатор строфи недійсний"}. +{"Stanza ID","ID кімнати"}. +{"Statically specify a replyto of the node owner(s)","Статично вкажіть відповідь власника(ів) вузла"}. {"Stopped Nodes","Зупинені вузли"}. -{"Stop","Зупинити"}. -{"Storage Type","Тип таблиці"}. {"Store binary backup:","Зберегти бінарну резервну копію:"}. {"Store plain text backup:","Зберегти текстову резервну копію:"}. +{"Stream management is already enabled","Керування потоком уже ввімкнено"}. +{"Stream management is not enabled","Керування потоком не ввімкнено"}. {"Subject","Тема"}. {"Submitted","Відправлено"}. -{"Submit","Відправити"}. {"Subscriber Address","Адреса абонента"}. -{"Subscription","Підписка"}. +{"Subscribers may publish","Підписники можуть публікувати"}. +{"Subscription requests must be approved and only subscribers may retrieve items","Запити на підписку мають бути схвалені, і лише передплатники можуть отримувати елементи"}. +{"Subscriptions are not allowed","Підписка заборонена"}. {"Sunday","Неділя"}. +{"Text associated with a picture","Текст, пов'язаний із зображенням"}. +{"Text associated with a sound","Текст, пов'язаний зі звуком"}. +{"Text associated with a video","Текст, пов’язаний із відео"}. +{"Text associated with speech","Текст, пов'язаний з мовленням"}. {"That nickname is already in use by another occupant","Псевдонім зайнято кимось з присутніх"}. {"That nickname is registered by another person","Псевдонім зареєстровано кимось іншим"}. -{"The CAPTCHA is valid.","Перевірку капчею закінчено успішно"}. +{"The account already exists","Обліковий запис уже існує"}. +{"The account was not unregistered","Обліковий запис не було видалено"}. +{"The body text of the last received message","Основний текст останнього отриманого повідомлення"}. +{"The CAPTCHA is valid.","Перевірку CAPTCHA успішно завершено."}. {"The CAPTCHA verification has failed","Перевірку капчею не пройдено"}. +{"The captcha you entered is wrong","Captcha, яку ви ввели, неправильна"}. +{"The child nodes (leaf or collection) associated with a collection","Дочірні вузли (лист або колекція), пов’язані з колекцією"}. {"The collections with which a node is affiliated","Колекція, до якої входить вузол"}. +{"The DateTime at which a leased subscription will end or has ended","DateTime, коли орендована підписка закінчиться або закінчилася"}. +{"The datetime when the node was created","Дата і час створення вузла"}. +{"The default language of the node","Мова вузла за замовчуванням"}. +{"The feature requested is not supported by the conference","Запитана функція не підтримується конференцією"}. +{"The JID of the node creator","JID творця вузла"}. +{"The JIDs of those to contact with questions","JID тих, до кого можна звернутися із запитаннями"}. +{"The JIDs of those with an affiliation of owner","JID тих, хто є афілійованим власником"}. +{"The JIDs of those with an affiliation of publisher","JID тих, хто пов’язаний із видавцем"}. +{"The list of all online users","Список усіх онлайн-користувачів"}. +{"The list of all users","Список усіх користувачів"}. +{"The list of JIDs that may associate leaf nodes with a collection","Список JID, які можуть пов’язувати листові вузли з колекцією"}. +{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","Максимальна кількість дочірніх вузлів, які можна пов’язати з колекцією, або `max` для відсутності конкретного обмеження, окрім максимального накладеного сервером"}. +{"The minimum number of milliseconds between sending any two notification digests","Мінімальна кількість мілісекунд між надсиланням будь-яких двох дайджестів сповіщень"}. +{"The name of the node","Ім'я вузла"}. +{"The node is a collection node","Вузол є вузлом колекції"}. +{"The node is a leaf node (default)","Вузол є листовим вузлом (за замовчуванням)"}. +{"The NodeID of the relevant node","NodeID відповідного вузла"}. +{"The number of pending incoming presence subscription requests","Кількість вхідних запитів на підписку про присутність, що очікують на розгляд"}. +{"The number of subscribers to the node","Кількість передплатників вузла"}. +{"The number of unread or undelivered messages","Кількість непрочитаних або недоставлених повідомлень"}. +{"The password contains unacceptable characters","Пароль містить неприйнятні символи"}. {"The password is too weak","Пароль надто простий"}. -{"the password is","пароль:"}. -{"The password of your Jabber account was successfully changed.","Пароль вашого Jabber-акаунту був успішно змінений."}. +{"the password is","паролем є"}. +{"The password of your XMPP account was successfully changed.","Пароль вашого облікового запису XMPP успішно змінено."}. +{"The password was not changed","Пароль не змінено"}. +{"The passwords are different","Паролі різні"}. +{"The presence states for which an entity wants to receive notifications","Стан присутності, для якого сутність хоче отримувати сповіщення"}. +{"The query is only allowed from local users","Запит дозволено лише від локальних користувачів"}. +{"The query must not contain elements","Запит не повинен містити елементів "}. +{"The room subject can be modified by participants","Тема кімнати може бути змінена учасниками"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","Інформація про семантичний тип даних у вузлі, зазвичай визначена простором імен корисного навантаження (якщо є)"}. +{"The sender of the last received message","Відправник останнього отриманого повідомлення"}. +{"The stanza MUST contain only one element, one element, or one element","Строфа ПОВИННА містити лише один елемент , один елемент або один елемент "}. +{"The subscription identifier associated with the subscription request","Ідентифікатор підписки, пов’язаний із запитом на підписку"}. +{"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","URL-адреса перетворення XSL, яке можна застосувати до корисних даних, щоб створити відповідний елемент тіла повідомлення."}. +{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","URL-адреса трансформації XSL, яку можна застосувати до формату корисного навантаження, щоб створити дійсний результат форм даних, який клієнт міг би відобразити за допомогою загального механізму візуалізації форм даних"}. {"There was an error changing the password: ","Помилка при зміні пароля: "}. -{"There was an error creating the account: ","Помилка при створенні акаунту:"}. +{"There was an error creating the account: ","Помилка при створенні облікового запису: "}. {"There was an error deleting the account: ","Помилка при видаленні акаунту: "}. -{"This IP address is blacklisted in ~s","Ця IP адреса у чорному списку ~s"}. -{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Регістр не має значення: \"МАША\" та \"маша\" буде сприйматися як одне й те саме ім'я."}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Тут ви можете зареєструвати обліковий запис Jabber на цьому сервері. Ваш JID (ідентифікатор Jabber) матиме вигляд \"користувач@сервер\". Щоб вірно заповнити поля нижче, будь ласка, уважно читайте інструкції до них."}. -{"This page allows to unregister a Jabber account in this Jabber server.","Ця сторінка дозволяє видалити свій акаунт з Jabber-сервера."}. +{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Тут не враховується регістр: Макбет – це те саме, що Макбет і Макбет."}. +{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Ця сторінка дозволяє зареєструвати обліковий запис XMPP на цьому сервері XMPP. Ваш JID (Jabber ID) матиме такий вигляд: ім’я користувача@сервер. Уважно прочитайте інструкції, щоб правильно заповнити поля."}. +{"This page allows to unregister an XMPP account in this XMPP server.","Ця сторінка дозволяє видалити свій обліковий запис з XMPP-сервера."}. +{"This room is not anonymous","Ця кімната не анонімна"}. +{"This service can not process the address: ~s","Ця служба не може обробити адресу: ~s"}. {"Thursday","Четвер"}. {"Time delay","Час затримки"}. -{"Time","Час"}. +{"Timed out waiting for stream resumption","Час очікування на відновлення потоку закінчився"}. +{"To register, visit ~s","Щоб зареєструватися, відвідайте ~s"}. +{"To ~ts","До ~ts"}. +{"Token TTL","Токен TTL"}. +{"Too many active bytestreams","Надто багато активних потоків байтів"}. {"Too many CAPTCHA requests","Надто багато CAPTCHA-запитів"}. -{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Забагато (~p) помилок авторизації з цієї IP адреси (~s). Адресу буде розблоковано о ~s UTC"}. +{"Too many child elements","Надто багато дочірніх елементів"}. +{"Too many elements","Надто багато елементів "}. +{"Too many elements","Надто багато елементів "}. +{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Забагато (~p) помилок авторизації з цієї IP адреси (~s). Адресу буде розблоковано о ~s UTC"}. +{"Too many receiver fields were specified","Вказано забагато одержувачів"}. {"Too many unacked stanzas","Занадто багато пакетів без відповідей"}. -{"To ~s","До ~s"}. -{"Total rooms","Всього кімнат"}. -{"To","Кому"}. +{"Too many users in this conference","Надто багато користувачів у цій конференції"}. {"Traffic rate limit is exceeded","Швидкість передачі інформації було перевищено"}. -{"Transactions Aborted:","Транзакції відмінені:"}. -{"Transactions Committed:","Транзакції завершені:"}. -{"Transactions Logged:","Транзакції запротокольовані:"}. -{"Transactions Restarted:","Транзакції перезапущені:"}. +{"~ts's MAM Archive","Архів МАМ ~ts"}. +{"~ts's Offline Messages Queue","Черга автономних повідомлень ~ts"}. {"Tuesday","Вівторок"}. {"Unable to generate a CAPTCHA","Нема можливості згенерувати капчу"}. +{"Unable to register route on existing local domain","Неможливо зареєструвати маршрут на наявному локальному домені"}. {"Unauthorized","Не авторизовано"}. -{"Unregister a Jabber account","Видалити Jabber-акаунт"}. -{"Unregister","Видалити"}. +{"Unexpected action","Несподівана дія"}. +{"Unexpected error condition: ~p","Умова несподіваної помилки: ~p"}. +{"Uninstall","Видалити"}. +{"Unregister an XMPP account","Видалити обліковий запис XMPP"}. +{"Unregister","Скасувати реєстрацію"}. +{"Unsupported element","Непідтримуваний елемент "}. +{"Unsupported version","Непідтримувана версія"}. {"Update message of the day (don't send)","Оновити повідомлення дня (не надсилати)"}. {"Update message of the day on all hosts (don't send)","Оновити повідомлення дня на всіх хостах (не надсилати)"}. -{"Update plan","План оновлення"}. -{"Update ~p","Оновлення ~p"}. -{"Update script","Сценарій поновлення"}. -{"Update","Обновити"}. -{"Uptime:","Час роботи:"}. -{"Use of STARTTLS required","Ви мусите використовувати STARTTLS"}. +{"Update specs to get modules source, then install desired ones.","Оновіть специфікації, щоб отримати джерело модулів, а потім встановіть потрібні."}. +{"Update Specs","Оновити характеристики"}. +{"Updating the vCard is not supported by the vCard storage backend","Оновлення vCard не підтримується системою зберігання vCard"}. +{"Upgrade","Оновлення"}. +{"URL for Archived Discussion Logs","URL-адреса для журналів архівних обговорень"}. +{"User already exists","Користувач уже існує"}. {"User JID","JID Користувача"}. +{"User (jid)","Користувач (jid)"}. {"User Management","Управління Користувачами"}. +{"User not allowed to perform an IQ set on another user's vCard.","Користувачеві заборонено виконувати тест IQ на vCard іншого користувача."}. +{"User removed","Користувача видалено"}. +{"User session not found","Сеанс користувача не знайдено"}. +{"User session terminated","Сеанс користувача припинено"}. +{"User ~ts","Користувач ~ts"}. {"Username:","Ім'я користувача:"}. {"Users are not allowed to register accounts so quickly","Користувачам не дозволено так часто реєструвати облікові записи"}. {"Users Last Activity","Статистика останнього підключення користувачів"}. -{"User ~s","Користувач ~s"}. {"Users","Користувачі"}. {"User","Користувач"}. -{"Validate","Затвердити"}. +{"Value 'get' of 'type' attribute is not allowed","Значення 'get' атрибута 'type' не дозволене"}. +{"Value of '~s' should be boolean","Значення \"~s\" має бути логічним"}. +{"Value of '~s' should be datetime string","Значення \"~s\" має бути рядком дати і часу"}. +{"Value of '~s' should be integer","Значення \"~s\" має бути цілим числом"}. +{"Value 'set' of 'type' attribute is not allowed","Значення 'set' атрибута 'type' не допускається"}. {"vCard User Search","Пошук користувачів по vCard"}. -{"Virtual Hosts","віртуальні хости"}. +{"View joined MIX channels","Перегляд приєднаних каналів MIX"}. +{"Virtual Hosts","Віртуальні хости"}. {"Visitors are not allowed to change their nicknames in this room","Відвідувачам не дозволяється змінювати псевдонім в цій кімнаті"}. {"Visitors are not allowed to send messages to all occupants","Відвідувачам не дозволяється надсилати повідомлення всім присутнім"}. {"Visitor","Відвідувач"}. {"Voice requests are disabled in this conference","Голосові запити відключені в цій конференції"}. {"Voice request","Голосовий запит"}. +{"Web client which allows to join the room anonymously","Веб-клієнт, який дозволяє анонімно приєднатися до кімнати"}. {"Wednesday","Середа"}. +{"When a new subscription is processed and whenever a subscriber comes online","Коли обробляється нова підписка та щоразу, коли абонент виходить в Інтернет"}. +{"When a new subscription is processed","Під час обробки нової підписки"}. {"When to send the last published item","Коли надсилати останній опублікований елемент"}. +{"Whether an entity wants to receive an XMPP message body in addition to the payload format","Чи бажає суб’єкт отримувати тіло повідомлення XMPP на додаток до формату корисного навантаження"}. +{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","Чи бажає організація отримувати дайджести (агрегації) сповіщень чи всі сповіщення окремо"}. +{"Whether an entity wants to receive or disable notifications","Чи бажає суб’єкт отримувати або вимикати сповіщення"}. +{"Whether owners or publisher should receive replies to items","Чи повинні власники або видавець отримувати відповіді на елементи"}. +{"Whether the node is a leaf (default) or a collection","Чи є вузол листом (типово) чи колекцією"}. {"Whether to allow subscriptions","Дозволяти підписку"}. -{"You can later change your password using a Jabber client.","Пізніше можна змінити пароль через Jabber-клієнт."}. +{"Whether to make all subscriptions temporary, based on subscriber presence","Чи робити всі підписки тимчасовими, залежно від присутності читача"}. +{"Whether to notify owners about new subscribers and unsubscribes","Чи повідомляти власників про нових читачів та їх втрату"}. +{"Who can send private messages","Хто може надсилати приватні повідомлення"}. +{"Who may associate leaf nodes with a collection","Хто може пов’язувати листові вузли з колекцією"}. +{"Wrong parameters in the web formulary","Неправильні параметри у веб-формі"}. +{"Wrong xmlns","Неправильний xmlns"}. +{"XMPP Account Registration","Реєстрація облікового запису XMPP"}. +{"XMPP Domains","Домени XMPP"}. +{"XMPP Show Value of Away","XMPP Показати значення Away"}. +{"XMPP Show Value of Chat","XMPP Показати значення чату"}. +{"XMPP Show Value of DND (Do Not Disturb)","XMPP Показати значення DND (Не турбувати)"}. +{"XMPP Show Value of XA (Extended Away)","XMPP Показати значення XA (розширено)"}. +{"XMPP URI of Associated Publish-Subscribe Node","XMPP URI-адреса асоційованого вузла публікацій-підписок"}. +{"You are being removed from the room because of a system shutdown","Ви будете видалені з кімнати через завершення роботи системи"}. +{"You are not allowed to send private messages","Ви не можете надсилати приватні повідомлення"}. +{"You are not joined to the channel","Ви не приєднані до каналу"}. +{"You can later change your password using an XMPP client.","Пізніше ви можете змінити пароль за допомогою XMPP-клієнта."}. {"You have been banned from this room","Вам заборонено входити в цю конференцію"}. +{"You have joined too many conferences","Ви приєднані до надто великої кількості конференцій"}. {"You must fill in field \"Nickname\" in the form","Вам необхідно заповнити поле \"Псевдонім\" у формі"}. {"You need a client that supports x:data and CAPTCHA to register","Для реєстрації псевдоніму необхідно використовувати клієнт з підтримкою x:data"}. {"You need a client that supports x:data to register the nickname","Для реєстрації псевдоніму необхідно використовувати клієнт з підтримкою x:data"}. -{"You need an x:data capable client to configure mod_irc settings","Для налагодження параметрів mod_irc необхідно використовувати клієнт, що має підтримку x:data"}. -{"You need an x:data capable client to configure room","Для конфігурування кімнати потрібно використовувати клієнт з підтримкою x:data"}. {"You need an x:data capable client to search","Для пошуку необхідний клієнт із підтримкою x:data"}. {"Your active privacy list has denied the routing of this stanza.","Маршрутизація цієї строфи була відмінена активним списком приватності."}. {"Your contact offline message queue is full. The message has been discarded.","Черга повідомлень, що не були доставлені, переповнена. Повідомлення не було збережено."}. -{"Your Jabber account was successfully created.","Ваш Jabber-акаунт було успішно створено."}. -{"Your Jabber account was successfully deleted.","Ваш Jabber-акаунт було успішно видалено."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","Ваші повідомлення до ~s блокуються. Для розблокування відвідайте ~s"}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Ваші повідомлення до ~s блокуються. Для розблокування відвідайте ~s"}. +{"Your XMPP account was successfully registered.","Ваш обліковий запис XMPP успішно зареєстровано."}. +{"Your XMPP account was successfully unregistered.","Ваш обліковий запис XMPP успішно видалено."}. +{"You're not allowed to create nodes","Вам заборонено створювати вузли"}. diff --git a/priv/msgs/uk.po b/priv/msgs/uk.po deleted file mode 100644 index 61506ab2e..000000000 --- a/priv/msgs/uk.po +++ /dev/null @@ -1,1936 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: 2.1.0-alpha\n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: Konstantin Khomoutov \n" -"Language-Team: \n" -"Language: uk\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Ukrainian (українська)\n" -"X-Additional-Translator: Oleg Deordiev\n" -"X-Additional-Translator: Ruslan Rakhmanin\n" -"X-Additional-Translator: Stoune\n" -"X-Additional-Translator: Sergei Golovan\n" -"X-Generator: Poedit 1.8.6\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "Ви мусите використовувати STARTTLS" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Не вказаний ресурс" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Замінено новим з'єднанням" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "вигнали з кімнати" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "Маршрутизація цієї строфи була відмінена активним списком приватності." - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "Занадто багато пакетів без відповідей" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Введіть текст, що ви бачите" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "Ваші повідомлення до ~s блокуються. Для розблокування відвідайте ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "Якщо ви не бачите зображення капчі, перейдіть за за цією адресою." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "Адреса капчі" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "Перевірку капчею закінчено успішно" - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Користувач" - -#: ejabberd_oauth.erl:256 -msgid "Server" -msgstr "Сервер:" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Пароль" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "Прийняти" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "Не авторизовано" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "Веб-інтерфейс Адміністрування ejabberd" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Адміністрування" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Списки керування доступом" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Відправлено" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Неправильний формат" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Відправити" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "необроблений формат" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Видалити виділені" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Правила доступу" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "Конфігурація правила доступу ~s" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "віртуальні хости" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Користувачі" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 mod_configure.erl:524 -msgid "Online Users" -msgstr "Підключені користувачі" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Статистика останнього підключення користувачів" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Період" - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "За останній місяць" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "За останній рік" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "Вся статистика" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Показати звичайну таблицю" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Показати інтегральну таблицю" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Статистика" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "не знайдено" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Вузол не знайдено" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Додати" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Хост" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Зареєстровані користувачі" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Додати користувача" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Офлайнові повідомлення" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Останнє підключення" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Ніколи" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Підключений" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Зареєстровані користувачі:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Підключені користувачі:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Вихідні s2s-з'єднання:" - -#: ejabberd_web_admin.erl:1559 -msgid "Incoming s2s Connections:" -msgstr "Вхідні s2s-з'єднання:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Немає" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Змінити пароль" - -#: ejabberd_web_admin.erl:1673 -msgid "User ~s" -msgstr "Користувач ~s" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Підключені ресурси:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Пароль:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Видалити користувача" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Немає даних" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Вузли" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Працюючі вузли" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Зупинені вузли" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -msgid "Node ~p" -msgstr "Вузол ~p" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "База даних" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Резервне копіювання" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Відкриті порти" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Обновити" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Перезапустити" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Зупинити" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Модулі" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "Помилка виклику RPC" - -#: ejabberd_web_admin.erl:1917 -msgid "Database Tables at ~p" -msgstr "Таблиці бази даних на ~p" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Назва" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Тип таблиці" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Елементи" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Пам'ять" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Помилка" - -#: ejabberd_web_admin.erl:1955 -msgid "Backup of ~p" -msgstr "Резервне копіювання ~p" - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Зауважте, що ця опція відповідає за резервне копіювання тільки вбудованної " -"бази даних Mnesia. Якщо Ви також використовуєте інше сховище для даних " -"(наприклад за допомогою модуля ODBC), то його резервне копіювання потрібно " -"робити окремо." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Зберегти бінарну резервну копію:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "Продовжити" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Відновити з бінарної резервної копії негайно:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"Відновити з бінарної резервної копії при наступному запуску (потребує менше " -"пам'яті):" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Зберегти текстову резервну копію:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Відновити з текстової резервної копії негайно:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "Імпорт даних користовучів з файлу PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "Експорт даних всіх користувачів сервера до файлу PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "Експорт даних користувачів домена до файлу PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "Експорт усіх таблиць, як SQL запити, у файл" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Імпорт користувачів з файла спула jabberd14:" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Імпорт користувачів з діректорії спула jabberd14:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Відкриті порти на " - -#: ejabberd_web_admin.erl:2144 -msgid "Modules at ~p" -msgstr "Модулі на ~p" - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "Статистика вузла ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Час роботи:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "Процесорний час:" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Транзакції завершені:" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Транзакції відмінені:" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Транзакції перезапущені:" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Транзакції запротокольовані:" - -#: ejabberd_web_admin.erl:2243 -msgid "Update ~p" -msgstr "Оновлення ~p" - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "План оновлення" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Змінені модулі" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Сценарій поновлення" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Низькорівневий сценарій поновлення" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Перевірка сценарію" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Порт" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Протокол" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Модуль" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Параметри" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Запустити" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Команди" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Пінг" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Понг" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "Насправді, видалити повідомлення дня?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Тема" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Тіло повідомлення" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "Тіло оголошення має бути непустим" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Сповіщення" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Надіслати сповіщення всім користувачам" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Надіслати сповіщення до усіх користувачів на усіх хостах" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Надіслати сповіщення всім підключеним користувачам" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "" -"Надіслати сповіщення всім підключеним користувачам на всіх віртуальних " -"серверах" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Встановити повідомлення дня та надіслати його підключеним користувачам" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" -"Встановити повідомлення дня на всіх хостах та надійслати його підключеним " -"користувачам" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Оновити повідомлення дня (не надсилати)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "Оновити повідомлення дня на всіх хостах (не надсилати)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Видалити повідомлення дня" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Видалити повідомлення дня на усіх хостах" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Конфігурація" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Запуск модулів" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Зупинка модулів" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Відновлення з резервної копії" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Копіювання в текстовий файл" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Імпорт з файла" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Імпорт з директорії" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Перезапустити Сервіс" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Вимкнути Сервіс" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Видалити Користувача" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Закінчити Сеанс Користувача" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Отримати Пароль Користувача" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Змінити Пароль Користувача" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "Отримати Час Останнього Підключення Користувача" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Отримати Статистику по Користувачу" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Отримати Кількість Зареєстрованих Користувачів" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Отримати Кількість Підключених Користувачів" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Управління Користувачами" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Всі користувачі" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Вихідні s2s-з'єднання" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Керування резервним копіюванням" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Імпорт користувачів з jabberd14 файлів \"Spool\"" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "До ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "Від ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Конфігурація таблиць бази даних на " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Оберіть тип збереження таблиць" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Тільки диск" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "ОЗП та диск" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "ОЗП" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "не зберігаеться локально" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Зупинка модулів на " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Виберіть модулі, які необхідно зупинити" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Запуск модулів на " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Введіть перелік такого виду {Module, [Options]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Список завантажуваних модулів" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Резервне копіювання в файл на " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Введіть шлях до резервного файла" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Шлях до файла" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Відновлення з резервної копії на " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Копіювання в текстовий файл на " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Введіть шлях до текстового файла" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Імпортування користувача з файла на " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Введіть шлях до файла зі спула jabberd14" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Імпортування користувача з директорії на " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Введіть шлях до директорії спула jabberd14" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Шлях до директорії" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "Час затримки" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Конфігурація списків керування доступом" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Списки керування доступом" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Конфігурація доступу" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Правила доступу" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Перевірка Пароля" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Кількість зареєстрованих користувачів" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Кількість підключених користувачів" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Останнє підключення" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "Кількість контактів" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "IP адреси" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Ресурси" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Адміністрування " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Дія над користувачем" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Змінити параметри" - -#: mod_fail2ban.erl:95 -msgid "" -"Too many (~p) failed authentications from this IP address (~s). The address " -"will be unblocked at ~s UTC" -msgstr "" -"Забагато (~p) помилок авторизації з цієї IP адреси (~s). Адресу буде " -"розблоковано о ~s UTC" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "Будь ласка вкажіть розмір файлу." - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "Будь ласка вкажіть ім'я файлу." - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "Ця IP адреса у чорному списку ~s" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "Доступ заборонений політикою служби" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "IRC Транспорт" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "ejabberd IRC модуль" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "" -"Для налагодження параметрів mod_irc необхідно використовувати клієнт, що має " -"підтримку x:data" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Реєстрація в mod_irc для " - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Введіть ім'я користувача, кодування, порти та паролі, що будуть " -"використовуватися при підключенні до IRC-серверів" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "Ім'я користувача IRC" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Щоб вказати різні порти, паролі та кодування для різних серверів IRC, " -"заповніть список значеннями в форматі '{\"irc server\", \"encoding\", port, " -"\"password\"}'. За замовчуванням ця служба використовує \"~s\" кодування, " -"порт ~p, пустий пароль." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Приклад: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta." -"fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Параметри з'єднання" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "Приєднатися до каналу IRC" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "Канал IRC (не включаючи #)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "IRC-сервер" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Приєднатися до каналу IRC" - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Приєднатися до каналу IRC з Jabber ID: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "Парметри IRC" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Введіть ім'я користувача та кодування, які будуть використовуватися при " -"підключенні до IRC-серверів Натисніть 'Далі' для заповнення додаткових " -"полів. Натисніть 'Завершити' для збереження параметрів." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "Ім'я користувача IRC" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Пароль ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Порт ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Кодування для сервера ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Сервер ~b" - -#: mod_mam.erl:541 -msgid "Only members may query archives of this room" -msgstr "Тільки модератори можуть запитувати архіви цієї кімнати" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "Тільки адміністратор сервісу може надсилати службові повідомлення" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "Створювати конференцію заборонено політикою служби" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "Конференція не існує" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Кімнати" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "Порожні кімнати" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "" -"Для реєстрації псевдоніму необхідно використовувати клієнт з підтримкою x:" -"data" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Реєстрація псевдоніма на " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Введіть псевдонім, який ви хочете зареєструвати" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Псевдонім" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "Псевдонім зареєстровано кимось іншим" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Вам необхідно заповнити поле \"Псевдонім\" у формі" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "ejabberd MUC модуль" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "Багато-користувальницький чат" - -#: mod_muc_admin.erl:249 -msgid "Total rooms" -msgstr "Всього кімнат" - -#: mod_muc_admin.erl:250 -msgid "Permanent rooms" -msgstr "Постійні кімнати" - -#: mod_muc_admin.erl:251 -msgid "Registered nicknames" -msgstr "Зареєстровані імена" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "Перелік кімнат" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "Конфігурація кімнати змінилась" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "увійшов(ла) в кімнату" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "вийшов(ла) з кімнати" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "заборонили вхід в кімнату" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "вигнано з кімнати внаслідок зміни рангу" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "вигнано з кімнати тому, що вона стала тільки для учасників" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "вигнано з кімнати внаслідок зупинки системи" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "змінив(ла) псевдонім на" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " встановив(ла) тему: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "Створено кімнату" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "Знищено кімнату" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "Запущено кімнату" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "Зупинено кімнату" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "Понеділок" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "Вівторок" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "Середа" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "Четвер" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "П'ятниця" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "Субота" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "Неділя" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "січня" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "лютого" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "березня" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "квітня" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "травня" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "червня" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "липня" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "серпня" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "вересня" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "грудня" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "листопада" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "грудня" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Конфігурація кімнати" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "Учасники кімнати" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "Швидкість передачі інформації було перевищено" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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), та був виганий з кімнати" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "Не дозволяється надсилати приватні повідомлення в конференцію" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "" -"Будь ласка, почекайте деякий час перед тим, як знову відправляти голосовий " -"запит" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "Голосові запити відключені в цій конференції" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "Помилка витягнення JID з вашого схвалення голосового запиту" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "Тільки модератори можуть схвалювати голосові запити" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Неправильний тип повідомлення" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "Не дозволяється надсилати приватні повідомлення типу \"groupchat\"" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "Адресата немає в конференції" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "Приватні повідомлення не дозволені" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Тільки присутнім дозволяється надсилати повідомленняя в конференцію" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "Тільки присутнім дозволяється відправляти запити в конференцію" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "Запити до користувачів в цій конференції заборонені" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "Тільки модератори та учасники можуть змінювати тему в цій кімнаті" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Тільки модератори можуть змінювати тему в цій кімнаті" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "Відвідувачам не дозволяється надсилати повідомлення всім присутнім" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "Відвідувачам не дозволяється змінювати псевдонім в цій кімнаті" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "Псевдонім зайнято кимось з присутніх" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Вам заборонено входити в цю конференцію" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "В цю конференцію можуть входити тільки її члени" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "Щоб зайти в цю конференцію, необхідно ввести пароль" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "Надто багато CAPTCHA-запитів" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "Нема можливості згенерувати капчу" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Неправильний пароль" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "Необхідні права адміністратора" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "Необхідні права модератора" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "Jabber ID ~s недопустимий" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "Псевдонім ~s в кімнаті відсутній" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Недопустимий ранг: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Недопустима роль: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "Необхідні права власника" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "Конфігурація кімнати ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Назва кімнати" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "Опис кімнати" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Зробити кімнату постійною" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Зробити кімнату видимою всім" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "Зробити список учасників видимим всім" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Зробити кімнату захищеною паролем" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Максимальна кількість учасників" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Без обмежень" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Зробити реальні Jabber ID учасників видимими" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "тільки модераторам" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "всім учасникам" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "Ролі для яких поширюється наявність" - -#: mod_muc_room.erl:3486 -msgid "Moderator" -msgstr "Модератор" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "Учасник" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "Відвідувач" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Кімната тільки для зареєтрованых учасників" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Зробити кімнату модерованою" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Зробити користувачів учасниками за замовчуванням" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Дозволити користувачам змінювати тему" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Дозволити приватні повідомлення" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "Дозволити відвідувачам відсилати приватні повідомлення" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "ніхто" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Дозволити iq-запити до користувачів" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Дозволити користувачам надсилати запрошення" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "" -"Дозволити відвідувачам відсилати текст статусу в оновленнях присутності" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Дозволити відвідувачам змінювати псевдонім" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "Дозволити відвідувачам надсилати голосові запрошення" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "Мінімальний інтервал між голосовими запитами (в секундах)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "Зробити кімнату захищеною капчею" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "Ввімкнути архівацію повідомлень" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "Пропускати ці Jabber ID без CAPTCHA-запиту" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Включити журнал роботи" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "" -"Для конфігурування кімнати потрібно використовувати клієнт з підтримкою x:" -"data" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Кількість присутніх" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "приватна, " - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "Голосовий запит" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "Підтвердить або відхилите голосовий запит" - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "JID Користувача" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "Надати голос персоні?" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s запрошує вас до кімнати ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "пароль:" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "Мультікаст" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "Мультікаст ejabberd сервіс" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "" -"Черга повідомлень, що не були доставлені, переповнена. Повідомлення не було " -"збережено." - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "Черга офлайнових повідомлень ~s" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Час" - -#: mod_offline.erl:812 -msgid "From" -msgstr "Від кого" - -#: mod_offline.erl:813 -msgid "To" -msgstr "Кому" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Пакет" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Офлайнові повідомлення:" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "Видалити всі офлайнові повідомлення" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "ejabberd SOCKS5 Bytestreams модуль" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Публікація-Підписка" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "Модуль ejabberd Публікації-Підписки" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "Запит на підписку PubSub" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Вирішіть, чи задовольнити запит цього об'єкту на підписку" - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "ID вузла" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Адреса абонента" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "Чи дозволити цьому Jabber ID підписатись новини наданого вузла" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Доставляти разом з повідомленнями про публікації самі публікації" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Доставляти сповіщення про події" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Повідомляти абонентів про зміни в конфігурації збірника" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Повідомляти абонентів про видалення збірника" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Повідомляти абонентів про видалення публікацій із збірника" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Зберегати публікації до сховища" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "Псевдонім для вузла" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Максимальне число збережених публікацій" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Дозволяти підписку" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Визначити модель доступу" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Дозволені для підписки групи ростера" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Умови публікації" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "" -"Видалити всі елементи, коли особа, що їх опублікувала, вимикається від мережі" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "Вкажіть тип повідомлень зі сповіщеннями про події" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Максимальний розмір корисного навантаження в байтах" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "Коли надсилати останній опублікований елемент" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Доставляти повідомленнями тільки доступним користувачам" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "Колекція, до якої входить вузол" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "Перевірку капчею не пройдено" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "" -"Для реєстрації псевдоніму необхідно використовувати клієнт з підтримкою x:" -"data" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "Виберіть назву користувача та пароль для реєстрації на цьому сервері" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "Пароль надто простий" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "Користувачам не дозволено так часто реєструвати облікові записи" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "Ваш Jabber-акаунт було успішно створено." - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "Помилка при створенні акаунту:" - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "Ваш Jabber-акаунт було успішно видалено." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "Помилка при видаленні акаунту: " - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "Пароль вашого Jabber-акаунту був успішно змінений." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "Помилка при зміні пароля: " - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Реєстрація Jabber-акаунту" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Зареєструвати Jabber-акаунт" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Видалити Jabber-акаунт" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"Тут ви можете зареєструвати обліковий запис Jabber на цьому сервері. Ваш JID " -"(ідентифікатор Jabber) матиме вигляд \"користувач@сервер\". Щоб вірно " -"заповнити поля нижче, будь ласка, уважно читайте інструкції до них." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "Ім'я користувача:" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" -"Регістр не має значення: \"МАША\" та \"маша\" буде сприйматися як одне й те " -"саме ім'я." - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "Заборонені символи:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "Сервер:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "Нікому не кажіть свій пароль, навіть адміністраторам сервера." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "Пізніше можна змінити пароль через Jabber-клієнт." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Деякі Jabber-клієнти можуть зберігати пароль на вашому комп'ютері. " -"Користуйтесь цією функцією тільки у тому випадку, якщо вважаєте її безпечною." - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"Запам'ятайте пароль, або запишіть його на папері, який треба зберегти у " -"безпечному місці. У Jabber'і немає автоматизованих засобів відновлення " -"пароля на той випадок, якщо ви його забудете." - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "Перевірка Пароля:" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "Реєстрація" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "Старий пароль:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "Новий Пароль:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "Ця сторінка дозволяє видалити свій акаунт з Jabber-сервера." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "Видалити" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Підписка" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Очікування" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Групи" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Затвердити" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Видалити" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Ростер користувача " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Додати Jabber ID" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Ростер" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Спільні групи контактів" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Назва:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Опис:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Члени:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Видимі групи:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Група " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Erlang Jabber Server" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "День народження" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Місто" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "Країна" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "Електронна пошта" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Прізвище" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Заповніть поля для пошуку користувача Jabber (Додайте * в кінець поля для " -"пошуку підрядка)" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Повне ім'я" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "По-батькові" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Назва організації" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Відділ організації" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Пошук користувачів в " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "Для пошуку необхідний клієнт із підтримкою x:data" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "Пошук користувачів по vCard" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "ejabberd vCard модуль" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Результати пошуку в " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "Заповніть поля для пошуку користувача Jabber" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Вихідні s2s-сервери:" - -#~ msgid "Delete" -#~ msgstr "Видалити" - -#~ msgid "This room is not anonymous" -#~ msgstr "Ця кімната не анонімна" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "" -#~ "Цього учасника було відключено від кімнати через те, що він надіслав " -#~ "помилкове повідомлення" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Цього учасника було відключено від кімнати через те, що він надіслав " -#~ "помилкове повідомлення іншому учаснику" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "" -#~ "Цього учасника було відключено від кімнати через те, що він надіслав " -#~ "помилковий статус присутності" - -#~ msgid "Captcha test failed" -#~ msgstr "Перевірка капчею закінчилась невдало" diff --git a/priv/msgs/vi.msg b/priv/msgs/vi.msg index 3e5fdf5f1..186eb20e7 100644 --- a/priv/msgs/vi.msg +++ b/priv/msgs/vi.msg @@ -1,31 +1,27 @@ -%% -*- coding: latin-1 -*- -{"Access Configuration","Cấu Hình Truy Cập"}. -{"Access Control List Configuration","Cấu Hình Danh Sách Kiểm Soát Truy Cập"}. -{"Access control lists","Danh sách kiểm soát truy cập"}. -{"Access Control Lists","Danh Sách Kiểm Soát Truy Cập"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" has set the subject to: "," đã đặt chủ đề thành: "}. {"Access denied by service policy","Sự truy cập bị chặn theo chính sách phục vụ"}. -{"Access rules","Quy tắc Truy Cập"}. -{"Access Rules","Quy Tắc Truy Cập"}. {"Action on user","Hành động đối với người sử dụng"}. -{"Add Jabber ID","Thêm Jabber ID"}. -{"Add New","Thêm Mới"}. {"Add User","Thêm Người Sử Dụng"}. {"Administration of ","Quản trị về "}. {"Administration","Quản trị"}. {"Administrator privileges required","Yêu cầu đặc quyền của nhà quản trị"}. {"All activity","Tất cả hoạt động"}. +{"All Users","Tất Cả Người Sử Dụng"}. {"Allow this Jabber ID to subscribe to this pubsub node?","Cho phép Jabber ID đăng ký nút môđun xuất bản đăng ký này không?"}. {"Allow users to query other users","Cho phép người sử dụng hỏi người sử dụng khác"}. {"Allow users to send invites","Cho phép người sử dụng gửi lời mời"}. {"Allow users to send private messages","Cho phép người sử dụng gửi thư riêng"}. -{"All Users","Tất Cả Người Sử Dụng"}. {"Announcements","Thông báo"}. -{"anyone","bất kỳ ai"}. {"April","Tháng Tư"}. {"August","Tháng Tám"}. {"Backup Management","Quản lý Sao Lưu Dự Phòng"}. -{"Backup","Sao lưu dự phòng"}. {"Backup to File at ","Sao lưu dự phòng ra Tập Tin tại"}. +{"Backup","Sao lưu dự phòng"}. {"Bad format","Định dạng hỏng"}. {"Birthday","Ngày sinh"}. {"Change Password","Thay Đổi Mật Khẩu"}. @@ -33,33 +29,26 @@ {"Chatroom configuration modified","Cấu hình phòng trò chuyện được chỉnh sửa"}. {"Chatrooms","Phòng trò chuyện"}. {"Choose a username and password to register with this server","Chọn một tên truy cập và mật khẩu để đăng ký với máy chủ này"}. -{"Choose modules to stop","Chọn môđun để dừng"}. {"Choose storage type of tables","Chọn loại bảng lưu trữ"}. {"Choose whether to approve this entity's subscription.","Chọn có nên chấp nhận sự đăng ký của đối tượng này không"}. {"City","Thành phố"}. {"Commands","Lệnh"}. {"Conference room does not exist","Phòng họp không tồn tại"}. {"Configuration","Cấu hình"}. -{"Connected Resources:","Tài Nguyên Được Kết Nối:"}. {"Country","Quốc gia"}. -{"CPU Time:","Thời Gian CPU:"}. -{"Database","Cơ sở dữ liệu"}. {"Database Tables Configuration at ","Cấu Hình Bảng Cơ Sở Dữ Liệu tại"}. +{"Database","Cơ sở dữ liệu"}. {"December","Tháng Mười Hai"}. {"Default users as participants","Người sử dụng mặc định là người tham dự"}. {"Delete message of the day on all hosts","Xóa thư trong ngày trên tất cả các máy chủ"}. {"Delete message of the day","Xóa thư trong ngày"}. -{"Delete Selected","Tùy chọn Xóa được Chọn"}. {"Delete User","Xóa Người Sử Dụng"}. {"Deliver event notifications","Đưa ra các thông báo sự kiện"}. {"Deliver payloads with event notifications","Đưa ra thông tin dung lượng với các thông báo sự kiện"}. -{"Description:","Miêu tả:"}. {"Disc only copy","Chỉ sao chép vào đĩa"}. -{"Displayed Groups:","Nhóm được hiển thị:"}. {"Dump Backup to Text File at ","Kết Xuất Sao Lưu ra Tập Tin Văn Bản tại"}. {"Dump to Text File","Kết xuất ra Tập Tin Văn Bản"}. {"Edit Properties","Chỉnh Sửa Thuộc Tính"}. -{"ejabberd IRC module","Môdun ejabberd IRC Bản quyền"}. {"ejabberd MUC module","Môdun ejabberd MUC Bản quyền"}. {"ejabberd Publish-Subscribe module","Môdun ejabberd Xuất Bản-Đăng Ký Bản quyền"}. {"ejabberd SOCKS5 Bytestreams module","Môdun SOCKS5 Bytestreams Bản quyền"}. @@ -67,32 +56,21 @@ {"Email","Email"}. {"Enable logging","Cho phép ghi nhật ký"}. {"End User Session","Kết Thúc Phiên Giao Dịch Người Sử Dụng"}. -{"Enter list of {Module, [Options]}","Nhập danh sách {Môđun, [Các Tùy Chọn]}"}. {"Enter nickname you want to register","Nhập bí danh bạn muốn đăng ký"}. {"Enter path to backup file","Nhập đường dẫn đến tập tin sao lưu dự phòng"}. {"Enter path to jabberd14 spool dir","Nhập đường dẫn đến thư mục spool jabberd14"}. {"Enter path to jabberd14 spool file","Nhập đường dẫn đến tập tin spool jabberd14"}. {"Enter path to text file","Nhập đường dẫn đến tập tin văn bản"}. -{"Erlang Jabber Server","Erlang Jabber Server Bản quyền"}. {"Family Name","Họ"}. {"February","Tháng Hai"}. -{"Fill in fields to search for any matching Jabber User","Điền vào các ô để tìm kiếm bất kỳ các thông tin nào khớp với Người sử dụng Jabber"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Điền vào mẫu này để tìm kiếm bất kỳ thông tin nào khớp với Người sử dụng Jabber (Thêm dấu * vào cuối ô để thông tin khớp với chuỗi bên trong)"}. {"Friday","Thứ Sáu"}. -{"From ~s","Nhận từ ~s"}. -{"From","Từ"}. {"Full Name","Tên Đầy Đủ"}. {"Get Number of Online Users","Nhận Số Người Sử Dụng Trực Tuyến"}. {"Get Number of Registered Users","Nhận Số Người Sử Dụng Đã Đăng Ký"}. {"Get User Last Login Time","Nhận Thời Gian Đăng Nhập Cuối Cùng Của Người Sử Dụng"}. -{"Get User Password","Nhận Mật Khẩu Người Sử Dụng"}. {"Get User Statistics","Nhận Thông Tin Thống Kê Người Sử Dụng"}. -{"Group ","Nhóm "}. -{"Groups","Nhóm"}. {"has been banned","đã bị cấm"}. {"has been kicked","đã bị đẩy ra khỏi"}. -{" has set the subject to: "," đã đặt chủ đề thành: "}. -{"Host","Máy chủ"}. {"Import Directory","Nhập Thư Mục"}. {"Import File","Nhập Tập Tin"}. {"Import User from File at ","Nhập Người Sử Dụng từ Tập Tin tại"}. @@ -100,16 +78,11 @@ {"Import Users From jabberd14 Spool Files","Nhập Người Sử Dụng Từ Các Tập Tin Spool jabberd14"}. {"Improper message type","Loại thư không phù hợp"}. {"Incorrect password","Mật khẩu sai"}. -{"Invalid affiliation: ~s","Tư cách không hợp lệ: ~s"}. -{"Invalid role: ~s","Vai trò không hợp lệ: ~s"}. {"IP addresses","Địa chỉ IP"}. -{"IRC Transport","Truyền tải IRC"}. -{"IRC Username","Tên truy cập IRC"}. {"is now known as","bây giờ được biết như"}. {"It is not allowed to send private messages of type \"groupchat\"","Không được phép gửi những thư riêng loại \"groupchat\""}. {"It is not allowed to send private messages to the conference","Không được phép gửi những thư riêng đến phòng họp"}. {"Jabber ID","Jabber ID"}. -{"Jabber ID ~s is invalid","Jabber ID ~s không hợp lệ"}. {"January","Tháng Một"}. {"joins the room","tham gia phòng này"}. {"July","Tháng Bảy"}. @@ -119,43 +92,31 @@ {"Last month","Tháng trước"}. {"Last year","Năm trước"}. {"leaves the room","rời khỏi phòng này"}. -{"Listened Ports at ","Cổng Liên Lạc tại"}. -{"Listened Ports","Cổng Kết Nối"}. -{"List of modules to start","Danh sách các môđun khởi động"}. -{"Low level update script","Lệnh cập nhật mức độ thấp"}. {"Make participants list public","Tạo danh sách người tham dự công khai"}. {"Make room members-only","Tạo phòng chỉ cho phép tư cách thành viên tham gia"}. {"Make room password protected","Tạo phòng được bảo vệ bằng mật khẩu"}. {"Make room persistent","Tạo phòng bền vững"}. {"Make room public searchable","Tạo phòng có thể tìm kiếm công khai"}. {"March","Tháng Ba"}. -{"Maximum Number of Occupants","Số Lượng Người Tham Dự Tối Đa"}. -{"Max # of items to persist","Số mục tối đa để lưu trữ"}. {"Max payload size in bytes","Kích thước dung lượng byte tối đa"}. +{"Maximum Number of Occupants","Số Lượng Người Tham Dự Tối Đa"}. {"May","Tháng Năm"}. -{"Members:","Thành viên:"}. -{"Memory","Bộ Nhớ"}. {"Message body","Thân thư"}. {"Middle Name","Họ Đệm"}. {"Moderator privileges required","Yêu cầu đặc quyền của nhà điều phối"}. -{"moderators only","nhà điều phối duy nhất"}. -{"Module","Môđun"}. -{"Modules","Môđun"}. {"Monday","Thứ Hai"}. -{"Name:","Tên:"}. {"Name","Tên"}. {"Never","Không bao giờ"}. -{"Nickname","Bí danh"}. {"Nickname Registration at ","Đăng Ký Bí Danh tại"}. {"Nickname ~s does not exist in the room","Bí danh ~s không tồn tại trong phòng này"}. +{"Nickname","Bí danh"}. {"No body provided for announce message","Không có nội dung trong thư thông báo"}. {"No Data","Không Dữ Liệu"}. +{"No limit","Không giới hạn"}. {"Node ID","ID Nút"}. {"Node not found","Nút không tìm thấy"}. {"Nodes","Nút"}. -{"No limit","Không giới hạn"}. {"None","Không có"}. -{"No resource provided","Không có nguồn lực cung cấp"}. {"Notify subscribers when items are removed from the node","Thông báo cho người đăng ký khi nào các mục chọn bị gỡ bỏ khỏi nút"}. {"Notify subscribers when the node configuration changes","Thông báo cho người đăng ký khi nào cấu hình nút thay đổi"}. {"Notify subscribers when the node is deleted","Thông báo cho người đăng ký khi nào nút bị xóa bỏ"}. @@ -164,34 +125,26 @@ {"Number of online users","Số người sử dụng trực tuyến"}. {"Number of registered users","Số người sử dụng đã đăng ký"}. {"October","Tháng Mười"}. -{"Offline Messages:","Thư Ngoại Tuyến:"}. -{"Offline Messages","Thư Ngoại Tuyến"}. {"OK","OK"}. -{"Online","Trực tuyến"}. -{"Online Users:","Người Sử Dụng Trực Tuyến:"}. {"Online Users","Người Sử Dụng Trực Tuyến"}. +{"Online","Trực tuyến"}. {"Only deliver notifications to available users","Chỉ gửi thông báo đến những người sử dụng hiện có"}. {"Only occupants are allowed to send messages to the conference","Chỉ có những đối tượng tham gia mới được phép gửi thư đến phòng họp"}. {"Only occupants are allowed to send queries to the conference","Chỉ có những đối tượng tham gia mới được phép gửi yêu cầu đến phòng họp"}. {"Only service administrators are allowed to send service messages","Chỉ có người quản trị dịch vụ mới được phép gửi những thư dịch vụ"}. -{"Options","Tùy chọn"}. {"Organization Name","Tên Tổ Chức"}. {"Organization Unit","Bộ Phận"}. -{"Outgoing s2s Connections:","Kết Nối Bên Ngoài s2s:"}. {"Outgoing s2s Connections","Kết Nối Bên Ngoài s2s"}. {"Owner privileges required","Yêu cầu đặc quyền của người sở hữu"}. -{"Packet","Gói thông tin"}. -{"Password:","Mật Khẩu:"}. -{"Password","Mật Khẩu"}. {"Password Verification","Kiểm Tra Mật Khẩu"}. +{"Password","Mật Khẩu"}. +{"Password:","Mật Khẩu:"}. {"Path to Dir","Đường Dẫn đến Thư Mục"}. {"Path to File","Đường dẫn đến Tập Tin"}. -{"Pending","Chờ"}. {"Period: ","Giai đoạn: "}. {"Persist items to storage","Những mục cần để lưu trữ"}. {"Ping","Ping"}. {"Pong","Pong"}. -{"Port","Cổng"}. {"Present real Jabber IDs to","Jabber ID thực tế hiện hành đến"}. {"private, ","riêng,"}. {"Publish-Subscribe","Xuất Bản-Đăng Ký"}. @@ -199,41 +152,30 @@ {"Queries to the conference members are not allowed in this room","Không được phép gửi các yêu cầu gửi đến các thành viên trong phòng họp này"}. {"RAM and disc copy","Sao chép vào RAM và đĩa"}. {"RAM copy","Sao chép vào RAM"}. -{"Raw","Thô"}. {"Really delete message of the day?","Có thực sự xóa thư trong ngày này không?"}. {"Recipient is not in the conference room","Người nhận không có trong phòng họp"}. -{"Registered Users:","Người Sử Dụng Đã Đăng Ký:"}. -{"Registered Users","Người Sử Dụng Đã Đăng Ký"}. -{"Registration in mod_irc for ","Đăng ký trong mod_irc cho "}. {"Remote copy","Sao chép từ xa"}. -{"Remove","Gỡ bỏ"}. {"Remove User","Gỡ Bỏ Người Sử Dụng"}. {"Replaced by new connection","Được thay thế bởi kết nối mới"}. {"Resources","Nguồn tài nguyên"}. -{"Restart","Khởi động lại"}. {"Restart Service","Khởi Động Lại Dịch Vụ"}. {"Restore Backup from File at ","Phục hồi Sao Lưu từ Tập Tin tại "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Khôi phục bản sao lưu dự phòng dạng nhị phân sau lần khởi động ejabberd kế tiếp (yêu cầu ít bộ nhớ hơn):"}. {"Restore binary backup immediately:","Khôi phục bản sao lưu dự phòng dạng nhị phận ngay lập tức:"}. -{"Restore","Khôi phục"}. {"Restore plain text backup immediately:","Khôi phục bản sao lưu dự phòng thuần văn bản ngay lập tức:"}. +{"Restore","Khôi phục"}. {"Room Configuration","Cấu Hình Phòng"}. {"Room creation is denied by service policy","Việc tạo phòng bị ngăn lại theo chính sách dịch vụ"}. {"Room title","Tên phòng"}. -{"Roster","Bảng phân công"}. -{"Roster of ","Bảng phân công của "}. {"Roster size","Kích thước bảng phân công"}. -{"RPC Call Error","Lỗi Gọi RPC"}. {"Running Nodes","Nút Hoạt Động"}. -{"~s access rule configuration","~s cấu hình quy tắc truy cập"}. {"Saturday","Thứ Bảy"}. -{"Script check","Lệnh kiểm tra"}. {"Search Results for ","Kết Quả Tìm Kiếm cho "}. {"Search users in ","Tìm kiếm người sử dụng trong"}. -{"Send announcement to all online users","Gửi thông báo đến tất cả người sử dụng trực tuyến"}. {"Send announcement to all online users on all hosts","Gửi thông báo đến tất cả người sử dụng trực tuyến trên tất cả các máy chủ"}. -{"Send announcement to all users","Gửi thông báo đến tất cả người sử dụng"}. +{"Send announcement to all online users","Gửi thông báo đến tất cả người sử dụng trực tuyến"}. {"Send announcement to all users on all hosts","Gửi thông báo đến tất cả người sử dụng trên tất cả các máy chủ"}. +{"Send announcement to all users","Gửi thông báo đến tất cả người sử dụng"}. {"September","Tháng Chín"}. {"Set message of the day and send to online users","Tạo lập thư trong ngày và gửi đến những người sử dụng trực tuyến"}. {"Set message of the day on all hosts and send to online users","Tạo lập thư trong ngày trên tất cả các máy chủ và gửi đến những người sử dụng trực tuyến"}. @@ -241,52 +183,27 @@ {"Show Integral Table","Hiển Thị Bảng Đầy Đủ"}. {"Show Ordinary Table","Hiển Thị Bảng Thường"}. {"Shut Down Service","Tắt Dịch Vụ"}. -{"~s invites you to the room ~s","~s mời bạn vào phòng ~s"}. {"Specify the access model","Xác định mô hình truy cập"}. {"Specify the publisher model","Xác định mô hình nhà xuất bản"}. -{"~s's Offline Messages Queue","~s's Danh Sách Chờ Thư Ngoại Tuyến"}. -{"Start","Khởi động"}. -{"Start Modules at ","Môđun Khởi Động tại "}. -{"Start Modules","Môđun Khởi Động"}. -{"Statistics of ~p","Thống kê về ~p"}. -{"Statistics","Số liệu thống kê"}. -{"Stop","Dừng"}. -{"Stop Modules at ","Môđun Dừng tại"}. -{"Stop Modules","Môđun Dừng"}. {"Stopped Nodes","Nút Dừng"}. -{"Storage Type","Loại Lưu Trữ"}. {"Store binary backup:","Lưu dữ liệu sao lưu dạng nhị phân:"}. {"Store plain text backup:","Khôi phục bản sao lưu dự phòng thuần văn bản"}. {"Subject","Tiêu đề"}. -{"Submit","Gửi"}. {"Submitted","Đã gửi"}. {"Subscriber Address","Địa Chỉ Người Đăng Ký"}. -{"Subscription","Đăng ký"}. {"Sunday","Chủ Nhật"}. {"the password is","mật khẩu là"}. +{"This room is not anonymous","Phòng này không nặc danh"}. {"Thursday","Thứ Năm"}. {"Time delay","Thời gian trì hoãn"}. -{"Time","Thời Gian"}. -{"To","Đến"}. -{"To ~s","Gửi đến ~s"}. {"Traffic rate limit is exceeded","Quá giới hạn tỷ lệ lưu lượng truyền tải"}. -{"Transactions Aborted:","Giao Dịch Hủy Bỏ:"}. -{"Transactions Committed:","Giao Dịch Được Cam Kết:"}. -{"Transactions Logged:","Giao Dịch Được Ghi Nhận:"}. -{"Transactions Restarted:","Giao Dịch Khởi Động Lại:"}. {"Tuesday","Thứ Ba"}. -{"Update","Cập Nhật"}. {"Update message of the day (don't send)","Cập nhật thư trong ngày (không gửi)"}. {"Update message of the day on all hosts (don't send)","Cập nhật thư trong ngày trên tất cả các máy chủ (không gửi)"}. -{"Update plan","Kế hoạch cập nhật"}. -{"Update script","Cập nhận lệnh"}. -{"Uptime:","Thời gian tải lên:"}. -{"Use of STARTTLS required","Yêu cầu sử dụng STARTTLS"}. {"User Management","Quản Lý Người Sử Dụng"}. {"User","Người sử dụng"}. {"Users Last Activity","Hoạt Động Cuối Cùng Của Người Sử Dụng"}. {"Users","Người sử dụng"}. -{"Validate","Xác nhận hợp lệ"}. {"vCard User Search","Tìm Kiếm Người Sử Dụng vCard"}. {"Virtual Hosts","Máy Chủ Ảo"}. {"Visitors are not allowed to send messages to all occupants","Người ghé thăm không được phép gửi thư đến tất cả các người tham dự"}. @@ -295,7 +212,5 @@ {"Whether to allow subscriptions","Xác định nên cho phép đăng ký không"}. {"You have been banned from this room","Bạn bị cấm tham gia phòng này"}. {"You must fill in field \"Nickname\" in the form","Bạn phải điền thông tin vào ô \"Nickname\" trong biểu mẫu này"}. -{"You need an x:data capable client to configure mod_irc settings","Bạn cần có một trình ứng dụng khách hỗ trợ định dạng dữ liệu x: để xác định các thiết lập mod_irc"}. -{"You need an x:data capable client to configure room","Bạn cần có một trình ứng dụng khách hỗ trợ định dạng dữ liệu x: để xác định cấu hình phòng họp"}. {"You need an x:data capable client to search","Bạn cần có một trình ứng dụng khách hỗ trợ định dạng dữ liệu x: để tìm kiếm"}. {"Your contact offline message queue is full. The message has been discarded.","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ỏ."}. diff --git a/priv/msgs/vi.po b/priv/msgs/vi.po deleted file mode 100644 index 67b9294e2..000000000 --- a/priv/msgs/vi.po +++ /dev/null @@ -1,1971 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: 2.1.0-alpha\n" -"Last-Translator: EQHO Communications (Thailand) Ltd. - http://www.eqho.com\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Vietnamese (tiếng việt)\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "Yêu cầu sử dụng STARTTLS" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Không có nguồn lực cung cấp" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Được thay thế bởi kết nối mới" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "đã bị đẩy ra khỏi" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "" - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -#, fuzzy -msgid "Enter the text you see" -msgstr "Nhập đường dẫn đến tập tin văn bản" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "" - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "" - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Người sử dụng" - -#: ejabberd_oauth.erl:256 -#, fuzzy -msgid "Server" -msgstr "Không bao giờ" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Mật Khẩu" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -#, fuzzy -msgid "ejabberd Web Admin" -msgstr "Giao diện Web ejabberd" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Quản trị" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Danh Sách Kiểm Soát Truy Cập" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Đã gửi" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Định dạng hỏng" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Gửi" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "Thô" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Tùy chọn Xóa được Chọn" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Quy Tắc Truy Cập" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "~s cấu hình quy tắc truy cập" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "Máy Chủ Ảo" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Người sử dụng" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "Người Sử Dụng Trực Tuyến" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Hoạt Động Cuối Cùng Của Người Sử Dụng" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Giai đoạn: " - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "Tháng trước" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "Năm trước" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "Tất cả hoạt động" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Hiển Thị Bảng Thường" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Hiển Thị Bảng Đầy Đủ" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Số liệu thống kê" - -#: ejabberd_web_admin.erl:1014 -#, fuzzy -msgid "Not Found" -msgstr "Nút không tìm thấy" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Nút không tìm thấy" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Thêm Mới" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Máy chủ" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Người Sử Dụng Đã Đăng Ký" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Thêm Người Sử Dụng" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Thư Ngoại Tuyến" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Hoạt Động Cuối Cùng" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Không bao giờ" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Trực tuyến" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Người Sử Dụng Đã Đăng Ký:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Người Sử Dụng Trực Tuyến:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Kết Nối Bên Ngoài s2s:" - -#: ejabberd_web_admin.erl:1559 -#, fuzzy -msgid "Incoming s2s Connections:" -msgstr "Kết Nối Bên Ngoài s2s:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Không có" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Thay Đổi Mật Khẩu" - -#: ejabberd_web_admin.erl:1673 -#, fuzzy -msgid "User ~s" -msgstr "Người sử dụng " - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Tài Nguyên Được Kết Nối:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Mật Khẩu:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Gỡ Bỏ Người Sử Dụng" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Không Dữ Liệu" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Nút" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Nút Hoạt Động" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Nút Dừng" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -#, fuzzy -msgid "Node ~p" -msgstr "Nút " - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "Cơ sở dữ liệu" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Sao lưu dự phòng" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Cổng Kết Nối" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Cập Nhật" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Khởi động lại" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Dừng" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Môđun" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "Lỗi Gọi RPC" - -#: ejabberd_web_admin.erl:1917 -#, fuzzy -msgid "Database Tables at ~p" -msgstr "Bảng Cơ Sở Dữ Liệu tại" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "Tên" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Loại Lưu Trữ" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Bộ Nhớ" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "" - -#: ejabberd_web_admin.erl:1955 -#, fuzzy -msgid "Backup of ~p" -msgstr "Sao lưu dự phòng về" - -#: ejabberd_web_admin.erl:1959 -#, fuzzy -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Lưu ý rằng những tùy chọn này sẽ chỉ được sao lưu cơ sở dữ liệu bên trong " -"Mnesia. Nếu bạn đang sử dụng môđun ODBC, bạn cũng cần sao lưu cơ sở dữ liệu " -"SQL của bạn riêng biệt." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Lưu dữ liệu sao lưu dạng nhị phân:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "OK" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Khôi phục bản sao lưu dự phòng dạng nhị phận ngay lập tức:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"Khôi phục bản sao lưu dự phòng dạng nhị phân sau lần khởi động ejabberd kế " -"tiếp (yêu cầu ít bộ nhớ hơn):" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Khôi phục bản sao lưu dự phòng thuần văn bản" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Khôi phục bản sao lưu dự phòng thuần văn bản ngay lập tức:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "" - -#: ejabberd_web_admin.erl:2076 -#, fuzzy -msgid "Import user data from jabberd14 spool file:" -msgstr "Nhập Người Sử Dụng Từ Các Tập Tin Spool jabberd14" - -#: ejabberd_web_admin.erl:2087 -#, fuzzy -msgid "Import users data from jabberd14 spool directory:" -msgstr "Nhập Người Sử Dụng Từ Các Tập Tin Spool jabberd14" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Cổng Liên Lạc tại" - -#: ejabberd_web_admin.erl:2144 -#, fuzzy -msgid "Modules at ~p" -msgstr "Môđun tại " - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "Thống kê về ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Thời gian tải lên:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "Thời Gian CPU:" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Giao Dịch Được Cam Kết:" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Giao Dịch Hủy Bỏ:" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Giao Dịch Khởi Động Lại:" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Giao Dịch Được Ghi Nhận:" - -#: ejabberd_web_admin.erl:2243 -#, fuzzy -msgid "Update ~p" -msgstr "Cập Nhật " - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "Kế hoạch cập nhật" - -#: ejabberd_web_admin.erl:2255 -#, fuzzy -msgid "Modified modules" -msgstr "Môđun cập nhật" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Cập nhận lệnh" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Lệnh cập nhật mức độ thấp" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Lệnh kiểm tra" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Cổng" - -#: ejabberd_web_admin.erl:2439 -#, fuzzy -msgid "Protocol" -msgstr "Cổng" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Môđun" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Tùy chọn" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Khởi động" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Lệnh" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "Có thực sự xóa thư trong ngày này không?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Tiêu đề" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Thân thư" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "Không có nội dung trong thư thông báo" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Thông báo" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Gửi thông báo đến tất cả người sử dụng" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Gửi thông báo đến tất cả người sử dụng trên tất cả các máy chủ" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Gửi thông báo đến tất cả người sử dụng trực tuyến" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "" -"Gửi thông báo đến tất cả người sử dụng trực tuyến trên tất cả các máy chủ" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Tạo lập thư trong ngày và gửi đến những người sử dụng trực tuyến" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" -"Tạo lập thư trong ngày trên tất cả các máy chủ và gửi đến những người sử " -"dụng trực tuyến" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Cập nhật thư trong ngày (không gửi)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "Cập nhật thư trong ngày trên tất cả các máy chủ (không gửi)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Xóa thư trong ngày" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Xóa thư trong ngày trên tất cả các máy chủ" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Cấu hình" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Môđun Khởi Động" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Môđun Dừng" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Khôi phục" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Kết xuất ra Tập Tin Văn Bản" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Nhập Tập Tin" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Nhập Thư Mục" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Khởi Động Lại Dịch Vụ" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Tắt Dịch Vụ" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Xóa Người Sử Dụng" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Kết Thúc Phiên Giao Dịch Người Sử Dụng" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Nhận Mật Khẩu Người Sử Dụng" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Thay Đổi Mật Khẩu Người Sử Dụng" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "Nhận Thời Gian Đăng Nhập Cuối Cùng Của Người Sử Dụng" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Nhận Thông Tin Thống Kê Người Sử Dụng" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Nhận Số Người Sử Dụng Đã Đăng Ký" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Nhận Số Người Sử Dụng Trực Tuyến" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Quản Lý Người Sử Dụng" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Tất Cả Người Sử Dụng" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Kết Nối Bên Ngoài s2s" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Quản lý Sao Lưu Dự Phòng" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Nhập Người Sử Dụng Từ Các Tập Tin Spool jabberd14" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "Gửi đến ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "Nhận từ ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Cấu Hình Bảng Cơ Sở Dữ Liệu tại" - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Chọn loại bảng lưu trữ" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Chỉ sao chép vào đĩa" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "Sao chép vào RAM và đĩa" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "Sao chép vào RAM" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "Sao chép từ xa" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Môđun Dừng tại" - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Chọn môđun để dừng" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Môđun Khởi Động tại " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Nhập danh sách {Môđun, [Các Tùy Chọn]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Danh sách các môđun khởi động" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Sao lưu dự phòng ra Tập Tin tại" - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Nhập đường dẫn đến tập tin sao lưu dự phòng" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Đường dẫn đến Tập Tin" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Phục hồi Sao Lưu từ Tập Tin tại " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Kết Xuất Sao Lưu ra Tập Tin Văn Bản tại" - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Nhập đường dẫn đến tập tin văn bản" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Nhập Người Sử Dụng từ Tập Tin tại" - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Nhập đường dẫn đến tập tin spool jabberd14" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Nhập Người Sử Dụng từ Thư Mục tại" - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Nhập đường dẫn đến thư mục spool jabberd14" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Đường Dẫn đến Thư Mục" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "Thời gian trì hoãn" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Cấu Hình Danh Sách Kiểm Soát Truy Cập" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Danh sách kiểm soát truy cập" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Cấu Hình Truy Cập" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Quy tắc Truy Cập" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Kiểm Tra Mật Khẩu" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Số người sử dụng đã đăng ký" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Số người sử dụng trực tuyến" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Đăng nhập lần cuối" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "Kích thước bảng phân công" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "Địa chỉ IP" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Nguồn tài nguyên" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Quản trị về " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Hành động đối với người sử dụng" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Chỉnh Sửa Thuộc Tính" - -#: mod_fail2ban.erl:95 -msgid "" -"Too many (~p) failed authentications from this IP address (~s). The address " -"will be unblocked at ~s UTC" -msgstr "" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "" - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "" - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "Sự truy cập bị chặn theo chính sách phục vụ" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "Truyền tải IRC" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "Môdun ejabberd IRC Bản quyền" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "" -"Bạn cần có một trình ứng dụng khách hỗ trợ định dạng dữ liệu x: để xác định " -"các thiết lập mod_irc" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Đăng ký trong mod_irc cho " - -#: mod_irc.erl:659 -#, fuzzy -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Nhập tên truy cập và mã hóa mà bạn muốn sử dụng khi kết nối với các máy chủ " -"IRC" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "Tên truy cập IRC" - -#: mod_irc.erl:682 -#, fuzzy -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Nếu bạn muốn xác định các cách thức mã hóa khác nhau cho các máy chủ IRC, " -"hãy điền vào danh sách này những giá trị theo định dạng '{\"máy chủ irc\", " -"\"mã hóa\"}'. Dịch vụ này mặc định sử dụng định dạng mã hóa \"~s\"." - -#: mod_irc.erl:704 -#, fuzzy -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Ví dụ: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", " -"\"iso8859-1\"}]" - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "" - -#: mod_irc.erl:903 -#, fuzzy -msgid "IRC server" -msgstr "Tên truy cập IRC" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "" - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "" - -#: mod_irc.erl:1051 -#, fuzzy -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Nhập tên truy cập và mã hóa mà bạn muốn sử dụng khi kết nối với các máy chủ " -"IRC" - -#: mod_irc.erl:1060 -#, fuzzy -msgid "IRC username" -msgstr "Tên truy cập IRC" - -#: mod_irc.erl:1126 -#, fuzzy -msgid "Password ~b" -msgstr "Mật Khẩu" - -#: mod_irc.erl:1137 -#, fuzzy -msgid "Port ~b" -msgstr "Cổng" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "" - -#: mod_mam.erl:541 -#, fuzzy -msgid "Only members may query archives of this room" -msgstr "Chỉ có những người điều phối được phép thay đổi chủ đề trong phòng này" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "Chỉ có người quản trị dịch vụ mới được phép gửi những thư dịch vụ" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "Việc tạo phòng bị ngăn lại theo chính sách dịch vụ" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "Phòng họp không tồn tại" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Phòng trò chuyện" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "" - -#: mod_muc.erl:933 -#, fuzzy -msgid "You need a client that supports x:data to register the nickname" -msgstr "" -"Bạn cần có một trình ứng dụng khách hỗ trợ định dạng dữ liệu x: để đăng ký " -"bí danh" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Đăng Ký Bí Danh tại" - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Nhập bí danh bạn muốn đăng ký" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Bí danh" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -#, fuzzy -msgid "That nickname is registered by another person" -msgstr "Một người khác đã đăng ký bí danh này rồi" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Bạn phải điền thông tin vào ô \"Nickname\" trong biểu mẫu này" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "Môdun ejabberd MUC Bản quyền" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "" - -#: mod_muc_admin.erl:249 -#, fuzzy -msgid "Total rooms" -msgstr "Phòng trò chuyện" - -#: mod_muc_admin.erl:250 -#, fuzzy -msgid "Permanent rooms" -msgstr "rời khỏi phòng này" - -#: mod_muc_admin.erl:251 -#, fuzzy -msgid "Registered nicknames" -msgstr "Người Sử Dụng Đã Đăng Ký" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "Cấu hình phòng trò chuyện được chỉnh sửa" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "tham gia phòng này" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "rời khỏi phòng này" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "đã bị cấm" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "bây giờ được biết như" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " đã đặt chủ đề thành: " - -#: mod_muc_log.erl:493 -#, fuzzy -msgid "Chatroom is created" -msgstr "Phòng trò chuyện" - -#: mod_muc_log.erl:495 -#, fuzzy -msgid "Chatroom is destroyed" -msgstr "Phòng trò chuyện" - -#: mod_muc_log.erl:497 -#, fuzzy -msgid "Chatroom is started" -msgstr "Phòng trò chuyện" - -#: mod_muc_log.erl:499 -#, fuzzy -msgid "Chatroom is stopped" -msgstr "Phòng trò chuyện" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "Thứ Hai" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "Thứ Ba" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "Thứ Tư" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "Thứ Năm" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "Thứ Sáu" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "Thứ Bảy" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "Chủ Nhật" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "Tháng Một" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "Tháng Hai" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "Tháng Ba" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "Tháng Tư" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "Tháng Năm" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "Tháng Sáu" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "Tháng Bảy" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "Tháng Tám" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "Tháng Chín" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "Tháng Mười" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "Tháng Mười Một" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "Tháng Mười Hai" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Cấu Hình Phòng" - -#: mod_muc_log.erl:932 -#, fuzzy -msgid "Room Occupants" -msgstr "Số người tham dự" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "Quá giới hạn tỷ lệ lưu lượng truyền tải" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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 "" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "Không được phép gửi những thư riêng đến phòng họp" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "" - -#: mod_muc_room.erl:377 -#, fuzzy -msgid "Only moderators can approve voice requests" -msgstr "Cho phép người sử dụng gửi lời mời" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Loại thư không phù hợp" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "Không được phép gửi những thư riêng loại \"groupchat\"" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "Người nhận không có trong phòng họp" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -#, fuzzy -msgid "It is not allowed to send private messages" -msgstr "Không được phép gửi những thư riêng đến phòng họp" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Chỉ có những đối tượng tham gia mới được phép gửi thư đến phòng họp" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "" -"Chỉ có những đối tượng tham gia mới được phép gửi yêu cầu đến phòng họp" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "" -"Không được phép gửi các yêu cầu gửi đến các thành viên trong phòng họp này" - -#: mod_muc_room.erl:961 -#, fuzzy -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "" -"Chỉ có những người điều phối và những người tham gia được phép thay đổi chủ " -"đề trong phòng này" - -#: mod_muc_room.erl:966 -#, fuzzy -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Chỉ có những người điều phối được phép thay đổi chủ đề trong phòng này" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "Người ghé thăm không được phép gửi thư đến tất cả các người tham dự" - -#: mod_muc_room.erl:1080 -#, fuzzy -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "Chỉ có những người điều phối được phép thay đổi chủ đề trong phòng này" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -#, fuzzy -msgid "That nickname is already in use by another occupant" -msgstr "Bí danh đang do một người tham dự khác sử dụng" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Bạn bị cấm tham gia phòng này" - -#: mod_muc_room.erl:1826 -#, fuzzy -msgid "Membership is required to enter this room" -msgstr "Yêu cầu tư cách thành viên khi tham gia vào phòng này" - -#: mod_muc_room.erl:1872 -#, fuzzy -msgid "A password is required to enter this room" -msgstr "Yêu cầu nhập mật khẩu để vào phòng này" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Mật khẩu sai" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "Yêu cầu đặc quyền của nhà quản trị" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "Yêu cầu đặc quyền của nhà điều phối" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "Jabber ID ~s không hợp lệ" - -#: mod_muc_room.erl:2772 -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:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Tư cách không hợp lệ: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Vai trò không hợp lệ: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "Yêu cầu đặc quyền của người sở hữu" - -#: mod_muc_room.erl:3348 -#, fuzzy -msgid "Configuration of room ~s" -msgstr "Cấu hình cho " - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Tên phòng" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -#, fuzzy -msgid "Room description" -msgstr "Miêu tả:" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Tạo phòng bền vững" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Tạo phòng có thể tìm kiếm công khai" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "Tạo danh sách người tham dự công khai" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Tạo phòng được bảo vệ bằng mật khẩu" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Số Lượng Người Tham Dự Tối Đa" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Không giới hạn" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Jabber ID thực tế hiện hành đến" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "nhà điều phối duy nhất" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "bất kỳ ai" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "" - -#: mod_muc_room.erl:3486 -#, fuzzy -msgid "Moderator" -msgstr "nhà điều phối duy nhất" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Tạo phòng chỉ cho phép tư cách thành viên tham gia" - -#: mod_muc_room.erl:3516 -#, fuzzy -msgid "Make room moderated" -msgstr "Tạo phòng bền vững" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Người sử dụng mặc định là người tham dự" - -#: mod_muc_room.erl:3522 -#, fuzzy -msgid "Allow users to change the subject" -msgstr "Cho phép người sử dụng thay đổi chủ đề" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Cho phép người sử dụng gửi thư riêng" - -#: mod_muc_room.erl:3533 -#, fuzzy -msgid "Allow visitors to send private messages to" -msgstr "Cho phép người sử dụng gửi thư riêng" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Cho phép người sử dụng hỏi người sử dụng khác" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Cho phép người sử dụng gửi lời mời" - -#: mod_muc_room.erl:3582 -#, fuzzy -msgid "Allow visitors to send status text in presence updates" -msgstr "Cho phép người sử dụng gửi thư riêng" - -#: mod_muc_room.erl:3586 -#, fuzzy -msgid "Allow visitors to change nickname" -msgstr "Cho phép người sử dụng thay đổi chủ đề" - -#: mod_muc_room.erl:3589 -#, fuzzy -msgid "Allow visitors to send voice requests" -msgstr "Cho phép người sử dụng gửi lời mời" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "" - -#: mod_muc_room.erl:3599 -#, fuzzy -msgid "Make room CAPTCHA protected" -msgstr "Tạo phòng được bảo vệ bằng mật khẩu" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Cho phép ghi nhật ký" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "" -"Bạn cần có một trình ứng dụng khách hỗ trợ định dạng dữ liệu x: để xác định " -"cấu hình phòng họp" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Số người tham dự" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "riêng," - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "" - -#: mod_muc_room.erl:4351 -#, fuzzy -msgid "User JID" -msgstr "Người sử dụng " - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s mời bạn vào phòng ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "mật khẩu là" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -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ỏ." - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "~s's Danh Sách Chờ Thư Ngoại Tuyến" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Thời Gian" - -#: mod_offline.erl:812 -msgid "From" -msgstr "Từ" - -#: mod_offline.erl:813 -msgid "To" -msgstr "Đến" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Gói thông tin" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Thư Ngoại Tuyến:" - -#: mod_offline.erl:996 -#, fuzzy -msgid "Remove All Offline Messages" -msgstr "Thư Ngoại Tuyến" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "Môdun SOCKS5 Bytestreams Bản quyền" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Xuất Bản-Đăng Ký" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "Môdun ejabberd Xuất Bản-Đăng Ký Bản quyền" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "Yêu cầu người đăng ký môđun Xuất Bản Đăng Ký" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Chọn có nên chấp nhận sự đăng ký của đối tượng này không" - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "ID Nút" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Địa Chỉ Người Đăng Ký" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "Cho phép Jabber ID đăng ký nút môđun xuất bản đăng ký này không?" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Đưa ra thông tin dung lượng với các thông báo sự kiện" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Đưa ra các thông báo sự kiện" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Thông báo cho người đăng ký khi nào cấu hình nút thay đổi" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Thông báo cho người đăng ký khi nào nút bị xóa bỏ" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Thông báo cho người đăng ký khi nào các mục chọn bị gỡ bỏ khỏi nút" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Những mục cần để lưu trữ" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Số mục tối đa để lưu trữ" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Xác định nên cho phép đăng ký không" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Xác định mô hình truy cập" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Xác định mô hình nhà xuất bản" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "" - -#: mod_pubsub.erl:3771 -#, fuzzy -msgid "Specify the event message type" -msgstr "Xác định mô hình truy cập" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Kích thước dung lượng byte tối đa" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "Khi cần gửi mục được xuất bản cuối cùng" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Chỉ gửi thông báo đến những người sử dụng hiện có" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "" - -#: mod_register.erl:253 -#, fuzzy -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "" -"Bạn cần có một trình ứng dụng khách hỗ trợ định dạng dữ liệu x: để đăng ký " -"bí danh" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "Chọn một tên truy cập và mật khẩu để đăng ký với máy chủ này" - -#: mod_register.erl:373 mod_register.erl:421 -#, fuzzy -msgid "The password is too weak" -msgstr "mật khẩu là" - -#: mod_register.erl:426 -#, fuzzy -msgid "Users are not allowed to register accounts so quickly" -msgstr "Người ghé thăm không được phép gửi thư đến tất cả các người tham dự" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "" - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "" - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "" - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "" - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "" - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "" - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -#, fuzzy -msgid "Username:" -msgstr "Tên truy cập IRC" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "" - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -#, fuzzy -msgid "Server:" -msgstr "Không bao giờ" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "" - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" - -#: mod_register_web.erl:262 mod_register_web.erl:374 -#, fuzzy -msgid "Password Verification:" -msgstr "Kiểm Tra Mật Khẩu" - -#: mod_register_web.erl:269 -#, fuzzy -msgid "Register" -msgstr "Bảng phân công" - -#: mod_register_web.erl:366 -#, fuzzy -msgid "Old Password:" -msgstr "Mật Khẩu:" - -#: mod_register_web.erl:370 -#, fuzzy -msgid "New Password:" -msgstr "Mật Khẩu:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "" - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Đăng ký" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Chờ" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Nhóm" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Xác nhận hợp lệ" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Gỡ bỏ" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Bảng phân công của " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Thêm Jabber ID" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Bảng phân công" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Nhóm Phân Công Chia Sẻ" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Tên:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Miêu tả:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Thành viên:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Nhóm được hiển thị:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Nhóm " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Erlang Jabber Server Bản quyền" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Ngày sinh" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Thành phố" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "Quốc gia" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "Email" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "Họ" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Điền vào mẫu này để tìm kiếm bất kỳ thông tin nào khớp với Người sử dụng " -"Jabber (Thêm dấu * vào cuối ô để thông tin khớp với chuỗi bên trong)" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "Tên Đầy Đủ" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "Họ Đệm" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "Tên Tổ Chức" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Bộ Phận" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Tìm kiếm người sử dụng trong" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "" -"Bạn cần có một trình ứng dụng khách hỗ trợ định dạng dữ liệu x: để tìm kiếm" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "Tìm Kiếm Người Sử Dụng vCard" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "Môdun ejabberd vCard Bản quyền" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Kết Quả Tìm Kiếm cho " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "" -"Điền vào các ô để tìm kiếm bất kỳ các thông tin nào khớp với Người sử dụng " -"Jabber" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Máy chủ Bên Ngoài s2s:" - -#~ msgid "Delete" -#~ msgstr "Xóa" - -#~ msgid "This room is not anonymous" -#~ msgstr "Phòng này không nặc danh" - -#~ msgid "Encodings" -#~ msgstr "Mã hóa" - -#~ msgid "(Raw)" -#~ msgstr "(Thô)" - -#~ msgid "Specified nickname is already registered" -#~ msgstr "Bí danh xác định đã đăng ký rồi" - -#~ msgid "Size" -#~ msgstr "Kích thước" - -#~ msgid "Roster groups that may subscribe (if access model is roster)" -#~ msgstr "" -#~ "Các nhóm phân công có thể đăng ký (nếu mô hình truy cập là dạng phân công)" diff --git a/priv/msgs/wa.msg b/priv/msgs/wa.msg index 8946b71dd..23fcf49f5 100644 --- a/priv/msgs/wa.msg +++ b/priv/msgs/wa.msg @@ -1,21 +1,20 @@ -%% -*- coding: latin-1 -*- -{"Accept","Accepter"}. -{"Access Configuration","Apontiaedje des accès"}. -{"Access Control List Configuration","Apontiaedje des droets (ACL)"}. -{"Access control lists","Droets (ACL)"}. -{"Access Control Lists","Droets (ACL)"}. -{"Access denied by service policy","L' accès a stî rfuzé pal politike do siervice"}. -{"Access rules","Rîles d' accès"}. -{"Access Rules","Rîles d' accès"}. -{"Action on user","Accion so l' uzeu"}. -{"Add Jabber ID","Radjouter èn ID Jabber"}. -{"Add New","Radjouter"}. -{"Add User","Radjouter èn uzeu"}. -{"Administration","Manaedjaedje"}. -{"Administration of ","Manaedjaedje di "}. -{"Administrator privileges required","I fåt des priviledjes di manaedjeu"}. +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" has set the subject to: "," a candjî l' tite a: "}. {"A friendly name for the node","On no uzeu-ahessåve pol nuk"}. +{"A password is required to enter this room","I fåt dner on scret po poleur intrer dins cisse såle ci"}. +{"Accept","Accepter"}. +{"Access denied by service policy","L' accès a stî rfuzé pal politike do siervice"}. +{"Action on user","Accion so l' uzeu"}. +{"Add User","Radjouter èn uzeu"}. +{"Administration of ","Manaedjaedje di "}. +{"Administration","Manaedjaedje"}. +{"Administrator privileges required","I fåt des priviledjes di manaedjeu"}. {"All activity","Dispoy todi"}. +{"All Users","Tos les uzeus"}. {"Allow this Jabber ID to subscribe to this pubsub node?","Permete ki ci Jabber ID ci si poye abouner a ç' nuk eplaidaedje-abounmint ci?"}. {"Allow users to change the subject","Les uzeus polèt candjî l' tite"}. {"Allow users to query other users","Les uzeus polèt cweri ls ôtes uzeus"}. @@ -25,16 +24,13 @@ {"Allow visitors to send private messages to","Les uzeus polèt evoyî des messaedjes privés"}. {"Allow visitors to send status text in presence updates","Permete ki les viziteus evoyexhe des tecse d' estat dins leus messaedjes di prezince"}. {"Allow visitors to send voice requests","Les uzeus polèt evoyî des dmandes di vwès"}. -{"All Users","Tos les uzeus"}. {"Announcements","Anonces"}. -{"anyone","tot l' minme kî"}. -{"A password is required to enter this room","I fåt dner on scret po poleur intrer dins cisse såle ci"}. {"April","avri"}. {"August","awousse"}. -{"Backup","Copeye di såvrité"}. {"Backup Management","Manaedjaedje des copeyes di såvrité"}. {"Backup of ~p","Copeye di såvrité po ~p"}. {"Backup to File at ","Fé ene copeye di såvrité dins on fitchî so "}. +{"Backup","Copeye di såvrité"}. {"Bad format","Mwais fôrmat"}. {"Birthday","Date d' askepiaedje"}. {"CAPTCHA web page","Pådje web CAPTCHA"}. @@ -48,63 +44,44 @@ {"Chatroom is stopped","Li såle di berdelaedje est ahotêye"}. {"Chatrooms","Såles di berdelaedje"}. {"Choose a username and password to register with this server","Tchoezixhoz on no d' uzeu eyet on scret po vs edjîstrer so ç' sierveu ci"}. -{"Choose modules to stop","Tchoezixhoz les modules a-z arester"}. {"Choose storage type of tables","Tchoezi l' sôre di wårdaedje po les tåves"}. {"Choose whether to approve this entity's subscription.","Tchoezi s' i fåt aprover ou nén l' abounmint di ciste intité."}. {"City","Veye"}. {"Commands","Comandes"}. {"Conference room does not exist","Li såle di conferince n' egzistêye nén"}. -{"Configuration","Apontiaedjes"}. {"Configuration of room ~s","Apontiaedje del såle ~s"}. -{"Connected Resources:","Raloyî avou les rsoûces:"}. -{"Connections parameters","Parametes des raloyaedjes"}. +{"Configuration","Apontiaedjes"}. {"Country","Payis"}. -{"CPU Time:","Tins CPU:"}. -{"Database","Båze di dnêyes"}. -{"Database Tables at ~p","Tåves del båze di dnêyes so ~p"}. {"Database Tables Configuration at ","Apontiaedje des tåves del båze di dnêyes so "}. +{"Database","Båze di dnêyes"}. {"December","decimbe"}. {"Default users as participants","Les uzeus sont des pårticipants come prémetowe dujhance"}. -{"Delete message of the day","Disfacer l' messaedje do djoû"}. {"Delete message of the day on all hosts","Disfacer l' messaedje do djoû so tos les lodjoes"}. -{"Delete Selected","Disfacer les elemints tchoezis"}. +{"Delete message of the day","Disfacer l' messaedje do djoû"}. {"Delete User","Disfacer èn uzeu"}. {"Deliver event notifications","Evoyî des notifiaedjes d' evenmints"}. {"Deliver payloads with event notifications","Evoyî l' contnou avou les notifiaedjes d' evenmints"}. -{"Description:","Discrijhaedje:"}. {"Disc only copy","Copeye seulmint sol deure plake"}. -{"Displayed Groups:","Groupes håynés:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","Ni dnez vosse sicret a nolu, nén ddja ås manaedjeus do sierveu Jabber."}. {"Dump Backup to Text File at ","Copeye di såvritè viè on fitchî tecse so "}. {"Dump to Text File","Schaper en on fitchî tecse"}. {"Edit Properties","Candjî les prôpietés"}. {"Either approve or decline the voice request.","Aprover oudonbén rifuzer li dmande di vwès."}. -{"ejabberd IRC module","Module IRC po ejabberd"}. {"ejabberd MUC module","Module MUC (såles di berdelaedje) po ejabberd"}. {"ejabberd Multicast service","siervice multicast d' ejabberd"}. {"ejabberd Publish-Subscribe module","Module d' eplaidaedje-abounmint po ejabberd"}. {"ejabberd SOCKS5 Bytestreams module","Module SOCKS5 Bytestreams po ejabberd"}. {"ejabberd vCard module","Module vCard ejabberd"}. {"ejabberd Web Admin","Manaedjeu waibe ejabberd"}. -{"Elements","Elemints"}. {"Email","Emile"}. -{"Empty Rooms","Såles vudes"}. {"Enable logging","Mete en alaedje li djournå"}. {"Enable message archiving","Mete en alaedje l' årtchivaedje des messaedjes"}. -{"Encoding for server ~b","Ecôdaedje pol sierveu ~b"}. {"End User Session","Fini l' session d' l' uzeu"}. -{"Enter list of {Module, [Options]}","Dinez ene djivêye del cogne {Module, [Tchuzes]}"}. {"Enter nickname you want to register","Dinez l' metou no ki vos vloz edjîstrer"}. {"Enter path to backup file","Dinez l' tchimin viè l' fitchî copeye di såvrité"}. {"Enter path to jabberd14 spool dir","Dinez l' tchimin viè l' ridant di spool jabberd14"}. {"Enter path to jabberd14 spool file","Dinez l' tchimin viè l' fitchî di spool jabberd14"}. {"Enter path to text file","Dinez l' tchimin viè l' fitchî tecse"}. {"Enter the text you see","Tapez l' tecse ki vos voeyoz"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Dinez les nos d' uzeu et ls ecôdaedjes ki vos vloz eployî po vs raloyî åzès sierveus IRC Clitchîz so «Shuvant» po-z aveur di pus di tchamps a rimpli. Clitchîz so «Fini» po schaper les apontiaedjes."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Dinez l' no d' uzeu, les ecôdaedjes, les pôrts et les screts ki vos vloz eployî po vs raloyî åzès sierveus IRC"}. -{"Erlang Jabber Server","Sierveu Jabber Erlang"}. -{"Error","Aroke"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Egzimpe: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. {"Exclude Jabber IDs from CAPTCHA challenge","Esclure les IDs Jabber des kesses CAPTCHA"}. {"Export all tables as SQL queries to a file:","Espoirter totes les tåves, come des cmandes SQL, viè on fitchî"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Espoirter les dnêyes di tos les uzeus do sierveu viè des fitchîs PIEFXIS (XEP-0227):"}. @@ -112,29 +89,19 @@ {"Failed to extract JID from your voice request approval","Nén moyén di rsaetchî on JID foû d' l' aprovaedje di vosse dimande di vwès"}. {"Family Name","No d' famile"}. {"February","fevrî"}. -{"Fill in fields to search for any matching Jabber User","Rimplixhoz les tchamps po cweri èn uzeu Jabber"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Rimplixhoz les tchamps do formulaire po cweri èn uzeu Jabber (radjouter «*» al fén do tchamp po cweri tot l' minme kéne fén d' tchinne"}. {"Friday","vénrdi"}. -{"From","Di"}. -{"From ~s","Dispoy ~s"}. {"Full Name","No etir"}. {"Get Number of Online Users","Riçure li nombe d' uzeus raloyîs"}. {"Get Number of Registered Users","Riçure li nombe d' uzeus edjîstrés"}. {"Get User Last Login Time","Riçure li date/eure do dierin elodjaedje di l' uzeu"}. -{"Get User Password","Riçure sicret d' l' uzeu"}. {"Get User Statistics","Riçure les statistikes di l' uzeu"}. {"Grant voice to this person?","Permete li vwès po cisse djin ci?"}. -{"Group ","Groupe "}. -{"Groups","Groupes"}. {"has been banned","a stî bani"}. -{"has been kicked","a stî pité evoye"}. -{"has been kicked because of an affiliation change","a stî pité evoye cåze d' on candjmint d' afiyaedje"}. {"has been kicked because of a system shutdown","a stî pité evoye cåze d' èn arestaedje do sistinme"}. +{"has been kicked because of an affiliation change","a stî pité evoye cåze d' on candjmint d' afiyaedje"}. {"has been kicked because the room has been changed to members-only","a stî pité evoye cåze ki l' såle a stî ristrindowe åzès mimbes seulmint"}. -{" has set the subject to: "," a candjî l' tite a: "}. -{"Host","Sierveu"}. +{"has been kicked","a stî pité evoye"}. {"If you don't see the CAPTCHA image here, visit the web page.","Si vos n' voeyoz nole imådje CAPTCHA chal, vizitez l' pådje waibe."}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","Si vos vloz dner des pôrts, sicrets ou ecôdaedjes diferins po les sierveus IRC, rimplixhoz cisse djivêye ci avou des valixhances del cogne «{\"sierveu irc\", \"ecôdaedje\", pôrt, \"sicret\"}». Les prémetowès valixhances do siervice sont «~s» po l' ecôdaedje, «~p» pol pôrt, et on vude sicret."}. {"Import Directory","Sititchî d' on ridant"}. {"Import File","Sititchî d' on fitchî"}. {"Import user data from jabberd14 spool file:","Sititchî des dnêyes uzeus foû d' on fitchî spoûle jabberd14:"}. @@ -144,31 +111,15 @@ {"Import Users from Dir at ","Sitichî des uzeus d' on ridant so "}. {"Import Users From jabberd14 Spool Files","Sititchî des uzeus Jabberd 1.4"}. {"Improper message type","Sôre di messaedje nén valide"}. -{"Incoming s2s Connections:","Raloyaedjes s2s en intrêye:"}. {"Incorrect password","Sicret nén corek"}. -{"Invalid affiliation: ~s","Afiyaedje nén valide: ~s"}. -{"Invalid role: ~s","Role nén valide: ~s"}. {"IP addresses","Adresses IP"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","Canå IRC (èn nén mete li prumî #)"}. -{"IRC server","Sierveu IRC"}. -{"IRC settings","Apontiaedjes IRC"}. -{"IRC Transport","Transpoirt IRC"}. -{"IRC username","No d' uzeu IRC"}. -{"IRC Username","No d' uzeu IRC"}. {"is now known as","est asteure kinoxhou come"}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","On n' pout nén evoyî des messaedjes d' aroke sol såle. Li pårticipan (~s) a-st evoyî on messaedje d' aroke (~s) ey a stî tapé foû."}. -{"It is not allowed to send private messages","Ci n' est nén permetou d' evoyî des messaedjes privés"}. {"It is not allowed to send private messages of type \"groupchat\"","C' est nén possibe d' evoyî des messaedjes privés del sôre «groupchat»"}. {"It is not allowed to send private messages to the conference","On n' pout nén evoyî des messaedjes privés dins cisse conferince ci"}. -{"Jabber Account Registration","Edjîstraedje di conte Jabber"}. {"Jabber ID","ID Jabber"}. -{"Jabber ID ~s is invalid","Li Jabber ID ~s n' est nén valide"}. {"January","djanvî"}. -{"Join IRC channel","Radjonde canå IRC"}. {"joins the room","arive sol såle"}. -{"Join the IRC channel here.","Radjonde li canå IRC droci."}. -{"Join the IRC channel in this Jabber ID: ~s","Radjonde li canå IRC e cist ID Jabber: ~s"}. {"July","djulete"}. {"June","djun"}. {"Last Activity","Dierinne activité"}. @@ -176,11 +127,6 @@ {"Last month","Dierin moes"}. {"Last year","Dierinne anêye"}. {"leaves the room","cwite li såle"}. -{"Listened Ports at ","Pôrts drovous so "}. -{"Listened Ports","Pôrts drovous"}. -{"List of modules to start","Djivêye di modules a-z enonder"}. -{"List of rooms","Djivêye des såles"}. -{"Low level update script","Sicripe di metaedje a djoû d' bas livea"}. {"Make participants list public","Rinde publike li djivêye des pårticipants"}. {"Make room CAPTCHA protected","Rinde li såle di berdelaedje protedjeye pa CAPTCHA"}. {"Make room members-only","Rinde li såle di berdelaedje ristrindowe ås mimbes seulmint"}. @@ -189,44 +135,32 @@ {"Make room persistent","Rinde li såle permaninte"}. {"Make room public searchable","Rinde li såle di berdelaedje cweråve publicmint"}. {"March","måss"}. -{"Maximum Number of Occupants","Nombe macsimom di prezints"}. -{"Max # of items to persist","Nombe macsimoms di cayets permanints"}. {"Max payload size in bytes","Contnou macsimom en octets"}. +{"Maximum Number of Occupants","Nombe macsimom di prezints"}. {"May","may"}. {"Membership is required to enter this room","I fåt esse mimbe po poleur intrer dins cisse såle ci"}. -{"Members:","Mimbes:"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","Rimimbrez vosse sicret, ou scrijhoz l' so on papî ki vos wådroz en ene place bén a hoûte, ca avou Jabber i n' a pont moyén di rapexhî vosse sicret si vos l' rovyîz."}. -{"Memory","Memwere"}. {"Message body","Coir do messaedje"}. {"Middle Name","No do mitan"}. {"Minimum interval between voice requests (in seconds)","Tins minimom etur deus dmandes di vwès (e segondes)"}. -{"Moderator","Moderateu"}. {"Moderator privileges required","I fåt des priviledjes di moderateu"}. -{"moderators only","les moderateus seulmint"}. -{"Modified modules","Modules di candjîs"}. -{"Module","Module"}. -{"Modules at ~p","Modules so ~p"}. -{"Modules","Modules"}. +{"Moderator","Moderateu"}. {"Monday","londi"}. {"Multicast","Multicast"}. {"Multi-User Chat","Berdelaedje a sacwants"}. {"Name","No"}. -{"Name:","Pitit no:"}. {"Never","Måy"}. {"New Password:","Novea scret:"}. -{"Nickname","Metou no"}. {"Nickname Registration at ","Edjîstraedje di metou no amon "}. {"Nickname ~s does not exist in the room","Li metou no ~s n' egzistêye nén dins l' såle"}. -{"nobody","nolu"}. +{"Nickname","Metou no"}. {"No body provided for announce message","I n' a nou coir do messaedje po ciste anonce la"}. {"No Data","Nole dinêye disponibe"}. +{"No limit","Pont d' limite"}. {"Node ID","ID d' nuk"}. {"Node not found","Nuk nén trové"}. {"Node ~p","Nuk ~p"}. {"Nodes","Nuks"}. -{"No limit","Pont d' limite"}. {"None","Nole"}. -{"No resource provided","Nole rissoûce di dnêye"}. {"Not Found","Nén trové"}. {"Notify subscribers when items are removed from the node","Notifyî åzès abounés cwand des cayets sont oisté foû do nuk"}. {"Notify subscribers when the node configuration changes","Notifyî åzès abounés cwand l' apontiaedje do nuk candje"}. @@ -236,13 +170,10 @@ {"Number of online users","Nombe d' uzeus raloyîs"}. {"Number of registered users","Nombe d' uzeus edjîstrés"}. {"October","octôbe"}. -{"Offline Messages:","Messaedjes ki ratindèt:"}. -{"Offline Messages","Messaedjes ki ratindèt"}. {"OK","'l est bon"}. {"Old Password:","Vî scret:"}. -{"Online","Raloyî"}. -{"Online Users:","Uzeus raloyîs:"}. {"Online Users","Uzeus raloyîs"}. +{"Online","Raloyî"}. {"Only deliver notifications to available users","Seulmint evoyî des notifiaedje åzès uzeus disponibes"}. {"Only members may query archives of this room","Seulmint les mimbes polèt cweri les årtchives dins cisse såle ci"}. {"Only moderators and participants are allowed to change the subject in this room","Seulmint les moderateus et les pårticipants polèt candjî l' sudjet dins cisse såle ci"}. @@ -251,58 +182,38 @@ {"Only occupants are allowed to send messages to the conference","Seulmint les prezints polèt evoyî des messaedjes al conferince"}. {"Only occupants are allowed to send queries to the conference","Seulmint les prezints polèt evoyî des cweraedjes sol conferince"}. {"Only service administrators are allowed to send service messages","Seulmint les manaedjeus d' siervices polèt evoyî des messaedjes di siervice"}. -{"Options","Tchuzes"}. {"Organization Name","No d' l' organizåcion"}. {"Organization Unit","Unité d' l' organizåcion"}. -{"Outgoing s2s Connections:","Raloyaedjes s2s e rexhowe:"}. {"Outgoing s2s Connections","Raloyaedjes s2s e rexhowe"}. {"Owner privileges required","I fåt des priviledjes di prôpietaire"}. -{"Packet","Paket"}. {"Participant","Pårticipant"}. -{"Password ~b","Sicret ~b"}. -{"Password:","Sicret:"}. -{"Password","Sicret"}. -{"Password Verification:","Acertinaedje do scret:"}. {"Password Verification","Acertinaedje do scret"}. +{"Password Verification:","Acertinaedje do scret:"}. +{"Password","Sicret"}. +{"Password:","Sicret:"}. {"Path to Dir","Tchimin viè l' ridant"}. {"Path to File","Tchimin viè l' fitchî"}. -{"Pending","Ratindant"}. {"Period: ","Termene:"}. -{"Permanent rooms","Såles tofer la"}. {"Persist items to storage","Cayets permanints a wårder"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Notez ki ces tchuzes la vont seulmint fé ene copeye di såvrité del båze di dnêyes Mnesia costrûte å dvins do programe. Si vos eployîz ene difoûtrinne båze di dnêyes avou l' module ODBC, vos dvoz fé ene copeye di såvrité del båze SQL da vosse sepårumint."}. -{"Please specify file name.","Dinez l' no do fitchî."}. -{"Please specify file size.","Dinez l' grandeu do fitchî."}. {"Please, wait for a while before sending new voice request","Ratindez ene miete s' i vs plait divant d' rivoyî ene nouve dimande di vwès"}. {"Pong","Pong"}. -{"Port ~b","Pôrt ~b"}. -{"Port","Pôrt"}. {"Present real Jabber IDs to","Mostrer les vraiys Jabber IDs a"}. {"private, ","privé, "}. -{"Protocol","Protocole"}. {"Publish-Subscribe","Eplaidaedje-abounmint"}. {"PubSub subscriber request","Dimande d' eplaidaedje-abounmint d' èn abouné"}. {"Purge all items when the relevant publisher goes offline","Purdjî tos les cayets cwand l' eplaideu aloyî va foû raloyaedje"}. {"Queries to the conference members are not allowed in this room","Les cweraedjes des mimbes del conferince ni sont nén permetous dins cisse såle ci"}. {"RAM and disc copy","Copeye e memwere (RAM) et sol deure plake"}. {"RAM copy","Copeye e memwere (RAM)"}. -{"Raw","Dinêyes brutes"}. {"Really delete message of the day?","Voloz vs vormint disfacer l' messaedje do djoû?"}. {"Recipient is not in the conference room","Li riçuveu n' est nén dins l' såle di conferince"}. -{"Register a Jabber account","Edjîstrer on conte Jabber"}. {"Register","Edjîstrer"}. -{"Registered nicknames","Metous nos edjistrés"}. -{"Registered Users:","Uzeus edjistrés:"}. -{"Registered Users","Uzeus edjistrés"}. -{"Registration in mod_irc for ","Edjîstraedje dins mod_irc po "}. {"Remote copy","Copeye å lon"}. -{"Remove All Offline Messages","Oister tos les messaedjes ki ratindèt"}. -{"Remove","Oister"}. {"Remove User","Disfacer l' uzeu"}. {"Replaced by new connection","Replaecî pa on novea raloyaedje"}. {"Resources","Rissoûces"}. -{"Restart","Renonder"}. {"Restart Service","Renonder siervice"}. {"Restore Backup from File at ","Rapexhî dispoy li fitchî copeye di såvrité so "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Rapexhî l' copeye di såvrité binaire après l' renondaedje ki vént d' ejabberd (çoula prind moens d' memwere del fé insi):"}. @@ -315,106 +226,63 @@ {"Room description","Discrijhaedje del såle"}. {"Room Occupants","Prezints el såle"}. {"Room title","Tite del såle"}. -{"Roster","Djivêye des soçons"}. {"Roster groups allowed to subscribe","Pårtaedjîs groupes di soçons k' on s' î pout abouner"}. -{"Roster of ","Djivêye des soçons da "}. {"Roster size","Grandeu del djivêye des soçons"}. -{"RPC Call Error","Aroke di houcaedje RPC"}. {"Running Nodes","Nuks en alaedje"}. -{"~s access rule configuration","Apontiaedje des rîles d' accès a ~s"}. {"Saturday","semdi"}. -{"Script check","Acertinaedje do scripe"}. {"Search Results for ","Rizultats do cweraedje po "}. {"Search users in ","Cweri des uzeus dins "}. -{"Send announcement to all online users","Evoyî l' anonce a tos les uzeus raloyîs"}. {"Send announcement to all online users on all hosts","Evoyî l' anonce a tos les uzeus raloyîs so tos les lodjoes"}. -{"Send announcement to all users","Evoyî l' anonce a tos les uzeus"}. +{"Send announcement to all online users","Evoyî l' anonce a tos les uzeus raloyîs"}. {"Send announcement to all users on all hosts","Evoyî l' anonce a tos les uzeus so tos les lodjoes"}. +{"Send announcement to all users","Evoyî l' anonce a tos les uzeus"}. {"September","setimbe"}. -{"Server ~b","Sierveu ~b"}. {"Server:","Sierveu:"}. -{"Server","Sierveu"}. {"Set message of the day and send to online users","Defini l' messaedje do djoû et l' evoyî åzès uzeus raloyîs"}. {"Set message of the day on all hosts and send to online users","Defini l' messaedje do djoû so tos les lodjoes et l' evoyî åzès uzeus raloyîs"}. {"Shared Roster Groups","Pårtaedjîs groupes ezès djivêyes di soçons"}. {"Show Integral Table","Mostrer totå"}. {"Show Ordinary Table","Mostrer crexhince"}. {"Shut Down Service","Arester siervice"}. -{"~s invites you to the room ~s","~s vos preye sol såle ~s"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Des cliyints Jabber k' i gn a polèt wårder vosse sicret sol copiutrece, mins vos n' duvrîz fé çoula ki sol copiutrece da vosse, po des råjhons di såvrité."}. {"Specify the access model","Sipecifyî l' modele d' accès"}. {"Specify the event message type","Sipecifyî l' sôre do messaedje d' evenmint"}. {"Specify the publisher model","Dinez l' modele d' eplaideu"}. -{"~s's Offline Messages Queue","messaedjes ki ratindèt el cawêye po ~s"}. -{"Start","Enonder"}. -{"Start Modules at ","Renonder les modules so "}. -{"Start Modules","Enonder des modules"}. -{"Statistics of ~p","Sitatistikes di ~p"}. -{"Statistics","Sitatistikes"}. -{"Stop","Arester"}. -{"Stop Modules","Arester des modules"}. -{"Stop Modules at ","Arester les modules so "}. {"Stopped Nodes","Nuks essoctés"}. -{"Storage Type","Sôre di wårdaedje"}. {"Store binary backup:","Copeye di såvrité binaire:"}. {"Store plain text backup:","Copeye di såvrité tecse:"}. {"Subject","Sudjet"}. -{"Submit","Evoyî"}. {"Submitted","Candjmints evoyîs"}. {"Subscriber Address","Adresse di l' abouné"}. -{"Subscription","Abounmimnt"}. {"Sunday","dimegne"}. {"That nickname is already in use by another occupant","Li metou no est ddja eployî pa ene ôte sakî sol såle"}. {"That nickname is registered by another person","Li metou no est ddja edjîstré pa ene ôte sakî"}. {"The CAPTCHA is valid.","Li CAPTCHA est valide."}. {"The CAPTCHA verification has failed","Li verifiaedje CAPTCHA a fwait berwete"}. {"The collections with which a node is affiliated","Les ramexhnêyes k' on nuk est afiyî avou"}. -{"the password is","li scret est"}. {"The password is too weak","li scret est trop flåw"}. -{"The password of your Jabber account was successfully changed.","Li scret do conte Jabber da vosse a stî candjî comifåt."}. -{"There was an error changing the password: ","Åk n' a nén stî tot candjant l' sicret: "}. +{"the password is","li scret est"}. {"There was an error creating the account: ","Åk n' a nén stî tot ahivant l' conte: "}. {"There was an error deleting the account: ","Åk n' a nén stî tot disfaçant l' conte: "}. -{"This IP address is blacklisted in ~s","Ciste adresse IP est so ene noere djivêye e ~s"}. -{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Pont d' diferince etur les grandes et ptitès letes: «macbeth» est l' minme ki «MacBeth» ou co «Macbeth»"}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Cisse pådje permete d' ahiver on conte Jabber so ç' sierveu Jabber ci. Li JID (IDintifieu Jabber) da vosse serè del cogne: noduzeu@sierveu. Lijhoz atintivmint les instruccions po bén rimpli les tchamps."}. -{"This page allows to unregister a Jabber account in this Jabber server.","Cisse pådje permete di disdjîstrer on conte Jabber so ç' sierveu ci."}. +{"This room is not anonymous","Cisse såle ci n' est nén anonime"}. {"Thursday","djudi"}. -{"Time","Date"}. {"Time delay","Tårdjaedje"}. {"Too many CAPTCHA requests","Pår trop di dmandes CAPTCHA"}. {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","I gn a-st avou pår trop (~p) d' otintifiaedjes k' ont fwait berwete vinant di ciste adresse IP la (~s). L' adresse serè disblokêye a ~s UTC"}. {"Too many unacked stanzas","Pår trop di messaedjes sins acertinaedje di rçuvaedje"}. -{"To","Po"}. -{"To ~s","Viè ~s"}. -{"Total rooms","Totå di såles"}. {"Traffic rate limit is exceeded","Li limite pol volume di trafik a stî passêye"}. -{"Transactions Aborted:","Transaccions arestêyes:"}. -{"Transactions Committed:","Transaccions evoyeyes:"}. -{"Transactions Logged:","Transaccions wårdêyes e djournå:"}. -{"Transactions Restarted:","Transaccions renondêyes:"}. {"Tuesday","mårdi"}. {"Unable to generate a CAPTCHA","Nén moyén di djenerer on CAPTCHA"}. {"Unauthorized","Nén otorijhî"}. -{"Unregister a Jabber account","Disdjîstrer on conte Jabber"}. {"Unregister","Disdjîstrer"}. {"Update message of the day (don't send)","Mete a djoû l' messaedje do djoû (nén l' evoyî)"}. {"Update message of the day on all hosts (don't send)","Mete a djoû l' messaedje do djoû so tos les lodjoes (nén l' evoyî)"}. -{"Update","Mete a djoû"}. -{"Update plan","Plan d' metaedje a djoû"}. -{"Update ~p","Metaedje a djoû di ~p"}. -{"Update script","Sicripe di metaedje a djoû"}. -{"Uptime:","Tins dispoy l' enondaedje:"}. -{"Use of STARTTLS required","L' eployaedje di STARTTL est oblidjî"}. {"User JID","JID d' l' uzeu"}. {"User Management","Manaedjaedje des uzeus"}. {"Username:","No d' uzeu:"}. {"Users are not allowed to register accounts so quickly","Les noveas uzeus n' si polèt nén edjîstrer si raddimint"}. {"Users Last Activity","Dierinne activité des uzeus"}. -{"User ~s","Uzeu ~s"}. {"Users","Uzeus"}. {"User","Uzeu"}. -{"Validate","Valider"}. {"vCard User Search","Calpin des uzeus"}. {"Virtual Hosts","Forveyous sierveus"}. {"Visitors are not allowed to change their nicknames in this room","Les viziteus èn polèt nén candjî leus metous no po ç' såle ci"}. @@ -425,16 +293,11 @@ {"Wednesday","mierkidi"}. {"When to send the last published item","Cwand evoyî l' dierin cayet eplaidî"}. {"Whether to allow subscriptions","Si on permete les abounmints"}. -{"You can later change your password using a Jabber client.","Vos ploz candjî vosse sicret pus tård avou on cliyint Jabber."}. {"You have been banned from this room","Vos avoz stî bani di cisse såle ci"}. {"You must fill in field \"Nickname\" in the form","Vos dvoz rimpli l' tchamp «Metou no» dins l' formiulaire"}. {"You need a client that supports x:data and CAPTCHA to register","Vos avoz mezåjhe d' on cliyint ki sopoite x:data eyet CAPTCHA po vs edjîstrer"}. {"You need a client that supports x:data to register the nickname","Vos avoz mezåjhe d' on cliyint ki sopoite x:data po-z edjîstrer l' metou no"}. -{"You need an x:data capable client to configure mod_irc settings","Vos avoz mezåjhe d' on cliyint ki sopoite x:data po candjî ls apontiaedjes di mod_irc"}. -{"You need an x:data capable client to configure room","I vs fåt on cliyint ki sopoite x:data por vos poleur apontyî l' såle"}. {"You need an x:data capable client to search","Vos avoz mezåjhe d' on cliyint ki sopoite x:data po fé on cweraedje"}. {"Your active privacy list has denied the routing of this stanza.","Vosse djivêye di privaceye active a rfuzé l' evoyaedje di ç' messaedje ci."}. {"Your contact offline message queue is full. The message has been discarded.","Li cawêye di messaedjes e môde disraloyî di vosse soçon est plinne. Li messaedje a stî tapé å diale."}. -{"Your Jabber account was successfully created.","Li conte Jabber da vosse a stî ahivé comifåt."}. -{"Your Jabber account was successfully deleted.","Li conte Jabber da vosse a stî disfacé comifåt."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","Vos messaedjes po ~s sont blokés. Po les disbloker, alez vey ~s"}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Vos messaedjes po ~s sont blokés. Po les disbloker, alez vey ~s"}. diff --git a/priv/msgs/wa.po b/priv/msgs/wa.po deleted file mode 100644 index 4eb670814..000000000 --- a/priv/msgs/wa.po +++ /dev/null @@ -1,1943 +0,0 @@ -# Pablo Saratxaga , 2015 -msgid "" -msgstr "" -"Project-Id-Version: 2.1.0-alpha\n" -"PO-Revision-Date: 2015-01-18 12:19+0000\n" -"Last-Translator: Pablo Saratxaga \n" -"Language-Team: Pablo Saratxaga \n" -"Language: wa\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Walon (Walloon)\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "L' eployaedje di STARTTL est oblidjî" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "Nole rissoûce di dnêye" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "Replaecî pa on novea raloyaedje" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "a stî pité evoye" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "Vosse djivêye di privaceye active a rfuzé l' evoyaedje di ç' messaedje ci." - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "Pår trop di messaedjes sins acertinaedje di rçuvaedje" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "Tapez l' tecse ki vos voeyoz" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "Vos messaedjes po ~s sont blokés. Po les disbloker, alez vey ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "Si vos n' voeyoz nole imådje CAPTCHA chal, vizitez l' pådje waibe." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "Pådje web CAPTCHA" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "Li CAPTCHA est valide." - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "Uzeu" - -#: ejabberd_oauth.erl:256 -msgid "Server" -msgstr "Sierveu" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "Sicret" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "Accepter" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "Nén otorijhî" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "Manaedjeu waibe ejabberd" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "Manaedjaedje" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "Droets (ACL)" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "Candjmints evoyîs" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "Mwais fôrmat" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "Evoyî" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "Dinêyes brutes" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "Disfacer les elemints tchoezis" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "Rîles d' accès" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "Apontiaedje des rîles d' accès a ~s" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "Forveyous sierveus" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "Uzeus" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "Uzeus raloyîs" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "Dierinne activité des uzeus" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "Termene:" - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "Dierin moes" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "Dierinne anêye" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "Dispoy todi" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "Mostrer crexhince" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "Mostrer totå" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "Sitatistikes" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "Nén trové" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "Nuk nén trové" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "Radjouter" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "Sierveu" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "Uzeus edjistrés" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "Radjouter èn uzeu" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "Messaedjes ki ratindèt" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "Dierinne activité" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "Måy" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "Raloyî" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "Uzeus edjistrés:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "Uzeus raloyîs:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "Raloyaedjes s2s e rexhowe:" - -#: ejabberd_web_admin.erl:1559 -msgid "Incoming s2s Connections:" -msgstr "Raloyaedjes s2s en intrêye:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "Nole" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "Candjî l' sicret" - -#: ejabberd_web_admin.erl:1673 -msgid "User ~s" -msgstr "Uzeu ~s" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "Raloyî avou les rsoûces:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "Sicret:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "Disfacer l' uzeu" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "Nole dinêye disponibe" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "Nuks" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "Nuks en alaedje" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "Nuks essoctés" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -msgid "Node ~p" -msgstr "Nuk ~p" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "Båze di dnêyes" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "Copeye di såvrité" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "Pôrts drovous" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "Mete a djoû" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "Renonder" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "Arester" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "Modules" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "Aroke di houcaedje RPC" - -#: ejabberd_web_admin.erl:1917 -msgid "Database Tables at ~p" -msgstr "Tåves del båze di dnêyes so ~p" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "No" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "Sôre di wårdaedje" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "Elemints" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "Memwere" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "Aroke" - -#: ejabberd_web_admin.erl:1955 -msgid "Backup of ~p" -msgstr "Copeye di såvrité po ~p" - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"Notez ki ces tchuzes la vont seulmint fé ene copeye di såvrité del båze di " -"dnêyes Mnesia costrûte å dvins do programe. Si vos eployîz ene difoûtrinne " -"båze di dnêyes avou l' module ODBC, vos dvoz fé ene copeye di såvrité del " -"båze SQL da vosse sepårumint." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "Copeye di såvrité binaire:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "'l est bon" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "Rapexhî do côp foû d' ene copeye di såvrité binaire:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "" -"Rapexhî l' copeye di såvrité binaire après l' renondaedje ki vént " -"d' ejabberd (çoula prind moens d' memwere del fé insi):" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "Copeye di såvrité tecse:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "Rapexhî do côp foû d' ene copeye di såvrité tecse:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "Sititchî des dnêyes uzeus foû d' on fitchî PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "Espoirter les dnêyes di tos les uzeus do sierveu viè des fitchîs PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "Espoirter les dnêyes di tos les uzeus do sierveu viè des fitchîs PIEFXIS (XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "Espoirter totes les tåves, come des cmandes SQL, viè on fitchî" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "Sititchî des dnêyes uzeus foû d' on fitchî spoûle jabberd14:" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "Sititchî des dnêyes uzeus foû d' on ridant spoûle jabberd14:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "Pôrts drovous so " - -#: ejabberd_web_admin.erl:2144 -msgid "Modules at ~p" -msgstr "Modules so ~p" - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "Sitatistikes di ~p" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "Tins dispoy l' enondaedje:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "Tins CPU:" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "Transaccions evoyeyes:" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "Transaccions arestêyes:" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "Transaccions renondêyes:" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "Transaccions wårdêyes e djournå:" - -#: ejabberd_web_admin.erl:2243 -msgid "Update ~p" -msgstr "Metaedje a djoû di ~p" - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "Plan d' metaedje a djoû" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "Modules di candjîs" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "Sicripe di metaedje a djoû" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "Sicripe di metaedje a djoû d' bas livea" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "Acertinaedje do scripe" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "Pôrt" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "Protocole" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "Module" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "Tchuzes" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "Enonder" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "Comandes" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "Voloz vs vormint disfacer l' messaedje do djoû?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "Sudjet" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "Coir do messaedje" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "I n' a nou coir do messaedje po ciste anonce la" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "Anonces" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "Evoyî l' anonce a tos les uzeus" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "Evoyî l' anonce a tos les uzeus so tos les lodjoes" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "Evoyî l' anonce a tos les uzeus raloyîs" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "Evoyî l' anonce a tos les uzeus raloyîs so tos les lodjoes" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "Defini l' messaedje do djoû et l' evoyî åzès uzeus raloyîs" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "" -"Defini l' messaedje do djoû so tos les lodjoes et l' evoyî åzès uzeus raloyîs" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "Mete a djoû l' messaedje do djoû (nén l' evoyî)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "Mete a djoû l' messaedje do djoû so tos les lodjoes (nén l' evoyî)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "Disfacer l' messaedje do djoû" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "Disfacer l' messaedje do djoû so tos les lodjoes" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "Apontiaedjes" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "Enonder des modules" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "Arester des modules" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "Rapexhî" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "Schaper en on fitchî tecse" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "Sititchî d' on fitchî" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "Sititchî d' on ridant" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "Renonder siervice" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "Arester siervice" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "Disfacer èn uzeu" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "Fini l' session d' l' uzeu" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "Riçure sicret d' l' uzeu" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "Candjî l' sicret d' l' uzeu" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "Riçure li date/eure do dierin elodjaedje di l' uzeu" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "Riçure les statistikes di l' uzeu" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "Riçure li nombe d' uzeus edjîstrés" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "Riçure li nombe d' uzeus raloyîs" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "Manaedjaedje des uzeus" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "Tos les uzeus" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "Raloyaedjes s2s e rexhowe" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "Manaedjaedje des copeyes di såvrité" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "Sititchî des uzeus Jabberd 1.4" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "Viè ~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "Dispoy ~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "Apontiaedje des tåves del båze di dnêyes so " - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "Tchoezi l' sôre di wårdaedje po les tåves" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "Copeye seulmint sol deure plake" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "Copeye e memwere (RAM) et sol deure plake" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "Copeye e memwere (RAM)" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "Copeye å lon" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "Arester les modules so " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "Tchoezixhoz les modules a-z arester" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "Renonder les modules so " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "Dinez ene djivêye del cogne {Module, [Tchuzes]}" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "Djivêye di modules a-z enonder" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "Fé ene copeye di såvrité dins on fitchî so " - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "Dinez l' tchimin viè l' fitchî copeye di såvrité" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "Tchimin viè l' fitchî" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "Rapexhî dispoy li fitchî copeye di såvrité so " - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "Copeye di såvritè viè on fitchî tecse so " - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "Dinez l' tchimin viè l' fitchî tecse" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "Sititchî uzeu d' on fitchî so " - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "Dinez l' tchimin viè l' fitchî di spool jabberd14" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "Sitichî des uzeus d' on ridant so " - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "Dinez l' tchimin viè l' ridant di spool jabberd14" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "Tchimin viè l' ridant" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "Tårdjaedje" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "Apontiaedje des droets (ACL)" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "Droets (ACL)" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "Apontiaedje des accès" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "Rîles d' accès" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "ID Jabber" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "Acertinaedje do scret" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "Nombe d' uzeus edjîstrés" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "Nombe d' uzeus raloyîs" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "Dierin elodjaedje" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "Grandeu del djivêye des soçons" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "Adresses IP" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "Rissoûces" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "Manaedjaedje di " - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "Accion so l' uzeu" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "Candjî les prôpietés" - -#: mod_fail2ban.erl:95 -msgid "" -"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" - -#: mod_http_upload.erl:586 -msgid "Please specify file size." -msgstr "Dinez l' grandeu do fitchî." - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "Dinez l' no do fitchî." - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "Ciste adresse IP est so ene noere djivêye e ~s" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "L' accès a stî rfuzé pal politike do siervice" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "Transpoirt IRC" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "Module IRC po ejabberd" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "" -"Vos avoz mezåjhe d' on cliyint ki sopoite x:data po candjî ls apontiaedjes " -"di mod_irc" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "Edjîstraedje dins mod_irc po " - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "" -"Dinez l' no d' uzeu, les ecôdaedjes, les pôrts et les screts ki vos vloz " -"eployî po vs raloyî åzès sierveus IRC" - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "No d' uzeu IRC" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"Si vos vloz dner des pôrts, sicrets ou ecôdaedjes diferins po les sierveus " -"IRC, rimplixhoz cisse djivêye ci avou des valixhances del cogne «{\"sierveu " -"irc\", \"ecôdaedje\", pôrt, \"sicret\"}». Les prémetowès valixhances do " -"siervice sont «~s» po l' ecôdaedje, «~p» pol pôrt, et on vude sicret." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"Egzimpe: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "Parametes des raloyaedjes" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "Radjonde canå IRC" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "Canå IRC (èn nén mete li prumî #)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "Sierveu IRC" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "Radjonde li canå IRC droci." - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "Radjonde li canå IRC e cist ID Jabber: ~s" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "Apontiaedjes IRC" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"Dinez les nos d' uzeu et ls ecôdaedjes ki vos vloz eployî po vs raloyî åzès " -"sierveus IRC Clitchîz so «Shuvant» po-z aveur di pus di tchamps a rimpli. " -"Clitchîz so «Fini» po schaper les apontiaedjes." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "No d' uzeu IRC" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "Sicret ~b" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "Pôrt ~b" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "Ecôdaedje pol sierveu ~b" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "Sierveu ~b" - -#: mod_mam.erl:541 -msgid "Only members may query archives of this room" -msgstr "Seulmint les mimbes polèt cweri les årtchives dins cisse såle ci" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "" -"Seulmint les manaedjeus d' siervices polèt evoyî des messaedjes di siervice" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "L' ahivaedje del såle est rfuzé pal politike do siervice" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "Li såle di conferince n' egzistêye nén" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "Såles di berdelaedje" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "Såles vudes" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "" -"Vos avoz mezåjhe d' on cliyint ki sopoite x:data po-z edjîstrer l' metou no" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "Edjîstraedje di metou no amon " - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "Dinez l' metou no ki vos vloz edjîstrer" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "Metou no" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "Li metou no est ddja edjîstré pa ene ôte sakî" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "Vos dvoz rimpli l' tchamp «Metou no» dins l' formiulaire" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "Module MUC (såles di berdelaedje) po ejabberd" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "Berdelaedje a sacwants" - -#: mod_muc_admin.erl:249 -msgid "Total rooms" -msgstr "Totå di såles" - -#: mod_muc_admin.erl:250 -msgid "Permanent rooms" -msgstr "Såles tofer la" - -#: mod_muc_admin.erl:251 -msgid "Registered nicknames" -msgstr "Metous nos edjistrés" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "Djivêye des såles" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "L' apontiaedje del såle di berdelaedje a candjî" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "arive sol såle" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "cwite li såle" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "a stî bani" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "a stî pité evoye cåze d' on candjmint d' afiyaedje" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "" -"a stî pité evoye cåze ki l' såle a stî ristrindowe åzès mimbes seulmint" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "a stî pité evoye cåze d' èn arestaedje do sistinme" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "est asteure kinoxhou come" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr " a candjî l' tite a: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "Li såle di berdelaedje est ahivêye" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "Li såle di berdelaedje est distrûte" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "Li såle di berdelaedje est enondêye" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "Li såle di berdelaedje est ahotêye" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "londi" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "mårdi" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "mierkidi" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "djudi" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "vénrdi" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "semdi" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "dimegne" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "djanvî" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "fevrî" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "måss" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "avri" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "may" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "djun" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "djulete" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "awousse" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "setimbe" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "octôbe" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "nôvimbe" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "decimbe" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "Apontiaedje del såle" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "Prezints el såle" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "Li limite pol volume di trafik a stî passêye" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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û." - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "On n' pout nén evoyî des messaedjes privés dins cisse conferince ci" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "Ratindez ene miete s' i vs plait divant d' rivoyî ene nouve " -"dimande di vwès" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "Les dmandes di vwès sont dismetowes e cisse conferince ci" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "Nén moyén di rsaetchî on JID foû d' l' aprovaedje di vosse dimande di vwès" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "Seulmint les moderateus polèt aprover des dmandes di vwès" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "Sôre di messaedje nén valide" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "C' est nén possibe d' evoyî des messaedjes privés del sôre «groupchat»" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "Li riçuveu n' est nén dins l' såle di conferince" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "Ci n' est nén permetou d' evoyî des messaedjes privés" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "Seulmint les prezints polèt evoyî des messaedjes al conferince" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "Seulmint les prezints polèt evoyî des cweraedjes sol conferince" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "" -"Les cweraedjes des mimbes del conferince ni sont nén permetous dins cisse " -"såle ci" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "" -"Seulmint les moderateus et les pårticipants polèt candjî l' sudjet dins " -"cisse såle ci" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "Seulmint les moderateus polèt candjî l' sudjet dins cisse såle ci" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "Les viziteus n' polèt nén evoyî des messaedjes a tos les prezints" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "Les viziteus èn polèt nén candjî leus metous no po ç' såle ci" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "Li metou no est ddja eployî pa ene ôte sakî sol såle" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "Vos avoz stî bani di cisse såle ci" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "I fåt esse mimbe po poleur intrer dins cisse såle ci" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "I fåt dner on scret po poleur intrer dins cisse såle ci" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "Pår trop di dmandes CAPTCHA" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "Nén moyén di djenerer on CAPTCHA" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "Sicret nén corek" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "I fåt des priviledjes di manaedjeu" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "I fåt des priviledjes di moderateu" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "Li Jabber ID ~s n' est nén valide" - -#: mod_muc_room.erl:2772 -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:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "Afiyaedje nén valide: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "Role nén valide: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "I fåt des priviledjes di prôpietaire" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "Apontiaedje del såle ~s" - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "Tite del såle" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "Discrijhaedje del såle" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "Rinde li såle permaninte" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "Rinde li såle di berdelaedje cweråve publicmint" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "Rinde publike li djivêye des pårticipants" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "Rinde li såle di berdelaedje protedjeye pa scret" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "Nombe macsimom di prezints" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "Pont d' limite" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "Mostrer les vraiys Jabber IDs a" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "les moderateus seulmint" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "tot l' minme kî" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "Roles ki leu prezince est difuzêye" - -#: mod_muc_room.erl:3486 -msgid "Moderator" -msgstr "Moderateu" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "Pårticipant" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "Viziteu" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "Rinde li såle di berdelaedje ristrindowe ås mimbes seulmint" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "Rinde li såle di berdelaedje moderêye" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "Les uzeus sont des pårticipants come prémetowe dujhance" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "Les uzeus polèt candjî l' tite" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "Les uzeus polèt evoyî des messaedjes privés" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "Les uzeus polèt evoyî des messaedjes privés" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "nolu" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "Les uzeus polèt cweri ls ôtes uzeus" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "Les uzeus polèt evoyî priyaedjes" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "" -"Permete ki les viziteus evoyexhe des tecse d' estat dins leus messaedjes di " -"prezince" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "Permete ki les viziteus candjexhe leus metous nos" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "Les uzeus polèt evoyî des dmandes di vwès" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "Tins minimom etur deus dmandes di vwès (e segondes)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "Rinde li såle di berdelaedje protedjeye pa CAPTCHA" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "Mete en alaedje l' årtchivaedje des messaedjes" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "Esclure les IDs Jabber des kesses CAPTCHA" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "Mete en alaedje li djournå" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "I vs fåt on cliyint ki sopoite x:data por vos poleur apontyî l' såle" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "Nombe di prezints" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "privé, " - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "Dimande di vwès" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "Aprover oudonbén rifuzer li dmande di vwès." - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "JID d' l' uzeu" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "Permete li vwès po cisse djin ci?" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s vos preye sol såle ~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "li scret est" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "Multicast" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "siervice multicast d' ejabberd" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "" -"Li cawêye di messaedjes e môde disraloyî di vosse soçon est plinne. Li " -"messaedje a stî tapé å diale." - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "messaedjes ki ratindèt el cawêye po ~s" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "Date" - -#: mod_offline.erl:812 -msgid "From" -msgstr "Di" - -#: mod_offline.erl:813 -msgid "To" -msgstr "Po" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "Paket" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "Messaedjes ki ratindèt:" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "Oister tos les messaedjes ki ratindèt" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "Module SOCKS5 Bytestreams po ejabberd" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "Eplaidaedje-abounmint" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "Module d' eplaidaedje-abounmint po ejabberd" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "Dimande d' eplaidaedje-abounmint d' èn abouné" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "Tchoezi s' i fåt aprover ou nén l' abounmint di ciste intité." - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "ID d' nuk" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "Adresse di l' abouné" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "" -"Permete ki ci Jabber ID ci si poye abouner a ç' nuk eplaidaedje-abounmint ci?" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "Evoyî l' contnou avou les notifiaedjes d' evenmints" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "Evoyî des notifiaedjes d' evenmints" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "Notifyî åzès abounés cwand l' apontiaedje do nuk candje" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "Notifyî åzès abounés cwand l' nuk est disfacé" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "Notifyî åzès abounés cwand des cayets sont oisté foû do nuk" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "Cayets permanints a wårder" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "On no uzeu-ahessåve pol nuk" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "Nombe macsimoms di cayets permanints" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "Si on permete les abounmints" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "Sipecifyî l' modele d' accès" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "Pårtaedjîs groupes di soçons k' on s' î pout abouner" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "Dinez l' modele d' eplaideu" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "Purdjî tos les cayets cwand l' eplaideu aloyî va foû raloyaedje" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "Sipecifyî l' sôre do messaedje d' evenmint" - -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "Contnou macsimom en octets" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "Cwand evoyî l' dierin cayet eplaidî" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "Seulmint evoyî des notifiaedje åzès uzeus disponibes" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "Les ramexhnêyes k' on nuk est afiyî avou" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "Li verifiaedje CAPTCHA a fwait berwete" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "" -"Vos avoz mezåjhe d' on cliyint ki sopoite x:data eyet CAPTCHA po vs edjîstrer" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "" -"Tchoezixhoz on no d' uzeu eyet on scret po vs edjîstrer so ç' sierveu ci" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "li scret est trop flåw" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "Les noveas uzeus n' si polèt nén edjîstrer si raddimint" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "Li conte Jabber da vosse a stî ahivé comifåt." - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "Åk n' a nén stî tot ahivant l' conte: " - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "Li conte Jabber da vosse a stî disfacé comifåt." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "Åk n' a nén stî tot disfaçant l' conte: " - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "Li scret do conte Jabber da vosse a stî candjî comifåt." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "Åk n' a nén stî tot candjant l' sicret: " - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Edjîstraedje di conte Jabber" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "Edjîstrer on conte Jabber" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "Disdjîstrer on conte Jabber" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"Cisse pådje permete d' ahiver on conte Jabber so ç' sierveu Jabber ci. " -"Li JID (IDintifieu Jabber) da vosse serè del cogne: noduzeu@sierveu. Lijhoz " -"atintivmint les instruccions po bén rimpli les tchamps." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "No d' uzeu:" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "Pont d' diferince etur les grandes et ptitès letes: «macbeth» est l' minme ki «MacBeth» ou co «Macbeth»" - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "Caracteres nén permetous:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "Sierveu:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "" -"Ni dnez vosse sicret a nolu, nén ddja ås manaedjeus do sierveu Jabber." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "Vos ploz candjî vosse sicret pus tård avou on cliyint Jabber." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"Des cliyints Jabber k' i gn a polèt wårder vosse sicret sol copiutrece, mins " -"vos n' duvrîz fé çoula ki sol copiutrece da vosse, po des råjhons di " -"såvrité." - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"Rimimbrez vosse sicret, ou scrijhoz l' so on papî ki vos wådroz en ene " -"place bén a hoûte, ca avou Jabber i n' a pont moyén di rapexhî vosse " -"sicret si vos l' rovyîz." - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "Acertinaedje do scret:" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "Edjîstrer" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "Vî scret:" - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "Novea scret:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "Cisse pådje permete di disdjîstrer on conte Jabber so ç' sierveu ci." - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "Disdjîstrer" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "Abounmimnt" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "Ratindant" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "Groupes" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "Valider" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "Oister" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "Djivêye des soçons da " - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "Radjouter èn ID Jabber" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "Djivêye des soçons" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "Pårtaedjîs groupes ezès djivêyes di soçons" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "Pitit no:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "Discrijhaedje:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "Mimbes:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "Groupes håynés:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "Groupe " - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Sierveu Jabber Erlang" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "Date d' askepiaedje" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "Veye" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "Payis" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "Emile" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "No d' famile" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "" -"Rimplixhoz les tchamps do formulaire po cweri èn uzeu Jabber (radjouter «*» " -"al fén do tchamp po cweri tot l' minme kéne fén d' tchinne" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "No etir" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "No do mitan" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "No d' l' organizåcion" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "Unité d' l' organizåcion" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "Cweri des uzeus dins " - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "Vos avoz mezåjhe d' on cliyint ki sopoite x:data po fé on cweraedje" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "Calpin des uzeus" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "Module vCard ejabberd" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "Rizultats do cweraedje po " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "Rimplixhoz les tchamps po cweri èn uzeu Jabber" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "Sierveus s2s e rexhowe:" - -#~ msgid "Delete" -#~ msgstr "Disfacer" - -#~ msgid "This room is not anonymous" -#~ msgstr "Cisse såle ci n' est nén anonime" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "" -#~ "Ci pårticipant ci a stî pité evoye del såle cåze k' il a-st evoyî on " -#~ "messaedje d' aroke" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "" -#~ "Ci pårticipant ci a stî pité evoye del såle cåze k' il a-st evoyî on " -#~ "messaedje d' aroke a èn ôte pårticipant" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "" -#~ "Ci pårticipant ci a stî pité evoye del såle cåze k' il a-st evoyî ene " -#~ "aroke di prezince" - -#~ msgid "Encodings" -#~ msgstr "Ecôdaedjes" - -#~ msgid "(Raw)" -#~ msgstr "(Dinêyes brutes)" - -#~ msgid "Specified nickname is already registered" -#~ msgstr "Li no metou dné est ddja edjîstré" - -#~ msgid "Size" -#~ msgstr "Grandeu" - diff --git a/priv/msgs/zh.msg b/priv/msgs/zh.msg index 84ac76919..4f5688244 100644 --- a/priv/msgs/zh.msg +++ b/priv/msgs/zh.msg @@ -1,440 +1,630 @@ -%% -*- coding: latin-1 -*- +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" (Add * to the end of field to match substring)"," (在字段末尾添加 * 以匹配子字符串)"}. +{" has set the subject to: "," 已将主题设置为: "}. +{"# participants","# 参与者"}. +{"A description of the node","节点的描述"}. +{"A friendly name for the node","节点的友好名称"}. +{"A password is required to enter this room","需要密码才能进入此房间"}. +{"A Web Page","网页"}. {"Accept","接受"}. -{"Access Configuration","访问配置"}. -{"Access Control List Configuration","访问控制列表(ACL)配置"}. -{"Access control lists","访问控制列表(ACL)"}. -{"Access Control Lists","访问控制列表(ACL)"}. -{"Access denied by service policy","访问被服务策略拒绝"}. -{"Access rules","访问规则"}. -{"Access Rules","访问规则"}. -{"Action on user","对用户的动作"}. -{"Add Jabber ID","添加Jabber ID"}. -{"Add New","添加新用户"}. +{"Access denied by service policy","服务策略拒绝访问"}. +{"Access model","访问模型"}. +{"Account doesn't exist","账号不存在"}. +{"Action on user","对用户的操作"}. +{"Add a hat to a user","给用户添加头衔"}. {"Add User","添加用户"}. -{"Administration of ","管理"}. +{"Administration of ","管理 "}. {"Administration","管理"}. {"Administrator privileges required","需要管理员权限"}. -{"A friendly name for the node","该节点的友好名称"}. {"All activity","所有活动"}. -{"Allow this Jabber ID to subscribe to this pubsub node?","允许该Jabber ID订阅该pubsub节点?"}. -{"Allow users to change the subject","允许用户更改主题"}. -{"Allow users to query other users","允许用户查询其它用户"}. -{"Allow users to send invites","允许用户发送邀请"}. -{"Allow users to send private messages","允许用户发送私聊消息"}. -{"Allow visitors to change nickname","允许用户更改昵称"}. -{"Allow visitors to send private messages to","允许访客发送私聊消息至"}. -{"Allow visitors to send status text in presence updates","更新在线状态时允许用户发送状态文本"}. -{"Allow visitors to send voice requests","允许访客发送声音请求"}. {"All Users","所有用户"}. -{"Announcements","通知"}. -{"anyone","任何人"}. -{"A password is required to enter this room","进入此房间需要密码"}. +{"Allow subscription","允许订阅"}. +{"Allow this Jabber ID to subscribe to this pubsub node?","是否允许此 Jabber ID 订阅此 pubsub 节点?"}. +{"Allow this person to register with the room?","是否允许此用户在房间注册?"}. +{"Allow users to change the subject","允许用户更改主题"}. +{"Allow users to query other users","允许用户查询其他用户"}. +{"Allow users to send invites","允许用户发送邀请"}. +{"Allow users to send private messages","允许用户发送私信"}. +{"Allow visitors to change nickname","允许参观者更改昵称"}. +{"Allow visitors to send private messages to","允许参观者发送私信至"}. +{"Allow visitors to send status text in presence updates","允许参观者在在线状态更新中发送状态文本"}. +{"Allow visitors to send voice requests","允许参观者发送发言权请求"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","与定义房间成员资格相关联的 LDAP 组;根据组的特定实施或特定部署的定义,使用 LDAP 专有名称。"}. +{"Announcements","公告"}. +{"Answer associated with a picture","与图片相关的答案"}. +{"Answer associated with a video","与视频相关的答案"}. +{"Answer associated with speech","与讲话相关的答案"}. +{"Answer to a question","问题的答案"}. +{"Anyone in the specified roster group(s) may subscribe and retrieve items","指定名册组中的任何人都可以订阅和检索项目"}. +{"Anyone may associate leaf nodes with the collection","任何人都可以将叶节点与集合关联"}. +{"Anyone may publish","任何人都可以发布"}. +{"Anyone may subscribe and retrieve items","任何人都可以订阅和检索项目"}. +{"Anyone with a presence subscription of both or from may subscribe and retrieve items","任何拥有 both 或 from 的在线状态订阅的用户都可以订阅和检索项目"}. +{"Anyone with Voice","任何有发言权的人"}. +{"Anyone","任何人"}. +{"API Commands","API 命令"}. {"April","四月"}. +{"Arguments","参数"}. +{"Attribute 'channel' is required for this request","此请求要求“channel”属性"}. +{"Attribute 'id' is mandatory for MIX messages","对于 MIX 消息,“id”属性是必需的"}. +{"Attribute 'jid' is not allowed here","此处不允许“jid”属性"}. +{"Attribute 'node' is not allowed here","此处不允许“node”属性"}. +{"Attribute 'to' of stanza that triggered challenge","触发挑战节的“to”属性"}. {"August","八月"}. +{"Automatic node creation is not enabled","未启用自动节点创建"}. {"Backup Management","备份管理"}. -{"Backup of ~p","~p的备份"}. -{"Backup to File at ","备份文件位于"}. +{"Backup of ~p","~p 的备份"}. +{"Backup to File at ","备份文件位于 "}. {"Backup","备份"}. {"Bad format","格式错误"}. -{"Birthday","出生日期"}. +{"Birthday","生日"}. +{"Both the username and the resource are required","用户名和资源均为必填项"}. +{"Bytestream already activated","字节流已激活"}. +{"Cannot remove active list","无法移除活动列表"}. +{"Cannot remove default list","无法移除默认列表"}. {"CAPTCHA web page","验证码网页"}. +{"Challenge ID","挑战 ID"}. {"Change Password","更改密码"}. {"Change User Password","更改用户密码"}. -{"Characters not allowed:","禁用字符:"}. +{"Changing password is not allowed","不允许更改密码"}. +{"Changing role/affiliation is not allowed","不允许更改角色/从属关系"}. +{"Channel already exists","频道已存在"}. +{"Channel does not exist","频道不存在"}. +{"Channel JID","频道 JID"}. +{"Channels","频道"}. +{"Characters not allowed:","不允许字符:"}. {"Chatroom configuration modified","聊天室配置已修改"}. -{"Chatroom is created","聊天室已被创建"}. -{"Chatroom is destroyed","聊天室已被销毁"}. -{"Chatroom is started","聊天室已被启动"}. -{"Chatroom is stopped","聊天室已被停用"}. +{"Chatroom is created","已创建聊天室"}. +{"Chatroom is destroyed","已解散聊天室"}. +{"Chatroom is started","已启动聊天室"}. +{"Chatroom is stopped","已停止聊天室"}. {"Chatrooms","聊天室"}. -{"Choose a username and password to register with this server","请选择在此服务器上注册所需的用户名和密码"}. -{"Choose modules to stop","请选择要停止的模块"}. -{"Choose storage type of tables","请选择表格的存储类型"}. -{"Choose whether to approve this entity's subscription.","选择是否允许该实体的订阅"}. +{"Choose a username and password to register with this server","请选择要在此服务器中注册的用户名和密码"}. +{"Choose storage type of tables","选择表的存储类型"}. +{"Choose whether to approve this entity's subscription.","选择是否批准此实体的订阅。"}. {"City","城市"}. +{"Client acknowledged more stanzas than sent by server","客户端确认的节数多于服务器发送的节数"}. +{"Clustering","集群"}. {"Commands","命令"}. {"Conference room does not exist","会议室不存在"}. -{"Configuration of room ~s","房间~s的配置 "}. +{"Configuration of room ~s","房间 ~s 的配置"}. {"Configuration","配置"}. -{"Connected Resources:","已连接资源:"}. -{"Connections parameters","连接参数"}. -{"Country","国家"}. -{"CPU Time:","CPU时间:"}. -{"Database Tables at ~p","位于~p的数据库表"}. -{"Database Tables Configuration at ","数据库表格配置位于"}. +{"Contact Addresses (normally, room owner or owners)","联系地址(通常为房间所有者)"}. +{"Country","国家/地区"}. +{"Current Discussion Topic","当前讨论话题"}. +{"Database failure","数据库失败"}. +{"Database Tables Configuration at ","数据库表配置在 "}. {"Database","数据库"}. {"December","十二月"}. -{"Default users as participants","用户默认被视为参与人"}. +{"Default users as participants","默认用户为参与者"}. {"Delete message of the day on all hosts","删除所有主机上的每日消息"}. {"Delete message of the day","删除每日消息"}. -{"Delete Selected","删除已选内容"}. {"Delete User","删除用户"}. {"Deliver event notifications","传递事件通知"}. -{"Deliver payloads with event notifications","用事件通告传输有效负载"}. -{"Description:","描述:"}. -{"Disc only copy","仅磁盘复制"}. -{"Displayed Groups:","已显示的组:"}. -{"Don't tell your password to anybody, not even the administrators of the Jabber server.","不要将密码告诉任何人, 就算是Jabber服务器的管理员也不可以."}. -{"Dump Backup to Text File at ","转储备份到文本文件于"}. +{"Deliver payloads with event notifications","用事件通知传递有效负载"}. +{"Disc only copy","仅磁盘副本"}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","不要将您的密码告诉任何人,甚至是 XMPP 服务器的管理员。"}. +{"Dump Backup to Text File at ","将备份转储到位于以下位置的文本文件 "}. {"Dump to Text File","转储到文本文件"}. +{"Duplicated groups are not allowed by RFC6121","按照 RFC6121 的规则,不允许重复的组"}. +{"Dynamically specify a replyto of the item publisher","动态指定项目发布者的 replyto"}. {"Edit Properties","编辑属性"}. -{"Either approve or decline the voice request.","接受或拒绝声音请求"}. -{"ejabberd IRC module","ejabberd IRC 模块"}. +{"Either approve or decline the voice request.","批准或拒绝发言权请求。"}. +{"ejabberd HTTP Upload service","ejabberd HTTP 上传服务"}. {"ejabberd MUC module","ejabberd MUC 模块"}. -{"ejabberd Multicast service","ejabberd多重映射服务"}. -{"ejabberd Publish-Subscribe module","ejabberd 发行-订阅模块"}. +{"ejabberd Multicast service","ejabberd 多播服务"}. +{"ejabberd Publish-Subscribe module","ejabberd 发布–订阅模块"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 字节流模块"}. -{"ejabberd vCard module","ejabberd vCard模块"}. -{"ejabberd Web Admin","ejabberd网页管理"}. -{"Elements","元素"}. +{"ejabberd vCard module","ejabberd vCard 模块"}. +{"ejabberd Web Admin","ejabberd Web 管理"}. +{"ejabberd","ejabberd"}. +{"Email Address","电子邮件地址"}. {"Email","电子邮件"}. -{"Empty Rooms","空房间"}. -{"Enable logging","启用服务器端聊天记录"}. +{"Enable hats","启用头衔"}. +{"Enable logging","启用日志记录"}. {"Enable message archiving","启用消息归档"}. -{"Encoding for server ~b","服务器~b的编码"}. +{"Enabling push without 'node' attribute is not supported","不支持没有“node”属性就启用推送"}. {"End User Session","结束用户会话"}. -{"Enter list of {Module, [Options]}","请输入{模块, [选项]}列表"}. -{"Enter nickname you want to register","请输入您想要注册的昵称"}. +{"Enter nickname you want to register","请输入要注册的昵称"}. {"Enter path to backup file","请输入备份文件的路径"}. -{"Enter path to jabberd14 spool dir","请输入jabberd14 spool目录的路径"}. +{"Enter path to jabberd14 spool dir","请输入 jabberd14 spool 目录的路径"}. {"Enter path to jabberd14 spool file","请输入 jabberd14 spool 文件的路径"}. {"Enter path to text file","请输入文本文件的路径"}. -{"Enter the text you see","请输入您所看到的文本"}. -{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","请输入您想使用的用来连接到 IRC 服务器的用户名和编码. 按 '下一步' 获取更多待填字段. 按 '完成' 保存设置."}. -{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","请输入您想使用的用来连接到IRC服务器的用户名, 编码, 端口和密码."}. -{"Erlang Jabber Server","Erlang Jabber服务器"}. -{"Error","错误"}. -{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","例如: [{\"irc.lucky.net\", \"koi8-r\"}, 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}. -{"Exclude Jabber IDs from CAPTCHA challenge","从验证码挑战中排除Jabber ID"}. -{"Export all tables as SQL queries to a file:","将所有表以SQL查询语句导出到文件:"}. -{"Export data of all users in the server to PIEFXIS files (XEP-0227):","将服务器上所有用户的数据导出到 PIEFXIS 文件 (XEP-0227):"}. -{"Export data of users in a host to PIEFXIS files (XEP-0227):","将某主机的用户数据导出到 PIEFXIS 文件 (XEP-0227):"}. -{"Failed to extract JID from your voice request approval","无法从你的声音请求确认信息中提取JID"}. +{"Enter the text you see","请输入您看到的文本"}. +{"Erlang XMPP Server","Erlang XMPP 服务器"}. +{"Exclude Jabber IDs from CAPTCHA challenge","从验证码挑战中排除的 Jabber ID"}. +{"Export all tables as SQL queries to a file:","将所有表以 SQL 查询导出到文件:"}. +{"Export data of all users in the server to PIEFXIS files (XEP-0227):","将服务器中所有用户的数据导出到 PIEFXIS 文件(XEP-0227):"}. +{"Export data of users in a host to PIEFXIS files (XEP-0227):","将主机中用户的数据导出到 PIEFXIS 文件(XEP-0227):"}. +{"External component failure","外部组件故障"}. +{"External component timeout","外部组件超时"}. +{"Failed to activate bytestream","无法激活字节流"}. +{"Failed to extract JID from your voice request approval","无法从您的发言权请求批准中提取 JID"}. +{"Failed to map delegated namespace to external component","无法将委托命名空间映射到外部组件"}. +{"Failed to parse HTTP response","无法解析 HTTP 响应"}. +{"Failed to process option '~s'","无法处理选项“~s”"}. {"Family Name","姓氏"}. +{"FAQ Entry","常见问题条目"}. {"February","二月"}. -{"Fill in fields to search for any matching Jabber User","填充字段以搜索任何匹配的Jabber用户"}. -{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","填充表单以搜索任何匹配的Jabber用户(在字段末添加*来匹配子串)"}. -{"Friday","星期五"}. -{"From ~s","来自~s"}. -{"From","从"}. +{"File larger than ~w bytes","文件大于 ~w 字节"}. +{"Fill in the form to search for any matching XMPP User","填写表单以搜索任何匹配的 XMPP 用户"}. +{"Friday","周五"}. +{"From ~ts","来自 ~ts"}. +{"Full List of Room Admins","房间管理员的完整列表"}. +{"Full List of Room Owners","房间所有者的完整列表"}. {"Full Name","全名"}. +{"Get List of Online Users","获取在线用户列表"}. +{"Get List of Registered Users","获取注册用户列表"}. {"Get Number of Online Users","获取在线用户数"}. {"Get Number of Registered Users","获取注册用户数"}. -{"Get User Last Login Time","获取用户上次登陆时间"}. -{"Get User Password","获取用户密码"}. -{"Get User Statistics","获取用户统计"}. -{"Grant voice to this person?","为此人授权声音?"}. -{"Groups","组"}. -{"Group ","组"}. -{"has been banned","已被禁止"}. -{"has been kicked because of an affiliation change","因联属关系改变而被踢出"}. -{"has been kicked because of a system shutdown","因系统关机而被踢出"}. -{"has been kicked because the room has been changed to members-only","因该房间改为只对会员开放而被踢出"}. +{"Get Pending","获取待处理"}. +{"Get User Last Login Time","获取用户上次登录时间"}. +{"Get User Statistics","获取用户统计数据"}. +{"Given Name","名字"}. +{"Grant voice to this person?","是否授予此用户发言权?"}. +{"has been banned","已被封禁"}. +{"has been kicked because of a system shutdown","因系统关闭而被踢出"}. +{"has been kicked because of an affiliation change","由于从属关系的更改而被踢出"}. +{"has been kicked because the room has been changed to members-only","被踢出,因为房间已更改为仅成员"}. {"has been kicked","已被踢出"}. -{" has set the subject to: ","已将标题设置为: "}. -{"Host","主机"}. -{"If you don't see the CAPTCHA image here, visit the web page.","如果您在这里没有看到验证码图片, 请访问网页."}. -{"If you want to specify different ports, passwords, encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\", port, \"password\"}'. By default this service use \"~s\" encoding, port ~p, empty password.","如果您想为 IRC 服务器指定不同的端口, 密码, 编码, 请用 '{\"irc 服务器\", \"编码\", 端口, \"密码\"}' 格式的值填充此表单. 默认情况下此服务使用\"~s\"编码, ~p 端口, 密码为空."}. +{"Hash of the vCard-temp avatar of this room","此房间的 vCard-temp 头像的散列"}. +{"Hat title","头衔标题"}. +{"Hat URI","头衔 URI"}. +{"Hats limit exceeded","已超过头衔限制"}. +{"Host unknown","主机未知"}. +{"HTTP File Upload","HTTP 文件上传"}. +{"Idle connection","空闲连接"}. +{"If you don't see the CAPTCHA image here, visit the web page.","如果您在此处没有看到验证码图片,请访问网页。"}. {"Import Directory","导入目录"}. {"Import File","导入文件"}. -{"Import user data from jabberd14 spool file:","从 jabberd14 Spool 文件导入用户数据:"}. -{"Import User from File at ","导入用户的文件位于"}. -{"Import users data from a PIEFXIS file (XEP-0227):","从 PIEFXIS 文件 (XEP-0227) 导入用户数据:"}. -{"Import users data from jabberd14 spool directory:","从jabberd14 Spool目录导入用户数据:"}. -{"Import Users from Dir at ","导入用户的目录位于"}. +{"Import user data from jabberd14 spool file:","从 jabberd14 Spool 文件导入用户数据:"}. +{"Import User from File at ","从以下位置的文件导入用户 "}. +{"Import users data from a PIEFXIS file (XEP-0227):","从 PIEFXIS 文件(XEP-0227)导入用户数据:"}. +{"Import users data from jabberd14 spool directory:","从 jabberd14 spool 目录导入用户数据:"}. +{"Import Users from Dir at ","从以下位置的目录导入用户 "}. {"Import Users From jabberd14 Spool Files","从 jabberd14 Spool 文件导入用户"}. -{"Improper message type","不恰当的消息类型"}. -{"Incoming s2s Connections:","入站 s2s 连接:"}. +{"Improper domain part of 'from' attribute","“from”属性域名部分不正确"}. +{"Improper message type","消息类型不正确"}. +{"Incorrect CAPTCHA submit","提交的验证码不正确"}. +{"Incorrect data form","数据表单不正确"}. {"Incorrect password","密码不正确"}. -{"Invalid affiliation: ~s","无效加入: ~s"}. -{"Invalid role: ~s","无效角色: ~s"}. -{"IP addresses","IP地址"}. -{"IP","IP"}. -{"IRC channel (don't put the first #)","IRC频道 (不要输入第一个#号)"}. -{"IRC server","IRC服务器"}. -{"IRC settings","IRC设置"}. -{"IRC Transport","IRC传输"}. -{"IRC username","IRC用户名"}. -{"IRC Username","IRC用户名"}. -{"is now known as","现在称呼为"}. -{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","不允许将错误消息发送到该房间. 参与者(~s)已发送过一条消息(~s)并已被踢出房间"}. -{"It is not allowed to send private messages of type \"groupchat\"","\"群组聊天\"类型不允许发送私聊消息"}. -{"It is not allowed to send private messages to the conference","不允许向会议发送私聊消息"}. -{"It is not allowed to send private messages","不可以发送私聊消息"}. -{"Jabber Account Registration","Jabber帐户注册"}. +{"Incorrect value of 'action' attribute","“action”属性的值不正确"}. +{"Incorrect value of 'action' in data form","数据表单中“action”的值不正确"}. +{"Incorrect value of 'path' in data form","数据表单中“path”的值不正确"}. +{"Installed Modules:","已安装的模块:"}. +{"Install","安装"}. +{"Insufficient privilege","权限不足"}. +{"Internal server error","内部服务器错误"}. +{"Invalid 'from' attribute in forwarded message","转发消息中的“from”属性无效"}. +{"Invalid node name","节点名称无效"}. +{"Invalid 'previd' value","“previd”值无效"}. +{"Invitations are not allowed in this conference","此会议不允许邀请"}. +{"IP addresses","IP 地址"}. +{"is now known as","现在昵称为"}. +{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","不允许向此房间发送错误消息。参与者(~s)发送了错误消息(~s),被踢出了房间"}. +{"It is not allowed to send private messages of type \"groupchat\"","不允许发送“groupchat”类型的私信"}. +{"It is not allowed to send private messages to the conference","不允许向会议发送私信"}. {"Jabber ID","Jabber ID"}. -{"Jabber ID ~s is invalid","Jabber ID ~s 无效"}. {"January","一月"}. -{"Join IRC channel","加入IRC频道"}. +{"JID normalization denied by service policy","服务策略拒绝 JID 规范化"}. +{"JID normalization failed","JID 规范化失败"}. +{"Joined MIX channels of ~ts","加入了 ~ts 的 MIX 频道"}. +{"Joined MIX channels:","加入了 MIX 频道:"}. {"joins the room","加入房间"}. -{"Join the IRC channel here.","在这里加入IRC频道."}. -{"Join the IRC channel in this Jabber ID: ~s","用此Jabber ID ~s加入IRC频道"}. {"July","七月"}. {"June","六月"}. +{"Just created","刚刚创建"}. {"Last Activity","上次活动"}. -{"Last login","上次登陆"}. +{"Last login","上次登录"}. +{"Last message","最后一条消息"}. {"Last month","上个月"}. -{"Last year","上一年"}. +{"Last year","去年"}. +{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","文本的 SHA-256 散列的最低有效位应等于十六进制标签"}. {"leaves the room","离开房间"}. -{"Listened Ports at ","监听的端口位于"}. -{"Listened Ports","被监听的端口"}. -{"List of modules to start","要启动的模块列表"}. -{"List of rooms","房间列表"}. -{"Low level update script","低级别更新脚本"}. -{"Make participants list public","公开参与人列表"}. -{"Make room CAPTCHA protected","保护房间验证码"}. -{"Make room members-only","设置房间只接收会员"}. -{"Make room moderated","设置房间只接收主持人"}. -{"Make room password protected","进入此房间需要密码"}. -{"Make room persistent","永久保存该房间"}. -{"Make room public searchable","使房间可被公开搜索"}. +{"List of users with hats","有头衔的用户列表"}. +{"List users with hats","列出有头衔的用户"}. +{"Logged Out","已登出"}. +{"Logging","日志记录"}. +{"Make participants list public","公开参与者列表"}. +{"Make room CAPTCHA protected","启用房间验证码保护"}. +{"Make room members-only","将房间设为仅成员"}. +{"Make room moderated","启用房间发言审核"}. +{"Make room password protected","启用房间密码保护"}. +{"Make room persistent","将房间设为持久"}. +{"Make room public searchable","将房间设为可公开搜索"}. +{"Malformed username","用户名格式不正确"}. +{"MAM preference modification denied by service policy","服务策略拒绝修改 MAM 首选项"}. {"March","三月"}. -{"Maximum Number of Occupants","允许的与会人最大数"}. -{"Max # of items to persist","允许持久化的最大内容条目数"}. -{"Max payload size in bytes","最大有效负载字节数"}. +{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","要保留的最大项目数 #,或 `max` 表示除服务器强制规定的最大值外无其他特定限制"}. +{"Max payload size in bytes","最大有效负载大小(字节)"}. +{"Maximum file size","最大文件大小"}. +{"Maximum Number of History Messages Returned by Room","房间返回的最大历史消息数"}. +{"Maximum number of items to persist","要保留的最大项目数"}. +{"Maximum Number of Occupants","最大使用者数"}. {"May","五月"}. -{"Membership is required to enter this room","进入此房间需要会员身份"}. -{"Members:","会员:"}. -{"Memorize your password, or write it in a paper placed in a safe place. In Jabber there isn't an automated way to recover your password if you forget it.","记住你的密码, 或将其记到纸上并放于安全位置. 如果你忘记了密码, Jabber也没有自动恢复密码的方式."}. -{"Memory","内存"}. -{"Message body","消息主体"}. +{"Membership is required to enter this room","进入此房间需要成员资格"}. +{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","请记住您的密码,或写在放在安全地方的纸上。在 XMPP 中,如果您忘记密码,没有自动恢复密码的方法。"}. +{"Mere Availability in XMPP (No Show Value)","XMPP 中的可用性(无显示值)"}. +{"Message body","消息正文"}. +{"Message not found in forwarded payload","在转发的有效负载中未找到消息"}. +{"Messages from strangers are rejected","拒绝来自陌生人的消息"}. +{"Messages of type headline","标题类型的消息"}. +{"Messages of type normal","普通类型的消息"}. {"Middle Name","中间名"}. -{"Minimum interval between voice requests (in seconds)","声音请求的最小间隔(以秒为单位)"}. +{"Minimum interval between voice requests (in seconds)","发言权请求的最短间隔时间(秒)"}. {"Moderator privileges required","需要主持人权限"}. -{"moderators only","仅主持人"}. +{"Moderators Only","仅主持人"}. {"Moderator","主持人"}. -{"Modified modules","被修改模块"}. -{"Modules at ~p","位于~p的模块"}. -{"Modules","模块"}. -{"Module","模块"}. -{"Monday","星期一"}. -{"Multicast","多重映射"}. +{"Module failed to handle the query","模块无法处理查询"}. +{"Monday","周一"}. +{"Multicast","多播"}. +{"Multiple elements are not allowed by RFC6121","按照 RFC6121,多个 元素是不允许的"}. {"Multi-User Chat","多用户聊天"}. -{"Name:","姓名:"}. -{"Name","姓名"}. -{"Never","从未"}. -{"New Password:","新密码:"}. -{"Nickname Registration at ","昵称注册于"}. -{"Nickname ~s does not exist in the room","昵称~s不在该房间"}. +{"Name","名称"}. +{"Natural Language for Room Discussions","房间讨论的自然语言"}. +{"Natural-Language Room Name","自然语言房间名称"}. +{"Neither 'jid' nor 'nick' attribute found","未找到“jid”和“nick”属性"}. +{"Neither 'role' nor 'affiliation' attribute found","未找到“role”或“affiliation”属性"}. +{"Never","从不"}. +{"New Password:","新密码:"}. +{"Nickname can't be empty","昵称不能为空"}. +{"Nickname Registration at ","昵称注册于 "}. +{"Nickname ~s does not exist in the room","昵称 ~s 在房间中不存在"}. {"Nickname","昵称"}. -{"No body provided for announce message","通知消息无正文内容"}. -{"nobody","没有人"}. -{"No Data","没有数据"}. -{"Node ID","节点ID"}. -{"Node not found","没有找到节点"}. -{"Node ~p","节点~p"}. +{"No address elements found","未找到地址元素"}. +{"No addresses element found","未找到地址元素"}. +{"No 'affiliation' attribute found","未找到“affiliation”属性"}. +{"No available resource found","未找到可用资源"}. +{"No body provided for announce message","未提供公告消息正文"}. +{"No child elements found","未找到子元素"}. +{"No data form found","未找到数据表单"}. +{"No Data","无数据"}. +{"No features available","无可用功能"}. +{"No element found","未找到 元素"}. +{"No hook has processed this command","没有钩子处理此命令"}. +{"No info about last activity found","未找到有关上次活动的信息"}. +{"No 'item' element found","未找到“item”元素"}. +{"No items found in this query","在此查询中未找到任何项目"}. +{"No limit","无限制"}. +{"No module is handling this query","没有模块正在处理此查询"}. +{"No node specified","未指定节点"}. +{"No 'password' found in data form","在数据表单中未找到“password”"}. +{"No 'password' found in this query","在此查询中未找到“password”"}. +{"No 'path' found in data form","在数据表单中未找到“path”"}. +{"No pending subscriptions found","未找到待处理的订阅"}. +{"No privacy list with this name found","未找到具有此名称的隐私列表"}. +{"No private data found in this query","在此查询中未找到专用数据"}. +{"No running node found","未找到正在运行的节点"}. +{"No services available","无可用服务"}. +{"No statistics found for this item","未找到此项目的统计数据"}. +{"No 'to' attribute found in the invitation","邀请中未找到“to”属性"}. +{"Nobody","没有人"}. +{"Node already exists","节点已存在"}. +{"Node ID","节点 ID"}. +{"Node index not found","未找到节点索引"}. +{"Node not found","未找到节点"}. +{"Node ~p","节点 ~p"}. +{"Nodeprep has failed","Nodeprep 失败了"}. {"Nodes","节点"}. -{"No limit","不限"}. +{"Node","节点"}. {"None","无"}. -{"No resource provided","无资源提供"}. -{"Not Found","没有找到"}. -{"Notify subscribers when items are removed from the node","当从节点删除内容条目时通知订阅人"}. -{"Notify subscribers when the node configuration changes","当节点设置改变时通知订阅人"}. -{"Notify subscribers when the node is deleted","当节点被删除时通知订阅人"}. +{"Not allowed","不允许"}. +{"Not Found","未找到"}. +{"Not subscribed","未订阅"}. +{"Notify subscribers when items are removed from the node","从节点中移除项目时通知订阅者"}. +{"Notify subscribers when the node configuration changes","节点配置更改时通知订阅者"}. +{"Notify subscribers when the node is deleted","删除节点时通知订阅者"}. {"November","十一月"}. -{"Number of occupants","驻留人数"}. +{"Number of answers required","所需答案数"}. +{"Number of occupants","使用者数"}. +{"Number of Offline Messages","离线消息数"}. {"Number of online users","在线用户数"}. {"Number of registered users","注册用户数"}. +{"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","自动清除项目前的秒数,`max` 表示除服务器强制规定的最大值外无其他特定限制"}. +{"Occupants are allowed to invite others","允许使用者邀请他人"}. +{"Occupants are allowed to query others","允许使用者查询他人"}. +{"Occupants May Change the Subject","使用者可以更改主题"}. {"October","十月"}. -{"Offline Messages:","离线消息:"}. -{"Offline Messages","离线消息"}. {"OK","确定"}. -{"Old Password:","旧密码: "}. -{"Online Users:","在线用户:"}. +{"Old Password:","旧密码:"}. {"Online Users","在线用户"}. {"Online","在线"}. -{"Only deliver notifications to available users","仅将通知发送给可发送的用户"}. -{"Only members may query archives of this room","只有会员可以查询本房间的存档"}. -{"Only moderators and participants are allowed to change the subject in this room","只有主持人和参与人可以在此房间里更改主题"}. -{"Only moderators are allowed to change the subject in this room","只有主持人可以在此房间里更改主题"}. -{"Only moderators can approve voice requests","仅主持人能确认声音请求"}. -{"Only occupants are allowed to send messages to the conference","只有与会人可以向大会发送消息"}. -{"Only occupants are allowed to send queries to the conference","只有与会人可以向大会发出查询请求"}. -{"Only service administrators are allowed to send service messages","只有服务管理员可以发送服务消息"}. -{"Options","选项"}. +{"Only collection node owners may associate leaf nodes with the collection","只有集合节点所有者可以将叶节点与集合关联"}. +{"Only deliver notifications to available users","仅向在线用户发送通知"}. +{"Only or tags are allowed","仅允许 标签"}. +{"Only element is allowed in this query","此查询中只允许 元素"}. +{"Only members may query archives of this room","只有成员才能查询此房间的归档"}. +{"Only moderators and participants are allowed to change the subject in this room","只允许主持人和参与者更改此房间的主题"}. +{"Only moderators are allowed to change the subject in this room","只允许主持人更改此房间的主题"}. +{"Only moderators are allowed to retract messages","只允许主持人撤回消息"}. +{"Only moderators can approve voice requests","只有主持人可以批准发言权请求"}. +{"Only occupants are allowed to send messages to the conference","只允许使用者向会议发送消息"}. +{"Only occupants are allowed to send queries to the conference","只允许使用者向会议发送查询"}. +{"Only publishers may publish","只有发布者才能发布"}. +{"Only service administrators are allowed to send service messages","只允许服务管理员发送服务消息"}. +{"Only those on a whitelist may associate leaf nodes with the collection","只有白名单上的那些可以将叶节点与集合关联"}. +{"Only those on a whitelist may subscribe and retrieve items","只有白名单上的那些才可以订阅和检索项目"}. {"Organization Name","组织名称"}. {"Organization Unit","组织单位"}. -{"Outgoing s2s Connections:","出站 s2s 连接:"}. -{"Outgoing s2s Connections","出站 s2s 连接"}. -{"Owner privileges required","需要持有人权限"}. -{"Packet","数据包"}. -{"Participant","参与人"}. -{"Password ~b","~b的密码"}. -{"Password Verification:","密码确认:"}. -{"Password Verification","确认密码"}. -{"Password:","密码:"}. +{"Other Modules Available:","其他可用模块:"}. +{"Outgoing s2s Connections","传出 s2s 连接"}. +{"Owner privileges required","需要所有者权限"}. +{"Packet relay is denied by service policy","服务策略拒绝数据包中继"}. +{"Participant ID","参与者 ID"}. +{"Participant","参与者"}. +{"Password Verification","密码验证"}. +{"Password Verification:","密码验证:"}. {"Password","密码"}. -{"Path to Dir","目录的路径"}. +{"Password:","密码:"}. +{"Path to Dir","目录路径"}. {"Path to File","文件路径"}. -{"Pending","挂起"}. -{"Period: ","持续时间: "}. -{"Permanent rooms","永久房间"}. -{"Persist items to storage","持久化内容条目"}. +{"Payload semantic type information","有效负载语义类型信息"}. +{"Period: ","时段: "}. +{"Persist items to storage","将项目保留到存储"}. +{"Persistent","持久"}. +{"Ping query is incorrect","Ping 查询不正确"}. {"Ping","Ping"}. -{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","注意:这些选项仅将备份内置的 Mnesia 数据库. 如果您正在使用 ODBC 模块, 您还需要分别备份您的数据库."}. -{"Please specify file name.","请指定文件名称."}. -{"Please specify file size.","请指定文件大小."}. -{"Please, wait for a while before sending new voice request","请稍后再发送新的声音请求"}. +{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","注意:这些选项只会备份内置的 Mnesia 数据库。如果使用 ODBC 模块,还需要单独备份 SQL 数据库。"}. +{"Please, wait for a while before sending new voice request","请稍候再发送新的发言权请求"}. {"Pong","Pong"}. -{"Port ~b","~b的端口"}. -{"Port","端口"}. -{"Present real Jabber IDs to","将真实Jabber ID显示给"}. -{"private, ","保密, "}. -{"Protocol","协议"}. -{"Publish-Subscribe","发行-订阅"}. -{"PubSub subscriber request","PubSub订阅人请求"}. -{"Purge all items when the relevant publisher goes offline","相关发布人离线后清除所有选项"}. -{"Queries to the conference members are not allowed in this room","本房间不可以查询会议成员信息"}. -{"RAM and disc copy","内存与磁盘复制"}. -{"RAM copy","内存(RAM)复制"}. -{"Raw","原始格式"}. -{"Really delete message of the day?","确实要删除每日消息吗?"}. -{"Recipient is not in the conference room","接收人不在会议室"}. -{"Register a Jabber account","注册Jabber帐户"}. -{"Registered nicknames","注册的昵称"}. -{"Registered Users:","注册用户:"}. -{"Registered Users","注册用户"}. +{"Possessing 'ask' attribute is not allowed by RFC6121","按照 RFC6121, 不允许有“ask”属性"}. +{"Present real Jabber IDs to","将真实 Jabber ID 显示给"}. +{"Previous session not found","未找到上一个会话"}. +{"Previous session PID has been killed","上一个会话 PID 已终止"}. +{"Previous session PID has exited","上一个会话 PID 已退出"}. +{"Previous session PID is dead","上一个会话 PID 已失效"}. +{"Previous session timed out","上一个会话超时"}. +{"private, ","私人, "}. +{"Public","公开"}. +{"Publish model","发布模型"}. +{"Publish-Subscribe","发布–订阅"}. +{"PubSub subscriber request","PubSub 订阅者请求"}. +{"Purge all items when the relevant publisher goes offline","相关发布者离线后清除所有项目"}. +{"Push record not found","未找到推送记录"}. +{"Queries to the conference members are not allowed in this room","此房间不允许向会议成员查询"}. +{"Query to another users is forbidden","禁止查询其他用户"}. +{"RAM and disc copy","RAM 和磁盘副本"}. +{"RAM copy","RAM 副本"}. +{"Really delete message of the day?","是否确定删除每日消息?"}. +{"Receive notification from all descendent nodes","接收所有后代节点的通知"}. +{"Receive notification from direct child nodes only","仅接收直接子节点的通知"}. +{"Receive notification of new items only","仅接收新项目的通知"}. +{"Receive notification of new nodes only","仅接收新节点的通知"}. +{"Recipient is not in the conference room","接收者不在会议室"}. +{"Register an XMPP account","注册 XMPP 账号"}. {"Register","注册"}. -{"Registration in mod_irc for ","mod_irc 中的注册是为 "}. -{"Remote copy","远程复制"}. -{"Remove All Offline Messages","移除所有离线消息"}. -{"Remove User","删除用户"}. -{"Remove","移除"}. -{"Replaced by new connection","被新的连接替换"}. +{"Remote copy","远程副本"}. +{"Remove a hat from a user","移除用户头衔"}. +{"Remove User","移除用户"}. +{"Replaced by new connection","替换为新连接"}. +{"Request has timed out","请求已超时"}. +{"Request is ignored","请求被忽略"}. +{"Requested role","请求的角色"}. {"Resources","资源"}. {"Restart Service","重启服务"}. -{"Restart","重启"}. -{"Restore Backup from File at ","要恢复的备份文件位于"}. -{"Restore binary backup after next ejabberd restart (requires less memory):","在下次 ejabberd 重启后恢复二进制备份(需要的内存更少):"}. -{"Restore binary backup immediately:","立即恢复二进制备份:"}. -{"Restore plain text backup immediately:","立即恢复普通文本备份:"}. +{"Restore Backup from File at ","从以下位置的文件恢复备份 "}. +{"Restore binary backup after next ejabberd restart (requires less memory):","在下次 ejabberd 重启后恢复二进制备份(所需内存较少):"}. +{"Restore binary backup immediately:","立即恢复二进制备份:"}. +{"Restore plain text backup immediately:","立即恢复纯文本备份:"}. {"Restore","恢复"}. -{"Roles for which Presence is Broadcasted","广播存在性的角色"}. +{"Result","结果"}. +{"Roles and Affiliations that May Retrieve Member List","可以检索成员列表的角色和从属关系"}. +{"Roles for which Presence is Broadcasted","广播在线状态的角色"}. +{"Roles that May Send Private Messages","可以发送私信的角色"}. {"Room Configuration","房间配置"}. -{"Room creation is denied by service policy","创建房间被服务策略拒绝"}. +{"Room creation is denied by service policy","服务策略拒绝创建房间"}. {"Room description","房间描述"}. -{"Room Occupants","房间人数"}. +{"Room Occupants","房间使用者"}. +{"Room terminates","房间终止"}. {"Room title","房间标题"}. -{"Roster groups allowed to subscribe","允许订阅的花名册组"}. -{"Roster of ","花名册属于"}. -{"Roster size","花名册大小"}. -{"Roster","花名册"}. -{"RPC Call Error","RPC 调用错误"}. -{"Running Nodes","运行中的节点"}. -{"~s access rule configuration","~s访问规则配置"}. -{"Saturday","星期六"}. -{"Script check","脚本检查"}. -{"Search Results for ","搜索结果属于关键词 "}. -{"Search users in ","搜索用户于"}. -{"Send announcement to all online users on all hosts","发送通知给所有主机的在线用户"}. -{"Send announcement to all online users","发送通知给所有在线用户"}. -{"Send announcement to all users on all hosts","发送通知给所有主机上的所有用户"}. -{"Send announcement to all users","发送通知给所有用户"}. +{"Roster groups allowed to subscribe","允许订阅的名册组"}. +{"Roster size","名册大小"}. +{"Running Nodes","正在运行的节点"}. +{"~s invites you to the room ~s","~s 邀请您加入房间 ~s"}. +{"Saturday","周六"}. +{"Search from the date","从日期搜索"}. +{"Search Results for ","搜索结果 "}. +{"Search the text","搜索文本"}. +{"Search until the date","搜索截至日期"}. +{"Search users in ","在以下位置搜索用户 "}. +{"Send announcement to all online users on all hosts","向所有主机上的所有在线用户发送公告"}. +{"Send announcement to all online users","向所有在线用户发送公告"}. +{"Send announcement to all users on all hosts","向所有主机上的所有用户发送公告"}. +{"Send announcement to all users","向所有用户发送公告"}. {"September","九月"}. -{"Server ~b","服务器~b"}. -{"Server:","服务器:"}. -{"Server","服务器"}. -{"Set message of the day and send to online users","设定每日消息并发送给所有在线用户"}. -{"Set message of the day on all hosts and send to online users","设置所有主机上的每日消息并发送给在线用户"}. -{"Shared Roster Groups","共享的花名册组群"}. -{"Show Integral Table","显示完整列表"}. -{"Show Ordinary Table","显示普通列表"}. +{"Server:","服务器:"}. +{"Service list retrieval timed out","服务列表检索超时"}. +{"Session state copying timed out","会话状态复制超时"}. +{"Set message of the day and send to online users","设置每日消息并发送给在线用户"}. +{"Set message of the day on all hosts and send to online users","在所有主机上设置每日消息并发送给在线用户"}. +{"Shared Roster Groups","共享名册组"}. +{"Show Integral Table","显示完整表"}. +{"Show Occupants Join/Leave","显示使用者加入/离开"}. +{"Show Ordinary Table","显示普通表"}. {"Shut Down Service","关闭服务"}. -{"~s invites you to the room ~s","~s邀请你到房间~s"}. -{"Some Jabber clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","某些 Jabber 客户端可以在你的计算机里存储密码. 请仅在你确认你的计算机安全的情况下使用该功能."}. -{"Specify the access model","指定访问范例"}. +{"SOCKS5 Bytestreams","SOCKS5 字节流"}. +{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","某些 XMPP 客户端可以将您的密码存储在计算机中,但出于安全考虑,您应该仅在个人计算机中存储密码。"}. +{"Sources Specs:","源规格:"}. +{"Specify the access model","指定访问模型"}. {"Specify the event message type","指定事件消息类型"}. -{"Specify the publisher model","指定发布人范例"}. -{"~s's Offline Messages Queue","~s的离线消息队列"}. -{"Start Modules at ","要启动的模块位于 "}. -{"Start Modules","启动模块"}. -{"Start","开始"}. -{"Statistics of ~p","~p的统计"}. -{"Statistics","统计"}. -{"Stop Modules at ","要停止的模块位于 "}. -{"Stop Modules","停止模块"}. -{"Stopped Nodes","已经停止的节点"}. -{"Stop","停止"}. -{"Storage Type","存储类型"}. -{"Store binary backup:","存储为二进制备份:"}. -{"Store plain text backup:","存储为普通文本备份:"}. -{"Subject","标题"}. +{"Specify the publisher model","指定发布者模型"}. +{"Stanza id is not valid","节 ID 无效"}. +{"Stanza ID","节 ID"}. +{"Statically specify a replyto of the node owner(s)","静态指定节点所有者的 replyto"}. +{"Stopped Nodes","已停止的节点"}. +{"Store binary backup:","存储二进制备份:"}. +{"Store plain text backup:","存储纯文本备份:"}. +{"Stream management is already enabled","已启用流管理"}. +{"Stream management is not enabled","未启用流管理"}. +{"Subject","主题"}. {"Submitted","已提交"}. -{"Submit","提交"}. -{"Subscriber Address","订阅人地址"}. -{"Subscription","订阅"}. -{"Sunday","星期天"}. -{"That nickname is already in use by another occupant","该昵称已被另一用户使用"}. -{"That nickname is registered by another person","该昵称已被另一个人注册了"}. -{"The CAPTCHA is valid.","验证码有效."}. -{"The CAPTCHA verification has failed","验证码检查失败"}. -{"The collections with which a node is affiliated","加入结点的集合"}. -{"The password is too weak","密码强度太弱"}. +{"Subscriber Address","订阅者地址"}. +{"Subscribers may publish","订阅者可以发布"}. +{"Subscription requests must be approved and only subscribers may retrieve items","订阅请求必须得到批准,只有订阅者才能检索项目"}. +{"Subscriptions are not allowed","不允许订阅"}. +{"Sunday","周日"}. +{"Text associated with a picture","与图片相关的文字"}. +{"Text associated with a sound","与声音相关的文字"}. +{"Text associated with a video","与视频相关的文字"}. +{"Text associated with speech","与语音相关的文字"}. +{"That nickname is already in use by another occupant","该昵称已被其他使用者使用"}. +{"That nickname is registered by another person","该昵称已被另一用户注册了"}. +{"The account already exists","此账号已存在"}. +{"The account was not unregistered","此账号未注销"}. +{"The body text of the last received message","最后收到的消息的正文"}. +{"The CAPTCHA is valid.","验证码有效。"}. +{"The CAPTCHA verification has failed","验证码验证失败"}. +{"The captcha you entered is wrong","您输入的验证码错误"}. +{"The child nodes (leaf or collection) associated with a collection","与集合关联的子节点(叶或集合)"}. +{"The collections with which a node is affiliated","节点所属的集合"}. +{"The DateTime at which a leased subscription will end or has ended","租赁订阅将结束或已结束的日期时间"}. +{"The datetime when the node was created","创建节点的日期时间"}. +{"The default language of the node","节点的默认语言"}. +{"The feature requested is not supported by the conference","会议不支持所请求的功能"}. +{"The JID of the node creator","节点创建者的 JID"}. +{"The JIDs of those to contact with questions","有疑问时需联系的人员的 JID"}. +{"The JIDs of those with an affiliation of owner","有所有者从属关系的人员的 JID"}. +{"The JIDs of those with an affiliation of publisher","有发布者从属关系的人员的 JID"}. +{"The list of all online users","所有在线用户的列表"}. +{"The list of all users","所有用户的列表"}. +{"The list of JIDs that may associate leaf nodes with a collection","可以将叶节点与集合关联的 JID 列表"}. +{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","可以与集合关联的子节点的最大数量,或 `max` 表示除服务器强制规定的最大值外无其他特定限制"}. +{"The minimum number of milliseconds between sending any two notification digests","发送任意两个通知摘要之间的最小毫秒数"}. +{"The name of the node","节点的名称"}. +{"The node is a collection node","节点是集合节点"}. +{"The node is a leaf node (default)","节点是叶节点(默认)"}. +{"The NodeID of the relevant node","相关节点的 NodeID"}. +{"The number of pending incoming presence subscription requests","待处理的传入在线状态订阅请求数"}. +{"The number of subscribers to the node","节点的订阅者数"}. +{"The number of unread or undelivered messages","未读或未传递的消息数"}. +{"The password contains unacceptable characters","密码包含不可接受的字符"}. +{"The password is too weak","密码太弱"}. {"the password is","密码是"}. -{"The password of your Jabber account was successfully changed.","你的Jabber帐户密码已成功更新."}. -{"There was an error changing the password: ","修改密码出错: "}. -{"There was an error creating the account: ","帐户创建出错: "}. -{"There was an error deleting the account: ","帐户删除失败: "}. -{"This IP address is blacklisted in ~s","此IP地址在~s中已被列入黑名单"}. -{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","此处不区分大小写: macbeth 与 MacBeth 和 Macbeth 是一样的."}. -{"This page allows to create a Jabber account in this Jabber server. Your JID (Jabber IDentifier) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","本页面允许在此服务器上创建Jabber帐户. 你的JID (Jabber ID) 的形式如下: 用户名@服务器. 请仔细阅读说明并正确填写相应字段."}. -{"This page allows to unregister a Jabber account in this Jabber server.","此页面允许在此Jabber服务器上注销Jabber帐户"}. -{"Thursday","星期四"}. +{"The password of your XMPP account was successfully changed.","您的 XMPP 账号密码已成功更改。"}. +{"The password was not changed","密码未更改"}. +{"The passwords are different","密码不同"}. +{"The presence states for which an entity wants to receive notifications","实体要接收通知的在线状态"}. +{"The query is only allowed from local users","仅允许来自本地用户的查询"}. +{"The query must not contain elements","查询不能包含 元素"}. +{"The room subject can be modified by participants","参与者可以修改房间主题"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","节点中数据的语义类型信息,通常由有效负载的命名空间指定(如果有)"}. +{"The sender of the last received message","最后收到的消息的发送者"}. +{"The stanza MUST contain only one element, one element, or one element","节必须仅包含一个 元素、一个 元素或一个 元素"}. +{"The subscription identifier associated with the subscription request","与订阅请求关联的订阅标识符"}. +{"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","XSL 转换的 URL,可以将其应用于有效负载以生成适当的消息正文元素。"}. +{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","XSL 转换的 URL,可以将其应用于有效负载格式,以生成有效的数据表单结果,客户端可以使用通用数据表单呈现引擎来显示该结果"}. +{"There was an error changing the password: ","更改密码时出错: "}. +{"There was an error creating the account: ","创建账号时出错: "}. +{"There was an error deleting the account: ","删除账号时出错: "}. +{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","此处不区分大小写:MacBeth 和 Macbeth 都是 macbeth。"}. +{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","本页面允许在此服务器中注册 XMPP 账号,您的 JID(Jabber ID)的格式为:用户名@服务器。请仔细阅读说明以正确填写字段。"}. +{"This page allows to unregister an XMPP account in this XMPP server.","本页面允许在此 XMPP 服务器中注销 XMPP 账号。"}. +{"This room is not anonymous","此房间是非匿名的"}. +{"This service can not process the address: ~s","此服务无法处理地址:~s"}. +{"Thursday","周四"}. {"Time delay","时间延迟"}. -{"Time","时间"}. +{"Timed out waiting for stream resumption","等待流恢复超时"}. +{"To register, visit ~s","要注册,请访问 ~s"}. +{"To ~ts","到 ~ts"}. +{"Token TTL","令牌 TTL"}. +{"Too many active bytestreams","活动字节流太多"}. {"Too many CAPTCHA requests","验证码请求太多"}. -{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","来自IP地址(~p)的(~s)失败认证太多. 该地址将在UTC时间~s被禁用."}. -{"Too many unacked stanzas","未被确认的节太多"}. -{"To ~s","发送给~s"}. -{"Total rooms","所有房间"}. -{"To","到"}. -{"Traffic rate limit is exceeded","已经超过传输率限制"}. -{"Transactions Aborted:","取消的事务:"}. -{"Transactions Committed:","提交的事务:"}. -{"Transactions Logged:","记入日志的事务:"}. -{"Transactions Restarted:","重启的事务:"}. -{"Tuesday","星期二"}. +{"Too many child elements","子元素太多"}. +{"Too many elements"," 元素太多"}. +{"Too many elements"," 元素太多"}. +{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","有太多(~p)失败的身份验证来自此 IP 地址(~s),将在 UTC 时间 ~s 取消对该地址的屏蔽"}. +{"Too many receiver fields were specified","指定的接收者字段太多"}. +{"Too many unacked stanzas","未确认的节太多"}. +{"Too many users in this conference","此会议中的用户太多"}. +{"Traffic rate limit is exceeded","超过流量速率限制"}. +{"~ts's MAM Archive","~ts 的 MAM 归档"}. +{"~ts's Offline Messages Queue","~ts 的离线消息队列"}. +{"Tuesday","周二"}. {"Unable to generate a CAPTCHA","无法生成验证码"}. -{"Unauthorized","未认证的"}. -{"Unregister a Jabber account","注销Jabber帐户"}. -{"Unregister","取消注册"}. -{"Update message of the day (don't send)","更新每日消息(不发送)"}. -{"Update message of the day on all hosts (don't send)","更新所有主机上的每日消息(不发送)"}. -{"Update plan","更新计划"}. -{"Update ~p","更新~p"}. -{"Update script","更新脚本"}. -{"Update","更新"}. -{"Uptime:","正常运行时间:"}. -{"Use of STARTTLS required","要求使用 STARTTLS"}. -{"User JID","用户JID"}. +{"Unable to register route on existing local domain","无法在现有本地域上注册路由"}. +{"Unauthorized","未经授权"}. +{"Unexpected action","意外操作"}. +{"Unexpected error condition: ~p","意外错误条件:~p"}. +{"Uninstall","卸载"}. +{"Unregister an XMPP account","注销 XMPP 账号"}. +{"Unregister","注销"}. +{"Unsupported element","不支持的 元素"}. +{"Unsupported version","不支持的版本"}. +{"Update message of the day (don't send)","更新每日消息(不发送)"}. +{"Update message of the day on all hosts (don't send)","更新所有主机上的每日消息(不发送)"}. +{"Update specs to get modules source, then install desired ones.","更新规格以获取模块源,然后安装所需的模块。"}. +{"Update Specs","更新规格"}. +{"Updating the vCard is not supported by the vCard storage backend","vCard 存储后端不支持更新 vCard"}. +{"Upgrade","升级"}. +{"URL for Archived Discussion Logs","已归档的讨论日志 URL"}. +{"User already exists","用户已存在"}. +{"User JID","用户 JID"}. +{"User (jid)","用户(JID)"}. {"User Management","用户管理"}. -{"Username:","用户名:"}. -{"Users are not allowed to register accounts so quickly","不允许用户太频繁地注册帐户"}. +{"User not allowed to perform an IQ set on another user's vCard.","不允许用户在其他用户的 vCard 上执行 IQ 设置。"}. +{"User removed","用户已移除"}. +{"User session not found","未找到用户会话"}. +{"User session terminated","用户会话已终止"}. +{"User ~ts","用户 ~ts"}. +{"Username:","用户名:"}. +{"Users are not allowed to register accounts so quickly","不允许用户太频繁地注册账号"}. {"Users Last Activity","用户上次活动"}. {"Users","用户"}. -{"User ~s","用户~s"}. {"User","用户"}. -{"Validate","确认"}. -{"vCard User Search","vCard用户搜索"}. +{"Value 'get' of 'type' attribute is not allowed","不允许“type”属性的“get”值"}. +{"Value of '~s' should be boolean","“~s”的值应为布尔值"}. +{"Value of '~s' should be datetime string","“~s”的值应为日期时间字符串"}. +{"Value of '~s' should be integer","“~s”的值应为整数"}. +{"Value 'set' of 'type' attribute is not allowed","不允许“type”属性的“set”值"}. +{"vCard User Search","vCard 用户搜索"}. +{"View joined MIX channels","查看已加入的 MIX 频道"}. {"Virtual Hosts","虚拟主机"}. -{"Visitors are not allowed to change their nicknames in this room","此房间不允许用户更改昵称"}. -{"Visitors are not allowed to send messages to all occupants","不允许访客给所有占有者发送消息"}. -{"Visitor","访客"}. -{"Voice requests are disabled in this conference","该会议的声音请求以被禁用"}. -{"Voice request","声音请求"}. -{"Wednesday","星期三"}. -{"When to send the last published item","何时发送最新发布的内容条目"}. +{"Visitors are not allowed to change their nicknames in this room","不允许参观者在此房间中更改其昵称"}. +{"Visitors are not allowed to send messages to all occupants","不允许参观者向所有使用者发送消息"}. +{"Visitor","参观者"}. +{"Voice requests are disabled in this conference","此会议中禁用了发言权请求"}. +{"Voice request","发言权请求"}. +{"Web client which allows to join the room anonymously","允许匿名加入房间的 Web 客户端"}. +{"Wednesday","周三"}. +{"When a new subscription is processed and whenever a subscriber comes online","处理新订阅时和订阅者上线时"}. +{"When a new subscription is processed","处理新订阅时"}. +{"When to send the last published item","何时发送最后发布的项目"}. +{"Whether an entity wants to receive an XMPP message body in addition to the payload format","除有效负载格式外,实体是否还希望接收 XMPP 消息正文"}. +{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","实体是要接收通知摘要(汇总) 还是要单独接收所有通知"}. +{"Whether an entity wants to receive or disable notifications","实体是否要接收或禁用通知"}. +{"Whether owners or publisher should receive replies to items","所有者或发布者是否应收到对项目的回复"}. +{"Whether the node is a leaf (default) or a collection","节点是叶(默认)还是集合"}. {"Whether to allow subscriptions","是否允许订阅"}. -{"You can later change your password using a Jabber client.","你可以稍后用Jabber客户端修改你的密码."}. -{"You have been banned from this room","您已被禁止进入该房间"}. -{"You must fill in field \"Nickname\" in the form","您必须填充表单中\"昵称\"项"}. -{"You need a client that supports x:data and CAPTCHA to register","您需要一个支持 x:data 和验证码的客户端进行注册"}. -{"You need a client that supports x:data to register the nickname","您需要一个支持 x:data 的客户端来注册昵称"}. -{"You need an x:data capable client to configure mod_irc settings","您需要一个兼容 x:data 的客户端来配置mod_irc设置"}. -{"You need an x:data capable client to configure room","您需要一个兼容 x:data 的客户端来配置房间"}. -{"You need an x:data capable client to search","您需要一个兼容 x:data 的客户端来搜索"}. -{"Your active privacy list has denied the routing of this stanza.","你的活跃私聊列表拒绝了在此房间进行路由分发."}. -{"Your contact offline message queue is full. The message has been discarded.","您的联系人离线消息队列已满. 消息已被丢弃"}. -{"Your Jabber account was successfully created.","你的Jabber帐户已成功创建."}. -{"Your Jabber account was successfully deleted.","你的 Jabber 帐户已成功删除."}. -{"Your messages to ~s are being blocked. To unblock them, visit ~s","您发送给~s的消息已被阻止. 要解除阻止, 请访问 ~s"}. +{"Whether to make all subscriptions temporary, based on subscriber presence","是否根据订阅者的在线状态将所有订阅设为临时订阅"}. +{"Whether to notify owners about new subscribers and unsubscribes","是否通知所有者新的订阅者和退订者"}. +{"Who can send private messages","谁可以发送私信"}. +{"Who may associate leaf nodes with a collection","谁可以将叶节点与集合关联"}. +{"Wrong parameters in the web formulary","Web 表单集中的参数错误"}. +{"Wrong xmlns","错误的 xmlns"}. +{"XMPP Account Registration","XMPP 账号注册"}. +{"XMPP Domains","XMPP 域"}. +{"XMPP Show Value of Away","XMPP 的离开显示值"}. +{"XMPP Show Value of Chat","XMPP 的聊天显示值"}. +{"XMPP Show Value of DND (Do Not Disturb)","XMPP 的 DND(请勿打扰)显示值"}. +{"XMPP Show Value of XA (Extended Away)","XMPP 的 XA(延长离开)显示值"}. +{"XMPP URI of Associated Publish-Subscribe Node","关联发布–订阅节点的 XMPP URI"}. +{"You are being removed from the room because of a system shutdown","由于系统关闭,您将被移出房间"}. +{"You are not allowed to send private messages","不允许您发送私信"}. +{"You are not joined to the channel","您未加入频道"}. +{"You can later change your password using an XMPP client.","您之后可以使用 XMPP 客户端更改密码。"}. +{"You have been banned from this room","禁止您进入此房间"}. +{"You have joined too many conferences","您加入了太多会议"}. +{"You must fill in field \"Nickname\" in the form","您必须在表单中填写“昵称”字段"}. +{"You need a client that supports x:data and CAPTCHA to register","您需要支持 x:data 和验证码的客户端来注册"}. +{"You need a client that supports x:data to register the nickname","您需要支持 x:data 的客户端来注册昵称"}. +{"You need an x:data capable client to search","您需要支持 x:data 的客户端来搜索"}. +{"Your active privacy list has denied the routing of this stanza.","您的活动隐私列表已拒绝路由此节。"}. +{"Your contact offline message queue is full. The message has been discarded.","您的联系人离线消息队列已满。消息已被丢弃。"}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","您发给 ~s 的订阅请求和/或消息已被屏蔽。若要取消屏蔽您的订阅请求,请访问 ~s"}. +{"Your XMPP account was successfully registered.","您的 XMPP 账号注册成功。"}. +{"Your XMPP account was successfully unregistered.","您的 XMPP 账号注销成功。"}. +{"You're not allowed to create nodes","不允许您创建节点"}. diff --git a/priv/msgs/zh.po b/priv/msgs/zh.po deleted file mode 100644 index 113e7564a..000000000 --- a/priv/msgs/zh.po +++ /dev/null @@ -1,1901 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: 2.1.0-alpha\n" -"Last-Translator: Shelley Shyan - shelleyshyan AT gmail DOT com\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Language: Chinese (中文)\n" -"X-Additional-Translator: Zhan Caibao - zhancaibao AT gmail DOT com\n" -"X-Additional-Translator: Mike Wang\n" - -#: ejabberd_c2s.erl:505 ejabberd_c2s.erl:853 -msgid "Use of STARTTLS required" -msgstr "要求使用 STARTTLS" - -#: ejabberd_c2s.erl:604 -msgid "No resource provided" -msgstr "无资源提供" - -#: ejabberd_c2s.erl:1349 -msgid "Replaced by new connection" -msgstr "被新的连接替换" - -#: ejabberd_c2s.erl:1353 mod_configure.erl:1854 mod_muc_log.erl:427 -#: mod_muc_log.erl:430 -msgid "has been kicked" -msgstr "已被踢出" - -#: ejabberd_c2s.erl:2114 -msgid "Your active privacy list has denied the routing of this stanza." -msgstr "你的活跃私聊列表拒绝了在此房间进行路由分发." - -#: ejabberd_c2s.erl:2429 -msgid "Too many unacked stanzas" -msgstr "未被确认的节太多" - -#: ejabberd_captcha.erl:122 ejabberd_captcha.erl:245 ejabberd_captcha.erl:284 -msgid "Enter the text you see" -msgstr "请输入您所看到的文本" - -#: ejabberd_captcha.erl:147 -msgid "Your messages to ~s are being blocked. To unblock them, visit ~s" -msgstr "您发送给~s的消息已被阻止. 要解除阻止, 请访问 ~s" - -#: ejabberd_captcha.erl:192 -msgid "If you don't see the CAPTCHA image here, visit the web page." -msgstr "如果您在这里没有看到验证码图片, 请访问网页." - -#: ejabberd_captcha.erl:227 -msgid "CAPTCHA web page" -msgstr "验证码网页" - -#: ejabberd_captcha.erl:381 -msgid "The CAPTCHA is valid." -msgstr "验证码有效." - -#: ejabberd_oauth.erl:253 ejabberd_web_admin.erl:1403 -#: ejabberd_web_admin.erl:1458 mod_register.erl:265 mod_vcard.erl:490 -msgid "User" -msgstr "用户" - -#: ejabberd_oauth.erl:256 -msgid "Server" -msgstr "服务器" - -#: ejabberd_oauth.erl:259 ejabberd_web_admin.erl:1408 mod_configure.erl:1398 -#: mod_configure.erl:1485 mod_configure.erl:1889 mod_configure.erl:2123 -#: mod_muc_room.erl:3383 mod_register.erl:275 -msgid "Password" -msgstr "密码" - -#: ejabberd_oauth.erl:267 -msgid "Accept" -msgstr "接受" - -#: ejabberd_web_admin.erl:202 ejabberd_web_admin.erl:214 -#: ejabberd_web_admin.erl:234 ejabberd_web_admin.erl:246 -msgid "Unauthorized" -msgstr "未认证的" - -#: ejabberd_web_admin.erl:303 ejabberd_web_admin.erl:335 -msgid "ejabberd Web Admin" -msgstr "ejabberd网页管理" - -#: ejabberd_web_admin.erl:669 ejabberd_web_admin.erl:680 -msgid "Administration" -msgstr "管理" - -#: ejabberd_web_admin.erl:737 ejabberd_web_admin.erl:773 mod_configure.erl:196 -#: mod_configure.erl:532 -msgid "Access Control Lists" -msgstr "访问控制列表(ACL)" - -#: ejabberd_web_admin.erl:741 ejabberd_web_admin.erl:777 -#: ejabberd_web_admin.erl:843 ejabberd_web_admin.erl:876 -#: ejabberd_web_admin.erl:917 ejabberd_web_admin.erl:1394 -#: ejabberd_web_admin.erl:1677 ejabberd_web_admin.erl:1836 -#: ejabberd_web_admin.erl:1870 ejabberd_web_admin.erl:1950 -#: ejabberd_web_admin.erl:2120 ejabberd_web_admin.erl:2149 -#: ejabberd_web_admin.erl:2246 mod_offline.erl:802 mod_roster.erl:1493 -#: mod_shared_roster.erl:1166 mod_shared_roster.erl:1261 -msgid "Submitted" -msgstr "已提交" - -#: ejabberd_web_admin.erl:742 ejabberd_web_admin.erl:778 -#: ejabberd_web_admin.erl:844 ejabberd_web_admin.erl:877 -#: ejabberd_web_admin.erl:918 ejabberd_web_admin.erl:1395 -#: ejabberd_web_admin.erl:1678 ejabberd_web_admin.erl:1837 -#: ejabberd_web_admin.erl:2121 ejabberd_web_admin.erl:2150 mod_roster.erl:1494 -#: mod_shared_roster.erl:1167 mod_shared_roster.erl:1262 -msgid "Bad format" -msgstr "格式错误" - -#: ejabberd_web_admin.erl:753 ejabberd_web_admin.erl:790 -#: ejabberd_web_admin.erl:855 ejabberd_web_admin.erl:925 -#: ejabberd_web_admin.erl:1939 mod_shared_roster.erl:1269 -msgid "Submit" -msgstr "提交" - -#: ejabberd_web_admin.erl:782 ejabberd_web_admin.erl:881 -msgid "Raw" -msgstr "原始格式" - -#: ejabberd_web_admin.erl:787 ejabberd_web_admin.erl:887 mod_offline.erl:824 -#: mod_shared_roster.erl:1175 -msgid "Delete Selected" -msgstr "删除已选内容" - -#: ejabberd_web_admin.erl:839 ejabberd_web_admin.erl:872 mod_configure.erl:198 -#: mod_configure.erl:533 -msgid "Access Rules" -msgstr "访问规则" - -#: ejabberd_web_admin.erl:913 -msgid "~s access rule configuration" -msgstr "~s访问规则配置" - -#: ejabberd_web_admin.erl:931 -msgid "Virtual Hosts" -msgstr "虚拟主机" - -#: ejabberd_web_admin.erl:940 ejabberd_web_admin.erl:948 -msgid "Users" -msgstr "用户" - -#: ejabberd_web_admin.erl:955 ejabberd_web_admin.erl:1340 -#: mod_configure.erl:524 -msgid "Online Users" -msgstr "在线用户" - -#: ejabberd_web_admin.erl:971 -msgid "Users Last Activity" -msgstr "用户上次活动" - -#: ejabberd_web_admin.erl:975 -msgid "Period: " -msgstr "持续时间: " - -#: ejabberd_web_admin.erl:988 -msgid "Last month" -msgstr "上个月" - -#: ejabberd_web_admin.erl:989 -msgid "Last year" -msgstr "上一年" - -#: ejabberd_web_admin.erl:991 -msgid "All activity" -msgstr "所有活动" - -#: ejabberd_web_admin.erl:994 -msgid "Show Ordinary Table" -msgstr "显示普通列表" - -#: ejabberd_web_admin.erl:997 -msgid "Show Integral Table" -msgstr "显示完整列表" - -#: ejabberd_web_admin.erl:1004 ejabberd_web_admin.erl:1847 -#: mod_muc_admin.erl:247 -msgid "Statistics" -msgstr "统计" - -#: ejabberd_web_admin.erl:1014 -msgid "Not Found" -msgstr "没有找到" - -#: ejabberd_web_admin.erl:1027 -msgid "Node not found" -msgstr "没有找到节点" - -#: ejabberd_web_admin.erl:1250 mod_shared_roster.erl:1161 -msgid "Add New" -msgstr "添加新用户" - -#: ejabberd_web_admin.erl:1338 -msgid "Host" -msgstr "主机" - -#: ejabberd_web_admin.erl:1339 -msgid "Registered Users" -msgstr "注册用户" - -#: ejabberd_web_admin.erl:1417 mod_configure.erl:177 mod_configure.erl:539 -#: mod_configure.erl:1386 -msgid "Add User" -msgstr "添加用户" - -#: ejabberd_web_admin.erl:1459 -msgid "Offline Messages" -msgstr "离线消息" - -#: ejabberd_web_admin.erl:1460 ejabberd_web_admin.erl:1688 -msgid "Last Activity" -msgstr "上次活动" - -#: ejabberd_web_admin.erl:1478 ejabberd_web_admin.erl:1660 -#: mod_configure.erl:1916 -msgid "Never" -msgstr "从未" - -#: ejabberd_web_admin.erl:1496 ejabberd_web_admin.erl:1671 -#: mod_configure.erl:1926 -msgid "Online" -msgstr "在线" - -#: ejabberd_web_admin.erl:1550 ejabberd_web_admin.erl:1569 -msgid "Registered Users:" -msgstr "注册用户:" - -#: ejabberd_web_admin.erl:1553 ejabberd_web_admin.erl:1572 -#: ejabberd_web_admin.erl:2187 -msgid "Online Users:" -msgstr "在线用户:" - -#: ejabberd_web_admin.erl:1556 -msgid "Outgoing s2s Connections:" -msgstr "出站 s2s 连接:" - -#: ejabberd_web_admin.erl:1559 -msgid "Incoming s2s Connections:" -msgstr "入站 s2s 连接:" - -#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794 -#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429 -msgid "None" -msgstr "无" - -#: ejabberd_web_admin.erl:1652 mod_register_web.erl:188 -#: mod_register_web.erl:347 mod_register_web.erl:355 mod_register_web.erl:379 -msgid "Change Password" -msgstr "更改密码" - -#: ejabberd_web_admin.erl:1673 -msgid "User ~s" -msgstr "用户~s" - -#: ejabberd_web_admin.erl:1684 -msgid "Connected Resources:" -msgstr "已连接资源:" - -#: ejabberd_web_admin.erl:1686 mod_register_web.erl:239 -#: mod_register_web.erl:475 -msgid "Password:" -msgstr "密码:" - -#: ejabberd_web_admin.erl:1693 mod_configure.erl:2117 -msgid "Remove User" -msgstr "删除用户" - -#: ejabberd_web_admin.erl:1740 -msgid "No Data" -msgstr "没有数据" - -#: ejabberd_web_admin.erl:1813 -msgid "Nodes" -msgstr "节点" - -#: ejabberd_web_admin.erl:1814 mod_configure.erl:528 -msgid "Running Nodes" -msgstr "运行中的节点" - -#: ejabberd_web_admin.erl:1815 mod_configure.erl:529 -msgid "Stopped Nodes" -msgstr "已经停止的节点" - -#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858 -msgid "Node ~p" -msgstr "节点~p" - -#: ejabberd_web_admin.erl:1842 mod_configure.erl:150 mod_configure.erl:611 -msgid "Database" -msgstr "数据库" - -#: ejabberd_web_admin.erl:1843 mod_configure.erl:159 mod_configure.erl:648 -msgid "Backup" -msgstr "备份" - -#: ejabberd_web_admin.erl:1845 -msgid "Listened Ports" -msgstr "被监听的端口" - -#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261 -msgid "Update" -msgstr "更新" - -#: ejabberd_web_admin.erl:1852 ejabberd_web_admin.erl:2469 -#: ejabberd_web_admin.erl:2613 -msgid "Restart" -msgstr "重启" - -#: ejabberd_web_admin.erl:1854 ejabberd_web_admin.erl:2473 -#: ejabberd_web_admin.erl:2617 -msgid "Stop" -msgstr "停止" - -#: ejabberd_web_admin.erl:1861 mod_configure.erl:613 mod_configure.erl:626 -msgid "Modules" -msgstr "模块" - -#: ejabberd_web_admin.erl:1866 -msgid "RPC Call Error" -msgstr "RPC 调用错误" - -#: ejabberd_web_admin.erl:1917 -msgid "Database Tables at ~p" -msgstr "位于~p的数据库表" - -#: ejabberd_web_admin.erl:1927 mod_vcard.erl:490 mod_vcard.erl:616 -msgid "Name" -msgstr "姓名" - -#: ejabberd_web_admin.erl:1928 -msgid "Storage Type" -msgstr "存储类型" - -#: ejabberd_web_admin.erl:1929 -msgid "Elements" -msgstr "元素" - -#: ejabberd_web_admin.erl:1930 -msgid "Memory" -msgstr "内存" - -#: ejabberd_web_admin.erl:1952 ejabberd_web_admin.erl:2123 -msgid "Error" -msgstr "错误" - -#: ejabberd_web_admin.erl:1955 -msgid "Backup of ~p" -msgstr "~p的备份" - -#: ejabberd_web_admin.erl:1959 -msgid "" -"Please note that these options will only backup the builtin Mnesia database. " -"If you are using the ODBC module, you also need to backup your SQL database " -"separately." -msgstr "" -"注意:这些选项仅将备份内置的 Mnesia 数据库. 如果您正在使用 ODBC 模块, 您还需" -"要分别备份您的数据库." - -#: ejabberd_web_admin.erl:1969 -msgid "Store binary backup:" -msgstr "存储为二进制备份:" - -#: ejabberd_web_admin.erl:1976 ejabberd_web_admin.erl:1986 -#: ejabberd_web_admin.erl:1997 ejabberd_web_admin.erl:2006 -#: ejabberd_web_admin.erl:2016 ejabberd_web_admin.erl:2029 -#: ejabberd_web_admin.erl:2041 ejabberd_web_admin.erl:2057 -#: ejabberd_web_admin.erl:2073 ejabberd_web_admin.erl:2084 -#: ejabberd_web_admin.erl:2094 -msgid "OK" -msgstr "确定" - -#: ejabberd_web_admin.erl:1979 -msgid "Restore binary backup immediately:" -msgstr "立即恢复二进制备份:" - -#: ejabberd_web_admin.erl:1989 -msgid "" -"Restore binary backup after next ejabberd restart (requires less memory):" -msgstr "在下次 ejabberd 重启后恢复二进制备份(需要的内存更少):" - -#: ejabberd_web_admin.erl:1999 -msgid "Store plain text backup:" -msgstr "存储为普通文本备份:" - -#: ejabberd_web_admin.erl:2009 -msgid "Restore plain text backup immediately:" -msgstr "立即恢复普通文本备份:" - -#: ejabberd_web_admin.erl:2019 -msgid "Import users data from a PIEFXIS file (XEP-0227):" -msgstr "从 PIEFXIS 文件 (XEP-0227) 导入用户数据:" - -#: ejabberd_web_admin.erl:2032 -msgid "Export data of all users in the server to PIEFXIS files (XEP-0227):" -msgstr "将服务器上所有用户的数据导出到 PIEFXIS 文件 (XEP-0227):" - -#: ejabberd_web_admin.erl:2044 -msgid "Export data of users in a host to PIEFXIS files (XEP-0227):" -msgstr "将某主机的用户数据导出到 PIEFXIS 文件 (XEP-0227):" - -#: ejabberd_web_admin.erl:2060 -msgid "Export all tables as SQL queries to a file:" -msgstr "将所有表以SQL查询语句导出到文件:" - -#: ejabberd_web_admin.erl:2076 -msgid "Import user data from jabberd14 spool file:" -msgstr "从 jabberd14 Spool 文件导入用户数据:" - -#: ejabberd_web_admin.erl:2087 -msgid "Import users data from jabberd14 spool directory:" -msgstr "从jabberd14 Spool目录导入用户数据:" - -#: ejabberd_web_admin.erl:2115 -msgid "Listened Ports at " -msgstr "监听的端口位于" - -#: ejabberd_web_admin.erl:2144 -msgid "Modules at ~p" -msgstr "位于~p的模块" - -#: ejabberd_web_admin.erl:2175 -msgid "Statistics of ~p" -msgstr "~p的统计" - -#: ejabberd_web_admin.erl:2179 -msgid "Uptime:" -msgstr "正常运行时间:" - -#: ejabberd_web_admin.erl:2183 -msgid "CPU Time:" -msgstr "CPU时间:" - -#: ejabberd_web_admin.erl:2191 -msgid "Transactions Committed:" -msgstr "提交的事务:" - -#: ejabberd_web_admin.erl:2195 -msgid "Transactions Aborted:" -msgstr "取消的事务:" - -#: ejabberd_web_admin.erl:2199 -msgid "Transactions Restarted:" -msgstr "重启的事务:" - -#: ejabberd_web_admin.erl:2203 -msgid "Transactions Logged:" -msgstr "记入日志的事务:" - -#: ejabberd_web_admin.erl:2243 -msgid "Update ~p" -msgstr "更新~p" - -#: ejabberd_web_admin.erl:2254 -msgid "Update plan" -msgstr "更新计划" - -#: ejabberd_web_admin.erl:2255 -msgid "Modified modules" -msgstr "被修改模块" - -#: ejabberd_web_admin.erl:2256 -msgid "Update script" -msgstr "更新脚本" - -#: ejabberd_web_admin.erl:2257 -msgid "Low level update script" -msgstr "低级别更新脚本" - -#: ejabberd_web_admin.erl:2258 -msgid "Script check" -msgstr "脚本检查" - -#: ejabberd_web_admin.erl:2438 -msgid "IP" -msgstr "IP" - -#: ejabberd_web_admin.erl:2438 -msgid "Port" -msgstr "端口" - -#: ejabberd_web_admin.erl:2439 -msgid "Protocol" -msgstr "协议" - -#: ejabberd_web_admin.erl:2440 ejabberd_web_admin.erl:2595 -msgid "Module" -msgstr "模块" - -#: ejabberd_web_admin.erl:2441 ejabberd_web_admin.erl:2596 -msgid "Options" -msgstr "选项" - -#: ejabberd_web_admin.erl:2493 ejabberd_web_admin.erl:2629 -msgid "Start" -msgstr "开始" - -#: mod_adhoc.erl:114 mod_adhoc.erl:148 mod_adhoc.erl:168 mod_adhoc.erl:191 -msgid "Commands" -msgstr "命令" - -#: mod_adhoc.erl:176 mod_adhoc.erl:265 -msgid "Ping" -msgstr "Ping" - -#: mod_adhoc.erl:279 -msgid "Pong" -msgstr "Pong" - -#: mod_announce.erl:523 -msgid "Really delete message of the day?" -msgstr "确实要删除每日消息吗?" - -#: mod_announce.erl:536 mod_configure.erl:1238 mod_configure.erl:1298 -msgid "Subject" -msgstr "标题" - -#: mod_announce.erl:544 mod_configure.erl:1244 mod_configure.erl:1304 -msgid "Message body" -msgstr "消息主体" - -#: mod_announce.erl:627 -msgid "No body provided for announce message" -msgstr "通知消息无正文内容" - -#: mod_announce.erl:662 -msgid "Announcements" -msgstr "通知" - -#: mod_announce.erl:664 -msgid "Send announcement to all users" -msgstr "发送通知给所有用户" - -#: mod_announce.erl:666 -msgid "Send announcement to all users on all hosts" -msgstr "发送通知给所有主机上的所有用户" - -#: mod_announce.erl:668 -msgid "Send announcement to all online users" -msgstr "发送通知给所有在线用户" - -#: mod_announce.erl:670 mod_configure.erl:1231 mod_configure.erl:1291 -msgid "Send announcement to all online users on all hosts" -msgstr "发送通知给所有主机的在线用户" - -#: mod_announce.erl:672 -msgid "Set message of the day and send to online users" -msgstr "设定每日消息并发送给所有在线用户" - -#: mod_announce.erl:674 -msgid "Set message of the day on all hosts and send to online users" -msgstr "设置所有主机上的每日消息并发送给在线用户" - -#: mod_announce.erl:676 -msgid "Update message of the day (don't send)" -msgstr "更新每日消息(不发送)" - -#: mod_announce.erl:678 -msgid "Update message of the day on all hosts (don't send)" -msgstr "更新所有主机上的每日消息(不发送)" - -#: mod_announce.erl:680 -msgid "Delete message of the day" -msgstr "删除每日消息" - -#: mod_announce.erl:682 -msgid "Delete message of the day on all hosts" -msgstr "删除所有主机上的每日消息" - -#: mod_configure.erl:140 mod_configure.erl:296 mod_configure.erl:318 -#: mod_configure.erl:522 -msgid "Configuration" -msgstr "配置" - -#: mod_configure.erl:153 mod_configure.erl:636 -msgid "Start Modules" -msgstr "启动模块" - -#: mod_configure.erl:156 mod_configure.erl:638 -msgid "Stop Modules" -msgstr "停止模块" - -#: mod_configure.erl:162 mod_configure.erl:650 -msgid "Restore" -msgstr "恢复" - -#: mod_configure.erl:165 mod_configure.erl:652 -msgid "Dump to Text File" -msgstr "转储到文本文件" - -#: mod_configure.erl:168 mod_configure.erl:663 -msgid "Import File" -msgstr "导入文件" - -#: mod_configure.erl:171 mod_configure.erl:665 -msgid "Import Directory" -msgstr "导入目录" - -#: mod_configure.erl:173 mod_configure.erl:619 mod_configure.erl:1205 -msgid "Restart Service" -msgstr "重启服务" - -#: mod_configure.erl:175 mod_configure.erl:621 mod_configure.erl:1265 -msgid "Shut Down Service" -msgstr "关闭服务" - -#: mod_configure.erl:179 mod_configure.erl:540 mod_configure.erl:1419 -msgid "Delete User" -msgstr "删除用户" - -#: mod_configure.erl:181 mod_configure.erl:542 mod_configure.erl:1437 -msgid "End User Session" -msgstr "结束用户会话" - -#: mod_configure.erl:183 mod_configure.erl:544 mod_configure.erl:1455 -#: mod_configure.erl:1473 -msgid "Get User Password" -msgstr "获取用户密码" - -#: mod_configure.erl:185 mod_configure.erl:546 -msgid "Change User Password" -msgstr "更改用户密码" - -#: mod_configure.erl:187 mod_configure.erl:548 mod_configure.erl:1500 -msgid "Get User Last Login Time" -msgstr "获取用户上次登陆时间" - -#: mod_configure.erl:189 mod_configure.erl:550 mod_configure.erl:1517 -msgid "Get User Statistics" -msgstr "获取用户统计" - -#: mod_configure.erl:191 mod_configure.erl:552 -msgid "Get Number of Registered Users" -msgstr "获取注册用户数" - -#: mod_configure.erl:194 mod_configure.erl:554 -msgid "Get Number of Online Users" -msgstr "获取在线用户数" - -#: mod_configure.erl:320 mod_configure.erl:523 -msgid "User Management" -msgstr "用户管理" - -#: mod_configure.erl:525 -msgid "All Users" -msgstr "所有用户" - -#: mod_configure.erl:526 -msgid "Outgoing s2s Connections" -msgstr "出站 s2s 连接" - -#: mod_configure.erl:615 -msgid "Backup Management" -msgstr "备份管理" - -#: mod_configure.erl:617 -msgid "Import Users From jabberd14 Spool Files" -msgstr "从 jabberd14 Spool 文件导入用户" - -#: mod_configure.erl:762 -msgid "To ~s" -msgstr "发送给~s" - -#: mod_configure.erl:782 -msgid "From ~s" -msgstr "来自~s" - -#: mod_configure.erl:1002 -msgid "Database Tables Configuration at " -msgstr "数据库表格配置位于" - -#: mod_configure.erl:1008 -msgid "Choose storage type of tables" -msgstr "请选择表格的存储类型" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Disc only copy" -msgstr "仅磁盘复制" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM and disc copy" -msgstr "内存与磁盘复制" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "RAM copy" -msgstr "内存(RAM)复制" - -#: mod_configure.erl:1017 mod_configure.erl:1019 -msgid "Remote copy" -msgstr "远程复制" - -#: mod_configure.erl:1045 -msgid "Stop Modules at " -msgstr "要停止的模块位于 " - -#: mod_configure.erl:1051 -msgid "Choose modules to stop" -msgstr "请选择要停止的模块" - -#: mod_configure.erl:1072 -msgid "Start Modules at " -msgstr "要启动的模块位于 " - -#: mod_configure.erl:1078 -msgid "Enter list of {Module, [Options]}" -msgstr "请输入{模块, [选项]}列表" - -#: mod_configure.erl:1080 -msgid "List of modules to start" -msgstr "要启动的模块列表" - -#: mod_configure.erl:1094 -msgid "Backup to File at " -msgstr "备份文件位于" - -#: mod_configure.erl:1099 mod_configure.erl:1120 -msgid "Enter path to backup file" -msgstr "请输入备份文件的路径" - -#: mod_configure.erl:1100 mod_configure.erl:1121 mod_configure.erl:1142 -#: mod_configure.erl:1163 -msgid "Path to File" -msgstr "文件路径" - -#: mod_configure.erl:1115 -msgid "Restore Backup from File at " -msgstr "要恢复的备份文件位于" - -#: mod_configure.erl:1136 -msgid "Dump Backup to Text File at " -msgstr "转储备份到文本文件于" - -#: mod_configure.erl:1141 -msgid "Enter path to text file" -msgstr "请输入文本文件的路径" - -#: mod_configure.erl:1156 -msgid "Import User from File at " -msgstr "导入用户的文件位于" - -#: mod_configure.erl:1162 -msgid "Enter path to jabberd14 spool file" -msgstr "请输入 jabberd14 spool 文件的路径" - -#: mod_configure.erl:1177 -msgid "Import Users from Dir at " -msgstr "导入用户的目录位于" - -#: mod_configure.erl:1183 -msgid "Enter path to jabberd14 spool dir" -msgstr "请输入jabberd14 spool目录的路径" - -#: mod_configure.erl:1184 -msgid "Path to Dir" -msgstr "目录的路径" - -#: mod_configure.erl:1209 mod_configure.erl:1269 -msgid "Time delay" -msgstr "时间延迟" - -#: mod_configure.erl:1316 -msgid "Access Control List Configuration" -msgstr "访问控制列表(ACL)配置" - -#: mod_configure.erl:1321 -msgid "Access control lists" -msgstr "访问控制列表(ACL)" - -#: mod_configure.erl:1352 -msgid "Access Configuration" -msgstr "访问配置" - -#: mod_configure.erl:1356 -msgid "Access rules" -msgstr "访问规则" - -#: mod_configure.erl:1390 mod_configure.erl:1423 mod_configure.erl:1441 -#: mod_configure.erl:1459 mod_configure.erl:1477 mod_configure.erl:1504 -#: mod_configure.erl:1521 mod_configure.erl:1887 mod_configure.erl:1934 -#: mod_configure.erl:1961 mod_roster.erl:1434 mod_vcard.erl:613 -#: mod_vcard_ldap.erl:606 -msgid "Jabber ID" -msgstr "Jabber ID" - -#: mod_configure.erl:1407 -msgid "Password Verification" -msgstr "确认密码" - -#: mod_configure.erl:1540 -msgid "Number of registered users" -msgstr "注册用户数" - -#: mod_configure.erl:1559 -msgid "Number of online users" -msgstr "在线用户数" - -#: mod_configure.erl:1936 -msgid "Last login" -msgstr "上次登陆" - -#: mod_configure.erl:1963 -msgid "Roster size" -msgstr "花名册大小" - -#: mod_configure.erl:1965 -msgid "IP addresses" -msgstr "IP地址" - -#: mod_configure.erl:1967 -msgid "Resources" -msgstr "资源" - -#: mod_configure.erl:2095 -msgid "Administration of " -msgstr "管理" - -#: mod_configure.erl:2100 -msgid "Action on user" -msgstr "对用户的动作" - -#: mod_configure.erl:2108 -msgid "Edit Properties" -msgstr "编辑属性" - -#: mod_fail2ban.erl:95 -msgid "" -"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_http_upload.erl:586 -msgid "Please specify file size." -msgstr "请指定文件大小." - -#: mod_http_upload.erl:590 -msgid "Please specify file name." -msgstr "请指定文件名称." - -#: mod_ip_blacklist.erl:121 -msgid "This IP address is blacklisted in ~s" -msgstr "此IP地址在~s中已被列入黑名单" - -#: mod_irc.erl:220 mod_muc.erl:467 -msgid "Access denied by service policy" -msgstr "访问被服务策略拒绝" - -#: mod_irc.erl:439 -msgid "IRC Transport" -msgstr "IRC传输" - -#: mod_irc.erl:476 -msgid "ejabberd IRC module" -msgstr "ejabberd IRC 模块" - -#: mod_irc.erl:644 -msgid "You need an x:data capable client to configure mod_irc settings" -msgstr "您需要一个兼容 x:data 的客户端来配置mod_irc设置" - -#: mod_irc.erl:653 -msgid "Registration in mod_irc for " -msgstr "mod_irc 中的注册是为 " - -#: mod_irc.erl:659 -msgid "" -"Enter username, encodings, ports and passwords you wish to use for " -"connecting to IRC servers" -msgstr "请输入您想使用的用来连接到IRC服务器的用户名, 编码, 端口和密码." - -#: mod_irc.erl:667 -msgid "IRC Username" -msgstr "IRC用户名" - -#: mod_irc.erl:682 -msgid "" -"If you want to specify different ports, passwords, encodings for IRC " -"servers, fill this list with values in format '{\"irc server\", \"encoding" -"\", port, \"password\"}'. By default this service use \"~s\" encoding, port " -"~p, empty password." -msgstr "" -"如果您想为 IRC 服务器指定不同的端口, 密码, 编码, 请用 '{\"irc 服务器\", \"编" -"码\", 端口, \"密码\"}' 格式的值填充此表单. 默认情况下此服务使用\"~s\"编码, " -"~p 端口, 密码为空." - -#: mod_irc.erl:704 -msgid "" -"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." -msgstr "" -"例如: [{\"irc.lucky.net\", \"koi8-r\"}, 6667, \"secret\"}, {\"vendetta.fef." -"net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]." - -#: mod_irc.erl:713 -msgid "Connections parameters" -msgstr "连接参数" - -#: mod_irc.erl:886 -msgid "Join IRC channel" -msgstr "加入IRC频道" - -#: mod_irc.erl:893 -msgid "IRC channel (don't put the first #)" -msgstr "IRC频道 (不要输入第一个#号)" - -#: mod_irc.erl:903 -msgid "IRC server" -msgstr "IRC服务器" - -#: mod_irc.erl:950 mod_irc.erl:958 -msgid "Join the IRC channel here." -msgstr "在这里加入IRC频道." - -#: mod_irc.erl:967 -msgid "Join the IRC channel in this Jabber ID: ~s" -msgstr "用此Jabber ID ~s加入IRC频道" - -#: mod_irc.erl:1046 -msgid "IRC settings" -msgstr "IRC设置" - -#: mod_irc.erl:1051 -msgid "" -"Enter username and encodings you wish to use for connecting to IRC servers. " -"Press 'Next' to get more fields to fill in. Press 'Complete' to save " -"settings." -msgstr "" -"请输入您想使用的用来连接到 IRC 服务器的用户名和编码. 按 '下一步' 获取更多待填" -"字段. 按 '完成' 保存设置." - -#: mod_irc.erl:1060 -msgid "IRC username" -msgstr "IRC用户名" - -#: mod_irc.erl:1126 -msgid "Password ~b" -msgstr "~b的密码" - -#: mod_irc.erl:1137 -msgid "Port ~b" -msgstr "~b的端口" - -#: mod_irc.erl:1150 -msgid "Encoding for server ~b" -msgstr "服务器~b的编码" - -#: mod_irc.erl:1171 -msgid "Server ~b" -msgstr "服务器~b" - -#: mod_mam.erl:541 -msgid "Only members may query archives of this room" -msgstr "只有会员可以查询本房间的存档" - -#: mod_muc.erl:585 -msgid "Only service administrators are allowed to send service messages" -msgstr "只有服务管理员可以发送服务消息" - -#: mod_muc.erl:622 -msgid "Room creation is denied by service policy" -msgstr "创建房间被服务策略拒绝" - -#: mod_muc.erl:629 -msgid "Conference room does not exist" -msgstr "会议室不存在" - -#: mod_muc.erl:740 mod_muc_admin.erl:321 -msgid "Chatrooms" -msgstr "聊天室" - -#: mod_muc.erl:781 -msgid "Empty Rooms" -msgstr "空房间" - -#: mod_muc.erl:933 -msgid "You need a client that supports x:data to register the nickname" -msgstr "您需要一个支持 x:data 的客户端来注册昵称" - -#: mod_muc.erl:943 -msgid "Nickname Registration at " -msgstr "昵称注册于" - -#: mod_muc.erl:949 -msgid "Enter nickname you want to register" -msgstr "请输入您想要注册的昵称" - -#: mod_muc.erl:950 mod_muc_room.erl:4353 mod_roster.erl:1435 mod_vcard.erl:490 -#: mod_vcard.erl:621 -msgid "Nickname" -msgstr "昵称" - -#: mod_muc.erl:1062 mod_muc_room.erl:1104 mod_muc_room.erl:1843 -msgid "That nickname is registered by another person" -msgstr "该昵称已被另一个人注册了" - -#: mod_muc.erl:1090 -msgid "You must fill in field \"Nickname\" in the form" -msgstr "您必须填充表单中\"昵称\"项" - -#: mod_muc.erl:1113 -msgid "ejabberd MUC module" -msgstr "ejabberd MUC 模块" - -#: mod_muc_admin.erl:231 mod_muc_admin.erl:234 mod_muc_admin.erl:246 -#: mod_muc_admin.erl:320 -msgid "Multi-User Chat" -msgstr "多用户聊天" - -#: mod_muc_admin.erl:249 -msgid "Total rooms" -msgstr "所有房间" - -#: mod_muc_admin.erl:250 -msgid "Permanent rooms" -msgstr "永久房间" - -#: mod_muc_admin.erl:251 -msgid "Registered nicknames" -msgstr "注册的昵称" - -#: mod_muc_admin.erl:254 -msgid "List of rooms" -msgstr "房间列表" - -#: mod_muc_log.erl:398 mod_muc_log.erl:407 -msgid "Chatroom configuration modified" -msgstr "聊天室配置已修改" - -#: mod_muc_log.erl:410 -msgid "joins the room" -msgstr "加入房间" - -#: mod_muc_log.erl:413 mod_muc_log.erl:416 -msgid "leaves the room" -msgstr "离开房间" - -#: mod_muc_log.erl:420 mod_muc_log.erl:423 -msgid "has been banned" -msgstr "已被禁止" - -#: mod_muc_log.erl:435 -msgid "has been kicked because of an affiliation change" -msgstr "因联属关系改变而被踢出" - -#: mod_muc_log.erl:440 -msgid "has been kicked because the room has been changed to members-only" -msgstr "因该房间改为只对会员开放而被踢出" - -#: mod_muc_log.erl:445 -msgid "has been kicked because of a system shutdown" -msgstr "因系统关机而被踢出" - -#: mod_muc_log.erl:450 -msgid "is now known as" -msgstr "现在称呼为" - -#: mod_muc_log.erl:453 mod_muc_log.erl:792 -msgid " has set the subject to: " -msgstr "已将标题设置为: " - -#: mod_muc_log.erl:493 -msgid "Chatroom is created" -msgstr "聊天室已被创建" - -#: mod_muc_log.erl:495 -msgid "Chatroom is destroyed" -msgstr "聊天室已被销毁" - -#: mod_muc_log.erl:497 -msgid "Chatroom is started" -msgstr "聊天室已被启动" - -#: mod_muc_log.erl:499 -msgid "Chatroom is stopped" -msgstr "聊天室已被停用" - -#: mod_muc_log.erl:503 -msgid "Monday" -msgstr "星期一" - -#: mod_muc_log.erl:504 -msgid "Tuesday" -msgstr "星期二" - -#: mod_muc_log.erl:505 -msgid "Wednesday" -msgstr "星期三" - -#: mod_muc_log.erl:506 -msgid "Thursday" -msgstr "星期四" - -#: mod_muc_log.erl:507 -msgid "Friday" -msgstr "星期五" - -#: mod_muc_log.erl:508 -msgid "Saturday" -msgstr "星期六" - -#: mod_muc_log.erl:509 -msgid "Sunday" -msgstr "星期天" - -#: mod_muc_log.erl:513 -msgid "January" -msgstr "一月" - -#: mod_muc_log.erl:514 -msgid "February" -msgstr "二月" - -#: mod_muc_log.erl:515 -msgid "March" -msgstr "三月" - -#: mod_muc_log.erl:516 -msgid "April" -msgstr "四月" - -#: mod_muc_log.erl:517 -msgid "May" -msgstr "五月" - -#: mod_muc_log.erl:518 -msgid "June" -msgstr "六月" - -#: mod_muc_log.erl:519 -msgid "July" -msgstr "七月" - -#: mod_muc_log.erl:520 -msgid "August" -msgstr "八月" - -#: mod_muc_log.erl:521 -msgid "September" -msgstr "九月" - -#: mod_muc_log.erl:522 -msgid "October" -msgstr "十月" - -#: mod_muc_log.erl:523 -msgid "November" -msgstr "十一月" - -#: mod_muc_log.erl:524 -msgid "December" -msgstr "十二月" - -#: mod_muc_log.erl:912 -msgid "Room Configuration" -msgstr "房间配置" - -#: mod_muc_log.erl:932 -msgid "Room Occupants" -msgstr "房间人数" - -#: mod_muc_room.erl:163 -msgid "Traffic rate limit is exceeded" -msgstr "已经超过传输率限制" - -#: mod_muc_room.erl:230 mod_muc_room.erl:518 mod_muc_room.erl:1059 -msgid "" -"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)并已被踢出房间" - -#: mod_muc_room.erl:241 -msgid "It is not allowed to send private messages to the conference" -msgstr "不允许向会议发送私聊消息" - -#: mod_muc_room.erl:316 -msgid "Please, wait for a while before sending new voice request" -msgstr "请稍后再发送新的声音请求" - -#: mod_muc_room.erl:329 -msgid "Voice requests are disabled in this conference" -msgstr "该会议的声音请求以被禁用" - -#: mod_muc_room.erl:347 -msgid "Failed to extract JID from your voice request approval" -msgstr "无法从你的声音请求确认信息中提取JID" - -#: mod_muc_room.erl:377 -msgid "Only moderators can approve voice requests" -msgstr "仅主持人能确认声音请求" - -#: mod_muc_room.erl:389 -msgid "Improper message type" -msgstr "不恰当的消息类型" - -#: mod_muc_room.erl:534 -msgid "It is not allowed to send private messages of type \"groupchat\"" -msgstr "\"群组聊天\"类型不允许发送私聊消息" - -#: mod_muc_room.erl:546 mod_muc_room.erl:621 -msgid "Recipient is not in the conference room" -msgstr "接收人不在会议室" - -#: mod_muc_room.erl:576 mod_muc_room.erl:598 -msgid "It is not allowed to send private messages" -msgstr "不可以发送私聊消息" - -#: mod_muc_room.erl:588 mod_muc_room.erl:983 mod_muc_room.erl:4594 -msgid "Only occupants are allowed to send messages to the conference" -msgstr "只有与会人可以向大会发送消息" - -#: mod_muc_room.erl:644 -msgid "Only occupants are allowed to send queries to the conference" -msgstr "只有与会人可以向大会发出查询请求" - -#: mod_muc_room.erl:657 -msgid "Queries to the conference members are not allowed in this room" -msgstr "本房间不可以查询会议成员信息" - -#: mod_muc_room.erl:961 -msgid "" -"Only moderators and participants are allowed to change the subject in this " -"room" -msgstr "只有主持人和参与人可以在此房间里更改主题" - -#: mod_muc_room.erl:966 -msgid "Only moderators are allowed to change the subject in this room" -msgstr "只有主持人可以在此房间里更改主题" - -#: mod_muc_room.erl:974 -msgid "Visitors are not allowed to send messages to all occupants" -msgstr "不允许访客给所有占有者发送消息" - -#: mod_muc_room.erl:1080 -msgid "Visitors are not allowed to change their nicknames in this room" -msgstr "此房间不允许用户更改昵称" - -#: mod_muc_room.erl:1093 mod_muc_room.erl:1835 -msgid "That nickname is already in use by another occupant" -msgstr "该昵称已被另一用户使用" - -#: mod_muc_room.erl:1822 -msgid "You have been banned from this room" -msgstr "您已被禁止进入该房间" - -#: mod_muc_room.erl:1826 -msgid "Membership is required to enter this room" -msgstr "进入此房间需要会员身份" - -#: mod_muc_room.erl:1872 -msgid "A password is required to enter this room" -msgstr "进入此房间需要密码" - -#: mod_muc_room.erl:1898 mod_register.erl:295 -msgid "Too many CAPTCHA requests" -msgstr "验证码请求太多" - -#: mod_muc_room.erl:1908 mod_register.erl:301 -msgid "Unable to generate a CAPTCHA" -msgstr "无法生成验证码" - -#: mod_muc_room.erl:1919 -msgid "Incorrect password" -msgstr "密码不正确" - -#: mod_muc_room.erl:2573 -msgid "Administrator privileges required" -msgstr "需要管理员权限" - -#: mod_muc_room.erl:2586 -msgid "Moderator privileges required" -msgstr "需要主持人权限" - -#: mod_muc_room.erl:2758 -msgid "Jabber ID ~s is invalid" -msgstr "Jabber ID ~s 无效" - -#: mod_muc_room.erl:2772 -msgid "Nickname ~s does not exist in the room" -msgstr "昵称~s不在该房间" - -#: mod_muc_room.erl:2795 mod_muc_room.erl:3175 -msgid "Invalid affiliation: ~s" -msgstr "无效加入: ~s" - -#: mod_muc_room.erl:2846 -msgid "Invalid role: ~s" -msgstr "无效角色: ~s" - -#: mod_muc_room.erl:3155 mod_muc_room.erl:3187 mod_muc_room.erl:4236 -msgid "Owner privileges required" -msgstr "需要持有人权限" - -#: mod_muc_room.erl:3348 -msgid "Configuration of room ~s" -msgstr "房间~s的配置 " - -#: mod_muc_room.erl:3359 -msgid "Room title" -msgstr "房间标题" - -#: mod_muc_room.erl:3361 mod_muc_room.erl:4190 -msgid "Room description" -msgstr "房间描述" - -#: mod_muc_room.erl:3369 -msgid "Make room persistent" -msgstr "永久保存该房间" - -#: mod_muc_room.erl:3375 -msgid "Make room public searchable" -msgstr "使房间可被公开搜索" - -#: mod_muc_room.erl:3378 -msgid "Make participants list public" -msgstr "公开参与人列表" - -#: mod_muc_room.erl:3380 -msgid "Make room password protected" -msgstr "进入此房间需要密码" - -#: mod_muc_room.erl:3394 -msgid "Maximum Number of Occupants" -msgstr "允许的与会人最大数" - -#: mod_muc_room.erl:3406 -msgid "No limit" -msgstr "不限" - -#: mod_muc_room.erl:3436 -msgid "Present real Jabber IDs to" -msgstr "将真实Jabber ID显示给" - -#: mod_muc_room.erl:3450 mod_muc_room.erl:3560 -msgid "moderators only" -msgstr "仅主持人" - -#: mod_muc_room.erl:3460 mod_muc_room.erl:3570 -msgid "anyone" -msgstr "任何人" - -#: mod_muc_room.erl:3471 -msgid "Roles for which Presence is Broadcasted" -msgstr "广播存在性的角色" - -#: mod_muc_room.erl:3486 -msgid "Moderator" -msgstr "主持人" - -#: mod_muc_room.erl:3496 -msgid "Participant" -msgstr "参与人" - -#: mod_muc_room.erl:3506 -msgid "Visitor" -msgstr "访客" - -#: mod_muc_room.erl:3513 -msgid "Make room members-only" -msgstr "设置房间只接收会员" - -#: mod_muc_room.erl:3516 -msgid "Make room moderated" -msgstr "设置房间只接收主持人" - -#: mod_muc_room.erl:3519 -msgid "Default users as participants" -msgstr "用户默认被视为参与人" - -#: mod_muc_room.erl:3522 -msgid "Allow users to change the subject" -msgstr "允许用户更改主题" - -#: mod_muc_room.erl:3525 -msgid "Allow users to send private messages" -msgstr "允许用户发送私聊消息" - -#: mod_muc_room.erl:3533 -msgid "Allow visitors to send private messages to" -msgstr "允许访客发送私聊消息至" - -#: mod_muc_room.erl:3551 -msgid "nobody" -msgstr "没有人" - -#: mod_muc_room.erl:3576 -msgid "Allow users to query other users" -msgstr "允许用户查询其它用户" - -#: mod_muc_room.erl:3579 -msgid "Allow users to send invites" -msgstr "允许用户发送邀请" - -#: mod_muc_room.erl:3582 -msgid "Allow visitors to send status text in presence updates" -msgstr "更新在线状态时允许用户发送状态文本" - -#: mod_muc_room.erl:3586 -msgid "Allow visitors to change nickname" -msgstr "允许用户更改昵称" - -#: mod_muc_room.erl:3589 -msgid "Allow visitors to send voice requests" -msgstr "允许访客发送声音请求" - -#: mod_muc_room.erl:3592 -msgid "Minimum interval between voice requests (in seconds)" -msgstr "声音请求的最小间隔(以秒为单位)" - -#: mod_muc_room.erl:3599 -msgid "Make room CAPTCHA protected" -msgstr "保护房间验证码" - -#: mod_muc_room.erl:3606 -msgid "Enable message archiving" -msgstr "启用消息归档" - -#: mod_muc_room.erl:3612 -msgid "Exclude Jabber IDs from CAPTCHA challenge" -msgstr "从验证码挑战中排除Jabber ID" - -#: mod_muc_room.erl:3621 -msgid "Enable logging" -msgstr "启用服务器端聊天记录" - -#: mod_muc_room.erl:3631 -msgid "You need an x:data capable client to configure room" -msgstr "您需要一个兼容 x:data 的客户端来配置房间" - -#: mod_muc_room.erl:4192 -msgid "Number of occupants" -msgstr "驻留人数" - -#: mod_muc_room.erl:4262 -msgid "private, " -msgstr "保密, " - -#: mod_muc_room.erl:4326 -msgid "Voice request" -msgstr "声音请求" - -#: mod_muc_room.erl:4331 -msgid "Either approve or decline the voice request." -msgstr "接受或拒绝声音请求" - -#: mod_muc_room.erl:4351 -msgid "User JID" -msgstr "用户JID" - -#: mod_muc_room.erl:4355 -msgid "Grant voice to this person?" -msgstr "为此人授权声音?" - -#: mod_muc_room.erl:4498 -msgid "~s invites you to the room ~s" -msgstr "~s邀请你到房间~s" - -#: mod_muc_room.erl:4509 -msgid "the password is" -msgstr "密码是" - -#: mod_multicast.erl:291 -msgid "Multicast" -msgstr "多重映射" - -#: mod_multicast.erl:306 -msgid "ejabberd Multicast service" -msgstr "ejabberd多重映射服务" - -#: mod_offline.erl:647 -msgid "" -"Your contact offline message queue is full. The message has been discarded." -msgstr "您的联系人离线消息队列已满. 消息已被丢弃" - -#: mod_offline.erl:798 -msgid "~s's Offline Messages Queue" -msgstr "~s的离线消息队列" - -#: mod_offline.erl:811 -msgid "Time" -msgstr "时间" - -#: mod_offline.erl:812 -msgid "From" -msgstr "从" - -#: mod_offline.erl:813 -msgid "To" -msgstr "到" - -#: mod_offline.erl:814 -msgid "Packet" -msgstr "数据包" - -#: mod_offline.erl:992 -msgid "Offline Messages:" -msgstr "离线消息:" - -#: mod_offline.erl:996 -msgid "Remove All Offline Messages" -msgstr "移除所有离线消息" - -#: mod_proxy65_service.erl:248 -msgid "ejabberd SOCKS5 Bytestreams module" -msgstr "ejabberd SOCKS5 字节流模块" - -#: mod_pubsub.erl:1102 -msgid "Publish-Subscribe" -msgstr "发行-订阅" - -#: mod_pubsub.erl:1222 -msgid "ejabberd Publish-Subscribe module" -msgstr "ejabberd 发行-订阅模块" - -#: mod_pubsub.erl:1537 -msgid "PubSub subscriber request" -msgstr "PubSub订阅人请求" - -#: mod_pubsub.erl:1543 -msgid "Choose whether to approve this entity's subscription." -msgstr "选择是否允许该实体的订阅" - -#: mod_pubsub.erl:1559 -msgid "Node ID" -msgstr "节点ID" - -#: mod_pubsub.erl:1571 -msgid "Subscriber Address" -msgstr "订阅人地址" - -#: mod_pubsub.erl:1584 -msgid "Allow this Jabber ID to subscribe to this pubsub node?" -msgstr "允许该Jabber ID订阅该pubsub节点?" - -#: mod_pubsub.erl:3745 -msgid "Deliver payloads with event notifications" -msgstr "用事件通告传输有效负载" - -#: mod_pubsub.erl:3747 -msgid "Deliver event notifications" -msgstr "传递事件通知" - -#: mod_pubsub.erl:3749 -msgid "Notify subscribers when the node configuration changes" -msgstr "当节点设置改变时通知订阅人" - -#: mod_pubsub.erl:3751 -msgid "Notify subscribers when the node is deleted" -msgstr "当节点被删除时通知订阅人" - -#: mod_pubsub.erl:3753 -msgid "Notify subscribers when items are removed from the node" -msgstr "当从节点删除内容条目时通知订阅人" - -#: mod_pubsub.erl:3755 -msgid "Persist items to storage" -msgstr "持久化内容条目" - -#: mod_pubsub.erl:3757 -msgid "A friendly name for the node" -msgstr "该节点的友好名称" - -#: mod_pubsub.erl:3759 -msgid "Max # of items to persist" -msgstr "允许持久化的最大内容条目数" - -#: mod_pubsub.erl:3761 -msgid "Whether to allow subscriptions" -msgstr "是否允许订阅" - -#: mod_pubsub.erl:3763 -msgid "Specify the access model" -msgstr "指定访问范例" - -#: mod_pubsub.erl:3765 -msgid "Roster groups allowed to subscribe" -msgstr "允许订阅的花名册组" - -#: mod_pubsub.erl:3767 -msgid "Specify the publisher model" -msgstr "指定发布人范例" - -#: mod_pubsub.erl:3769 -msgid "Purge all items when the relevant publisher goes offline" -msgstr "相关发布人离线后清除所有选项" - -#: mod_pubsub.erl:3771 -msgid "Specify the event message type" -msgstr "指定事件消息类型" - -# bytes was translated as 'bits'. It's corrected now. -#: mod_pubsub.erl:3773 -msgid "Max payload size in bytes" -msgstr "最大有效负载字节数" - -#: mod_pubsub.erl:3775 -msgid "When to send the last published item" -msgstr "何时发送最新发布的内容条目" - -#: mod_pubsub.erl:3777 -msgid "Only deliver notifications to available users" -msgstr "仅将通知发送给可发送的用户" - -#: mod_pubsub.erl:3779 -msgid "The collections with which a node is affiliated" -msgstr "加入结点的集合" - -#: mod_register.erl:209 -msgid "The CAPTCHA verification has failed" -msgstr "验证码检查失败" - -#: mod_register.erl:253 -msgid "You need a client that supports x:data and CAPTCHA to register" -msgstr "您需要一个支持 x:data 和验证码的客户端进行注册" - -#: mod_register.erl:259 mod_register.erl:320 -msgid "Choose a username and password to register with this server" -msgstr "请选择在此服务器上注册所需的用户名和密码" - -#: mod_register.erl:373 mod_register.erl:421 -msgid "The password is too weak" -msgstr "密码强度太弱" - -#: mod_register.erl:426 -msgid "Users are not allowed to register accounts so quickly" -msgstr "不允许用户太频繁地注册帐户" - -#: mod_register_web.erl:105 -msgid "Your Jabber account was successfully created." -msgstr "你的Jabber帐户已成功创建." - -#: mod_register_web.erl:110 -msgid "There was an error creating the account: " -msgstr "帐户创建出错: " - -#: mod_register_web.erl:119 -msgid "Your Jabber account was successfully deleted." -msgstr "你的 Jabber 帐户已成功删除." - -#: mod_register_web.erl:124 -msgid "There was an error deleting the account: " -msgstr "帐户删除失败: " - -#: mod_register_web.erl:135 -msgid "The password of your Jabber account was successfully changed." -msgstr "你的Jabber帐户密码已成功更新." - -#: mod_register_web.erl:140 -msgid "There was an error changing the password: " -msgstr "修改密码出错: " - -#: mod_register_web.erl:175 mod_register_web.erl:183 -msgid "Jabber Account Registration" -msgstr "Jabber帐户注册" - -#: mod_register_web.erl:186 mod_register_web.erl:204 mod_register_web.erl:212 -msgid "Register a Jabber account" -msgstr "注册Jabber帐户" - -#: mod_register_web.erl:191 mod_register_web.erl:453 mod_register_web.erl:461 -msgid "Unregister a Jabber account" -msgstr "注销Jabber帐户" - -#: mod_register_web.erl:214 -msgid "" -"This page allows to create a Jabber account in this Jabber server. Your JID " -"(Jabber IDentifier) will be of the form: username@server. Please read " -"carefully the instructions to fill correctly the fields." -msgstr "" -"本页面允许在此服务器上创建Jabber帐户. 你的JID (Jabber ID) 的形式如下: 用户" -"名@服务器. 请仔细阅读说明并正确填写相应字段." - -#: mod_register_web.erl:224 mod_register_web.erl:360 mod_register_web.erl:469 -msgid "Username:" -msgstr "用户名:" - -#: mod_register_web.erl:230 -msgid "This is case insensitive: macbeth is the same that MacBeth and Macbeth." -msgstr "此处不区分大小写: macbeth 与 MacBeth 和 Macbeth 是一样的." - -#: mod_register_web.erl:233 -msgid "Characters not allowed:" -msgstr "禁用字符:" - -#: mod_register_web.erl:236 mod_register_web.erl:364 mod_register_web.erl:473 -msgid "Server:" -msgstr "服务器:" - -#: mod_register_web.erl:245 -msgid "" -"Don't tell your password to anybody, not even the administrators of the " -"Jabber server." -msgstr "不要将密码告诉任何人, 就算是Jabber服务器的管理员也不可以." - -#: mod_register_web.erl:249 -msgid "You can later change your password using a Jabber client." -msgstr "你可以稍后用Jabber客户端修改你的密码." - -#: mod_register_web.erl:252 -msgid "" -"Some Jabber clients can store your password in the computer, but you should " -"do this only in your personal computer for safety reasons." -msgstr "" -"某些 Jabber 客户端可以在你的计算机里存储密码. 请仅在你确认你的计算机安全的情" -"况下使用该功能." - -#: mod_register_web.erl:256 -msgid "" -"Memorize your password, or write it in a paper placed in a safe place. In " -"Jabber there isn't an automated way to recover your password if you forget " -"it." -msgstr "" -"记住你的密码, 或将其记到纸上并放于安全位置. 如果你忘记了密码, Jabber也没有自" -"动恢复密码的方式." - -#: mod_register_web.erl:262 mod_register_web.erl:374 -msgid "Password Verification:" -msgstr "密码确认:" - -#: mod_register_web.erl:269 -msgid "Register" -msgstr "注册" - -#: mod_register_web.erl:366 -msgid "Old Password:" -msgstr "旧密码: " - -#: mod_register_web.erl:370 -msgid "New Password:" -msgstr "新密码:" - -#: mod_register_web.erl:463 -msgid "This page allows to unregister a Jabber account in this Jabber server." -msgstr "此页面允许在此Jabber服务器上注销Jabber帐户" - -#: mod_register_web.erl:480 -msgid "Unregister" -msgstr "取消注册" - -#: mod_roster.erl:1436 -msgid "Subscription" -msgstr "订阅" - -#: mod_roster.erl:1437 -msgid "Pending" -msgstr "挂起" - -#: mod_roster.erl:1438 -msgid "Groups" -msgstr "组" - -#: mod_roster.erl:1476 -msgid "Validate" -msgstr "确认" - -#: mod_roster.erl:1485 -msgid "Remove" -msgstr "移除" - -#: mod_roster.erl:1490 -msgid "Roster of " -msgstr "花名册属于" - -#: mod_roster.erl:1504 -msgid "Add Jabber ID" -msgstr "添加Jabber ID" - -#: mod_roster.erl:1622 -msgid "Roster" -msgstr "花名册" - -#: mod_shared_roster.erl:1120 mod_shared_roster.erl:1162 -#: mod_shared_roster.erl:1256 -msgid "Shared Roster Groups" -msgstr "共享的花名册组群" - -#: mod_shared_roster.erl:1232 -msgid "Name:" -msgstr "姓名:" - -#: mod_shared_roster.erl:1236 -msgid "Description:" -msgstr "描述:" - -#: mod_shared_roster.erl:1243 -msgid "Members:" -msgstr "会员:" - -#: mod_shared_roster.erl:1250 -msgid "Displayed Groups:" -msgstr "已显示的组:" - -#: mod_shared_roster.erl:1259 -msgid "Group " -msgstr "组" - -#: mod_vcard.erl:168 mod_vcard_ldap.erl:225 -msgid "Erlang Jabber Server" -msgstr "Erlang Jabber服务器" - -#: mod_vcard.erl:490 mod_vcard.erl:622 -msgid "Birthday" -msgstr "出生日期" - -#: mod_vcard.erl:490 mod_vcard.erl:624 -msgid "City" -msgstr "城市" - -#: mod_vcard.erl:490 mod_vcard.erl:623 -msgid "Country" -msgstr "国家" - -#: mod_vcard.erl:490 mod_vcard.erl:625 -msgid "Email" -msgstr "电子邮件" - -#: mod_vcard.erl:490 mod_vcard.erl:619 -msgid "Family Name" -msgstr "姓氏" - -#: mod_vcard.erl:490 -msgid "" -"Fill in the form to search for any matching Jabber User (Add * to the end of " -"field to match substring)" -msgstr "填充表单以搜索任何匹配的Jabber用户(在字段末添加*来匹配子串)" - -#: mod_vcard.erl:490 mod_vcard.erl:615 -msgid "Full Name" -msgstr "全名" - -#: mod_vcard.erl:490 mod_vcard.erl:617 -msgid "Middle Name" -msgstr "中间名" - -#: mod_vcard.erl:490 mod_vcard.erl:626 -msgid "Organization Name" -msgstr "组织名称" - -#: mod_vcard.erl:490 mod_vcard.erl:628 -msgid "Organization Unit" -msgstr "组织单位" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "Search users in " -msgstr "搜索用户于" - -#: mod_vcard.erl:490 mod_vcard_ldap.erl:502 -msgid "You need an x:data capable client to search" -msgstr "您需要一个兼容 x:data 的客户端来搜索" - -#: mod_vcard.erl:519 mod_vcard_ldap.erl:531 -msgid "vCard User Search" -msgstr "vCard用户搜索" - -#: mod_vcard.erl:580 mod_vcard_ldap.erl:586 -msgid "ejabberd vCard module" -msgstr "ejabberd vCard模块" - -#: mod_vcard.erl:609 mod_vcard_ldap.erl:602 -msgid "Search Results for " -msgstr "搜索结果属于关键词 " - -#: mod_vcard_ldap.erl:502 -msgid "Fill in fields to search for any matching Jabber User" -msgstr "填充字段以搜索任何匹配的Jabber用户" - -#~ msgid "Outgoing s2s Servers:" -#~ msgstr "出站 s2s 服务器" - -#~ msgid "Delete" -#~ msgstr "删除" - -#~ msgid "This room is not anonymous" -#~ msgstr "此房间不是匿名房间" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message" -#~ msgstr "该参与人由于发送了错误消息而被踢出了房间" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error message " -#~ "to another participant" -#~ msgstr "该参与人由于给其他人发送了出错消息而被踢出了房间" - -#~ msgid "" -#~ "This participant is kicked from the room because he sent an error presence" -#~ msgstr "该用户由于发送了错误状态而被踢出了房间" - -#~ msgid "Captcha test failed" -#~ msgstr "验证码检测失败." - -#~ msgid "Encodings" -#~ msgstr "编码" - -#~ msgid "(Raw)" -#~ msgstr "(原始格式)" - -#~ msgid "Specified nickname is already registered" -#~ msgstr "指定的昵称已被注册" - -#~ msgid "Size" -#~ msgstr "大小" diff --git a/rebar b/rebar index 8553bb177..3f6203bcf 100755 Binary files a/rebar and b/rebar differ diff --git a/rebar.config b/rebar.config index 80eb25a93..3d85402e3 100644 --- a/rebar.config +++ b/rebar.config @@ -1,142 +1,317 @@ -%------------------------------------------------------------------- -%%% @author Evgeniy Khramtsov -%%% @copyright (C) 2013-2016, Evgeniy Khramtsov -%%% @doc +%%%---------------------------------------------------------------------- %%% -%%% @end -%%% Created : 1 May 2013 by Evgeniy Khramtsov -%%%------------------------------------------------------------------- +%%% ejabberd, Copyright (C) 2002-2025 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. +%%% +%%%---------------------------------------------------------------------- -{deps, [{lager, ".*", {git, "https://github.com/basho/lager", {tag, "3.2.1"}}}, - {p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.6"}}}, - {cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.5"}}}, - {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.8"}}}, - {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.7"}}}, - {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.18"}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.1.2"}}}, - {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.8"}}}, - {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.9"}}}, - {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.7"}}}, - {jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}}, - {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.1"}}}, - {luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "v0.2"}}}, - {if_var_true, mysql, {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", - {tag, "1.0.2"}}}}, - {if_var_true, pgsql, {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql", - {tag, "1.1.1"}}}}, - {if_var_true, sqlite, {sqlite3, ".*", {git, "https://github.com/processone/erlang-sqlite3", - {tag, "1.1.5"}}}}, - {if_var_true, pam, {p1_pam, ".*", {git, "https://github.com/processone/epam", - {tag, "1.0.0"}}}}, - {if_var_true, zlib, {ezlib, ".*", {git, "https://github.com/processone/ezlib", - {tag, "1.0.1"}}}}, - {if_var_true, riak, {riakc, ".*", {git, "https://github.com/basho/riak-erlang-client", - {tag, "2.4.1"}}}}, - %% Elixir support, needed to run tests - {if_var_true, elixir, {elixir, ".*", {git, "https://github.com/elixir-lang/elixir", - {tag, {if_version_above, "17", "v1.2.6", "v1.1.1"}}}}}, - %% TODO: When modules are fully migrated to new structure and mix, we will not need anymore rebar_elixir_plugin - {if_var_true, elixir, {rebar_elixir_plugin, ".*", - {git, "https://github.com/processone/rebar_elixir_plugin", "0.1.0"}}}, - {if_var_true, iconv, {iconv, ".*", {git, "https://github.com/processone/iconv", - {tag, "1.0.3"}}}}, - {if_var_true, tools, {meck, "0.8.*", {git, "https://github.com/eproxus/meck", - {tag, "0.8.4"}}}}, - {if_var_true, tools, {moka, ".*", {git, "https://github.com/processone/moka.git", - {tag, "1.0.5c"}}}}, - {if_var_true, redis, {eredis, ".*", {git, "https://github.com/wooga/eredis", - {tag, "v1.0.8"}}}}]}. +%%% +%%% Dependencies +%%% + +{deps, [{if_not_rebar3, + {if_version_below, "24", + {base64url, "~> 1.0", {git, "https://github.com/dvv/base64url", {tag, "1.0.1"}}} + }}, + {cache_tab, "~> 1.0.33", {git, "https://github.com/processone/cache_tab", {tag, "1.0.33"}}}, + {eimp, "~> 1.0.26", {git, "https://github.com/processone/eimp", {tag, "1.0.26"}}}, + {if_var_true, pam, + {epam, "~> 1.0.14", {git, "https://github.com/processone/epam", {tag, "1.0.14"}}}}, + {if_var_true, redis, + {if_not_rebar3, + {eredis, "~> 1.2.0", {git, "https://github.com/wooga/eredis/", {tag, "v1.2.0"}}} + }}, + {if_var_true, redis, + {if_rebar3, + {if_version_below, "21", + {eredis, "1.2.0", {git, "https://github.com/wooga/eredis/", {tag, "v1.2.0"}}}, + {eredis, "~> 1.7.1", {git, "https://github.com/Nordix/eredis/", {tag, "v1.7.1"}}} + }}}, + {if_var_true, sip, + {esip, "~> 1.0.59", {git, "https://github.com/processone/esip", {tag, "1.0.59"}}}}, + {if_var_true, zlib, + {ezlib, "~> 1.0.15", {git, "https://github.com/processone/ezlib", {tag, "1.0.15"}}}}, + {fast_tls, "~> 1.1.25", {git, "https://github.com/processone/fast_tls", {tag, "1.1.25"}}}, + {fast_xml, "~> 1.1.57", {git, "https://github.com/processone/fast_xml", {tag, "1.1.57"}}}, + {fast_yaml, "~> 1.0.39", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.39"}}}, + {idna, "~> 6.0", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, + {if_version_below, "27", + {jiffy, "~> 1.1.1", {git, "https://github.com/davisp/jiffy", {tag, "1.1.1"}}} + }, + {if_version_above, "23", + {jose, "~> 1.11.10", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.11.10"}}}, + {jose, "1.11.1", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.11.1"}}} + }, + {if_version_below, "22", + {lager, "~> 3.9.1", {git, "https://github.com/erlang-lager/lager", {tag, "3.9.1"}}} + }, + {if_var_true, lua, + {if_version_below, "21", + {luerl, "1.0.0", {git, "https://github.com/rvirding/luerl", {tag, "1.0"}}}, + {luerl, "~> 1.2.0", {git, "https://github.com/rvirding/luerl", {tag, "1.2"}}} + }}, + {mqtree, "~> 1.0.19", {git, "https://github.com/processone/mqtree", {tag, "1.0.19"}}}, + {p1_acme, "~> 1.0.28", {git, "https://github.com/processone/p1_acme", {tag, "1.0.28"}}}, + {if_var_true, mysql, + {p1_mysql, "~> 1.0.26", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.26"}}}}, + {p1_oauth2, "~> 0.6.14", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, + {if_var_true, pgsql, + {p1_pgsql, "~> 1.1.35", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.35"}}}}, + {p1_utils, "~> 1.0.28", {git, "https://github.com/processone/p1_utils", {tag, "1.0.28"}}}, + {pkix, "~> 1.0.10", {git, "https://github.com/processone/pkix", {tag, "1.0.10"}}}, + {if_var_true, sqlite, + {sqlite3, "~> 1.1.15", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.15"}}}}, + {stringprep, "~> 1.0.33", {git, "https://github.com/processone/stringprep", {tag, "1.0.33"}}}, + {if_var_true, stun, + {stun, "~> 1.2.21", {git, "https://github.com/processone/stun", {tag, "1.2.21"}}}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "e9d901ea84fd3910ad32b715853397eb1155b41c"}}, + {yconf, ".*", {git, "https://github.com/processone/yconf", "95692795a8a8d950ba560e5b07e6b80660557259"}} + ]}. + +{gitonly_deps, [ejabberd_po]}. {if_var_true, latest_deps, {floating_deps, [cache_tab, - fast_tls, - stringprep, - fast_xml, + eimp, + epam, esip, - luerl, - stun, - fast_yaml, - xmpp, - p1_utils, - p1_mysql, - p1_pgsql, - p1_pam, ezlib, - iconv]}}. + fast_tls, + fast_xml, + fast_yaml, + mqtree, + p1_acme, + p1_mysql, + p1_oauth2, + p1_pgsql, + p1_utils, + pkix, + sqlite3, + stringprep, + stun, + xmpp, + yconf]}}. -{erl_first_files, ["src/ejabberd_config.erl", "src/gen_mod.erl", "src/mod_muc_room.erl"]}. +%%% +%%% Compile +%%% + +{recursive_cmds, ['configure-deps']}. + +{post_hook_configure, [{"eimp", []}, + {if_var_true, pam, {"epam", []}}, + {if_var_true, sip, {"esip", []}}, + {if_var_true, zlib, {"ezlib", []}}, + {"fast_tls", []}, + {"fast_xml", [{if_var_true, full_xml, "--enable-full-xml"}]}, + {"fast_yaml", []}, + {"stringprep", []}]}. + +{erl_first_files, ["src/ejabberd_sql_pt.erl", "src/ejabberd_config.erl", + "src/gen_mod.erl", "src/mod_muc_room.erl", + "src/mod_push.erl", "src/xmpp_socket.erl"]}. {erl_opts, [nowarn_deprecated_function, {i, "include"}, - {i, "deps/fast_xml/include"}, - {i, "deps/xmpp/include"}, + {if_version_above, "20", {d, 'HAVE_ERL_ERROR'}}, + {if_version_above, "20", {d, 'HAVE_URI_STRING'}}, + {if_version_below, "21", {d, 'USE_OLD_HTTP_URI'}}, + {if_version_below, "22", {d, 'LAGER'}}, + {if_version_below, "21", {d, 'NO_CUSTOMIZE_HOSTNAME_CHECK'}}, + {if_version_below, "23", {d, 'USE_OLD_CRYPTO_HMAC'}}, + {if_version_below, "23", {d, 'USE_OLD_PG2'}}, + {if_version_below, "24", {d, 'COMPILER_REPORTS_ONLY_LINES'}}, + {if_version_below, "24", {d, 'SYSTOOLS_APP_DEF_WITHOUT_OPTIONAL'}}, + {if_version_below, "24", {d, 'OTP_BELOW_24'}}, + {if_version_below, "25", {d, 'OTP_BELOW_25'}}, + {if_version_below, "26", {d, 'OTP_BELOW_26'}}, + {if_version_below, "27", {d, 'OTP_BELOW_27'}}, + {if_version_below, "28", {d, 'OTP_BELOW_28'}}, {if_var_false, debug, no_debug_info}, {if_var_true, debug, debug_info}, - {if_var_true, roster_gateway_workaround, {d, 'ROSTER_GATWAY_WORKAROUND'}}, - {if_var_match, db_type, mssql, {d, 'mssql'}}, {if_var_true, elixir, {d, 'ELIXIR_ENABLED'}}, - {if_var_true, erlang_deprecated_types, {d, 'ERL_DEPRECATED_TYPES'}}, - {if_version_above, "18", {d, 'STRONG_RAND_BYTES'}}, - {if_var_true, hipe, native}, - {src_dirs, [asn1, src, - {if_var_true, tools, tools}, - {if_var_true, elixir, include}]}]}. + {if_var_true, new_sql_schema, {d, 'NEW_SQL_SCHEMA'}}, + {if_var_true, roster_gateway_workaround, {d, 'ROSTER_GATEWAY_WORKAROUND'}}, + {if_var_true, sip, {d, 'SIP'}}, + {if_var_true, stun, {d, 'STUN'}}, + {src_dirs, [src, + {if_rebar3, sql}, + {if_var_true, tools, tools}]}]}. -{deps_erl_opts, [{if_var_true, hipe, native}]}. +{if_rebar3, {plugins, [{if_version_below, "21", {rebar3_hex, "7.0.7"}}, + {if_version_above, "20", {rebar3_hex, "~> 7.0.8"}}, + {provider_asn1, "0.4.1"}, + %% Protocol consolidation doesn't work correctly in upstream rebar_mix, see + %% https://github.com/Supersonido/rebar_mix/issues/27#issuecomment-894873335 + %% Let's use this fixed rebar_mix fork, see its PR: + %% https://github.com/Supersonido/rebar_mix/pull/31 + {if_var_true, elixir, {rebar_mix, ".*", + {git, "https://github.com/bsanyi/rebar_mix.git", + {branch, "consolidation_fix"}}} + }]}}. +{if_rebar3, {project_plugins, [configure_deps, + {if_var_true, tools, rebar3_format}, + {if_var_true, tools, {rebar3_lint, "4.1.1"}} + ]}}. +{if_not_rebar3, {plugins, [ + deps_erl_opts, override_deps_versions2, override_opts, configure_deps + ]}}. -{plugins, [deps_erl_opts, - {if_var_true, elixir, rebar_elixir_compiler}, - {if_var_true, elixir, rebar_exunit}]}. +{if_rebar3, {if_var_true, elixir, + {provider_hooks, [ + {post, [{compile, {mix, consolidate_protocols}}]} + ]}}}. -{if_var_true, elixir, - {lib_dirs, ["deps/elixir/lib"]}}. -{if_var_true, elixir, - {src_dirs, ["include"]}}. +%% Compiling Jose 1.11.10 with Erlang/OTP 27.0 throws warnings on public_key deprecated functions +{if_rebar3, {overrides, [{del, jose, [{erl_opts, [warnings_as_errors]}]}]}}. {sub_dirs, ["rel"]}. {keep_build_info, true}. +%%% +%%% Test +%%% + {xref_warnings, false}. -{xref_checks, [deprecated_function_calls]}. +{if_rebar3, + {xref_checks, + [deprecated_function_calls, deprecated_functions, locals_not_used, + undefined_function_calls, undefined_functions]} +}. +{if_not_rebar3, + {xref_checks, + [deprecated_function_calls, deprecated_functions, + undefined_function_calls, undefined_functions]} +}. {xref_exclusions, [ "(\"gen_transport\":_/_)", "(\"eprof\":_/_)", - {if_var_false, mysql, "(\".*mysql.*\":_/_)"}, - {if_var_false, pgsql, "(\".*pgsql.*\":_/_)"}, - {if_var_false, pam, "(\"epam\":_/_)"}, - {if_var_false, riak, "(\"riak.*\":_/_)"}, - {if_var_true, riak, "(\"riak_object\":_/_)"}, - {if_var_false, zlib, "(\"ezlib\":_/_)"}, - {if_var_false, http, "(\"lhttpc\":_/_)"}, - {if_var_false, iconv, "(\"iconv\":_/_)"}, - {if_var_false, odbc, "(\"odbc\":_/_)"}, - {if_var_false, sqlite, "(\"sqlite3\":_/_)"}, {if_var_false, elixir, "(\"Elixir.*\":_/_)"}, - {if_var_false, redis, "(\"eredis\":_/_)"}]}. + {if_var_false, http, "(\"lhttpc\":_/_)"}, + {if_var_false, mysql, "(\".*mysql.*\":_/_)"}, + {if_var_false, odbc, "(\"odbc\":_/_)"}, + {if_var_false, pam, "(\"epam\":_/_)"}, + {if_var_false, pgsql, "(\".*pgsql.*\":_/_)"}, + {if_var_false, redis, "(\"eredis\":_/_)"}, + {if_var_false, sqlite, "(\"sqlite3\":_/_)"}, + {if_var_false, zlib, "(\"ezlib\":_/_)"}]}. + +{xref_ignores, [{eldap_filter_yecc, return_error, 2}, + {http_uri, encode, 1}]}. {eunit_compile_opts, [{i, "tools"}, - {i, "include"}, - {i, "deps/fast_xml/include"}, - {i, "deps/xmpp/include"}]}. + {i, "include"}]}. -{if_version_above, "17", {cover_enabled, true}}. +{dialyzer, [{get_warnings, false}, % Show warnings of dependencies + {if_version_above, "25", + {plt_extra_apps, + [asn1, odbc, public_key, stdlib, syntax_tools, + idna, jose, + cache_tab, eimp, fast_tls, fast_xml, fast_yaml, + mqtree, p1_acme, p1_oauth2, p1_utils, pkix, + stringprep, xmpp, yconf, + {if_version_below, "27", jiffy}, + {if_var_true, pam, epam}, + {if_var_true, redis, eredis}, + {if_var_true, sip, esip}, + {if_var_true, zlib, ezlib}, + {if_var_true, lua, luerl}, + {if_var_true, mysql, p1_mysql}, + {if_var_true, pgsql, p1_pgsql}, + {if_var_true, stun, stun}, + {if_var_true, sqlite, sqlite3}]}, + {plt_extra_apps, % For Erlang/OTP 25 and older + [cache_tab, eimp, fast_tls, fast_xml, fast_yaml, + mqtree, p1_acme, p1_oauth2, p1_utils, pkix, stringprep, xmpp, yconf, + {if_var_true, pam, epam}, + {if_var_true, redis, eredis}, + {if_var_true, sip, esip}, + {if_var_true, zlib, ezlib}, + {if_var_true, lua, luerl}, + {if_var_true, mysql, p1_mysql}, + {if_var_true, pgsql, p1_pgsql}, + {if_var_true, stun, stun}, + {if_var_true, sqlite, sqlite3}]} + } ]}. + +{ct_opts, [{keep_logs, 20}]}. + +{cover_enabled, true}. {cover_export_enabled, true}. +{cover_excl_mods, [eldap_filter_yecc]}. +{coveralls_coverdata, "_build/test/cover/ct.coverdata"}. +{coveralls_service_name, "github"}. -{post_hook_configure, [{"fast_tls", []}, - {"stringprep", []}, - {"fast_yaml", []}, - {"esip", []}, - {"fast_xml", [{if_var_true, full_xml, "--enable-full-xml"}]}, - {if_var_true, pam, {"p1_pam", []}}, - {if_var_true, zlib, {"ezlib", []}}, - {if_var_true, iconv, {"iconv", []}}]}. +%%% +%%% OTP Release +%%% -{port_env, [{"CFLAGS", "-g -O2 -Wall"}]}. +{relx, [{release, {ejabberd, {cmd, "grep {vsn, vars.config | sed 's|{vsn, \"||;s|\"}.||' | tr -d '\012'"}}, + [ejabberd]}, + {sys_config, "./rel/sys.config"}, + {vm_args, "./rel/vm.args"}, + {overlay_vars, "vars.config"}, + {overlay, [{mkdir, "logs"}, + {mkdir, "database"}, + {mkdir, "conf"}, + {copy, "rel/files/erl", "erts-\{\{erts_vsn\}\}/bin/erl"}, + {template, "ejabberdctl.template", "bin/ejabberdctl"}, + {copy, "_build/default/lib/ejabberd/ebin/Elixir.*", "lib/ejabberd-{{release_version}}/ebin/"}, + {copy, "{{base_dir}}/consolidated/*", "lib/ejabberd-{{release_version}}/ebin/"}, + {copy, "rel/overlays/iex", "releases/{{release_version}}/"}, + {if_var_true, elixir, + {template, "rel/overlays/elixir", "releases/{{release_version}}/elixir"} + }, + {copy, "inetrc", "conf/inetrc"}, + {copy, "tools/captcha*.sh", "lib/ejabberd-\{\{release_version\}\}/priv/bin/"}, + {copy, "rel/files/install_upgrade.escript", "bin/install_upgrade.escript"}]} + ]}. -{port_specs, [{"priv/lib/jid.so", ["c_src/jid.c"]}]}. +{profiles, [{prod, [{relx, [{debug_info, strip}, + {dev_mode, false}, + {include_erts, true}, + {include_src, true}, + {generate_start_script, false}, + {overlay, [{copy, "sql/*", "lib/ejabberd-\{\{release_version\}\}/priv/sql/"}, + {copy, "ejabberdctl.cfg.example", "conf/ejabberdctl.cfg"}, + {copy, "ejabberd.yml.example", "conf/ejabberd.yml"}]}]}]}, + {dev, [{post_hooks, [{release, "rel/setup-dev.sh rebar3"}]}, + {deps, [{if_version_above, "20", sync}]}, + {relx, [{debug_info, keep}, + {dev_mode, true}, + {include_erts, true}, + {include_src, false}, + {generate_start_script, true}, + {extended_start_script, true}, + {overlay, [{copy, "ejabberdctl.cfg.example", "conf/ejabberdctl.cfg.example"}, + {copy, "ejabberd.yml.example", "conf/ejabberd.yml.example"}, + {copy, "test/ejabberd_SUITE_data/ca.pem", "conf/"}, + {copy, "test/ejabberd_SUITE_data/cert.pem", "conf/"}]}]}]}, + {translations, [{deps, [{ejabberd_po, ".*", {git, "https://github.com/processone/ejabberd-po", {branch, "main"}}}]}]}, + {test, [{erl_opts, [nowarn_export_all]}]}]}. + +{alias, [{relive, [{shell, "--apps ejabberd \ + --config rel/relive.config \ + --eval sync:go(). \ + --script rel/relive.escript \ + --name ejabberd@localhost"}]} +]}. %% Local Variables: %% mode: erlang diff --git a/rebar.config.script b/rebar.config.script index 34e0c328e..e476df448 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -1,53 +1,94 @@ -%%%------------------------------------------------------------------- -%%% @author Evgeniy Khramtsov -%%% @copyright (C) 2013-2016, Evgeniy Khramtsov -%%% @doc +%%%---------------------------------------------------------------------- %%% -%%% @end -%%% Created : 1 May 2013 by Evgeniy Khramtsov -%%%------------------------------------------------------------------- +%%% ejabberd, Copyright (C) 2002-2025 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. +%%% +%%%---------------------------------------------------------------------- +Vars = case file:consult(filename:join([filename:dirname(SCRIPT),"vars.config"])) of + {ok, Terms} -> + Terms; + _Err -> + [] + end ++ [{cflags, "-g -O2 -Wall"}, {cppflags, "-g -O2 -Wall"}, + {ldflags, ""}, {system_deps, false}], +{cflags, CFlags} = lists:keyfind(cflags, 1, Vars), +{cppflags, CPPFlags} = lists:keyfind(cppflags, 1, Vars), +{ldflags, LDFlags} = lists:keyfind(ldflags, 1, Vars), +{system_deps, SystemDeps} = lists:keyfind(system_deps, 1, Vars), -ModCfg0 = fun(F, Cfg, [Key|Tail], Op, Default) -> - {OldVal,PartCfg} = case lists:keytake(Key, 1, Cfg) of - {value, {_, V1}, V2} -> {V1, V2}; - false -> {if Tail == [] -> Default; true -> [] end, Cfg} - end, - case Tail of - [] -> - [{Key, Op(OldVal)} | PartCfg]; - _ -> - [{Key, F(F, OldVal, Tail, Op, Default)} | PartCfg] - end - end, -ModCfg = fun(Cfg, Keys, Op, Default) -> ModCfg0(ModCfg0, Cfg, Keys, Op, Default) end, +GetCfg = fun GetCfg(Cfg, [Key | Tail], Default) -> + Val = case lists:keyfind(Key, 1, Cfg) of + {Key, V1} -> V1; + false -> Default + end, + case Tail of + [] -> + Val; + _ -> + GetCfg(Val, Tail, Default) + end + end, +ModCfg = fun ModCfg(Cfg, [Key | Tail], Op, Default) -> + {OldVal, PartCfg} = case lists:keytake(Key, 1, Cfg) of + {value, {_, V1}, V2} -> {V1, V2}; + false -> {if Tail == [] -> Default; true -> [] end, Cfg} + end, + case Tail of + [] -> + [{Key, Op(OldVal)} | PartCfg]; + _ -> + [{Key, ModCfg(OldVal, Tail, Op, Default)} | PartCfg] + end + end, + +FilterConfig = fun FilterConfig(Cfg, [{Path, true, ModFun, Default} | Tail]) -> + FilterConfig(ModCfg(Cfg, Path, ModFun, Default), Tail); + FilterConfig(Cfg, [{Path, SourcePath, true, ModFun, Default, SourceDefault} | Tail]) -> + SourceVal = GetCfg(Cfg, SourcePath, SourceDefault), + ModFun2 = fun(V) -> ModFun(V, SourceVal) end, + FilterConfig(ModCfg(Cfg, Path, ModFun2, Default), Tail); + FilterConfig(Cfg, [_ | Tail]) -> + FilterConfig(Cfg, Tail); + FilterConfig(Cfg, []) -> + Cfg + end, IsRebar3 = case application:get_key(rebar, vsn) of {ok, VSN} -> [VSN1 | _] = string:tokens(VSN, "-"), - [Maj, Min, Patch] = string:tokens(VSN1, "."), + [Maj|_] = string:tokens(VSN1, "."), (list_to_integer(Maj) >= 3); undefined -> lists:keymember(mix, 1, application:loaded_applications()) end, -Cfg = case file:consult(filename:join(filename:dirname(SCRIPT), "vars.config")) of - {ok, Terms} -> - Terms; - _Err -> - [] - end, + +SysVer = erlang:system_info(otp_release), ProcessSingleVar = fun(F, Var, Tail) -> - case F(F, [Var], []) of + case F([Var], []) of [] -> Tail; [Val] -> [Val | Tail] end end, -ProcessVars = fun(_F, [], Acc) -> - lists:reverse(Acc); - (F, [{Type, Ver, Value} | Tail], Acc) when - Type == if_version_above orelse - Type == if_version_below -> +ProcessVars = fun F([], Acc) -> + lists:reverse(Acc); + F([{Type, Ver, Value} | Tail], Acc) when + Type == if_version_above orelse + Type == if_version_below -> SysVer = erlang:system_info(otp_release), Include = if Type == if_version_above -> SysVer > Ver; @@ -55,181 +96,365 @@ ProcessVars = fun(_F, [], Acc) -> SysVer < Ver end, if Include -> - F(F, Tail, ProcessSingleVar(F, Value, Acc)); + F(Tail, ProcessSingleVar(F, Value, Acc)); true -> - F(F, Tail, Acc) + F(Tail, Acc) end; - (F, [{Type, Ver, Value, ElseValue} | Tail], Acc) when - Type == if_version_above orelse - Type == if_version_below -> - SysVer = erlang:system_info(otp_release), + F([{Type, Ver, Value, ElseValue} | Tail], Acc) when + Type == if_version_above orelse + Type == if_version_below -> Include = if Type == if_version_above -> SysVer > Ver; true -> SysVer < Ver end, if Include -> - F(F, Tail, ProcessSingleVar(F, Value, Acc)); + F(Tail, ProcessSingleVar(F, Value, Acc)); true -> - F(F, Tail, ProcessSingleVar(F, ElseValue, Acc)) + F(Tail, ProcessSingleVar(F, ElseValue, Acc)) end; - (F, [{Type, Var, Value} | Tail], Acc) when - Type == if_var_true orelse - Type == if_var_false -> - Flag = Type == if_var_true, - case proplists:get_bool(Var, Cfg) of - V when V == Flag -> - F(F, Tail, ProcessSingleVar(F, Value, Acc)); - _ -> - F(F, Tail, Acc) - end; - (F, [{Type, Var, Match, Value} | Tail], Acc) when - Type == if_var_match orelse - Type == if_var_no_match -> - case proplists:get_value(Var, Cfg) of - V when V == Match -> - F(F, Tail, ProcessSingleVar(F, Value, Acc)); - _ -> - F(F, Tail, Acc) - end; - (F, [Other1 | Tail1], Acc) -> - F(F, Tail1, [F(F, Other1, []) | Acc]); - (F, Val, Acc) when is_tuple(Val) -> - list_to_tuple(F(F, tuple_to_list(Val), Acc)); - (_F, Other2, _Acc) -> - Other2 - end, + F([{Type, Var, Value} | Tail], Acc) when + Type == if_var_true orelse + Type == if_var_false -> + Flag = Type == if_var_true, + case proplists:get_bool(Var, Vars) of + V when V == Flag -> + F(Tail, ProcessSingleVar(F, Value, Acc)); + _ -> + F(Tail, Acc) + end; + F([{Type, Value} | Tail], Acc) when + Type == if_rebar3 orelse + Type == if_not_rebar3 -> + Flag = Type == if_rebar3, + case IsRebar3 == Flag of + true -> + F(Tail, ProcessSingleVar(F, Value, Acc)); + _ -> + F(Tail, Acc) + end; + F([{Type, Var, Match, Value} | Tail], Acc) when + Type == if_var_match orelse + Type == if_var_no_match -> + case proplists:get_value(Var, Vars) of + V when V == Match -> + F(Tail, ProcessSingleVar(F, Value, Acc)); + _ -> + F(Tail, Acc) + end; + F([{if_have_fun, MFA, Value} | Tail], Acc) -> + {Mod, Fun, Arity} = MFA, + code:ensure_loaded(Mod), + case erlang:function_exported(Mod, Fun, Arity) of + true -> + F(Tail, ProcessSingleVar(F, Value, Acc)); + false -> + F(Tail, Acc) + end; + F([{Type, {Mod, TypeDef}, Value} | Tail], Acc) when + Type == if_type_exported orelse + Type == if_type_not_exported -> + try + {ok, Concrete} = dialyzer_utils:get_core_from_beam(code:which(Mod)), + {ok, Types} = dialyzer_utils:get_record_and_type_info(Concrete), + maps:get(TypeDef, Types, undefined) + of + undefined when Type == if_type_not_exported -> + F(Tail, ProcessSingleVar(F, Value, Acc)); + undefined -> + F(Tail, Acc); + _ when Type == if_type_exported -> + F(Tail, ProcessSingleVar(F, Value, Acc)); + _ -> + F(Tail, Acc) + catch _:_ -> + if + Type == if_type_not_exported -> + F(Tail, ProcessSingleVar(F, Value, Acc)); + true -> + F(Tail, Acc) + end + end; + F([Other1 | Tail1], Acc) -> + F(Tail1, [F(Other1, []) | Acc]); + F(Val, Acc) when is_tuple(Val) -> + list_to_tuple(F(tuple_to_list(Val), Acc)); + F(Other2, _Acc) -> + Other2 + end, -CFLags = proplists:get_value(cflags, Cfg, ""), -CPPFLags = proplists:get_value(cppflags, Cfg, ""), -LDFLags = proplists:get_value(ldflags, Cfg, ""), +MaybeApply = fun(Val) when is_function(Val) -> + Val(); + (Val) -> + Val + end, +MaybeApply2 = fun(Val, Arg) when is_function(Val) -> + Val(Arg); + (Val, _) -> + Val + end, -ConfigureCmd = fun(Pkg, Flags) -> - {'get-deps', - "sh -c 'cd deps/" ++ Pkg ++ - " && CFLAGS=\""++ CFLags ++"\" CPPFLAGS=\""++ CPPFLags ++"\" LDFLAGS=\""++ LDFLags ++"\"" ++ - " ./configure " ++ Flags ++ "'"} - end, +AppendStr = fun(Append) -> + fun("") -> + lists:flatten(MaybeApply(Append)); + (Val) -> + lists:flatten([Val, " ", MaybeApply(Append)]) + end + end, +AppendList = fun(Append) -> + fun(Val) -> + Val ++ MaybeApply(Append) + end + end, +AppendStr2 = fun(Append) -> + fun("", Arg) -> + lists:flatten(MaybeApply2(Append, Arg)); + (Val, Arg) -> + lists:flatten([Val, " ", MaybeApply2(Append, Arg)]) + end + end, +AppendList2 = fun(Append) -> + fun(Val, Arg) -> + Val ++ MaybeApply2(Append, Arg) + end + end, -Conf = ProcessVars(ProcessVars, CONFIG, []), +% Convert our rich deps syntax to rebar2 format: +% https://github.com/rebar/rebar/wiki/Dependency-management +Rebar2DepsFilter = +fun(DepsList, GitOnlyDeps) -> + lists:map(fun({DepName, _HexVersion, Source}) -> + {DepName, ".*", Source} + end, DepsList) +end, -Conf1 = case lists:keytake(post_hook_configure, 1, Conf) of - {value, {_, Items}, Rest} -> - [{post_hooks, [ConfigureCmd(Mod, string:join(Opts, " ")) || {Mod, Opts} <- Items]} | Rest]; - _ -> - Conf - end, +% Convert our rich deps syntax to rebar3 version definition format: +% https://rebar3.org/docs/configuration/dependencies/#dependency-version-handling +% https://hexdocs.pm/elixir/Version.html +Rebar3DepsFilter = +fun(DepsList, GitOnlyDeps) -> + lists:map(fun({DepName, HexVersion, {git, _, {tag, GitVersion}} = Source}) -> + case {lists:member(DepName, GitOnlyDeps), HexVersion == ".*"} of + {true, _} -> + {DepName, ".*", Source}; + {false, true} -> + {DepName, GitVersion}; + {false, false} -> + {DepName, HexVersion} + end; + ({DepName, _HexVersion, Source}) -> + {DepName, ".*", Source} + end, DepsList) +end, + + +DepAlts = fun("esip") -> ["esip", "p1_sip"]; + ("xmpp") -> ["xmpp", "p1_xmpp"]; + ("fast_xml") -> ["fast_xml", "p1_xml"]; + (Val) -> [Val] + end, + +LibDirInt = fun F([Dep|Rest], Suffix) -> + case code:lib_dir(Dep) of + {error, _} -> + F(Rest, Suffix); + V -> V ++ Suffix + end; + F([], _) -> + error + end, + +LibDir = fun(Name, Suffix) -> + LibDirInt(DepAlts(Name), Suffix) + end, + +GlobalDepsFilter = +fun(Deps) -> + DepNames = lists:map(fun({DepName, _, _}) -> DepName; + ({DepName, _}) -> DepName; + (DepName) -> DepName + end, Deps), + lists:filtermap(fun(Dep) -> + case LibDir(atom_to_list(Dep), "") of + error -> + exit("Unable to locate dep '" ++ atom_to_list(Dep) ++ "' in system deps."); + _ -> + false + end + end, DepNames) +end, {ok, Cwd} = file:get_cwd(), TestConfigFile = filename:join([Cwd, "test", "config.ctc"]), TestConfig = case file:read_file_info(TestConfigFile) of - {ok, _} -> - "-userconfig ct_config_plain " ++ TestConfigFile ++ " "; - _ -> - "" + {ok, _} -> + [" -userconfig ct_config_plain ", TestConfigFile, " "]; + _ -> + "" end, -ResolveDepPath = case IsRebar3 of - true -> +ResolveDepPath = case {SystemDeps, IsRebar3} of + {true, _} -> fun("deps/" ++ Rest) -> Slash = string:str(Rest, "/"), - Dir = "_build/default/lib/" ++ - string:sub_string(Rest, 1, Slash-1), - Dir ++ string:sub_string(Rest, Slash); + case LibDir(string:sub_string(Rest, 1, Slash -1), string:sub_string(Rest, Slash)) of + error -> Rest; + V -> V + end; + (Path) -> + Path + end; + {_, true} -> + fun("deps/" ++ Rest) -> + Slash = string:str(Rest, "/"), + "_build/default/lib/" ++ + string:sub_string(Rest, 1, Slash - 1) ++ + string:sub_string(Rest, Slash); (Path) -> Path end; _ -> fun(P) -> - P + P end end, -CtIncludes = case lists:keyfind(eunit_compile_opts, 1, Conf1) of - false -> - []; - {_, EunitCompOpts} -> - [[" -include ", filename:join([Cwd, ResolveDepPath(IncPath)])] - || {i, IncPath} <- EunitCompOpts] - end, +CtParams = fun(CompileOpts) -> + ["-ct_hooks cth_surefire ", + lists:map(fun({i, IncPath}) -> + [" -include ", filename:absname(ResolveDepPath(IncPath), Cwd)] + end, CompileOpts), + TestConfig] + end, -ProcessErlOpt = fun({i, Path}) -> - {i, ResolveDepPath(Path)}; - (ErlOpt) -> - ErlOpt - end, +GenDepConfigureLine = +fun(DepPath, Flags) -> + ["sh -c 'if test ! -f config.status -o ", + "../../config.status -nt config.status; ", + "then (", + "CFLAGS=\"", CFlags,"\" ", + "CPPFLAGS=\"", CPPFlags, "\" " + "LDFLAGS=\"", LDFlags, "\"", + " ./configure ", string:join(Flags, " "), + "); fi'"] +end, -Conf1a = ModCfg(Conf1, [erl_opts], - fun(ErlOpts) -> lists:map(ProcessErlOpt, ErlOpts) end, []), +GenDepsConfigure = +fun(Hooks) -> + lists:map(fun({Pkg, Flags}) -> + DepPath = ResolveDepPath("deps/" ++ Pkg ++ "/"), + Line = lists:flatten(GenDepConfigureLine(DepPath, Flags)), + {add, list_to_atom(Pkg), [{pre_hooks, [{{pc, compile}, Line}, {'compile', Line}, {'configure-deps', Line}]}]} + end, Hooks) +end, -Conf2a = [{ct_extra_params, lists:flatten(["-ct_hooks cth_surefire ", TestConfig, - CtIncludes])} | Conf1a], +ProcessErlOpt = fun(Vals) -> + R = lists:map( + fun({i, Path}) -> + {i, ResolveDepPath(Path)}; + (ErlOpt) -> + ErlOpt + end, Vals), + M = lists:filter(fun({d, M}) -> true; (_) -> false end, R), + [{d, 'ALL_DEFS', M} | R] + end, -Conf2 = case IsRebar3 of - true -> - DepsFun = fun(DepsList) -> - lists:filtermap(fun({rebar_elixir_plugin, _, _}) -> - false; - ({DepName,_, {git,_, _} = Git}) -> - {true, {DepName, Git}}; - (Dep) -> - true - end, DepsList) - end, - RB1 = ModCfg(Conf2a, [deps], DepsFun, []), - ModCfg(RB1, [plugins], fun(V) -> V -- [deps_erl_opts, - rebar_elixir_compiler, - rebar_exunit] ++ - [rebar3_hex] end, []); - false -> - Conf2a - end, +ProcssXrefExclusions = fun(Items) -> + [{lists:flatten(["(XC - UC) || (XU - X - B ", + [[" - ", V] || V <- Items], ")"]), + []}] + end, + +ProcessFloatingDeps = +fun(Deps, FDeps) -> + lists:map(fun({DepName, _Ver, {git, Repo, _Commit}} = Dep) -> + case lists:member(DepName, FDeps) of + true -> + {DepName, ".*", {git, Repo}}; + _ -> + Dep + end; + (Dep2) -> + Dep2 + end, Deps) +end, -Conf3 = case lists:keytake(xref_exclusions, 1, Conf2) of - {value, {_, Items2}, Rest2} -> - [{xref_queries, [{lists:flatten(["(XC - UC) || (XU - X - B ", - [[" - ", V] || V <- Items2], ")"]), []}]} | Rest2]; - _ -> - Conf2 - end, +VarsApps = case file:consult(filename:join([filename:dirname(SCRIPT),"vars.config"])) of + {ok, TermsV} -> + case proplists:get_bool(odbc, TermsV) of + true -> [odbc]; + false -> [] + end; + _-> + [] + end, -Conf5 = case lists:keytake(floating_deps, 1, Conf3) of - {value, {_, FloatingDeps}, Rest4} -> - case lists:keytake(deps, 1, Rest4) of - {value, {_, Deps}, Rest41} -> - ND = lists:map(fun({DepName, Ver, {git, Repo, _Commit}}=Dep) -> - case lists:member(DepName, FloatingDeps) of - true -> - {DepName, ".*", {git, Repo}}; - _ -> - Dep - end; - (Dep2) -> - Dep2 - end, Deps), - [{deps, ND} | Rest41]; - _ -> - Rest4 - end; - _ -> - Conf3 - end, +ProcessRelx = fun(Relx, Deps) -> + {value, {release, NameVersion, DefaultApps}, RelxTail} = lists:keytake(release, 1, Relx), + DepApps = lists:map(fun({DepName, _, _}) -> DepName; + ({DepName, _}) -> DepName; + (DepName) -> DepName + end, Deps), + [{release, NameVersion, DefaultApps ++ VarsApps ++ DepApps} | RelxTail] + end, -%% When running Travis test, upload test coverage result to coveralls: -Conf6 = case {lists:keyfind(cover_enabled, 1, Conf5), os:getenv("TRAVIS")} of - {{cover_enabled, true}, "true"} -> - JobId = os:getenv("TRAVIS_JOB_ID"), - CfgTemp = ModCfg(Conf5, [deps], fun(V) -> [{coveralls, ".*", {git, "https://github.com/markusn/coveralls-erl.git", "master"}}|V] end, []), - ModCfg(CfgTemp, [post_hooks], fun(V) -> V ++ [{ct, "echo '\n%%! -pa ebin/ deps/coveralls/ebin\nmain(_)->{ok,F}=file:open(\"erlang.json\",[write]),io:fwrite(F,\"~s\",[coveralls:convert_file(\"logs/all.coverdata\", \""++JobId++"\", \"travis-ci\")]).' > getcover.erl"}, - {ct, "escript ./getcover.erl"}] end, []); - _ -> - Conf5 - end, +GithubConfig = case {os:getenv("GITHUB_ACTIONS"), os:getenv("GITHUB_TOKEN")} of + {"true", Token} when is_list(Token) -> + CONFIG1 = [{coveralls_repo_token, Token}, + {coveralls_service_job_id, os:getenv("GITHUB_RUN_ID")}, + {coveralls_commit_sha, os:getenv("GITHUB_SHA")}, + {coveralls_service_number, os:getenv("GITHUB_RUN_NUMBER")}], + case os:getenv("GITHUB_EVENT_NAME") =:= "pull_request" + andalso string:tokens(os:getenv("GITHUB_REF"), "/") of + [_, "pull", PRNO, _] -> + [{coveralls_service_pull_request, PRNO} | CONFIG1]; + _ -> + CONFIG1 + end; + _ -> + [] +end, -%io:format("ejabberd configuration:~n ~p~n", [Conf6]), +Rules = [ + {[plugins], IsRebar3, + AppendList([{pc, "~> 1.15.0"}]), []}, + {[provider_hooks], IsRebar3, + AppendList([{pre, [ + {compile, {asn, compile}}, + {clean, {asn, clean}} + ]}]), []}, + {[plugins], IsRebar3 and (os:getenv("GITHUB_ACTIONS") == "true"), + AppendList([{coveralls, {git, + "https://github.com/processone/coveralls-erl.git", + {branch, "addjsonfile"}}} ]), []}, + {[overrides], [post_hook_configure], SystemDeps == false, + AppendList2(GenDepsConfigure), [], []}, + {[ct_extra_params], [eunit_compile_opts], true, + AppendStr2(CtParams), "", []}, + {[erl_opts], true, + ProcessErlOpt, []}, + {[xref_queries], [xref_exclusions], true, + AppendList2(ProcssXrefExclusions), [], []}, + {[relx], [deps], IsRebar3, + ProcessRelx, [], []}, + {[deps], [floating_deps], true, + ProcessFloatingDeps, [], []}, + {[deps], [gitonly_deps], (not IsRebar3), + Rebar2DepsFilter, [], []}, + {[deps], [gitonly_deps], IsRebar3, + Rebar3DepsFilter, [], []}, + {[deps], SystemDeps /= false, + GlobalDepsFilter, []} + ], -Conf6. +Config = [{plugin_dir, filename:join([filename:dirname(SCRIPT),"plugins"])}]++ +FilterConfig(ProcessVars(CONFIG, []), Rules)++ +GithubConfig, + +%io:format("ejabberd configuration:~n ~p~n", [Config]), + +Config. %% Local Variables: %% mode: erlang diff --git a/rebar.lock b/rebar.lock new file mode 100644 index 000000000..d69fcec1e --- /dev/null +++ b/rebar.lock @@ -0,0 +1,88 @@ +{"1.2.0", +[{<<"base64url">>,{pkg,<<"base64url">>,<<"1.0.1">>},1}, + {<<"cache_tab">>,{pkg,<<"cache_tab">>,<<"1.0.33">>},0}, + {<<"eimp">>,{pkg,<<"eimp">>,<<"1.0.26">>},0}, + {<<"epam">>,{pkg,<<"epam">>,<<"1.0.14">>},0}, + {<<"eredis">>,{pkg,<<"eredis">>,<<"1.7.1">>},0}, + {<<"esip">>,{pkg,<<"esip">>,<<"1.0.59">>},0}, + {<<"ezlib">>,{pkg,<<"ezlib">>,<<"1.0.15">>},0}, + {<<"fast_tls">>,{pkg,<<"fast_tls">>,<<"1.1.25">>},0}, + {<<"fast_xml">>,{pkg,<<"fast_xml">>,<<"1.1.57">>},0}, + {<<"fast_yaml">>,{pkg,<<"fast_yaml">>,<<"1.0.39">>},0}, + {<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},0}, + {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},1}, + {<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0}, + {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.3">>},0}, + {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.19">>},0}, + {<<"p1_acme">>,{pkg,<<"p1_acme">>,<<"1.0.28">>},0}, + {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.26">>},0}, + {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, + {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.35">>},0}, + {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.28">>},0}, + {<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.10">>},0}, + {<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.15">>},0}, + {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.33">>},0}, + {<<"stun">>,{pkg,<<"stun">>,<<"1.2.21">>},0}, + {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.1">>},1}, + {<<"xmpp">>, + {git,"https://github.com/processone/xmpp", + {ref,"e9d901ea84fd3910ad32b715853397eb1155b41c"}}, + 0}, + {<<"yconf">>, + {git,"https://github.com/processone/yconf", + {ref,"95692795a8a8d950ba560e5b07e6b80660557259"}}, + 0}]}. +[ +{pkg_hash,[ + {<<"base64url">>, <<"F8C7F2DA04CA9A5D0F5F50258F055E1D699F0E8BF4CFDB30B750865368403CF6">>}, + {<<"cache_tab">>, <<"E2542AFB34F17EE3CA19D2B0F546A074922C2B99FB6B2ACFB38160D7D0336EC3">>}, + {<<"eimp">>, <<"C0B05F32E35629C4D9BCFB832FF879A92B0F92B19844BC7835E0A45635F2899A">>}, + {<<"epam">>, <<"AA0B85D27F4EF3A756AE995179DF952A0721237E83C6B79D644347B75016681A">>}, + {<<"eredis">>, <<"39E31AA02ADCD651C657F39AAFD4D31A9B2F63C6C700DC9CECE98D4BC3C897AB">>}, + {<<"esip">>, <<"EB202F8C62928193588091DFEDBC545FE3274C34ECD209961F86DCB6C9EBCE88">>}, + {<<"ezlib">>, <<"D74F5DF191784744726A5B1AE9062522C606334F11086363385EB3B772D91357">>}, + {<<"fast_tls">>, <<"DA8ED6F05A2452121B087158B17234749F36704C1F2B74DC51DB99A1E27ED5E8">>}, + {<<"fast_xml">>, <<"31EFC0F9BCEDA92069704F7A25830407DA5DC3DAD1272B810D6F2E13E73CC11A">>}, + {<<"fast_yaml">>, <<"2E71168091949BAB0E5F583B340A99072B4D22D93EB86624E7850A12B1517BE4">>}, + {<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>}, + {<<"jiffy">>, <<"A9B6C9A7EC268E7CF493D028F0A4C9144F59CCB878B1AFE42841597800840A1B">>}, + {<<"jose">>, <<"A903F5227417BD2A08C8A00A0CBCC458118BE84480955E8D251297A425723F83">>}, + {<<"luerl">>, <<"DF25F41944E57A7C4D9EF09D238BC3E850276C46039CFC12B8BB42ECCF36FCB1">>}, + {<<"mqtree">>, <<"D769C25F898810725FC7DB0DBFFE5F72098647048B1BE2E6D772F1C2F31D8476">>}, + {<<"p1_acme">>, <<"64D9C17F5412AA92D75B29206B2B984D734A4FE1B7EACB66C3D7A7C697AC612C">>}, + {<<"p1_mysql">>, <<"574D07C9936C53B1EC3556DB3CF064CC14A6C39039835B3D940471BFA5AC8E2B">>}, + {<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>}, + {<<"p1_pgsql">>, <<"E13D89F14D717553E85C88A152CE77461916B013D88FCB851E354A0B332D4218">>}, + {<<"p1_utils">>, <<"9A7088A98D788B4C4880FD3C82D0C135650DB13F2E4EF7E10DB179791BC94D59">>}, + {<<"pkix">>, <<"D3BFADF7B7CFE2A3636F1B256C9CCE5F646A07CE31E57EE527668502850765A0">>}, + {<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>}, + {<<"stringprep">>, <<"22F42866B4F6F3C238EA2B9CB6241791184DDEDBAB55E94A025511F46325F3CA">>}, + {<<"stun">>, <<"735855314AD22CB7816B88597D2F5CA22E24AA5E4D6010A0EF3AFFB33CEED6A5">>}, + {<<"unicode_util_compat">>, <<"A48703A25C170EEDADCA83B11E88985AF08D35F37C6F664D6DCFB106A97782FC">>}]}, +{pkg_hash_ext,[ + {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, + {<<"cache_tab">>, <<"4258009EB050B22AABE0C848E230BBA58401A6895C58C2FF74DFB635E3C35900">>}, + {<<"eimp">>, <<"D96D4E8572B9DFC40F271E47F0CB1D8849373BC98A21223268781765ED52044C">>}, + {<<"epam">>, <<"2F3449E72885A72A6C2A843F561ADD0FC2F70D7A21F61456930A547473D4D989">>}, + {<<"eredis">>, <<"7C2B54C566FED55FEEF3341CA79B0100A6348FD3F162184B7ED5118D258C3CC1">>}, + {<<"esip">>, <<"0BDF2E3C349DC0B144F173150329E675C6A51AC473D7A0B2E362245FAAD3FBE6">>}, + {<<"ezlib">>, <<"DD14BA6C12521AF5CFE6923E73E3D545F4A0897DC66BFAB5287FBB7AE3962EAB">>}, + {<<"fast_tls">>, <<"59E183B5740E670E02B8AA6BE673B5E7779E5FE5BFCC679FE2D4993D1949A821">>}, + {<<"fast_xml">>, <<"EEC34E90ADACAFE467D5DDAB635A014DED73B98B4061554B2D1972173D929C39">>}, + {<<"fast_yaml">>, <<"24C7B9AB9E2B9269D64E45F4A2A1280966ADB17D31E63365CFD3EE277FB0A78D">>}, + {<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>}, + {<<"jiffy">>, <<"BB61BC42A720BBD33CB09A410E48BB79A61012C74CB8B3E75F26D988485CF381">>}, + {<<"jose">>, <<"0D6CD36FF8BA174DB29148FC112B5842186B68A90CE9FC2B3EC3AFE76593E614">>}, + {<<"luerl">>, <<"1B4B9D0CA5D7D280D1D2787A6A5EE9F5A212641B62BFF91556BAA53805DF3AED">>}, + {<<"mqtree">>, <<"C81065715C49A1882812F80A5AE2D842E80DD3F2D130530DF35990248BF8CE3C">>}, + {<<"p1_acme">>, <<"CE686986DE3F9D5FD285AFE87523CB45329A349C6C6BE7ACC1ED916725D46423">>}, + {<<"p1_mysql">>, <<"EA138083F2C54719B9CF549DBF5802A288B0019EA3E5449B354C74CC03FAFDEC">>}, + {<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>}, + {<<"p1_pgsql">>, <<"E99594446C411C660696795B062336F5C4BD800451D8F620BB4D4CE304E255C2">>}, + {<<"p1_utils">>, <<"C49BD44BC4A40AD996691AF826DD7E0AA56D4D0CD730817190A1F84D1A7F0033">>}, + {<<"pkix">>, <<"E02164F83094CB124C41B1AB28988A615D54B9ADC38575F00F19A597A3AC5D0E">>}, + {<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>}, + {<<"stringprep">>, <<"96F8B30BC50887F605B33B46BCA1D248C19A879319B8C482790E3B4DA5DA98C0">>}, + {<<"stun">>, <<"3D7FE8EFB9D05B240A6AA9A6BF8B8B7BFF2D802895D170443C588987DC1E12D9">>}, + {<<"unicode_util_compat">>, <<"B3A917854CE3AE233619744AD1E0102E05673136776FB2FA76234F3E03B23642">>}]} +]. diff --git a/rebar3 b/rebar3 new file mode 100755 index 000000000..deaea1f90 Binary files /dev/null and b/rebar3 differ diff --git a/rel/relive.config b/rel/relive.config new file mode 100644 index 000000000..49da88b79 --- /dev/null +++ b/rel/relive.config @@ -0,0 +1,4 @@ +[{mnesia, [{dir, "_build/relive/database"}]}, + {sync,[{src_dirs, {replace, [{"ejabberd/src", []}]}}]}, + {ejabberd, [{config, "_build/relive/conf/ejabberd.yml"}, + {log_path, "_build/relive/logs/ejabberd.log"}]}]. diff --git a/rel/relive.escript b/rel/relive.escript new file mode 100644 index 000000000..3ee2de0f3 --- /dev/null +++ b/rel/relive.escript @@ -0,0 +1,26 @@ +#!/usr/bin/env escript + +main(_) -> + Base = "_build/relive", + prepare(Base, "", none), + prepare(Base, "conf", {os, cmd, "rel/setup-relive.sh"}), + prepare(Base, "database", none), + prepare(Base, "logs", none), + c:erlangrc([os:cmd("echo -n $HOME")]), + ok. + +prepare(BaseDir, SuffixDir, MFA) -> + Dir = filename:join(BaseDir, SuffixDir), + case file:make_dir(Dir) of + ok -> + io:format("Preparing relive dir ~s...~n", [Dir]), + case MFA of + none -> ok; + {M, F, A} -> M:F(A) + end; + {error, eexist} -> + ok; + {error, LogsError} -> + io:format("Error creating dir ~s: ~p~n", [Dir, LogsError]), + halt(1) + end. diff --git a/rel/reltool.config.script b/rel/reltool.config.script index d84c202a3..4f142efe2 100644 --- a/rel/reltool.config.script +++ b/rel/reltool.config.script @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Evgeniy Khramtsov -%%% @copyright (C) 2013-2016, Evgeniy Khramtsov +%%% @copyright (C) 2013-2025, Evgeniy Khramtsov %%% @doc %%% %%% @end @@ -38,11 +38,12 @@ Vars = case file:consult(filename:join([TopDir, "vars.config"])) of RequiredOTPApps = [sasl, crypto, public_key, ssl, mnesia, inets, compiler, asn1, + observer, tools, syntax_tools, os_mon, xmerl], ConfiguredOTPApps = lists:flatmap( fun({tools, true}) -> - [tools, runtime_tools]; + [runtime_tools]; ({odbc, true}) -> [odbc]; (_) -> @@ -53,6 +54,8 @@ OTPApps = RequiredOTPApps ++ ConfiguredOTPApps, DepApps = lists:usort(lists:flatten(GetDeps(filename:join(TopDir, "rebar.config"), GetDeps))), +SysVer = erlang:system_info(otp_release), + Sys = [{lib_dirs, []}, {erts, [{mod_cond, derived}, {app_file, strip}]}, {app_file, strip}, @@ -70,13 +73,17 @@ Sys = [{lib_dirs, []}, {boot_rel, "ejabberd"}, {profile, embedded}, {incl_cond, exclude}, - {excl_archive_filters, [".*"]}, %% Do not archive built libs {excl_sys_filters, ["^bin/.*", "^erts.*/bin/(dialyzer|typer)", "^erts.*/(doc|info|include|lib|man|src)"]}, {excl_app_filters, ["\.gitignore"]}, {app, stdlib, [{incl_cond, include}]}, {app, kernel, [{incl_cond, include}]}, {app, ejabberd, [{incl_cond, include}, {lib_dir, ".."}]}] +++ if SysVer < "26" -> + [{excl_archive_filters, [".*"]}]; %% Do not archive built libs + true -> + [] + end ++ lists:map( fun(App) -> {app, App, [{incl_cond, include}, @@ -88,16 +95,15 @@ Sys = [{lib_dirs, []}, end, OTPApps). Overlay = [ - {mkdir, "var/log/ejabberd"}, - {mkdir, "var/lock"}, - {mkdir, "var/lib/ejabberd"}, - {mkdir, "etc/ejabberd"}, + {mkdir, "logs"}, + {mkdir, "database"}, + {mkdir, "conf"}, {mkdir, "doc"}, {template, "files/erl", "\{\{erts_vsn\}\}/bin/erl"}, {template, "../ejabberdctl.template", "bin/ejabberdctl"}, - {copy, "../ejabberdctl.cfg.example", "etc/ejabberd/ejabberdctl.cfg"}, - {copy, "../ejabberd.yml.example", "etc/ejabberd/ejabberd.yml"}, - {copy, "../inetrc", "etc/ejabberd/inetrc"}, + {copy, "../ejabberdctl.cfg.example", "conf/ejabberdctl.cfg"}, + {copy, "../ejabberd.yml.example", "conf/ejabberd.yml"}, + {copy, "../inetrc", "conf/inetrc"}, {copy, "files/install_upgrade.escript", "bin/install_upgrade.escript"} ], diff --git a/rel/setup-dev.sh b/rel/setup-dev.sh new file mode 100755 index 000000000..af3875cf0 --- /dev/null +++ b/rel/setup-dev.sh @@ -0,0 +1,36 @@ +printf "===> Preparing dev configuration files: " + +PWD_DIR=$(pwd) +REL_DIR=$PWD_DIR/_build/dev/rel/ejabberd/ +CON_DIR=$REL_DIR/conf/ + +[ -z "$REL_DIR_TEMP" ] && REL_DIR_TEMP=$REL_DIR +CON_DIR_TEMP=$REL_DIR_TEMP/conf/ + +cd $CON_DIR_TEMP || exit + +sed -i "s|# certfiles:|certfiles:\n - $CON_DIR/cert.pem|g" ejabberd.yml.example +sed -i "s|certfiles:|ca_file: $CON_DIR/ca.pem\ncertfiles:|g" ejabberd.yml.example +sed -i 's|^acl:$|acl:\n admin: [user: admin]|g' ejabberd.yml.example +[ ! -f "$CON_DIR/ejabberd.yml" ] \ + && printf "ejabberd.yml " \ + && mv ejabberd.yml.example ejabberd.yml + +sed -i "s|#' POLL|EJABBERD_BYPASS_WARNINGS=true\n\n#' POLL|g" ejabberdctl.cfg.example +[ ! -f "$CON_DIR/ejabberdctl.cfg" ] \ + && printf "ejabberdctl.cfg " \ + && mv ejabberdctl.cfg.example ejabberdctl.cfg + +echo "" +echo "===> Some example ways to start this ejabberd dev:" +echo " _build/dev/rel/ejabberd/bin/ejabberdctl live" +case "$1" in + "rebar3") + echo " _build/dev/rel/ejabberd/bin/ejabberd console" + ;; + "mix") + echo " RELEASE_NODE=ejabberd@localhost _build/dev/rel/ejabberd/bin/ejabberd start" + ;; + "*") + ;; +esac diff --git a/rel/setup-relive.sh b/rel/setup-relive.sh new file mode 100755 index 000000000..a4c88f6c5 --- /dev/null +++ b/rel/setup-relive.sh @@ -0,0 +1,31 @@ +PWD_DIR=$(pwd) +REL_DIR=$PWD_DIR/_build/relive/ +CON_DIR=$REL_DIR/conf/ + +[ -z "$REL_DIR_TEMP" ] && REL_DIR_TEMP=$REL_DIR +CON_DIR_TEMP=$REL_DIR_TEMP/conf/ + +make ejabberdctl.relive +chmod +x ejabberdctl.relive +mv ejabberdctl.relive $REL_DIR/ejabberdctl + +cp inetrc $CON_DIR/ +cp ejabberdctl.cfg.example $CON_DIR/ejabberdctl.cfg.example +cp ejabberd.yml.example $CON_DIR/ejabberd.yml.example +cp test/ejabberd_SUITE_data/ca.pem $CON_DIR +cp test/ejabberd_SUITE_data/cert.pem $CON_DIR + +cd $CON_DIR_TEMP || exit + +sed -i "s|# certfiles:|certfiles:\n - $CON_DIR/cert.pem|g" ejabberd.yml.example +sed -i "s|certfiles:|ca_file: $CON_DIR/ca.pem\ncertfiles:|g" ejabberd.yml.example +sed -i 's|^acl:$|acl:\n admin: [user: admin]|g' ejabberd.yml.example +[ ! -f "$CON_DIR/ejabberd.yml" ] \ + && printf "ejabberd.yml " \ + && mv ejabberd.yml.example ejabberd.yml + +sed -i "s|#' POLL|EJABBERD_BYPASS_WARNINGS=true\n\n#' POLL|g" ejabberdctl.cfg.example +[ ! -f "$CON_DIR/ejabberdctl.cfg" ] \ + && printf "ejabberdctl.cfg " \ + && mv ejabberdctl.cfg.example ejabberdctl.cfg \ + || printf diff --git a/rel/sys.config b/rel/sys.config new file mode 100644 index 000000000..26b0d0c61 --- /dev/null +++ b/rel/sys.config @@ -0,0 +1,2 @@ +[{ejabberd, [{config, "conf/ejabberd.yml"}, + {log_path, "logs/ejabberd.log"}]}]. diff --git a/rel/vm.args b/rel/vm.args new file mode 100644 index 000000000..6301f464d --- /dev/null +++ b/rel/vm.args @@ -0,0 +1,32 @@ +## Name of the node +-sname ejabberd@localhost + +## Cookie for distributed erlang +#-setcookie ejabberd + +-mnesia dir \"database\" + +## Heartbeat management; auto-restarts VM if it dies or becomes unresponsive +## (Disabled by default..use with caution!) +##-heart + +## Enable kernel poll and a few async threads +##+K true +##+A 5 + +## Increase number of concurrent ports/sockets +##-env ERL_MAX_PORTS 4096 + +## Tweak GC to run more often +##-env ERL_FULLSWEEP_AFTER 10 + +# +B [c | d | i] +# Option c makes Ctrl-C interrupt the current shell instead of invoking the emulator break +# handler. Option d (same as specifying +B without an extra option) disables the break handler. # Option i makes the emulator ignore any break signal. +# If option c is used with oldshell on Unix, Ctrl-C will restart the shell process rather than +# interrupt it. +# Disable the emulator break handler +# it easy to accidentally type ctrl-c when trying +# to reach for ctrl-d. ctrl-c on a live node can +# have very undesirable results +##+Bi diff --git a/sql/lite.new.sql b/sql/lite.new.sql new file mode 100644 index 000000000..42f289fb3 --- /dev/null +++ b/sql/lite.new.sql @@ -0,0 +1,491 @@ +-- +-- ejabberd, Copyright (C) 2002-2025 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. +-- + +CREATE TABLE users ( + username text NOT NULL, + type smallint, + server_host text NOT NULL, + password text NOT NULL, + serverkey text NOT NULL DEFAULT '', + salt text NOT NULL DEFAULT '', + iterationcount integer NOT NULL DEFAULT 0, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (server_host, username, type) +); + + +CREATE TABLE last ( + username text NOT NULL, + server_host text NOT NULL, + seconds text NOT NULL, + state text NOT NULL, + PRIMARY KEY (server_host, username) +); + + +CREATE TABLE rosterusers ( + username text NOT NULL, + server_host text NOT NULL, + jid text NOT NULL, + nick text NOT NULL, + subscription character(1) NOT NULL, + ask character(1) NOT NULL, + askmessage text NOT NULL, + server character(1) NOT NULL, + subscribe text NOT NULL, + type text, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX i_rosteru_sh_user_jid ON rosterusers (server_host, username, jid); +CREATE INDEX i_rosteru_sh_jid ON rosterusers (server_host, jid); + + +CREATE TABLE rostergroups ( + username text NOT NULL, + server_host text NOT NULL, + jid text NOT NULL, + grp text NOT NULL +); + +CREATE INDEX i_rosterg_sh_user_jid ON rostergroups (server_host, username, jid); + +CREATE TABLE sr_group ( + name text NOT NULL, + server_host text NOT NULL, + opts text NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (server_host, name) +); + +CREATE UNIQUE INDEX i_sr_group_sh_name ON sr_group (server_host, name); + +CREATE TABLE sr_user ( + jid text NOT NULL, + server_host text NOT NULL, + grp text NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (server_host, jid, grp) +); + +CREATE UNIQUE INDEX i_sr_user_sh_jid_grp ON sr_user (server_host, jid, grp); +CREATE INDEX i_sr_user_sh_grp ON sr_user (server_host, grp); + +CREATE TABLE spool ( + username text NOT NULL, + server_host text NOT NULL, + xml text NOT NULL, + seq INTEGER PRIMARY KEY AUTOINCREMENT, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX i_spool_sh_username ON spool (server_host, username); + +CREATE TABLE archive ( + username text NOT NULL, + server_host text NOT NULL, + timestamp BIGINT UNSIGNED NOT NULL, + peer text NOT NULL, + bare_peer text NOT NULL, + xml text NOT NULL, + txt text, + id INTEGER PRIMARY KEY AUTOINCREMENT, + kind text, + nick text, + origin_id text, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX i_archive_sh_username_timestamp ON archive (server_host, username, timestamp); +CREATE INDEX i_archive_sh_username_peer ON archive (server_host, username, peer); +CREATE INDEX i_archive_sh_username_bare_peer ON archive (server_host, username, bare_peer); +CREATE INDEX i_archive_sh_timestamp ON archive (server_host, timestamp); +CREATE INDEX i_archive_sh_username_origin_id ON archive (server_host, username, origin_id); + +-- To update 'archive' from ejabberd <= 23.10: +-- ALTER TABLE archive ADD COLUMN origin_id text NOT NULL DEFAULT ''; +-- CREATE INDEX i_archive_sh_username_origin_id ON archive (server_host, username, origin_id); + +CREATE TABLE archive_prefs ( + username text NOT NULL, + server_host text NOT NULL, + def text NOT NULL, + always text NOT NULL, + never text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (server_host, username) +); + +CREATE TABLE vcard ( + username text NOT NULL, + server_host text NOT NULL, + vcard text NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (server_host, username) +); + +CREATE TABLE vcard_search ( + username text NOT NULL, + lusername text NOT NULL, + server_host text NOT NULL, + fn text NOT NULL, + lfn text NOT NULL, + family text NOT NULL, + lfamily text NOT NULL, + given text NOT NULL, + lgiven text NOT NULL, + middle text NOT NULL, + lmiddle text NOT NULL, + nickname text NOT NULL, + lnickname text NOT NULL, + bday text NOT NULL, + lbday text NOT NULL, + ctry text NOT NULL, + lctry text NOT NULL, + locality text NOT NULL, + llocality text NOT NULL, + email text NOT NULL, + lemail text NOT NULL, + orgname text NOT NULL, + lorgname text NOT NULL, + orgunit text NOT NULL, + lorgunit text NOT NULL, + PRIMARY KEY (server_host, lusername) +); + +CREATE INDEX i_vcard_search_sh_lfn ON vcard_search(server_host, lfn); +CREATE INDEX i_vcard_search_sh_lfamily ON vcard_search(server_host, lfamily); +CREATE INDEX i_vcard_search_sh_lgiven ON vcard_search(server_host, lgiven); +CREATE INDEX i_vcard_search_sh_lmiddle ON vcard_search(server_host, lmiddle); +CREATE INDEX i_vcard_search_sh_lnickname ON vcard_search(server_host, lnickname); +CREATE INDEX i_vcard_search_sh_lbday ON vcard_search(server_host, lbday); +CREATE INDEX i_vcard_search_sh_lctry ON vcard_search(server_host, lctry); +CREATE INDEX i_vcard_search_sh_llocality ON vcard_search(server_host, llocality); +CREATE INDEX i_vcard_search_sh_lemail ON vcard_search(server_host, lemail); +CREATE INDEX i_vcard_search_sh_lorgname ON vcard_search(server_host, lorgname); +CREATE INDEX i_vcard_search_sh_lorgunit ON vcard_search(server_host, lorgunit); + +CREATE TABLE privacy_default_list ( + username text NOT NULL, + server_host text NOT NULL, + name text NOT NULL, + PRIMARY KEY (server_host, username) +); + +CREATE TABLE privacy_list ( + username text NOT NULL, + server_host text NOT NULL, + name text NOT NULL, + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX i_privacy_list_sh_username_name ON privacy_list (server_host, username, name); + +CREATE TABLE privacy_list_data ( + id bigint REFERENCES privacy_list(id) ON DELETE CASCADE, + t character(1) NOT NULL, + value text NOT NULL, + action character(1) NOT NULL, + ord NUMERIC NOT NULL, + match_all boolean NOT NULL, + match_iq boolean NOT NULL, + match_message boolean NOT NULL, + match_presence_in boolean NOT NULL, + match_presence_out boolean NOT NULL +); + +CREATE TABLE private_storage ( + username text NOT NULL, + server_host text NOT NULL, + namespace text NOT NULL, + data text NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (server_host, username, namespace) +); + +CREATE TABLE roster_version ( + username text NOT NULL, + server_host text NOT NULL, + version text NOT NULL, + PRIMARY KEY (server_host, username) +); + +CREATE TABLE pubsub_node ( + host text NOT NULL, + node text NOT NULL, + parent text NOT NULL DEFAULT '', + plugin text NOT NULL, + nodeid INTEGER PRIMARY KEY AUTOINCREMENT +); +CREATE INDEX i_pubsub_node_parent ON pubsub_node (parent); +CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node (host, node); + +CREATE TABLE pubsub_node_option ( + nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, + name text NOT NULL, + val text NOT NULL +); +CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option (nodeid); + +CREATE TABLE pubsub_node_owner ( + nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, + owner text NOT NULL +); +CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner (nodeid); + +CREATE TABLE pubsub_state ( + nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, + jid text NOT NULL, + affiliation character(1), + subscriptions text NOT NULL DEFAULT '', + stateid INTEGER PRIMARY KEY AUTOINCREMENT +); +CREATE INDEX i_pubsub_state_jid ON pubsub_state (jid); +CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state (nodeid, jid); + +CREATE TABLE pubsub_item ( + nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, + itemid text NOT NULL, + publisher text NOT NULL, + creation varchar(32) NOT NULL, + modification varchar(32) NOT NULL, + payload text NOT NULL DEFAULT '' +); +CREATE INDEX i_pubsub_item_itemid ON pubsub_item (itemid); +CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item (nodeid, itemid); + + CREATE TABLE pubsub_subscription_opt ( + subid text NOT NULL, + opt_name varchar(32), + opt_value text NOT NULL +); +CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt (subid, opt_name); + +CREATE TABLE muc_room ( + name text NOT NULL, + server_host text NOT NULL, + host text NOT NULL, + opts text NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX i_muc_room_name_host ON muc_room (name, host); +CREATE INDEX i_muc_room_host_created_at ON muc_room (host, created_at); + +CREATE TABLE muc_registered ( + jid text NOT NULL, + host text NOT NULL, + server_host text NOT NULL, + nick text NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX i_muc_registered_nick ON muc_registered (nick); +CREATE UNIQUE INDEX i_muc_registered_jid_host ON muc_registered (jid, host); + +CREATE TABLE muc_online_room ( + name text NOT NULL, + host text NOT NULL, + server_host text NOT NULL, + node text NOT NULL, + pid text NOT NULL +); + +CREATE UNIQUE INDEX i_muc_online_room_name_host ON muc_online_room (name, host); + +CREATE TABLE muc_online_users ( + username text NOT NULL, + server text NOT NULL, + resource text NOT NULL, + name text NOT NULL, + host text NOT NULL, + server_host text NOT NULL, + node text NOT NULL +); + +CREATE UNIQUE INDEX i_muc_online_users ON muc_online_users (username, server, resource, name, host); + +CREATE TABLE muc_room_subscribers ( + room text NOT NULL, + host text NOT NULL, + jid text NOT NULL, + nick text NOT NULL, + nodes text NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers(host, jid); +CREATE INDEX i_muc_room_subscribers_jid ON muc_room_subscribers(jid); +CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers(host, room, jid); + +CREATE TABLE motd ( + username text NOT NULL, + server_host text NOT NULL, + xml text, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (server_host, username) +); + +CREATE TABLE caps_features ( + node text NOT NULL, + subnode text NOT NULL, + feature text, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX i_caps_features_node_subnode ON caps_features (node, subnode); + +CREATE TABLE sm ( + usec bigint NOT NULL, + pid text NOT NULL, + node text NOT NULL, + username text NOT NULL, + server_host text NOT NULL, + resource text NOT NULL, + priority text NOT NULL, + info text NOT NULL, + PRIMARY KEY (usec, pid) +); + +CREATE INDEX i_sm_node ON sm(node); +CREATE INDEX i_sm_sh_username ON sm (server_host, username); + +CREATE TABLE oauth_token ( + token text NOT NULL PRIMARY KEY, + jid text NOT NULL, + scope text NOT NULL, + expire bigint NOT NULL +); + +CREATE TABLE oauth_client ( + client_id text PRIMARY KEY, + client_name text NOT NULL, + grant_type text NOT NULL, + options text NOT NULL +); + +CREATE TABLE route ( + domain text NOT NULL, + server_host text NOT NULL, + node text NOT NULL, + pid text NOT NULL, + local_hint text NOT NULL +); + +CREATE UNIQUE INDEX i_route ON route(domain, server_host, node, pid); + +CREATE TABLE bosh ( + sid text NOT NULL, + node text NOT NULL, + pid text NOT NULL +); + +CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid); + +CREATE TABLE proxy65 ( + sid text NOT NULL, + pid_t text NOT NULL, + pid_i text NOT NULL, + node_t text NOT NULL, + node_i text NOT NULL, + jid_i text NOT NULL +); + +CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 (sid); +CREATE INDEX i_proxy65_jid ON proxy65 (jid_i); + +CREATE TABLE push_session ( + username text NOT NULL, + server_host text NOT NULL, + timestamp bigint NOT NULL, + service text NOT NULL, + node text NOT NULL, + xml text NOT NULL, + PRIMARY KEY (server_host, username, timestamp) +); + +CREATE UNIQUE INDEX i_push_session_susn ON push_session (server_host, username, service, node); +CREATE INDEX i_push_session_sh_username_timestamp ON push_session (server_host, username, timestamp); + +CREATE TABLE mix_channel ( + channel text NOT NULL, + service text NOT NULL, + username text NOT NULL, + domain text NOT NULL, + jid text NOT NULL, + hidden boolean NOT NULL, + hmac_key text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX i_mix_channel ON mix_channel (channel, service); +CREATE INDEX i_mix_channel_serv ON mix_channel (service); + +CREATE TABLE mix_participant ( + channel text NOT NULL, + service text NOT NULL, + username text NOT NULL, + domain text NOT NULL, + jid text NOT NULL, + id text NOT NULL, + nick text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX i_mix_participant ON mix_participant (channel, service, username, domain); + +CREATE TABLE mix_subscription ( + channel text NOT NULL, + service text NOT NULL, + username text NOT NULL, + domain text NOT NULL, + node text NOT NULL, + jid text NOT NULL +); + +CREATE UNIQUE INDEX i_mix_subscription ON mix_subscription (channel, service, username, domain, node); +CREATE INDEX i_mix_subscription_chan_serv_node ON mix_subscription (channel, service, node); + +CREATE TABLE mix_pam ( + username text NOT NULL, + server_host text NOT NULL, + channel text NOT NULL, + service text NOT NULL, + id text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username, server_host, channel, service); + +CREATE TABLE mqtt_pub ( + username text NOT NULL, + server_host text NOT NULL, + resource text NOT NULL, + topic text NOT NULL, + qos smallint NOT NULL, + payload blob NOT NULL, + payload_format smallint NOT NULL, + content_type text NOT NULL, + response_topic text NOT NULL, + correlation_data blob NOT NULL, + user_properties blob NOT NULL, + expiry bigint NOT NULL +); + +CREATE UNIQUE INDEX i_mqtt_topic_server ON mqtt_pub (topic, server_host); diff --git a/sql/lite.sql b/sql/lite.sql index aacea11e7..b31e02b79 100644 --- a/sql/lite.sql +++ b/sql/lite.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2016 ProcessOne +-- ejabberd, Copyright (C) 2002-2025 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as @@ -17,12 +17,14 @@ -- CREATE TABLE users ( - username text PRIMARY KEY, + username text, + type smallint, password text NOT NULL, serverkey text NOT NULL DEFAULT '', salt text NOT NULL DEFAULT '', iterationcount integer NOT NULL DEFAULT 0, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + primary key (username, type) ); @@ -41,13 +43,12 @@ CREATE TABLE rosterusers ( ask character(1) NOT NULL, askmessage text NOT NULL, server character(1) NOT NULL, - subscribe text, + subscribe text NOT NULL, type text, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE UNIQUE INDEX i_rosteru_user_jid ON rosterusers (username, jid); -CREATE INDEX i_rosteru_username ON rosterusers (username); CREATE INDEX i_rosteru_jid ON rosterusers (jid); @@ -65,6 +66,8 @@ CREATE TABLE sr_group ( created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); +CREATE UNIQUE INDEX i_sr_group_name ON sr_group (name); + CREATE TABLE sr_user ( jid text NOT NULL, grp text NOT NULL, @@ -72,7 +75,6 @@ CREATE TABLE sr_user ( ); CREATE UNIQUE INDEX i_sr_user_jid_grp ON sr_user (jid, grp); -CREATE INDEX i_sr_user_jid ON sr_user (jid); CREATE INDEX i_sr_user_grp ON sr_user (grp); CREATE TABLE spool ( @@ -84,6 +86,37 @@ CREATE TABLE spool ( CREATE INDEX i_despool ON spool (username); +CREATE TABLE archive ( + username text NOT NULL, + timestamp BIGINT UNSIGNED NOT NULL, + peer text NOT NULL, + bare_peer text NOT NULL, + xml text NOT NULL, + txt text, + id INTEGER PRIMARY KEY AUTOINCREMENT, + kind text, + nick text, + origin_id text, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX i_username_timestamp ON archive(username, timestamp); +CREATE INDEX i_archive_username_peer ON archive (username, peer); +CREATE INDEX i_archive_username_bare_peer ON archive (username, bare_peer); +CREATE INDEX i_timestamp ON archive(timestamp); +CREATE INDEX i_archive_username_origin_id ON archive (username, origin_id); + +-- To update 'archive' from ejabberd <= 23.10: +-- ALTER TABLE archive ADD COLUMN origin_id text NOT NULL DEFAULT ''; +-- CREATE INDEX i_archive_username_origin_id ON archive (username, origin_id); + +CREATE TABLE archive_prefs ( + username text NOT NULL PRIMARY KEY, + def text NOT NULL, + always text NOT NULL, + never text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); CREATE TABLE vcard ( username text PRIMARY KEY, @@ -91,12 +124,6 @@ CREATE TABLE vcard ( created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); -CREATE TABLE vcard_xupdate ( - username text PRIMARY KEY, - hash text NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); - CREATE TABLE vcard_search ( username text NOT NULL, lusername text PRIMARY KEY, @@ -148,7 +175,6 @@ CREATE TABLE privacy_list ( created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); -CREATE INDEX i_privacy_list_username ON privacy_list (username); CREATE UNIQUE INDEX i_privacy_list_username_name ON privacy_list (username, name); CREATE TABLE privacy_list_data ( @@ -171,7 +197,6 @@ CREATE TABLE private_storage ( created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); -CREATE INDEX i_private_storage_username ON private_storage (username); CREATE UNIQUE INDEX i_private_storage_username_namespace ON private_storage (username, namespace); @@ -181,10 +206,10 @@ CREATE TABLE roster_version ( ); CREATE TABLE pubsub_node ( - host text, - node text, - parent text, - type text, + host text NOT NULL, + node text NOT NULL, + parent text NOT NULL DEFAULT '', + plugin text NOT NULL, nodeid INTEGER PRIMARY KEY AUTOINCREMENT ); CREATE INDEX i_pubsub_node_parent ON pubsub_node (parent); @@ -192,22 +217,22 @@ CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node (host, node); CREATE TABLE pubsub_node_option ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, - name text, - val text + name text NOT NULL, + val text NOT NULL ); CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option (nodeid); CREATE TABLE pubsub_node_owner ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, - owner text + owner text NOT NULL ); CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner (nodeid); CREATE TABLE pubsub_state ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, - jid text, + jid text NOT NULL, affiliation character(1), - subscriptions text, + subscriptions text NOT NULL DEFAULT '', stateid INTEGER PRIMARY KEY AUTOINCREMENT ); CREATE INDEX i_pubsub_state_jid ON pubsub_state (jid); @@ -215,19 +240,19 @@ CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state (nodeid, jid); CREATE TABLE pubsub_item ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, - itemid text, - publisher text, - creation text, - modification text, - payload text + itemid text NOT NULL, + publisher text NOT NULL, + creation varchar(32) NOT NULL, + modification varchar(32) NOT NULL, + payload text NOT NULL DEFAULT '' ); CREATE INDEX i_pubsub_item_itemid ON pubsub_item (itemid); CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item (nodeid, itemid); CREATE TABLE pubsub_subscription_opt ( - subid text, + subid text NOT NULL, opt_name varchar(32), - opt_value text + opt_value text NOT NULL ); CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt (subid, opt_name); @@ -239,6 +264,7 @@ CREATE TABLE muc_room ( ); CREATE UNIQUE INDEX i_muc_room_name_host ON muc_room (name, host); +CREATE INDEX i_muc_room_host_created_at ON muc_room (host, created_at); CREATE TABLE muc_registered ( jid text NOT NULL, @@ -250,14 +276,38 @@ CREATE TABLE muc_registered ( CREATE INDEX i_muc_registered_nick ON muc_registered (nick); CREATE UNIQUE INDEX i_muc_registered_jid_host ON muc_registered (jid, host); -CREATE TABLE irc_custom ( - jid text NOT NULL, +CREATE TABLE muc_online_room ( + name text NOT NULL, host text NOT NULL, - data text NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP + node text NOT NULL, + pid text NOT NULL ); -CREATE UNIQUE INDEX i_irc_custom_jid_host ON irc_custom (jid, host); +CREATE UNIQUE INDEX i_muc_online_room_name_host ON muc_online_room (name, host); + +CREATE TABLE muc_online_users ( + username text NOT NULL, + server text NOT NULL, + resource text NOT NULL, + name text NOT NULL, + host text NOT NULL, + node text NOT NULL +); + +CREATE UNIQUE INDEX i_muc_online_users ON muc_online_users (username, server, resource, name, host); + +CREATE TABLE muc_room_subscribers ( + room text NOT NULL, + host text NOT NULL, + jid text NOT NULL, + nick text NOT NULL, + nodes text NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers(host, jid); +CREATE INDEX i_muc_room_subscribers_jid ON muc_room_subscribers(jid); +CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers(host, room, jid); CREATE TABLE motd ( username text PRIMARY KEY, @@ -274,32 +324,6 @@ CREATE TABLE caps_features ( CREATE INDEX i_caps_features_node_subnode ON caps_features (node, subnode); -CREATE TABLE archive ( - username text NOT NULL, - timestamp BIGINT UNSIGNED NOT NULL, - peer text NOT NULL, - bare_peer text NOT NULL, - xml text NOT NULL, - txt text, - id INTEGER PRIMARY KEY AUTOINCREMENT, - kind text, - nick text, - created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP -); - -CREATE INDEX i_username ON archive(username); -CREATE INDEX i_timestamp ON archive(timestamp); -CREATE INDEX i_peer ON archive(peer); -CREATE INDEX i_bare_peer ON archive(bare_peer); - -CREATE TABLE archive_prefs ( - username text NOT NULL PRIMARY KEY, - def text NOT NULL, - always text NOT NULL, - never text NOT NULL, - created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP -); - CREATE TABLE sm ( usec bigint NOT NULL, pid text NOT NULL, @@ -320,3 +344,116 @@ CREATE TABLE oauth_token ( scope text NOT NULL, expire bigint NOT NULL ); + +CREATE TABLE oauth_client ( + client_id text PRIMARY KEY, + client_name text NOT NULL, + grant_type text NOT NULL, + options text NOT NULL +); + +CREATE TABLE route ( + domain text NOT NULL, + server_host text NOT NULL, + node text NOT NULL, + pid text NOT NULL, + local_hint text NOT NULL +); + +CREATE UNIQUE INDEX i_route ON route(domain, server_host, node, pid); + +CREATE TABLE bosh ( + sid text NOT NULL, + node text NOT NULL, + pid text NOT NULL +); + +CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid); + +CREATE TABLE proxy65 ( + sid text NOT NULL, + pid_t text NOT NULL, + pid_i text NOT NULL, + node_t text NOT NULL, + node_i text NOT NULL, + jid_i text NOT NULL +); + +CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 (sid); +CREATE INDEX i_proxy65_jid ON proxy65 (jid_i); + +CREATE TABLE push_session ( + username text NOT NULL, + timestamp bigint NOT NULL, + service text NOT NULL, + node text NOT NULL, + xml text NOT NULL +); + +CREATE UNIQUE INDEX i_push_usn ON push_session (username, service, node); +CREATE UNIQUE INDEX i_push_ut ON push_session (username, timestamp); + +CREATE TABLE mix_channel ( + channel text NOT NULL, + service text NOT NULL, + username text NOT NULL, + domain text NOT NULL, + jid text NOT NULL, + hidden boolean NOT NULL, + hmac_key text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX i_mix_channel ON mix_channel (channel, service); +CREATE INDEX i_mix_channel_serv ON mix_channel (service); + +CREATE TABLE mix_participant ( + channel text NOT NULL, + service text NOT NULL, + username text NOT NULL, + domain text NOT NULL, + jid text NOT NULL, + id text NOT NULL, + nick text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX i_mix_participant ON mix_participant (channel, service, username, domain); + +CREATE TABLE mix_subscription ( + channel text NOT NULL, + service text NOT NULL, + username text NOT NULL, + domain text NOT NULL, + node text NOT NULL, + jid text NOT NULL +); + +CREATE UNIQUE INDEX i_mix_subscription ON mix_subscription (channel, service, username, domain, node); +CREATE INDEX i_mix_subscription_chan_serv_node ON mix_subscription (channel, service, node); + +CREATE TABLE mix_pam ( + username text NOT NULL, + channel text NOT NULL, + service text NOT NULL, + id text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username, channel, service); + +CREATE TABLE mqtt_pub ( + username text NOT NULL, + resource text NOT NULL, + topic text NOT NULL, + qos smallint NOT NULL, + payload blob NOT NULL, + payload_format smallint NOT NULL, + content_type text NOT NULL, + response_topic text NOT NULL, + correlation_data blob NOT NULL, + user_properties blob NOT NULL, + expiry bigint NOT NULL +); + +CREATE UNIQUE INDEX i_mqtt_topic ON mqtt_pub (topic); diff --git a/sql/mssql.new.sql b/sql/mssql.new.sql new file mode 100644 index 000000000..f67033eed --- /dev/null +++ b/sql/mssql.new.sql @@ -0,0 +1,652 @@ +-- +-- ejabberd, Copyright (C) 2002-2025 ProcessOne +-- +-- This program is free software; you can redistribute it and/or +-- modify it under the terms of the GNU General Public License as +-- published by the Free Software Foundation; either version 2 of the +-- License, or (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +-- General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License along +-- with this program; if not, write to the Free Software Foundation, Inc., +-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +-- + +SET ANSI_PADDING OFF; +SET ANSI_NULLS ON; +SET QUOTED_IDENTIFIER ON; +SET ANSI_PADDING ON; + +CREATE TABLE [dbo].[archive] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [timestamp] [bigint] NOT NULL, + [peer] [varchar] (250) NOT NULL, + [bare_peer] [varchar] (250) NOT NULL, + [xml] [ntext] NOT NULL, + [txt] [ntext] NULL, + [id] [bigint] IDENTITY(1,1) NOT NULL, + [kind] [varchar] (10) NULL, + [nick] [varchar] (250) NULL, + [origin_id] [varchar] (250) NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE(), + CONSTRAINT [archive_PK] PRIMARY KEY CLUSTERED +( + [id] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE INDEX [archive_sh_username_timestamp] ON [archive] (server_host, username, timestamp) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [archive_sh_username_peer] ON [archive] (server_host, username, peer) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [archive_sh_username_bare_peer] ON [archive] (server_host, username, bare_peer) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [archive_sh_timestamp] ON [archive] (server_host, timestamp) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [archive_sh_username_origin_id] ON [archive] (server_host, username, origin_id) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[archive_prefs] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [def] [text] NOT NULL, + [always] [text] NOT NULL, + [never] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE(), + CONSTRAINT [archive_prefs_PRIMARY] PRIMARY KEY CLUSTERED +( + [server_host] ASC, + [username] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE TABLE [dbo].[caps_features] ( + [node] [varchar] (250) NOT NULL, + [subnode] [varchar] (250) NOT NULL, + [feature] [text] NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE CLUSTERED INDEX [caps_features_node_subnode] ON [caps_features] (node, subnode) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[last] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [seconds] [text] NOT NULL, + [state] [text] NOT NULL, + CONSTRAINT [last_PRIMARY] PRIMARY KEY CLUSTERED +( + [server_host] ASC, + [username] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE TABLE [dbo].[motd] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [xml] [text] NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE(), + CONSTRAINT [motd_PRIMARY] PRIMARY KEY CLUSTERED +( + [server_host] ASC, + [username] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE TABLE [dbo].[muc_registered] ( + [jid] [varchar] (255) NOT NULL, + [host] [varchar] (255) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [nick] [varchar] (255) NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +); + +CREATE INDEX [muc_registered_nick] ON [muc_registered] (nick) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE UNIQUE CLUSTERED INDEX [muc_registered_jid_host] ON [muc_registered] (jid, host) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[muc_room] ( + [name] [varchar] (250) NOT NULL, + [host] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [opts] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [muc_room_name_host] ON [muc_room] (name, host) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); +CREATE INDEX [muc_room_host_created_at] ON [muc_registered] (host, nick) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[muc_online_room] ( + [name] [varchar] (250) NOT NULL, + [host] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [node] [varchar] (250) NOT NULL, + [pid] [varchar] (100) NOT NULL +); + +CREATE UNIQUE CLUSTERED INDEX [muc_online_room_name_host] ON [muc_online_room] (name, host) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[muc_online_users] ( + [username] [varchar] (250) NOT NULL, + [server] [varchar] (250) NOT NULL, + [resource] [varchar] (250) NOT NULL, + [name] [varchar] (250) NOT NULL, + [host] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [node] [varchar] (250) NOT NULL +); + +CREATE UNIQUE INDEX [muc_online_users_i] ON [muc_online_users] (username, server, resource, name, host) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[muc_room_subscribers] ( + [room] [varchar] (191) NOT NULL, + [host] [varchar] (191) NOT NULL, + [jid] [varchar] (191) NOT NULL, + [nick] [text] NOT NULL, + [nodes] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +); + +CREATE UNIQUE CLUSTERED INDEX [muc_room_subscribers_host_room_jid] ON [muc_room_subscribers] (host, room, jid); +CREATE INDEX [muc_room_subscribers_host_jid] ON [muc_room_subscribers] (host, jid); +CREATE INDEX [muc_room_subscribers_jid] ON [muc_room_subscribers] (jid); + +CREATE TABLE [dbo].[privacy_default_list] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [name] [varchar] (250) NOT NULL, + CONSTRAINT [privacy_default_list_PRIMARY] PRIMARY KEY CLUSTERED +( + [server_host] ASC, + [username] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +); + +CREATE TABLE [dbo].[privacy_list] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [name] [varchar] (250) NOT NULL, + [id] [bigint] IDENTITY(1,1) NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE(), + CONSTRAINT [privacy_list_PK] PRIMARY KEY CLUSTERED +( + [id] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +); + +CREATE UNIQUE INDEX [privacy_list_sh_username_name] ON [privacy_list] (server_host, username, name) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[privacy_list_data] ( + [id] [bigint] NULL, + [t] [char] (1) NOT NULL, + [value] [text] NOT NULL, + [action] [char] (1) NOT NULL, + [ord] [smallint] NOT NULL, + [match_all] [smallint] NOT NULL, + [match_iq] [smallint] NOT NULL, + [match_message] [smallint] NOT NULL, + [match_presence_in] [smallint] NOT NULL, + [match_presence_out] [smallint] NOT NULL +) TEXTIMAGE_ON [PRIMARY]; + +CREATE CLUSTERED INDEX [privacy_list_data_id] ON [privacy_list_data] (id) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[private_storage] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [namespace] [varchar] (250) NOT NULL, + [data] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [private_storage_sh_username_namespace] ON [private_storage] (server_host, username, namespace) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[pubsub_item] ( + [nodeid] [bigint] NULL, + [itemid] [varchar] (255) NOT NULL, + [publisher] [varchar] (250) NOT NULL, + [creation] [varchar] (32) NOT NULL, + [modification] [varchar] (32) NOT NULL, + [payload] [text] NOT NULL DEFAULT '' +) TEXTIMAGE_ON [PRIMARY]; + +CREATE INDEX [pubsub_item_itemid] ON [pubsub_item] (itemid) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE UNIQUE CLUSTERED INDEX [pubsub_item_nodeid_itemid] ON [pubsub_item] (nodeid, itemid) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[pubsub_node_option] ( + [nodeid] [bigint] NULL, + [name] [varchar] (250) NOT NULL, + [val] [varchar] (250) NOT NULL +); + +CREATE CLUSTERED INDEX [pubsub_node_option_nodeid] ON [pubsub_node_option] (nodeid) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[pubsub_node_owner] ( + [nodeid] [bigint] NULL, + [owner] [text] NOT NULL +) TEXTIMAGE_ON [PRIMARY]; + +CREATE CLUSTERED INDEX [pubsub_node_owner_nodeid] ON [pubsub_node_owner] (nodeid) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[pubsub_state] ( + [nodeid] [bigint] NULL, + [jid] [varchar] (255) NOT NULL, + [affiliation] [char] (1) NOT NULL, + [subscriptions] [text] NOT NULL DEFAULT '', + [stateid] [bigint] IDENTITY(1,1) NOT NULL, + CONSTRAINT [pubsub_state_PRIMARY] PRIMARY KEY CLUSTERED +( + [stateid] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE INDEX [pubsub_state_jid] ON [pubsub_state] (jid) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE UNIQUE INDEX [pubsub_state_nodeid_jid] ON [pubsub_state] (nodeid, jid) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[pubsub_subscription_opt] ( + [subid] [varchar] (255) NOT NULL, + [opt_name] [varchar] (32) NOT NULL, + [opt_value] [text] NOT NULL +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [pubsub_subscription_opt_subid_opt_name] ON [pubsub_subscription_opt] (subid, opt_name) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[pubsub_node] ( + [host] [varchar] (255) NOT NULL, + [node] [varchar] (255) NOT NULL, + [parent] [varchar] (255) NOT NULL DEFAULT '', + [plugin] [varchar] (32) NOT NULL, + [nodeid] [bigint] IDENTITY(1,1) NOT NULL, + CONSTRAINT [pubsub_node_PRIMARY] PRIMARY KEY CLUSTERED +( + [nodeid] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +); + +CREATE INDEX [pubsub_node_parent] ON [pubsub_node] (parent) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE UNIQUE INDEX [pubsub_node_host_node] ON [pubsub_node] (host, node) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[roster_version] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [version] [text] NOT NULL, + CONSTRAINT [roster_version_PRIMARY] PRIMARY KEY CLUSTERED +( + [server_host] ASC, + [username] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE TABLE [dbo].[rostergroups] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [jid] [varchar] (250) NOT NULL, + [grp] [text] NOT NULL +) TEXTIMAGE_ON [PRIMARY]; + +CREATE CLUSTERED INDEX [rostergroups_sh_username_jid] ON [rostergroups] ([server_host], [username], [jid]) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[rosterusers] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [jid] [varchar] (250) NOT NULL, + [nick] [text] NOT NULL, + [subscription] [char] (1) NOT NULL, + [ask] [char] (1) NOT NULL, + [askmessage] [text] NOT NULL, + [server] [char] (1) NOT NULL, + [subscribe] [text] NOT NULL, + [type] [text] NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [rosterusers_sh_username_jid] ON [rosterusers] ([server_host], [username], [jid]) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [rosterusers_sh_jid] ON [rosterusers] ([server_host], [jid]) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[sm] ( + [usec] [bigint] NOT NULL, + [pid] [varchar] (100) NOT NULL, + [node] [varchar] (255) NOT NULL, + [username] [varchar] (255) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [resource] [varchar] (255) NOT NULL, + [priority] [text] NOT NULL, + [info] [text] NOT NULL +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [sm_sid] ON [sm] (usec, pid) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [sm_node] ON [sm] (node) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [sm_sh_username] ON [sm] (server_host, username) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[spool] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [xml] [text] NOT NULL, + [seq] [bigint] IDENTITY(1,1) NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE(), + CONSTRAINT [spool_PK] PRIMARY KEY CLUSTERED +( + [seq] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE INDEX [spool_sh_username] ON [spool] (server_host, username) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [spool_created_at] ON [spool] (created_at) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +; + +CREATE TABLE [dbo].[sr_group] ( + [name] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [opts] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [sr_group_sh_name] ON [sr_group] ([server_host], [name]) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[sr_user] ( + [jid] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [grp] [varchar] (250) NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +); + +CREATE UNIQUE CLUSTERED INDEX [sr_user_sh_jid_group] ON [sr_user] ([server_host], [jid], [grp]) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [sr_user_sh_grp] ON [sr_user] ([server_host], [grp]) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[users] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [type] [smallint] NOT NULL, + [password] [text] NOT NULL, + [serverkey] [text] NOT NULL DEFAULT '', + [salt] [text] NOT NULL DEFAULT '', + [iterationcount] [smallint] NOT NULL DEFAULT 0, + [created_at] [datetime] NOT NULL DEFAULT GETDATE(), + CONSTRAINT [users_PRIMARY] PRIMARY KEY CLUSTERED +( + [server_host] ASC, + [username] ASC, + [type] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE TABLE [dbo].[vcard] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [vcard] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE(), + CONSTRAINT [vcard_PRIMARY] PRIMARY KEY CLUSTERED +( + [server_host] ASC, + [username] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE TABLE [dbo].[vcard_search] ( + [username] [varchar] (250) NOT NULL, + [lusername] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [fn] [text] NOT NULL, + [lfn] [varchar] (250) NOT NULL, + [family] [text] NOT NULL, + [lfamily] [varchar] (250) NOT NULL, + [given] [text] NOT NULL, + [lgiven] [varchar] (250) NOT NULL, + [middle] [text] NOT NULL, + [lmiddle] [varchar] (250) NOT NULL, + [nickname] [text] NOT NULL, + [lnickname] [varchar] (250) NOT NULL, + [bday] [text] NOT NULL, + [lbday] [varchar] (250) NOT NULL, + [ctry] [text] NOT NULL, + [lctry] [varchar] (250) NOT NULL, + [locality] [text] NOT NULL, + [llocality] [varchar] (250) NOT NULL, + [email] [text] NOT NULL, + [lemail] [varchar] (250) NOT NULL, + [orgname] [text] NOT NULL, + [lorgname] [varchar] (250) NOT NULL, + [orgunit] [text] NOT NULL, + [lorgunit] [varchar] (250) NOT NULL, + CONSTRAINT [vcard_search_PRIMARY] PRIMARY KEY CLUSTERED +( + [server_host] ASC, + [lusername] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE INDEX [vcard_search_sh_lfn] ON [vcard_search] (server_host, lfn) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_lfamily] ON [vcard_search] (server_host, lfamily) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_lgiven] ON [vcard_search] (server_host, lgiven) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_lmiddle] ON [vcard_search] (server_host, lmiddle) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_lnickname] ON [vcard_search] (server_host, lnickname) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_lbday] ON [vcard_search] (server_host, lbday) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_lctry] ON [vcard_search] (server_host, lctry) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_llocality] ON [vcard_search] (server_host, llocality) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_lemail] ON [vcard_search] (server_host, lemail) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_lorgname] ON [vcard_search] (server_host, lorgname) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_lorgunit] ON [vcard_search] (server_host, lorgunit) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +ALTER TABLE [dbo].[pubsub_item] WITH CHECK ADD CONSTRAINT [pubsub_item_ibfk_1] FOREIGN KEY([nodeid]) +REFERENCES [dbo].[pubsub_node] ([nodeid]) +ON DELETE CASCADE; + +ALTER TABLE [dbo].[pubsub_item] CHECK CONSTRAINT [pubsub_item_ibfk_1]; + +ALTER TABLE [dbo].[pubsub_node_option] WITH CHECK ADD CONSTRAINT [pubsub_node_option_ibfk_1] FOREIGN KEY([nodeid]) +REFERENCES [dbo].[pubsub_node] ([nodeid]) +ON DELETE CASCADE; + +ALTER TABLE [dbo].[pubsub_node_option] CHECK CONSTRAINT [pubsub_node_option_ibfk_1]; + +ALTER TABLE [dbo].[pubsub_node_owner] WITH CHECK ADD CONSTRAINT [pubsub_node_owner_ibfk_1] FOREIGN KEY([nodeid]) +REFERENCES [dbo].[pubsub_node] ([nodeid]) +ON DELETE CASCADE; + +ALTER TABLE [dbo].[pubsub_node_owner] CHECK CONSTRAINT [pubsub_node_owner_ibfk_1]; + +ALTER TABLE [dbo].[pubsub_state] WITH CHECK ADD CONSTRAINT [pubsub_state_ibfk_1] FOREIGN KEY([nodeid]) +REFERENCES [dbo].[pubsub_node] ([nodeid]) +ON DELETE CASCADE; + +ALTER TABLE [dbo].[pubsub_state] CHECK CONSTRAINT [pubsub_state_ibfk_1]; + +CREATE TABLE [dbo].[oauth_token] ( + [token] [varchar] (250) NOT NULL, + [jid] [text] NOT NULL, + [scope] [text] NOT NULL, + [expire] [bigint] NOT NULL, + CONSTRAINT [oauth_token_PRIMARY] PRIMARY KEY CLUSTERED +( + [token] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE TABLE [dbo].[route] ( + [domain] [varchar] (255) NOT NULL, + [server_host] [varchar] (255) NOT NULL, + [node] [varchar] (255) NOT NULL, + [pid] [varchar](100) NOT NULL, + [local_hint] [text] NOT NULL +); + +CREATE UNIQUE CLUSTERED INDEX [route_i] ON [route] (domain, server_host, node, pid) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[bosh] ( + [sid] [varchar] (255) NOT NULL, + [node] [varchar] (255) NOT NULL, + [pid] [varchar](100) NOT NULL + CONSTRAINT [bosh_PRIMARY] PRIMARY KEY CLUSTERED +( + [sid] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +); + +CREATE TABLE [dbo].[push_session] ( + [username] [varchar] (255) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [timestamp] [bigint] NOT NULL, + [service] [varchar] (255) NOT NULL, + [node] [varchar] (255) NOT NULL, + [xml] [varchar] (255) NOT NULL +); + +CREATE UNIQUE NONCLUSTERED INDEX [push_session_susn] ON [push_session] (server_host, username, service, node) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [push_session_sh_username_timestamp] ON [push_session] (server_host, username, timestamp) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[mix_channel] ( + [channel] [varchar] (250) NOT NULL, + [service] [varchar] (250) NOT NULL, + [username] [varchar] (250) NOT NULL, + [domain] [varchar] (250) NOT NULL, + [jid] [varchar] (250) NOT NULL, + [hidden] [smallint] NOT NULL, + [hmac_key] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [mix_channel] ON [mix_channel] (channel, service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_channel_serv] ON [mix_channel] (service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[mix_participant] ( + [channel] [varchar] (250) NOT NULL, + [service] [varchar] (250) NOT NULL, + [username] [varchar] (250) NOT NULL, + [domain] [varchar] (250) NOT NULL, + [jid] [varchar] (250) NOT NULL, + [id] [text] NOT NULL, + [nick] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE INDEX [mix_participant] ON [mix_participant] (channel, service, username, domain) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_participant_chan_serv] ON [mix_participant] (channel, service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[mix_subscription] ( + [channel] [varchar] (250) NOT NULL, + [service] [varchar] (250) NOT NULL, + [username] [varchar] (250) NOT NULL, + [domain] [varchar] (250) NOT NULL, + [node] [varchar] (250) NOT NULL, + [jid] [varchar] (250) NOT NULL +); + +CREATE UNIQUE INDEX [mix_subscription] ON [mix_subscription] (channel, service, username, domain, node) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_subscription_chan_serv_ud] ON [mix_subscription] (channel, service, username, domain) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_subscription_chan_serv_node] ON [mix_subscription] (channel, service, node) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_subscription_chan_serv] ON [mix_subscription] (channel, service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[mix_pam] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [channel] [varchar] (250) NOT NULL, + [service] [varchar] (250) NOT NULL, + [id] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE NONCLUSTERED INDEX [mix_pam] ON [mix_pam] (username, server_host, channel, service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[mqtt_pub] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [resource] [varchar] (250) NOT NULL, + [topic] [varchar] (250) NOT NULL, + [qos] [tinyint] NOT NULL, + [payload] [varbinary](max) NOT NULL, + [payload_format] [tinyint] NOT NULL, + [content_type] [text] NOT NULL, + [response_topic] [text] NOT NULL, + [correlation_data] [varbinary](max) NOT NULL, + [user_properties] [varbinary](max) NOT NULL, + [expiry] [int] NOT NULL +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [mqtt_topic_server] ON [mqtt_pub] (topic, server_host) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); diff --git a/sql/mssql.sql b/sql/mssql.sql index 0dfaa7161..ab5596d48 100644 --- a/sql/mssql.sql +++ b/sql/mssql.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2015 ProcessOne +-- ejabberd, Copyright (C) 2002-2025 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as @@ -26,28 +26,32 @@ CREATE TABLE [dbo].[archive] ( [timestamp] [bigint] NOT NULL, [peer] [varchar] (250) NOT NULL, [bare_peer] [varchar] (250) NOT NULL, - [xml] [text] NOT NULL, - [txt] [text] NULL, + [xml] [ntext] NOT NULL, + [txt] [ntext] NULL, [id] [bigint] IDENTITY(1,1) NOT NULL, [kind] [varchar] (10) NULL, [nick] [varchar] (250) NULL, + [origin_id] [varchar] (250) NOT NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE(), - CONSTRAINT [archive_PK] PRIMARY KEY CLUSTERED + CONSTRAINT [archive_PK] PRIMARY KEY CLUSTERED ( [id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) TEXTIMAGE_ON [PRIMARY]; -CREATE INDEX [archive_username] ON [archive] (username) +CREATE INDEX [archive_username_timestamp] ON [archive] (username, timestamp) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [archive_username_peer] ON [archive] (username, peer) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [archive_username_bare_peer] ON [archive] (username, bare_peer) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [archive_timestamp] ON [archive] (timestamp) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); -CREATE INDEX [archive_peer] ON [archive] (peer) -WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); - -CREATE INDEX [archive_bare_peer] ON [archive] (bare_peer) +CREATE INDEX [archive_username_origin_id] ON [archive] (username, origin_id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[archive_prefs] ( @@ -56,7 +60,7 @@ CREATE TABLE [dbo].[archive_prefs] ( [always] [text] NOT NULL, [never] [text] NOT NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE(), - CONSTRAINT [archive_prefs_PRIMARY] PRIMARY KEY CLUSTERED + CONSTRAINT [archive_prefs_PRIMARY] PRIMARY KEY CLUSTERED ( [username] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) @@ -72,21 +76,11 @@ CREATE TABLE [dbo].[caps_features] ( CREATE CLUSTERED INDEX [caps_features_node_subnode] ON [caps_features] (node, subnode) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); -CREATE TABLE [dbo].[irc_custom] ( - [jid] [varchar] (255) NOT NULL, - [host] [varchar] (255) NOT NULL, - [data] [text] NOT NULL, - [created_at] [datetime] NOT NULL DEFAULT GETDATE() -) TEXTIMAGE_ON [PRIMARY]; - -CREATE UNIQUE CLUSTERED INDEX [irc_custom_jid_host] ON [irc_custom] (jid, host) -WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); - CREATE TABLE [dbo].[last] ( [username] [varchar] (250) NOT NULL, [seconds] [text] NOT NULL, [state] [text] NOT NULL, - CONSTRAINT [last_PRIMARY] PRIMARY KEY CLUSTERED + CONSTRAINT [last_PRIMARY] PRIMARY KEY CLUSTERED ( [username] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) @@ -96,7 +90,7 @@ CREATE TABLE [dbo].[motd] ( [username] [varchar] (250) NOT NULL, [xml] [text] NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE(), - CONSTRAINT [motd_PRIMARY] PRIMARY KEY CLUSTERED + CONSTRAINT [motd_PRIMARY] PRIMARY KEY CLUSTERED ( [username] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) @@ -124,11 +118,48 @@ CREATE TABLE [dbo].[muc_room] ( CREATE UNIQUE CLUSTERED INDEX [muc_room_name_host] ON [muc_room] (name, host) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); +CREATE INDEX [muc_room_host_created_at] ON [muc_registered] (host, nick) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[muc_online_room] ( + [name] [varchar] (250) NOT NULL, + [host] [varchar] (250) NOT NULL, + [node] [varchar] (250) NOT NULL, + [pid] [varchar] (100) NOT NULL +); + +CREATE UNIQUE CLUSTERED INDEX [muc_online_room_name_host] ON [muc_online_room] (name, host) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[muc_online_users] ( + [username] [varchar] (250) NOT NULL, + [server] [varchar] (250) NOT NULL, + [resource] [varchar] (250) NOT NULL, + [name] [varchar] (250) NOT NULL, + [host] [varchar] (250) NOT NULL, + [node] [varchar] (250) NOT NULL +); + +CREATE UNIQUE INDEX [muc_online_users_i] ON [muc_online_users] (username, server, resource, name, host) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[muc_room_subscribers] ( + [room] [varchar] (191) NOT NULL, + [host] [varchar] (191) NOT NULL, + [jid] [varchar] (191) NOT NULL, + [nick] [text] NOT NULL, + [nodes] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +); + +CREATE UNIQUE CLUSTERED INDEX [muc_room_subscribers_host_room_jid] ON [muc_room_subscribers] (host, room, jid); +CREATE INDEX [muc_room_subscribers_host_jid] ON [muc_room_subscribers] (host, jid); +CREATE INDEX [muc_room_subscribers_jid] ON [muc_room_subscribers] (jid); CREATE TABLE [dbo].[privacy_default_list] ( [username] [varchar] (250) NOT NULL, [name] [varchar] (250) NOT NULL, - CONSTRAINT [privacy_default_list_PRIMARY] PRIMARY KEY CLUSTERED + CONSTRAINT [privacy_default_list_PRIMARY] PRIMARY KEY CLUSTERED ( [username] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) @@ -139,15 +170,12 @@ CREATE TABLE [dbo].[privacy_list] ( [name] [varchar] (250) NOT NULL, [id] [bigint] IDENTITY(1,1) NOT NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE(), - CONSTRAINT [privacy_list_PK] PRIMARY KEY CLUSTERED + CONSTRAINT [privacy_list_PK] PRIMARY KEY CLUSTERED ( [id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ); -CREATE INDEX [privacy_list_username] ON [privacy_list] (username) -WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); - CREATE UNIQUE INDEX [privacy_list_username_name] ON [privacy_list] (username, name) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); @@ -174,19 +202,16 @@ CREATE TABLE [dbo].[private_storage] ( [created_at] [datetime] NOT NULL DEFAULT GETDATE() ) TEXTIMAGE_ON [PRIMARY]; -CREATE INDEX [private_storage_username] ON [private_storage] (username) -WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); - CREATE UNIQUE CLUSTERED INDEX [private_storage_username_namespace] ON [private_storage] (username, namespace) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[pubsub_item] ( [nodeid] [bigint] NULL, - [itemid] [varchar] (255) NULL, - [publisher] [text] NULL, - [creation] [text] NULL, - [modification] [varchar] (255) NULL, - [payload] [text] NULL + [itemid] [varchar] (255) NOT NULL, + [publisher] [varchar] (250) NOT NULL, + [creation] [varchar] (32) NOT NULL, + [modification] [varchar] (32) NOT NULL, + [payload] [text] NOT NULL DEFAULT '' ) TEXTIMAGE_ON [PRIMARY]; CREATE INDEX [pubsub_item_itemid] ON [pubsub_item] (itemid) @@ -197,16 +222,16 @@ WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW CREATE TABLE [dbo].[pubsub_node_option] ( [nodeid] [bigint] NULL, - [name] [text] NULL, - [val] [text] NULL -) TEXTIMAGE_ON [PRIMARY]; + [name] [varchar] (250) NOT NULL, + [val] [varchar] (250) NOT NULL +); CREATE CLUSTERED INDEX [pubsub_node_option_nodeid] ON [pubsub_node_option] (nodeid) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[pubsub_node_owner] ( [nodeid] [bigint] NULL, - [owner] [text] NULL + [owner] [text] NOT NULL ) TEXTIMAGE_ON [PRIMARY]; CREATE CLUSTERED INDEX [pubsub_node_owner_nodeid] ON [pubsub_node_owner] (nodeid) @@ -214,11 +239,11 @@ WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW CREATE TABLE [dbo].[pubsub_state] ( [nodeid] [bigint] NULL, - [jid] [varchar] (255) NULL, - [affiliation] [char] (1) NULL, - [subscriptions] [text] NULL, + [jid] [varchar] (255) NOT NULL, + [affiliation] [char] (1) NOT NULL, + [subscriptions] [text] NOT NULL DEFAULT '', [stateid] [bigint] IDENTITY(1,1) NOT NULL, - CONSTRAINT [pubsub_state_PRIMARY] PRIMARY KEY CLUSTERED + CONSTRAINT [pubsub_state_PRIMARY] PRIMARY KEY CLUSTERED ( [stateid] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) @@ -231,25 +256,25 @@ CREATE UNIQUE INDEX [pubsub_state_nodeid_jid] ON [pubsub_state] (nodeid, jid) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[pubsub_subscription_opt] ( - [subid] [varchar] (255) NULL, - [opt_name] [varchar] (32) NULL, - [opt_value] [text] NULL + [subid] [varchar] (255) NOT NULL, + [opt_name] [varchar] (32) NOT NULL, + [opt_value] [text] NOT NULL ) TEXTIMAGE_ON [PRIMARY]; CREATE UNIQUE CLUSTERED INDEX [pubsub_subscription_opt_subid_opt_name] ON [pubsub_subscription_opt] (subid, opt_name) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[pubsub_node] ( - [host] [varchar] (255) NULL, - [node] [varchar] (255) NULL, - [parent] [varchar] (255) NULL, - [type] [text] NULL, + [host] [varchar] (255) NOT NULL, + [node] [varchar] (255) NOT NULL, + [parent] [varchar] (255) NOT NULL DEFAULT '', + [plugin] [varchar] (32) NOT NULL, [nodeid] [bigint] IDENTITY(1,1) NOT NULL, - CONSTRAINT [pubsub_node_PRIMARY] PRIMARY KEY CLUSTERED + CONSTRAINT [pubsub_node_PRIMARY] PRIMARY KEY CLUSTERED ( [nodeid] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) -) TEXTIMAGE_ON [PRIMARY]; +); CREATE INDEX [pubsub_node_parent] ON [pubsub_node] (parent) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); @@ -260,7 +285,7 @@ WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW CREATE TABLE [dbo].[roster_version] ( [username] [varchar] (250) NOT NULL, [version] [text] NOT NULL, - CONSTRAINT [roster_version_PRIMARY] PRIMARY KEY CLUSTERED + CONSTRAINT [roster_version_PRIMARY] PRIMARY KEY CLUSTERED ( [username] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) @@ -291,9 +316,6 @@ CREATE TABLE [dbo].[rosterusers] ( CREATE UNIQUE CLUSTERED INDEX [rosterusers_username_jid] ON [rosterusers] ([username], [jid]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); -CREATE INDEX [rosterusers_username] ON [rosterusers] ([username]) -WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); - CREATE INDEX [rosterusers_jid] ON [rosterusers] ([jid]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); @@ -321,7 +343,7 @@ CREATE TABLE [dbo].[spool] ( [xml] [text] NOT NULL, [seq] [bigint] IDENTITY(1,1) NOT NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE(), - CONSTRAINT [spool_PK] PRIMARY KEY CLUSTERED + CONSTRAINT [spool_PK] PRIMARY KEY CLUSTERED ( [seq] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) @@ -337,13 +359,12 @@ WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW CREATE TABLE [dbo].[sr_group] ( [name] [varchar] (250) NOT NULL, [opts] [text] NOT NULL, - [created_at] [datetime] NOT NULL DEFAULT GETDATE(), - CONSTRAINT [sr_group_PRIMARY] PRIMARY KEY CLUSTERED -( - [name] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + [created_at] [datetime] NOT NULL DEFAULT GETDATE() ) TEXTIMAGE_ON [PRIMARY]; +CREATE UNIQUE CLUSTERED INDEX [sr_group_name] ON [sr_group] ([name]) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + CREATE TABLE [dbo].[sr_user] ( [jid] [varchar] (250) NOT NULL, [grp] [varchar] (250) NOT NULL, @@ -353,22 +374,21 @@ CREATE TABLE [dbo].[sr_user] ( CREATE UNIQUE CLUSTERED INDEX [sr_user_jid_group] ON [sr_user] ([jid], [grp]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); -CREATE INDEX [sr_user_jid] ON [sr_user] ([jid]) -WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); - CREATE INDEX [sr_user_grp] ON [sr_user] ([grp]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[users] ( [username] [varchar] (250) NOT NULL, + [type] [smallint] NOT NULL, [password] [text] NOT NULL, [serverkey] [text] NOT NULL DEFAULT '', [salt] [text] NOT NULL DEFAULT '', [iterationcount] [smallint] NOT NULL DEFAULT 0, [created_at] [datetime] NOT NULL DEFAULT GETDATE(), - CONSTRAINT [users_PRIMARY] PRIMARY KEY CLUSTERED + CONSTRAINT [users_PRIMARY] PRIMARY KEY CLUSTERED ( - [username] ASC + [username] ASC, + [type] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) TEXTIMAGE_ON [PRIMARY]; @@ -376,7 +396,7 @@ CREATE TABLE [dbo].[vcard] ( [username] [varchar] (250) NOT NULL, [vcard] [text] NOT NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE(), - CONSTRAINT [vcard_PRIMARY] PRIMARY KEY CLUSTERED + CONSTRAINT [vcard_PRIMARY] PRIMARY KEY CLUSTERED ( [username] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) @@ -407,7 +427,7 @@ CREATE TABLE [dbo].[vcard_search] ( [lorgname] [varchar] (250) NOT NULL, [orgunit] [text] NOT NULL, [lorgunit] [varchar] (250) NOT NULL, - CONSTRAINT [vcard_search_PRIMARY] PRIMARY KEY CLUSTERED + CONSTRAINT [vcard_search_PRIMARY] PRIMARY KEY CLUSTERED ( [lusername] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) @@ -446,16 +466,6 @@ WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW CREATE INDEX [vcard_search_lorgunit] ON [vcard_search] (lorgunit) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); -CREATE TABLE [dbo].[vcard_xupdate] ( - [username] [varchar] (250) NOT NULL, - [hash] [text] NOT NULL, - [created_at] [datetime] NOT NULL DEFAULT GETDATE(), - CONSTRAINT [vcard_xupdate_PRIMARY] PRIMARY KEY CLUSTERED -( - [username] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) -) TEXTIMAGE_ON [PRIMARY]; - ALTER TABLE [dbo].[pubsub_item] WITH CHECK ADD CONSTRAINT [pubsub_item_ibfk_1] FOREIGN KEY([nodeid]) REFERENCES [dbo].[pubsub_node] ([nodeid]) ON DELETE CASCADE; @@ -485,8 +495,126 @@ CREATE TABLE [dbo].[oauth_token] ( [jid] [text] NOT NULL, [scope] [text] NOT NULL, [expire] [bigint] NOT NULL, - CONSTRAINT [oauth_token_PRIMARY] PRIMARY KEY CLUSTERED + CONSTRAINT [oauth_token_PRIMARY] PRIMARY KEY CLUSTERED ( [token] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) TEXTIMAGE_ON [PRIMARY]; + +CREATE TABLE [dbo].[route] ( + [domain] [varchar] (255) NOT NULL, + [server_host] [varchar] (255) NOT NULL, + [node] [varchar] (255) NOT NULL, + [pid] [varchar](100) NOT NULL, + [local_hint] [text] NOT NULL +); + +CREATE UNIQUE CLUSTERED INDEX [route_i] ON [route] (domain, server_host, node, pid) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[bosh] ( + [sid] [varchar] (255) NOT NULL, + [node] [varchar] (255) NOT NULL, + [pid] [varchar](100) NOT NULL + CONSTRAINT [bosh_PRIMARY] PRIMARY KEY CLUSTERED +( + [sid] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +); + +CREATE TABLE [dbo].[push_session] ( + [username] [varchar] (255) NOT NULL, + [timestamp] [bigint] NOT NULL, + [service] [varchar] (255) NOT NULL, + [node] [varchar] (255) NOT NULL, + [xml] [varchar] (255) NOT NULL +); + +CREATE UNIQUE CLUSTERED INDEX [i_push_usn] ON [push_session] (username, service, node) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [i_push_ut] ON [push_session] (username, timestamp) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[mix_channel] ( + [channel] [varchar] (250) NOT NULL, + [service] [varchar] (250) NOT NULL, + [username] [varchar] (250) NOT NULL, + [domain] [varchar] (250) NOT NULL, + [jid] [varchar] (250) NOT NULL, + [hidden] [smallint] NOT NULL, + [hmac_key] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [mix_channel] ON [mix_channel] (channel, service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_channel_serv] ON [mix_channel] (service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[mix_participant] ( + [channel] [varchar] (250) NOT NULL, + [service] [varchar] (250) NOT NULL, + [username] [varchar] (250) NOT NULL, + [domain] [varchar] (250) NOT NULL, + [jid] [varchar] (250) NOT NULL, + [id] [text] NOT NULL, + [nick] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE INDEX [mix_participant] ON [mix_participant] (channel, service, username, domain) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_participant_chan_serv] ON [mix_participant] (channel, service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[mix_subscription] ( + [channel] [varchar] (250) NOT NULL, + [service] [varchar] (250) NOT NULL, + [username] [varchar] (250) NOT NULL, + [domain] [varchar] (250) NOT NULL, + [node] [varchar] (250) NOT NULL, + [jid] [varchar] (250) NOT NULL +); + +CREATE UNIQUE INDEX [mix_subscription] ON [mix_subscription] (channel, service, username, domain, node) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_subscription_chan_serv_ud] ON [mix_subscription] (channel, service, username, domain) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_subscription_chan_serv_node] ON [mix_subscription] (channel, service, node) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_subscription_chan_serv] ON [mix_subscription] (channel, service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[mix_pam] ( + [username] [varchar] (250) NOT NULL, + [channel] [varchar] (250) NOT NULL, + [service] [varchar] (250) NOT NULL, + [id] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [mix_pam] ON [mix_pam] (username, channel, service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[mqtt_pub] ( + [username] [varchar] (250) NOT NULL, + [resource] [varchar] (250) NOT NULL, + [topic] [varchar] (250) NOT NULL, + [qos] [tinyint] NOT NULL, + [payload] [varbinary](max) NOT NULL, + [payload_format] [tinyint] NOT NULL, + [content_type] [text] NOT NULL, + [response_topic] [text] NOT NULL, + [correlation_data] [varbinary](max) NOT NULL, + [user_properties] [varbinary](max) NOT NULL, + [expiry] [int] NOT NULL +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [mqtt_topic] ON [mqtt_pub] (topic) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); diff --git a/sql/mysql.new.sql b/sql/mysql.new.sql new file mode 100644 index 000000000..cf818ad3d --- /dev/null +++ b/sql/mysql.new.sql @@ -0,0 +1,509 @@ +-- +-- ejabberd, Copyright (C) 2002-2025 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. +-- + +CREATE TABLE users ( + username varchar(191) NOT NULL, + type smallint NOT NULL, + server_host varchar(191) NOT NULL, + password text NOT NULL, + serverkey varchar(128) NOT NULL DEFAULT '', + salt varchar(128) NOT NULL DEFAULT '', + iterationcount integer NOT NULL DEFAULT 0, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (server_host(191), username, type) +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- Add support for SCRAM auth to a database created before ejabberd 16.03: +-- ALTER TABLE users ADD COLUMN serverkey varchar(64) NOT NULL DEFAULT ''; +-- ALTER TABLE users ADD COLUMN salt varchar(64) NOT NULL DEFAULT ''; +-- ALTER TABLE users ADD COLUMN iterationcount integer NOT NULL DEFAULT 0; + +CREATE TABLE last ( + username varchar(191) NOT NULL, + server_host varchar(191) NOT NULL, + seconds text NOT NULL, + state text NOT NULL, + PRIMARY KEY (server_host(191), username) +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + + +CREATE TABLE rosterusers ( + username varchar(191) NOT NULL, + server_host varchar(191) NOT NULL, + jid varchar(191) NOT NULL, + nick text NOT NULL, + subscription character(1) NOT NULL, + ask character(1) NOT NULL, + askmessage text NOT NULL, + server character(1) NOT NULL, + subscribe text NOT NULL, + type text, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_rosteru_sh_user_jid ON rosterusers(server_host(191), username(75), jid(75)); +CREATE INDEX i_rosteru_sh_jid ON rosterusers(server_host(191), jid); + +CREATE TABLE rostergroups ( + username varchar(191) NOT NULL, + server_host varchar(191) NOT NULL, + jid varchar(191) NOT NULL, + grp text NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE INDEX i_rosterg_sh_user_jid ON rostergroups(server_host(191), username(75), jid(75)); + +CREATE TABLE sr_group ( + name varchar(191) NOT NULL, + server_host varchar(191) NOT NULL, + opts text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (server_host(191), name) +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_sr_group_sh_name ON sr_group(server_host(191), name); + +CREATE TABLE sr_user ( + jid varchar(191) NOT NULL, + server_host varchar(191) NOT NULL, + grp varchar(191) NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (server_host(191), jid, grp) +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_sr_user_sh_jid_grp ON sr_user(server_host(191), jid, grp); +CREATE INDEX i_sr_user_sh_grp ON sr_user(server_host(191), grp); + +CREATE TABLE spool ( + username varchar(191) NOT NULL, + server_host varchar(191) NOT NULL, + xml mediumtext NOT NULL, + seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE INDEX i_spool_sh_username USING BTREE ON spool(server_host(191), username); +CREATE INDEX i_spool_created_at USING BTREE ON spool(created_at); + +CREATE TABLE archive ( + username varchar(191) NOT NULL, + server_host varchar(191) NOT NULL, + timestamp BIGINT UNSIGNED NOT NULL, + peer varchar(191) NOT NULL, + bare_peer varchar(191) NOT NULL, + xml mediumtext NOT NULL, + txt mediumtext, + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, + kind varchar(10), + nick varchar(191), + origin_id varchar(191), + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE FULLTEXT INDEX i_text ON archive(txt); +CREATE INDEX i_archive_sh_username_timestamp USING BTREE ON archive(server_host(191), username(191), timestamp); +CREATE INDEX i_archive_sh_username_peer USING BTREE ON archive(server_host(191), username(191), peer(191)); +CREATE INDEX i_archive_sh_username_bare_peer USING BTREE ON archive(server_host(191), username(191), bare_peer(191)); +CREATE INDEX i_archive_sh_timestamp USING BTREE ON archive(server_host(191), timestamp); +CREATE INDEX i_archive_sh_username_origin_id USING BTREE ON archive(server_host(191), username(191), origin_id(191)); + +-- To update 'archive' from ejabberd <= 23.10: +-- ALTER TABLE archive ADD COLUMN origin_id varchar(191) NOT NULL DEFAULT ''; +-- ALTER TABLE archive ALTER COLUMN origin_id DROP DEFAULT; +-- CREATE INDEX i_archive_sh_username_origin_id USING BTREE ON archive(server_host(191), username(191), origin_id(191)); + +CREATE TABLE archive_prefs ( + username varchar(191) NOT NULL, + server_host varchar(191) NOT NULL, + def text NOT NULL, + always text NOT NULL, + never text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (server_host(191), username) +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE TABLE vcard ( + username varchar(191) NOT NULL, + server_host varchar(191) NOT NULL, + vcard mediumtext NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (server_host(191), username) +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE TABLE vcard_search ( + username varchar(191) NOT NULL, + lusername varchar(191) NOT NULL, + server_host varchar(191) NOT NULL, + fn text NOT NULL, + lfn varchar(191) NOT NULL, + family text NOT NULL, + lfamily varchar(191) NOT NULL, + given text NOT NULL, + lgiven varchar(191) NOT NULL, + middle text NOT NULL, + lmiddle varchar(191) NOT NULL, + nickname text NOT NULL, + lnickname varchar(191) NOT NULL, + bday text NOT NULL, + lbday varchar(191) NOT NULL, + ctry text NOT NULL, + lctry varchar(191) NOT NULL, + locality text NOT NULL, + llocality varchar(191) NOT NULL, + email text NOT NULL, + lemail varchar(191) NOT NULL, + orgname text NOT NULL, + lorgname varchar(191) NOT NULL, + orgunit text NOT NULL, + lorgunit varchar(191) NOT NULL, + PRIMARY KEY (server_host(191), lusername) +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE INDEX i_vcard_search_sh_lfn ON vcard_search(server_host(191), lfn); +CREATE INDEX i_vcard_search_sh_lfamily ON vcard_search(server_host(191), lfamily); +CREATE INDEX i_vcard_search_sh_lgiven ON vcard_search(server_host(191), lgiven); +CREATE INDEX i_vcard_search_sh_lmiddle ON vcard_search(server_host(191), lmiddle); +CREATE INDEX i_vcard_search_sh_lnickname ON vcard_search(server_host(191), lnickname); +CREATE INDEX i_vcard_search_sh_lbday ON vcard_search(server_host(191), lbday); +CREATE INDEX i_vcard_search_sh_lctry ON vcard_search(server_host(191), lctry); +CREATE INDEX i_vcard_search_sh_llocality ON vcard_search(server_host(191), llocality); +CREATE INDEX i_vcard_search_sh_lemail ON vcard_search(server_host(191), lemail); +CREATE INDEX i_vcard_search_sh_lorgname ON vcard_search(server_host(191), lorgname); +CREATE INDEX i_vcard_search_sh_lorgunit ON vcard_search(server_host(191), lorgunit); + +CREATE TABLE privacy_default_list ( + username varchar(191) NOT NULL, + server_host varchar(191) NOT NULL, + name varchar(191) NOT NULL, + PRIMARY KEY (server_host(191), username) +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE TABLE privacy_list ( + username varchar(191) NOT NULL, + server_host varchar(191) NOT NULL, + name varchar(191) NOT NULL, + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_privacy_list_sh_username_name USING BTREE ON privacy_list (server_host(191), username(75), name(75)); + +CREATE TABLE privacy_list_data ( + id bigint, + t character(1) NOT NULL, + value text NOT NULL, + action character(1) NOT NULL, + ord NUMERIC NOT NULL, + match_all boolean NOT NULL, + match_iq boolean NOT NULL, + match_message boolean NOT NULL, + match_presence_in boolean NOT NULL, + match_presence_out boolean NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE INDEX i_privacy_list_data_id ON privacy_list_data(id); + +CREATE TABLE private_storage ( + username varchar(191) NOT NULL, + server_host varchar(191) NOT NULL, + namespace varchar(191) NOT NULL, + data text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_private_storage_sh_sername_namespace USING BTREE ON private_storage(server_host(191), username, namespace); + +-- Not tested in mysql +CREATE TABLE roster_version ( + username varchar(191) NOT NULL, + server_host varchar(191) NOT NULL, + version text NOT NULL, + PRIMARY KEY (server_host(191), username) +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- To update from 1.x: +-- ALTER TABLE rosterusers ADD COLUMN askmessage text AFTER ask; +-- UPDATE rosterusers SET askmessage = ''; +-- ALTER TABLE rosterusers ALTER COLUMN askmessage SET NOT NULL; + +CREATE TABLE pubsub_node ( + host text NOT NULL, + node text NOT NULL, + parent VARCHAR(191) NOT NULL DEFAULT '', + plugin text NOT NULL, + nodeid bigint auto_increment primary key +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +CREATE INDEX i_pubsub_node_parent ON pubsub_node(parent(120)); +CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node(host(71), node(120)); + +CREATE TABLE pubsub_node_option ( + nodeid bigint, + name text NOT NULL, + val text NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option(nodeid); +ALTER TABLE `pubsub_node_option` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE; + +CREATE TABLE pubsub_node_owner ( + nodeid bigint, + owner text NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner(nodeid); +ALTER TABLE `pubsub_node_owner` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE; + +CREATE TABLE pubsub_state ( + nodeid bigint, + jid text NOT NULL, + affiliation character(1), + subscriptions VARCHAR(191) NOT NULL DEFAULT '', + stateid bigint auto_increment primary key +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +CREATE INDEX i_pubsub_state_jid ON pubsub_state(jid(60)); +CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state(nodeid, jid(60)); +ALTER TABLE `pubsub_state` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE; + +CREATE TABLE pubsub_item ( + nodeid bigint, + itemid text NOT NULL, + publisher text NOT NULL, + creation varchar(32) NOT NULL, + modification varchar(32) NOT NULL, + payload mediumtext NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +CREATE INDEX i_pubsub_item_itemid ON pubsub_item(itemid(36)); +CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item(nodeid, itemid(36)); +ALTER TABLE `pubsub_item` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE; + +CREATE TABLE pubsub_subscription_opt ( + subid text NOT NULL, + opt_name varchar(32), + opt_value text NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt(subid(32), opt_name(32)); + +CREATE TABLE muc_room ( + name text NOT NULL, + host text NOT NULL, + server_host varchar(191) NOT NULL, + opts mediumtext NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_muc_room_name_host USING BTREE ON muc_room(name(75), host(75)); +CREATE INDEX i_muc_room_host_created_at ON muc_room(host(75), created_at); + +CREATE TABLE muc_registered ( + jid text NOT NULL, + host text NOT NULL, + server_host varchar(191) NOT NULL, + nick text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE INDEX i_muc_registered_nick USING BTREE ON muc_registered(nick(75)); +CREATE UNIQUE INDEX i_muc_registered_jid_host USING BTREE ON muc_registered(jid(75), host(75)); + +CREATE TABLE muc_online_room ( + name text NOT NULL, + host text NOT NULL, + server_host varchar(191) NOT NULL, + node text NOT NULL, + pid text NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_muc_online_room_name_host USING BTREE ON muc_online_room(name(75), host(75)); + +CREATE TABLE muc_online_users ( + username text NOT NULL, + server text NOT NULL, + resource text NOT NULL, + name text NOT NULL, + host text NOT NULL, + server_host varchar(191) NOT NULL, + node text NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_muc_online_users USING BTREE ON muc_online_users(username(75), server(75), resource(75), name(75), host(75)); + +CREATE TABLE muc_room_subscribers ( + room varchar(191) NOT NULL, + host varchar(191) NOT NULL, + jid varchar(191) NOT NULL, + nick text NOT NULL, + nodes text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + UNIQUE KEY i_muc_room_subscribers_host_room_jid (host, room, jid) +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE INDEX i_muc_room_subscribers_host_jid USING BTREE ON muc_room_subscribers(host, jid); +CREATE INDEX i_muc_room_subscribers_jid USING BTREE ON muc_room_subscribers(jid); + +CREATE TABLE motd ( + username varchar(191) NOT NULL, + server_host varchar(191) NOT NULL, + xml text, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (server_host(191), username) +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE TABLE caps_features ( + node varchar(191) NOT NULL, + subnode varchar(191) NOT NULL, + feature text, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE INDEX i_caps_features_node_subnode ON caps_features(node(75), subnode(75)); + +CREATE TABLE sm ( + usec bigint NOT NULL, + pid text NOT NULL, + node text NOT NULL, + username varchar(191) NOT NULL, + server_host varchar(191) NOT NULL, + resource varchar(191) NOT NULL, + priority text NOT NULL, + info text NOT NULL, + PRIMARY KEY (usec, pid(75)) +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE INDEX i_sm_node ON sm(node(75)); +CREATE INDEX i_sm_sh_username ON sm(server_host(191), username); + +CREATE TABLE oauth_token ( + token varchar(191) NOT NULL PRIMARY KEY, + jid text NOT NULL, + scope text NOT NULL, + expire bigint NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE TABLE oauth_client ( + client_id varchar(191) NOT NULL PRIMARY KEY, + client_name text NOT NULL, + grant_type text NOT NULL, + options text NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE TABLE route ( + domain text NOT NULL, + server_host varchar(191) NOT NULL, + node text NOT NULL, + pid text NOT NULL, + local_hint text NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_route ON route(domain(75), server_host(75), node(75), pid(75)); + +CREATE TABLE bosh ( + sid text NOT NULL, + node text NOT NULL, + pid text NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid(75)); + +CREATE TABLE proxy65 ( + sid text NOT NULL, + pid_t text NOT NULL, + pid_i text NOT NULL, + node_t text NOT NULL, + node_i text NOT NULL, + jid_i text NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 (sid(191)); +CREATE INDEX i_proxy65_jid ON proxy65 (jid_i(191)); + +CREATE TABLE push_session ( + username text NOT NULL, + server_host varchar(191) NOT NULL, + timestamp bigint NOT NULL, + service text NOT NULL, + node text NOT NULL, + xml text NOT NULL, + PRIMARY KEY (server_host(191), username(191), timestamp) +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_push_session_susn ON push_session (server_host(191), username(191), service(191), node(191)); +CREATE INDEX i_push_session_sh_username_timestamp ON push_session (server_host, username(191), timestamp); + +CREATE TABLE mix_channel ( + channel text NOT NULL, + service text NOT NULL, + username text NOT NULL, + domain text NOT NULL, + jid text NOT NULL, + hidden boolean NOT NULL, + hmac_key text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_mix_channel ON mix_channel (channel(191), service(191)); +CREATE INDEX i_mix_channel_serv ON mix_channel (service(191)); + +CREATE TABLE mix_participant ( + channel text NOT NULL, + service text NOT NULL, + username text NOT NULL, + domain text NOT NULL, + jid text NOT NULL, + id text NOT NULL, + nick text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_mix_participant ON mix_participant (channel(191), service(191), username(191), domain(191)); + +CREATE TABLE mix_subscription ( + channel text NOT NULL, + service text NOT NULL, + username text NOT NULL, + domain text NOT NULL, + node text NOT NULL, + jid text NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_mix_subscription ON mix_subscription (channel(153), service(153), username(153), domain(153), node(153)); +CREATE INDEX i_mix_subscription_chan_serv_node ON mix_subscription (channel(191), service(191), node(191)); + +CREATE TABLE mix_pam ( + username text NOT NULL, + server_host varchar(191) NOT NULL, + channel text NOT NULL, + service text NOT NULL, + id text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username(191), server_host(191), channel(191), service(191)); + +CREATE TABLE mqtt_pub ( + username varchar(191) NOT NULL, + server_host varchar(191) NOT NULL, + resource varchar(191) NOT NULL, + topic text NOT NULL, + qos tinyint NOT NULL, + payload blob NOT NULL, + payload_format tinyint NOT NULL, + content_type text NOT NULL, + response_topic text NOT NULL, + correlation_data blob NOT NULL, + user_properties blob NOT NULL, + expiry int unsigned NOT NULL, + UNIQUE KEY i_mqtt_topic_server (topic(191), server_host) +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; diff --git a/sql/mysql.old-to-new.sql b/sql/mysql.old-to-new.sql new file mode 100644 index 000000000..a58a90a46 --- /dev/null +++ b/sql/mysql.old-to-new.sql @@ -0,0 +1,157 @@ +SET @DEFAULT_HOST = ''; -- Please fill with name of your current host name + +BEGIN; +DELIMITER ## +CREATE PROCEDURE update_server_host(IN DEFAULT_HOST TEXT) +BEGIN + START TRANSACTION; + SET FOREIGN_KEY_CHECKS = 0; + SET @DEFAULT_HOST = DEFAULT_HOST; + IF DEFAULT_HOST = '' THEN + SELECT 'Please fill @DEFAULT_HOST parameter' as Error; + ROLLBACK; + ELSE + ALTER TABLE `push_session` DROP INDEX `i_push_usn`; + ALTER TABLE `push_session` DROP INDEX `i_push_ut`; + ALTER TABLE `push_session` ADD COLUMN `server_host` VARCHAR (191) NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; + ALTER TABLE `push_session` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `push_session` ADD PRIMARY KEY (`server_host`, `username`(191), `timestamp`); + ALTER TABLE `push_session` ADD UNIQUE INDEX `i_push_session_susn` (`server_host`, `username`(191), `service`(191), `node`(191)); + ALTER TABLE `push_session` ADD INDEX `i_push_session_sh_username_timestamp` (`server_host`, `username`(191), `timestamp`); + ALTER TABLE `roster_version` DROP PRIMARY KEY; + ALTER TABLE `roster_version` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; + ALTER TABLE `roster_version` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `roster_version` ADD PRIMARY KEY (`server_host`, `username`); + ALTER TABLE `muc_online_room` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `host`; + ALTER TABLE `muc_online_room` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `motd` DROP PRIMARY KEY; + ALTER TABLE `motd` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; + ALTER TABLE `motd` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `motd` ADD PRIMARY KEY (`server_host`, `username`); + ALTER TABLE `rosterusers` DROP INDEX `i_rosteru_username`; + ALTER TABLE `rosterusers` DROP INDEX `i_rosteru_jid`; + ALTER TABLE `rosterusers` DROP INDEX `i_rosteru_user_jid`; + ALTER TABLE `rosterusers` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; + ALTER TABLE `rosterusers` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `rosterusers` ADD UNIQUE INDEX `i_rosteru_sh_user_jid` (`server_host`, `username`(75), `jid`(75)); + ALTER TABLE `rosterusers` ADD INDEX `i_rosteru_sh_jid` (`server_host`, `jid`); + ALTER TABLE `private_storage` DROP INDEX `i_private_storage_username_namespace`; + ALTER TABLE `private_storage` DROP INDEX `i_private_storage_username`; + ALTER TABLE `private_storage` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; + ALTER TABLE `private_storage` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `private_storage` ADD PRIMARY KEY (`server_host`, `username`, `namespace`); + ALTER TABLE `mqtt_pub` DROP INDEX `i_mqtt_topic`; + ALTER TABLE `mqtt_pub` ADD COLUMN `server_host` VARCHAR (191) NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; + ALTER TABLE `mqtt_pub` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `mqtt_pub` ADD UNIQUE INDEX `i_mqtt_topic_server` (`topic`(191), `server_host`); + ALTER TABLE `vcard_search` DROP PRIMARY KEY; + ALTER TABLE `vcard_search` DROP INDEX `i_vcard_search_lgiven`; + ALTER TABLE `vcard_search` DROP INDEX `i_vcard_search_lmiddle`; + ALTER TABLE `vcard_search` DROP INDEX `i_vcard_search_lnickname`; + ALTER TABLE `vcard_search` DROP INDEX `i_vcard_search_lbday`; + ALTER TABLE `vcard_search` DROP INDEX `i_vcard_search_lctry`; + ALTER TABLE `vcard_search` DROP INDEX `i_vcard_search_lfn`; + ALTER TABLE `vcard_search` DROP INDEX `i_vcard_search_lemail`; + ALTER TABLE `vcard_search` DROP INDEX `i_vcard_search_lorgunit`; + ALTER TABLE `vcard_search` DROP INDEX `i_vcard_search_llocality`; + ALTER TABLE `vcard_search` DROP INDEX `i_vcard_search_lorgname`; + ALTER TABLE `vcard_search` DROP INDEX `i_vcard_search_lfamily`; + ALTER TABLE `vcard_search` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `lusername`; + ALTER TABLE `vcard_search` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `vcard_search` ADD INDEX `i_vcard_search_sh_lfn` (`server_host`, `lfn`); + ALTER TABLE `vcard_search` ADD INDEX `i_vcard_search_sh_llocality` (`server_host`, `llocality`); + ALTER TABLE `vcard_search` ADD PRIMARY KEY (`server_host`, `lusername`); + ALTER TABLE `vcard_search` ADD INDEX `i_vcard_search_sh_lnickname` (`server_host`, `lnickname`); + ALTER TABLE `vcard_search` ADD INDEX `i_vcard_search_sh_lctry` (`server_host`, `lctry`); + ALTER TABLE `vcard_search` ADD INDEX `i_vcard_search_sh_lgiven` (`server_host`, `lgiven`); + ALTER TABLE `vcard_search` ADD INDEX `i_vcard_search_sh_lmiddle` (`server_host`, `lmiddle`); + ALTER TABLE `vcard_search` ADD INDEX `i_vcard_search_sh_lorgname` (`server_host`, `lorgname`); + ALTER TABLE `vcard_search` ADD INDEX `i_vcard_search_sh_lfamily` (`server_host`, `lfamily`); + ALTER TABLE `vcard_search` ADD INDEX `i_vcard_search_sh_lbday` (`server_host`, `lbday`); + ALTER TABLE `vcard_search` ADD INDEX `i_vcard_search_sh_lemail` (`server_host`, `lemail`); + ALTER TABLE `vcard_search` ADD INDEX `i_vcard_search_sh_lorgunit` (`server_host`, `lorgunit`); + ALTER TABLE `last` DROP PRIMARY KEY; + ALTER TABLE `last` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; + ALTER TABLE `last` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `last` ADD PRIMARY KEY (`server_host`, `username`); + ALTER TABLE `sr_group` DROP INDEX `i_sr_group_name`; + ALTER TABLE `sr_group` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `name`; + ALTER TABLE `sr_group` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `sr_group` ADD UNIQUE INDEX `i_sr_group_sh_name` (`server_host`, `name`); + ALTER TABLE `muc_registered` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `host`; + ALTER TABLE `muc_registered` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `sm` DROP INDEX `i_node`; + ALTER TABLE `sm` DROP INDEX `i_username`; + ALTER TABLE `sm` DROP INDEX `i_sid`; + ALTER TABLE `sm` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; + ALTER TABLE `sm` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `sm` ADD INDEX `i_sm_node` (`node`(75)); + ALTER TABLE `sm` ADD INDEX `i_sm_sh_username` (`server_host`, `username`); + ALTER TABLE `sm` ADD PRIMARY KEY (`usec`, `pid`(75)); + ALTER TABLE `privacy_list` DROP INDEX `i_privacy_list_username_name`; + ALTER TABLE `privacy_list` DROP INDEX `i_privacy_list_username`; + ALTER TABLE `privacy_list` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; + ALTER TABLE `privacy_list` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `privacy_list` ADD UNIQUE INDEX `i_privacy_list_sh_username_name` USING BTREE (`server_host`, `username`(75), `name`(75)); + ALTER TABLE `sr_user` DROP INDEX `i_sr_user_jid`; + ALTER TABLE `sr_user` DROP INDEX `i_sr_user_grp`; + ALTER TABLE `sr_user` DROP INDEX `i_sr_user_jid_group`; + ALTER TABLE `sr_user` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `jid`; + ALTER TABLE `sr_user` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `sr_user` ADD UNIQUE INDEX `i_sr_user_sh_jid_group` (`server_host`, `jid`, `grp`); + ALTER TABLE `sr_user` ADD INDEX `i_sr_user_sh_grp` (`server_host`, `grp`); + ALTER TABLE `muc_online_users` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `host`; + ALTER TABLE `muc_online_users` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `vcard` DROP PRIMARY KEY; + ALTER TABLE `vcard` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; + ALTER TABLE `vcard` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `vcard` ADD PRIMARY KEY (`server_host`, `username`); + ALTER TABLE `archive_prefs` DROP PRIMARY KEY; + ALTER TABLE `archive_prefs` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; + ALTER TABLE `archive_prefs` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `archive_prefs` ADD PRIMARY KEY (`server_host`, `username`); + ALTER TABLE `mix_pam` DROP INDEX `i_mix_pam`; + ALTER TABLE `mix_pam` DROP INDEX `i_mix_pam_u`; + ALTER TABLE `mix_pam` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; + ALTER TABLE `mix_pam` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `mix_pam` ADD UNIQUE INDEX `i_mix_pam` (`username`(191), `server_host`, `channel`(191), `service`(191)); + ALTER TABLE `route` CHANGE COLUMN `server_host` `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL; + ALTER TABLE `users` DROP PRIMARY KEY; + ALTER TABLE `users` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; + ALTER TABLE `users` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `users` ADD PRIMARY KEY (`server_host`, `username`); + ALTER TABLE `privacy_default_list` DROP PRIMARY KEY; + ALTER TABLE `privacy_default_list` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; + ALTER TABLE `privacy_default_list` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `privacy_default_list` ADD PRIMARY KEY (`server_host`, `username`); + ALTER TABLE `rostergroups` DROP INDEX `pk_rosterg_user_jid`; + ALTER TABLE `rostergroups` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; + ALTER TABLE `rostergroups` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `rostergroups` ADD INDEX `i_rosterg_sh_user_jid` (`server_host`, `username`(75), `jid`(75)); + ALTER TABLE `muc_room` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `host`; + ALTER TABLE `muc_room` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `spool` DROP INDEX `i_despool`; + ALTER TABLE `spool` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; + ALTER TABLE `spool` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `spool` ADD INDEX `i_spool_sh_username` USING BTREE (`server_host`, `username`); + ALTER TABLE `archive` DROP INDEX `i_username_timestamp`; + ALTER TABLE `archive` DROP INDEX `i_username_peer`; + ALTER TABLE `archive` DROP INDEX `i_username_bare_peer`; + ALTER TABLE `archive` DROP INDEX `i_timestamp`; + ALTER TABLE `archive` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; + ALTER TABLE `archive` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `archive` ADD INDEX `i_archive_sh_username_bare_peer` USING BTREE (`server_host`, `username`, `bare_peer`); + ALTER TABLE `archive` ADD INDEX `i_archive_sh_timestamp` USING BTREE (`server_host`, `timestamp`); + ALTER TABLE `archive` ADD INDEX `i_archive_sh_username_timestamp` USING BTREE (`server_host`, `username`, `timestamp`); + ALTER TABLE `archive` ADD INDEX `i_archive_sh_username_peer` USING BTREE (`server_host`, `username`, `peer`); + END IF; + SET FOREIGN_KEY_CHECKS = 1; +END; +## +DELIMITER ; + +CALL update_server_host(@DEFAULT_HOST); + +DROP PROCEDURE update_server_host; + +COMMIT; diff --git a/sql/mysql.sql b/sql/mysql.sql index 3d253c574..630c4a557 100644 --- a/sql/mysql.sql +++ b/sql/mysql.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2016 ProcessOne +-- ejabberd, Copyright (C) 2002-2025 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as @@ -17,12 +17,14 @@ -- CREATE TABLE users ( - username varchar(191) PRIMARY KEY, + username varchar(191) NOT NULL, + type smallint NOT NULL, password text NOT NULL, - serverkey varchar(64) NOT NULL DEFAULT '', - salt varchar(64) NOT NULL DEFAULT '', + serverkey varchar(128) NOT NULL DEFAULT '', + salt varchar(128) NOT NULL DEFAULT '', iterationcount integer NOT NULL DEFAULT 0, - created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (username, type) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- Add support for SCRAM auth to a database created before ejabberd 16.03: @@ -51,7 +53,6 @@ CREATE TABLE rosterusers ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_rosteru_user_jid ON rosterusers(username(75), jid(75)); -CREATE INDEX i_rosteru_username ON rosterusers(username); CREATE INDEX i_rosteru_jid ON rosterusers(jid); CREATE TABLE rostergroups ( @@ -68,6 +69,8 @@ CREATE TABLE sr_group ( created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +CREATE UNIQUE INDEX i_sr_group_name ON sr_group(name); + CREATE TABLE sr_user ( jid varchar(191) NOT NULL, grp varchar(191) NOT NULL, @@ -75,12 +78,11 @@ CREATE TABLE sr_user ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_sr_user_jid_group ON sr_user(jid(75), grp(75)); -CREATE INDEX i_sr_user_jid ON sr_user(jid); CREATE INDEX i_sr_user_grp ON sr_user(grp); CREATE TABLE spool ( username varchar(191) NOT NULL, - xml BLOB NOT NULL, + xml mediumtext NOT NULL, seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; @@ -93,19 +95,26 @@ CREATE TABLE archive ( timestamp BIGINT UNSIGNED NOT NULL, peer varchar(191) NOT NULL, bare_peer varchar(191) NOT NULL, - xml text NOT NULL, - txt text, + xml mediumtext NOT NULL, + txt mediumtext, id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, kind varchar(10), nick varchar(191), + origin_id varchar(191), created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE FULLTEXT INDEX i_text ON archive(txt); -CREATE INDEX i_username USING BTREE ON archive(username); +CREATE INDEX i_username_timestamp USING BTREE ON archive(username(191), timestamp); +CREATE INDEX i_username_peer USING BTREE ON archive(username(191), peer(191)); +CREATE INDEX i_username_bare_peer USING BTREE ON archive(username(191), bare_peer(191)); CREATE INDEX i_timestamp USING BTREE ON archive(timestamp); -CREATE INDEX i_peer USING BTREE ON archive(peer); -CREATE INDEX i_bare_peer USING BTREE ON archive(bare_peer); +CREATE INDEX i_archive_username_origin_id USING BTREE ON archive(username(191), origin_id(191)); + +-- To update 'archive' from ejabberd <= 23.10: +-- ALTER TABLE archive ADD COLUMN origin_id varchar(191) NOT NULL DEFAULT ''; +-- ALTER TABLE archive ALTER COLUMN origin_id DROP DEFAULT; +-- CREATE INDEX i_archive_username_origin_id USING BTREE ON archive(username(191), origin_id(191)); CREATE TABLE archive_prefs ( username varchar(191) NOT NULL PRIMARY KEY, @@ -121,12 +130,6 @@ CREATE TABLE vcard ( created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE TABLE vcard_xupdate ( - username varchar(191) PRIMARY KEY, - hash text NOT NULL, - created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP -) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - CREATE TABLE vcard_search ( username varchar(191) NOT NULL, lusername varchar(191) PRIMARY KEY, @@ -178,7 +181,6 @@ CREATE TABLE privacy_list ( created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE INDEX i_privacy_list_username USING BTREE ON privacy_list(username); CREATE UNIQUE INDEX i_privacy_list_username_name USING BTREE ON privacy_list (username(75), name(75)); CREATE TABLE privacy_list_data ( @@ -203,7 +205,6 @@ CREATE TABLE private_storage ( created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE INDEX i_private_storage_username USING BTREE ON private_storage(username); CREATE UNIQUE INDEX i_private_storage_username_namespace USING BTREE ON private_storage(username(75), namespace(75)); -- Not tested in mysql @@ -218,35 +219,35 @@ CREATE TABLE roster_version ( -- ALTER TABLE rosterusers ALTER COLUMN askmessage SET NOT NULL; CREATE TABLE pubsub_node ( - host text, - node text, - parent text, - type text, + host text NOT NULL, + node text NOT NULL, + parent VARCHAR(191) NOT NULL DEFAULT '', + plugin text NOT NULL, nodeid bigint auto_increment primary key ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_pubsub_node_parent ON pubsub_node(parent(120)); -CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node(host(20), node(120)); +CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node(host(71), node(120)); CREATE TABLE pubsub_node_option ( nodeid bigint, - name text, - val text + name text NOT NULL, + val text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option(nodeid); ALTER TABLE `pubsub_node_option` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE; CREATE TABLE pubsub_node_owner ( nodeid bigint, - owner text + owner text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner(nodeid); ALTER TABLE `pubsub_node_owner` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE; CREATE TABLE pubsub_state ( nodeid bigint, - jid text, + jid text NOT NULL, affiliation character(1), - subscriptions text, + subscriptions VARCHAR(191) NOT NULL DEFAULT '', stateid bigint auto_increment primary key ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_pubsub_state_jid ON pubsub_state(jid(60)); @@ -255,20 +256,20 @@ ALTER TABLE `pubsub_state` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` ( CREATE TABLE pubsub_item ( nodeid bigint, - itemid text, - publisher text, - creation text, - modification text, - payload text + itemid text NOT NULL, + publisher text NOT NULL, + creation varchar(32) NOT NULL, + modification varchar(32) NOT NULL, + payload mediumtext NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_pubsub_item_itemid ON pubsub_item(itemid(36)); CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item(nodeid, itemid(36)); ALTER TABLE `pubsub_item` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE; CREATE TABLE pubsub_subscription_opt ( - subid text, + subid text NOT NULL, opt_name varchar(32), - opt_value text + opt_value text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt(subid(32), opt_name(32)); @@ -280,6 +281,7 @@ CREATE TABLE muc_room ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_muc_room_name_host USING BTREE ON muc_room(name(75), host(75)); +CREATE INDEX i_muc_room_host_created_at ON muc_room(host(75), created_at); CREATE TABLE muc_registered ( jid text NOT NULL, @@ -291,14 +293,38 @@ CREATE TABLE muc_registered ( CREATE INDEX i_muc_registered_nick USING BTREE ON muc_registered(nick(75)); CREATE UNIQUE INDEX i_muc_registered_jid_host USING BTREE ON muc_registered(jid(75), host(75)); -CREATE TABLE irc_custom ( - jid text NOT NULL, +CREATE TABLE muc_online_room ( + name text NOT NULL, host text NOT NULL, - data text NOT NULL, - created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP + node text NOT NULL, + pid text NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE UNIQUE INDEX i_irc_custom_jid_host USING BTREE ON irc_custom(jid(75), host(75)); +CREATE UNIQUE INDEX i_muc_online_room_name_host USING BTREE ON muc_online_room(name(75), host(75)); + +CREATE TABLE muc_online_users ( + username text NOT NULL, + server text NOT NULL, + resource text NOT NULL, + name text NOT NULL, + host text NOT NULL, + node text NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_muc_online_users USING BTREE ON muc_online_users(username(75), server(75), resource(75), name(75), host(75)); + +CREATE TABLE muc_room_subscribers ( + room varchar(191) NOT NULL, + host varchar(191) NOT NULL, + jid varchar(191) NOT NULL, + nick text NOT NULL, + nodes text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + UNIQUE KEY i_muc_room_subscribers_host_room_jid (host, room, jid) +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE INDEX i_muc_room_subscribers_host_jid USING BTREE ON muc_room_subscribers(host, jid); +CREATE INDEX i_muc_room_subscribers_jid USING BTREE ON muc_room_subscribers(jid); CREATE TABLE motd ( username varchar(191) PRIMARY KEY, @@ -335,3 +361,115 @@ CREATE TABLE oauth_token ( scope text NOT NULL, expire bigint NOT NULL ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE TABLE oauth_client ( + client_id varchar(191) NOT NULL PRIMARY KEY, + client_name text NOT NULL, + grant_type text NOT NULL, + options text NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE TABLE route ( + domain text NOT NULL, + server_host text NOT NULL, + node text NOT NULL, + pid text NOT NULL, + local_hint text NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_route ON route(domain(75), server_host(75), node(75), pid(75)); + +CREATE TABLE bosh ( + sid text NOT NULL, + node text NOT NULL, + pid text NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid(75)); + +CREATE TABLE proxy65 ( + sid text NOT NULL, + pid_t text NOT NULL, + pid_i text NOT NULL, + node_t text NOT NULL, + node_i text NOT NULL, + jid_i text NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 (sid(191)); +CREATE INDEX i_proxy65_jid ON proxy65 (jid_i(191)); + +CREATE TABLE push_session ( + username text NOT NULL, + timestamp bigint NOT NULL, + service text NOT NULL, + node text NOT NULL, + xml text NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_push_usn ON push_session (username(191), service(191), node(191)); +CREATE UNIQUE INDEX i_push_ut ON push_session (username(191), timestamp); + +CREATE TABLE mix_channel ( + channel text NOT NULL, + service text NOT NULL, + username text NOT NULL, + domain text NOT NULL, + jid text NOT NULL, + hidden boolean NOT NULL, + hmac_key text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_mix_channel ON mix_channel (channel(191), service(191)); +CREATE INDEX i_mix_channel_serv ON mix_channel (service(191)); + +CREATE TABLE mix_participant ( + channel text NOT NULL, + service text NOT NULL, + username text NOT NULL, + domain text NOT NULL, + jid text NOT NULL, + id text NOT NULL, + nick text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_mix_participant ON mix_participant (channel(191), service(191), username(191), domain(191)); + +CREATE TABLE mix_subscription ( + channel text NOT NULL, + service text NOT NULL, + username text NOT NULL, + domain text NOT NULL, + node text NOT NULL, + jid text NOT NULL +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_mix_subscription ON mix_subscription (channel(153), service(153), username(153), domain(153), node(153)); +CREATE INDEX i_mix_subscription_chan_serv_node ON mix_subscription (channel(191), service(191), node(191)); + +CREATE TABLE mix_pam ( + username text NOT NULL, + channel text NOT NULL, + service text NOT NULL, + id text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username(191), channel(191), service(191)); + +CREATE TABLE mqtt_pub ( + username varchar(191) NOT NULL, + resource varchar(191) NOT NULL, + topic text NOT NULL, + qos tinyint NOT NULL, + payload blob NOT NULL, + payload_format tinyint NOT NULL, + content_type text NOT NULL, + response_topic text NOT NULL, + correlation_data blob NOT NULL, + user_properties blob NOT NULL, + expiry int unsigned NOT NULL, + UNIQUE KEY i_mqtt_topic (topic(191)) +) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; diff --git a/sql/pg.new.sql b/sql/pg.new.sql new file mode 100644 index 000000000..1e59ec571 --- /dev/null +++ b/sql/pg.new.sql @@ -0,0 +1,664 @@ +-- +-- ejabberd, Copyright (C) 2002-2025 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. +-- + +-- To update from the old schema, replace with the host's domain: + +-- ALTER TABLE users ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- ALTER TABLE users DROP CONSTRAINT users_pkey; +-- ALTER TABLE users ADD PRIMARY KEY (server_host, username, "type"); +-- ALTER TABLE users ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE last ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- ALTER TABLE last DROP CONSTRAINT last_pkey; +-- ALTER TABLE last ADD PRIMARY KEY (server_host, username); +-- ALTER TABLE last ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE rosterusers ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- DROP INDEX i_rosteru_user_jid; +-- DROP INDEX i_rosteru_jid; +-- CREATE UNIQUE INDEX i_rosteru_sh_user_jid ON rosterusers USING btree (server_host, username, jid); +-- CREATE INDEX i_rosteru_sh_jid ON rosterusers USING btree (server_host, jid); +-- ALTER TABLE rosterusers ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE rostergroups ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- DROP INDEX pk_rosterg_user_jid; +-- CREATE INDEX i_rosterg_sh_user_jid ON rostergroups USING btree (server_host, username, jid); +-- ALTER TABLE rostergroups ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE sr_group ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- DROP INDEX i_sr_group_name; +-- ALTER TABLE sr_group ADD PRIMARY KEY (server_host, name); +-- CREATE UNIQUE INDEX i_sr_group_sh_name ON sr_group USING btree (server_host, name); +-- ALTER TABLE sr_group ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE sr_user ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- DROP INDEX i_sr_user_jid_grp; +-- DROP INDEX i_sr_user_grp; +-- ALTER TABLE sr_user ADD PRIMARY KEY (server_host, jid, grp); +-- CREATE INDEX i_sr_user_sh_grp ON sr_user USING btree (server_host, grp); +-- ALTER TABLE sr_user ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE spool ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- DROP INDEX i_despool; +-- CREATE INDEX i_spool_sh_username ON spool USING btree (server_host, username); +-- ALTER TABLE spool ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE archive ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- DROP INDEX i_username_timestamp; +-- DROP INDEX i_username_peer; +-- DROP INDEX i_username_bare_peer; +-- DROP INDEX i_timestamp; +-- CREATE INDEX i_archive_sh_username_timestamp ON archive USING btree (server_host, username, timestamp); +-- CREATE INDEX i_archive_sh_username_peer ON archive USING btree (server_host, username, peer); +-- CREATE INDEX i_archive_sh_username_bare_peer ON archive USING btree (server_host, username, bare_peer); +-- CREATE INDEX i_archive_sh_timestamp ON archive USING btree (server_host, timestamp); +-- ALTER TABLE archive ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE archive_prefs ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- ALTER TABLE archive_prefs DROP CONSTRAINT archive_prefs_pkey; +-- ALTER TABLE archive_prefs ADD PRIMARY KEY (server_host, username); +-- ALTER TABLE archive_prefs ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE vcard ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- ALTER TABLE vcard DROP CONSTRAINT vcard_pkey; +-- ALTER TABLE vcard ADD PRIMARY KEY (server_host, username); +-- ALTER TABLE vcard ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE vcard_search ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- ALTER TABLE vcard_search DROP CONSTRAINT vcard_search_pkey; +-- DROP INDEX i_vcard_search_lfn; +-- DROP INDEX i_vcard_search_lfamily; +-- DROP INDEX i_vcard_search_lgiven; +-- DROP INDEX i_vcard_search_lmiddle; +-- DROP INDEX i_vcard_search_lnickname; +-- DROP INDEX i_vcard_search_lbday; +-- DROP INDEX i_vcard_search_lctry; +-- DROP INDEX i_vcard_search_llocality; +-- DROP INDEX i_vcard_search_lemail; +-- DROP INDEX i_vcard_search_lorgname; +-- DROP INDEX i_vcard_search_lorgunit; +-- ALTER TABLE vcard_search ADD PRIMARY KEY (server_host, lusername); +-- CREATE INDEX i_vcard_search_sh_lfn ON vcard_search(server_host, lfn); +-- CREATE INDEX i_vcard_search_sh_lfamily ON vcard_search(server_host, lfamily); +-- CREATE INDEX i_vcard_search_sh_lgiven ON vcard_search(server_host, lgiven); +-- CREATE INDEX i_vcard_search_sh_lmiddle ON vcard_search(server_host, lmiddle); +-- CREATE INDEX i_vcard_search_sh_lnickname ON vcard_search(server_host, lnickname); +-- CREATE INDEX i_vcard_search_sh_lbday ON vcard_search(server_host, lbday); +-- CREATE INDEX i_vcard_search_sh_lctry ON vcard_search(server_host, lctry); +-- CREATE INDEX i_vcard_search_sh_llocality ON vcard_search(server_host, llocality); +-- CREATE INDEX i_vcard_search_sh_lemail ON vcard_search(server_host, lemail); +-- CREATE INDEX i_vcard_search_sh_lorgname ON vcard_search(server_host, lorgname); +-- CREATE INDEX i_vcard_search_sh_lorgunit ON vcard_search(server_host, lorgunit); +-- ALTER TABLE vcard_search ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE privacy_default_list ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- ALTER TABLE privacy_default_list DROP CONSTRAINT privacy_default_list_pkey; +-- ALTER TABLE privacy_default_list ADD PRIMARY KEY (server_host, username); +-- ALTER TABLE privacy_default_list ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE privacy_list ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- DROP INDEX i_privacy_list_username_name; +-- CREATE UNIQUE INDEX i_privacy_list_sh_username_name ON privacy_list USING btree (server_host, username, name); +-- ALTER TABLE privacy_list ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE private_storage ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- DROP INDEX i_private_storage_username_namespace; +-- ALTER TABLE private_storage ADD PRIMARY KEY (server_host, username, namespace); +-- ALTER TABLE private_storage ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE roster_version ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- ALTER TABLE roster_version DROP CONSTRAINT roster_version_pkey; +-- ALTER TABLE roster_version ADD PRIMARY KEY (server_host, username); +-- ALTER TABLE roster_version ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE muc_room ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- ALTER TABLE muc_room ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE muc_registered ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- ALTER TABLE muc_registered ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE muc_online_room ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- ALTER TABLE muc_online_room ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE muc_online_users ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- ALTER TABLE muc_online_users ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE motd ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- ALTER TABLE motd DROP CONSTRAINT motd_pkey; +-- ALTER TABLE motd ADD PRIMARY KEY (server_host, username); +-- ALTER TABLE motd ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE sm ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- DROP INDEX i_sm_sid; +-- DROP INDEX i_sm_username; +-- ALTER TABLE sm ADD PRIMARY KEY (usec, pid); +-- CREATE INDEX i_sm_sh_username ON sm USING btree (server_host, username); +-- ALTER TABLE sm ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE push_session ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- DROP INDEX i_push_usn; +-- DROP INDEX i_push_ut; +-- ALTER TABLE push_session ADD PRIMARY KEY (server_host, username, timestamp); +-- CREATE UNIQUE INDEX i_push_session_susn ON push_session USING btree (server_host, username, service, node); +-- CREATE INDEX i_push_session_sh_username_timestamp ON push_session USING btree (server_host, username, timestamp); +-- ALTER TABLE push_session ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE mix_pam ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- DROP INDEX i_mix_pam; +-- CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username, server_host, channel, service); +-- ALTER TABLE mix_pam ALTER COLUMN server_host DROP DEFAULT; + +-- ALTER TABLE mqtt_pub ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- DROP INDEX i_mqtt_topic; +-- CREATE UNIQUE INDEX i_mqtt_topic_server ON mqtt_pub (topic, server_host); +-- ALTER TABLE mqtt_pub ALTER COLUMN server_host DROP DEFAULT; + + +CREATE TABLE users ( + username text NOT NULL, + server_host text NOT NULL, + "type" smallint NOT NULL, + "password" text NOT NULL, + serverkey text NOT NULL DEFAULT '', + salt text NOT NULL DEFAULT '', + iterationcount integer NOT NULL DEFAULT 0, + created_at TIMESTAMP NOT NULL DEFAULT now(), + PRIMARY KEY (server_host, username, "type") +); + +-- Add support for SCRAM auth to a database created before ejabberd 16.03: +-- ALTER TABLE users ADD COLUMN serverkey text NOT NULL DEFAULT ''; +-- ALTER TABLE users ADD COLUMN salt text NOT NULL DEFAULT ''; +-- ALTER TABLE users ADD COLUMN iterationcount integer NOT NULL DEFAULT 0; + +CREATE TABLE last ( + username text NOT NULL, + server_host text NOT NULL, + seconds text NOT NULL, + state text NOT NULL, + PRIMARY KEY (server_host, username) +); + + +CREATE TABLE rosterusers ( + username text NOT NULL, + server_host text NOT NULL, + jid text NOT NULL, + nick text NOT NULL, + subscription character(1) NOT NULL, + ask character(1) NOT NULL, + askmessage text NOT NULL, + server character(1) NOT NULL, + subscribe text NOT NULL, + "type" text, + created_at TIMESTAMP NOT NULL DEFAULT now() +); + +CREATE UNIQUE INDEX i_rosteru_sh_user_jid ON rosterusers USING btree (server_host, username, jid); +CREATE INDEX i_rosteru_sh_jid ON rosterusers USING btree (server_host, jid); + + +CREATE TABLE rostergroups ( + username text NOT NULL, + server_host text NOT NULL, + jid text NOT NULL, + grp text NOT NULL +); + +CREATE INDEX i_rosterg_sh_user_jid ON rostergroups USING btree (server_host, username, jid); + +CREATE TABLE sr_group ( + name text NOT NULL, + server_host text NOT NULL, + opts text NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT now() +); + +CREATE UNIQUE INDEX i_sr_group_sh_name ON sr_group USING btree (server_host, name); + +CREATE TABLE sr_user ( + jid text NOT NULL, + server_host text NOT NULL, + grp text NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT now() +); + +CREATE UNIQUE INDEX i_sr_user_sh_jid_grp ON sr_user USING btree (server_host, jid, grp); +CREATE INDEX i_sr_user_sh_grp ON sr_user USING btree (server_host, grp); + +CREATE TABLE spool ( + username text NOT NULL, + server_host text NOT NULL, + xml text NOT NULL, + seq BIGSERIAL, + created_at TIMESTAMP NOT NULL DEFAULT now() +); + +CREATE INDEX i_spool_sh_username ON spool USING btree (server_host, username); + +CREATE TABLE archive ( + username text NOT NULL, + server_host text NOT NULL, + timestamp BIGINT NOT NULL, + peer text NOT NULL, + bare_peer text NOT NULL, + xml text NOT NULL, + txt text, + id BIGSERIAL, + kind text, + nick text, + origin_id text, + created_at TIMESTAMP NOT NULL DEFAULT now() +); + +CREATE INDEX i_archive_sh_username_timestamp ON archive USING btree (server_host, username, timestamp); +CREATE INDEX i_archive_sh_username_peer ON archive USING btree (server_host, username, peer); +CREATE INDEX i_archive_sh_username_bare_peer ON archive USING btree (server_host, username, bare_peer); +CREATE INDEX i_archive_sh_timestamp ON archive USING btree (server_host, timestamp); +CREATE INDEX i_archive_sh_username_origin_id ON archive USING btree (server_host, username, origin_id); + +-- To update 'archive' from ejabberd <= 23.10: +-- ALTER TABLE archive ADD COLUMN origin_id text NOT NULL DEFAULT ''; +-- ALTER TABLE archive ALTER COLUMN origin_id DROP DEFAULT; +-- CREATE INDEX i_archive_sh_username_origin_id ON archive USING btree (server_host, username, origin_id); + +CREATE TABLE archive_prefs ( + username text NOT NULL, + server_host text NOT NULL, + def text NOT NULL, + always text NOT NULL, + never text NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT now(), + PRIMARY KEY (server_host, username) +); + +CREATE TABLE vcard ( + username text NOT NULL, + server_host text NOT NULL, + vcard text NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT now(), + PRIMARY KEY (server_host, username) +); + +CREATE TABLE vcard_search ( + username text NOT NULL, + lusername text NOT NULL, + server_host text NOT NULL, + fn text NOT NULL, + lfn text NOT NULL, + "family" text NOT NULL, + lfamily text NOT NULL, + given text NOT NULL, + lgiven text NOT NULL, + middle text NOT NULL, + lmiddle text NOT NULL, + nickname text NOT NULL, + lnickname text NOT NULL, + bday text NOT NULL, + lbday text NOT NULL, + ctry text NOT NULL, + lctry text NOT NULL, + locality text NOT NULL, + llocality text NOT NULL, + email text NOT NULL, + lemail text NOT NULL, + orgname text NOT NULL, + lorgname text NOT NULL, + orgunit text NOT NULL, + lorgunit text NOT NULL, + PRIMARY KEY (server_host, lusername) +); + +CREATE INDEX i_vcard_search_sh_lfn ON vcard_search(server_host, lfn); +CREATE INDEX i_vcard_search_sh_lfamily ON vcard_search(server_host, lfamily); +CREATE INDEX i_vcard_search_sh_lgiven ON vcard_search(server_host, lgiven); +CREATE INDEX i_vcard_search_sh_lmiddle ON vcard_search(server_host, lmiddle); +CREATE INDEX i_vcard_search_sh_lnickname ON vcard_search(server_host, lnickname); +CREATE INDEX i_vcard_search_sh_lbday ON vcard_search(server_host, lbday); +CREATE INDEX i_vcard_search_sh_lctry ON vcard_search(server_host, lctry); +CREATE INDEX i_vcard_search_sh_llocality ON vcard_search(server_host, llocality); +CREATE INDEX i_vcard_search_sh_lemail ON vcard_search(server_host, lemail); +CREATE INDEX i_vcard_search_sh_lorgname ON vcard_search(server_host, lorgname); +CREATE INDEX i_vcard_search_sh_lorgunit ON vcard_search(server_host, lorgunit); + +CREATE TABLE privacy_default_list ( + username text NOT NULL, + server_host text NOT NULL, + name text NOT NULL, + PRIMARY KEY (server_host, username) +); + +CREATE TABLE privacy_list ( + username text NOT NULL, + server_host text NOT NULL, + name text NOT NULL, + id BIGSERIAL UNIQUE, + created_at TIMESTAMP NOT NULL DEFAULT now() +); + +CREATE UNIQUE INDEX i_privacy_list_sh_username_name ON privacy_list USING btree (server_host, username, name); + +CREATE TABLE privacy_list_data ( + id bigint REFERENCES privacy_list(id) ON DELETE CASCADE, + t character(1) NOT NULL, + value text NOT NULL, + action character(1) NOT NULL, + ord NUMERIC NOT NULL, + match_all boolean NOT NULL, + match_iq boolean NOT NULL, + match_message boolean NOT NULL, + match_presence_in boolean NOT NULL, + match_presence_out boolean NOT NULL +); + +CREATE INDEX i_privacy_list_data_id ON privacy_list_data USING btree (id); + +CREATE TABLE private_storage ( + username text NOT NULL, + server_host text NOT NULL, + namespace text NOT NULL, + data text NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT now() +); + +CREATE UNIQUE INDEX i_private_storage_sh_username_namespace ON private_storage USING btree (server_host, username, namespace); + +CREATE TABLE roster_version ( + username text NOT NULL, + server_host text NOT NULL, + version text NOT NULL, + PRIMARY KEY (server_host, username) +); + +-- To update from 0.9.8: +-- CREATE SEQUENCE spool_seq_seq; +-- ALTER TABLE spool ADD COLUMN seq integer; +-- ALTER TABLE spool ALTER COLUMN seq SET DEFAULT nextval('spool_seq_seq'); +-- UPDATE spool SET seq = DEFAULT; +-- ALTER TABLE spool ALTER COLUMN seq SET NOT NULL; + +-- To update from 1.x: +-- ALTER TABLE rosterusers ADD COLUMN askmessage text; +-- UPDATE rosterusers SET askmessage = ''; +-- ALTER TABLE rosterusers ALTER COLUMN askmessage SET NOT NULL; + +CREATE TABLE pubsub_node ( + host text NOT NULL, + node text NOT NULL, + parent text NOT NULL DEFAULT '', + plugin text NOT NULL, + nodeid BIGSERIAL UNIQUE +); +CREATE INDEX i_pubsub_node_parent ON pubsub_node USING btree (parent); +CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node USING btree (host, node); + +CREATE TABLE pubsub_node_option ( + nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, + name text NOT NULL, + val text NOT NULL +); +CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option USING btree (nodeid); + +CREATE TABLE pubsub_node_owner ( + nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, + owner text NOT NULL +); +CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner USING btree (nodeid); + +CREATE TABLE pubsub_state ( + nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, + jid text NOT NULL, + affiliation character(1), + subscriptions text NOT NULL DEFAULT '', + stateid BIGSERIAL UNIQUE +); +CREATE INDEX i_pubsub_state_jid ON pubsub_state USING btree (jid); +CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state USING btree (nodeid, jid); + +CREATE TABLE pubsub_item ( + nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, + itemid text NOT NULL, + publisher text NOT NULL, + creation varchar(32) NOT NULL, + modification varchar(32) NOT NULL, + payload text NOT NULL DEFAULT '' +); +CREATE INDEX i_pubsub_item_itemid ON pubsub_item USING btree (itemid); +CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item USING btree (nodeid, itemid); + +CREATE TABLE pubsub_subscription_opt ( + subid text NOT NULL, + opt_name varchar(32), + opt_value text NOT NULL +); +CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt USING btree (subid, opt_name); + +CREATE TABLE muc_room ( + name text NOT NULL, + host text NOT NULL, + server_host text NOT NULL, + opts text NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT now() +); + +CREATE UNIQUE INDEX i_muc_room_name_host ON muc_room USING btree (name, host); +CREATE INDEX i_muc_room_host_created_at ON muc_room USING btree (host, created_at); + +CREATE TABLE muc_registered ( + jid text NOT NULL, + host text NOT NULL, + server_host text NOT NULL, + nick text NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT now() +); + +CREATE INDEX i_muc_registered_nick ON muc_registered USING btree (nick); +CREATE UNIQUE INDEX i_muc_registered_jid_host ON muc_registered USING btree (jid, host); + +CREATE TABLE muc_online_room ( + name text NOT NULL, + host text NOT NULL, + server_host text NOT NULL, + node text NOT NULL, + pid text NOT NULL +); + +CREATE UNIQUE INDEX i_muc_online_room_name_host ON muc_online_room USING btree (name, host); + +CREATE TABLE muc_online_users ( + username text NOT NULL, + server text NOT NULL, + resource text NOT NULL, + name text NOT NULL, + host text NOT NULL, + server_host text NOT NULL, + node text NOT NULL +); + +CREATE UNIQUE INDEX i_muc_online_users ON muc_online_users USING btree (username, server, resource, name, host); + +CREATE TABLE muc_room_subscribers ( + room text NOT NULL, + host text NOT NULL, + jid text NOT NULL, + nick text NOT NULL, + nodes text NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT now() +); + +CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers USING btree (host, jid); +CREATE INDEX i_muc_room_subscribers_jid ON muc_room_subscribers USING btree (jid); +CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers USING btree (host, room, jid); + +CREATE TABLE motd ( + username text NOT NULL, + server_host text NOT NULL, + xml text, + created_at TIMESTAMP NOT NULL DEFAULT now(), + PRIMARY KEY (server_host, username) +); + +CREATE TABLE caps_features ( + node text NOT NULL, + subnode text NOT NULL, + feature text, + created_at TIMESTAMP NOT NULL DEFAULT now() +); + +CREATE INDEX i_caps_features_node_subnode ON caps_features USING btree (node, subnode); + +CREATE TABLE sm ( + usec bigint NOT NULL, + pid text NOT NULL, + node text NOT NULL, + username text NOT NULL, + server_host text NOT NULL, + resource text NOT NULL, + priority text NOT NULL, + info text NOT NULL, + PRIMARY KEY (usec, pid) +); + +CREATE INDEX i_sm_node ON sm USING btree (node); +CREATE INDEX i_sm_sh_username ON sm USING btree (server_host, username); + +CREATE TABLE oauth_token ( + token text NOT NULL, + jid text NOT NULL, + scope text NOT NULL, + expire bigint NOT NULL +); + +CREATE UNIQUE INDEX i_oauth_token_token ON oauth_token USING btree (token); + +CREATE TABLE oauth_client ( + client_id text PRIMARY KEY, + client_name text NOT NULL, + grant_type text NOT NULL, + options text NOT NULL +); + +CREATE TABLE route ( + domain text NOT NULL, + server_host text NOT NULL, + node text NOT NULL, + pid text NOT NULL, + local_hint text NOT NULL +); + +CREATE UNIQUE INDEX i_route ON route USING btree (domain, server_host, node, pid); + +CREATE TABLE bosh ( + sid text NOT NULL, + node text NOT NULL, + pid text NOT NULL +); + +CREATE UNIQUE INDEX i_bosh_sid ON bosh USING btree (sid); + +CREATE TABLE proxy65 ( + sid text NOT NULL, + pid_t text NOT NULL, + pid_i text NOT NULL, + node_t text NOT NULL, + node_i text NOT NULL, + jid_i text NOT NULL +); + +CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 USING btree (sid); +CREATE INDEX i_proxy65_jid ON proxy65 USING btree (jid_i); + +CREATE TABLE push_session ( + username text NOT NULL, + server_host text NOT NULL, + timestamp bigint NOT NULL, + service text NOT NULL, + node text NOT NULL, + xml text NOT NULL, + PRIMARY KEY (server_host, username, timestamp) +); + +CREATE UNIQUE INDEX i_push_session_susn ON push_session USING btree (server_host, username, service, node); +CREATE INDEX i_push_session_sh_username_timestamp ON push_session USING btree (server_host, username, timestamp); + +CREATE TABLE mix_channel ( + channel text NOT NULL, + service text NOT NULL, + username text NOT NULL, + domain text NOT NULL, + jid text NOT NULL, + hidden boolean NOT NULL, + hmac_key text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX i_mix_channel ON mix_channel (channel, service); +CREATE INDEX i_mix_channel_serv ON mix_channel (service); + +CREATE TABLE mix_participant ( + channel text NOT NULL, + service text NOT NULL, + username text NOT NULL, + domain text NOT NULL, + jid text NOT NULL, + id text NOT NULL, + nick text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX i_mix_participant ON mix_participant (channel, service, username, domain); + +CREATE TABLE mix_subscription ( + channel text NOT NULL, + service text NOT NULL, + username text NOT NULL, + domain text NOT NULL, + node text NOT NULL, + jid text NOT NULL +); + +CREATE UNIQUE INDEX i_mix_subscription ON mix_subscription (channel, service, username, domain, node); +CREATE INDEX i_mix_subscription_chan_serv_node ON mix_subscription (channel, service, node); + +CREATE TABLE mix_pam ( + username text NOT NULL, + server_host text NOT NULL, + channel text NOT NULL, + service text NOT NULL, + id text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username, server_host, channel, service); + +CREATE TABLE mqtt_pub ( + username text NOT NULL, + server_host text NOT NULL, + resource text NOT NULL, + topic text NOT NULL, + qos smallint NOT NULL, + payload bytea NOT NULL, + payload_format smallint NOT NULL, + content_type text NOT NULL, + response_topic text NOT NULL, + correlation_data bytea NOT NULL, + user_properties bytea NOT NULL, + expiry bigint NOT NULL +); + +CREATE UNIQUE INDEX i_mqtt_topic_server ON mqtt_pub (topic, server_host); diff --git a/sql/pg.sql b/sql/pg.sql index 3d7de4285..dd83e087e 100644 --- a/sql/pg.sql +++ b/sql/pg.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2016 ProcessOne +-- ejabberd, Copyright (C) 2002-2025 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as @@ -10,19 +10,21 @@ -- 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. -- CREATE TABLE users ( - username text PRIMARY KEY, + username text NOT NULL, + "type" smallint NOT NULL, "password" text NOT NULL, serverkey text NOT NULL DEFAULT '', salt text NOT NULL DEFAULT '', iterationcount integer NOT NULL DEFAULT 0, - created_at TIMESTAMP NOT NULL DEFAULT now() + created_at TIMESTAMP NOT NULL DEFAULT now(), + PRIMARY KEY (username, "type") ); -- Add support for SCRAM auth to a database created before ejabberd 16.03: @@ -45,13 +47,12 @@ CREATE TABLE rosterusers ( ask character(1) NOT NULL, askmessage text NOT NULL, server character(1) NOT NULL, - subscribe text, + subscribe text NOT NULL, "type" text, created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE UNIQUE INDEX i_rosteru_user_jid ON rosterusers USING btree (username, jid); -CREATE INDEX i_rosteru_username ON rosterusers USING btree (username); CREATE INDEX i_rosteru_jid ON rosterusers USING btree (jid); @@ -69,6 +70,8 @@ CREATE TABLE sr_group ( created_at TIMESTAMP NOT NULL DEFAULT now() ); +CREATE UNIQUE INDEX i_sr_group_name ON sr_group USING btree (name); + CREATE TABLE sr_user ( jid text NOT NULL, grp text NOT NULL, @@ -76,13 +79,12 @@ CREATE TABLE sr_user ( ); CREATE UNIQUE INDEX i_sr_user_jid_grp ON sr_user USING btree (jid, grp); -CREATE INDEX i_sr_user_jid ON sr_user USING btree (jid); CREATE INDEX i_sr_user_grp ON sr_user USING btree (grp); CREATE TABLE spool ( username text NOT NULL, xml text NOT NULL, - seq SERIAL, + seq BIGSERIAL, created_at TIMESTAMP NOT NULL DEFAULT now() ); @@ -95,16 +97,23 @@ CREATE TABLE archive ( bare_peer text NOT NULL, xml text NOT NULL, txt text, - id SERIAL, + id BIGSERIAL, kind text, nick text, + origin_id text, created_at TIMESTAMP NOT NULL DEFAULT now() ); -CREATE INDEX i_username ON archive USING btree (username); +CREATE INDEX i_username_timestamp ON archive USING btree (username, timestamp); +CREATE INDEX i_username_peer ON archive USING btree (username, peer); +CREATE INDEX i_username_bare_peer ON archive USING btree (username, bare_peer); CREATE INDEX i_timestamp ON archive USING btree (timestamp); -CREATE INDEX i_peer ON archive USING btree (peer); -CREATE INDEX i_bare_peer ON archive USING btree (bare_peer); +CREATE INDEX i_archive_username_origin_id ON archive USING btree (username, origin_id); + +-- To update 'archive' from ejabberd <= 23.10: +-- ALTER TABLE archive ADD COLUMN origin_id text NOT NULL DEFAULT ''; +-- ALTER TABLE archive ALTER COLUMN origin_id DROP DEFAULT; +-- CREATE INDEX i_archive_username_origin_id ON archive USING btree (username, origin_id); CREATE TABLE archive_prefs ( username text NOT NULL PRIMARY KEY, @@ -120,18 +129,12 @@ CREATE TABLE vcard ( created_at TIMESTAMP NOT NULL DEFAULT now() ); -CREATE TABLE vcard_xupdate ( - username text PRIMARY KEY, - hash text NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT now() -); - CREATE TABLE vcard_search ( username text NOT NULL, lusername text PRIMARY KEY, fn text NOT NULL, lfn text NOT NULL, - family text NOT NULL, + "family" text NOT NULL, lfamily text NOT NULL, given text NOT NULL, lgiven text NOT NULL, @@ -173,11 +176,10 @@ CREATE TABLE privacy_default_list ( CREATE TABLE privacy_list ( username text NOT NULL, name text NOT NULL, - id SERIAL UNIQUE, + id BIGSERIAL UNIQUE, created_at TIMESTAMP NOT NULL DEFAULT now() ); -CREATE INDEX i_privacy_list_username ON privacy_list USING btree (username); CREATE UNIQUE INDEX i_privacy_list_username_name ON privacy_list USING btree (username, name); CREATE TABLE privacy_list_data ( @@ -202,7 +204,6 @@ CREATE TABLE private_storage ( created_at TIMESTAMP NOT NULL DEFAULT now() ); -CREATE INDEX i_private_storage_username ON private_storage USING btree (username); CREATE UNIQUE INDEX i_private_storage_username_namespace ON private_storage USING btree (username, namespace); @@ -224,53 +225,53 @@ CREATE TABLE roster_version ( -- ALTER TABLE rosterusers ALTER COLUMN askmessage SET NOT NULL; CREATE TABLE pubsub_node ( - host text, - node text, - parent text, - "type" text, - nodeid SERIAL UNIQUE + host text NOT NULL, + node text NOT NULL, + parent text NOT NULL DEFAULT '', + plugin text NOT NULL, + nodeid BIGSERIAL UNIQUE ); CREATE INDEX i_pubsub_node_parent ON pubsub_node USING btree (parent); CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node USING btree (host, node); CREATE TABLE pubsub_node_option ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, - name text, - val text + name text NOT NULL, + val text NOT NULL ); CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option USING btree (nodeid); CREATE TABLE pubsub_node_owner ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, - owner text + owner text NOT NULL ); CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner USING btree (nodeid); CREATE TABLE pubsub_state ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, - jid text, + jid text NOT NULL, affiliation character(1), - subscriptions text, - stateid SERIAL UNIQUE + subscriptions text NOT NULL DEFAULT '', + stateid BIGSERIAL UNIQUE ); CREATE INDEX i_pubsub_state_jid ON pubsub_state USING btree (jid); CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state USING btree (nodeid, jid); CREATE TABLE pubsub_item ( nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE, - itemid text, - publisher text, - creation text, - modification text, - payload text + itemid text NOT NULL, + publisher text NOT NULL, + creation varchar(32) NOT NULL, + modification varchar(32) NOT NULL, + payload text NOT NULL DEFAULT '' ); CREATE INDEX i_pubsub_item_itemid ON pubsub_item USING btree (itemid); CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item USING btree (nodeid, itemid); CREATE TABLE pubsub_subscription_opt ( - subid text, + subid text NOT NULL, opt_name varchar(32), - opt_value text + opt_value text NOT NULL ); CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt USING btree (subid, opt_name); @@ -282,6 +283,7 @@ CREATE TABLE muc_room ( ); CREATE UNIQUE INDEX i_muc_room_name_host ON muc_room USING btree (name, host); +CREATE INDEX i_muc_room_host_created_at ON muc_room USING btree (host, created_at); CREATE TABLE muc_registered ( jid text NOT NULL, @@ -293,14 +295,38 @@ CREATE TABLE muc_registered ( CREATE INDEX i_muc_registered_nick ON muc_registered USING btree (nick); CREATE UNIQUE INDEX i_muc_registered_jid_host ON muc_registered USING btree (jid, host); -CREATE TABLE irc_custom ( - jid text NOT NULL, +CREATE TABLE muc_online_room ( + name text NOT NULL, host text NOT NULL, - data text NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT now() + node text NOT NULL, + pid text NOT NULL ); -CREATE UNIQUE INDEX i_irc_custom_jid_host ON irc_custom USING btree (jid, host); +CREATE UNIQUE INDEX i_muc_online_room_name_host ON muc_online_room USING btree (name, host); + +CREATE TABLE muc_online_users ( + username text NOT NULL, + server text NOT NULL, + resource text NOT NULL, + name text NOT NULL, + host text NOT NULL, + node text NOT NULL +); + +CREATE UNIQUE INDEX i_muc_online_users ON muc_online_users USING btree (username, server, resource, name, host); + +CREATE TABLE muc_room_subscribers ( + room text NOT NULL, + host text NOT NULL, + jid text NOT NULL, + nick text NOT NULL, + nodes text NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT now() +); + +CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers USING btree (host, jid); +CREATE INDEX i_muc_room_subscribers_jid ON muc_room_subscribers USING btree (jid); +CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers USING btree (host, room, jid); CREATE TABLE motd ( username text PRIMARY KEY, @@ -339,3 +365,116 @@ CREATE TABLE oauth_token ( ); CREATE UNIQUE INDEX i_oauth_token_token ON oauth_token USING btree (token); + +CREATE TABLE oauth_client ( + client_id text PRIMARY KEY, + client_name text NOT NULL, + grant_type text NOT NULL, + options text NOT NULL +); + +CREATE TABLE route ( + domain text NOT NULL, + server_host text NOT NULL, + node text NOT NULL, + pid text NOT NULL, + local_hint text NOT NULL +); + +CREATE UNIQUE INDEX i_route ON route USING btree (domain, server_host, node, pid); + +CREATE TABLE bosh ( + sid text NOT NULL, + node text NOT NULL, + pid text NOT NULL +); + +CREATE UNIQUE INDEX i_bosh_sid ON bosh USING btree (sid); + +CREATE TABLE proxy65 ( + sid text NOT NULL, + pid_t text NOT NULL, + pid_i text NOT NULL, + node_t text NOT NULL, + node_i text NOT NULL, + jid_i text NOT NULL +); + +CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 USING btree (sid); +CREATE INDEX i_proxy65_jid ON proxy65 USING btree (jid_i); + +CREATE TABLE push_session ( + username text NOT NULL, + timestamp bigint NOT NULL, + service text NOT NULL, + node text NOT NULL, + xml text NOT NULL +); + +CREATE UNIQUE INDEX i_push_usn ON push_session USING btree (username, service, node); +CREATE INDEX i_push_ut ON push_session USING btree (username, timestamp); + +CREATE TABLE mix_channel ( + channel text NOT NULL, + service text NOT NULL, + username text NOT NULL, + domain text NOT NULL, + jid text NOT NULL, + hidden boolean NOT NULL, + hmac_key text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX i_mix_channel ON mix_channel (channel, service); +CREATE INDEX i_mix_channel_serv ON mix_channel (service); + +CREATE TABLE mix_participant ( + channel text NOT NULL, + service text NOT NULL, + username text NOT NULL, + domain text NOT NULL, + jid text NOT NULL, + id text NOT NULL, + nick text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX i_mix_participant ON mix_participant (channel, service, username, domain); + +CREATE TABLE mix_subscription ( + channel text NOT NULL, + service text NOT NULL, + username text NOT NULL, + domain text NOT NULL, + node text NOT NULL, + jid text NOT NULL +); + +CREATE UNIQUE INDEX i_mix_subscription ON mix_subscription (channel, service, username, domain, node); +CREATE INDEX i_mix_subscription_chan_serv_node ON mix_subscription (channel, service, node); + +CREATE TABLE mix_pam ( + username text NOT NULL, + channel text NOT NULL, + service text NOT NULL, + id text NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username, channel, service); + +CREATE TABLE mqtt_pub ( + username text NOT NULL, + resource text NOT NULL, + topic text NOT NULL, + qos smallint NOT NULL, + payload bytea NOT NULL, + payload_format smallint NOT NULL, + content_type text NOT NULL, + response_topic text NOT NULL, + correlation_data bytea NOT NULL, + user_properties bytea NOT NULL, + expiry bigint NOT NULL +); + +CREATE UNIQUE INDEX i_mqtt_topic ON mqtt_pub (topic); diff --git a/src/ELDAPv3.erl b/src/ELDAPv3.erl index 494573164..3c102e7ec 100644 --- a/src/ELDAPv3.erl +++ b/src/ELDAPv3.erl @@ -3,6 +3,7 @@ -module('ELDAPv3'). -compile(nowarn_unused_vars). +-dialyzer(no_match). -include("ELDAPv3.hrl"). -asn1_info([{vsn,'2.0.1'}, {module,'ELDAPv3'}, @@ -349,7 +350,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'enc_ExtendedRequest'(element(2,Val), [<<119>>]); extendedResp -> 'enc_ExtendedResponse'(element(2,Val), [<<120>>]); - Else -> + Else -> exit({error,{asn1,{invalid_choice_type,Else}}}) end, @@ -361,105 +362,105 @@ Tlv1 = match_tags(Tlv, TagIn), case (case Tlv1 of [CtempTlv1] -> CtempTlv1; _ -> Tlv1 end) of %% 'bindRequest' - {65536, V1} -> + {65536, V1} -> {bindRequest, 'dec_BindRequest'(V1, [])}; %% 'bindResponse' - {65537, V1} -> + {65537, V1} -> {bindResponse, 'dec_BindResponse'(V1, [])}; %% 'unbindRequest' - {65538, V1} -> + {65538, V1} -> {unbindRequest, decode_null(V1,[])}; %% 'searchRequest' - {65539, V1} -> + {65539, V1} -> {searchRequest, 'dec_SearchRequest'(V1, [])}; %% 'searchResEntry' - {65540, V1} -> + {65540, V1} -> {searchResEntry, 'dec_SearchResultEntry'(V1, [])}; %% 'searchResDone' - {65541, V1} -> + {65541, V1} -> {searchResDone, 'dec_SearchResultDone'(V1, [])}; %% 'searchResRef' - {65555, V1} -> + {65555, V1} -> {searchResRef, 'dec_SearchResultReference'(V1, [])}; %% 'modifyRequest' - {65542, V1} -> + {65542, V1} -> {modifyRequest, 'dec_ModifyRequest'(V1, [])}; %% 'modifyResponse' - {65543, V1} -> + {65543, V1} -> {modifyResponse, 'dec_ModifyResponse'(V1, [])}; %% 'addRequest' - {65544, V1} -> + {65544, V1} -> {addRequest, 'dec_AddRequest'(V1, [])}; %% 'addResponse' - {65545, V1} -> + {65545, V1} -> {addResponse, 'dec_AddResponse'(V1, [])}; %% 'delRequest' - {65546, V1} -> + {65546, V1} -> {delRequest, decode_restricted_string(V1,[])}; %% 'delResponse' - {65547, V1} -> + {65547, V1} -> {delResponse, 'dec_DelResponse'(V1, [])}; %% 'modDNRequest' - {65548, V1} -> + {65548, V1} -> {modDNRequest, 'dec_ModifyDNRequest'(V1, [])}; %% 'modDNResponse' - {65549, V1} -> + {65549, V1} -> {modDNResponse, 'dec_ModifyDNResponse'(V1, [])}; %% 'compareRequest' - {65550, V1} -> + {65550, V1} -> {compareRequest, 'dec_CompareRequest'(V1, [])}; %% 'compareResponse' - {65551, V1} -> + {65551, V1} -> {compareResponse, 'dec_CompareResponse'(V1, [])}; %% 'abandonRequest' - {65552, V1} -> + {65552, V1} -> {abandonRequest, decode_integer(V1,{0,2147483647},[])}; %% 'extendedReq' - {65559, V1} -> + {65559, V1} -> {extendedReq, 'dec_ExtendedRequest'(V1, [])}; %% 'extendedResp' - {65560, V1} -> + {65560, V1} -> {extendedResp, 'dec_ExtendedResponse'(V1, [])}; - Else -> + Else -> exit({error,{asn1,{invalid_choice_tag,Else}}}) end . @@ -470,20 +471,20 @@ case (case Tlv1 of [CtempTlv1] -> CtempTlv1; _ -> Tlv1 end) of 'dec_LDAPMessage'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute messageID(1) with type INTEGER %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_integer(V1,{0,2147483647},[2]), %%------------------------------------------------- %% attribute protocolOp(2) with type CHOICE %%------------------------------------------------- -[V2|Tlv3] = Tlv2, +[V2|Tlv3] = Tlv2, Term2 = 'dec_LDAPMessage_protocolOp'(V2, []), %%------------------------------------------------- @@ -639,7 +640,7 @@ decode_restricted_string(Tlv,TagIn). {EncBytes,EncLen} = 'enc_AttributeDescriptionList_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). -'enc_AttributeDescriptionList_components'([], AccBytes, AccLen) -> +'enc_AttributeDescriptionList_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_AttributeDescriptionList_components'([H|T],AccBytes, AccLen) -> @@ -653,7 +654,7 @@ decode_restricted_string(Tlv,TagIn). 'dec_AttributeDescriptionList'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), [decode_restricted_string(V1,[4]) || V1 <- Tlv1]. @@ -708,20 +709,20 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_AttributeValueAssertion'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute attributeDesc(1) with type OCTET STRING %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute assertionValue(2) with type OCTET STRING %%------------------------------------------------- -[V2|Tlv3] = Tlv2, +[V2|Tlv3] = Tlv2, Term2 = decode_restricted_string(V2,[4]), case Tlv3 of @@ -781,7 +782,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). {EncBytes,EncLen} = 'enc_Attribute_vals_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). -'enc_Attribute_vals_components'([], AccBytes, AccLen) -> +'enc_Attribute_vals_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_Attribute_vals_components'([H|T],AccBytes, AccLen) -> @@ -790,7 +791,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_Attribute_vals'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), [decode_restricted_string(V1,[4]) || V1 <- Tlv1]. @@ -803,20 +804,20 @@ Tlv1 = match_tags(Tlv, TagIn), 'dec_Attribute'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute type(1) with type OCTET STRING %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute vals(2) with type SET OF %%------------------------------------------------- -[V2|Tlv3] = Tlv2, +[V2|Tlv3] = Tlv2, Term2 = 'dec_Attribute_vals'(V2, [17]), case Tlv3 of @@ -928,26 +929,26 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_LDAPResult'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute resultCode(1) with type ENUMERATED %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_enumerated(V1,[{success,0},{operationsError,1},{protocolError,2},{timeLimitExceeded,3},{sizeLimitExceeded,4},{compareFalse,5},{compareTrue,6},{authMethodNotSupported,7},{strongAuthRequired,8},{referral,10},{adminLimitExceeded,11},{unavailableCriticalExtension,12},{confidentialityRequired,13},{saslBindInProgress,14},{noSuchAttribute,16},{undefinedAttributeType,17},{inappropriateMatching,18},{constraintViolation,19},{attributeOrValueExists,20},{invalidAttributeSyntax,21},{noSuchObject,32},{aliasProblem,33},{invalidDNSyntax,34},{aliasDereferencingProblem,36},{inappropriateAuthentication,48},{invalidCredentials,49},{insufficientAccessRights,50},{busy,51},{unavailable,52},{unwillingToPerform,53},{loopDetect,54},{namingViolation,64},{objectClassViolation,65},{notAllowedOnNonLeaf,66},{notAllowedOnRDN,67},{entryAlreadyExists,68},{objectClassModsProhibited,69},{affectsMultipleDSAs,71},{other,80}],[10]), %%------------------------------------------------- %% attribute matchedDN(2) with type OCTET STRING %%------------------------------------------------- -[V2|Tlv3] = Tlv2, +[V2|Tlv3] = Tlv2, Term2 = decode_restricted_string(V2,[4]), %%------------------------------------------------- %% attribute errorMessage(3) with type OCTET STRING %%------------------------------------------------- -[V3|Tlv4] = Tlv3, +[V3|Tlv4] = Tlv3, Term3 = decode_restricted_string(V3,[4]), %%------------------------------------------------- @@ -977,7 +978,7 @@ end, {EncBytes,EncLen} = 'enc_Referral_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). -'enc_Referral_components'([], AccBytes, AccLen) -> +'enc_Referral_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_Referral_components'([H|T],AccBytes, AccLen) -> @@ -991,7 +992,7 @@ end, 'dec_Referral'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), [decode_restricted_string(V1,[4]) || V1 <- Tlv1]. @@ -1027,7 +1028,7 @@ decode_restricted_string(Tlv,TagIn). {EncBytes,EncLen} = 'enc_Controls_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). -'enc_Controls_components'([], AccBytes, AccLen) -> +'enc_Controls_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_Controls_components'([H|T],AccBytes, AccLen) -> @@ -1041,7 +1042,7 @@ decode_restricted_string(Tlv,TagIn). 'dec_Controls'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), ['dec_Control'(V1, [16]) || V1 <- Tlv1]. @@ -1092,14 +1093,14 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_Control'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute controlType(1) with type OCTET STRING %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- @@ -1163,26 +1164,26 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_BindRequest'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute version(1) with type INTEGER %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_integer(V1,{1,127},[2]), %%------------------------------------------------- %% attribute name(2) with type OCTET STRING %%------------------------------------------------- -[V2|Tlv3] = Tlv2, +[V2|Tlv3] = Tlv2, Term2 = decode_restricted_string(V2,[4]), %%------------------------------------------------- %% attribute authentication(3) External ELDAPv3:AuthenticationChoice %%------------------------------------------------- -[V3|Tlv4] = Tlv3, +[V3|Tlv4] = Tlv3, Term3 = 'dec_AuthenticationChoice'(V3, []), case Tlv4 of @@ -1204,7 +1205,7 @@ end, encode_restricted_string(element(2,Val), [<<128>>]); sasl -> 'enc_SaslCredentials'(element(2,Val), [<<163>>]); - Else -> + Else -> exit({error,{asn1,{invalid_choice_type,Else}}}) end, @@ -1221,15 +1222,15 @@ Tlv1 = match_tags(Tlv, TagIn), case (case Tlv1 of [CtempTlv1] -> CtempTlv1; _ -> Tlv1 end) of %% 'simple' - {131072, V1} -> + {131072, V1} -> {simple, decode_restricted_string(V1,[])}; %% 'sasl' - {131075, V1} -> + {131075, V1} -> {sasl, 'dec_SaslCredentials'(V1, [])}; - Else -> + Else -> exit({error,{asn1,{invalid_choice_tag,Else}}}) end . @@ -1268,14 +1269,14 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_SaslCredentials'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute mechanism(1) with type OCTET STRING %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- @@ -1388,26 +1389,26 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_BindResponse'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute resultCode(1) with type ENUMERATED %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_enumerated(V1,[{success,0},{operationsError,1},{protocolError,2},{timeLimitExceeded,3},{sizeLimitExceeded,4},{compareFalse,5},{compareTrue,6},{authMethodNotSupported,7},{strongAuthRequired,8},{referral,10},{adminLimitExceeded,11},{unavailableCriticalExtension,12},{confidentialityRequired,13},{saslBindInProgress,14},{noSuchAttribute,16},{undefinedAttributeType,17},{inappropriateMatching,18},{constraintViolation,19},{attributeOrValueExists,20},{invalidAttributeSyntax,21},{noSuchObject,32},{aliasProblem,33},{invalidDNSyntax,34},{aliasDereferencingProblem,36},{inappropriateAuthentication,48},{invalidCredentials,49},{insufficientAccessRights,50},{busy,51},{unavailable,52},{unwillingToPerform,53},{loopDetect,54},{namingViolation,64},{objectClassViolation,65},{notAllowedOnNonLeaf,66},{notAllowedOnRDN,67},{entryAlreadyExists,68},{objectClassModsProhibited,69},{affectsMultipleDSAs,71},{other,80}],[10]), %%------------------------------------------------- %% attribute matchedDN(2) with type OCTET STRING %%------------------------------------------------- -[V2|Tlv3] = Tlv2, +[V2|Tlv3] = Tlv2, Term2 = decode_restricted_string(V2,[4]), %%------------------------------------------------- %% attribute errorMessage(3) with type OCTET STRING %%------------------------------------------------- -[V3|Tlv4] = Tlv3, +[V3|Tlv4] = Tlv3, Term3 = decode_restricted_string(V3,[4]), %%------------------------------------------------- @@ -1525,56 +1526,56 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_SearchRequest'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute baseObject(1) with type OCTET STRING %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute scope(2) with type ENUMERATED %%------------------------------------------------- -[V2|Tlv3] = Tlv2, +[V2|Tlv3] = Tlv2, Term2 = decode_enumerated(V2,[{baseObject,0},{singleLevel,1},{wholeSubtree,2}],[10]), %%------------------------------------------------- %% attribute derefAliases(3) with type ENUMERATED %%------------------------------------------------- -[V3|Tlv4] = Tlv3, +[V3|Tlv4] = Tlv3, Term3 = decode_enumerated(V3,[{neverDerefAliases,0},{derefInSearching,1},{derefFindingBaseObj,2},{derefAlways,3}],[10]), %%------------------------------------------------- %% attribute sizeLimit(4) with type INTEGER %%------------------------------------------------- -[V4|Tlv5] = Tlv4, +[V4|Tlv5] = Tlv4, Term4 = decode_integer(V4,{0,2147483647},[2]), %%------------------------------------------------- %% attribute timeLimit(5) with type INTEGER %%------------------------------------------------- -[V5|Tlv6] = Tlv5, +[V5|Tlv6] = Tlv5, Term5 = decode_integer(V5,{0,2147483647},[2]), %%------------------------------------------------- %% attribute typesOnly(6) with type BOOLEAN %%------------------------------------------------- -[V6|Tlv7] = Tlv6, +[V6|Tlv7] = Tlv6, Term6 = decode_boolean(V6,[1]), %%------------------------------------------------- %% attribute filter(7) External ELDAPv3:Filter %%------------------------------------------------- -[V7|Tlv8] = Tlv7, +[V7|Tlv8] = Tlv7, Term7 = 'dec_Filter'(V7, []), %%------------------------------------------------- %% attribute attributes(8) External ELDAPv3:AttributeDescriptionList %%------------------------------------------------- -[V8|Tlv9] = Tlv8, +[V8|Tlv9] = Tlv8, Term8 = 'dec_AttributeDescriptionList'(V8, [16]), case Tlv9 of @@ -1612,7 +1613,7 @@ end, 'enc_AttributeValueAssertion'(element(2,Val), [<<168>>]); extensibleMatch -> 'enc_MatchingRuleAssertion'(element(2,Val), [<<169>>]); - Else -> + Else -> exit({error,{asn1,{invalid_choice_type,Else}}}) end, @@ -1629,7 +1630,7 @@ encode_tags(TagIn, EncBytes, EncLen). {EncBytes,EncLen} = 'enc_Filter_and_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). -'enc_Filter_and_components'([], AccBytes, AccLen) -> +'enc_Filter_and_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_Filter_and_components'([H|T],AccBytes, AccLen) -> @@ -1638,7 +1639,7 @@ encode_tags(TagIn, EncBytes, EncLen). 'dec_Filter_and'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), ['dec_Filter'(V1, []) || V1 <- Tlv1]. @@ -1654,7 +1655,7 @@ Tlv1 = match_tags(Tlv, TagIn), {EncBytes,EncLen} = 'enc_Filter_or_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). -'enc_Filter_or_components'([], AccBytes, AccLen) -> +'enc_Filter_or_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_Filter_or_components'([H|T],AccBytes, AccLen) -> @@ -1663,7 +1664,7 @@ Tlv1 = match_tags(Tlv, TagIn), 'dec_Filter_or'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), ['dec_Filter'(V1, []) || V1 <- Tlv1]. @@ -1679,55 +1680,55 @@ Tlv1 = match_tags(Tlv, TagIn), case (case Tlv1 of [CtempTlv1] -> CtempTlv1; _ -> Tlv1 end) of %% 'and' - {131072, V1} -> + {131072, V1} -> {'and', 'dec_Filter_and'(V1, [])}; %% 'or' - {131073, V1} -> + {131073, V1} -> {'or', 'dec_Filter_or'(V1, [])}; %% 'not' - {131074, V1} -> + {131074, V1} -> {'not', 'dec_Filter'(V1, [])}; %% 'equalityMatch' - {131075, V1} -> + {131075, V1} -> {equalityMatch, 'dec_AttributeValueAssertion'(V1, [])}; %% 'substrings' - {131076, V1} -> + {131076, V1} -> {substrings, 'dec_SubstringFilter'(V1, [])}; %% 'greaterOrEqual' - {131077, V1} -> + {131077, V1} -> {greaterOrEqual, 'dec_AttributeValueAssertion'(V1, [])}; %% 'lessOrEqual' - {131078, V1} -> + {131078, V1} -> {lessOrEqual, 'dec_AttributeValueAssertion'(V1, [])}; %% 'present' - {131079, V1} -> + {131079, V1} -> {present, decode_restricted_string(V1,[])}; %% 'approxMatch' - {131080, V1} -> + {131080, V1} -> {approxMatch, 'dec_AttributeValueAssertion'(V1, [])}; %% 'extensibleMatch' - {131081, V1} -> + {131081, V1} -> {extensibleMatch, 'dec_MatchingRuleAssertion'(V1, [])}; - Else -> + Else -> exit({error,{asn1,{invalid_choice_tag,Else}}}) end . @@ -1765,7 +1766,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). {EncBytes,EncLen} = 'enc_SubstringFilter_substrings_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). -'enc_SubstringFilter_substrings_components'([], AccBytes, AccLen) -> +'enc_SubstringFilter_substrings_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_SubstringFilter_substrings_components'([H|T],AccBytes, AccLen) -> @@ -1786,7 +1787,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). encode_restricted_string(element(2,Val), [<<129>>]); final -> encode_restricted_string(element(2,Val), [<<130>>]); - Else -> + Else -> exit({error,{asn1,{invalid_choice_type,Else}}}) end, @@ -1798,26 +1799,26 @@ Tlv1 = match_tags(Tlv, TagIn), case (case Tlv1 of [CtempTlv1] -> CtempTlv1; _ -> Tlv1 end) of %% 'initial' - {131072, V1} -> + {131072, V1} -> {initial, decode_restricted_string(V1,[])}; %% 'any' - {131073, V1} -> + {131073, V1} -> {any, decode_restricted_string(V1,[])}; %% 'final' - {131074, V1} -> + {131074, V1} -> {final, decode_restricted_string(V1,[])}; - Else -> + Else -> exit({error,{asn1,{invalid_choice_tag,Else}}}) end . 'dec_SubstringFilter_substrings'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), ['dec_SubstringFilter_substrings_SEQOF'(V1, []) || V1 <- Tlv1]. @@ -1830,20 +1831,20 @@ Tlv1 = match_tags(Tlv, TagIn), 'dec_SubstringFilter'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute type(1) with type OCTET STRING %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute substrings(2) with type SEQUENCE OF %%------------------------------------------------- -[V2|Tlv3] = Tlv2, +[V2|Tlv3] = Tlv2, Term2 = 'dec_SubstringFilter_substrings'(V2, [16]), case Tlv3 of @@ -1905,7 +1906,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_MatchingRuleAssertion'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), @@ -1932,7 +1933,7 @@ end, %%------------------------------------------------- %% attribute matchValue(3) with type OCTET STRING %%------------------------------------------------- -[V3|Tlv4] = Tlv3, +[V3|Tlv4] = Tlv3, Term3 = decode_restricted_string(V3,[131075]), %%------------------------------------------------- @@ -1981,20 +1982,20 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_SearchResultEntry'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute objectName(1) with type OCTET STRING %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute attributes(2) External ELDAPv3:PartialAttributeList %%------------------------------------------------- -[V2|Tlv3] = Tlv2, +[V2|Tlv3] = Tlv2, Term2 = 'dec_PartialAttributeList'(V2, [16]), case Tlv3 of @@ -2014,7 +2015,7 @@ end, {EncBytes,EncLen} = 'enc_PartialAttributeList_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). -'enc_PartialAttributeList_components'([], AccBytes, AccLen) -> +'enc_PartialAttributeList_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_PartialAttributeList_components'([H|T],AccBytes, AccLen) -> @@ -2053,7 +2054,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). {EncBytes,EncLen} = 'enc_PartialAttributeList_SEQOF_vals_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). -'enc_PartialAttributeList_SEQOF_vals_components'([], AccBytes, AccLen) -> +'enc_PartialAttributeList_SEQOF_vals_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_PartialAttributeList_SEQOF_vals_components'([H|T],AccBytes, AccLen) -> @@ -2062,7 +2063,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_PartialAttributeList_SEQOF_vals'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), [decode_restricted_string(V1,[4]) || V1 <- Tlv1]. @@ -2070,20 +2071,20 @@ Tlv1 = match_tags(Tlv, TagIn), 'dec_PartialAttributeList_SEQOF'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute type(1) with type OCTET STRING %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute vals(2) with type SET OF %%------------------------------------------------- -[V2|Tlv3] = Tlv2, +[V2|Tlv3] = Tlv2, Term2 = 'dec_PartialAttributeList_SEQOF_vals'(V2, [17]), case Tlv3 of @@ -2098,7 +2099,7 @@ end, 'dec_PartialAttributeList'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), ['dec_PartialAttributeList_SEQOF'(V1, [16]) || V1 <- Tlv1]. @@ -2116,7 +2117,7 @@ Tlv1 = match_tags(Tlv, TagIn), {EncBytes,EncLen} = 'enc_SearchResultReference_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). -'enc_SearchResultReference_components'([], AccBytes, AccLen) -> +'enc_SearchResultReference_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_SearchResultReference_components'([H|T],AccBytes, AccLen) -> @@ -2130,7 +2131,7 @@ Tlv1 = match_tags(Tlv, TagIn), 'dec_SearchResultReference'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), [decode_restricted_string(V1,[4]) || V1 <- Tlv1]. @@ -2188,7 +2189,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). {EncBytes,EncLen} = 'enc_ModifyRequest_modification_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). -'enc_ModifyRequest_modification_components'([], AccBytes, AccLen) -> +'enc_ModifyRequest_modification_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_ModifyRequest_modification_components'([H|T],AccBytes, AccLen) -> @@ -2224,20 +2225,20 @@ LenSoFar = EncLen1 + EncLen2, encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_ModifyRequest_modification_SEQOF'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute operation(1) with type ENUMERATED %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_enumerated(V1,[{add,0},{delete,1},{replace,2}],[10]), %%------------------------------------------------- %% attribute modification(2) External ELDAPv3:AttributeTypeAndValues %%------------------------------------------------- -[V2|Tlv3] = Tlv2, +[V2|Tlv3] = Tlv2, Term2 = 'dec_AttributeTypeAndValues'(V2, [16]), case Tlv3 of @@ -2247,7 +2248,7 @@ end, 'dec_ModifyRequest_modification'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), ['dec_ModifyRequest_modification_SEQOF'(V1, [16]) || V1 <- Tlv1]. @@ -2260,20 +2261,20 @@ Tlv1 = match_tags(Tlv, TagIn), 'dec_ModifyRequest'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute object(1) with type OCTET STRING %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute modification(2) with type SEQUENCE OF %%------------------------------------------------- -[V2|Tlv3] = Tlv2, +[V2|Tlv3] = Tlv2, Term2 = 'dec_ModifyRequest_modification'(V2, [16]), case Tlv3 of @@ -2315,7 +2316,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). {EncBytes,EncLen} = 'enc_AttributeTypeAndValues_vals_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). -'enc_AttributeTypeAndValues_vals_components'([], AccBytes, AccLen) -> +'enc_AttributeTypeAndValues_vals_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_AttributeTypeAndValues_vals_components'([H|T],AccBytes, AccLen) -> @@ -2324,7 +2325,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_AttributeTypeAndValues_vals'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), [decode_restricted_string(V1,[4]) || V1 <- Tlv1]. @@ -2337,20 +2338,20 @@ Tlv1 = match_tags(Tlv, TagIn), 'dec_AttributeTypeAndValues'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute type(1) with type OCTET STRING %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute vals(2) with type SET OF %%------------------------------------------------- -[V2|Tlv3] = Tlv2, +[V2|Tlv3] = Tlv2, Term2 = 'dec_AttributeTypeAndValues_vals'(V2, [17]), case Tlv3 of @@ -2407,20 +2408,20 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_AddRequest'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute entry(1) with type OCTET STRING %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute attributes(2) External ELDAPv3:AttributeList %%------------------------------------------------- -[V2|Tlv3] = Tlv2, +[V2|Tlv3] = Tlv2, Term2 = 'dec_AttributeList'(V2, [16]), case Tlv3 of @@ -2440,7 +2441,7 @@ end, {EncBytes,EncLen} = 'enc_AttributeList_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). -'enc_AttributeList_components'([], AccBytes, AccLen) -> +'enc_AttributeList_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_AttributeList_components'([H|T],AccBytes, AccLen) -> @@ -2479,7 +2480,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). {EncBytes,EncLen} = 'enc_AttributeList_SEQOF_vals_components'(Val,[],0), encode_tags(TagIn, EncBytes, EncLen). -'enc_AttributeList_SEQOF_vals_components'([], AccBytes, AccLen) -> +'enc_AttributeList_SEQOF_vals_components'([], AccBytes, AccLen) -> {lists:reverse(AccBytes),AccLen}; 'enc_AttributeList_SEQOF_vals_components'([H|T],AccBytes, AccLen) -> @@ -2488,7 +2489,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_AttributeList_SEQOF_vals'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), [decode_restricted_string(V1,[4]) || V1 <- Tlv1]. @@ -2496,20 +2497,20 @@ Tlv1 = match_tags(Tlv, TagIn), 'dec_AttributeList_SEQOF'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute type(1) with type OCTET STRING %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute vals(2) with type SET OF %%------------------------------------------------- -[V2|Tlv3] = Tlv2, +[V2|Tlv3] = Tlv2, Term2 = 'dec_AttributeList_SEQOF_vals'(V2, [17]), case Tlv3 of @@ -2524,7 +2525,7 @@ end, 'dec_AttributeList'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), ['dec_AttributeList_SEQOF'(V1, [16]) || V1 <- Tlv1]. @@ -2629,26 +2630,26 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_ModifyDNRequest'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute entry(1) with type OCTET STRING %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute newrdn(2) with type OCTET STRING %%------------------------------------------------- -[V2|Tlv3] = Tlv2, +[V2|Tlv3] = Tlv2, Term2 = decode_restricted_string(V2,[4]), %%------------------------------------------------- %% attribute deleteoldrdn(3) with type BOOLEAN %%------------------------------------------------- -[V3|Tlv4] = Tlv3, +[V3|Tlv4] = Tlv3, Term3 = decode_boolean(V3,[1]), %%------------------------------------------------- @@ -2715,20 +2716,20 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_CompareRequest'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute entry(1) with type OCTET STRING %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[4]), %%------------------------------------------------- %% attribute ava(2) External ELDAPv3:AttributeValueAssertion %%------------------------------------------------- -[V2|Tlv3] = Tlv2, +[V2|Tlv3] = Tlv2, Term2 = 'dec_AttributeValueAssertion'(V2, [16]), case Tlv3 of @@ -2807,14 +2808,14 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_ExtendedRequest'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute requestName(1) with type OCTET STRING %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_restricted_string(V1,[131072]), %%------------------------------------------------- @@ -2936,26 +2937,26 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_ExtendedResponse'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), %%------------------------------------------------- %% attribute resultCode(1) with type ENUMERATED %%------------------------------------------------- -[V1|Tlv2] = Tlv1, +[V1|Tlv2] = Tlv1, Term1 = decode_enumerated(V1,[{success,0},{operationsError,1},{protocolError,2},{timeLimitExceeded,3},{sizeLimitExceeded,4},{compareFalse,5},{compareTrue,6},{authMethodNotSupported,7},{strongAuthRequired,8},{referral,10},{adminLimitExceeded,11},{unavailableCriticalExtension,12},{confidentialityRequired,13},{saslBindInProgress,14},{noSuchAttribute,16},{undefinedAttributeType,17},{inappropriateMatching,18},{constraintViolation,19},{attributeOrValueExists,20},{invalidAttributeSyntax,21},{noSuchObject,32},{aliasProblem,33},{invalidDNSyntax,34},{aliasDereferencingProblem,36},{inappropriateAuthentication,48},{invalidCredentials,49},{insufficientAccessRights,50},{busy,51},{unavailable,52},{unwillingToPerform,53},{loopDetect,54},{namingViolation,64},{objectClassViolation,65},{notAllowedOnNonLeaf,66},{notAllowedOnRDN,67},{entryAlreadyExists,68},{objectClassModsProhibited,69},{affectsMultipleDSAs,71},{other,80}],[10]), %%------------------------------------------------- %% attribute matchedDN(2) with type OCTET STRING %%------------------------------------------------- -[V2|Tlv3] = Tlv2, +[V2|Tlv3] = Tlv2, Term2 = decode_restricted_string(V2,[4]), %%------------------------------------------------- %% attribute errorMessage(3) with type OCTET STRING %%------------------------------------------------- -[V3|Tlv4] = Tlv3, +[V3|Tlv4] = Tlv3, Term3 = decode_restricted_string(V3,[4]), %%------------------------------------------------- @@ -3041,7 +3042,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_PasswdModifyRequestValue'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), @@ -3110,7 +3111,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar). 'dec_PasswdModifyResponseValue'(Tlv, TagIn) -> %%------------------------------------------------- - %% decode tag and length + %% decode tag and length %%------------------------------------------------- Tlv1 = match_tags(Tlv, TagIn), diff --git a/src/acl.erl b/src/acl.erl index 595228ee9..eaa0aa50f 100644 --- a/src/acl.erl +++ b/src/acl.erl @@ -1,11 +1,5 @@ %%%---------------------------------------------------------------------- -%%% File : acl.erl -%%% Author : Alexey Shchepin -%%% Purpose : ACL support -%%% Created : 18 Jan 2003 by Alexey Shchepin -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -22,683 +16,351 @@ %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- - -module(acl). +-behaviour(gen_server). --behaviour(ejabberd_config). +-export([start_link/0]). +-export([reload_from_config/0]). +-export([match_rule/3, match_acl/3]). +-export([match_rules/4, match_acls/3]). +-export([access_rules_validator/0, access_validator/0]). +-export([validator/1, validators/0]). +-export([loaded_shared_roster_module/1]). +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). --author('alexey@process-one.net'). - --export([add_access/3, clear/0]). --export([start/0, add/3, add_list/3, add_local/3, add_list_local/3, - load_from_config/0, match_rule/3, any_rules_allowed/3, - transform_options/1, opt_type/1, acl_rule_matches/3, - acl_rule_verify/1, access_matches/3, - transform_access_rules_config/1, - parse_ip_netmask/1, - access_rules_validator/1, shaper_rules_validator/1, - normalize_spec/1, resolve_access/2]). - --include("ejabberd.hrl"). -include("logger.hrl"). --include("jid.hrl"). --record(acl, {aclname, aclspec}). --record(access, {name :: aclname(), - rules = [] :: [access_rule()]}). +-type state() :: #{hosts := [binary()]}. +-type action() :: allow | deny. +-type ip_mask() :: {inet:ip4_address(), 0..32} | {inet:ip6_address(), 0..128}. +-type access_rule() :: {acl, atom()} | acl_rule(). +-type acl_rule() :: {user, {binary(), binary()} | binary()} | + {server, binary()} | + {resource, binary()} | + {user_regexp, {misc:re_mp(), binary()} | misc:re_mp()} | + {server_regexp, misc:re_mp()} | + {resource_regexp, misc:re_mp()} | + {node_regexp, {misc:re_mp(), misc:re_mp()}} | + {user_glob, {misc:re_mp(), binary()} | misc:re_mp()} | + {server_glob, misc:re_mp()} | + {resource_glob, misc:re_mp()} | + {node_glob, {misc:re_mp(), misc:re_mp()}} | + {shared_group, {binary(), binary()} | binary()} | + {ip, ip_mask()}. +-type access() :: [{action(), [access_rule()]}]. +-type acl() :: atom() | access(). +-type match() :: #{ip => inet:ip_address(), + usr => jid:ljid(), + atom() => term()}. --type regexp() :: binary(). --type iprange() :: {inet:ip_address(), integer()} | binary(). --type glob() :: binary(). --type access_name() :: atom(). --type access_rule() :: {atom(), any()}. --type host() :: binary(). --type aclname() :: {atom(), binary() | global}. --type aclspec() :: all | none | - {user, {binary(), host()} | binary()} | - {server, binary()} | - {resource, binary()} | - {user_regexp, {regexp(), host()} | regexp()} | - {shared_group, {binary(), host()} | binary()} | - {user_regexp, {regexp(), host()} | regexp()} | - {server_regexp, regexp()} | - {resource_regexp, regexp()} | - {node_regexp, {regexp(), regexp()}} | - {user_glob, {glob(), host()} | glob()} | - {server_glob, glob()} | - {resource_glob, glob()} | - {ip, iprange()} | - {node_glob, {glob(), glob()}}. +-export_type([acl/0, acl_rule/0, access/0, access_rule/0, match/0]). --type acl() :: #acl{aclname :: aclname(), - aclspec :: aclspec()}. +%%%=================================================================== +%%% API +%%%=================================================================== +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). --export_type([acl/0]). - -start() -> - mnesia:create_table(acl, - [{ram_copies, [node()]}, {type, bag}, - {local_content, true}, - {attributes, record_info(fields, acl)}]), - mnesia:create_table(access, - [{ram_copies, [node()]}, - {local_content, true}, - {attributes, record_info(fields, access)}]), - mnesia:add_table_copy(acl, node(), ram_copies), - mnesia:add_table_copy(access, node(), ram_copies), - load_from_config(), - ok. - --spec add(binary(), aclname(), aclspec()) -> ok | {error, any()}. - -add(Host, ACLName, ACLSpec) -> - {ResL, BadNodes} = ejabberd_cluster:multicall( - ?MODULE, add_local, - [Host, ACLName, ACLSpec]), - case lists:keyfind(aborted, 1, ResL) of - false when BadNodes == [] -> - ok; - false -> - {error, {failed_nodes, BadNodes}}; - Err -> - {error, Err} - end. - -add_local(Host, ACLName, ACLSpec) -> - F = fun () -> - mnesia:write(#acl{aclname = {ACLName, Host}, - aclspec = normalize_spec(ACLSpec)}) - end, - case mnesia:transaction(F) of - {atomic, ok} -> - ok; - Err -> - Err - end. - --spec add_list(binary(), [acl()], boolean()) -> ok | {error, any()}. - -add_list(Host, ACLs, Clear) -> - {ResL, BadNodes} = ejabberd_cluster:multicall( - ?MODULE, add_list_local, - [Host, ACLs, Clear]), - case lists:keyfind(aborted, 1, ResL) of - false when BadNodes == [] -> - ok; - false -> - {error, {failed_nodes, BadNodes}}; - Err -> - {error, Err} - end. - -add_list_local(Host, ACLs, Clear) -> - F = fun () -> - if Clear -> - Ks = mnesia:select(acl, - [{{acl, {'$1', Host}, '$2'}, [], - ['$1']}]), - lists:foreach(fun (K) -> mnesia:delete({acl, {K, Host}}) - end, - Ks); - true -> ok - end, - lists:foreach(fun (ACL) -> - case ACL of - #acl{aclname = ACLName, - aclspec = ACLSpec} -> - mnesia:write(#acl{aclname = - {ACLName, - Host}, - aclspec = - normalize_spec(ACLSpec)}) - end - end, - ACLs) - end, - mnesia:transaction(F). - --spec add_access(binary() | global, - access_name(), [access_rule()]) -> ok | {error, any()}. - -add_access(Host, Access, Rules) -> - Obj = #access{name = {Access, Host}, rules = Rules}, - case mnesia:transaction(fun() -> mnesia:write(Obj) end) of - {atomic, ok} -> - ok; - Err -> - {error, Err} - end. - --spec load_from_config() -> ok. - -load_from_config() -> - Hosts = [global|?MYHOSTS], - lists:foreach( - fun(Host) -> - ACLs = ejabberd_config:get_option( - {acl, Host}, fun(V) -> V end, []), - AccessRules = ejabberd_config:get_option( - {access, Host}, fun(V) -> V end, []), - AccessRulesNew = ejabberd_config:get_option( - {access_rules, Host}, fun(V) -> V end, []), - ShaperRules = ejabberd_config:get_option( - {shaper_rules, Host}, fun(V) -> V end, []), - lists:foreach( - fun({ACLName, SpecList}) -> - lists:foreach( - fun({ACLType, ACLSpecs}) when is_list(ACLSpecs) -> - lists:foreach( - fun(ACLSpec) -> - add(Host, ACLName, - {ACLType, ACLSpec}) - end, lists:flatten(ACLSpecs)); - ({ACLType, ACLSpecs}) -> - add(Host, ACLName, {ACLType, ACLSpecs}) - end, lists:flatten(SpecList)) - end, ACLs), - lists:foreach( - fun({Access, Rules}) -> - NRules = lists:map(fun({ACL, Type}) -> - {Type, [{acl, ACL}]} - end, Rules), - add_access(Host, Access, NRules ++ [{deny, [all]}]) - end, AccessRules), - lists:foreach( - fun({Access, Rules}) -> - add_access(Host, Access, Rules) - end, AccessRulesNew), - lists:foreach( - fun({Access, Rules}) -> - add_access(Host, Access, Rules) - end, ShaperRules) - end, Hosts). - -%% Delete all previous set ACLs and Access rules -clear() -> - mnesia:clear_table(acl), - mnesia:clear_table(access), - ok. - -b(S) -> - iolist_to_binary(S). - -nodeprep(S) -> - jid:nodeprep(b(S)). - -nameprep(S) -> - jid:nameprep(b(S)). - -resourceprep(S) -> - jid:resourceprep(b(S)). - -split_user_server(Str, NormFunUsr, NormFunSrv) -> - case binary:split(Str, <<"@">>) of - [U, S] -> - {NormFunUsr(U), NormFunSrv(S)}; - _ -> - NormFunUsr(Str) - end. - -normalize_spec(Spec) -> - case Spec of - all -> all; - none -> none; - {acl, N} -> {acl, N}; - {user, {U, S}} -> {user, {nodeprep(U), nameprep(S)}}; - {user, U} -> {user, split_user_server(U, fun nodeprep/1, fun nameprep/1)}; - {shared_group, {G, H}} -> {shared_group, {b(G), nameprep(H)}}; - {shared_group, G} -> {shared_group, split_user_server(G, fun b/1, fun nameprep/1)}; - {user_regexp, {UR, S}} -> {user_regexp, {b(UR), nameprep(S)}}; - {user_regexp, UR} -> {user_regexp, split_user_server(UR, fun b/1, fun nameprep/1)}; - {node_regexp, {UR, SR}} -> {node_regexp, {b(UR), b(SR)}}; - {user_glob, {UR, S}} -> {user_glob, {b(UR), nameprep(S)}}; - {user_glob, UR} -> {user_glob, split_user_server(UR, fun b/1, fun nameprep/1)}; - {node_glob, {UR, SR}} -> {node_glob, {b(UR), b(SR)}}; - {server, S} -> {server, nameprep(S)}; - {resource, R} -> {resource, resourceprep(R)}; - {server_regexp, SR} -> {server_regexp, b(SR)}; - {resource_regexp, R} -> {resource_regexp, b(R)}; - {server_glob, S} -> {server_glob, b(S)}; - {resource_glob, R} -> {resource_glob, b(R)}; - {ip, {Net, Mask}} -> {ip, {Net, Mask}}; - {ip, S} -> - case parse_ip_netmask(b(S)) of - {ok, Net, Mask} -> - {ip, {Net, Mask}}; - error -> - ?INFO_MSG("Invalid network address: ~p", [S]), - none - end - end. - --spec any_rules_allowed(global | binary(), access_name(), - jid() | ljid() | inet:ip_address()) -> boolean(). - -any_rules_allowed(Host, Access, Entity) -> - lists:any(fun (Rule) -> - allow == acl:match_rule(Host, Rule, Entity) - end, - Access). - --spec match_rule(global | binary(), access_name(), - jid() | ljid() | inet:ip_address()) -> any(). - -match_rule(Host, Access, IP) when tuple_size(IP) == 4; - tuple_size(IP) == 8 -> - access_matches(Access, #{ip => IP}, Host); +-spec match_rule(global | binary(), atom() | access(), + jid:jid() | jid:ljid() | inet:ip_address() | match()) -> action(). +match_rule(_, all, _) -> + allow; +match_rule(_, none, _) -> + deny; +match_rule(Host, Access, Match) when is_map(Match) -> + Rules = if is_atom(Access) -> read_access(Access, Host); + true -> Access + end, + match_rules(Host, Rules, Match, deny); +match_rule(Host, Access, IP) when tuple_size(IP) == 4; tuple_size(IP) == 8 -> + match_rule(Host, Access, #{ip => IP}); match_rule(Host, Access, JID) -> - access_matches(Access, #{usr => jid:tolower(JID)}, Host). + match_rule(Host, Access, #{usr => jid:tolower(JID)}). --spec acl_rule_verify(aclspec()) -> boolean(). - -acl_rule_verify(all) -> +-spec match_acl(global | binary(), access_rule(), match()) -> boolean(). +match_acl(_Host, {acl, all}, _) -> true; -acl_rule_verify(none) -> - true; -acl_rule_verify({ip, {{A,B,C,D}, Mask}}) - when is_integer(A), is_integer(B), is_integer(C), is_integer(D), - A >= 0, A =< 255, B >= 0, B =< 255, C >= 0, C =< 255, D >= 0, D =< 255, - is_integer(Mask), Mask >= 0, Mask =< 32 -> - true; -acl_rule_verify({ip, {{A,B,C,D,E,F,G,H}, Mask}}) when - is_integer(A), is_integer(B), is_integer(C), is_integer(D), - is_integer(E), is_integer(F), is_integer(G), is_integer(H), - A >= 0, A =< 65535, B >= 0, B =< 65535, C >= 0, C =< 65535, D >= 0, D =< 65535, - E >= 0, E =< 65535, F >= 0, F =< 65535, G >= 0, G =< 65535, H >= 0, H =< 65535, - is_integer(Mask), Mask >= 0, Mask =< 64 -> - true; -acl_rule_verify({user, {U, S}}) when is_binary(U), is_binary(S) -> - true; -acl_rule_verify({user, U}) when is_binary(U) -> - true; -acl_rule_verify({server, S}) when is_binary(S) -> - true; -acl_rule_verify({resource, R}) when is_binary(R) -> - true; -acl_rule_verify({shared_group, {G, H}}) when is_binary(G), is_binary(H) -> - true; -acl_rule_verify({shared_group, G}) when is_binary(G) -> - true; -acl_rule_verify({user_regexp, {UR, S}}) when is_binary(UR), is_binary(S) -> - true; -acl_rule_verify({user_regexp, UR}) when is_binary(UR) -> - true; -acl_rule_verify({server_regexp, SR}) when is_binary(SR) -> - true; -acl_rule_verify({resource_regexp, RR}) when is_binary(RR) -> - true; -acl_rule_verify({node_regexp, {UR, SR}}) when is_binary(UR), is_binary(SR) -> - true; -acl_rule_verify({user_glob, {UR, S}}) when is_binary(UR), is_binary(S) -> - true; -acl_rule_verify({user_glob, UR}) when is_binary(UR) -> - true; -acl_rule_verify({server_glob, SR}) when is_binary(SR) -> - true; -acl_rule_verify({resource_glob, RR}) when is_binary(RR) -> - true; -acl_rule_verify({node_glob, {UR, SR}}) when is_binary(UR), is_binary(SR) -> - true; -acl_rule_verify(_Spec) -> - false. -invalid_syntax(Msg, Data) -> - throw({invalid_syntax, (str:format(Msg, Data))}). - -acl_rules_verify([{acl, Name} | Rest], true) when is_atom(Name) -> - acl_rules_verify(Rest, true); -acl_rules_verify([{acl, Name} = Rule | _Rest], false) when is_atom(Name) -> - invalid_syntax(<<"Using acl: rules not allowed: ~p">>, [Rule]); -acl_rules_verify([Rule | Rest], AllowAcl) -> - case acl_rule_verify(Rule) of - false -> - invalid_syntax(<<"Invalid rule: ~p">>, [Rule]); - true -> - acl_rules_verify(Rest, AllowAcl) - end; -acl_rules_verify([], _AllowAcl) -> - true; -acl_rules_verify(Rules, _AllowAcl) -> - invalid_syntax(<<"Not a acl rules list: ~p">>, [Rules]). - - - -all_acl_rules_matches([], _Data, _Host) -> +match_acl(_Host, {acl, none}, _) -> false; -all_acl_rules_matches(Rules, Data, Host) -> - all_acl_rules_matches2(Rules, Data, Host). - -all_acl_rules_matches2([Rule | Tail], Data, Host) -> - case acl_rule_matches(Rule, Data, Host) of - true -> - all_acl_rules_matches2(Tail, Data, Host); - false -> - false +match_acl(Host, {acl, ACLName}, Match) -> + lists:any( + fun(ACL) -> + match_acl(Host, ACL, Match) + end, read_acl(ACLName, Host)); +match_acl(_Host, {ip, {Net, Mask}}, #{ip := {IP, _Port}}) -> + misc:match_ip_mask(IP, Net, Mask); +match_acl(_Host, {ip, {Net, Mask}}, #{ip := IP}) -> + misc:match_ip_mask(IP, Net, Mask); +match_acl(_Host, {user, {U, S}}, #{usr := {U, S, _}}) -> + true; +match_acl(_Host, {user, U}, #{usr := {U, S, _}}) -> + ejabberd_router:is_my_host(S); +match_acl(_Host, {server, S}, #{usr := {_, S, _}}) -> + true; +match_acl(_Host, {resource, R}, #{usr := {_, _, R}}) -> + true; +match_acl(_Host, {shared_group, {G, H}}, #{usr := {U, S, _}}) -> + case loaded_shared_roster_module(H) of + undefined -> false; + Mod -> Mod:is_user_in_group({U, S}, G, H) end; -all_acl_rules_matches2([], _Data, _Host) -> - true. - -any_acl_rules_matches([], _Data, _Host) -> - false; -any_acl_rules_matches([Rule|Tail], Data, Host) -> - case acl_rule_matches(Rule, Data, Host) of - true -> - true; - false -> - any_acl_rules_matches(Tail, Data, Host) - end. - --spec acl_rule_matches(aclspec(), any(), global|binary()) -> boolean(). - -acl_rule_matches(all, _Data, _Host) -> - true; -acl_rule_matches({acl, all}, _Data, _Host) -> - true; -acl_rule_matches({acl, Name}, Data, Host) -> - ACLs = get_aclspecs(Name, Host), - RawACLs = lists:map(fun(#acl{aclspec = R}) -> R end, ACLs), - any_acl_rules_matches(RawACLs, Data, Host); -acl_rule_matches({ip, {Net, Mask}}, #{ip := {IP, _Port}}, _Host) -> - is_ip_match(IP, Net, Mask); -acl_rule_matches({ip, {Net, Mask}}, #{ip := IP}, _Host) -> - is_ip_match(IP, Net, Mask); -acl_rule_matches({user, {U, S}}, #{usr := {U, S, _}}, _Host) -> - true; -acl_rule_matches({user, U}, #{usr := {U, S, _}}, _Host) -> - lists:member(S, ?MYHOSTS); -acl_rule_matches({server, S}, #{usr := {_, S, _}}, _Host) -> - true; -acl_rule_matches({resource, R}, #{usr := {_, _, R}}, _Host) -> - true; -acl_rule_matches({shared_group, {G, H}}, #{usr := {U, S, _}}, _Host) -> - Mod = loaded_shared_roster_module(H), - Mod:is_user_in_group({U, S}, G, H); -acl_rule_matches({shared_group, G}, #{usr := {U, S, _}}, Host) -> - Mod = loaded_shared_roster_module(Host), - Mod:is_user_in_group({U, S}, G, Host); -acl_rule_matches({user_regexp, {UR, S}}, #{usr := {U, S, _}}, _Host) -> - is_regexp_match(U, UR); -acl_rule_matches({user_regexp, UR}, #{usr := {U, S, _}}, _Host) -> - lists:member(S, ?MYHOSTS) andalso is_regexp_match(U, UR); -acl_rule_matches({server_regexp, SR}, #{usr := {_, S, _}}, _Host) -> - is_regexp_match(S, SR); -acl_rule_matches({resource_regexp, RR}, #{usr := {_, _, R}}, _Host) -> - is_regexp_match(R, RR); -acl_rule_matches({node_regexp, {UR, SR}}, #{usr := {U, S, _}}, _Host) -> - is_regexp_match(U, UR) andalso is_regexp_match(S, SR); -acl_rule_matches({user_glob, {UR, S}}, #{usr := {U, S, _}}, _Host) -> - is_glob_match(U, UR); -acl_rule_matches({user_glob, UR}, #{usr := {U, S, _}}, _Host) -> - lists:member(S, ?MYHOSTS) andalso is_glob_match(U, UR); -acl_rule_matches({server_glob, SR}, #{usr := {_, S, _}}, _Host) -> - is_glob_match(S, SR); -acl_rule_matches({resource_glob, RR}, #{usr := {_, _, R}}, _Host) -> - is_glob_match(R, RR); -acl_rule_matches({node_glob, {UR, SR}}, #{usr := {U, S, _}}, _Host) -> - is_glob_match(U, UR) andalso is_glob_match(S, SR); -acl_rule_matches(_ACL, _Data, _Host) -> +match_acl(Host, {shared_group, G}, #{usr := {_, S, _}} = Map) -> + match_acl(Host, {shared_group, {G, S}}, Map); +match_acl(_Host, {user_regexp, {UR, S1}}, #{usr := {U, S2, _}}) -> + S1 == S2 andalso match_regexp(U, UR); +match_acl(_Host, {user_regexp, UR}, #{usr := {U, S, _}}) -> + ejabberd_router:is_my_host(S) andalso match_regexp(U, UR); +match_acl(_Host, {server_regexp, SR}, #{usr := {_, S, _}}) -> + match_regexp(S, SR); +match_acl(_Host, {resource_regexp, RR}, #{usr := {_, _, R}}) -> + match_regexp(R, RR); +match_acl(_Host, {node_regexp, {UR, SR}}, #{usr := {U, S, _}}) -> + match_regexp(U, UR) andalso match_regexp(S, SR); +match_acl(_Host, {user_glob, {UR, S1}}, #{usr := {U, S2, _}}) -> + S1 == S2 andalso match_regexp(U, UR); +match_acl(_Host, {user_glob, UR}, #{usr := {U, S, _}}) -> + ejabberd_router:is_my_host(S) andalso match_regexp(U, UR); +match_acl(_Host, {server_glob, SR}, #{usr := {_, S, _}}) -> + match_regexp(S, SR); +match_acl(_Host, {resource_glob, RR}, #{usr := {_, _, R}}) -> + match_regexp(R, RR); +match_acl(_Host, {node_glob, {UR, SR}}, #{usr := {U, S, _}}) -> + match_regexp(U, UR) andalso match_regexp(S, SR); +match_acl(_, _, _) -> false. -resolve_access(all, _Host) -> - all; -resolve_access(none, _Host) -> - none; -resolve_access(Name, Host) when is_atom(Name) -> - GAccess = mnesia:dirty_read(access, {Name, global}), - LAccess = - if Host /= global -> mnesia:dirty_read(access, {Name, Host}); - true -> [] - end, - case GAccess ++ LAccess of - [] -> - []; - AccessList -> - lists:flatmap( - fun(#access{rules = Rs}) -> - Rs - end, AccessList) - end; -resolve_access(Rules, _Host) when is_list(Rules) -> - Rules. - --spec access_matches(atom()|list(), any(), global|binary()) -> allow|deny. -access_matches(Rules, Data, Host) -> - case resolve_access(Rules, Host) of - all -> allow; - none -> deny; - RRules -> access_rules_matches(RRules, Data, Host) - end. - --spec access_rules_matches(list(), any(), global|binary()) -> any(). - -access_rules_matches(AR, Data, Host) -> - access_rules_matches(AR, Data, Host, deny). - -access_rules_matches([{Type, Acls} | Rest], Data, Host, Default) -> - case all_acl_rules_matches(Acls, Data, Host) of +-spec match_rules(global | binary(), [{T, [access_rule()]}], match(), T) -> T. +match_rules(Host, [{Return, Rules} | Rest], Match, Default) -> + case match_acls(Host, Rules, Match) of false -> - access_rules_matches(Rest, Data, Host, Default); + match_rules(Host, Rest, Match, Default); true -> - Type + Return end; -access_rules_matches([], _Data, _Host, Default) -> +match_rules(_Host, [], _Match, Default) -> Default. -get_aclspecs(ACL, Host) -> - mnesia:dirty_read(acl, {ACL, Host}) ++ mnesia:dirty_read(acl, {ACL, global}). +-spec match_acls(global | binary(), [access_rule()], match()) -> boolean(). +match_acls(_Host, [], _Match) -> + false; +match_acls(Host, Rules, Match) -> + lists:all( + fun(Rule) -> + match_acl(Host, Rule, Match) + end, Rules). -is_regexp_match(String, RegExp) -> - case ejabberd_regexp:run(String, RegExp) of - nomatch -> false; - match -> true; - {error, ErrDesc} -> - ?ERROR_MSG("Wrong regexp ~p in ACL: ~p", - [RegExp, ErrDesc]), - false +-spec reload_from_config() -> ok. +reload_from_config() -> + gen_server:call(?MODULE, reload_from_config, timer:minutes(1)). + +-spec validator(access_rules | acl) -> econf:validator(). +validator(access_rules) -> + econf:options( + #{'_' => access_rules_validator()}, + [{disallowed, [all, none]}, unique]); +validator(acl) -> + econf:options( + #{'_' => acl_validator()}, + [{disallowed, [all, none]}, unique]). + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== +-spec init([]) -> {ok, state()}. +init([]) -> + create_tab(acl), + create_tab(access), + Hosts = ejabberd_option:hosts(), + load_from_config(Hosts), + ejabberd_hooks:add(config_reloaded, ?MODULE, reload_from_config, 20), + {ok, #{hosts => Hosts}}. + +-spec handle_call(term(), term(), state()) -> {reply, ok, state()} | {noreply, state()}. +handle_call(reload_from_config, _, State) -> + NewHosts = ejabberd_option:hosts(), + load_from_config(NewHosts), + {reply, ok, State#{hosts => NewHosts}}; +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), + {noreply, State}. + +-spec handle_cast(term(), state()) -> {noreply, state()}. +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), + {noreply, State}. + +-spec handle_info(term(), state()) -> {noreply, state()}. +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), + {noreply, State}. + +-spec terminate(any(), state()) -> ok. +terminate(_Reason, _State) -> + ejabberd_hooks:delete(config_reloaded, ?MODULE, reload_from_config, 20). + +-spec code_change(term(), state(), term()) -> {ok, state()}. +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +%%%=================================================================== +%%% Table management +%%%=================================================================== +-spec load_from_config([binary()]) -> ok. +load_from_config(NewHosts) -> + ?DEBUG("Loading access rules from config", []), + load_tab(acl, NewHosts, fun ejabberd_option:acl/1), + load_tab(access, NewHosts, fun ejabberd_option:access_rules/1), + ?DEBUG("Access rules loaded successfully", []). + +-spec create_tab(atom()) -> atom(). +create_tab(Tab) -> + _ = mnesia:delete_table(Tab), + ets:new(Tab, [named_table, set, {read_concurrency, true}]). + +-spec load_tab(atom(), [binary()], fun((global | binary()) -> {atom(), list()})) -> ok. +load_tab(Tab, Hosts, Fun) -> + Old = ets:tab2list(Tab), + New = lists:flatmap( + fun(Host) -> + [{{Name, Host}, List} || {Name, List} <- Fun(Host)] + end, [global|Hosts]), + ets:insert(Tab, New), + lists:foreach( + fun({Key, _}) -> + case lists:keymember(Key, 1, New) of + false -> ets:delete(Tab, Key); + true -> ok + end + end, Old). + +-spec read_access(atom(), global | binary()) -> access(). +read_access(Name, Host) -> + case ets:lookup(access, {Name, Host}) of + [{_, Access}] -> Access; + [] -> [] end. -is_glob_match(String, Glob) -> - is_regexp_match(String, - ejabberd_regexp:sh_to_awk(Glob)). +-spec read_acl(atom(), global | binary()) -> [acl_rule()]. +read_acl(Name, Host) -> + case ets:lookup(acl, {Name, Host}) of + [{_, ACL}] -> ACL; + [] -> [] + end. -is_ip_match({_, _, _, _} = IP, {_, _, _, _} = Net, Mask) -> - IPInt = ip_to_integer(IP), - NetInt = ip_to_integer(Net), - M = bnot (1 bsl (32 - Mask) - 1), - IPInt band M =:= NetInt band M; -is_ip_match({_, _, _, _, _, _, _, _} = IP, - {_, _, _, _, _, _, _, _} = Net, Mask) -> - IPInt = ip_to_integer(IP), - NetInt = ip_to_integer(Net), - M = bnot (1 bsl (128 - Mask) - 1), - IPInt band M =:= NetInt band M; -is_ip_match(_, _, _) -> - false. +%%%=================================================================== +%%% Validators +%%%=================================================================== +validators() -> + #{ip => econf:list_or_single(econf:ip_mask()), + user => user_validator(econf:user(), econf:domain()), + user_regexp => user_validator(econf:re([unicode]), econf:domain()), + user_glob => user_validator(econf:glob([unicode]), econf:domain()), + server => econf:list_or_single(econf:domain()), + server_regexp => econf:list_or_single(econf:re([unicode])), + server_glob => econf:list_or_single(econf:glob([unicode])), + resource => econf:list_or_single(econf:resource()), + resource_regexp => econf:list_or_single(econf:re([unicode])), + resource_glob => econf:list_or_single(econf:glob([unicode])), + node_regexp => node_validator(econf:re([unicode]), econf:re([unicode])), + node_glob => node_validator(econf:glob([unicode]), econf:glob([unicode])), + shared_group => user_validator(econf:binary(), econf:domain()), + acl => econf:atom()}. -ip_to_integer({IP1, IP2, IP3, IP4}) -> - IP1 bsl 8 bor IP2 bsl 8 bor IP3 bsl 8 bor IP4; -ip_to_integer({IP1, IP2, IP3, IP4, IP5, IP6, IP7, - IP8}) -> - IP1 bsl 16 bor IP2 bsl 16 bor IP3 bsl 16 bor IP4 bsl 16 - bor IP5 - bsl 16 - bor IP6 - bsl 16 - bor IP7 - bsl 16 - bor IP8. +rule_validator() -> + rule_validator(validators()). +rule_validator(RVs) -> + econf:and_then( + econf:non_empty(econf:options(RVs, [])), + fun(Rules) -> + lists:flatmap( + fun({Type, Rs}) when is_list(Rs) -> + [{Type, R} || R <- Rs]; + (Other) -> + [Other] + end, Rules) + end). + +access_validator() -> + econf:and_then( + fun(L) when is_list(L) -> + lists:map( + fun({K, V}) -> {(econf:atom())(K), V}; + (A) -> {acl, (econf:atom())(A)} + end, lists:flatten(L)); + (A) -> + [{acl, (econf:atom())(A)}] + end, + rule_validator()). + +access_rules_validator() -> + econf:and_then( + fun(L) when is_list(L) -> + lists:map( + fun({K, V}) -> {(econf:atom())(K), V}; + (A) -> {(econf:atom())(A), [{acl, all}]} + end, lists:flatten(L)); + (Bad) -> + Bad + end, + econf:non_empty( + econf:options( + #{allow => access_validator(), + deny => access_validator()}, + []))). + +acl_validator() -> + econf:and_then( + fun(L) when is_list(L) -> lists:flatten(L); + (Bad) -> Bad + end, + rule_validator(maps:remove(acl, validators()))). + +user_validator(UV, SV) -> + econf:and_then( + econf:list_or_single( + fun({U, S}) -> + {UV(U), SV(S)}; + (M) when is_list(M) -> + (econf:map(UV, SV))(M); + (Val) -> + US = (econf:binary())(Val), + case binary:split(US, <<"@">>, [global]) of + [U, S] -> {UV(U), SV(S)}; + [U] -> UV(U); + _ -> econf:fail({bad_user, Val}) + end + end), + fun lists:flatten/1). + +node_validator(UV, SV) -> + econf:and_then( + econf:and_then( + econf:list(econf:any()), + fun lists:flatten/1), + econf:map(UV, SV)). + +%%%=================================================================== +%%% Aux +%%%=================================================================== +-spec match_regexp(iodata(), misc:re_mp()) -> boolean(). +match_regexp(Data, RegExp) -> + re:run(Data, RegExp) /= nomatch. + +-spec loaded_shared_roster_module(global | binary()) -> atom(). +loaded_shared_roster_module(global) -> + loaded_shared_roster_module(ejabberd_config:get_myname()); loaded_shared_roster_module(Host) -> case gen_mod:is_loaded(Host, mod_shared_roster_ldap) of - true -> mod_shared_roster_ldap; - false -> mod_shared_roster - end. - -parse_ip_netmask(S) -> - case str:tokens(S, <<"/">>) of - [IPStr] -> - case inet_parse:address(binary_to_list(IPStr)) of - {ok, {_, _, _, _} = IP} -> {ok, IP, 32}; - {ok, {_, _, _, _, _, _, _, _} = IP} -> {ok, IP, 128}; - _ -> error - end; - [IPStr, MaskStr] -> - case catch binary_to_integer(MaskStr) of - Mask when is_integer(Mask), Mask >= 0 -> - case inet_parse:address(binary_to_list(IPStr)) of - {ok, {_, _, _, _} = IP} when Mask =< 32 -> - {ok, IP, Mask}; - {ok, {_, _, _, _, _, _, _, _} = IP} when Mask =< 128 -> - {ok, IP, Mask}; - _ -> error - end; - _ -> error - end; - _ -> error - end. - -transform_access_rules_config(Config) when is_list(Config) -> - lists:map(fun transform_access_rules_config2/1, lists:flatten(Config)); -transform_access_rules_config(Config) -> - transform_access_rules_config([Config]). - -transform_access_rules_config2(Type) when is_integer(Type); is_atom(Type) -> - {Type, [all]}; -transform_access_rules_config2({Type, ACL}) when is_atom(ACL) -> - {Type, [{acl, ACL}]}; -transform_access_rules_config2({Res, Rules}) when is_list(Rules) -> - T = lists:map(fun({Type, Args}) when is_list(Args) -> - normalize_spec({Type, hd(lists:flatten(Args))}); - (V) -> normalize_spec(V) - end, lists:flatten(Rules)), - {Res, T}; -transform_access_rules_config2({Res, Rule}) -> - {Res, [Rule]}. - -access_rules_validator(Name) when is_atom(Name) -> - Name; -access_rules_validator(Rules0) -> - Rules = transform_access_rules_config(Rules0), - access_shaper_rules_validator(Rules, fun(allow) -> true; - (deny) -> true; - (_) -> false - end), - throw({replace_with, Rules}). - - -shaper_rules_validator(Name) when is_atom(Name) -> - Name; -shaper_rules_validator(Rules0) -> - Rules = transform_access_rules_config(Rules0), - access_shaper_rules_validator(Rules, fun(V) when is_atom(V) -> true; - (V2) when is_integer(V2) -> true; - (_) -> false - end), - throw({replace_with, Rules}). - -access_shaper_rules_validator([{Type, Acls} = Rule | Rest], RuleTypeCheck) -> - case RuleTypeCheck(Type) of - true -> - case acl_rules_verify(Acls, true) of - true -> - access_shaper_rules_validator(Rest, RuleTypeCheck); - Err -> - Err - end; + true -> mod_shared_roster_ldap; false -> - invalid_syntax(<<"Invalid rule type: ~p in rule ~p">>, [Type, Rule]) - end; -access_shaper_rules_validator([], _RuleTypeCheck) -> - true; -access_shaper_rules_validator(Value, _RuleTypeCheck) -> - invalid_syntax(<<"Not a rule definition: ~p">>, [Value]). - - -transform_options(Opts) -> - Opts1 = lists:foldl(fun transform_options/2, [], Opts), - {ACLOpts, Opts2} = lists:mapfoldl( - fun({acl, Os}, Acc) -> - {Os, Acc}; - (O, Acc) -> - {[], [O|Acc]} - end, [], Opts1), - {AccessOpts, Opts3} = lists:mapfoldl( - fun({access, Os}, Acc) -> - {Os, Acc}; - (O, Acc) -> - {[], [O|Acc]} - end, [], Opts2), - {NewAccessOpts, Opts4} = lists:mapfoldl( - fun({access_rules, Os}, Acc) -> - {Os, Acc}; - (O, Acc) -> - {[], [O|Acc]} - end, [], Opts3), - {ShaperOpts, Opts5} = lists:mapfoldl( - fun({shaper_rules, Os}, Acc) -> - {Os, Acc}; - (O, Acc) -> - {[], [O|Acc]} - end, [], Opts4), - ACLOpts1 = ejabberd_config:collect_options(lists:flatten(ACLOpts)), - AccessOpts1 = case ejabberd_config:collect_options( - lists:flatten(AccessOpts)) of - [] -> []; - L1 -> [{access, L1}] - end, - ACLOpts2 = case lists:map( - fun({ACLName, Os}) -> - {ACLName, ejabberd_config:collect_options(Os)} - end, ACLOpts1) of - [] -> []; - L2 -> [{acl, L2}] - end, - NewAccessOpts1 = case lists:map( - fun({NAName, Os}) -> - {NAName, transform_access_rules_config(Os)} - end, lists:flatten(NewAccessOpts)) of - [] -> []; - L3 -> [{access_rules, L3}] - end, - ShaperOpts1 = case lists:map( - fun({SName, Ss}) -> - {SName, transform_access_rules_config(Ss)} - end, lists:flatten(ShaperOpts)) of - [] -> []; - L4 -> [{shaper_rules, L4}] - end, - ACLOpts2 ++ AccessOpts1 ++ NewAccessOpts1 ++ ShaperOpts1 ++ Opts5. - -transform_options({acl, Name, Type}, Opts) -> - T = case Type of - all -> all; - none -> none; - {user, U} -> {user, [b(U)]}; - {user, U, S} -> {user, [[{b(U), b(S)}]]}; - {shared_group, G} -> {shared_group, [b(G)]}; - {shared_group, G, H} -> {shared_group, [[{b(G), b(H)}]]}; - {user_regexp, UR} -> {user_regexp, [b(UR)]}; - {user_regexp, UR, S} -> {user_regexp, [[{b(UR), b(S)}]]}; - {node_regexp, UR, SR} -> {node_regexp, [[{b(UR), b(SR)}]]}; - {user_glob, UR} -> {user_glob, [b(UR)]}; - {user_glob, UR, S} -> {user_glob, [[{b(UR), b(S)}]]}; - {node_glob, UR, SR} -> {node_glob, [[{b(UR), b(SR)}]]}; - {server, S} -> {server, [b(S)]}; - {resource, R} -> {resource, [b(R)]}; - {server_regexp, SR} -> {server_regexp, [b(SR)]}; - {server_glob, S} -> {server_glob, [b(S)]}; - {ip, S} -> {ip, [b(S)]}; - {resource_glob, R} -> {resource_glob, [b(R)]}; - {resource_regexp, R} -> {resource_regexp, [b(R)]} - end, - [{acl, [{Name, [T]}]}|Opts]; -transform_options({access, Name, Rules}, Opts) -> - NewRules = [{ACL, Action} || {Action, ACL} <- Rules], - [{access, [{Name, NewRules}]}|Opts]; -transform_options(Opt, Opts) -> - [Opt|Opts]. - -opt_type(access) -> fun (V) -> V end; -opt_type(access_rules) -> fun (V) -> V end; -opt_type(shaper_rules) -> fun (V) -> V end; -opt_type(acl) -> fun (V) -> V end; -opt_type(_) -> [access, acl, access_rules, shaper_rules]. + case gen_mod:is_loaded(Host, mod_shared_roster) of + true -> mod_shared_roster; + false -> undefined + end + end. diff --git a/src/cyrsasl.erl b/src/cyrsasl.erl deleted file mode 100644 index 4b0f5a26b..000000000 --- a/src/cyrsasl.erl +++ /dev/null @@ -1,212 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : cyrsasl.erl -%%% Author : Alexey Shchepin -%%% Purpose : Cyrus SASL-like library -%%% Created : 8 Mar 2003 by Alexey Shchepin -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2016 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(cyrsasl). - --behaviour(ejabberd_config). - --author('alexey@process-one.net'). - --export([start/0, register_mechanism/3, listmech/1, - server_new/7, server_start/3, server_step/2, - opt_type/1]). - --include("ejabberd.hrl"). --include("logger.hrl"). - -%% --export_type([ - mechanism/0, - mechanisms/0, - sasl_mechanism/0 -]). - --record(sasl_mechanism, - {mechanism = <<"">> :: mechanism() | '$1', - module :: atom(), - password_type = plain :: password_type() | '$2'}). - --type(mechanism() :: binary()). --type(mechanisms() :: [mechanism(),...]). --type(password_type() :: plain | digest | scram). --type(props() :: [{username, binary()} | - {authzid, binary()} | - {auth_module, atom()}]). - --type(sasl_mechanism() :: #sasl_mechanism{}). - --record(sasl_state, -{ - service, - myname, - realm, - get_password, - check_password, - check_password_digest, - mech_mod, - mech_state -}). - --callback mech_new(binary(), fun(), fun(), fun()) -> any(). --callback mech_step(any(), binary()) -> {ok, props()} | - {ok, props(), binary()} | - {continue, binary(), any()} | - {error, atom()} | - {error, atom(), binary()}. - -start() -> - ets:new(sasl_mechanism, - [named_table, public, - {keypos, #sasl_mechanism.mechanism}]), - cyrsasl_plain:start([]), - cyrsasl_digest:start([]), - cyrsasl_scram:start([]), - cyrsasl_anonymous:start([]), - cyrsasl_oauth:start([]), - ok. - -%% --spec register_mechanism(Mechanim :: mechanism(), Module :: module(), - PasswordType :: password_type()) -> any(). - -register_mechanism(Mechanism, Module, PasswordType) -> - case is_disabled(Mechanism) of - false -> - ets:insert(sasl_mechanism, - #sasl_mechanism{mechanism = Mechanism, module = Module, - password_type = PasswordType}); - true -> - ?DEBUG("SASL mechanism ~p is disabled", [Mechanism]), - true - end. - -check_credentials(_State, Props) -> - User = proplists:get_value(authzid, Props, <<>>), - case jid:nodeprep(User) of - error -> {error, 'not-authorized'}; - <<"">> -> {error, 'not-authorized'}; - _LUser -> ok - end. - --spec listmech(Host ::binary()) -> Mechanisms::mechanisms(). - -listmech(Host) -> - Mechs = ets:select(sasl_mechanism, - [{#sasl_mechanism{mechanism = '$1', - password_type = '$2', _ = '_'}, - case catch ejabberd_auth:store_type(Host) of - external -> [{'==', '$2', plain}]; - scram -> [{'/=', '$2', digest}]; - {'EXIT', {undef, [{Module, store_type, []} | _]}} -> - ?WARNING_MSG("~p doesn't implement the function store_type/0", - [Module]), - []; - _Else -> [] - end, - ['$1']}]), - filter_anonymous(Host, Mechs). - -server_new(Service, ServerFQDN, UserRealm, _SecFlags, - GetPassword, CheckPassword, CheckPasswordDigest) -> - #sasl_state{service = Service, myname = ServerFQDN, - realm = UserRealm, get_password = GetPassword, - check_password = CheckPassword, - check_password_digest = CheckPasswordDigest}. - -server_start(State, Mech, undefined) -> - server_start(State, Mech, <<"">>); -server_start(State, Mech, ClientIn) -> - case lists:member(Mech, - listmech(State#sasl_state.myname)) - of - true -> - case ets:lookup(sasl_mechanism, Mech) of - [#sasl_mechanism{module = Module}] -> - {ok, MechState} = - Module:mech_new(State#sasl_state.myname, - State#sasl_state.get_password, - State#sasl_state.check_password, - State#sasl_state.check_password_digest), - server_step(State#sasl_state{mech_mod = Module, - mech_state = MechState}, - ClientIn); - _ -> {error, 'no-mechanism'} - end; - false -> {error, 'no-mechanism'} - end. - -server_step(State, undefined) -> - server_step(State, <<"">>); -server_step(State, ClientIn) -> - Module = State#sasl_state.mech_mod, - MechState = State#sasl_state.mech_state, - case Module:mech_step(MechState, ClientIn) of - {ok, Props} -> - case check_credentials(State, Props) of - ok -> {ok, Props}; - {error, Error} -> {error, Error} - end; - {ok, Props, ServerOut} -> - case check_credentials(State, Props) of - ok -> {ok, Props, ServerOut}; - {error, Error} -> {error, Error} - end; - {continue, ServerOut, NewMechState} -> - {continue, ServerOut, State#sasl_state{mech_state = NewMechState}}; - {error, Error, Username} -> - {error, Error, Username}; - {error, Error} -> - {error, Error} - end. - -%% Remove the anonymous mechanism from the list if not enabled for the given -%% host -%% --spec filter_anonymous(Host :: binary(), Mechs :: mechanisms()) -> mechanisms(). - -filter_anonymous(Host, Mechs) -> - case ejabberd_auth_anonymous:is_sasl_anonymous_enabled(Host) of - true -> Mechs; - false -> Mechs -- [<<"ANONYMOUS">>] - end. - --spec is_disabled(Mechanism :: mechanism()) -> boolean(). - -is_disabled(Mechanism) -> - Disabled = ejabberd_config:get_option( - disable_sasl_mechanisms, - fun(V) when is_list(V) -> - lists:map(fun(M) -> str:to_upper(M) end, V); - (V) -> - [str:to_upper(V)] - end, []), - lists:member(Mechanism, Disabled). - -opt_type(disable_sasl_mechanisms) -> - fun (V) when is_list(V) -> - lists:map(fun (M) -> str:to_upper(M) end, V); - (V) -> [str:to_upper(V)] - end; -opt_type(_) -> [disable_sasl_mechanisms]. diff --git a/src/cyrsasl_anonymous.erl b/src/cyrsasl_anonymous.erl deleted file mode 100644 index 15980afc5..000000000 --- a/src/cyrsasl_anonymous.erl +++ /dev/null @@ -1,52 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : cyrsasl_anonymous.erl -%%% Author : Magnus Henoch -%%% Purpose : ANONYMOUS SASL mechanism -%%% See http://www.ietf.org/internet-drafts/draft-ietf-sasl-anon-05.txt -%%% Created : 23 Aug 2005 by Magnus Henoch -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2016 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(cyrsasl_anonymous). - --protocol({xep, 175, '1.2'}). - --export([start/1, stop/0, mech_new/4, mech_step/2]). - --behaviour(cyrsasl). - --record(state, {server = <<"">> :: binary()}). - -start(_Opts) -> - cyrsasl:register_mechanism(<<"ANONYMOUS">>, ?MODULE, plain), - ok. - -stop() -> ok. - -mech_new(Host, _GetPassword, _CheckPassword, _CheckPasswordDigest) -> - {ok, #state{server = Host}}. - -mech_step(#state{server = Server} = S, ClientIn) -> - User = iolist_to_binary([randoms:get_string(), - integer_to_binary(p1_time_compat:unique_integer([positive]))]), - case ejabberd_auth:is_user_exists(User, Server) of - true -> mech_step(S, ClientIn); - false -> {ok, [{username, User}, {authzid, User}, {auth_module, ejabberd_auth_anonymous}]} - end. diff --git a/src/cyrsasl_digest.erl b/src/cyrsasl_digest.erl deleted file mode 100644 index 150aa854c..000000000 --- a/src/cyrsasl_digest.erl +++ /dev/null @@ -1,267 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : cyrsasl_digest.erl -%%% Author : Alexey Shchepin -%%% Purpose : DIGEST-MD5 SASL mechanism -%%% Created : 11 Mar 2003 by Alexey Shchepin -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2016 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(cyrsasl_digest). - --behaviour(ejabberd_config). - --author('alexey@sevcom.net'). - --export([start/1, stop/0, mech_new/4, mech_step/2, - parse/1, opt_type/1]). - --include("ejabberd.hrl"). --include("logger.hrl"). - --behaviour(cyrsasl). - --type get_password_fun() :: fun((binary()) -> {false, any()} | - {binary(), atom()}). - --type check_password_fun() :: fun((binary(), binary(), binary(), - fun((binary()) -> binary())) -> - {boolean(), any()} | - false). - --record(state, {step = 1 :: 1 | 3 | 5, - nonce = <<"">> :: binary(), - username = <<"">> :: binary(), - authzid = <<"">> :: binary(), - get_password = fun(_) -> {false, <<>>} end :: get_password_fun(), - check_password = fun(_, _, _, _, _) -> false end :: check_password_fun(), - auth_module :: atom(), - host = <<"">> :: binary(), - hostfqdn = <<"">> :: binary() | [binary()]}). - -start(_Opts) -> - Fqdn = get_local_fqdn(), - ?INFO_MSG("FQDN used to check DIGEST-MD5 SASL authentication: ~p", - [Fqdn]), - cyrsasl:register_mechanism(<<"DIGEST-MD5">>, ?MODULE, - digest). - -stop() -> ok. - -mech_new(Host, GetPassword, _CheckPassword, - CheckPasswordDigest) -> - {ok, - #state{step = 1, nonce = randoms:get_string(), - host = Host, hostfqdn = get_local_fqdn(), - get_password = GetPassword, - check_password = CheckPasswordDigest}}. - -mech_step(#state{step = 1, nonce = Nonce} = State, _) -> - {continue, - <<"nonce=\"", Nonce/binary, - "\",qop=\"auth\",charset=utf-8,algorithm=md5-sess">>, - State#state{step = 3}}; -mech_step(#state{step = 3, nonce = Nonce} = State, - ClientIn) -> - case parse(ClientIn) of - bad -> {error, 'bad-protocol'}; - KeyVals -> - DigestURI = proplists:get_value(<<"digest-uri">>, KeyVals, <<>>), - UserName = proplists:get_value(<<"username">>, KeyVals, <<>>), - case is_digesturi_valid(DigestURI, State#state.host, - State#state.hostfqdn) - of - false -> - ?DEBUG("User login not authorized because digest-uri " - "seems invalid: ~p (checking for Host " - "~p, FQDN ~p)", - [DigestURI, State#state.host, State#state.hostfqdn]), - {error, 'not-authorized', UserName}; - true -> - AuthzId = proplists:get_value(<<"authzid">>, KeyVals, <<>>), - case (State#state.get_password)(UserName) of - {false, _} -> {error, 'not-authorized', UserName}; - {Passwd, AuthModule} -> - case (State#state.check_password)(UserName, UserName, <<"">>, - proplists:get_value(<<"response">>, KeyVals, <<>>), - fun (PW) -> - response(KeyVals, - UserName, - PW, - Nonce, - AuthzId, - <<"AUTHENTICATE">>) - end) - of - {true, _} -> - RspAuth = response(KeyVals, UserName, Passwd, Nonce, - AuthzId, <<"">>), - {continue, <<"rspauth=", RspAuth/binary>>, - State#state{step = 5, auth_module = AuthModule, - username = UserName, - authzid = AuthzId}}; - false -> {error, 'not-authorized', UserName}; - {false, _} -> {error, 'not-authorized', UserName} - end - end - end - end; -mech_step(#state{step = 5, auth_module = AuthModule, - username = UserName, authzid = AuthzId}, - <<"">>) -> - {ok, - [{username, UserName}, {authzid, case AuthzId of - <<"">> -> UserName; - _ -> AuthzId - end - }, - {auth_module, AuthModule}]}; -mech_step(A, B) -> - ?DEBUG("SASL DIGEST: A ~p B ~p", [A, B]), - {error, 'bad-protocol'}. - -parse(S) -> parse1(binary_to_list(S), "", []). - -parse1([$= | Cs], S, Ts) -> - parse2(Cs, lists:reverse(S), "", Ts); -parse1([$, | Cs], [], Ts) -> parse1(Cs, [], Ts); -parse1([$\s | Cs], [], Ts) -> parse1(Cs, [], Ts); -parse1([C | Cs], S, Ts) -> parse1(Cs, [C | S], Ts); -parse1([], [], T) -> lists:reverse(T); -parse1([], _S, _T) -> bad. - -parse2([$" | Cs], Key, Val, Ts) -> - parse3(Cs, Key, Val, Ts); -parse2([C | Cs], Key, Val, Ts) -> - parse4(Cs, Key, [C | Val], Ts); -parse2([], _, _, _) -> bad. - -parse3([$" | Cs], Key, Val, Ts) -> - parse4(Cs, Key, Val, Ts); -parse3([$\\, C | Cs], Key, Val, Ts) -> - parse3(Cs, Key, [C | Val], Ts); -parse3([C | Cs], Key, Val, Ts) -> - parse3(Cs, Key, [C | Val], Ts); -parse3([], _, _, _) -> bad. - -parse4([$, | Cs], Key, Val, Ts) -> - parse1(Cs, "", [{list_to_binary(Key), list_to_binary(lists:reverse(Val))} | Ts]); -parse4([$\s | Cs], Key, Val, Ts) -> - parse4(Cs, Key, Val, Ts); -parse4([C | Cs], Key, Val, Ts) -> - parse4(Cs, Key, [C | Val], Ts); -parse4([], Key, Val, Ts) -> -%% @doc Check if the digest-uri is valid. -%% RFC-2831 allows to provide the IP address in Host, -%% however ejabberd doesn't allow that. -%% If the service (for example jabber.example.org) -%% is provided by several hosts (being one of them server3.example.org), -%% then acceptable digest-uris would be: -%% xmpp/server3.example.org/jabber.example.org, xmpp/server3.example.org and -%% xmpp/jabber.example.org -%% The last version is not actually allowed by the RFC, but implemented by popular clients - parse1([], "", [{list_to_binary(Key), list_to_binary(lists:reverse(Val))} | Ts]). - -is_digesturi_valid(DigestURICase, JabberDomain, - JabberFQDN) -> - DigestURI = stringprep:tolower(DigestURICase), - case catch str:tokens(DigestURI, <<"/">>) of - [<<"xmpp">>, Host] -> - IsHostFqdn = is_host_fqdn(Host, JabberFQDN), - (Host == JabberDomain) or IsHostFqdn; - [<<"xmpp">>, Host, ServName] -> - IsHostFqdn = is_host_fqdn(Host, JabberFQDN), - (ServName == JabberDomain) and IsHostFqdn; - _ -> - false - end. - -is_host_fqdn(Host, Fqdn) when is_binary(Fqdn) -> - Host == Fqdn; -is_host_fqdn(_Host, []) -> - false; -is_host_fqdn(Host, [Fqdn | _FqdnTail]) when Host == Fqdn -> - true; -is_host_fqdn(Host, [Fqdn | FqdnTail]) when Host /= Fqdn -> - is_host_fqdn(Host, FqdnTail). - -get_local_fqdn() -> - case catch get_local_fqdn2() of - Str when is_binary(Str) -> Str; - List when is_list(List) -> List; - _ -> - <<"unknown-fqdn, please configure fqdn " - "option in ejabberd.yml!">> - end. - -get_local_fqdn2() -> - case ejabberd_config:get_option( - fqdn, fun(X) -> X end) of - ConfiguredFqdn when is_binary(ConfiguredFqdn) -> - ConfiguredFqdn; - [A | _] = ConfiguredFqdns when is_binary(A) -> - ConfiguredFqdns; - undefined -> - {ok, Hostname} = inet:gethostname(), - {ok, {hostent, Fqdn, _, _, _, _}} = - inet:gethostbyname(Hostname), - list_to_binary(Fqdn) - end. - -hex(S) -> - p1_sha:to_hexlist(S). - -proplists_get_bin_value(Key, Pairs, Default) -> - case proplists:get_value(Key, Pairs, Default) of - L when is_list(L) -> - list_to_binary(L); - L2 -> - L2 - end. - -response(KeyVals, User, Passwd, Nonce, AuthzId, - A2Prefix) -> - Realm = proplists_get_bin_value(<<"realm">>, KeyVals, <<>>), - CNonce = proplists_get_bin_value(<<"cnonce">>, KeyVals, <<>>), - DigestURI = proplists_get_bin_value(<<"digest-uri">>, KeyVals, <<>>), - NC = proplists_get_bin_value(<<"nc">>, KeyVals, <<>>), - QOP = proplists_get_bin_value(<<"qop">>, KeyVals, <<>>), - MD5Hash = erlang:md5(<>), - A1 = case AuthzId of - <<"">> -> - <>; - _ -> - <> - end, - A2 = case QOP of - <<"auth">> -> - <>; - _ -> - <> - end, - T = <<(hex((erlang:md5(A1))))/binary, ":", Nonce/binary, - ":", NC/binary, ":", CNonce/binary, ":", QOP/binary, - ":", (hex((erlang:md5(A2))))/binary>>, - hex((erlang:md5(T))). - -opt_type(fqdn) -> fun iolist_to_binary/1; -opt_type(_) -> [fqdn]. diff --git a/src/cyrsasl_oauth.erl b/src/cyrsasl_oauth.erl deleted file mode 100644 index 21dedc6db..000000000 --- a/src/cyrsasl_oauth.erl +++ /dev/null @@ -1,91 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : cyrsasl_oauth.erl -%%% Author : Alexey Shchepin -%%% Purpose : X-OAUTH2 SASL mechanism -%%% Created : 17 Sep 2015 by Alexey Shchepin -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2016 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(cyrsasl_oauth). - --author('alexey@process-one.net'). - --export([start/1, stop/0, mech_new/4, mech_step/2, parse/1]). - --behaviour(cyrsasl). - --record(state, {host}). - -start(_Opts) -> - cyrsasl:register_mechanism(<<"X-OAUTH2">>, ?MODULE, plain), - ok. - -stop() -> ok. - -mech_new(Host, _GetPassword, _CheckPassword, _CheckPasswordDigest) -> - {ok, #state{host = Host}}. - -mech_step(State, ClientIn) -> - case prepare(ClientIn) of - [AuthzId, User, Token] -> - case ejabberd_oauth:check_token( - User, State#state.host, <<"sasl_auth">>, Token) of - true -> - {ok, - [{username, User}, {authzid, AuthzId}, - {auth_module, ejabberd_oauth}]}; - _ -> - {error, 'not-authorized', User} - end; - _ -> {error, 'bad-protocol'} - end. - -prepare(ClientIn) -> - case parse(ClientIn) of - [<<"">>, UserMaybeDomain, Token] -> - case parse_domain(UserMaybeDomain) of - %% login@domainpwd - [User, _Domain] -> [UserMaybeDomain, User, Token]; - %% loginpwd - [User] -> [<<"">>, User, Token] - end; - %% login@domainloginpwd - [AuthzId, User, Token] -> [AuthzId, User, Token]; - _ -> error - end. - -parse(S) -> parse1(binary_to_list(S), "", []). - -parse1([0 | Cs], S, T) -> - parse1(Cs, "", [list_to_binary(lists:reverse(S)) | T]); -parse1([C | Cs], S, T) -> parse1(Cs, [C | S], T); -%parse1([], [], T) -> -% lists:reverse(T); -parse1([], S, T) -> - lists:reverse([list_to_binary(lists:reverse(S)) | T]). - -parse_domain(S) -> parse_domain1(binary_to_list(S), "", []). - -parse_domain1([$@ | Cs], S, T) -> - parse_domain1(Cs, "", [list_to_binary(lists:reverse(S)) | T]); -parse_domain1([C | Cs], S, T) -> - parse_domain1(Cs, [C | S], T); -parse_domain1([], S, T) -> - lists:reverse([list_to_binary(lists:reverse(S)) | T]). diff --git a/src/cyrsasl_plain.erl b/src/cyrsasl_plain.erl deleted file mode 100644 index 8e9b32b99..000000000 --- a/src/cyrsasl_plain.erl +++ /dev/null @@ -1,94 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : cyrsasl_plain.erl -%%% Author : Alexey Shchepin -%%% Purpose : PLAIN SASL mechanism -%%% Created : 8 Mar 2003 by Alexey Shchepin -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2016 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(cyrsasl_plain). - --author('alexey@process-one.net'). - --export([start/1, stop/0, mech_new/4, mech_step/2, parse/1]). - --behaviour(cyrsasl). - --record(state, {check_password}). - -start(_Opts) -> - cyrsasl:register_mechanism(<<"PLAIN">>, ?MODULE, plain), - ok. - -stop() -> ok. - -mech_new(_Host, _GetPassword, CheckPassword, _CheckPasswordDigest) -> - {ok, #state{check_password = CheckPassword}}. - -mech_step(State, ClientIn) -> - case prepare(ClientIn) of - [AuthzId, User, Password] -> - case (State#state.check_password)(User, AuthzId, Password) of - {true, AuthModule} -> - {ok, - [{username, User}, {authzid, AuthzId}, - {auth_module, AuthModule}]}; - _ -> {error, 'not-authorized', User} - end; - _ -> {error, 'bad-protocol'} - end. - -prepare(ClientIn) -> - case parse(ClientIn) of - [<<"">>, UserMaybeDomain, Password] -> - case parse_domain(UserMaybeDomain) of - %% login@domainpwd - [User, _Domain] -> [User, User, Password]; - %% loginpwd - [User] -> [User, User, Password] - end; - [AuthzId, User, Password] -> - case parse_domain(AuthzId) of - %% login@domainloginpwd - [AuthzUser, _Domain] -> [AuthzUser, User, Password]; - %% loginloginpwd - [AuthzUser] -> [AuthzUser, User, Password] - end; - _ -> error - end. - -parse(S) -> parse1(binary_to_list(S), "", []). - -parse1([0 | Cs], S, T) -> - parse1(Cs, "", [list_to_binary(lists:reverse(S)) | T]); -parse1([C | Cs], S, T) -> parse1(Cs, [C | S], T); -%parse1([], [], T) -> -% lists:reverse(T); -parse1([], S, T) -> - lists:reverse([list_to_binary(lists:reverse(S)) | T]). - -parse_domain(S) -> parse_domain1(binary_to_list(S), "", []). - -parse_domain1([$@ | Cs], S, T) -> - parse_domain1(Cs, "", [list_to_binary(lists:reverse(S)) | T]); -parse_domain1([C | Cs], S, T) -> - parse_domain1(Cs, [C | S], T); -parse_domain1([], S, T) -> - lists:reverse([list_to_binary(lists:reverse(S)) | T]). diff --git a/src/cyrsasl_scram.erl b/src/cyrsasl_scram.erl deleted file mode 100644 index 1e2a5c681..000000000 --- a/src/cyrsasl_scram.erl +++ /dev/null @@ -1,219 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : cyrsasl_scram.erl -%%% Author : Stephen Röttger -%%% Purpose : SASL SCRAM authentication -%%% Created : 7 Aug 2011 by Stephen Röttger -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2016 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(cyrsasl_scram). - --author('stephen.roettger@googlemail.com'). - --protocol({rfc, 5802}). - --export([start/1, stop/0, mech_new/4, mech_step/2]). - --include("ejabberd.hrl"). --include("logger.hrl"). - --behaviour(cyrsasl). - --record(state, - {step = 2 :: 2 | 4, - stored_key = <<"">> :: binary(), - server_key = <<"">> :: binary(), - username = <<"">> :: binary(), - get_password :: fun(), - check_password :: fun(), - auth_message = <<"">> :: binary(), - client_nonce = <<"">> :: binary(), - server_nonce = <<"">> :: binary()}). - --define(SALT_LENGTH, 16). - --define(NONCE_LENGTH, 16). - -start(_Opts) -> - cyrsasl:register_mechanism(<<"SCRAM-SHA-1">>, ?MODULE, - scram). - -stop() -> ok. - -mech_new(_Host, GetPassword, _CheckPassword, - _CheckPasswordDigest) -> - {ok, #state{step = 2, get_password = GetPassword}}. - -mech_step(#state{step = 2} = State, ClientIn) -> - case re:split(ClientIn, <<",">>, [{return, binary}]) of - [_CBind, _AuthorizationIdentity, _UserNameAttribute, _ClientNonceAttribute, ExtensionAttribute | _] - when ExtensionAttribute /= [] -> - {error, 'protocol-error-extension-not-supported'}; - [CBind, _AuthorizationIdentity, UserNameAttribute, ClientNonceAttribute | _] - when (CBind == <<"y">>) or (CBind == <<"n">>) -> - case parse_attribute(UserNameAttribute) of - {error, Reason} -> {error, Reason}; - {_, EscapedUserName} -> - case unescape_username(EscapedUserName) of - error -> {error, 'protocol-error-bad-username'}; - UserName -> - case parse_attribute(ClientNonceAttribute) of - {$r, ClientNonce} -> - {Ret, _AuthModule} = (State#state.get_password)(UserName), - case {Ret, jid:resourceprep(Ret)} of - {false, _} -> {error, 'not-authorized', UserName}; - {_, error} when is_binary(Ret) -> ?WARNING_MSG("invalid plain password", []), {error, 'not-authorized', UserName}; - {Ret, _} -> - {StoredKey, ServerKey, Salt, IterationCount} = - if is_tuple(Ret) -> Ret; - true -> - TempSalt = - randoms:bytes(?SALT_LENGTH), - SaltedPassword = - scram:salted_password(Ret, - TempSalt, - ?SCRAM_DEFAULT_ITERATION_COUNT), - {scram:stored_key(scram:client_key(SaltedPassword)), - scram:server_key(SaltedPassword), - TempSalt, - ?SCRAM_DEFAULT_ITERATION_COUNT} - end, - ClientFirstMessageBare = - str:substr(ClientIn, - str:str(ClientIn, <<"n=">>)), - ServerNonce = - jlib:encode_base64(randoms:bytes(?NONCE_LENGTH)), - ServerFirstMessage = - iolist_to_binary( - ["r=", - ClientNonce, - ServerNonce, - ",", "s=", - jlib:encode_base64(Salt), - ",", "i=", - integer_to_list(IterationCount)]), - {continue, ServerFirstMessage, - State#state{step = 4, stored_key = StoredKey, - server_key = ServerKey, - auth_message = - <>, - client_nonce = ClientNonce, - server_nonce = ServerNonce, - username = UserName}} - end; - _Else -> {error, 'not-supported'} - end - end - end; - _Else -> {error, 'bad-protocol'} - end; -mech_step(#state{step = 4} = State, ClientIn) -> - case str:tokens(ClientIn, <<",">>) of - [GS2ChannelBindingAttribute, NonceAttribute, - ClientProofAttribute] -> - case parse_attribute(GS2ChannelBindingAttribute) of - {$c, CVal} -> - ChannelBindingSupport = binary:at(jlib:decode_base64(CVal), 0), - if (ChannelBindingSupport == $n) - or (ChannelBindingSupport == $y) -> - Nonce = <<(State#state.client_nonce)/binary, - (State#state.server_nonce)/binary>>, - case parse_attribute(NonceAttribute) of - {$r, CompareNonce} when CompareNonce == Nonce -> - case parse_attribute(ClientProofAttribute) of - {$p, ClientProofB64} -> - ClientProof = jlib:decode_base64(ClientProofB64), - AuthMessage = iolist_to_binary( - [State#state.auth_message, - ",", - str:substr(ClientIn, 1, - str:str(ClientIn, <<",p=">>) - - 1)]), - ClientSignature = - scram:client_signature(State#state.stored_key, - AuthMessage), - ClientKey = scram:client_key(ClientProof, - ClientSignature), - CompareStoredKey = scram:stored_key(ClientKey), - if CompareStoredKey == State#state.stored_key -> - ServerSignature = - scram:server_signature(State#state.server_key, - AuthMessage), - {ok, [{username, State#state.username}, - {authzid, State#state.username}], - <<"v=", - (jlib:encode_base64(ServerSignature))/binary>>}; - true -> {error, 'bad-auth', State#state.username} - end; - _Else -> {error, 'bad-protocol'} - end; - {$r, _} -> {error, 'bad-nonce'}; - _Else -> {error, 'bad-protocol'} - end; - true -> {error, 'bad-channel-binding'} - end; - _Else -> {error, 'bad-protocol'} - end; - _Else -> {error, 'bad-protocol'} - end. - -parse_attribute(Attribute) -> - AttributeLen = byte_size(Attribute), - if AttributeLen >= 3 -> - AttributeS = binary_to_list(Attribute), - SecondChar = lists:nth(2, AttributeS), - case is_alpha(lists:nth(1, AttributeS)) of - true -> - if SecondChar == $= -> - String = str:substr(Attribute, 3), - {lists:nth(1, AttributeS), String}; - true -> {error, 'bad-format-second-char-not-equal-sign'} - end; - _Else -> {error, 'bad-format-first-char-not-a-letter'} - end; - true -> {error, 'bad-format-attribute-too-short'} - end. - -unescape_username(<<"">>) -> <<"">>; -unescape_username(EscapedUsername) -> - Pos = str:str(EscapedUsername, <<"=">>), - if Pos == 0 -> EscapedUsername; - true -> - Start = str:substr(EscapedUsername, 1, Pos - 1), - End = str:substr(EscapedUsername, Pos), - EndLen = byte_size(End), - if EndLen < 3 -> error; - true -> - case str:substr(End, 1, 3) of - <<"=2C">> -> - <>; - <<"=3D">> -> - < + Mods = lists:filter( + fun(M) -> + case atom_to_list(M) of + "mod_" ++ _ -> true; + "Elixir.Mod" ++ _ -> true; + _ -> false + end + end, ejabberd_config:beams(all)), + format("~ts: unknown ~ts: ~ts. Did you mean ~ts?", + [yconf:format_ctx(Ctx), + format_module_type(Ctx), + format_module(Mod), + format_module(misc:best_match(Mod, Mods))]); +format_error({bad_export, {F, A}, Mod}, Ctx) + when Ctx == [listen, module]; + Ctx == [listen, request_handlers]; + Ctx == [modules] -> + Type = format_module_type(Ctx), + Slogan = yconf:format_ctx(Ctx), + case lists:member(Mod, ejabberd_config:beams(local)) of + true -> + format("~ts: '~ts' is not a ~ts", + [Slogan, format_module(Mod), Type]); + false -> + case lists:member(Mod, ejabberd_config:beams(external)) of + true -> + format("~ts: third-party ~ts '~ts' doesn't export " + "function ~ts/~B. If it's really a ~ts, " + "consider to upgrade it", + [Slogan, Type, format_module(Mod),F, A, Type]); + false -> + format("~ts: '~ts' doesn't match any known ~ts", + [Slogan, format_module(Mod), Type]) + end + end; +format_error({unknown_option, [], _} = Why, Ctx) -> + format("~ts. There are no available options", + [yconf:format_error(Why, Ctx)]); +format_error({unknown_option, Known, Opt} = Why, Ctx) -> + format("~ts. Did you mean ~ts? ~ts", + [yconf:format_error(Why, Ctx), + misc:best_match(Opt, Known), + format_known("Available options", Known)]); +format_error({bad_enum, Known, Bad} = Why, Ctx) -> + format("~ts. Did you mean ~ts? ~ts", + [yconf:format_error(Why, Ctx), + misc:best_match(Bad, Known), + format_known("Possible values", Known)]); +format_error({bad_yaml, _, _} = Why, _) -> + format_error(Why); +format_error(Reason, Ctx) -> + yconf:format_ctx(Ctx) ++ ": " ++ format_error(Reason). + +format_error({bad_db_type, _, Atom}) -> + format("unsupported database: ~ts", [Atom]); +format_error({bad_lang, Lang}) -> + format("Invalid language tag: ~ts", [Lang]); +format_error({bad_pem, Why, Path}) -> + format("Failed to read PEM file '~ts': ~ts", + [Path, pkix:format_error(Why)]); +format_error({bad_cert, Why, Path}) -> + format_error({bad_pem, Why, Path}); +format_error({bad_jwt_key, Path}) -> + format("No valid JWT key found in file: ~ts", [Path]); +format_error({bad_jwt_key_set, Path}) -> + format("JWK set contains multiple JWT keys in file: ~ts", [Path]); +format_error({bad_jid, Bad}) -> + format("Invalid XMPP address: ~ts", [Bad]); +format_error({bad_user, Bad}) -> + format("Invalid user part: ~ts", [Bad]); +format_error({bad_domain, Bad}) -> + format("Invalid domain: ~ts", [Bad]); +format_error({bad_resource, Bad}) -> + format("Invalid resource part: ~ts", [Bad]); +format_error({bad_ldap_filter, Bad}) -> + format("Invalid LDAP filter: ~ts", [Bad]); +format_error({bad_sip_uri, Bad}) -> + format("Invalid SIP URI: ~ts", [Bad]); +format_error({route_conflict, R}) -> + format("Failed to reuse route '~ts' because it's " + "already registered on a virtual host", + [R]); +format_error({listener_dup, AddrPort}) -> + format("Overlapping listeners found at ~ts", + [format_addr_port(AddrPort)]); +format_error({listener_conflict, AddrPort1, AddrPort2}) -> + format("Overlapping listeners found at ~ts and ~ts", + [format_addr_port(AddrPort1), + format_addr_port(AddrPort2)]); +format_error({invalid_syntax, Reason}) -> + format("~ts", [Reason]); +format_error({missing_module_dep, Mod, DepMod}) -> + format("module ~ts depends on module ~ts, " + "which is not found in the config", + [Mod, DepMod]); +format_error(eimp_error) -> + format("ejabberd is built without image converter support", []); +format_error({mqtt_codec, Reason}) -> + mqtt_codec:format_error(Reason); +format_error({external_module_error, Module, Error}) -> + try Module:format_error(Error) + catch _:_ -> + format("Invalid value", []) + end; +format_error(Reason) -> + yconf:format_error(Reason). + +-spec format_module(atom() | string()) -> string(). +format_module(Mod) when is_atom(Mod) -> + format_module(atom_to_list(Mod)); +format_module(Mod) -> + case Mod of + "Elixir." ++ M -> M; + M -> M + end. + +format_module_type([listen, module]) -> + "listening module"; +format_module_type([listen, request_handlers]) -> + "HTTP request handler"; +format_module_type([modules]) -> + "ejabberd module". + +format_known(_, Known) when length(Known) > 20 -> + ""; +format_known(Prefix, Known) -> + [Prefix, " are: ", format_join(Known)]. + +format_join([]) -> + "(empty)"; +format_join([H|_] = L) when is_atom(H) -> + format_join([atom_to_binary(A, utf8) || A <- L]); +format_join(L) -> + str:join(lists:sort(L), <<", ">>). + +%% All duplicated options having list-values are grouped +%% into a single option with all list-values being concatenated +-spec group_dups(list(T)) -> list(T). +group_dups(Y1) -> + lists:reverse( + lists:foldl( + fun({Option, Values}, Acc) when is_list(Values) -> + case lists:keyfind(Option, 1, Acc) of + {Option, Vals} when is_list(Vals) -> + lists:keyreplace(Option, 1, Acc, {Option, Vals ++ Values}); + _ -> + [{Option, Values}|Acc] + end; + (Other, Acc) -> + [Other|Acc] + end, [], Y1)). + +%%%=================================================================== +%%% Validators from yconf +%%%=================================================================== +pos_int() -> + yconf:pos_int(). + +pos_int(Inf) -> + yconf:pos_int(Inf). + +non_neg_int() -> + yconf:non_neg_int(). + +non_neg_int(Inf) -> + yconf:non_neg_int(Inf). + +int() -> + yconf:int(). + +int(Min, Max) -> + yconf:int(Min, Max). + +number(Min) -> + yconf:number(Min). + +octal() -> + yconf:octal(). + +binary() -> + yconf:binary(). + +binary(Re) -> + yconf:binary(Re). + +binary(Re, Opts) -> + yconf:binary(Re, Opts). + +enum(L) -> + yconf:enum(L). + +bool() -> + yconf:bool(). + +atom() -> + yconf:atom(). + +string() -> + yconf:string(). + +string(Re) -> + yconf:string(Re). + +string(Re, Opts) -> + yconf:string(Re, Opts). + +any() -> + yconf:any(). + +url() -> + yconf:url(). + +url(Schemes) -> + yconf:url(Schemes). + +file() -> + yconf:file(). + +file(Type) -> + yconf:file(Type). + +directory() -> + yconf:directory(). + +directory(Type) -> + yconf:directory(Type). + +ip() -> + yconf:ip(). + +ipv4() -> + yconf:ipv4(). + +ipv6() -> + yconf:ipv6(). + +ip_mask() -> + yconf:ip_mask(). + +port() -> + yconf:port(). + +re() -> + yconf:re(). + +re(Opts) -> + yconf:re(Opts). + +glob() -> + yconf:glob(). + +glob(Opts) -> + yconf:glob(Opts). + +path() -> + yconf:path(). + +binary_sep(Sep) -> + yconf:binary_sep(Sep). + +timeout(Units) -> + yconf:timeout(Units). + +timeout(Units, Inf) -> + yconf:timeout(Units, Inf). + +base64() -> + yconf:base64(). + +non_empty(F) -> + yconf:non_empty(F). + +list(F) -> + yconf:list(F). + +list(F, Opts) -> + yconf:list(F, Opts). + +list_or_single(F) -> + yconf:list_or_single(F). + +list_or_single(F, Opts) -> + yconf:list_or_single(F, Opts). + +map(F1, F2) -> + yconf:map(F1, F2). + +map(F1, F2, Opts) -> + yconf:map(F1, F2, Opts). + +either(F1, F2) -> + yconf:either(F1, F2). + +and_then(F1, F2) -> + yconf:and_then(F1, F2). + +options(V) -> + yconf:options(V). + +options(V, O) -> + yconf:options(V, O). + +%%%=================================================================== +%%% Custom validators +%%%=================================================================== +beam() -> + beam([]). + +beam(Exports) -> + and_then( + non_empty(binary()), + fun(<<"Elixir.", _/binary>> = Val) -> + (yconf:beam(Exports))(Val); + (<> = Val) when C >= $A, C =< $Z -> + (yconf:beam(Exports))(<<"Elixir.", Val/binary>>); + (Val) -> + (yconf:beam(Exports))(Val) + end). + +acl() -> + either( + atom(), + acl:access_rules_validator()). + +shaper() -> + either( + atom(), + ejabberd_shaper:shaper_rules_validator()). + +-spec url_or_file() -> yconf:validator({file | url, binary()}). +url_or_file() -> + either( + and_then(url(), fun(URL) -> {url, URL} end), + and_then(file(), fun(File) -> {file, File} end)). + +-spec lang() -> yconf:validator(binary()). +lang() -> + and_then( + binary(), + fun(Lang) -> + try xmpp_lang:check(Lang) + catch _:_ -> fail({bad_lang, Lang}) + end + end). + +-spec pem() -> yconf:validator(binary()). +pem() -> + and_then( + path(), + fun(Path) -> + case pkix:is_pem_file(Path) of + true -> Path; + {false, Reason} -> + fail({bad_pem, Reason, Path}) + end + end). + +-spec jid() -> yconf:validator(jid:jid()). +jid() -> + and_then( + binary(), + fun(Val) -> + try jid:decode(Val) + catch _:{bad_jid, _} = Reason -> fail(Reason) + end + end). + +-spec user() -> yconf:validator(binary()). +user() -> + and_then( + binary(), + fun(Val) -> + case jid:nodeprep(Val) of + error -> fail({bad_user, Val}); + U -> U + end + end). + +-spec domain() -> yconf:validator(binary()). +domain() -> + and_then( + non_empty(binary()), + fun(Val) -> + try jid:tolower(jid:decode(Val)) of + {<<"">>, <<"xn--", _/binary>> = Domain, <<"">>} -> + unicode:characters_to_binary(idna:decode(binary_to_list(Domain)), utf8); + {<<"">>, Domain, <<"">>} -> Domain; + _ -> fail({bad_domain, Val}) + catch _:{bad_jid, _} -> + fail({bad_domain, Val}) + end + end). + +-spec resource() -> yconf:validator(binary()). +resource() -> + and_then( + binary(), + fun(Val) -> + case jid:resourceprep(Val) of + error -> fail({bad_resource, Val}); + R -> R + end + end). + +-spec db_type(module()) -> yconf:validator(atom()). +db_type(M) -> + and_then( + atom(), + fun(T) -> + case code:ensure_loaded(db_module(M, T)) of + {module, _} -> T; + {error, _} -> + ElixirModule = "Elixir." ++ atom_to_list(T), + case code:ensure_loaded(list_to_atom(ElixirModule)) of + {module, _} -> list_to_atom(ElixirModule); + {error, _} -> fail({bad_db_type, M, T}) + end + end + end). + +-spec queue_type() -> yconf:validator(ram | file). +queue_type() -> + enum([ram, file]). + +-spec ldap_filter() -> yconf:validator(binary()). +ldap_filter() -> + and_then( + binary(), + fun(Val) -> + case eldap_filter:parse(Val) of + {ok, _} -> Val; + _ -> fail({bad_ldap_filter, Val}) + end + end). + +-ifdef(SIP). +sip_uri() -> + and_then( + binary(), + fun(Val) -> + case esip:decode_uri(Val) of + error -> fail({bad_sip_uri, Val}); + URI -> URI + end + end). +-endif. + +-spec host() -> yconf:validator(binary()). +host() -> + fun(Domain) -> + Hosts = ejabberd_config:get_option(hosts), + Domain3 = (domain())(Domain), + case lists:member(Domain3, Hosts) of + true -> fail({route_conflict, Domain}); + false -> Domain3 + end + end. + +-spec hosts() -> yconf:validator([binary()]). +hosts() -> + list(host(), [unique]). + +-spec vcard_temp() -> yconf:validator(). +vcard_temp() -> + and_then( + vcard_validator( + vcard_temp, undefined, + [{version, undefined, binary()}, + {fn, undefined, binary()}, + {n, undefined, vcard_name()}, + {nickname, undefined, binary()}, + {photo, undefined, vcard_photo()}, + {bday, undefined, binary()}, + {adr, [], list(vcard_adr())}, + {label, [], list(vcard_label())}, + {tel, [], list(vcard_tel())}, + {email, [], list(vcard_email())}, + {jabberid, undefined, binary()}, + {mailer, undefined, binary()}, + {tz, undefined, binary()}, + {geo, undefined, vcard_geo()}, + {title, undefined, binary()}, + {role, undefined, binary()}, + {logo, undefined, vcard_logo()}, + {org, undefined, vcard_org()}, + {categories, [], list(binary())}, + {note, undefined, binary()}, + {prodid, undefined, binary()}, + {rev, undefined, binary()}, + {sort_string, undefined, binary()}, + {sound, undefined, vcard_sound()}, + {uid, undefined, binary()}, + {url, undefined, binary()}, + {class, undefined, enum([confidential, private, public])}, + {key, undefined, vcard_key()}, + {desc, undefined, binary()}]), + fun(Tuple) -> + list_to_tuple(tuple_to_list(Tuple) ++ [[]]) + end). + + +-spec vcard_name() -> yconf:validator(). +vcard_name() -> + vcard_validator( + vcard_name, undefined, + [{family, undefined, binary()}, + {given, undefined, binary()}, + {middle, undefined, binary()}, + {prefix, undefined, binary()}, + {suffix, undefined, binary()}]). + +-spec vcard_photo() -> yconf:validator(). +vcard_photo() -> + vcard_validator( + vcard_photo, undefined, + [{type, undefined, binary()}, + {binval, undefined, base64()}, + {extval, undefined, binary()}]). + +-spec vcard_adr() -> yconf:validator(). +vcard_adr() -> + vcard_validator( + vcard_adr, [], + [{home, false, bool()}, + {work, false, bool()}, + {postal, false, bool()}, + {parcel, false, bool()}, + {dom, false, bool()}, + {intl, false, bool()}, + {pref, false, bool()}, + {pobox, undefined, binary()}, + {extadd, undefined, binary()}, + {street, undefined, binary()}, + {locality, undefined, binary()}, + {region, undefined, binary()}, + {pcode, undefined, binary()}, + {ctry, undefined, binary()}]). + +-spec vcard_label() -> yconf:validator(). +vcard_label() -> + vcard_validator( + vcard_label, [], + [{home, false, bool()}, + {work, false, bool()}, + {postal, false, bool()}, + {parcel, false, bool()}, + {dom, false, bool()}, + {intl, false, bool()}, + {pref, false, bool()}, + {line, [], list(binary())}]). + +-spec vcard_tel() -> yconf:validator(). +vcard_tel() -> + vcard_validator( + vcard_tel, [], + [{home, false, bool()}, + {work, false, bool()}, + {voice, false, bool()}, + {fax, false, bool()}, + {pager, false, bool()}, + {msg, false, bool()}, + {cell, false, bool()}, + {video, false, bool()}, + {bbs, false, bool()}, + {modem, false, bool()}, + {isdn, false, bool()}, + {pcs, false, bool()}, + {pref, false, bool()}, + {number, undefined, binary()}]). + +-spec vcard_email() -> yconf:validator(). +vcard_email() -> + vcard_validator( + vcard_email, [], + [{home, false, bool()}, + {work, false, bool()}, + {internet, false, bool()}, + {pref, false, bool()}, + {x400, false, bool()}, + {userid, undefined, binary()}]). + +-spec vcard_geo() -> yconf:validator(). +vcard_geo() -> + vcard_validator( + vcard_geo, undefined, + [{lat, undefined, binary()}, + {lon, undefined, binary()}]). + +-spec vcard_logo() -> yconf:validator(). +vcard_logo() -> + vcard_validator( + vcard_logo, undefined, + [{type, undefined, binary()}, + {binval, undefined, base64()}, + {extval, undefined, binary()}]). + +-spec vcard_org() -> yconf:validator(). +vcard_org() -> + vcard_validator( + vcard_org, undefined, + [{name, undefined, binary()}, + {units, [], list(binary())}]). + +-spec vcard_sound() -> yconf:validator(). +vcard_sound() -> + vcard_validator( + vcard_sound, undefined, + [{phonetic, undefined, binary()}, + {binval, undefined, base64()}, + {extval, undefined, binary()}]). + +-spec vcard_key() -> yconf:validator(). +vcard_key() -> + vcard_validator( + vcard_key, undefined, + [{type, undefined, binary()}, + {cred, undefined, binary()}]). + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +-spec db_module(module(), atom()) -> module(). +db_module(M, Type) -> + try list_to_atom(atom_to_list(M) ++ "_" ++ atom_to_list(Type)) + catch _:system_limit -> + fail({bad_length, 255}) + end. + +format_addr_port({IP, Port}) -> + IPStr = case tuple_size(IP) of + 4 -> inet:ntoa(IP); + 8 -> "[" ++ inet:ntoa(IP) ++ "]" + end, + IPStr ++ ":" ++ integer_to_list(Port). + +-spec format(iolist(), list()) -> string(). +format(Fmt, Args) -> + lists:flatten(io_lib:format(Fmt, Args)). + +-spec vcard_validator(atom(), term(), [{atom(), term(), validator()}]) -> validator(). +vcard_validator(Name, Default, Schema) -> + Defaults = [{Key, Val} || {Key, Val, _} <- Schema], + and_then( + options( + maps:from_list([{Key, Fun} || {Key, _, Fun} <- Schema]), + [{return, map}, {unique, true}]), + fun(Options) -> + merge(Defaults, Options, Name, Default) + end). + +-spec merge([{atom(), term()}], #{atom() => term()}, atom(), T) -> tuple() | T. +merge(_, Options, _, Default) when Options == #{} -> + Default; +merge(Defaults, Options, Name, _) -> + list_to_tuple([Name|[maps:get(Key, Options, Val) || {Key, Val} <- Defaults]]). diff --git a/src/ejabberd.app.src.in b/src/ejabberd.app.src.in deleted file mode 100644 index 4b0864831..000000000 --- a/src/ejabberd.app.src.in +++ /dev/null @@ -1,16 +0,0 @@ -%% $Id$ - -{application, ejabberd, - [{description, "@PACKAGE_NAME@"}, - {vsn, "@PACKAGE_VERSION@"}, - {modules, []}, - {registered, []}, - {applications, [kernel, stdlib]}, - {env, []}, - {mod, {ejabberd_app, []}}]}. - - -%% Local Variables: -%% mode: erlang -%% End: -%% vim: set filetype=erlang tabstop=8: diff --git a/src/ejabberd.app.src.script b/src/ejabberd.app.src.script new file mode 100644 index 000000000..a4e245461 --- /dev/null +++ b/src/ejabberd.app.src.script @@ -0,0 +1,56 @@ +{Vars, ElixirApps} = case file:consult(filename:join([filename:dirname(SCRIPT), "..", "vars.config"])) of + {ok, Terms} -> + Backends = [mssql, mysql, odbc, pgsql, redis, sqlite], + EBs = lists:filter(fun(Backend) -> lists:member({Backend, true}, Terms) end, Backends), + Elixirs = case proplists:get_bool(elixir, Terms) of + true -> [elixir, logger, mix]; + false -> [] + end, + + ProfileEnvironmentVariable = os:getenv("REBAR_PROFILE"), + AsProfiles = case lists:dropwhile(fun("as") -> false; (_) -> true end, + init:get_plain_arguments()) of + ["as", Profiles | _] -> string:split(Profiles, ","); + _ -> [] + end, + Terms2 = case lists:member("dev", [ProfileEnvironmentVariable | AsProfiles]) of + true -> lists:keystore(tools, 1, Terms, {tools, true}); + false -> Terms + end, + Tools = case lists:keyfind(tools, 1, Terms2) of + {tools, true} -> [observer]; + _ -> [] + end, + + {[lists:keyfind(description, 1, Terms), + lists:keyfind(vsn, 1, Terms), + {env, [{enabled_backends, EBs}]} + ], Elixirs ++ Tools}; + _Err -> + {[], []} + end, + +{application, ejabberd, + Vars ++ + [{modules, []}, + {registered, []}, + {applications, [kernel, sasl, ssl, stdlib, syntax_tools]}, + {included_applications, + [compiler, inets, mnesia, os_mon, + cache_tab, + eimp, + fast_tls, + fast_xml, + fast_yaml, + p1_acme, + p1_utils, + pkix, + stringprep, + yconf, + xmpp | ElixirApps]}, + {mod, {ejabberd_app, []}}]}. + +%% Local Variables: +%% mode: erlang +%% End: +%% vim: set filetype=erlang tabstop=8: diff --git a/src/ejabberd.erl b/src/ejabberd.erl index 5a6fc64d7..844ef7ea2 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -5,7 +5,7 @@ %%% Created : 16 Nov 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -25,31 +25,43 @@ -module(ejabberd). -author('alexey@process-one.net'). +-compile({no_auto_import, [{halt, 0}]}). --protocol({xep, 4, '2.9'}). --protocol({xep, 86, '1.0'}). --protocol({xep, 106, '1.1'}). --protocol({xep, 170, '1.0'}). --protocol({xep, 205, '1.0'}). --protocol({xep, 212, '1.0'}). --protocol({xep, 216, '1.0'}). --protocol({xep, 243, '1.0'}). --protocol({xep, 270, '1.0'}). +-protocol({rfc, 6122}). +-protocol({rfc, 7590}). +-protocol({xep, 4, '2.9', '0.5.0', "complete", ""}). +-protocol({xep, 59, '1.0', '2.1.0', "complete", ""}). +-protocol({xep, 82, '1.1.1', '2.1.0', "complete", ""}). +-protocol({xep, 86, '1.0', '0.5.0', "complete", ""}). +-protocol({xep, 106, '1.1', '0.5.0', "complete", ""}). +-protocol({xep, 170, '1.0', '17.12', "complete", ""}). +-protocol({xep, 178, '1.1', '17.03', "complete", ""}). +-protocol({xep, 205, '1.0', '1.1.2', "complete", ""}). +-protocol({xep, 368, '1.1.0', '17.09', "complete", ""}). +-protocol({xep, 386, '0.3.0', '24.02', "complete", ""}). +-protocol({xep, 388, '0.4.0', '24.02', "complete", ""}). +-protocol({xep, 440, '0.4.0', '24.02', "complete", ""}). +-protocol({xep, 474, '0.4.0', '24.02', "complete", "0.4.0 since 25.03"}). --export([start/0, stop/0, start_app/1, start_app/2, - get_pid_file/0, check_app/1]). +-export([start/0, stop/0, halt/0, start_app/1, start_app/2, + get_pid_file/0, check_apps/0, module_name/1, is_loaded/0]). -include("logger.hrl"). start() -> - %%ejabberd_cover:start(), - application:start(ejabberd). + case application:ensure_all_started(ejabberd) of + {error, Err} -> error_logger:error_msg("Failed to start ejabberd application: ~p", [Err]); + Ok -> Ok + end. stop() -> application:stop(ejabberd). - %%ejabberd_cover:stop(). -%% @spec () -> false | string() +halt() -> + ejabberd_logger:flush(), + erlang:halt(1, [{flush, true}]). + +-spec get_pid_file() -> false | string(). get_pid_file() -> case os:getenv("EJABBERD_PID_PATH") of false -> @@ -67,21 +79,15 @@ start_app(App, Type) -> StartFlag = not is_loaded(), start_app(App, Type, StartFlag). -check_app(App) -> - StartFlag = not is_loaded(), - spawn(fun() -> check_app_modules(App, StartFlag) end), - ok. - is_loaded() -> Apps = application:which_applications(), lists:keymember(ejabberd, 1, Apps). -start_app(App, Type, StartFlag) when not is_list(App) -> +start_app(App, Type, StartFlag) when is_atom(App) -> start_app([App], Type, StartFlag); start_app([App|Apps], Type, StartFlag) -> case application:start(App,Type) of ok -> - spawn(fun() -> check_app_modules(App, StartFlag) end), start_app(Apps, Type, StartFlag); {error, {already_started, _}} -> start_app(Apps, Type, StartFlag); @@ -89,23 +95,23 @@ start_app([App|Apps], Type, StartFlag) -> case lists:member(DepApp, [App|Apps]) of true -> Reason = io_lib:format( - "failed to start application '~p': " - "circular dependency on '~p' detected", + "Failed to start Erlang application '~ts': " + "circular dependency with '~ts' detected", [App, DepApp]), exit_or_halt(Reason, StartFlag); false -> start_app([DepApp,App|Apps], Type, StartFlag) end; - Err -> - Reason = io_lib:format("failed to start application '~p': ~p", - [App, Err]), + {error, Why} -> + Reason = io_lib:format( + "Failed to start Erlang application '~ts': ~ts. ~ts", + [App, format_error(Why), hint()]), exit_or_halt(Reason, StartFlag) end; start_app([], _Type, _StartFlag) -> ok. check_app_modules(App, StartFlag) -> - sleep(5000), case application:get_key(App, modules) of {ok, Mods} -> lists:foreach( @@ -114,12 +120,12 @@ check_app_modules(App, StartFlag) -> non_existing -> File = get_module_file(App, Mod), Reason = io_lib:format( - "couldn't find module ~s " - "needed for application '~p'", - [File, App]), + "Couldn't find file ~ts needed " + "for Erlang application '~ts'. ~ts", + [File, App, hint()]), exit_or_halt(Reason, StartFlag); _ -> - sleep(10) + ok end end, Mods); _ -> @@ -127,24 +133,81 @@ check_app_modules(App, StartFlag) -> ok end. +check_apps() -> + spawn( + fun() -> + Apps = [ejabberd | + [App || {App, _, _} <- application:which_applications(), + App /= ejabberd, App /= hex]], + ?DEBUG("Checking consistency of applications: ~ts", + [misc:join_atoms(Apps, <<", ">>)]), + misc:peach( + fun(App) -> + check_app_modules(App, true) + end, Apps), + ?DEBUG("All applications are intact", []), + lists:foreach(fun erlang:garbage_collect/1, processes()) + end). + +-spec exit_or_halt(iodata(), boolean()) -> no_return(). exit_or_halt(Reason, StartFlag) -> ?CRITICAL_MSG(Reason, []), if StartFlag -> %% Wait for the critical message is written in the console/log - timer:sleep(1000), - halt(string:substr(lists:flatten(Reason), 1, 199)); + halt(); true -> erlang:error(application_start_failed) end. -sleep(N) -> - timer:sleep(randoms:uniform(N)). - get_module_file(App, Mod) -> BaseName = atom_to_list(Mod), - case code:lib_dir(App, ebin) of + case code:lib_dir(App) of {error, _} -> BaseName; Dir -> - filename:join([Dir, BaseName ++ ".beam"]) + filename:join([Dir, "ebin", BaseName ++ ".beam"]) end. + +module_name([Dir, _, <> | _] = Mod) when H >= 65, H =< 90 -> + Module = str:join([elixir_name(M) || M<-tl(Mod)], <<>>), + Prefix = case elixir_name(Dir) of + <<"Ejabberd">> -> <<"Elixir.Ejabberd.">>; + Lib -> <<"Elixir.Ejabberd.", Lib/binary, ".">> + end, + misc:binary_to_atom(<>); + +module_name([<<"auth">> | T] = Mod) -> + case hd(T) of + %% T already starts with "Elixir" if an Elixir module is + %% loaded with that name, as per `econf:db_type/1` + <<"Elixir", _/binary>> -> misc:binary_to_atom(hd(T)); + _ -> module_name([<<"ejabberd">>] ++ Mod) + end; + +module_name([<<"ejabberd">> | _] = Mod) -> + Module = str:join([erlang_name(M) || M<-Mod], $_), + misc:binary_to_atom(Module); +module_name(Mod) when is_list(Mod) -> + Module = str:join([erlang_name(M) || M<-tl(Mod)], $_), + misc:binary_to_atom(Module). + +elixir_name(Atom) when is_atom(Atom) -> + elixir_name(misc:atom_to_binary(Atom)); +elixir_name(<>) when H >= 65, H =< 90 -> + <>; +elixir_name(<>) -> + <<(H-32), T/binary>>. + +erlang_name(Atom) when is_atom(Atom) -> + misc:atom_to_binary(Atom); +erlang_name(Bin) when is_binary(Bin) -> + Bin. + +format_error({Reason, File}) when is_list(Reason), is_list(File) -> + Reason ++ ": " ++ File; +format_error(Term) -> + io_lib:format("~p", [Term]). + +hint() -> + "This usually means that ejabberd or Erlang " + "was compiled/installed incorrectly.". diff --git a/src/ejabberd_access_permissions.erl b/src/ejabberd_access_permissions.erl index 60ad68a29..57b3637e3 100644 --- a/src/ejabberd_access_permissions.erl +++ b/src/ejabberd_access_permissions.erl @@ -5,7 +5,7 @@ %%% Created : 7 Sep 2016 by Paweł Chmielowski %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -29,17 +29,13 @@ -include("logger.hrl"). -behaviour(gen_server). --behavior(ejabberd_config). %% API -export([start_link/0, - parse_api_permissions/1, can_access/2, invalidate/0, - opt_type/1, - show_current_definitions/0, - register_permission_addon/2, - unregister_permission_addon/1]). + validator/0, + show_current_definitions/0]). %% gen_server callbacks -export([init/1, @@ -50,92 +46,43 @@ code_change/3]). -define(SERVER, ?MODULE). +-define(CACHE_TAB, access_permissions_cache). --record(state, { - definitions = none, - fragments_generators = [] -}). +-record(state, + {definitions = none :: none | [definition()]}). + +-type state() :: #state{}. +-type rule() :: {access, acl:access()} | + {acl, all | none | acl:acl_rule()}. +-type what() :: all | none | [atom() | {tag, atom()}]. +-type who() :: rule() | {oauth, {[binary()], [rule()]}}. +-type from() :: atom(). +-type permission() :: {binary(), {[from()], [who()], {what(), what()}}}. +-type definition() :: {binary(), {[from()], [who()], [atom()] | all}}. +-type caller_info() :: #{caller_module => module(), + caller_host => global | binary(), + tag => binary() | none, + extra_permissions => [definition()], + atom() => term()}. + +-export_type([permission/0]). %%%=================================================================== %%% API %%%=================================================================== - --spec can_access(atom(), map()) -> allow | deny. +-spec can_access(atom(), caller_info()) -> allow | deny. can_access(Cmd, CallerInfo) -> - gen_server:call(?MODULE, {can_access, Cmd, CallerInfo}). - --spec invalidate() -> ok. -invalidate() -> - gen_server:cast(?MODULE, invalidate). - --spec register_permission_addon(atom(), fun()) -> ok. -register_permission_addon(Name, Fun) -> - gen_server:call(?MODULE, {register_config_fragment_generator, Name, Fun}). - --spec unregister_permission_addon(atom()) -> ok. -unregister_permission_addon(Name) -> - gen_server:call(?MODULE, {unregister_config_fragment_generator, Name}). - --spec show_current_definitions() -> any(). -show_current_definitions() -> - gen_server:call(?MODULE, show_current_definitions). - -%%-------------------------------------------------------------------- -%% @doc -%% Starts the server -%% -%% @end -%%-------------------------------------------------------------------- --spec start_link() -> {ok, Pid :: pid()} | ignore | {error, Reason :: term()}. -start_link() -> - gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). - -%%%=================================================================== -%%% gen_server callbacks -%%%=================================================================== - -%%-------------------------------------------------------------------- -%% @private -%% @doc -%% Initializes the server -%% -%% @spec init(Args) -> {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} -%% @end -%%-------------------------------------------------------------------- --spec init(Args :: term()) -> - {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} | - {stop, Reason :: term()} | ignore. -init([]) -> - {ok, #state{}}. - -%%-------------------------------------------------------------------- -%% @private -%% @doc -%% Handling call messages -%% -%% @end -%%-------------------------------------------------------------------- --spec handle_call(Request :: term(), From :: {pid(), Tag :: term()}, - State :: #state{}) -> - {reply, Reply :: term(), NewState :: #state{}} | - {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} | - {noreply, NewState :: #state{}} | - {noreply, NewState :: #state{}, timeout() | hibernate} | - {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} | - {stop, Reason :: term(), NewState :: #state{}}. -handle_call({can_access, Cmd, CallerInfo}, _From, State) -> + Defs0 = show_current_definitions(), CallerModule = maps:get(caller_module, CallerInfo, none), Host = maps:get(caller_host, CallerInfo, global), - {State2, Defs0} = get_definitions(State), + Tag = maps:get(tag, CallerInfo, none), Defs = maps:get(extra_permissions, CallerInfo, []) ++ Defs0, Res = lists:foldl( fun({Name, _} = Def, none) -> - case matches_definition(Def, Cmd, CallerModule, Host, CallerInfo) of + case matches_definition(Def, Cmd, CallerModule, Tag, Host, CallerInfo) of true -> - ?DEBUG("Command '~p' execution allowed by rule '~s' (CallerInfo=~p)", [Cmd, Name, CallerInfo]), + ?DEBUG("Command '~p' execution allowed by rule " + "'~ts'~n (CallerInfo=~p)", [Cmd, Name, CallerInfo]), allow; _ -> none @@ -143,146 +90,123 @@ handle_call({can_access, Cmd, CallerInfo}, _From, State) -> (_, Val) -> Val end, none, Defs), - Res2 = case Res of - allow -> allow; - _ -> - ?DEBUG("Command '~p' execution denied (CallerInfo=~p)", [Cmd, CallerInfo]), - deny - end, - {reply, Res2, State2}; + case Res of + allow -> allow; + _ -> + ?DEBUG("Command '~p' execution denied~n " + "(CallerInfo=~p)", [Cmd, CallerInfo]), + deny + end. + +-spec invalidate() -> ok. +invalidate() -> + gen_server:cast(?MODULE, invalidate), + ets_cache:delete(?CACHE_TAB, definitions). + +-spec show_current_definitions() -> [definition()]. +show_current_definitions() -> + ets_cache:lookup(?CACHE_TAB, definitions, + fun() -> + {cache, gen_server:call(?MODULE, show_current_definitions)} + end). +start_link() -> + ets_cache:new(?CACHE_TAB, [{max_size, 2}]), + gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== +-spec init([]) -> {ok, state()}. +init([]) -> + ejabberd_hooks:add(config_reloaded, ?MODULE, invalidate, 90), + ets_cache:new(access_permissions), + {ok, #state{}}. + +-spec handle_call(show_current_definitions | term(), + term(), state()) -> {reply, term(), state()}. handle_call(show_current_definitions, _From, State) -> {State2, Defs} = get_definitions(State), {reply, Defs, State2}; -handle_call({register_config_fragment_generator, Name, Fun}, _From, #state{fragments_generators = Gens} = State) -> - NGens = lists:keystore(Name, 1, Gens, {Name, Fun}), - {reply, ok, State#state{fragments_generators = NGens}}; -handle_call({unregister_config_fragment_generator, Name}, _From, #state{fragments_generators = Gens} = State) -> - NGens = lists:keydelete(Name, 1, Gens), - {reply, ok, State#state{fragments_generators = NGens}}; -handle_call(_Request, _From, State) -> - {reply, ok, State}. +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), + {noreply, State}. -%%-------------------------------------------------------------------- -%% @private -%% @doc -%% Handling cast messages -%% -%% @end -%%-------------------------------------------------------------------- --spec handle_cast(Request :: term(), State :: #state{}) -> - {noreply, NewState :: #state{}} | - {noreply, NewState :: #state{}, timeout() | hibernate} | - {stop, Reason :: term(), NewState :: #state{}}. +-spec handle_cast(invalidate | term(), state()) -> {noreply, state()}. handle_cast(invalidate, State) -> {noreply, State#state{definitions = none}}; -handle_cast(_Request, State) -> +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), {noreply, State}. -%%-------------------------------------------------------------------- -%% @private -%% @doc -%% Handling all non call/cast messages -%% -%% @spec handle_info(Info, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% @end -%%-------------------------------------------------------------------- --spec handle_info(Info :: timeout() | term(), State :: #state{}) -> - {noreply, NewState :: #state{}} | - {noreply, NewState :: #state{}, timeout() | hibernate} | - {stop, Reason :: term(), NewState :: #state{}}. -handle_info(_Info, State) -> +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), {noreply, State}. -%%-------------------------------------------------------------------- -%% @private -%% @doc -%% This function is called by a gen_server when it is about to -%% terminate. It should be the opposite of Module:init/1 and do any -%% necessary cleaning up. When it returns, the gen_server terminates -%% with Reason. The return value is ignored. -%% -%% @spec terminate(Reason, State) -> void() -%% @end -%%-------------------------------------------------------------------- --spec terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()), - State :: #state{}) -> term(). terminate(_Reason, _State) -> - ok. + ejabberd_hooks:delete(config_reloaded, ?MODULE, invalidate, 90). -%%-------------------------------------------------------------------- -%% @private -%% @doc -%% Convert process state when code is changed -%% -%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} -%% @end -%%-------------------------------------------------------------------- --spec code_change(OldVsn :: term() | {down, term()}, State :: #state{}, - Extra :: term()) -> - {ok, NewState :: #state{}} | {error, Reason :: term()}. code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== - --spec get_definitions(#state{}) -> {#state{}, any()}. -get_definitions(#state{definitions = Defs, fragments_generators = Gens} = State) -> - DefaultOptions = [{<<"console commands">>, - {[ejabberd_ctl], - [{acl, all}], - {all, none}}}, - {<<"admin access">>, - {[], - [{acl, admin}], - {all, [start, stop]}}}], - NDefs = case Defs of - none -> - ApiPerms = ejabberd_config:get_option(api_permissions, fun(A) -> A end, DefaultOptions), - AllCommands = ejabberd_commands:get_commands_definition(), - Frags = lists:foldl( - fun({_Name, Generator}, Acc) -> - Acc ++ Generator() - end, [], Gens), - lists:map( - fun({Name, {From, Who, {Add, Del}}}) -> - Cmds = filter_commands_with_permissions(AllCommands, Add, Del), - {Name, {From, Who, Cmds}} - end, ApiPerms ++ Frags); - V -> - V +-spec get_definitions(state()) -> {state(), [definition()]}. +get_definitions(#state{definitions = Defs} = State) when Defs /= none -> + {State, Defs}; +get_definitions(#state{definitions = none} = State) -> + ApiPerms = ejabberd_option:api_permissions(), + AllCommands = ejabberd_commands:get_commands_definition(), + NDefs0 = lists:map( + fun({Name, {From, Who, {Add, Del}}}) -> + Cmds = filter_commands_with_permissions(AllCommands, Add, Del), + {Name, {From, Who, Cmds}} + end, ApiPerms), + NDefs = case lists:keyfind(<<"console commands">>, 1, NDefs0) of + false -> + [{<<"console commands">>, + {[ejabberd_ctl], + [{acl, all}], + filter_commands_with_permissions(AllCommands, all, none)}} | NDefs0]; + _ -> + NDefs0 end, {State#state{definitions = NDefs}, NDefs}. -matches_definition({_Name, {From, Who, What}}, Cmd, Module, Host, CallerInfo) -> +-spec matches_definition(definition(), atom(), module(), + atom(), global | binary(), caller_info()) -> boolean(). +matches_definition({_Name, {From, Who, What}}, Cmd, Module, Tag, Host, CallerInfo) -> case What == all orelse lists:member(Cmd, What) of true -> - case From == [] orelse lists:member(Module, From) of + {Tags, Modules} = lists:partition(fun({tag, _}) -> true; (_) -> false end, From), + case (Modules == [] orelse lists:member(Module, Modules)) andalso + (Tags == [] orelse lists:member({tag, Tag}, Tags)) of true -> Scope = maps:get(oauth_scope, CallerInfo, none), lists:any( - fun({access, Access}) when Scope == none -> - acl:access_matches(Access, CallerInfo, Host) == allow; - ({acl, _} = Acl) when Scope == none -> - acl:acl_rule_matches(Acl, CallerInfo, Host); - ({oauth, Scopes, List}) when Scope /= none -> - case ejabberd_oauth:scope_in_scope_list(Scope, Scopes) of - true -> - lists:any( - fun({access, Access}) -> - acl:access_matches(Access, CallerInfo, Host) == allow; - ({acl, _} = Acl) -> - acl:acl_rule_matches(Acl, CallerInfo, Host) - end, List); - _ -> - false - end; - (_) -> - false - end, Who); + fun({access, Access}) when Scope == none -> + acl:match_rule(Host, Access, CallerInfo) == allow; + ({acl, Name} = Acl) when Scope == none, is_atom(Name) -> + acl:match_acl(Host, Acl, CallerInfo); + ({acl, Acl}) when Scope == none -> + acl:match_acl(Host, Acl, CallerInfo); + ({oauth, {Scopes, List}}) when Scope /= none -> + case ejabberd_oauth:scope_in_scope_list(Scope, Scopes) of + true -> + lists:any( + fun({access, Access}) -> + acl:match_rule(Host, Access, CallerInfo) == allow; + ({acl, Name} = Acl) when is_atom(Name) -> + acl:match_acl(Host, Acl, CallerInfo); + ({acl, Acl}) -> + acl:match_acl(Host, Acl, CallerInfo) + end, List); + _ -> + false + end; + (_) -> + false + end, Who); _ -> false end; @@ -290,12 +214,15 @@ matches_definition({_Name, {From, Who, What}}, Cmd, Module, Host, CallerInfo) -> false end. +-spec filter_commands_with_permissions([#ejabberd_commands{}], what(), what()) -> [atom()]. filter_commands_with_permissions(AllCommands, Add, Del) -> CommandsAdd = filter_commands_with_patterns(AllCommands, Add, []), CommandsDel = filter_commands_with_patterns(CommandsAdd, Del, []), lists:map(fun(#ejabberd_commands{name = N}) -> N end, CommandsAdd -- CommandsDel). +-spec filter_commands_with_patterns([#ejabberd_commands{}], what(), + [#ejabberd_commands{}]) -> [#ejabberd_commands{}]. filter_commands_with_patterns([], _Patterns, Acc) -> Acc; filter_commands_with_patterns([C | CRest], Patterns, Acc) -> @@ -306,6 +233,7 @@ filter_commands_with_patterns([C | CRest], Patterns, Acc) -> filter_commands_with_patterns(CRest, Patterns, Acc) end. +-spec command_matches_patterns(#ejabberd_commands{}, what()) -> boolean(). command_matches_patterns(_, all) -> true; command_matches_patterns(_, none) -> @@ -325,124 +253,26 @@ command_matches_patterns(C, [_ | Tail]) -> command_matches_patterns(C, Tail). %%%=================================================================== -%%% Options parsing code +%%% Validators %%%=================================================================== - -parse_api_permissions(Data) when is_list(Data) -> - throw({replace_with, [parse_api_permission(Name, Args) || {Name, Args} <- Data]}). - -parse_api_permission(Name, Args) -> - {From, Who, What} = case key_split(Args, [{from, []}, {who, none}, {what, []}]) of - {error, Msg} -> - report_error(<<"~s inside api_permission '~s' section">>, [Msg, Name]); - Val -> Val - end, - {Name, {parse_from(Name, From), parse_who(Name, Who, oauth), parse_what(Name, What)}}. - -parse_from(_Name, Module) when is_atom(Module) -> - [Module]; -parse_from(Name, Modules) when is_list(Modules) -> - lists:foreach(fun(Module) when is_atom(Module) -> - ok; - (Val) -> - report_error(<<"Invalid value '~p' used inside 'from' section for api_permission '~s'">>, - [Val, Name]) - end, Modules), - Modules; -parse_from(Name, Val) -> - report_error(<<"Invalid value '~p' used inside 'from' section for api_permission '~s'">>, - [Val, Name]). - -parse_who(Name, Atom, ParseOauth) when is_atom(Atom) -> - parse_who(Name, [Atom], ParseOauth); -parse_who(Name, Defs, ParseOauth) when is_list(Defs) -> - lists:map( - fun([{access, Val}]) -> - try acl:access_rules_validator(Val) of - Rule -> - {access, Rule} - catch - throw:{invalid_syntax, Msg} -> - report_error(<<"Invalid access rule: '~s' used inside 'who' section for api_permission '~s'">>, - [Msg, Name]); - throw:{replace_with, NVal} -> - {access, NVal}; - error:_ -> - report_error(<<"Invalid access rule '~p' used inside 'who' section for api_permission '~s'">>, - [Val, Name]) - end; - ([{oauth, OauthList}]) when is_list(OauthList) -> - case ParseOauth of - oauth -> - Nested = parse_who(Name, lists:flatten(OauthList), scope), - {Scopes, Rest} = lists:partition( - fun({scope, _}) -> true; - (_) -> false - end, Nested), - case Scopes of - [] -> - report_error(<<"Oauth rule must contain at least one scope rule in 'who' section for api_permission '~s'">>, - [Name]); - _ -> - {oauth, lists:foldl(fun({scope, S}, A) -> S ++ A end, [], Scopes), Rest} - end; - scope -> - report_error(<<"Oauth rule can't be embeded inside other oauth rule in 'who' section for api_permission '~s'">>, - [Name]) - end; - ({scope, ScopeList}) -> - case ParseOauth of - oauth -> - report_error(<<"Scope can be included only inside oauth rule in 'who' section for api_permission '~s'">>, - [Name]); - scope -> - ScopeList2 = case ScopeList of - V when is_binary(V) -> [V]; - V2 when is_list(V2) -> V2; - V3 -> - report_error(<<"Invalid value for scope '~p' in 'who' section for api_permission '~s'">>, - [V3, Name]) - end, - {scope, ScopeList2} - end; - (Atom) when is_atom(Atom) -> - {acl, Atom}; - ([Other]) -> - try acl:normalize_spec(Other) of - Rule2 -> - {acl, Rule2} - catch - _:_ -> - report_error(<<"Invalid value '~p' used inside 'who' section for api_permission '~s'">>, - [Other, Name]) - end; - (Invalid) -> - report_error(<<"Invalid value '~p' used inside 'who' section for api_permission '~s'">>, - [Invalid, Name]) - end, Defs); -parse_who(Name, Val, _ParseOauth) -> - report_error(<<"Invalid value '~p' used inside 'who' section for api_permission '~s'">>, - [Val, Name]). - -parse_what(Name, Binary) when is_binary(Binary) -> - parse_what(Name, [Binary]); -parse_what(Name, Defs) when is_list(Defs) -> - {A, D} = lists:foldl( - fun(Def, {Add, Del}) -> - case parse_single_what(Def) of - {error, Err} -> - report_error(<<"~s used in value '~p' in 'what' section for api_permission '~s'">>, - [Err, Def, Name]); - all -> - {case Add of none -> none; _ -> all end, Del}; - {neg, all} -> - {none, all}; - {neg, Value} -> - {Add, case Del of L when is_list(L) -> [Value | L]; L2 -> L2 end}; - Value -> - {case Add of L when is_list(L) -> [Value | L]; L2 -> L2 end, Del} - end - end, {[], []}, Defs), +-spec parse_what([binary()]) -> {what(), what()}. +parse_what(Defs) -> + {A, D} = + lists:foldl( + fun(Def, {Add, Del}) -> + case parse_single_what(Def) of + {error, Err} -> + econf:fail({invalid_syntax, [Err, ": ", Def]}); + all -> + {case Add of none -> none; _ -> all end, Del}; + {neg, all} -> + {none, all}; + {neg, Value} -> + {Add, case Del of L when is_list(L) -> [Value | L]; L2 -> L2 end}; + Value -> + {case Add of L when is_list(L) -> [Value | L]; L2 -> L2 end, Del} + end + end, {[], []}, Defs), case {A, D} of {[], _} -> {none, all}; @@ -450,11 +280,9 @@ parse_what(Name, Defs) when is_list(Defs) -> {A2, none}; V -> V - end; -parse_what(Name, Val) -> - report_error(<<"Invalid value '~p' used inside 'what' section for api_permission '~s'">>, - [Val, Name]). + end. +-spec parse_single_what(binary()) -> atom() | {neg, atom()} | {tag, atom()} | {error, string()}. parse_single_what(<<"*">>) -> all; parse_single_what(<<"!*">>) -> @@ -462,7 +290,7 @@ parse_single_what(<<"!*">>) -> parse_single_what(<<"!", Rest/binary>>) -> case parse_single_what(Rest) of {neg, _} -> - {error, <<"Double negation">>}; + {error, "double negation"}; {error, _} = Err -> Err; V -> @@ -477,67 +305,88 @@ parse_single_what(<<"[tag:", Rest/binary>>) -> V when is_atom(V) -> {tag, V}; _ -> - {error, <<"Invalid tag">>} + {error, "invalid tag"} end; _ -> - {error, <<"Invalid tag">>} + {error, "invalid tag"} end; -parse_single_what(Binary) when is_binary(Binary) -> - case is_valid_command_name(Binary) of - true -> - binary_to_atom(Binary, latin1); - _ -> - {error, <<"Invalid value">>} - end; -parse_single_what(_) -> - {error, <<"Invalid value">>}. - -is_valid_command_name(<<>>) -> - false; -is_valid_command_name(Val) -> - is_valid_command_name2(Val). - -is_valid_command_name2(<<>>) -> - true; -is_valid_command_name2(<>) when K >= $a andalso K =< $z orelse K == $_ -> - is_valid_command_name2(Rest); -is_valid_command_name2(_) -> - false. - -key_split(Args, Fields) -> - {_, Order1, Results1, Required1} = lists:foldl( - fun({Field, Default}, {Idx, Order, Results, Required}) -> - {Idx + 1, maps:put(Field, Idx, Order), [Default | Results], Required}; - (Field, {Idx, Order, Results, Required}) -> - {Idx + 1, maps:put(Field, Idx, Order), [none | Results], maps:put(Field, 1, Required)} - end, {1, #{}, [], #{}}, Fields), - key_split(Args, list_to_tuple(Results1), Order1, Required1, #{}). - -key_split([], _Results, _Order, Required, _Duplicates) when map_size(Required) > 0 -> - parse_error(<<"Missing fields '~s">>, [str:join(maps:keys(Required), <<", ">>)]); -key_split([], Results, _Order, _Required, _Duplicates) -> - Results; -key_split([{Arg, Value} | Rest], Results, Order, Required, Duplicates) -> - case maps:find(Arg, Order) of - {ok, Idx} -> - case maps:is_key(Arg, Duplicates) of - false -> - Results2 = setelement(Idx, Results, Value), - key_split(Rest, Results2, Order, maps:remove(Arg, Required), maps:put(Arg, 1, Duplicates)); - true -> - parse_error(<<"Duplicate field '~s'">>, [Arg]) - end; - _ -> - parse_error(<<"Unknown field '~s'">>, [Arg]) +parse_single_what(B) -> + case re:run(B, "^[a-z0-9_\\-]*$") of + nomatch -> {error, "invalid command"}; + _ -> binary_to_atom(B, latin1) end. -report_error(Format, Args) -> - throw({invalid_syntax, (str:format(Format, Args))}). +validator(Map, Opts) -> + econf:and_then( + fun(L) when is_list(L) -> + lists:map( + fun({K, V}) -> {(econf:atom())(K), V}; + (A) -> {acl, (econf:atom())(A)} + end, lists:flatten(L)); + (A) -> + [{acl, (econf:atom())(A)}] + end, + econf:and_then( + econf:options(maps:merge(acl:validators(), Map), Opts), + fun(Rules) -> + lists:flatmap( + fun({Type, Rs}) when is_list(Rs) -> + case maps:is_key(Type, acl:validators()) of + true -> [{acl, {Type, R}} || R <- Rs]; + false -> [{Type, Rs}] + end; + (Other) -> + [Other] + end, Rules) + end)). -parse_error(Format, Args) -> - {error, (str:format(Format, Args))}. +validator(from) -> + fun(L) when is_list(L) -> + lists:map( + fun({K, V}) -> {(econf:enum([tag]))(K), (econf:binary())(V)}; + (A) -> (econf:enum([ejabberd_ctl, + ejabberd_web_admin, + ejabberd_xmlrpc, + mod_adhoc_api, + mod_cron, + mod_http_api]))(A) + end, lists:flatten(L)); + (A) -> + [(econf:enum([ejabberd_ctl, + ejabberd_web_admin, + ejabberd_xmlrpc, + mod_adhoc_api, + mod_cron, + mod_http_api]))(A)] + end; +validator(what) -> + econf:and_then( + econf:list_or_single(econf:non_empty(econf:binary())), + fun parse_what/1); +validator(who) -> + validator(#{access => econf:acl(), oauth => validator(oauth)}, []); +validator(oauth) -> + econf:and_then( + validator(#{access => econf:acl(), + scope => econf:non_empty( + econf:list_or_single(econf:binary()))}, + [{required, [scope]}]), + fun(Os) -> + {[Scopes], Rest} = proplists:split(Os, [scope]), + {lists:flatten([S || {_, S} <- Scopes]), Rest} + end). -opt_type(api_permissions) -> - fun parse_api_permissions/1; -opt_type(_) -> - [api_permissions]. +validator() -> + econf:map( + econf:binary(), + econf:and_then( + econf:options( + #{from => validator(from), + what => validator(what), + who => validator(who)}), + fun(Os) -> + {proplists:get_value(from, Os, []), + proplists:get_value(who, Os, none), + proplists:get_value(what, Os, {none, none})} + end), + [unique]). diff --git a/src/ejabberd_acme.erl b/src/ejabberd_acme.erl new file mode 100644 index 000000000..8b16fc727 --- /dev/null +++ b/src/ejabberd_acme.erl @@ -0,0 +1,682 @@ +%%%---------------------------------------------------------------------- +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- +-module(ejabberd_acme). +-behaviour(gen_server). + +%% API +-export([start_link/0]). +-export([default_directory_url/0]). +%% HTTP API +-export([process/2]). +%% Hooks +-export([ejabberd_started/0, register_certfiles/0, cert_expired/2]). +%% ejabberd commands +-export([get_commands_spec/0, request_certificate/1, + revoke_certificate/1, list_certificates/0]). +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). +%% WebAdmin +-export([webadmin_menu_node/3, webadmin_page_node/3]). + +-include("logger.hrl"). +-include("ejabberd_commands.hrl"). +-include("ejabberd_http.hrl"). +-include("ejabberd_web_admin.hrl"). +-include_lib("public_key/include/public_key.hrl"). +-include_lib("stdlib/include/ms_transform.hrl"). +-include_lib("xmpp/include/xmpp.hrl"). + +-define(CALL_TIMEOUT, timer:minutes(10)). + +-record(state, {}). + +-type state() :: #state{}. +-type priv_key() :: public_key:private_key(). +-type cert() :: #'OTPCertificate'{}. +-type cert_type() :: ec | rsa. +-type io_error() :: file:posix(). +-type issue_result() :: ok | p1_acme:issue_return() | + {error, {file, io_error()} | + {idna_failed, binary()}}. + +%%%=================================================================== +%%% API +%%%=================================================================== +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +-spec register_certfiles() -> ok. +register_certfiles() -> + lists:foreach(fun ejabberd_pkix:add_certfile/1, + list_certfiles()). + +-spec process([binary()], _) -> {integer(), [{binary(), binary()}], binary()}. +process([Token], _) -> + ?DEBUG("Received ACME challenge request for token: ~ts", [Token]), + try ets:lookup_element(acme_challenge, Token, 2) of + Key -> {200, [{<<"Content-Type">>, + <<"application/octet-stream">>}], + Key} + catch _:_ -> + {404, [], <<>>} + end; +process(_, _) -> + {404, [], <<>>}. + +-spec cert_expired(_, pkix:cert_info()) -> ok | stop. +cert_expired(_, #{domains := Domains, files := Files}) -> + CertFiles = list_certfiles(), + case lists:any( + fun({File, _}) -> + lists:member(File, CertFiles) + end, Files) of + true -> + gen_server:cast(?MODULE, {request, Domains}), + stop; + false -> + ok + end. + +-spec ejabberd_started() -> ok. +ejabberd_started() -> + gen_server:cast(?MODULE, ejabberd_started). + +default_directory_url() -> + <<"https://acme-v02.api.letsencrypt.org/directory">>. + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== +init([]) -> + ets:new(acme_challenge, [named_table, public]), + process_flag(trap_exit, true), + ejabberd:start_app(p1_acme), + delete_obsolete_data(), + ejabberd_hooks:add(cert_expired, ?MODULE, cert_expired, 60), + ejabberd_hooks:add(config_reloaded, ?MODULE, register_certfiles, 40), + ejabberd_hooks:add(ejabberd_started, ?MODULE, ejabberd_started, 110), + ejabberd_hooks:add(config_reloaded, ?MODULE, ejabberd_started, 110), + ejabberd_hooks:add(webadmin_menu_node, ?MODULE, webadmin_menu_node, 110), + ejabberd_hooks:add(webadmin_page_node, ?MODULE, webadmin_page_node, 110), + ejabberd_commands:register_commands(get_commands_spec()), + register_certfiles(), + {ok, #state{}}. + +handle_call({request, [_|_] = Domains}, _From, State) -> + ?INFO_MSG("Requesting new certificate for ~ts from ~ts", + [misc:format_hosts_list(Domains), directory_url()]), + {Ret, State1} = issue_request(State, Domains), + {reply, Ret, State1}; +handle_call({revoke, Cert, Key, Path}, _From, State) -> + ?INFO_MSG("Revoking certificate from file ~ts", [Path]), + {Ret, State1} = revoke_request(State, Cert, Key, Path), + {reply, Ret, State1}; +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), + {noreply, State}. + +handle_cast(ejabberd_started, State) -> + case request_on_start() of + {true, Domains} -> + ?INFO_MSG("Requesting new certificate for ~ts from ~ts", + [misc:format_hosts_list(Domains), directory_url()]), + {_, State1} = issue_request(State, Domains), + {noreply, State1}; + false -> + {noreply, State} + end; +handle_cast({request, [_|_] = Domains}, State) -> + ?INFO_MSG("Requesting renewal of certificate for ~ts from ~ts", + [misc:format_hosts_list(Domains), directory_url()]), + {_, State1} = issue_request(State, Domains), + {noreply, State1}; +handle_cast(Request, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Request]), + {noreply, State}. + +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), + {noreply, State}. + +terminate(_Reason, _State) -> + ejabberd_hooks:delete(cert_expired, ?MODULE, cert_expired, 60), + ejabberd_hooks:delete(config_reloaded, ?MODULE, register_certfiles, 40), + ejabberd_hooks:delete(ejabberd_started, ?MODULE, ejabberd_started, 110), + ejabberd_hooks:delete(config_reloaded, ?MODULE, ejabberd_started, 110), + ejabberd_hooks:delete(webadmin_menu_node, ?MODULE, webadmin_menu_node, 110), + ejabberd_hooks:delete(webadmin_page_node, ?MODULE, webadmin_page_node, 110), + ejabberd_commands:unregister_commands(get_commands_spec()). + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +%%%=================================================================== +%%% Challenge callback +%%%=================================================================== +-spec register_challenge(p1_acme:challenge_data(), reference()) -> true. +register_challenge(Auth, Ref) -> + ?DEBUG("Registering ACME challenge ~p -> ~p", [Ref, Auth]), + ejabberd_hooks:run(acme_challenge, [{start, Auth, Ref}]), + ets:insert( + acme_challenge, + lists:map( + fun(#{token := Token, key := Key}) -> + {Token, Key, Ref} + end, Auth)). + +-spec unregister_challenge(reference()) -> non_neg_integer(). +unregister_challenge(Ref) -> + ?DEBUG("Unregistering ACME challenge ~p", [Ref]), + ejabberd_hooks:run(acme_challenge, [{stop, Ref}]), + ets:select_delete( + acme_challenge, + ets:fun2ms( + fun({_, _, Ref1}) -> + Ref1 == Ref + end)). + +%%%=================================================================== +%%% Issuance +%%%=================================================================== +-spec issue_request(state(), [binary(),...]) -> {issue_result(), state()}. +issue_request(State, Domains) -> + case check_idna(Domains) of + {ok, AsciiDomains} -> + case read_account_key() of + {ok, AccKey} -> + Config = ejabberd_option:acme(), + DirURL = maps:get(ca_url, Config, default_directory_url()), + Contact = maps:get(contact, Config, []), + CertType = maps:get(cert_type, Config, rsa), + issue_request(State, DirURL, Domains, AsciiDomains, AccKey, CertType, Contact); + {error, Reason} = Err -> + ?ERROR_MSG("Failed to request certificate for ~ts: ~ts", + [misc:format_hosts_list(Domains), + format_error(Reason)]), + {Err, State} + end; + {error, Reason} = Err -> + ?ERROR_MSG("Failed to request certificate for ~ts: ~ts", + [misc:format_hosts_list(Domains), + format_error(Reason)]), + {Err, State} + end. + +-spec issue_request(state(), binary(), [binary(),...], [string(), ...], priv_key(), + cert_type(), [binary()]) -> {issue_result(), state()}. +issue_request(State, DirURL, Domains, AsciiDomains, AccKey, CertType, Contact) -> + Ref = make_ref(), + ChallengeFun = fun(Auth) -> register_challenge(Auth, Ref) end, + Ret = case p1_acme:issue(DirURL, AsciiDomains, AccKey, + [{cert_type, CertType}, + {contact, Contact}, + {debug_fun, debug_fun()}, + {challenge_fun, ChallengeFun}]) of + {ok, #{cert_key := CertKey, + cert_chain := Certs}} -> + case store_cert(CertKey, Certs, CertType, Domains) of + {ok, Path} -> + ejabberd_pkix:add_certfile(Path), + ejabberd_pkix:commit(), + ?INFO_MSG("Certificate for ~ts has been received, " + "stored and loaded successfully", + [misc:format_hosts_list(Domains)]), + {ok, State}; + {error, Reason} = Err -> + ?ERROR_MSG("Failed to store certificate for ~ts: ~ts", + [misc:format_hosts_list(Domains), + format_error(Reason)]), + {Err, State} + end; + {error, Reason} = Err -> + ?ERROR_MSG("Failed to request certificate for ~ts: ~ts", + [misc:format_hosts_list(Domains), + format_error(Reason)]), + {Err, State} + end, + unregister_challenge(Ref), + Ret. + +%%%=================================================================== +%%% Revocation +%%%=================================================================== +revoke_request(State, Cert, Key, Path) -> + case p1_acme:revoke(directory_url(), Cert, Key, + [{debug_fun, debug_fun()}]) of + ok -> + ?INFO_MSG("Certificate from file ~ts has been " + "revoked successfully", [Path]), + case delete_file(Path) of + ok -> + ejabberd_pkix:del_certfile(Path), + ejabberd_pkix:commit(), + {ok, State}; + Err -> + {Err, State} + end; + {error, Reason} = Err -> + ?ERROR_MSG("Failed to revoke certificate from file ~ts: ~ts", + [Path, format_error(Reason)]), + {Err, State} + end. + +%%%=================================================================== +%%% File management +%%%=================================================================== +-spec acme_dir() -> file:filename_all(). +acme_dir() -> + MnesiaDir = mnesia:system_info(directory), + filename:join(MnesiaDir, "acme"). + +-spec acme_certs_dir(atom()) -> file:filename_all(). +acme_certs_dir(Tag) -> + filename:join(acme_dir(), Tag). + +-spec account_file() -> file:filename_all(). +account_file() -> + filename:join(acme_dir(), "account.key"). + +-spec cert_file(cert_type(), [binary()]) -> file:filename_all(). +cert_file(CertType, Domains) -> + L = [erlang:atom_to_binary(CertType, latin1)|Domains], + Hash = str:sha(str:join(L, <<0>>)), + filename:join(acme_certs_dir(live), Hash). + +-spec prep_path(file:filename_all()) -> binary(). +prep_path(Path) -> + unicode:characters_to_binary(Path). + +-spec list_certfiles() -> [binary()]. +list_certfiles() -> + filelib:fold_files( + acme_certs_dir(live), "^[0-9a-f]{40}$", false, + fun(F, Fs) -> [prep_path(F)|Fs] end, []). + +-spec read_account_key() -> {ok, #'ECPrivateKey'{}} | {error, {file, io_error()}}. +read_account_key() -> + Path = account_file(), + case pkix:read_file(Path) of + {ok, _, KeyMap} -> + case maps:keys(KeyMap) of + [#'ECPrivateKey'{} = Key|_] -> {ok, Key}; + _ -> + ?WARNING_MSG("File ~ts doesn't contain ACME account key. " + "Trying to create a new one...", + [Path]), + create_account_key() + end; + {error, enoent} -> + create_account_key(); + {error, {bad_cert, _, _} = Reason} -> + ?WARNING_MSG("ACME account key from '~ts' is corrupted: ~ts. " + "Trying to create a new one...", + [Path, pkix:format_error(Reason)]), + create_account_key(); + {error, Reason} -> + ?ERROR_MSG("Failed to read ACME account from ~ts: ~ts. " + "Try to fix permissions or delete the file completely", + [Path, pkix:format_error(Reason)]), + {error, {file, Reason}} + end. + +-spec create_account_key() -> {ok, #'ECPrivateKey'{}} | {error, {file, io_error()}}. +create_account_key() -> + Path = account_file(), + ?DEBUG("Creating ACME account key in ~ts", [Path]), + Key = p1_acme:generate_key(ec), + DER = public_key:der_encode(element(1, Key), Key), + PEM = public_key:pem_encode([{element(1, Key), DER, not_encrypted}]), + case write_file(Path, PEM) of + ok -> + ?DEBUG("ACME account key has been created successfully in ~ts", + [Path]), + {ok, Key}; + {error, Reason} -> + {error, {file, Reason}} + end. + +-spec store_cert(priv_key(), [cert()], cert_type(), [binary()]) -> {ok, file:filename_all()} | + {error, {file, io_error()}}. +store_cert(Key, Chain, CertType, Domains) -> + DerKey = public_key:der_encode(element(1, Key), Key), + PemKey = [{element(1, Key), DerKey, not_encrypted}], + PemChain = lists:map( + fun(Cert) -> + DerCert = public_key:pkix_encode( + element(1, Cert), Cert, otp), + {'Certificate', DerCert, not_encrypted} + end, Chain), + PEM = public_key:pem_encode(PemChain ++ PemKey), + Path = cert_file(CertType, Domains), + ?DEBUG("Storing certificate for ~ts in ~ts", + [misc:format_hosts_list(Domains), Path]), + case write_file(Path, PEM) of + ok -> + {ok, Path}; + {error, Reason} -> + {error, {file, Reason}} + end. + +-spec read_cert(file:filename_all()) -> {ok, [cert()], priv_key()} | + {error, {file, io_error()} | + {bad_cert, _, _} | + unexpected_certfile}. +read_cert(Path) -> + ?DEBUG("Reading certificate from ~ts", [Path]), + case pkix:read_file(Path) of + {ok, CertsMap, KeysMap} -> + case {maps:to_list(CertsMap), maps:keys(KeysMap)} of + {[_|_] = Certs, [CertKey]} -> + {ok, [Cert || {Cert, _} <- lists:keysort(2, Certs)], CertKey}; + _ -> + {error, unexpected_certfile} + end; + {error, Why} when is_atom(Why) -> + {error, {file, Why}}; + {error, _} = Err -> + Err + end. + +-spec write_file(file:filename_all(), iodata()) -> ok | {error, io_error()}. +write_file(Path, Data) -> + case ensure_dir(Path) of + ok -> + case file:write_file(Path, Data) of + ok -> + case file:change_mode(Path, 8#600) of + ok -> ok; + {error, Why} -> + ?WARNING_MSG("Failed to change permissions of ~ts: ~ts", + [Path, file:format_error(Why)]) + end; + {error, Why} = Err -> + ?ERROR_MSG("Failed to write file ~ts: ~ts", + [Path, file:format_error(Why)]), + Err + end; + Err -> + Err + end. + +-spec delete_file(file:filename_all()) -> ok | {error, io_error()}. +delete_file(Path) -> + case file:delete(Path) of + ok -> ok; + {error, Why} = Err -> + ?WARNING_MSG("Failed to delete file ~ts: ~ts", + [Path, file:format_error(Why)]), + Err + end. + +-spec ensure_dir(file:filename_all()) -> ok | {error, io_error()}. +ensure_dir(Path) -> + case filelib:ensure_dir(Path) of + ok -> ok; + {error, Why} = Err -> + ?ERROR_MSG("Failed to create directory ~ts: ~ts", + [filename:dirname(Path), + file:format_error(Why)]), + Err + end. + +-spec delete_obsolete_data() -> ok. +delete_obsolete_data() -> + Path = filename:join(ejabberd_pkix:certs_dir(), "acme"), + case filelib:is_dir(Path) of + true -> + ?INFO_MSG("Deleting obsolete directory ~ts", [Path]), + _ = misc:delete_dir(Path), + ok; + false -> + ok + end. + +%%%=================================================================== +%%% ejabberd commands +%%%=================================================================== +get_commands_spec() -> + [#ejabberd_commands{name = request_certificate, tags = [acme], + desc = "Requests certificates for all or some domains", + longdesc = "Domains can be `all`, or a list of domains separared with comma characters", + module = ?MODULE, function = request_certificate, + args_desc = ["Domains for which to acquire a certificate"], + args_example = ["example.com,domain.tld,conference.domain.tld"], + args = [{domains, string}], + result = {res, restuple}}, + #ejabberd_commands{name = list_certificates, tags = [acme], + desc = "Lists all ACME certificates", + module = ?MODULE, function = list_certificates, + args = [], + result = {certificates, + {list, {certificate, + {tuple, [{domain, string}, + {file, string}, + {used, string}]}}}}}, + #ejabberd_commands{name = revoke_certificate, tags = [acme], + desc = "Revokes the selected ACME certificate", + module = ?MODULE, function = revoke_certificate, + args_desc = ["Filename of the certificate"], + args = [{file, string}], + result = {res, restuple}}]. + +-spec request_certificate(iodata()) -> {ok | error, string()}. +request_certificate(Arg) -> + Ret = case lists:filter( + fun(S) -> S /= <<>> end, + re:split(Arg, "[\\h,;]+", [{return, binary}])) of + [<<"all">>] -> + case auto_domains() of + [] -> {error, no_auto_hosts}; + Domains -> + gen_server:call(?MODULE, {request, Domains}, ?CALL_TIMEOUT) + end; + [_|_] = Domains -> + case lists:dropwhile( + fun(D) -> + try ejabberd_router:is_my_route(D) of + true -> not is_ip_or_localhost(D); + false -> false + catch _:{invalid_domain, _} -> false + end + end, Domains) of + [Bad|_] -> + {error, {invalid_host, Bad}}; + [] -> + gen_server:call(?MODULE, {request, Domains}, ?CALL_TIMEOUT) + end; + [] -> + {error, invalid_argument} + end, + case Ret of + ok -> {ok, ""}; + {error, Why} -> {error, format_error(Why)} + end. + +-spec revoke_certificate(iodata()) -> {ok | error, string()}. +revoke_certificate(Path0) -> + Path = prep_path(Path0), + Ret = case read_cert(Path) of + {ok, [Cert|_], Key} -> + gen_server:call(?MODULE, {revoke, Cert, Key, Path}, ?CALL_TIMEOUT); + {error, _} = Err -> + Err + end, + case Ret of + ok -> {ok, ""}; + {error, Reason} -> {error, format_error(Reason)} + end. + +-spec list_certificates() -> [{binary(), binary(), boolean()}]. +list_certificates() -> + Known = lists:flatmap( + fun(Path) -> + try + {ok, [Cert|_], _} = read_cert(Path), + Domains = pkix:extract_domains(Cert), + [{Domain, Path} || Domain <- Domains] + catch _:{badmatch, _} -> + [] + end + end, list_certfiles()), + Used = lists:foldl( + fun(Domain, S) -> + try + {ok, Path} = ejabberd_pkix:get_certfile_no_default(Domain), + {ok, [Cert|_], _} = read_cert(Path), + {ok, #{files := Files}} = pkix:get_cert_info(Cert), + lists:foldl(fun sets:add_element/2, + S, [{Domain, File} || {File, _} <- Files]) + catch _:{badmatch, _} -> + S + end + end, sets:new(), all_domains()), + lists:sort( + lists:map( + fun({Domain, Path} = E) -> + {Domain, Path, sets:is_element(E, Used)} + end, Known)). + +%%%=================================================================== +%%% WebAdmin +%%%=================================================================== + +webadmin_menu_node(Acc, _Node, _Lang) -> + Acc ++ [{<<"acme">>, <<"ACME">>}]. + +webadmin_page_node(_, Node, #request{path = [<<"acme">>]} = R) -> + Head = ?H1GLraw(<<"ACME Certificates">>, <<"admin/configuration/basic/#acme">>, <<"ACME">>), + Set = [ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [request_certificate, R]), + ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [revoke_certificate, R])], + Get = [ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [list_certificates, R])], + {stop, Head ++ Get ++ Set}; +webadmin_page_node(Acc, _, _) -> Acc. + +%%%=================================================================== +%%% Other stuff +%%%=================================================================== +-spec all_domains() -> [binary(),...]. +all_domains() -> + ejabberd_option:hosts() ++ ejabberd_router:get_all_routes(). + +-spec auto_domains() -> [binary()]. +auto_domains() -> + lists:filter( + fun(Host) -> + not is_ip_or_localhost(Host) + end, all_domains()). + +-spec directory_url() -> binary(). +directory_url() -> + maps:get(ca_url, ejabberd_option:acme(), default_directory_url()). + +-spec debug_fun() -> fun((string(), list()) -> ok). +debug_fun() -> + fun(Fmt, Args) -> ?DEBUG(Fmt, Args) end. + +-spec request_on_start() -> false | {true, [binary()]}. +request_on_start() -> + Config = ejabberd_option:acme(), + case maps:get(auto, Config, true) of + false -> false; + true -> + case ejabberd_listener:tls_listeners() of + [] -> false; + _ -> + case lists:filter( + fun(Host) -> + not (have_cert_for_domain(Host) + orelse is_ip_or_localhost(Host)) + end, auto_domains()) of + [] -> false; + Hosts -> + case have_acme_listener() of + true -> {true, Hosts}; + false -> + ?WARNING_MSG( + "No HTTP listeners for ACME challenges " + "are configured, automatic " + "certificate requests are aborted. Hint: " + "configure the listener and restart/reload " + "ejabberd. Or set acme->auto option to " + "`false` to suppress this warning.", + []), + false + end + end + end + end. + +well_known() -> + [<<".well-known">>, <<"acme-challenge">>]. + +-spec have_cert_for_domain(binary()) -> boolean(). +have_cert_for_domain(Host) -> + ejabberd_pkix:get_certfile_no_default(Host) /= error. + +-spec is_ip_or_localhost(binary()) -> boolean(). +is_ip_or_localhost(Host) -> + Parts = binary:split(Host, <<".">>), + TLD = binary_to_list(lists:last(Parts)), + case inet:parse_address(TLD) of + {ok, _} -> true; + _ -> TLD == "localhost" + end. + +-spec have_acme_listener() -> boolean(). +have_acme_listener() -> + lists:any( + fun({_, ejabberd_http, #{tls := false, + request_handlers := Handlers}}) -> + lists:keymember(well_known(), 1, Handlers); + (_) -> + false + end, ejabberd_option:listen()). + +-spec check_idna([binary()]) -> {ok, [string()]} | {error, {idna_failed, binary()}}. +check_idna(Domains) -> + lists:foldl( + fun(D, {ok, Ds}) -> + try {ok, [idna:utf8_to_ascii(D)|Ds]} + catch _:_ -> {error, {idna_failed, D}} + end; + (_, Err) -> + Err + end, {ok, []}, Domains). + +-spec format_error(term()) -> string(). +format_error({file, Reason}) -> + "I/O error: " ++ file:format_error(Reason); +format_error({invalid_host, Domain}) -> + "Unknown or unacceptable virtual host: " ++ binary_to_list(Domain); +format_error(no_auto_hosts) -> + "You have no virtual hosts acceptable for ACME certification"; +format_error(invalid_argument) -> + "Invalid argument"; +format_error(unexpected_certfile) -> + "The certificate file was not obtained using ACME"; +format_error({idna_failed, Domain}) -> + "Not an IDN hostname: " ++ binary_to_list(Domain); +format_error({bad_cert, _, _} = Reason) -> + "Malformed certificate file: " ++ pkix:format_error(Reason); +format_error(Reason) -> + p1_acme:format_error(Reason). diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 99aa51794..5ec0ac051 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -5,7 +5,7 @@ %%% Created : 7 May 2006 by Mickael Remond %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -26,17 +26,26 @@ -module(ejabberd_admin). -author('mickael.remond@process-one.net'). --export([start/0, stop/0, +-behaviour(gen_server). + +-export([start_link/0, %% Server - status/0, reopen_log/0, rotate_log/0, + status/0, stop/0, restart/0, + reopen_log/0, rotate_log/0, set_loglevel/1, + evacuate_kindly/2, stop_kindly/2, send_service_message_all_mucs/2, registered_vhosts/0, reload_config/0, + dump_config/1, + convert_to_yaml/2, %% Cluster - join_cluster/1, leave_cluster/1, list_cluster/0, + join_cluster/1, leave_cluster/1, + join_cluster_here/1, + list_cluster/0, list_cluster_detailed/0, + get_cluster_node_details3/0, %% Erlang - update_list/0, update/1, + update_list/0, update/1, update/0, %% Accounts register/3, unregister/2, registered_users/1, @@ -45,26 +54,75 @@ %% Purge DB delete_expired_messages/0, delete_old_messages/1, %% Mnesia - set_master/1, + get_master/0, set_master/1, backup_mnesia/1, restore_mnesia/1, dump_mnesia/1, dump_table/2, load_mnesia/1, + mnesia_info/0, mnesia_table_info/1, install_fallback_mnesia/1, dump_to_textfile/1, dump_to_textfile/2, mnesia_change_nodename/4, restore/1, % Still used by some modules - get_commands_spec/0 - ]). + clear_cache/0, + gc/0, + get_commands_spec/0, + delete_old_messages_batch/4, delete_old_messages_status/1, delete_old_messages_abort/1, + %% Internal + mnesia_list_tables/0, + mnesia_table_details/1, + mnesia_table_change_storage/2, + mnesia_table_clear/1, + mnesia_table_delete/1, + echo/1, echo3/3]). +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). --include("ejabberd.hrl"). --include("logger.hrl"). +-export([web_menu_main/2, web_page_main/2, + web_menu_node/3, web_page_node/3]). + +-include_lib("xmpp/include/xmpp.hrl"). -include("ejabberd_commands.hrl"). +-include("ejabberd_http.hrl"). +-include("ejabberd_web_admin.hrl"). +-include("logger.hrl"). +-include("translate.hrl"). %+++ TODO -start() -> - ejabberd_commands:register_commands(get_commands_spec()). +-record(state, {}). -stop() -> +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +init([]) -> + process_flag(trap_exit, true), + ejabberd_commands:register_commands(get_commands_spec()), + ejabberd_hooks:add(webadmin_menu_main, ?MODULE, web_menu_main, 50), + ejabberd_hooks:add(webadmin_page_main, ?MODULE, web_page_main, 50), + ejabberd_hooks:add(webadmin_menu_node, ?MODULE, web_menu_node, 50), + ejabberd_hooks:add(webadmin_page_node, ?MODULE, web_page_node, 50), + {ok, #state{}}. + +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), + {noreply, State}. + +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), + {noreply, State}. + +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), + {noreply, State}. + +terminate(_Reason, _State) -> + ejabberd_hooks:delete(webadmin_menu_main, ?MODULE, web_menu_main, 50), + ejabberd_hooks:delete(webadmin_page_main, ?MODULE, web_page_main, 50), + ejabberd_hooks:delete(webadmin_menu_node, ?MODULE, web_menu_node, 50), + ejabberd_hooks:delete(webadmin_page_node, ?MODULE, web_page_node, 50), ejabberd_commands:unregister_commands(get_commands_spec()). +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + %%% %%% ejabberd commands %%% @@ -76,196 +134,524 @@ get_commands_spec() -> #ejabberd_commands{name = status, tags = [server], desc = "Get status of the ejabberd server", module = ?MODULE, function = status, + result_desc = "Result tuple", + result_example = {ok, <<"The node ejabberd@localhost is started with status: started" + "ejabberd X.X is running in that node">>}, args = [], result = {res, restuple}}, #ejabberd_commands{name = stop, tags = [server], desc = "Stop ejabberd gracefully", - module = init, function = stop, + module = ?MODULE, function = stop, + args = [], result = {res, rescode}}, + #ejabberd_commands{name = halt, tags = [server], + desc = "Halt ejabberd abruptly with status code 1", + note = "added in 23.10", + module = ejabberd, function = halt, args = [], result = {res, rescode}}, #ejabberd_commands{name = restart, tags = [server], desc = "Restart ejabberd gracefully", - module = init, function = restart, + module = ?MODULE, function = restart, args = [], result = {res, rescode}}, - #ejabberd_commands{name = reopen_log, tags = [logs, server], - desc = "Reopen the log files", + #ejabberd_commands{name = reopen_log, tags = [logs], + desc = "Reopen maybe the log files after being renamed", + longdesc = "Has no effect on ejabberd main log files, " + "only on log files generated by some modules.\n" + "This can be useful when an external tool is " + "used for log rotation. See " + "_`../../admin/guide/troubleshooting.md#log-files|Log Files`_.", policy = admin, module = ?MODULE, function = reopen_log, args = [], result = {res, rescode}}, - #ejabberd_commands{name = rotate_log, tags = [logs, server], - desc = "Rotate the log files", + #ejabberd_commands{name = rotate_log, tags = [logs], + desc = "Rotate maybe log file of some module", + longdesc = "Has no effect on ejabberd main log files, " + "only on log files generated by some modules.", module = ?MODULE, function = rotate_log, args = [], result = {res, rescode}}, - #ejabberd_commands{name = stop_kindly, tags = [server], - desc = "Inform users and rooms, wait, and stop the server", - longdesc = "Provide the delay in seconds, and the " - "announcement quoted, for example: \n" - "ejabberdctl stop_kindly 60 " - "\\\"The server will stop in one minute.\\\"", - module = ?MODULE, function = stop_kindly, + #ejabberd_commands{name = evacuate_kindly, tags = [server], + desc = "Evacuate kindly all users (kick and prevent login)", + longdesc = "Inform users and rooms, don't allow login, wait, " + "restart the server, and don't allow new logins.\n" + "Provide the delay in seconds, and the " + "announcement quoted, for example: \n" + "`ejabberdctl evacuate_kindly 60 " + "\\\"The server will stop in one minute.\\\"`", + note = "added in 24.12", + module = ?MODULE, function = evacuate_kindly, + args_desc = ["Seconds to wait", "Announcement to send, with quotes"], + args_example = [60, <<"Server will stop now.">>], args = [{delay, integer}, {announcement, string}], result = {res, rescode}}, - #ejabberd_commands{name = get_loglevel, tags = [logs, server], + #ejabberd_commands{name = stop_kindly, tags = [server], + desc = "Stop kindly the server (informing users)", + longdesc = "Inform users and rooms, wait, and stop the server.\n" + "Provide the delay in seconds, and the " + "announcement quoted, for example: \n" + "`ejabberdctl stop_kindly 60 " + "\\\"The server will stop in one minute.\\\"`", + module = ?MODULE, function = stop_kindly, + args_desc = ["Seconds to wait", "Announcement to send, with quotes"], + args_example = [60, <<"Server will stop now.">>], + args = [{delay, integer}, {announcement, string}], + result = {res, rescode}}, + #ejabberd_commands{name = get_loglevel, tags = [logs], desc = "Get the current loglevel", module = ejabberd_logger, function = get, + result_desc = "Tuple with the log level number, its keyword and description", + result_example = warning, args = [], - result = {leveltuple, {tuple, [{levelnumber, integer}, - {levelatom, atom}, - {leveldesc, string} - ]}}}, - #ejabberd_commands{name = set_loglevel, tags = [logs, server], - desc = "Set the loglevel (0 to 5)", + result = {levelatom, atom}}, + #ejabberd_commands{name = set_loglevel, tags = [logs], + desc = "Set the loglevel", + longdesc = "Possible loglevels: `none`, `emergency`, `alert`, `critical`, + `error`, `warning`, `notice`, `info`, `debug`.", module = ?MODULE, function = set_loglevel, - args = [{loglevel, integer}], - result = {logger, atom}}, + args_desc = ["Desired logging level"], + args_example = ["debug"], + args = [{loglevel, string}], + result = {res, rescode}}, #ejabberd_commands{name = update_list, tags = [server], desc = "List modified modules that can be updated", module = ?MODULE, function = update_list, args = [], + result_example = ["mod_configure", "mod_vcard"], result = {modules, {list, {module, string}}}}, #ejabberd_commands{name = update, tags = [server], - desc = "Update the given module, or use the keyword: all", + desc = "Update the given module", + longdesc = "To update all the possible modules, use `all`.", + note = "improved in 24.10", module = ?MODULE, function = update, + args_example = ["all"], args = [{module, string}], + result_example = {ok, <<"Updated modules: mod_configure, mod_vcard">>}, result = {res, restuple}}, #ejabberd_commands{name = register, tags = [accounts], desc = "Register a user", policy = admin, module = ?MODULE, function = register, + args_desc = ["Username", "Local vhost served by ejabberd", "Password"], + args_example = [<<"bob">>, <<"example.com">>, <<"SomEPass44">>], args = [{user, binary}, {host, binary}, {password, binary}], result = {res, restuple}}, #ejabberd_commands{name = unregister, tags = [accounts], desc = "Unregister a user", + longdesc = "This deletes the authentication and all the " + "data associated to the account (roster, vcard...).", + policy = admin, module = ?MODULE, function = unregister, + args_desc = ["Username", "Local vhost served by ejabberd"], + args_example = [<<"bob">>, <<"example.com">>], args = [{user, binary}, {host, binary}], result = {res, restuple}}, #ejabberd_commands{name = registered_users, tags = [accounts], desc = "List all registered users in HOST", module = ?MODULE, function = registered_users, + args_desc = ["Local vhost"], + args_example = [<<"example.com">>], + result_desc = "List of registered accounts usernames", + result_example = [<<"user1">>, <<"user2">>], args = [{host, binary}], result = {users, {list, {username, string}}}}, - #ejabberd_commands{name = registered_vhosts, tags = [server], + #ejabberd_commands{name = registered_vhosts, tags = [server], desc = "List all registered vhosts in SERVER", module = ?MODULE, function = registered_vhosts, + result_desc = "List of available vhosts", + result_example = [<<"example.com">>, <<"anon.example.com">>], args = [], result = {vhosts, {list, {vhost, string}}}}, - #ejabberd_commands{name = reload_config, tags = [server], - desc = "Reload config file in memory (only affects ACL and Access)", + #ejabberd_commands{name = reload_config, tags = [config], + desc = "Reload config file in memory", module = ?MODULE, function = reload_config, args = [], result = {res, rescode}}, #ejabberd_commands{name = join_cluster, tags = [cluster], - desc = "Join this node into the cluster handled by Node", + desc = "Join our local node into the cluster handled by Node", + longdesc = "This command returns immediately, + even before the joining process has + completed. Consequently, if you are using + `ejabberdctl` (or some `CTL_ON_` container + environment variables) to run more commands + afterwards, you may want to precede them with + the `started` + _`../../admin/guide/managing.md#ejabberdctl-commands|ejabberdctl command`_ + to ensure the + clustering process has completed before + proceeding. For example: `join_cluster + ejabberd@main` > `started` > `list_cluster`.", + note = "improved in 24.06", module = ?MODULE, function = join_cluster, + args_desc = ["Nodename of the node to join"], + args_example = [<<"ejabberd1@machine7">>], args = [{node, binary}], - result = {res, rescode}}, + result = {res, restuple}}, + #ejabberd_commands{name = join_cluster_here, tags = [cluster], + desc = "Join a remote Node here, into our cluster", + note = "added in 24.06", + module = ?MODULE, function = join_cluster_here, + args_desc = ["Nodename of the node to join here"], + args_example = [<<"ejabberd1@machine7">>], + args = [{node, binary}], + result = {res, restuple}}, #ejabberd_commands{name = leave_cluster, tags = [cluster], - desc = "Remove node handled by Node from the cluster", + desc = "Remove and shutdown Node from the running cluster", + longdesc = "This command can be run from any running " + "node of the cluster, even the node to be removed. " + "In the removed node, this command works only when " + "using ejabberdctl, not _`mod_http_api`_ or other code that " + "runs inside the same ejabberd node that will leave.", module = ?MODULE, function = leave_cluster, + args_desc = ["Nodename of the node to kick from the cluster"], + args_example = [<<"ejabberd1@machine8">>], args = [{node, binary}], result = {res, rescode}}, + #ejabberd_commands{name = list_cluster, tags = [cluster], - desc = "List nodes that are part of the cluster handled by Node", + desc = "List running nodes that are part of this cluster", module = ?MODULE, function = list_cluster, + result_example = [ejabberd1@machine7, ejabberd1@machine8], args = [], result = {nodes, {list, {node, atom}}}}, + #ejabberd_commands{name = list_cluster_detailed, tags = [cluster], + desc = "List nodes (both running and known) and some stats", + note = "added in 24.06", + module = ?MODULE, function = list_cluster_detailed, + args = [], + result_example = [{'ejabberd@localhost', "true", + "The node ejabberd is started. Status...", + 7, 348, 60, none}], + result = {nodes, {list, {node, {tuple, [{name, atom}, + {running, string}, + {status, string}, + {online_users, integer}, + {processes, integer}, + {uptime_seconds, integer}, + {master_node, atom} + ]}}}}}, #ejabberd_commands{name = import_file, tags = [mnesia], desc = "Import user data from jabberd14 spool file", module = ?MODULE, function = import_file, + args_desc = ["Full path to the jabberd14 spool file"], + args_example = ["/var/lib/ejabberd/jabberd14.spool"], args = [{file, string}], result = {res, restuple}}, #ejabberd_commands{name = import_dir, tags = [mnesia], desc = "Import users data from jabberd14 spool dir", module = ?MODULE, function = import_dir, + args_desc = ["Full path to the jabberd14 spool directory"], + args_example = ["/var/lib/ejabberd/jabberd14/"], args = [{file, string}], result = {res, restuple}}, - #ejabberd_commands{name = import_piefxis, tags = [mnesia], desc = "Import users data from a PIEFXIS file (XEP-0227)", module = ejabberd_piefxis, function = import_file, - args = [{file, string}], result = {res, rescode}}, + args_desc = ["Full path to the PIEFXIS file"], + args_example = ["/var/lib/ejabberd/example.com.xml"], + args = [{file, binary}], result = {res, rescode}}, #ejabberd_commands{name = export_piefxis, tags = [mnesia], desc = "Export data of all users in the server to PIEFXIS files (XEP-0227)", module = ejabberd_piefxis, function = export_server, - args = [{dir, string}], result = {res, rescode}}, + args_desc = ["Full path to a directory"], + args_example = ["/var/lib/ejabberd/"], + args = [{dir, binary}], result = {res, rescode}}, #ejabberd_commands{name = export_piefxis_host, tags = [mnesia], desc = "Export data of users in a host to PIEFXIS files (XEP-0227)", module = ejabberd_piefxis, function = export_host, - args = [{dir, string}, {host, string}], result = {res, rescode}}, + args_desc = ["Full path to a directory", "Vhost to export"], + args_example = ["/var/lib/ejabberd/", "example.com"], + args = [{dir, binary}, {host, binary}], result = {res, rescode}}, - #ejabberd_commands{name = delete_mnesia, tags = [mnesia, sql], - desc = "Export all tables as SQL queries to a file", + #ejabberd_commands{name = delete_mnesia, tags = [mnesia], + desc = "Delete elements in Mnesia database for a given vhost", module = ejd2sql, function = delete, + args_desc = ["Vhost which content will be deleted in Mnesia database"], + args_example = ["example.com"], args = [{host, string}], result = {res, rescode}}, #ejabberd_commands{name = convert_to_scram, tags = [sql], - desc = "Convert the passwords in 'users' ODBC table to SCRAM", - module = ejabberd_auth_sql, function = convert_to_scram, + desc = "Convert the passwords of users to SCRAM", + module = ejabberd_auth, function = convert_to_scram, + args_desc = ["Vhost which users' passwords will be scrammed"], + args_example = ["example.com"], args = [{host, binary}], result = {res, rescode}}, - - #ejabberd_commands{name = import_prosody, tags = [mnesia, sql, riak], + #ejabberd_commands{name = import_prosody, tags = [mnesia, sql], desc = "Import data from Prosody", + longdesc = "Note: this requires ejabberd to be " + "compiled with `./configure --enable-lua` " + "(which installs the `luerl` library).", module = prosody2ejabberd, function = from_dir, + args_desc = ["Full path to the Prosody data directory"], + args_example = ["/var/lib/prosody/datadump/"], args = [{dir, string}], result = {res, rescode}}, #ejabberd_commands{name = convert_to_yaml, tags = [config], desc = "Convert the input file from Erlang to YAML format", - module = ejabberd_config, function = convert_to_yaml, + module = ?MODULE, function = convert_to_yaml, + args_desc = ["Full path to the original configuration file", "And full path to final file"], + args_example = ["/etc/ejabberd/ejabberd.cfg", "/etc/ejabberd/ejabberd.yml"], args = [{in, string}, {out, string}], result = {res, rescode}}, + #ejabberd_commands{name = dump_config, tags = [config], + desc = "Dump configuration in YAML format as seen by ejabberd", + module = ?MODULE, function = dump_config, + args_desc = ["Full path to output file"], + args_example = ["/tmp/ejabberd.yml"], + args = [{out, string}], + result = {res, rescode}}, - #ejabberd_commands{name = delete_expired_messages, tags = [purge], + #ejabberd_commands{name = delete_expired_messages, tags = [offline, purge], desc = "Delete expired offline messages from database", module = ?MODULE, function = delete_expired_messages, args = [], result = {res, rescode}}, - #ejabberd_commands{name = delete_old_messages, tags = [purge], + #ejabberd_commands{name = delete_old_messages, tags = [offline, purge], desc = "Delete offline messages older than DAYS", module = ?MODULE, function = delete_old_messages, + args_desc = ["Number of days"], + args_example = [31], args = [{days, integer}], result = {res, rescode}}, + #ejabberd_commands{name = delete_old_messages_batch, tags = [offline, purge], + desc = "Delete offline messages older than DAYS", + note = "added in 22.05", + module = ?MODULE, function = delete_old_messages_batch, + args_desc = ["Name of host where messages should be deleted", + "Days to keep messages", + "Number of messages to delete per batch", + "Desired rate of messages to delete per minute"], + args_example = [<<"localhost">>, 31, 1000, 10000], + args = [{host, binary}, {days, integer}, {batch_size, integer}, {rate, integer}], + result = {res, restuple}, + result_desc = "Result tuple", + result_example = {ok, <<"Removal of 5000 messages in progress">>}}, + #ejabberd_commands{name = delete_old_messages_status, tags = [offline, purge], + desc = "Status of delete old offline messages operation", + note = "added in 22.05", + module = ?MODULE, function = delete_old_messages_status, + args_desc = ["Name of host where messages should be deleted"], + args_example = [<<"localhost">>], + args = [{host, binary}], + result = {status, string}, + result_desc = "Status test", + result_example = "Operation in progress, delete 5000 messages"}, + #ejabberd_commands{name = abort_delete_old_messages, tags = [offline, purge], + desc = "Abort currently running delete old offline messages operation", + note = "added in 22.05", + module = ?MODULE, function = delete_old_messages_abort, + args_desc = ["Name of host where operation should be aborted"], + args_example = [<<"localhost">>], + args = [{host, binary}], + result = {status, string}, + result_desc = "Status text", + result_example = "Operation aborted"}, #ejabberd_commands{name = export2sql, tags = [mnesia], - desc = "Export virtual host information from Mnesia tables to SQL files", + desc = "Export virtual host information from Mnesia tables to SQL file", + longdesc = "Configure the modules to use SQL, then call this command. " + "After correctly exported the database of a vhost, " + "you may want to delete from mnesia with " + "the _`delete_mnesia`_ API.", module = ejd2sql, function = export, + args_desc = ["Vhost", "Full path to the destination SQL file"], + args_example = ["example.com", "/var/lib/ejabberd/example.com.sql"], args = [{host, string}, {file, string}], result = {res, rescode}}, - #ejabberd_commands{name = set_master, tags = [mnesia], + #ejabberd_commands{name = get_master, tags = [cluster], + desc = "Get master node of the clustered Mnesia tables", + note = "added in 24.06", + longdesc = "If there is no master, returns `none`.", + module = ?MODULE, function = get_master, + result = {nodename, atom}}, + #ejabberd_commands{name = set_master, tags = [cluster], desc = "Set master node of the clustered Mnesia tables", - longdesc = "If you provide as nodename \"self\", this " + longdesc = "If `nodename` is set to `self`, then this " "node will be set as its own master.", module = ?MODULE, function = set_master, + args_desc = ["Name of the erlang node that will be considered master of this node"], + args_example = ["ejabberd@machine7"], args = [{nodename, string}], result = {res, restuple}}, #ejabberd_commands{name = mnesia_change_nodename, tags = [mnesia], desc = "Change the erlang node name in a backup file", module = ?MODULE, function = mnesia_change_nodename, + args_desc = ["Name of the old erlang node", "Name of the new node", + "Path to old backup file", "Path to the new backup file"], + args_example = ["ejabberd@machine1", "ejabberd@machine2", + "/var/lib/ejabberd/old.backup", "/var/lib/ejabberd/new.backup"], args = [{oldnodename, string}, {newnodename, string}, {oldbackup, string}, {newbackup, string}], result = {res, restuple}}, #ejabberd_commands{name = backup, tags = [mnesia], - desc = "Store the database to backup file", + desc = "Backup the Mnesia database to a binary file", module = ?MODULE, function = backup_mnesia, + args_desc = ["Full path for the destination backup file"], + args_example = ["/var/lib/ejabberd/database.backup"], args = [{file, string}], result = {res, restuple}}, #ejabberd_commands{name = restore, tags = [mnesia], - desc = "Restore the database from backup file", + desc = "Restore the Mnesia database from a binary backup file", + longdesc = "This restores immediately from a " + "binary backup file the internal Mnesia " + "database. This will consume a lot of memory if " + "you have a large database, you may prefer " + "_`install_fallback`_ API.", module = ?MODULE, function = restore_mnesia, + args_desc = ["Full path to the backup file"], + args_example = ["/var/lib/ejabberd/database.backup"], args = [{file, string}], result = {res, restuple}}, #ejabberd_commands{name = dump, tags = [mnesia], - desc = "Dump the database to text file", + desc = "Dump the Mnesia database to a text file", module = ?MODULE, function = dump_mnesia, + args_desc = ["Full path for the text file"], + args_example = ["/var/lib/ejabberd/database.txt"], args = [{file, string}], result = {res, restuple}}, #ejabberd_commands{name = dump_table, tags = [mnesia], - desc = "Dump a table to text file", + desc = "Dump a Mnesia table to a text file", module = ?MODULE, function = dump_table, + args_desc = ["Full path for the text file", "Table name"], + args_example = ["/var/lib/ejabberd/table-muc-registered.txt", "muc_registered"], args = [{file, string}, {table, string}], result = {res, restuple}}, #ejabberd_commands{name = load, tags = [mnesia], - desc = "Restore the database from text file", + desc = "Restore Mnesia database from a text dump file", + longdesc = "Restore immediately. This is not " + "recommended for big databases, as it will " + "consume much time, memory and processor. In " + "that case it's preferable to use " + "_`backup`_ API and " + "_`install_fallback`_ API.", module = ?MODULE, function = load_mnesia, + args_desc = ["Full path to the text file"], + args_example = ["/var/lib/ejabberd/database.txt"], args = [{file, string}], result = {res, restuple}}, + #ejabberd_commands{name = mnesia_info, tags = [mnesia], + desc = "Dump info on global Mnesia state", + module = ?MODULE, function = mnesia_info, + args = [], result = {res, string}}, + #ejabberd_commands{name = mnesia_table_info, tags = [mnesia], + desc = "Dump info on Mnesia table state", + module = ?MODULE, function = mnesia_table_info, + args_desc = ["Mnesia table name"], + args_example = ["roster"], + args = [{table, string}], result = {res, string}}, #ejabberd_commands{name = install_fallback, tags = [mnesia], - desc = "Install the database from a fallback file", + desc = "Install Mnesia database from a binary backup file", + longdesc = "The binary backup file is " + "installed as fallback: it will be used to " + "restore the database at the next ejabberd " + "start. This means that, after running this " + "command, you have to restart ejabberd. This " + "command requires less memory than " + "_`restore`_ API.", module = ?MODULE, function = install_fallback_mnesia, - args = [{file, string}], result = {res, restuple}} - ]. + args_desc = ["Full path to the fallback file"], + args_example = ["/var/lib/ejabberd/database.fallback"], + args = [{file, string}], result = {res, restuple}}, + #ejabberd_commands{name = clear_cache, tags = [server], + desc = "Clear database cache on all nodes", + module = ?MODULE, function = clear_cache, + args = [], result = {res, rescode}}, + #ejabberd_commands{name = gc, tags = [server], + desc = "Force full garbage collection", + note = "added in 20.01", + module = ?MODULE, function = gc, + args = [], result = {res, rescode}}, + #ejabberd_commands{name = man, tags = [documentation], + desc = "Generate Unix manpage for current ejabberd version", + note = "added in 20.01", + module = ejabberd_doc, function = man, + args = [], result = {res, restuple}}, + #ejabberd_commands{name = webadmin_host_user_queue, tags = [offline, internal], + desc = "Generate WebAdmin offline queue HTML", + module = mod_offline, function = webadmin_host_user_queue, + args = [{user, binary}, {host, binary}, {query, any}, {lang, binary}], + result = {res, any}}, + + #ejabberd_commands{name = webadmin_host_last_activity, tags = [internal], + desc = "Generate WebAdmin Last Activity HTML", + module = ejabberd_web_admin, function = webadmin_host_last_activity, + args = [{host, binary}, {query, any}, {lang, binary}], + result = {res, any}}, + #ejabberd_commands{name = webadmin_host_srg, tags = [internal], + desc = "Generate WebAdmin Shared Roster Group HTML", + module = mod_shared_roster, function = webadmin_host_srg, + args = [{host, binary}, {query, any}, {lang, binary}], + result = {res, any}}, + #ejabberd_commands{name = webadmin_host_srg_group, tags = [internal], + desc = "Generate WebAdmin Shared Roster Group HTML for a group", + module = mod_shared_roster, function = webadmin_host_srg_group, + args = [{host, binary}, {group, binary}, {query, any}, {lang, binary}], + result = {res, any}}, + + #ejabberd_commands{name = webadmin_node_contrib, tags = [internal], + desc = "Generate WebAdmin ejabberd-contrib HTML", + module = ext_mod, function = webadmin_node_contrib, + args = [{node, atom}, {query, any}, {lang, binary}], + result = {res, any}}, + #ejabberd_commands{name = webadmin_node_db, tags = [internal], + desc = "Generate WebAdmin Mnesia database HTML", + module = ejabberd_web_admin, function = webadmin_node_db, + args = [{node, atom}, {query, any}, {lang, binary}], + result = {res, any}}, + #ejabberd_commands{name = webadmin_node_db_table, tags = [internal], + desc = "Generate WebAdmin Mnesia database HTML for a table", + module = ejabberd_web_admin, function = webadmin_node_db_table, + args = [{node, atom}, {table, binary}, {lang, binary}], + result = {res, any}}, + #ejabberd_commands{name = webadmin_node_db_table_page, tags = [internal], + desc = "Generate WebAdmin Mnesia database HTML for a table content", + module = ejabberd_web_admin, function = webadmin_node_db_table_page, + args = [{node, atom}, {table, binary}, {page, integer}], + result = {res, any}}, + + #ejabberd_commands{name = mnesia_list_tables, tags = [mnesia], + desc = "List of Mnesia tables", + note = "added in 25.03", + module = ?MODULE, function = mnesia_list_tables, + result = {tables, {list, {table, {tuple, [{name, atom}, + {storage_type, binary}, + {elements, integer}, + {memory_kb, integer}, + {memory_mb, integer} + ]}}}}}, + #ejabberd_commands{name = mnesia_table_details, tags = [internal, mnesia], + desc = "Get details of a Mnesia table", + module = ?MODULE, function = mnesia_table_details, + args = [{table, binary}], + result = {details, {list, {detail, {tuple, [{name, atom}, + {value, binary} + ]}}}}}, + + #ejabberd_commands{name = mnesia_table_change_storage, tags = [mnesia], + desc = "Change storage type of a Mnesia table", + note = "added in 25.03", + longdesc = "Storage type can be: `ram_copies`, `disc_copies`, `disc_only_copies`, `remote_copy`.", + module = ?MODULE, function = mnesia_table_change_storage, + args = [{table, binary}, {storage_type, binary}], + result = {res, restuple}}, + #ejabberd_commands{name = mnesia_table_clear, tags = [internal, mnesia], + desc = "Delete all content in a Mnesia table", + module = ?MODULE, function = mnesia_table_clear, + args = [{table, binary}], + result = {res, restuple}}, + #ejabberd_commands{name = mnesia_table_destroy, tags = [internal, mnesia], + desc = "Destroy a Mnesia table", + module = ?MODULE, function = mnesia_table_destroy, + args = [{table, binary}], + result = {res, restuple}}, + #ejabberd_commands{name = echo, tags = [internal], + desc = "Return the same sentence that was provided", + module = ?MODULE, function = echo, + args_desc = ["Sentence to echoe"], + args_example = [<<"Test Sentence">>], + args = [{sentence, binary}], + result = {sentence, string}, + result_example = "Test Sentence"}, + #ejabberd_commands{name = echo3, tags = [internal], + desc = "Return the same sentence that was provided", + module = ?MODULE, function = echo3, + args_desc = ["First argument", "Second argument", "Sentence to echoe"], + args_example = [<<"example.com">>, <<"Group1">>, <<"Test Sentence">>], + args = [{first, binary}, {second, binary}, {sentence, binary}], + result = {sentence, string}, + result_example = "Test Sentence"} + ]. %%% %%% Server management @@ -282,69 +668,97 @@ status() -> {value, {_, _, Version}} -> {ok, io_lib:format("ejabberd ~s is running in that node", [Version])} end, - {Is_running, String1 ++ String2}. + {Is_running, String1 ++ "\n" ++String2}. + +stop() -> + _ = supervisor:terminate_child(ejabberd_sup, ejabberd_sm), + timer:sleep(1000), + init:stop(). + +restart() -> + _ = supervisor:terminate_child(ejabberd_sup, ejabberd_sm), + timer:sleep(1000), + init:restart(). reopen_log() -> - ejabberd_hooks:run(reopen_log_hook, []), - ejabberd_logger:reopen_log(). + ejabberd_hooks:run(reopen_log_hook, []). rotate_log() -> - ejabberd_hooks:run(rotate_log_hook, []), - ejabberd_logger:rotate_log(). + ejabberd_hooks:run(rotate_log_hook, []). set_loglevel(LogLevel) -> - {module, Module} = ejabberd_logger:set(LogLevel), - Module. - + try binary_to_existing_atom(iolist_to_binary(LogLevel), latin1) of + Level -> + case lists:member(Level, ejabberd_logger:loglevels()) of + true -> + ejabberd_logger:set(Level); + false -> + {error, "Invalid log level"} + end + catch _:_ -> + {error, "Invalid log level"} + end. %%% %%% Stop Kindly %%% +evacuate_kindly(DelaySeconds, AnnouncementTextString) -> + perform_kindly(DelaySeconds, AnnouncementTextString, evacuate). + stop_kindly(DelaySeconds, AnnouncementTextString) -> - Subject = (str:format("Server stop in ~p seconds!", [DelaySeconds])), - WaitingDesc = (str:format("Waiting ~p seconds", [DelaySeconds])), + perform_kindly(DelaySeconds, AnnouncementTextString, stop). + +perform_kindly(DelaySeconds, AnnouncementTextString, Action) -> + Subject = str:format("Server stop in ~p seconds!", [DelaySeconds]), + WaitingDesc = str:format("Waiting ~p seconds", [DelaySeconds]), AnnouncementText = list_to_binary(AnnouncementTextString), - Steps = [ - {"Stopping ejabberd port listeners", - ejabberd_listener, stop_listeners, []}, - {"Sending announcement to connected users", - mod_announce, send_announcement_to_all, - [?MYNAME, Subject, AnnouncementText]}, - {"Sending service message to MUC rooms", - ejabberd_admin, send_service_message_all_mucs, - [Subject, AnnouncementText]}, - {WaitingDesc, timer, sleep, [DelaySeconds * 1000]}, - {"Stopping ejabberd", application, stop, [ejabberd]}, - {"Stopping Mnesia", mnesia, stop, []}, - {"Stopping Erlang node", init, stop, []} - ], + PreSteps = + [{"Stopping ejabberd port listeners", ejabberd_listener, stop_listeners, []}, + {"Sending announcement to connected users", + mod_announce, + send_announcement_to_all, + [ejabberd_config:get_myname(), Subject, AnnouncementText]}, + {"Sending service message to MUC rooms", + ejabberd_admin, + send_service_message_all_mucs, + [Subject, AnnouncementText]}, + {WaitingDesc, timer, sleep, [DelaySeconds * 1000]}, + {"Stopping ejabberd", application, stop, [ejabberd]}], + SpecificSteps = + case Action of + evacuate -> + [{"Starting ejabberd", application, start, [ejabberd]}, + {"Stopping ejabberd port listeners", ejabberd_listener, stop_listeners, []}]; + stop -> + [{"Stopping Mnesia", mnesia, stop, []}, {"Stopping Erlang node", init, stop, []}] + end, + Steps = PreSteps ++ SpecificSteps, NumberLast = length(Steps), TimestampStart = calendar:datetime_to_gregorian_seconds({date(), time()}), - lists:foldl( - fun({Desc, Mod, Func, Args}, NumberThis) -> - SecondsDiff = - calendar:datetime_to_gregorian_seconds({date(), time()}) - - TimestampStart, - io:format("[~p/~p ~ps] ~s... ", - [NumberThis, NumberLast, SecondsDiff, Desc]), - Result = (catch apply(Mod, Func, Args)), - io:format("~p~n", [Result]), - NumberThis+1 - end, - 1, - Steps), + lists:foldl(fun({Desc, Mod, Func, Args}, NumberThis) -> + SecondsDiff = + calendar:datetime_to_gregorian_seconds({date(), time()}) - TimestampStart, + io:format("[~p/~p ~ps] ~ts... ", [NumberThis, NumberLast, SecondsDiff, Desc]), + Result = (catch apply(Mod, Func, Args)), + io:format("~p~n", [Result]), + NumberThis + 1 + end, + 1, + Steps), ok. send_service_message_all_mucs(Subject, AnnouncementText) -> Message = str:format("~s~n~s", [Subject, AnnouncementText]), lists:foreach( fun(ServerHost) -> - MUCHost = gen_mod:get_module_opt_host( - ServerHost, mod_muc, <<"conference.@HOST@">>), - mod_muc:broadcast_service_message(MUCHost, Message) + MUCHosts = gen_mod:get_module_opt_hosts(ServerHost, mod_muc), + lists:foreach( + fun(MUCHost) -> + mod_muc:broadcast_service_message(ServerHost, MUCHost, Message) + end, MUCHosts) end, - ?MYHOSTS). + ejabberd_option:hosts()). %%% %%% ejabberd_update @@ -356,8 +770,14 @@ update_list() -> [atom_to_list(Beam) || Beam <- UpdatedBeams]. update("all") -> - [update_module(ModStr) || ModStr <- update_list()], - {ok, []}; + ResList = [{ModStr, update_module(ModStr)} || ModStr <- update_list()], + String = case string:join([Mod || {Mod, {ok, _}} <- ResList], ", ") of + [] -> + "No modules updated"; + ModulesString -> + "Updated modules: " ++ ModulesString + end, + {ok, String}; update(ModStr) -> update_module(ModStr). @@ -366,58 +786,174 @@ update_module(ModuleNameBin) when is_binary(ModuleNameBin) -> update_module(ModuleNameString) -> ModuleName = list_to_atom(ModuleNameString), case ejabberd_update:update([ModuleName]) of - {ok, _Res} -> {ok, []}; - {error, Reason} -> {error, Reason} + {ok, []} -> + {ok, "Not updated: "++ModuleNameString}; + {ok, [ModuleName]} -> + {ok, "Updated: "++ModuleNameString}; + {error, Reason} -> {error, Reason} end. +update() -> + io:format("Compiling ejabberd...~n", []), + os:cmd("make"), + Mods = ejabberd_admin:update_list(), + io:format("Updating modules: ~p~n", [Mods]), + ejabberd_admin:update("all"), + Mods2 = Mods -- ejabberd_admin:update_list(), + io:format("Updated modules: ~p~n", [Mods2]), + ok. + %%% %%% Account management %%% register(User, Host, Password) -> - case ejabberd_auth:try_register(User, Host, Password) of - {atomic, ok} -> - {ok, io_lib:format("User ~s@~s successfully registered", [User, Host])}; - {atomic, exists} -> - Msg = io_lib:format("User ~s@~s already registered", [User, Host]), - {error, conflict, 10090, Msg}; - {error, Reason} -> - String = io_lib:format("Can't register user ~s@~s at node ~p: ~p", - [User, Host, node(), Reason]), - {error, cannot_register, 10001, String} + case is_my_host(Host) of + true -> + case ejabberd_auth:try_register(User, Host, Password) of + ok -> + {ok, io_lib:format("User ~s@~s successfully registered", [User, Host])}; + {error, exists} -> + Msg = io_lib:format("User ~s@~s already registered", [User, Host]), + {error, conflict, 10090, Msg}; + {error, Reason} -> + String = io_lib:format("Can't register user ~s@~s at node ~p: ~s", + [User, Host, node(), + mod_register:format_error(Reason)]), + {error, cannot_register, 10001, String} + end; + false -> + {error, cannot_register, 10001, "Unknown virtual host"} end. unregister(User, Host) -> - ejabberd_auth:remove_user(User, Host), - {ok, ""}. + case is_my_host(Host) of + true -> + ejabberd_auth:remove_user(User, Host), + {ok, ""}; + false -> + {error, "Unknown virtual host"} + end. registered_users(Host) -> - Users = ejabberd_auth:get_vh_registered_users(Host), - SUsers = lists:sort(Users), - lists:map(fun({U, _S}) -> U end, SUsers). + case is_my_host(Host) of + true -> + Users = ejabberd_auth:get_users(Host), + SUsers = lists:sort(Users), + lists:map(fun({U, _S}) -> U end, SUsers); + false -> + {error, "Unknown virtual host"} + end. registered_vhosts() -> - ?MYHOSTS. + ejabberd_option:hosts(). reload_config() -> - ejabberd_config:reload_file(), - acl:start(), - shaper:start(), - ejabberd_access_permissions:invalidate(). + case ejabberd_config:reload() of + ok -> ok; + Err -> + Reason = ejabberd_config:format_error(Err), + {error, Reason} + end. + +dump_config(Path) -> + case ejabberd_config:dump(Path) of + ok -> ok; + Err -> + Reason = ejabberd_config:format_error(Err), + {error, Reason} + end. + +convert_to_yaml(In, Out) -> + case ejabberd_config:convert_to_yaml(In, Out) of + ok -> {ok, ""}; + Err -> + Reason = ejabberd_config:format_error(Err), + {error, Reason} + end. %%% %%% Cluster management %%% -join_cluster(NodeBin) -> - ejabberd_cluster:join(list_to_atom(binary_to_list(NodeBin))). +join_cluster(NodeBin) when is_binary(NodeBin) -> + join_cluster(list_to_atom(binary_to_list(NodeBin))); +join_cluster(Node) when is_atom(Node) -> + IsNodes = lists:member(Node, ejabberd_cluster:get_nodes()), + IsKnownNodes = lists:member(Node, ejabberd_cluster:get_known_nodes()), + Ping = net_adm:ping(Node), + join_cluster(Node, IsNodes, IsKnownNodes, Ping). -leave_cluster(NodeBin) -> - ejabberd_cluster:leave(list_to_atom(binary_to_list(NodeBin))). +join_cluster(_Node, true, _IsKnownNodes, _Ping) -> + {error, "This node already joined that running node."}; +join_cluster(_Node, _IsNodes, true, _Ping) -> + {error, "This node already joined that known node."}; +join_cluster(_Node, _IsNodes, _IsKnownNodes, pang) -> + {error, "This node cannot reach that node."}; +join_cluster(Node, false, false, pong) -> + case timer:apply_after(1000, ejabberd_cluster, join, [Node]) of + {ok, _} -> + {ok, "Trying to join that cluster, wait a few seconds and check the list of nodes."}; + Error -> + {error, io_lib:format("Can't join that cluster: ~p", [Error])} + end. + +join_cluster_here(NodeBin) -> + Node = list_to_atom(binary_to_list(NodeBin)), + IsNodes = lists:member(Node, ejabberd_cluster:get_nodes()), + IsKnownNodes = lists:member(Node, ejabberd_cluster:get_known_nodes()), + Ping = net_adm:ping(Node), + join_cluster_here(Node, IsNodes, IsKnownNodes, Ping). + +join_cluster_here(_Node, true, _IsKnownNodes, _Ping) -> + {error, "This node already joined that running node."}; +join_cluster_here(_Node, _IsNodes, true, _Ping) -> + {error, "This node already joined that known node."}; +join_cluster_here(_Node, _IsNodes, _IsKnownNodes, pang) -> + {error, "This node cannot reach that node."}; +join_cluster_here(Node, false, false, pong) -> + case ejabberd_cluster:call(Node, ejabberd_admin, join_cluster, [misc:atom_to_binary(node())]) of + {ok, _} -> + {ok, "Trying to join node to this cluster, wait a few seconds and check the list of nodes."}; + Error -> + {error, io_lib:format("Can't join node to this cluster: ~p", [Error])} + end. + +leave_cluster(NodeBin) when is_binary(NodeBin) -> + leave_cluster(list_to_atom(binary_to_list(NodeBin))); +leave_cluster(Node) -> + ejabberd_cluster:leave(Node). list_cluster() -> ejabberd_cluster:get_nodes(). +list_cluster_detailed() -> + KnownNodes = ejabberd_cluster:get_known_nodes(), + RunningNodes = ejabberd_cluster:get_nodes(), + [get_cluster_node_details(Node, RunningNodes) || Node <- KnownNodes]. + +get_cluster_node_details(Node, RunningNodes) -> + get_cluster_node_details2(Node, lists:member(Node, RunningNodes)). + +get_cluster_node_details2(Node, false) -> + {Node, "false", "", -1, -1, -1, unknown}; +get_cluster_node_details2(Node, true) -> + try ejabberd_cluster:call(Node, ejabberd_admin, get_cluster_node_details3, []) of + Result -> Result + catch + E:R -> + Status = io_lib:format("~p: ~p", [E, R]), + {Node, "true", Status, -1, -1, -1, unknown} + end. + +get_cluster_node_details3() -> + {ok, StatusString} = status(), + UptimeSeconds = mod_admin_extra:stats(<<"uptimeseconds">>), + Processes = mod_admin_extra:stats(<<"processes">>), + OnlineUsers = mod_admin_extra:stats(<<"onlineusersnode">>), + GetMaster = get_master(), + {node(), "true", StatusString, OnlineUsers, Processes, UptimeSeconds, GetMaster}. + %%% %%% Migration management %%% @@ -442,7 +978,6 @@ import_dir(Path) -> {cannot_import_dir, String} end. - %%% %%% Purge DB %%% @@ -451,18 +986,82 @@ delete_expired_messages() -> lists:foreach( fun(Host) -> {atomic, ok} = mod_offline:remove_expired_messages(Host) - end, ?MYHOSTS). + end, ejabberd_option:hosts()). delete_old_messages(Days) -> lists:foreach( fun(Host) -> {atomic, _} = mod_offline:remove_old_messages(Days, Host) - end, ?MYHOSTS). + end, ejabberd_option:hosts()). + +delete_old_messages_batch(Server, Days, BatchSize, Rate) -> + LServer = jid:nameprep(Server), + Mod = gen_mod:db_mod(LServer, mod_offline), + case ejabberd_batch:register_task({spool, LServer}, 0, Rate, {LServer, Days, BatchSize, none}, + fun({L, Da, B, IS} = S) -> + case {erlang:function_exported(Mod, remove_old_messages_batch, 3), + erlang:function_exported(Mod, remove_old_messages_batch, 4)} of + {true, _} -> + case Mod:remove_old_messages_batch(L, Da, B) of + {ok, Count} -> + {ok, S, Count}; + {error, _} = E -> + E + end; + {_, true} -> + case Mod:remove_old_messages_batch(L, Da, B, IS) of + {ok, IS2, Count} -> + {ok, {L, Da, B, IS2}, Count}; + {error, _} = E -> + E + end; + _ -> + {error, not_implemented_for_backend} + end + end) of + ok -> + {ok, ""}; + {error, in_progress} -> + {error, "Operation in progress"} + end. + +delete_old_messages_status(Server) -> + LServer = jid:nameprep(Server), + Msg = case ejabberd_batch:task_status({spool, LServer}) of + not_started -> + "Operation not started"; + {failed, Steps, Error} -> + io_lib:format("Operation failed after deleting ~p messages with error ~p", + [Steps, misc:format_val(Error)]); + {aborted, Steps} -> + io_lib:format("Operation was aborted after deleting ~p messages", + [Steps]); + {working, Steps} -> + io_lib:format("Operation in progress, deleted ~p messages", + [Steps]); + {completed, Steps} -> + io_lib:format("Operation was completed after deleting ~p messages", + [Steps]) + end, + lists:flatten(Msg). + +delete_old_messages_abort(Server) -> + LServer = jid:nameprep(Server), + case ejabberd_batch:abort_task({spool, LServer}) of + aborted -> "Operation aborted"; + not_started -> "No task running" + end. %%% %%% Mnesia management %%% +get_master() -> + case mnesia:table_info(session, master_nodes) of + [] -> none; + [Node] -> Node + end. + set_master("self") -> set_master(node()); set_master(NodeString) when is_list(NodeString) -> @@ -470,7 +1069,7 @@ set_master(NodeString) when is_list(NodeString) -> set_master(Node) when is_atom(Node) -> case mnesia:set_master_nodes([Node]) of ok -> - {ok, ""}; + {ok, "ok"}; {error, Reason} -> String = io_lib:format("Can't set master node ~p at node ~p:~n~p", [Node, node(), Reason]), @@ -491,10 +1090,6 @@ restore_mnesia(Path) -> case ejabberd_admin:restore(Path) of {atomic, _} -> {ok, ""}; - {error, Reason} -> - String = io_lib:format("Can't restore backup from ~p at node ~p: ~p", - [filename:absname(Path), node(), Reason]), - {cannot_restore, String}; {aborted,{no_exists,Table}} -> String = io_lib:format("Can't restore backup from ~p at node ~p: Table ~p does not exist.", [filename:absname(Path), node(), Table]), @@ -517,20 +1112,19 @@ restore(Path) -> %% Obsolete tables or tables created by module who are no longer used are not %% restored and are ignored. keep_tables() -> - lists:flatten([acl, passwd, config, local_config, + lists:flatten([acl, passwd, config, keep_modules_tables()]). %% Returns the list of modules tables in use, according to the list of actually %% loaded modules keep_modules_tables() -> lists:map(fun(Module) -> module_tables(Module) end, - gen_mod:loaded_modules(?MYNAME)). + gen_mod:loaded_modules(ejabberd_config:get_myname())). %% TODO: This mapping should probably be moved to a callback function in each %% module. %% Mapping between modules and their tables module_tables(mod_announce) -> [motd, motd_users]; -module_tables(mod_irc) -> [irc_custom]; module_tables(mod_last) -> [last_activity]; module_tables(mod_muc) -> [muc_room, muc_registered]; module_tables(mod_offline) -> [offline_msg]; @@ -610,6 +1204,13 @@ load_mnesia(Path) -> {cannot_load, String} end. +mnesia_info() -> + lists:flatten(io_lib:format("~p", [mnesia:system_info(all)])). + +mnesia_table_info(Table) -> + ATable = list_to_atom(Table), + lists:flatten(io_lib:format("~p", [mnesia:table_info(ATable, all)])). + install_fallback_mnesia(Path) -> case mnesia:install_fallback(Path) of ok -> @@ -665,3 +1266,256 @@ mnesia_change_nodename(FromString, ToString, Source, Target) -> {[Other], Acc} end, mnesia:traverse_backup(Source, Target, Convert, switched). + +clear_cache() -> + Nodes = ejabberd_cluster:get_nodes(), + lists:foreach(fun(T) -> ets_cache:clear(T, Nodes) end, ets_cache:all()). + +gc() -> + lists:foreach(fun erlang:garbage_collect/1, processes()). + +-spec is_my_host(binary()) -> boolean(). +is_my_host(Host) -> + try ejabberd_router:is_my_host(Host) + catch _:{invalid_domain, _} -> false + end. + +%%% +%%% Internal +%%% + +%% @format-begin + +mnesia_table_change_storage(STable, SType) -> + Table = binary_to_existing_atom(STable, latin1), + Type = + case SType of + <<"remote_copy">> -> + remote_copy; + <<"ram_copies">> -> + ram_copies; + <<"disc_copies">> -> + disc_copies; + <<"disc_only_copies">> -> + disc_only_copies; + _ -> + false + end, + Node = node(), + Result = + case Type of + false -> + "Nothing to do"; + remote_copy -> + mnesia:del_table_copy(Table, Node), + "Deleted table copy"; + _ -> + case mnesia:add_table_copy(Table, Node, Type) of + {aborted, _} -> + mnesia:change_table_copy_type(Table, Node, Type), + "Changed table copy type"; + _ -> + "Added table copy" + end + end, + {ok, Result}. + +mnesia_table_clear(STable) -> + Table = binary_to_existing_atom(STable, latin1), + mnesia:clear_table(Table). + +mnesia_table_delete(STable) -> + Table = binary_to_existing_atom(STable, latin1), + mnesia:delete_table(Table). + +mnesia_table_details(STable) -> + Table = binary_to_existing_atom(STable, latin1), + [{Name, iolist_to_binary(str:format("~p", [Value]))} + || {Name, Value} <- mnesia:table_info(Table, all)]. + +mnesia_list_tables() -> + STables = + lists:sort( + mnesia:system_info(tables)), + lists:map(fun(Table) -> + TInfo = mnesia:table_info(Table, all), + {value, {storage_type, Type}} = lists:keysearch(storage_type, 1, TInfo), + {value, {size, Size}} = lists:keysearch(size, 1, TInfo), + {value, {memory, Memory}} = lists:keysearch(memory, 1, TInfo), + MemoryB = Memory * erlang:system_info(wordsize), + MemoryKB = MemoryB div 1024, + MemoryMB = MemoryKB div 1024, + {Table, storage_type_bin(Type), Size, MemoryKB, MemoryMB} + end, + STables). + +storage_type_bin(ram_copies) -> + <<"RAM copy">>; +storage_type_bin(disc_copies) -> + <<"RAM and disc copy">>; +storage_type_bin(disc_only_copies) -> + <<"Disc only copy">>; +storage_type_bin(unknown) -> + <<"Remote copy">>. + +echo(Sentence) -> + Sentence. + +echo3(_, _, Sentence) -> + Sentence. + +%%% +%%% Web Admin: Main +%%% + +web_menu_main(Acc, _Lang) -> + Acc ++ [{<<"purge">>, <<"Purge">>}, {<<"stanza">>, <<"Stanza">>}]. + +web_page_main(_, #request{path = [<<"purge">>]} = R) -> + Types = + [{<<"#erlang">>, <<"Erlang">>}, + {<<"#users">>, <<"Users">>}, + {<<"#offline">>, <<"Offline">>}, + {<<"#mam">>, <<"MAM">>}, + {<<"#pubsub">>, <<"PubSub">>}, + {<<"#push">>, <<"Push">>}], + Head = [?XC(<<"h1">>, <<"Purge">>)], + Set = [?XE(<<"ul">>, [?LI([?AC(MIU, MIN)]) || {MIU, MIN} <- Types]), + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"erlang">>}], <<"Erlang">>), + ?XE(<<"blockquote">>, + [ejabberd_web_admin:make_command(clear_cache, R), + ejabberd_web_admin:make_command(gc, R)]), + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"users">>}], <<"Users">>), + ?XE(<<"blockquote">>, [ejabberd_web_admin:make_command(delete_old_users, R)]), + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"offline">>}], <<"Offline">>), + ?XE(<<"blockquote">>, + [ejabberd_web_admin:make_command(delete_expired_messages, R), + ejabberd_web_admin:make_command(delete_old_messages, R), + ejabberd_web_admin:make_command(delete_old_messages_batch, R), + ejabberd_web_admin:make_command(delete_old_messages_status, R)]), + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"mam">>}], <<"MAM">>), + ?XE(<<"blockquote">>, + [ejabberd_web_admin:make_command(delete_old_mam_messages, R), + ejabberd_web_admin:make_command(delete_old_mam_messages_batch, R), + ejabberd_web_admin:make_command(delete_old_mam_messages_status, R)]), + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"pubsub">>}], <<"PubSub">>), + ?XE(<<"blockquote">>, + [ejabberd_web_admin:make_command(delete_expired_pubsub_items, R), + ejabberd_web_admin:make_command(delete_old_pubsub_items, R)]), + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"push">>}], <<"Push">>), + ?XE(<<"blockquote">>, [ejabberd_web_admin:make_command(delete_old_push_sessions, R)])], + {stop, Head ++ Set}; +web_page_main(_, #request{path = [<<"stanza">>]} = R) -> + Head = [?XC(<<"h1">>, <<"Stanza">>)], + Set = [ejabberd_web_admin:make_command(send_message, R), + ejabberd_web_admin:make_command(send_stanza, R), + ejabberd_web_admin:make_command(send_stanza_c2s, R)], + {stop, Head ++ Set}; +web_page_main(Acc, _) -> + Acc. + +%%% +%%% Web Admin: Node +%%% + +web_menu_node(Acc, _Node, _Lang) -> + Acc + ++ [{<<"cluster">>, <<"Clustering">>}, + {<<"update">>, <<"Code Update">>}, + {<<"config-file">>, <<"Configuration File">>}, + {<<"logs">>, <<"Logs">>}, + {<<"stop">>, <<"Stop Node">>}]. + +web_page_node(_, Node, #request{path = [<<"cluster">>]} = R) -> + {ok, Names} = net_adm:names(), + NodeNames = lists:join(", ", [Name || {Name, _Port} <- Names]), + Hint = + list_to_binary(io_lib:format("Hint: Erlang nodes found in this machine that may be running ejabberd: ~s", + [NodeNames])), + Head = ?H1GLraw(<<"Clustering">>, <<"admin/guide/clustering/">>, <<"Clustering">>), + Set1 = + [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [join_cluster_here, R, [], []]), + ?XE(<<"blockquote">>, [?C(Hint)]), + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [join_cluster, R, [], [{style, danger}]]), + ?XE(<<"blockquote">>, [?C(Hint)]), + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [leave_cluster, R, [], [{style, danger}]])], + Set2 = + [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [set_master, R, [], [{style, danger}]])], + timer:sleep(100), % leaving a cluster takes a while, let's delay the get commands + Get1 = + [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [list_cluster_detailed, + R, + [], + [{result_links, [{name, node, 3, <<"">>}]}]])], + Get2 = + [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [get_master, + R, + [], + [{result_named, true}, + {result_links, [{nodename, node, 3, <<"">>}]}]])], + {stop, Head ++ Get1 ++ Set1 ++ Get2 ++ Set2}; +web_page_node(_, Node, #request{path = [<<"update">>]} = R) -> + Head = [?XC(<<"h1">>, <<"Code Update">>)], + Set = [ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [update, R])], + Get = [ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [update_list, R])], + {stop, Head ++ Get ++ Set}; +web_page_node(_, Node, #request{path = [<<"config-file">>]} = R) -> + Res = ?H1GLraw(<<"Configuration File">>, + <<"admin/configuration/file-format/">>, + <<"File Format">>) + ++ [ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [convert_to_yaml, R]), + ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [dump_config, R]), + ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [reload_config, R])], + {stop, Res}; +web_page_node(_, Node, #request{path = [<<"stop">>]} = R) -> + Res = [?XC(<<"h1">>, <<"Stop This Node">>), + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [restart, R, [], [{style, danger}]]), + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [stop_kindly, R, [], [{style, danger}]]), + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [stop, R, [], [{style, danger}]]), + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [halt, R, [], [{style, danger}]])], + {stop, Res}; +web_page_node(_, Node, #request{path = [<<"logs">>]} = R) -> + Res = ?H1GLraw(<<"Logs">>, <<"admin/configuration/basic/#logging">>, <<"Logging">>) + ++ [ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [set_loglevel, R]), + ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [get_loglevel, R]), + ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [reopen_log, R]), + ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [rotate_log, R])], + {stop, Res}; +web_page_node(Acc, _, _) -> + Acc. diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl index e4333c816..cbe74fb08 100644 --- a/src/ejabberd_app.erl +++ b/src/ejabberd_app.erl @@ -5,7 +5,7 @@ %%% Created : 31 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -25,133 +25,104 @@ -module(ejabberd_app). --behaviour(ejabberd_config). -author('alexey@process-one.net'). -behaviour(application). --export([start/2, prep_stop/1, stop/1, - init/0, opt_type/1]). +-export([start/2, prep_stop/1, stop/1]). --include("ejabberd.hrl"). -include("logger.hrl"). + %%% %%% Application API %%% start(normal, _Args) -> - ejabberd_logger:start(), - write_pid_file(), - start_apps(), - start_elixir_application(), - ejabberd:check_app(ejabberd), - randoms:start(), - db_init(), - start(), - translate:start(), - ejabberd_access_permissions:start_link(), - ejabberd_ctl:init(), - ejabberd_commands:init(), - ejabberd_admin:start(), - gen_mod:start(), - ext_mod:start(), - setup_if_elixir_conf_used(), - ejabberd_config:start(), - set_settings_from_config(), - acl:start(), - shaper:start(), - connect_nodes(), - Sup = ejabberd_sup:start_link(), - ejabberd_rdbms:start(), - ejabberd_riak_sup:start(), - ejabberd_redis:start(), - ejabberd_sm:start(), - cyrsasl:start(), - % Profiling - %ejabberd_debug:eprof_start(), - %ejabberd_debug:fprof_start(), - maybe_add_nameservers(), - ejabberd_auth:start(), - ejabberd_oauth:start(), - gen_mod:start_modules(), - ejabberd_listener:start_listeners(), - register_elixir_config_hooks(), - ?INFO_MSG("ejabberd ~s is started in the node ~p", [?VERSION, node()]), - Sup; + try + {T1, _} = statistics(wall_clock), + ejabberd_logger:start(), + write_pid_file(), + start_included_apps(), + misc:warn_unset_home(), + start_elixir_application(), + setup_if_elixir_conf_used(), + case ejabberd_config:load() of + ok -> + ejabberd_mnesia:start(), + file_queue_init(), + maybe_add_nameservers(), + case ejabberd_sup:start_link() of + {ok, SupPid} -> + ejabberd_system_monitor:start(), + register_elixir_config_hooks(), + ejabberd_cluster:wait_for_sync(infinity), + ejabberd_hooks:run(ejabberd_started, []), + ejabberd:check_apps(), + ejabberd_systemd:ready(), + maybe_start_exsync(), + {T2, _} = statistics(wall_clock), + ?INFO_MSG("ejabberd ~ts is started in the node ~p in ~.2fs", + [ejabberd_option:version(), + node(), (T2-T1)/1000]), + maybe_print_elixir_version(), + ?INFO_MSG("~ts", + [erlang:system_info(system_version)]), + {ok, SupPid}; + Err -> + ?CRITICAL_MSG("Failed to start ejabberd application: ~p", [Err]), + ejabberd:halt() + end; + Err -> + ?CRITICAL_MSG("Failed to start ejabberd application: ~ts", + [ejabberd_config:format_error(Err)]), + ejabberd:halt() + end + catch throw:{?MODULE, Error} -> + ?DEBUG("Failed to start ejabberd application: ~p", [Error]), + ejabberd:halt() + end; start(_, _) -> {error, badarg}. +start_included_apps() -> + {ok, Apps} = application:get_key(ejabberd, included_applications), + lists:foreach( + fun(mnesia) -> + ok; + (lager) -> + ok; + (os_mon)-> + ok; + (App) -> + application:ensure_all_started(App) + end, Apps). + %% Prepare the application for termination. %% This function is called when an application is about to be stopped, %% before shutting down the processes of the application. prep_stop(State) -> - ejabberd_listener:stop_listeners(), - ejabberd_admin:stop(), - broadcast_c2s_shutdown(), - gen_mod:stop_modules(), - timer:sleep(5000), + ejabberd_systemd:stopping(), + ejabberd_hooks:run(ejabberd_stopping, []), + ejabberd_listener:stop(), + ejabberd_sm:stop(), + ejabberd_service:stop(), + ejabberd_s2s:stop(), + ejabberd_system_monitor:stop(), + gen_mod:prep_stop(), + gen_mod:stop(), State. %% All the processes were killed when this function is called stop(_State) -> - ?INFO_MSG("ejabberd ~s is stopped in the node ~p", [?VERSION, node()]), - delete_pid_file(), - %%ejabberd_debug:stop(), - ok. - + ?INFO_MSG("ejabberd ~ts is stopped in the node ~p", + [ejabberd_option:version(), node()]), + delete_pid_file(). %%% %%% Internal functions %%% -start() -> - spawn_link(?MODULE, init, []). - -init() -> - register(ejabberd, self()), - loop(). - -loop() -> - receive - _ -> - loop() - end. - -db_init() -> - ejabberd_config:env_binary_to_list(mnesia, dir), - MyNode = node(), - DbNodes = mnesia:system_info(db_nodes), - case lists:member(MyNode, DbNodes) of - true -> - ok; - false -> - ?CRITICAL_MSG("Node name mismatch: I'm [~s], " - "the database is owned by ~p", [MyNode, DbNodes]), - ?CRITICAL_MSG("Either set ERLANG_NODE in ejabberdctl.cfg " - "or change node name in Mnesia", []), - erlang:error(node_name_mismatch) - end, - case mnesia:system_info(extra_db_nodes) of - [] -> - mnesia:create_schema([node()]); - _ -> - ok - end, - ejabberd:start_app(mnesia, permanent), - mnesia:wait_for_tables(mnesia:system_info(local_tables), infinity). - -connect_nodes() -> - Nodes = ejabberd_config:get_option( - cluster_nodes, - fun(Ns) -> - true = lists:all(fun is_atom/1, Ns), - Ns - end, []), - lists:foreach(fun(Node) -> - net_kernel:connect_node(Node) - end, Nodes). - %% If ejabberd is running on some Windows machine, get nameservers and add to Erlang maybe_add_nameservers() -> case os:type() of @@ -164,16 +135,6 @@ add_windows_nameservers() -> ?INFO_MSG("Adding machine's DNS IPs to Erlang system:~n~p", [IPTs]), lists:foreach(fun(IPT) -> inet_db:add_ns(IPT) end, IPTs). - -broadcast_c2s_shutdown() -> - Children = ejabberd_sm:get_all_pids(), - lists:foreach( - fun(C2SPid) when node(C2SPid) == node() -> - C2SPid ! system_shutdown; - (_) -> - ok - end, Children). - %%% %%% PID file %%% @@ -187,13 +148,13 @@ write_pid_file() -> end. write_pid_file(Pid, PidFilename) -> - case file:open(PidFilename, [write]) of - {ok, Fd} -> - io:format(Fd, "~s~n", [Pid]), - file:close(Fd); - {error, Reason} -> - ?ERROR_MSG("Cannot write PID file ~s~nReason: ~p", [PidFilename, Reason]), - throw({cannot_write_pid_file, PidFilename, Reason}) + case file:write_file(PidFilename, io_lib:format("~ts~n", [Pid])) of + ok -> + ok; + {error, Reason} = Err -> + ?CRITICAL_MSG("Cannot write PID file ~ts: ~ts", + [PidFilename, file:format_error(Reason)]), + throw({?MODULE, Err}) end. delete_pid_file() -> @@ -204,61 +165,62 @@ delete_pid_file() -> file:delete(PidFilename) end. -set_settings_from_config() -> - Level = ejabberd_config:get_option( - loglevel, - fun(P) when P>=0, P=<5 -> P end, - 4), - ejabberd_logger:set(Level), - Ticktime = ejabberd_config:get_option( - net_ticktime, - opt_type(net_ticktime), - 60), - net_kernel:set_net_ticktime(Ticktime). +file_queue_init() -> + QueueDir = case ejabberd_option:queue_dir() of + undefined -> + MnesiaDir = mnesia:system_info(directory), + filename:join(MnesiaDir, "queue"); + Path -> + Path + end, + case p1_queue:start(QueueDir) of + ok -> ok; + Err -> throw({?MODULE, Err}) + end. -start_apps() -> - crypto:start(), - ejabberd:start_app(sasl), - ejabberd:start_app(ssl), - ejabberd:start_app(fast_yaml), - ejabberd:start_app(fast_tls), - ejabberd:start_app(xmpp), - ejabberd:start_app(cache_tab). +%%% +%%% Elixir +%%% -opt_type(net_ticktime) -> - fun (P) when is_integer(P), P > 0 -> P end; -opt_type(cluster_nodes) -> - fun (Ns) -> true = lists:all(fun is_atom/1, Ns), Ns end; -opt_type(loglevel) -> - fun (P) when P >= 0, P =< 5 -> P end; -opt_type(modules) -> - fun (Mods) -> - lists:map(fun ({M, A}) when is_atom(M), is_list(A) -> - {M, A} - end, - Mods) - end; -opt_type(_) -> [cluster_nodes, loglevel, modules, net_ticktime]. +-ifdef(ELIXIR_ENABLED). +is_using_elixir_config() -> + Config = ejabberd_config:path(), + try 'Elixir.Ejabberd.ConfigUtil':is_elixir_config(Config) of + B when is_boolean(B) -> B + catch + _:_ -> false + end. setup_if_elixir_conf_used() -> - case ejabberd_config:is_using_elixir_config() of + case is_using_elixir_config() of true -> 'Elixir.Ejabberd.Config.Store':start_link(); false -> ok end. register_elixir_config_hooks() -> - case ejabberd_config:is_using_elixir_config() of + case is_using_elixir_config() of true -> 'Elixir.Ejabberd.Config':start_hooks(); false -> ok end. start_elixir_application() -> - case ejabberd_config:is_elixir_enabled() of - true -> - case application:ensure_started(elixir) of - ok -> ok; - {error, _Msg} -> ?ERROR_MSG("Elixir application not started.", []) - end; - _ -> - ok + case application:ensure_started(elixir) of + ok -> ok; + {error, _Msg} -> ?ERROR_MSG("Elixir application not started.", []) end. + +maybe_start_exsync() -> + case os:getenv("RELIVE") of + "true" -> rpc:call(node(), 'Elixir.ExSync.Application', start, []); + _ -> ok + end. + +maybe_print_elixir_version() -> + ?INFO_MSG("Elixir ~ts", [maps:get(build, 'Elixir.System':build_info())]). +-else. +setup_if_elixir_conf_used() -> ok. +register_elixir_config_hooks() -> ok. +start_elixir_application() -> ok. +maybe_start_exsync() -> ok. +maybe_print_elixir_version() -> ok. +-endif. diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 74c8009c2..0c5d2fc69 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -1,11 +1,11 @@ %%%---------------------------------------------------------------------- %%% File : ejabberd_auth.erl %%% Author : Alexey Shchepin -%%% Purpose : Authentification +%%% Purpose : Authentication %%% Created : 23 Nov 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -22,33 +22,46 @@ %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- - -%% TODO: Use the functions in ejabberd auth to add and remove users. - -module(ejabberd_auth). --behaviour(ejabberd_config). +-behaviour(gen_server). -author('alexey@process-one.net'). +-protocol({rfc, 5802}). + %% External exports --export([start/0, set_password/3, check_password/4, +-export([start_link/0, host_up/1, host_down/1, config_reloaded/0, + set_password/3, check_password/4, check_password/6, check_password_with_authmodule/4, check_password_with_authmodule/6, try_register/3, - dirty_get_registered_users/0, get_vh_registered_users/1, - get_vh_registered_users/2, export/1, import_info/0, - get_vh_registered_users_number/1, import/5, import_start/2, - get_vh_registered_users_number/2, get_password/2, + get_users/0, get_users/1, password_to_scram/2, + get_users/2, import_info/0, + count_users/1, import/5, import_start/2, + count_users/2, get_password/2, get_password_s/2, get_password_with_authmodule/2, - is_user_exists/2, is_user_exists_in_other_modules/3, + user_exists/2, user_exists_in_other_modules/3, remove_user/2, remove_user/3, plain_password_required/1, - store_type/1, entropy/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]). --export([auth_modules/1, opt_type/1]). +-export([auth_modules/1, convert_to_scram/1, drop_password_type/2, set_password_instance/3]). --include("ejabberd.hrl"). +-include_lib("xmpp/include/scram.hrl"). -include("logger.hrl"). +-define(SALT_LENGTH, 16). + +-record(state, {host_modules = #{} :: host_modules()}). + +-type host_modules() :: #{binary => [module()]}. +-type password() :: binary() | #scram{}. +-type digest_fun() :: fun((binary()) -> binary()). +-export_type([password/0]). + %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- @@ -57,333 +70,563 @@ {offset, integer()}]. -callback start(binary()) -> any(). --callback plain_password_required() -> boolean(). --callback store_type() -> plain | external | scram. --callback set_password(binary(), binary(), binary()) -> ok | {error, atom()}. --callback remove_user(binary(), binary()) -> any(). --callback remove_user(binary(), binary(), binary()) -> any(). --callback is_user_exists(binary(), binary()) -> boolean() | {error, atom()}. --callback check_password(binary(), binary(), binary(), binary()) -> boolean(). --callback check_password(binary(), binary(), binary(), binary(), binary(), - fun((binary()) -> binary())) -> boolean(). --callback try_register(binary(), binary(), binary()) -> {atomic, atom()} | - {error, atom()}. --callback dirty_get_registered_users() -> [{binary(), binary()}]. --callback get_vh_registered_users(binary()) -> [{binary(), binary()}]. --callback get_vh_registered_users(binary(), opts()) -> [{binary(), binary()}]. --callback get_vh_registered_users_number(binary()) -> number(). --callback get_vh_registered_users_number(binary(), opts()) -> number(). --callback get_password(binary(), binary()) -> false | binary() | {binary(), binary(), binary(), integer()}. --callback get_password_s(binary(), binary()) -> binary() | {binary(), binary(), binary(), integer()}. +-callback stop(binary()) -> any(). +-callback reload(binary()) -> any(). +-callback plain_password_required(binary()) -> boolean(). +-callback store_type(binary()) -> plain | external | scram. +-callback set_password(binary(), binary(), password()) -> + {ets_cache:tag(), {ok, password()} | {error, db_failure | not_allowed}}. +-callback set_password_multiple(binary(), binary(), [password()]) -> + {ets_cache:tag(), {ok, [password()]} | {error, db_failure | not_allowed}}. +-callback set_password_instance(binary(), binary(), password()) -> + ok | {error, db_failure | not_allowed}. +-callback remove_user(binary(), binary()) -> ok | {error, db_failure | not_allowed}. +-callback user_exists(binary(), binary()) -> {ets_cache:tag(), boolean() | {error, db_failure}}. +-callback check_password(binary(), binary(), binary(), binary()) -> {ets_cache:tag(), boolean() | {stop, boolean()}}. +-callback try_register(binary(), binary(), password()) -> + {ets_cache:tag(), {ok, password()} | {error, exists | db_failure | not_allowed}}. +-callback try_register_multiple(binary(), binary(), [password()]) -> + {ets_cache:tag(), {ok, [password()]} | {error, exists | db_failure | not_allowed}}. +-callback get_users(binary(), opts()) -> [{binary(), binary()}]. +-callback count_users(binary(), opts()) -> number(). +-callback get_password(binary(), binary()) -> {ets_cache:tag(), {ok, password() | [password()]} | error}. +-callback drop_password_type(binary(), atom()) -> + ok | {error, db_failure | not_allowed}. +-callback use_cache(binary()) -> boolean(). +-callback cache_nodes(binary()) -> boolean(). -start() -> - %% This is only executed by ejabberd_c2s for non-SASL auth client - lists:foreach(fun (Host) -> - lists:foreach(fun (M) -> M:start(Host) end, - auth_modules(Host)) - end, - ?MYHOSTS). +-optional_callbacks([reload/1, + set_password/3, + set_password_multiple/3, + set_password_instance/3, + remove_user/2, + user_exists/2, + check_password/4, + try_register/3, + try_register_multiple/3, + get_users/2, + count_users/2, + get_password/2, + drop_password_type/2, + use_cache/1, + cache_nodes/1]). +-spec start_link() -> {ok, pid()} | {error, any()}. +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +init([]) -> + ejabberd_hooks:add(host_up, ?MODULE, host_up, 30), + ejabberd_hooks:add(host_down, ?MODULE, host_down, 80), + ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 40), + HostModules = lists:foldl( + fun(Host, Acc) -> + Modules = auth_modules(Host), + maps:put(Host, Modules, Acc) + end, #{}, ejabberd_option:hosts()), + lists:foreach( + fun({Host, Modules}) -> + start(Host, Modules) + end, maps:to_list(HostModules)), + init_cache(HostModules), + {ok, #state{host_modules = HostModules}}. + +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), + {noreply, State}. + +handle_cast({host_up, Host}, #state{host_modules = HostModules} = State) -> + Modules = auth_modules(Host), + start(Host, Modules), + NewHostModules = maps:put(Host, Modules, HostModules), + init_cache(NewHostModules), + {noreply, State#state{host_modules = NewHostModules}}; +handle_cast({host_down, Host}, #state{host_modules = HostModules} = State) -> + Modules = maps:get(Host, HostModules, []), + stop(Host, Modules), + NewHostModules = maps:remove(Host, HostModules), + init_cache(NewHostModules), + {noreply, State#state{host_modules = NewHostModules}}; +handle_cast(config_reloaded, #state{host_modules = HostModules} = State) -> + NewHostModules = + lists:foldl( + fun(Host, Acc) -> + OldModules = maps:get(Host, HostModules, []), + NewModules = auth_modules(Host), + start(Host, NewModules -- OldModules), + stop(Host, OldModules -- NewModules), + reload(Host, misc:intersection(OldModules, NewModules)), + maps:put(Host, NewModules, Acc) + end, HostModules, ejabberd_option:hosts()), + init_cache(NewHostModules), + {noreply, State#state{host_modules = NewHostModules}}; +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), + {noreply, State}. + +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), + {noreply, State}. + +terminate(_Reason, State) -> + ejabberd_hooks:delete(host_up, ?MODULE, host_up, 30), + ejabberd_hooks:delete(host_down, ?MODULE, host_down, 80), + ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 40), + lists:foreach( + fun({Host, Modules}) -> + stop(Host, Modules) + end, maps:to_list(State#state.host_modules)). + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +start(Host, Modules) -> + lists:foreach(fun(M) -> M:start(Host) end, Modules). + +stop(Host, Modules) -> + lists:foreach(fun(M) -> M:stop(Host) end, Modules). + +reload(Host, Modules) -> + lists:foreach( + fun(M) -> + case erlang:function_exported(M, reload, 1) of + true -> M:reload(Host); + false -> ok + end + end, Modules). + +host_up(Host) -> + gen_server:cast(?MODULE, {host_up, Host}). + +host_down(Host) -> + gen_server:cast(?MODULE, {host_down, Host}). + +config_reloaded() -> + gen_server:cast(?MODULE, config_reloaded). + +-spec plain_password_required(binary()) -> boolean(). plain_password_required(Server) -> - lists:any(fun (M) -> M:plain_password_required() end, + lists:any(fun (M) -> M:plain_password_required(Server) end, auth_modules(Server)). +-spec store_type(binary()) -> plain | scram | external. store_type(Server) -> -%% @doc Check if the user and password can login in server. -%% @spec (User::string(), Server::string(), Password::string()) -> -%% true | false - lists:foldl(fun (_, external) -> external; - (M, scram) -> - case M:store_type() of - external -> external; - _Else -> scram - end; - (M, plain) -> M:store_type() - end, - plain, auth_modules(Server)). + case auth_modules(Server) of + [ejabberd_auth_anonymous] -> external; + Modules -> + lists:foldl( + fun(ejabberd_auth_anonymous, Type) -> Type; + (_, external) -> external; + (M, scram) -> + case M:store_type(Server) of + external -> external; + _ -> scram + end; + (M, plain) -> + M:store_type(Server) + end, plain, Modules) + end. -spec check_password(binary(), binary(), binary(), binary()) -> boolean(). - check_password(User, AuthzId, Server, Password) -> - case check_password_with_authmodule(User, AuthzId, Server, - Password) - of - {true, _AuthModule} -> true; - false -> false - end. + check_password(User, AuthzId, Server, Password, <<"">>, undefined). -%% @doc Check if the user and password can login in server. -%% @spec (User::string(), AuthzId::string(), Server::string(), Password::string(), -%% Digest::string(), DigestGen::function()) -> -%% true | false -spec check_password(binary(), binary(), binary(), binary(), binary(), - fun((binary()) -> binary())) -> boolean(). - -check_password(User, AuthzId, Server, Password, Digest, - DigestGen) -> - case check_password_with_authmodule(User, AuthzId, Server, - Password, Digest, DigestGen) - of - {true, _AuthModule} -> true; - false -> false + digest_fun() | undefined) -> boolean(). +check_password(User, AuthzId, Server, Password, Digest, DigestGen) -> + case check_password_with_authmodule( + User, AuthzId, Server, Password, Digest, DigestGen) of + {true, _AuthModule} -> true; + {false, _ErrorAtom, _Reason} -> false; + false -> false end. -%% @doc Check if the user and password can login in server. -%% The user can login if at least an authentication method accepts the user -%% and the password. -%% The first authentication method that accepts the credentials is returned. -%% @spec (User::string(), AuthzId::string(), Server::string(), Password::string()) -> -%% {true, AuthModule} | false -%% where -%% AuthModule = ejabberd_auth_anonymous | ejabberd_auth_external -%% | ejabberd_auth_mnesia | ejabberd_auth_ldap -%% | ejabberd_auth_sql | ejabberd_auth_pam | ejabberd_auth_riak --spec check_password_with_authmodule(binary(), binary(), binary(), binary()) -> false | - {true, atom()}. +-spec check_password_with_authmodule(binary(), binary(), + binary(), binary()) -> false | {true, atom()}. +check_password_with_authmodule(User, AuthzId, Server, Password) -> + check_password_with_authmodule( + User, AuthzId, Server, Password, <<"">>, undefined). -check_password_with_authmodule(User, AuthzId, Server, - Password) -> - check_password_loop(auth_modules(Server), - [User, AuthzId, Server, Password]). - --spec check_password_with_authmodule(binary(), binary(), binary(), binary(), binary(), - fun((binary()) -> binary())) -> false | - {true, atom()}. - -check_password_with_authmodule(User, AuthzId, Server, Password, - Digest, DigestGen) -> - check_password_loop(auth_modules(Server), - [User, AuthzId, Server, Password, Digest, DigestGen]). - -check_password_loop([], _Args) -> false; -check_password_loop([AuthModule | AuthModules], Args) -> - case apply(AuthModule, check_password, Args) of - true -> {true, AuthModule}; - false -> check_password_loop(AuthModules, Args) +-spec check_password_with_authmodule( + binary(), binary(), binary(), binary(), binary(), + digest_fun() | undefined) -> false | {false, atom(), binary()} | {true, atom()}. +check_password_with_authmodule(User, AuthzId, Server, Password, Digest, DigestGen) -> + case validate_credentials(User, Server) of + {ok, LUser, LServer} -> + case {jid:nodeprep(AuthzId), get_is_banned(LUser, LServer)} of + {error, _} -> + false; + {_, {is_banned, BanReason}} -> + {false, 'account-disabled', BanReason}; + {LAuthzId, _} -> + untag_stop( + lists:foldl( + fun(Mod, false) -> + case db_check_password( + LUser, LAuthzId, LServer, Password, + Digest, DigestGen, Mod) of + true -> {true, Mod}; + false -> false; + {stop, true} -> {stop, {true, Mod}}; + {stop, false} -> {stop, false} + end; + (_, Acc) -> + Acc + end, false, auth_modules(LServer))) + end; + _ -> + false end. --spec set_password(binary(), binary(), binary()) -> ok | - {error, atom()}. +convert_password_for_storage(_Server, #scram{} = Password) -> + {Password, [Password]}; +convert_password_for_storage(Server, Password) -> + P = case ejabberd_option:auth_stored_password_types(Server) of + [] -> + case ejabberd_option:auth_password_format(Server) of + plain -> + [Password]; + _ -> + [password_to_scram(Server, Password)] + end; + M -> + lists:sort(lists:map( + fun(scram_sha1) -> + password_to_scram(Server, Password, sha, ?SCRAM_DEFAULT_ITERATION_COUNT); + (scram_sha256) -> + password_to_scram(Server, Password, sha256, ?SCRAM_DEFAULT_ITERATION_COUNT); + (scram_sha512) -> + password_to_scram(Server, Password, sha512, ?SCRAM_DEFAULT_ITERATION_COUNT); + (plain) -> + Password + end, M)) + end, + {Password, P}. -%% @spec (User::string(), Server::string(), Password::string()) -> -%% ok | {error, ErrorType} -%% where ErrorType = empty_password | not_allowed | invalid_jid -set_password(_User, _Server, <<"">>) -> - {error, empty_password}; +-spec set_password(binary(), binary(), password()) -> ok | {error, + db_failure | not_allowed | + invalid_jid | invalid_password}. set_password(User, Server, Password) -> -%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, not_allowed} - lists:foldl(fun (M, {error, _}) -> - M:set_password(User, Server, Password); - (_M, Res) -> Res - end, - {error, not_allowed}, auth_modules(Server)). - --spec try_register(binary(), binary(), binary()) -> {atomic, atom()} | - {error, atom()}. - -try_register(_User, _Server, <<"">>) -> - {error, not_allowed}; -try_register(User, Server, Password) -> - case is_user_exists(User, Server) of - true -> {atomic, exists}; - false -> - LServer = jid:nameprep(Server), - case lists:member(LServer, ?MYHOSTS) of - true -> - Res = lists:foldl(fun (_M, {atomic, ok} = Res) -> Res; - (M, _) -> - M:try_register(User, Server, Password) - end, - {error, not_allowed}, auth_modules(Server)), - case Res of - {atomic, ok} -> - ejabberd_hooks:run(register_user, Server, - [User, Server]), - {atomic, ok}; - _ -> Res - end; - false -> {error, not_allowed} - end + case validate_credentials(User, Server, Password) of + {ok, LUser, LServer} -> + {Plain, Passwords} = convert_password_for_storage(Server, Password), + lists:foldl( + fun(M, {error, _}) -> + db_set_password(LUser, LServer, Plain, Passwords, M); + (_, ok) -> + ok + end, {error, not_allowed}, auth_modules(LServer)); + Err -> + Err end. -%% Registered users list do not include anonymous users logged --spec dirty_get_registered_users() -> [{binary(), binary()}]. +set_password_instance(User, Server, Password) -> + case validate_credentials(User, Server, Password) of + {ok, LUser, LServer} -> + lists:foldl( + fun(Mod, {error, _} = Acc) -> + case erlang:function_exported(Mod, set_password_instance, 3) of + true -> + R = Mod:set_password_instance(LUser, LServer, Password), + case use_cache(Mod, LServer) of + true -> + ets_cache:delete(cache_tab(Mod), {LUser, LServer}, + cache_nodes(Mod, LServer)); + _ -> + ok + end, + R; + _ -> + Acc + end; + (_, ok) -> + ok + end, {error, not_allowed}, auth_modules(LServer)); + Err -> + Err + end. -dirty_get_registered_users() -> - lists:flatmap(fun (M) -> M:dirty_get_registered_users() - end, - auth_modules()). +-spec try_register(binary(), binary(), password()) -> ok | {error, + db_failure | not_allowed | exists | + invalid_jid | invalid_password}. +try_register(User, Server, Password) -> + case validate_credentials(User, Server, Password) of + {ok, LUser, LServer} -> + case user_exists(LUser, LServer) of + true -> + {error, exists}; + false -> + case ejabberd_router:is_my_host(LServer) of + true -> + case ejabberd_hooks:run_fold(check_register_user, LServer, true, + [User, Server, Password]) of + true -> + {Plain, Passwords} = convert_password_for_storage(Server, Password), + case lists:foldl( + fun(_, ok) -> + ok; + (Mod, _) -> + db_try_register( + LUser, LServer, Plain, Passwords, Mod) + end, {error, not_allowed}, auth_modules(LServer)) of + ok -> + ejabberd_hooks:run( + register_user, LServer, [LUser, LServer]); + {error, _} = Err -> + Err + end; + false -> + {error, not_allowed} + end; + false -> + {error, not_allowed} + end + end; + Err -> + Err + end. --spec get_vh_registered_users(binary()) -> [{binary(), binary()}]. +-spec get_users() -> [{binary(), binary()}]. +get_users() -> + lists:flatmap( + fun({Host, Mod}) -> + db_get_users(Host, [], Mod) + end, auth_modules()). -%% Registered users list do not include anonymous users logged -get_vh_registered_users(Server) -> - lists:flatmap(fun (M) -> - M:get_vh_registered_users(Server) - end, - auth_modules(Server)). +-spec get_users(binary()) -> [{binary(), binary()}]. +get_users(Server) -> + get_users(Server, []). --spec get_vh_registered_users(binary(), opts()) -> [{binary(), binary()}]. +-spec get_users(binary(), opts()) -> [{binary(), binary()}]. +get_users(Server, Opts) -> + case jid:nameprep(Server) of + error -> []; + LServer -> + lists:flatmap( + fun(M) -> db_get_users(LServer, Opts, M) end, + auth_modules(LServer)) + end. -get_vh_registered_users(Server, Opts) -> - lists:flatmap(fun (M) -> - case erlang:function_exported(M, - get_vh_registered_users, - 2) - of - true -> M:get_vh_registered_users(Server, Opts); - false -> M:get_vh_registered_users(Server) - end - end, - auth_modules(Server)). +-spec count_users(binary()) -> non_neg_integer(). +count_users(Server) -> + count_users(Server, []). -get_vh_registered_users_number(Server) -> - lists:sum(lists:map(fun (M) -> - case erlang:function_exported(M, - get_vh_registered_users_number, - 1) - of - true -> - M:get_vh_registered_users_number(Server); - false -> - length(M:get_vh_registered_users(Server)) - end - end, - auth_modules(Server))). - --spec get_vh_registered_users_number(binary(), opts()) -> number(). - -get_vh_registered_users_number(Server, Opts) -> -%% @doc Get the password of the user. -%% @spec (User::string(), Server::string()) -> Password::string() - lists:sum(lists:map(fun (M) -> - case erlang:function_exported(M, - get_vh_registered_users_number, - 2) - of - true -> - M:get_vh_registered_users_number(Server, - Opts); - false -> - length(M:get_vh_registered_users(Server)) - end - end, - auth_modules(Server))). - --spec get_password(binary(), binary()) -> false | binary() | {binary(), binary(), binary(), integer()}. +-spec count_users(binary(), opts()) -> non_neg_integer(). +count_users(Server, Opts) -> + case jid:nameprep(Server) of + error -> 0; + LServer -> + lists:sum( + lists:map( + fun(M) -> db_count_users(LServer, Opts, M) end, + auth_modules(LServer))) + end. +-spec get_password(binary(), binary()) -> false | [password()]. get_password(User, Server) -> - lists:foldl(fun (M, false) -> - M:get_password(User, Server); - (_M, Password) -> Password - end, - false, auth_modules(Server)). - --spec get_password_s(binary(), binary()) -> binary() | {binary(), binary(), binary(), integer()}. + {Passwords, _} = get_password_with_authmodule(User, Server), + Passwords. +-spec get_password_s(binary(), binary()) -> password(). get_password_s(User, Server) -> case get_password(User, Server) of false -> <<"">>; - Password -> Password + Passwords -> + {_, Pass} = lists:foldl( + fun(Plain, _) when is_binary(Plain) -> {true, Plain}; + (Pass, {false, _}) -> {true, Pass}; + (_, Acc) -> Acc + end, {false, <<"">>}, Passwords), + Pass end. -%% @doc Get the password of the user and the auth module. -%% @spec (User::string(), Server::string()) -> -%% {Password::string(), AuthModule::atom()} | {false, none} --spec get_password_with_authmodule(binary(), binary()) -> {false | binary(), atom()}. - +-spec get_password_with_authmodule(binary(), binary()) -> + {false | {false, atom(), binary()} | [password()], module()}. get_password_with_authmodule(User, Server) -> -%% Returns true if the user exists in the DB or if an anonymous user is logged -%% under the given name - lists:foldl(fun (M, {false, _}) -> - {M:get_password(User, Server), M}; - (_M, {Password, AuthModule}) -> {Password, AuthModule} - end, - {false, none}, auth_modules(Server)). - --spec is_user_exists(binary(), binary()) -> boolean(). - -is_user_exists(_User, <<"">>) -> - false; - -is_user_exists(User, Server) -> -%% Check if the user exists in all authentications module except the module -%% passed as parameter -%% @spec (Module::atom(), User, Server) -> true | false | maybe - lists:any(fun (M) -> - case M:is_user_exists(User, Server) of - {error, Error} -> - ?ERROR_MSG("The authentication module ~p returned " - "an error~nwhen checking user ~p in server " - "~p~nError message: ~p", - [M, User, Server, Error]), - false; - Else -> Else - end - end, - auth_modules(Server)). - --spec is_user_exists_in_other_modules(atom(), binary(), binary()) -> boolean() | maybe. - -is_user_exists_in_other_modules(Module, User, Server) -> - is_user_exists_in_other_modules_loop(auth_modules(Server) - -- [Module], - User, Server). - -is_user_exists_in_other_modules_loop([], _User, - _Server) -> - false; -is_user_exists_in_other_modules_loop([AuthModule - | AuthModules], - User, Server) -> - case AuthModule:is_user_exists(User, Server) of - true -> true; - false -> - is_user_exists_in_other_modules_loop(AuthModules, User, - Server); - {error, Error} -> - ?DEBUG("The authentication module ~p returned " - "an error~nwhen checking user ~p in server " - "~p~nError message: ~p", - [AuthModule, User, Server, Error]), - maybe + case validate_credentials(User, Server) of + {ok, LUser, LServer} -> + case get_is_banned(LUser, LServer) of + {is_banned, BanReason} -> + {{false, 'account-disabled', BanReason}, module_not_consulted}; + not_banned -> + case lists:foldl( + fun(M, {error, _}) -> + {db_get_password(LUser, LServer, M), M}; + (_M, Acc) -> + Acc + end, {error, undefined}, auth_modules(LServer)) of + {{ok, Password}, Module} when is_list(Password) -> + {Password, Module}; + {{ok, Password}, Module} -> + {[Password], Module}; + {error, Module} -> + {false, Module} + end + end; + _ -> + {false, undefined} end. +-spec user_exists(binary(), binary()) -> boolean(). +user_exists(_User, <<"">>) -> + false; +user_exists(User, Server) -> + case validate_credentials(User, Server) of + {ok, LUser, LServer} -> + {Exists, PerformExternalUserCheck} = + lists:foldl( + fun(M, {Exists0, PerformExternalUserCheck0}) -> + case db_user_exists(LUser, LServer, M) of + {{error, _}, Check} -> + {Exists0, PerformExternalUserCheck0 orelse Check}; + {Else, Check2} -> + {Exists0 orelse Else, PerformExternalUserCheck0 orelse Check2} + end + end, {false, false}, auth_modules(LServer)), + case (not Exists) andalso PerformExternalUserCheck andalso + ejabberd_option:auth_external_user_exists_check(Server) andalso + gen_mod:is_loaded(Server, mod_last) of + true -> + case mod_last:get_last_info(User, Server) of + not_found -> + false; + _ -> + true + end; + _ -> + Exists + end; + _ -> + false + end. + +-spec user_exists_in_other_modules(atom(), binary(), binary()) -> boolean() | maybe_exists. +user_exists_in_other_modules(Module, User, Server) -> + user_exists_in_other_modules_loop( + auth_modules(Server) -- [Module], User, Server). + +user_exists_in_other_modules_loop([], _User, _Server) -> + false; +user_exists_in_other_modules_loop([AuthModule | AuthModules], User, Server) -> + case db_user_exists(User, Server, AuthModule) of + {true, _} -> + true; + {false, _} -> + user_exists_in_other_modules_loop(AuthModules, User, Server); + {{error, _}, _} -> + maybe_exists + end. + +drop_password_type(LServer, Type) -> + Hash = case Type of + plain -> plain; + scram_sha1 -> sha; + scram_sha256 -> sha256; + scram_sha512 -> sha512 + end, + lists:foreach( + fun(M) -> + case erlang:function_exported(M, drop_password_type, 2) of + true -> + M:drop_password_type(LServer, Hash), + case use_cache(M, LServer) of + true -> + ets_cache:clear(cache_tab(M), + cache_nodes(M, LServer)); + false -> + ok + end; + _ -> + ok + end + end, auth_modules(LServer)). + +-spec which_users_exists(list({binary(), binary()})) -> list({binary(), binary()}). +which_users_exists(USPairs) -> + ByServer = lists:foldl( + 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. - -%% @spec (User, Server) -> ok -%% @doc Remove user. -%% Note: it may return ok even if there was some problem removing the user. remove_user(User, Server) -> - lists:foreach(fun (M) -> M:remove_user(User, Server) - end, - auth_modules(Server)), - ejabberd_hooks:run(remove_user, jid:nameprep(Server), - [User, Server]), - ok. - -%% @spec (User, Server, Password) -> ok | not_exists | not_allowed | bad_request | error -%% @doc Try to remove user if the provided password is correct. -%% The removal is attempted in each auth method provided: -%% when one returns 'ok' the loop stops; -%% if no method returns 'ok' then it returns the error message indicated by the last method attempted. --spec remove_user(binary(), binary(), binary()) -> any(). + case validate_credentials(User, Server) of + {ok, LUser, LServer} -> + lists:foreach( + fun(Mod) -> db_remove_user(LUser, LServer, Mod) end, + auth_modules(LServer)), + ejabberd_hooks:run(remove_user, LServer, [LUser, LServer]); + _Err -> + ok + end. +-spec remove_user(binary(), binary(), password()) -> ok | {error, atom()}. remove_user(User, Server, Password) -> - R = lists:foldl(fun (_M, ok = Res) -> Res; - (M, _) -> M:remove_user(User, Server, Password) - end, - error, auth_modules(Server)), - case R of - ok -> - ejabberd_hooks:run(remove_user, jid:nameprep(Server), - [User, Server]); - _ -> none - end, - R. + case validate_credentials(User, Server, Password) of + {ok, LUser, LServer} -> + case lists:foldl( + fun (_, ok) -> + ok; + (Mod, _) -> + case db_check_password( + LUser, <<"">>, LServer, Password, + <<"">>, undefined, Mod) of + true -> + db_remove_user(LUser, LServer, Mod); + {stop, true} -> + db_remove_user(LUser, LServer, Mod); + false -> + {error, not_allowed}; + {stop, false} -> + {error, not_allowed} + end + end, {error, not_allowed}, auth_modules(Server)) of + ok -> + ejabberd_hooks:run( + remove_user, LServer, [LUser, LServer]); + Err -> + Err + end; + Err -> + Err + end. -%% @spec (IOList) -> non_negative_float() %% @doc Calculate informational entropy. +-spec entropy(iodata()) -> float(). entropy(B) -> case binary_to_list(B) of "" -> 0.0; @@ -412,31 +655,449 @@ entropy(B) -> length(S) * math:log(lists:sum(Set)) / math:log(2) end. +-spec backend_type(atom()) -> atom(). +backend_type(Mod) -> + case atom_to_list(Mod) of + "ejabberd_auth_" ++ T -> list_to_atom(T); + _ -> Mod + end. + +-spec password_format(binary() | global) -> plain | scram. +password_format(LServer) -> + ejabberd_option:auth_password_format(LServer). + +get_is_banned(User, Server) -> + case mod_admin_extra:get_ban_details(User, Server) of + [] -> + not_banned; + BanDetails -> + {_, ReasonText} = lists:keyfind("reason", 1, BanDetails), + {is_banned, <<"Account is banned: ", ReasonText/binary>>} + end. + +%%%---------------------------------------------------------------------- +%%% Backend calls +%%%---------------------------------------------------------------------- +-spec db_try_register(binary(), binary(), binary(), [password()], module()) -> ok | {error, exists | db_failure | not_allowed}. +db_try_register(User, Server, PlainPassword, Passwords, Mod) -> + Ret = case erlang:function_exported(Mod, try_register_multiple, 3) of + true -> + case use_cache(Mod, Server) of + true -> + ets_cache:update( + cache_tab(Mod), {User, Server}, {ok, Passwords}, + fun() -> Mod:try_register_multiple(User, Server, Passwords) end, + cache_nodes(Mod, Server)); + false -> + ets_cache:untag(Mod:try_register_multiple(User, Server, Passwords)) + end; + _ -> + case erlang:function_exported(Mod, try_register, 3) of + true -> + case use_cache(Mod, Server) of + true -> + ets_cache:update( + cache_tab(Mod), {User, Server}, {ok, [PlainPassword]}, + fun() -> + case Mod:try_register(User, Server, PlainPassword) of + {Tag, {ok, Pass}} -> {Tag, {ok, [Pass]}}; + Other -> Other + end + end, cache_nodes(Mod, Server)); + false -> + case Mod:try_register(User, Server, PlainPassword) of + {_, {ok, Pass}} -> {ok, [Pass]}; + V -> ets_cache:untag(V) + end + end; + false -> + {error, not_allowed} + end + end, + case Ret of + {ok, _} -> ok; + {error, _} = Err -> Err + end. + +-spec db_set_password(binary(), binary(), binary(), [password()], module()) -> ok | {error, db_failure | not_allowed}. +db_set_password(User, Server, PlainPassword, Passwords, Mod) -> + Ret = case erlang:function_exported(Mod, set_password_multiple, 3) of + true -> + case use_cache(Mod, Server) of + true -> + ets_cache:update( + cache_tab(Mod), {User, Server}, {ok, Passwords}, + fun() -> Mod:set_password_multiple(User, Server, Passwords) end, + cache_nodes(Mod, Server)); + false -> + ets_cache:untag(Mod:set_password_multiple(User, Server, Passwords)) + end; + _ -> + case erlang:function_exported(Mod, set_password, 3) of + true -> + case use_cache(Mod, Server) of + true -> + ets_cache:update( + cache_tab(Mod), {User, Server}, {ok, [PlainPassword]}, + fun() -> + case Mod:set_password(User, Server, PlainPassword) of + {Tag, {ok, Pass}} -> {Tag, {ok, [Pass]}}; + Other -> Other + end + end, cache_nodes(Mod, Server)); + false -> + case Mod:set_password(User, Server, PlainPassword) of + {_, {ok, Pass}} -> {ok, [Pass]}; + V -> ets_cache:untag(V) + end + end; + false -> + {error, not_allowed} + end + end, + case Ret of + {ok, _} -> ejabberd_hooks:run(set_password, Server, [User, Server]); + {error, _} = Err -> Err + end. + +db_get_password(User, Server, Mod) -> + UseCache = use_cache(Mod, Server), + case erlang:function_exported(Mod, get_password, 2) of + false when UseCache -> + case ets_cache:lookup(cache_tab(Mod), {User, Server}) of + {ok, exists} -> error; + not_found -> error; + {ok, List} = V when is_list(List) -> V; + {ok, Single} -> {ok, [Single]}; + Other -> Other + end; + false -> + error; + true when UseCache -> + ets_cache:lookup( + cache_tab(Mod), {User, Server}, + fun() -> + case Mod:get_password(User, Server) of + {_, {ok, List}} = V when is_list(List) -> V; + {Tag, {ok, Single}} -> {Tag, {ok, [Single]}}; + Other -> Other + end + end); + true -> + case Mod:get_password(User, Server) of + {_, {ok, List}} when is_list(List) -> {ok, List}; + {_, {ok, Single}} -> {ok, [Single]}; + Other -> ets_cache:untag(Other) + end + end. + +db_user_exists(User, Server, Mod) -> + case db_get_password(User, Server, Mod) of + {ok, _} -> + {true, false}; + not_found -> + {false, false}; + error -> + case {Mod:store_type(Server), use_cache(Mod, Server)} of + {external, true} -> + Val = case ets_cache:lookup(cache_tab(Mod), {User, Server}, error) of + error -> + ets_cache:update(cache_tab(Mod), {User, Server}, {ok, exists}, + fun() -> + case Mod:user_exists(User, Server) of + {CacheTag, true} -> {CacheTag, {ok, exists}}; + {CacheTag, false} -> {CacheTag, not_found}; + {_, {error, _}} = Err -> Err + end + end); + Other -> + Other + end, + case Val of + {ok, _} -> + {true, Mod /= ejabberd_auth_anonymous}; + not_found -> + {false, Mod /= ejabberd_auth_anonymous}; + error -> + {false, Mod /= ejabberd_auth_anonymous}; + {error, _} = Err -> + {Err, Mod /= ejabberd_auth_anonymous} + end; + {external, false} -> + {ets_cache:untag(Mod:user_exists(User, Server)), Mod /= ejabberd_auth_anonymous}; + _ -> + {false, false} + end + end. + +db_check_password(User, AuthzId, Server, ProvidedPassword, + Digest, DigestFun, Mod) -> + case db_get_password(User, Server, Mod) of + {ok, ValidPasswords} -> + match_passwords(ProvidedPassword, ValidPasswords, Digest, DigestFun); + error -> + case {Mod:store_type(Server), use_cache(Mod, Server)} of + {external, true} -> + case ets_cache:update( + cache_tab(Mod), {User, Server}, {ok, ProvidedPassword}, + fun() -> + case Mod:check_password( + User, AuthzId, Server, ProvidedPassword) of + {CacheTag, true} -> {CacheTag, {ok, ProvidedPassword}}; + {CacheTag, {stop, true}} -> {CacheTag, {ok, ProvidedPassword}}; + {CacheTag, false} -> {CacheTag, error}; + {CacheTag, {stop, false}} -> {CacheTag, error} + end + end) of + {ok, _} -> + true; + error -> + false + end; + {external, false} -> + ets_cache:untag( + Mod:check_password(User, AuthzId, Server, ProvidedPassword)); + _ -> + false + end + end. + +db_remove_user(User, Server, Mod) -> + case erlang:function_exported(Mod, remove_user, 2) of + true -> + case Mod:remove_user(User, Server) of + ok -> + case use_cache(Mod, Server) of + true -> + ets_cache:delete(cache_tab(Mod), {User, Server}, + cache_nodes(Mod, Server)); + false -> + ok + end; + {error, _} = Err -> + Err + end; + false -> + {error, not_allowed} + end. + +db_get_users(Server, Opts, Mod) -> + case erlang:function_exported(Mod, get_users, 2) of + true -> + Mod:get_users(Server, Opts); + false -> + case use_cache(Mod, Server) of + true -> + ets_cache:fold( + fun({User, S}, {ok, _}, Users) when S == Server -> + [{User, Server}|Users]; + (_, _, Users) -> + Users + end, [], cache_tab(Mod)); + false -> + [] + end + end. + +db_count_users(Server, Opts, Mod) -> + case erlang:function_exported(Mod, count_users, 2) of + true -> + Mod:count_users(Server, Opts); + false -> + case use_cache(Mod, Server) of + true -> + ets_cache:fold( + fun({_, S}, {ok, _}, Num) when S == Server -> + Num + 1; + (_, _, Num) -> + Num + end, 0, cache_tab(Mod)); + false -> + 0 + end + end. + +%%%---------------------------------------------------------------------- +%%% SCRAM stuff +%%%---------------------------------------------------------------------- +is_password_scram_valid(Password, Scram) -> + case jid:resourceprep(Password) of + error -> + false; + _ -> + IterationCount = Scram#scram.iterationcount, + Hash = Scram#scram.hash, + Salt = base64:decode(Scram#scram.salt), + SaltedPassword = scram:salted_password(Hash, Password, Salt, IterationCount), + StoredKey = scram:stored_key(Hash, scram:client_key(Hash, SaltedPassword)), + base64:decode(Scram#scram.storedkey) == StoredKey + end. + +password_to_scram(Host, Password) -> + password_to_scram(Host, Password, ?SCRAM_DEFAULT_ITERATION_COUNT). + +password_to_scram(_Host, #scram{} = Password, _IterationCount) -> + Password; +password_to_scram(Host, Password, IterationCount) -> + password_to_scram(Host, Password, ejabberd_option:auth_scram_hash(Host), IterationCount). + +password_to_scram(_Host, Password, Hash, IterationCount) -> + Salt = p1_rand:bytes(?SALT_LENGTH), + SaltedPassword = scram:salted_password(Hash, Password, Salt, IterationCount), + StoredKey = scram:stored_key(Hash, scram:client_key(Hash, SaltedPassword)), + ServerKey = scram:server_key(Hash, SaltedPassword), + #scram{storedkey = base64:encode(StoredKey), + serverkey = base64:encode(ServerKey), + salt = base64:encode(Salt), + hash = Hash, + iterationcount = IterationCount}. + +%%%---------------------------------------------------------------------- +%%% Cache stuff +%%%---------------------------------------------------------------------- +-spec init_cache(host_modules()) -> ok. +init_cache(HostModules) -> + CacheOpts = cache_opts(), + {True, False} = use_cache(HostModules), + lists:foreach( + fun(Module) -> + ets_cache:new(cache_tab(Module), CacheOpts) + end, True), + lists:foreach( + fun(Module) -> + ets_cache:delete(cache_tab(Module)) + end, False). + +-spec cache_opts() -> [proplists:property()]. +cache_opts() -> + MaxSize = ejabberd_option:auth_cache_size(), + CacheMissed = ejabberd_option:auth_cache_missed(), + LifeTime = ejabberd_option:auth_cache_life_time(), + [{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}]. + +-spec use_cache(host_modules()) -> {True :: [module()], False :: [module()]}. +use_cache(HostModules) -> + {Enabled, Disabled} = + maps:fold( + fun(Host, Modules, Acc) -> + lists:foldl( + fun(Module, {True, False}) -> + case use_cache(Module, Host) of + true -> + {sets:add_element(Module, True), False}; + false -> + {True, sets:add_element(Module, False)} + end + end, Acc, Modules) + end, {sets:new(), sets:new()}, HostModules), + {sets:to_list(Enabled), sets:to_list(sets:subtract(Disabled, Enabled))}. + +-spec use_cache(module(), binary()) -> boolean(). +use_cache(Mod, LServer) -> + case erlang:function_exported(Mod, use_cache, 1) of + true -> Mod:use_cache(LServer); + false -> + ejabberd_option:auth_use_cache(LServer) + end. + +-spec cache_nodes(module(), binary()) -> [node()]. +cache_nodes(Mod, LServer) -> + case erlang:function_exported(Mod, cache_nodes, 1) of + true -> Mod:cache_nodes(LServer); + false -> ejabberd_cluster:get_nodes() + end. + +-spec cache_tab(module()) -> atom(). +cache_tab(Mod) -> + list_to_atom(atom_to_list(Mod) ++ "_cache"). + %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- -%% Return the lists of all the auth modules actually used in the -%% configuration +-spec auth_modules() -> [{binary(), module()}]. auth_modules() -> - lists:usort(lists:flatmap(fun (Server) -> - auth_modules(Server) - end, - ?MYHOSTS)). + lists:flatmap( + fun(Host) -> + [{Host, Mod} || Mod <- auth_modules(Host)] + end, ejabberd_option:hosts()). --spec auth_modules(binary()) -> [atom()]. - -%% Return the list of authenticated modules for a given host +-spec auth_modules(binary()) -> [module()]. auth_modules(Server) -> LServer = jid:nameprep(Server), - Default = ejabberd_config:default_db(LServer, ?MODULE), - Methods = ejabberd_config:get_option( - {auth_method, LServer}, opt_type(auth_method), [Default]), - [jlib:binary_to_atom(<<"ejabberd_auth_", - (jlib:atom_to_binary(M))/binary>>) + Methods = ejabberd_option:auth_method(LServer), + [ejabberd:module_name([<<"auth">>, + misc:atom_to_binary(M)]) || M <- Methods]. -export(Server) -> - ejabberd_auth_mnesia:export(Server). +-spec match_passwords(password(), [password()], + binary(), digest_fun() | undefined) -> boolean(). +match_passwords(Provided, Passwords, Digest, DigestFun) -> + lists:any( + fun(Pass) -> + match_password(Provided, Pass, Digest, DigestFun) + end, Passwords). + +-spec match_password(password(), password(), + binary(), digest_fun() | undefined) -> boolean(). +match_password(Password, #scram{} = Scram, <<"">>, undefined) -> + is_password_scram_valid(Password, Scram); +match_password(Password, #scram{} = Scram, Digest, DigestFun) -> + StoredKey = base64:decode(Scram#scram.storedkey), + DigRes = if Digest /= <<"">> -> + Digest == DigestFun(StoredKey); + true -> false + end, + if DigRes -> + true; + true -> + StoredKey == Password andalso Password /= <<"">> + end; +match_password(ProvidedPassword, ValidPassword, <<"">>, undefined) -> + ProvidedPassword == ValidPassword andalso ProvidedPassword /= <<"">>; +match_password(ProvidedPassword, ValidPassword, Digest, DigestFun) -> + DigRes = if Digest /= <<"">> -> + Digest == DigestFun(ValidPassword); + true -> false + end, + if DigRes -> + true; + true -> + ValidPassword == ProvidedPassword andalso ProvidedPassword /= <<"">> + end. + +-spec validate_credentials(binary(), binary()) -> + {ok, binary(), binary()} | {error, invalid_jid}. +validate_credentials(User, Server) -> + validate_credentials(User, Server, #scram{}). + +-spec validate_credentials(binary(), binary(), password()) -> + {ok, binary(), binary()} | {error, invalid_jid | invalid_password}. +validate_credentials(_User, _Server, <<"">>) -> + {error, invalid_password}; +validate_credentials(User, Server, Password) -> + case jid:nodeprep(User) of + error -> + {error, invalid_jid}; + LUser -> + case jid:nameprep(Server) of + error -> + {error, invalid_jid}; + LServer -> + if is_record(Password, scram) -> + {ok, LUser, LServer}; + true -> + case jid:resourceprep(Password) of + error -> + {error, invalid_password}; + _ -> + {ok, LUser, LServer} + end + end + end + end. + +untag_stop({stop, Val}) -> Val; +untag_stop(Val) -> Val. import_info() -> [{<<"users">>, 3}]. @@ -448,14 +1109,26 @@ import_start(_LServer, _) -> import(Server, {sql, _}, mnesia, <<"users">>, Fields) -> ejabberd_auth_mnesia:import(Server, Fields); -import(Server, {sql, _}, riak, <<"users">>, Fields) -> - ejabberd_auth_riak:import(Server, Fields); import(_LServer, {sql, _}, sql, <<"users">>, _) -> ok. -opt_type(auth_method) -> - fun (V) when is_list(V) -> - lists:map(fun(M) -> ejabberd_config:v_db(?MODULE, M) end, V); - (V) -> [ejabberd_config:v_db(?MODULE, V)] - end; -opt_type(_) -> [auth_method]. +-spec convert_to_scram(binary()) -> {error, any()} | ok. +convert_to_scram(Server) -> + LServer = jid:nameprep(Server), + if + LServer == error; + LServer == <<>> -> + {error, {incorrect_server_name, Server}}; + true -> + lists:foreach( + fun({U, S}) -> + case get_password(U, S) of + [Pass] when is_binary(Pass) -> + SPass = password_to_scram(Server, Pass), + set_password(U, S, SPass); + _ -> + ok + end + end, get_users(LServer)), + ok + end. diff --git a/src/ejabberd_auth_anonymous.erl b/src/ejabberd_auth_anonymous.erl index c84321ad9..8f67b695b 100644 --- a/src/ejabberd_auth_anonymous.erl +++ b/src/ejabberd_auth_anonymous.erl @@ -5,7 +5,7 @@ %%% Created : 17 Feb 2006 by Mickael Remond %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -25,10 +25,14 @@ -module(ejabberd_auth_anonymous). --behaviour(ejabberd_config). +-behaviour(ejabberd_auth). -author('mickael.remond@process-one.net'). +-protocol({xep, 175, '1.2', '1.1.0', "complete", ""}). + -export([start/1, + stop/1, + use_cache/1, allow_anonymous/1, is_sasl_anonymous_enabled/1, is_login_anonymous_enabled/1, @@ -38,37 +42,29 @@ unregister_connection/3 ]). --export([login/2, set_password/3, check_password/4, - check_password/6, try_register/3, - dirty_get_registered_users/0, get_vh_registered_users/1, - get_vh_registered_users/2, - get_vh_registered_users_number/1, - get_vh_registered_users_number/2, get_password_s/2, - get_password/2, get_password/3, is_user_exists/2, - remove_user/2, remove_user/3, store_type/0, - plain_password_required/0, opt_type/1]). +-export([login/2, check_password/4, user_exists/2, + get_users/2, count_users/2, store_type/1, + plain_password_required/1]). --include("ejabberd.hrl"). -include("logger.hrl"). --include("jid.hrl"). - -%% Create the anonymous table if at least one virtual host has anonymous features enabled -%% Register to login / logout events --record(anonymous, {us = {<<"">>, <<"">>} :: {binary(), binary()}, - sid = ejabberd_sm:make_sid() :: ejabberd_sm:sid()}). +-include_lib("xmpp/include/jid.hrl"). start(Host) -> - %% TODO: Check cluster mode - mnesia:create_table(anonymous, [{ram_copies, [node()]}, - {type, bag}, - {attributes, record_info(fields, anonymous)}]), - %% The hooks are needed to add / remove users from the anonymous tables ejabberd_hooks:add(sm_register_connection_hook, Host, ?MODULE, register_connection, 100), ejabberd_hooks:add(sm_remove_connection_hook, Host, ?MODULE, unregister_connection, 100), ok. +stop(Host) -> + ejabberd_hooks:delete(sm_register_connection_hook, Host, + ?MODULE, register_connection, 100), + ejabberd_hooks:delete(sm_remove_connection_hook, Host, + ?MODULE, unregister_connection, 100). + +use_cache(_) -> + false. + %% Return true if anonymous is allowed for host or false otherwise allow_anonymous(Host) -> lists:member(?MODULE, ejabberd_auth:auth_modules(Host)). @@ -103,95 +99,65 @@ is_login_anonymous_enabled(Host) -> %% Return the anonymous protocol to use: sasl_anon|login_anon|both %% defaults to login_anon anonymous_protocol(Host) -> - ejabberd_config:get_option( - {anonymous_protocol, Host}, - fun(sasl_anon) -> sasl_anon; - (login_anon) -> login_anon; - (both) -> both - end, - sasl_anon). + ejabberd_option:anonymous_protocol(Host). %% Return true if multiple connections have been allowed in the config file %% defaults to false allow_multiple_connections(Host) -> - ejabberd_config:get_option( - {allow_multiple_connections, Host}, - fun(V) when is_boolean(V) -> V end, - false). + ejabberd_option:allow_multiple_connections(Host). -%% Check if user exist in the anonymus database anonymous_user_exist(User, Server) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - US = {LUser, LServer}, - case catch mnesia:dirty_read({anonymous, US}) of - [] -> - false; - [_H|_T] -> - true - end. - -%% Remove connection from Mnesia tables -remove_connection(SID, LUser, LServer) -> - US = {LUser, LServer}, - F = fun () -> mnesia:delete_object({anonymous, US, SID}) - end, - mnesia:transaction(F). + lists:any( + fun({_LResource, Info}) -> + proplists:get_value(auth_module, Info) == ?MODULE + end, ejabberd_sm:get_user_info(User, Server)). %% Register connection -spec register_connection(ejabberd_sm:sid(), jid(), ejabberd_sm:info()) -> ok. -register_connection(SID, - #jid{luser = LUser, lserver = LServer}, Info) -> - AuthModule = proplists:get_value(auth_module, Info, undefined), - case AuthModule == (?MODULE) of - true -> - ejabberd_hooks:run(register_user, LServer, - [LUser, LServer]), - US = {LUser, LServer}, - mnesia:sync_dirty(fun () -> - mnesia:write(#anonymous{us = US, - sid = SID}) - end); - false -> ok +register_connection(_SID, + #jid{luser = LUser, lserver = LServer, lresource = LResource}, Info) -> + case proplists:get_value(auth_module, Info) of + ?MODULE -> + % Register user only if we are first resource + case ejabberd_sm:get_user_resources(LUser, LServer) of + [LResource] -> + ejabberd_hooks:run(register_user, LServer, [LUser, LServer]); + _ -> + ok + end; + _ -> + ok end. %% Remove an anonymous user from the anonymous users table -spec unregister_connection(ejabberd_sm:sid(), jid(), ejabberd_sm:info()) -> any(). -unregister_connection(SID, - #jid{luser = LUser, lserver = LServer}, _) -> - purge_hook(anonymous_user_exist(LUser, LServer), LUser, - LServer), - remove_connection(SID, LUser, LServer). - -%% Launch the hook to purge user data only for anonymous users -purge_hook(false, _LUser, _LServer) -> - ok; -purge_hook(true, LUser, LServer) -> - ejabberd_hooks:run(anonymous_purge_hook, LServer, - [LUser, LServer]). +unregister_connection(_SID, + #jid{luser = LUser, lserver = LServer}, Info) -> + case proplists:get_value(auth_module, Info) of + ?MODULE -> + % Remove user data only if there is no more resources around + case ejabberd_sm:get_user_resources(LUser, LServer) of + [] -> + ejabberd_hooks:run(remove_user, LServer, [LUser, LServer]); + _ -> + ok + end; + _ -> + ok + end. %% --------------------------------- %% Specific anonymous auth functions %% --------------------------------- - -%% When anonymous login is enabled, check the password for permenant users -%% before allowing access -check_password(User, AuthzId, Server, Password) -> - check_password(User, AuthzId, Server, Password, undefined, - undefined). - -check_password(User, _AuthzId, Server, _Password, _Digest, - _DigestGen) -> - case - ejabberd_auth:is_user_exists_in_other_modules(?MODULE, - User, Server) - of - %% If user exists in other module, reject anonnymous authentication - true -> false; - %% If we are not sure whether the user exists in other module, reject anon auth - maybe -> false; - false -> login(User, Server) - end. +check_password(User, _AuthzId, Server, _Password) -> + {nocache, + case ejabberd_auth:user_exists_in_other_modules(?MODULE, User, Server) of + %% If user exists in other module, reject anonnymous authentication + true -> false; + %% If we are not sure whether the user exists in other module, reject anon auth + maybe_exists -> false; + false -> login(User, Server) + end}. login(User, Server) -> case is_login_anonymous_enabled(Server) of @@ -207,77 +173,17 @@ login(User, Server) -> end end. -%% When anonymous login is enabled, check that the user is permanent before -%% changing its password -set_password(User, Server, _Password) -> - case anonymous_user_exist(User, Server) of - true -> ok; - false -> {error, not_allowed} - end. +get_users(Server, _) -> + [{U, S} || {U, S, _R} <- ejabberd_sm:get_vh_session_list(Server)]. -%% When anonymous login is enabled, check if permanent users are allowed on -%% the server: -try_register(_User, _Server, _Password) -> - {error, not_allowed}. +count_users(Server, Opts) -> + length(get_users(Server, Opts)). -dirty_get_registered_users() -> []. +user_exists(User, Server) -> + {nocache, anonymous_user_exist(User, Server)}. -get_vh_registered_users(Server) -> - [{U, S} - || {U, S, _R} - <- ejabberd_sm:get_vh_session_list(Server)]. +plain_password_required(_) -> + false. -get_vh_registered_users(Server, _) -> - get_vh_registered_users(Server). - -get_vh_registered_users_number(Server) -> - length(get_vh_registered_users(Server)). - -get_vh_registered_users_number(Server, _) -> - get_vh_registered_users_number(Server). - -%% Return password of permanent user or false for anonymous users -get_password(User, Server) -> - get_password(User, Server, <<"">>). - -get_password(User, Server, DefaultValue) -> - case anonymous_user_exist(User, Server) or - login(User, Server) - of - %% We return the default value if the user is anonymous - true -> DefaultValue; - %% We return the permanent user password otherwise - false -> false - end. - -get_password_s(User, Server) -> - case get_password(User, Server) of - false -> - <<"">>; - Password -> - Password - end. - -%% Returns true if the user exists in the DB or if an anonymous user is logged -%% under the given name -is_user_exists(User, Server) -> - anonymous_user_exist(User, Server). - -remove_user(_User, _Server) -> {error, not_allowed}. - -remove_user(_User, _Server, _Password) -> not_allowed. - -plain_password_required() -> false. - -store_type() -> - plain. - -opt_type(allow_multiple_connections) -> - fun (V) when is_boolean(V) -> V end; -opt_type(anonymous_protocol) -> - fun (sasl_anon) -> sasl_anon; - (login_anon) -> login_anon; - (both) -> both - end; -opt_type(_) -> - [allow_multiple_connections, anonymous_protocol]. +store_type(_) -> + external. diff --git a/src/ejabberd_auth_external.erl b/src/ejabberd_auth_external.erl index bba65af1c..1b69a9a10 100644 --- a/src/ejabberd_auth_external.erl +++ b/src/ejabberd_auth_external.erl @@ -1,11 +1,11 @@ %%%---------------------------------------------------------------------- %%% File : ejabberd_auth_external.erl %%% Author : Alexey Shchepin -%%% Purpose : Authentification via LDAP external script +%%% Purpose : Authentication via LDAP external script %%% Created : 12 Dec 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -25,304 +25,81 @@ -module(ejabberd_auth_external). --behaviour(ejabberd_config). - -author('alexey@process-one.net'). -behaviour(ejabberd_auth). --export([start/1, set_password/3, check_password/4, - check_password/6, try_register/3, - dirty_get_registered_users/0, get_vh_registered_users/1, - get_vh_registered_users/2, - get_vh_registered_users_number/1, - get_vh_registered_users_number/2, get_password/2, - get_password_s/2, is_user_exists/2, remove_user/2, - remove_user/3, store_type/0, plain_password_required/0, - opt_type/1]). +-export([start/1, stop/1, reload/1, set_password/3, check_password/4, + try_register/3, user_exists/2, remove_user/2, + store_type/1, plain_password_required/1]). --include("ejabberd.hrl"). -include("logger.hrl"). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start(Host) -> - Cmd = ejabberd_config:get_option( - {extauth_program, Host}, - fun(V) -> - binary_to_list(iolist_to_binary(V)) - end, - "extauth"), - extauth:start(Host, Cmd), - check_cache_last_options(Host), - ejabberd_auth_mnesia:start(Host). + extauth:start(Host). -check_cache_last_options(Server) -> - case get_cache_option(Server) of - false -> no_cache; - {true, _CacheTime} -> - case get_mod_last_configured(Server) of - no_mod_last -> - ?ERROR_MSG("In host ~p extauth is used, extauth_cache " - "is enabled but mod_last is not enabled.", - [Server]), - no_cache; - _ -> cache - end - end. +stop(Host) -> + extauth:stop(Host). -plain_password_required() -> true. +reload(Host) -> + extauth:reload(Host). -store_type() -> external. +plain_password_required(_) -> true. + +store_type(_) -> external. check_password(User, AuthzId, Server, Password) -> if AuthzId /= <<>> andalso AuthzId /= User -> - false; + {nocache, false}; true -> - case get_cache_option(Server) of - false -> - check_password_extauth(User, AuthzId, Server, Password); - {true, CacheTime} -> - check_password_cache(User, AuthzId, Server, Password, - CacheTime) - end + check_password_extauth(User, AuthzId, Server, Password) end. -check_password(User, AuthzId, Server, Password, _Digest, - _DigestGen) -> - check_password(User, AuthzId, Server, Password). - set_password(User, Server, Password) -> case extauth:set_password(User, Server, Password) of - true -> - set_password_mnesia(User, Server, Password), ok; - _ -> {error, unknown_problem} + Res when is_boolean(Res) -> {cache, {ok, Password}}; + {error, Reason} -> failure(User, Server, set_password, Reason) end. try_register(User, Server, Password) -> - case get_cache_option(Server) of - false -> try_register_extauth(User, Server, Password); - {true, _CacheTime} -> - try_register_external_cache(User, Server, Password) + case extauth:try_register(User, Server, Password) of + true -> {cache, {ok, Password}}; + false -> {cache, {error, not_allowed}}; + {error, Reason} -> failure(User, Server, try_register, Reason) end. -dirty_get_registered_users() -> - ejabberd_auth_mnesia:dirty_get_registered_users(). - -get_vh_registered_users(Server) -> - ejabberd_auth_mnesia:get_vh_registered_users(Server). - -get_vh_registered_users(Server, Data) -> - ejabberd_auth_mnesia:get_vh_registered_users(Server, - Data). - -get_vh_registered_users_number(Server) -> - ejabberd_auth_mnesia:get_vh_registered_users_number(Server). - -get_vh_registered_users_number(Server, Data) -> - ejabberd_auth_mnesia:get_vh_registered_users_number(Server, - Data). - -%% The password can only be returned if cache is enabled, cached info exists and is fresh enough. -get_password(User, Server) -> - case get_cache_option(Server) of - false -> false; - {true, CacheTime} -> - get_password_cache(User, Server, CacheTime) - end. - -get_password_s(User, Server) -> - case get_password(User, Server) of - false -> <<"">>; - Other -> Other - end. - -%% @spec (User, Server) -> true | false | {error, Error} -is_user_exists(User, Server) -> - try extauth:is_user_exists(User, Server) of - Res -> Res - catch - _:Error -> {error, Error} +user_exists(User, Server) -> + case extauth:user_exists(User, Server) of + Res when is_boolean(Res) -> {cache, Res}; + {error, Reason} -> failure(User, Server, user_exists, Reason) end. remove_user(User, Server) -> case extauth:remove_user(User, Server) of - false -> false; - true -> - case get_cache_option(Server) of - false -> false; - {true, _CacheTime} -> - ejabberd_auth_mnesia:remove_user(User, Server) - end + false -> {error, not_allowed}; + true -> ok; + {error, Reason} -> + {_, Err} = failure(User, Server, remove_user, Reason), + Err end. -remove_user(User, Server, Password) -> - case extauth:remove_user(User, Server, Password) of - false -> false; - true -> - case get_cache_option(Server) of - false -> false; - {true, _CacheTime} -> - ejabberd_auth_mnesia:remove_user(User, Server, - Password) - end - end. - -%%% -%%% Extauth cache management -%%% - -%% @spec (Host::string()) -> false | {true, CacheTime::integer()} -get_cache_option(Host) -> - case ejabberd_config:get_option( - {extauth_cache, Host}, - fun(false) -> undefined; - (I) when is_integer(I), I >= 0 -> I - end) of - undefined -> false; - CacheTime -> {true, CacheTime} - end. - -%% @spec (User, AuthzId, Server, Password) -> true | false check_password_extauth(User, _AuthzId, Server, Password) -> - extauth:check_password(User, Server, Password) andalso - Password /= <<"">>. - -%% @spec (User, Server, Password) -> true | false -try_register_extauth(User, Server, Password) -> - extauth:try_register(User, Server, Password). - -check_password_cache(User, AuthzId, Server, Password, 0) -> - check_password_external_cache(User, AuthzId, Server, Password); -check_password_cache(User, AuthzId, Server, Password, - CacheTime) -> - case get_last_access(User, Server) of - online -> - check_password_mnesia(User, AuthzId, Server, Password); - never -> - check_password_external_cache(User, AuthzId, Server, Password); - mod_last_required -> - ?ERROR_MSG("extauth is used, extauth_cache is enabled " - "but mod_last is not enabled in that " - "host", - []), - check_password_external_cache(User, AuthzId, Server, Password); - TimeStamp -> - case is_fresh_enough(TimeStamp, CacheTime) of - %% If no need to refresh, check password against Mnesia - true -> - case check_password_mnesia(User, AuthzId, Server, Password) of - %% If password valid in Mnesia, accept it - true -> true; - %% Else (password nonvalid in Mnesia), check in extauth and cache result - false -> - check_password_external_cache(User, AuthzId, Server, Password) - end; - %% Else (need to refresh), check in extauth and cache result - false -> - check_password_external_cache(User, AuthzId, Server, Password) - end + if Password /= <<"">> -> + case extauth:check_password(User, Server, Password) of + Res when is_boolean(Res) -> {cache, Res}; + {error, Reason} -> + {Tag, _} = failure(User, Server, check_password, Reason), + {Tag, false} + end; + true -> + {nocache, false} end. -get_password_mnesia(User, Server) -> - ejabberd_auth_mnesia:get_password(User, Server). - --spec get_password_cache(User::binary(), Server::binary(), CacheTime::integer()) -> Password::string() | false. -get_password_cache(User, Server, CacheTime) -> - case get_last_access(User, Server) of - online -> get_password_mnesia(User, Server); - never -> false; - mod_last_required -> - ?ERROR_MSG("extauth is used, extauth_cache is enabled " - "but mod_last is not enabled in that " - "host", - []), - false; - TimeStamp -> - case is_fresh_enough(TimeStamp, CacheTime) of - true -> get_password_mnesia(User, Server); - false -> false - end - end. - -%% Check the password using extauth; if success then cache it -check_password_external_cache(User, AuthzId, Server, Password) -> - case check_password_extauth(User, AuthzId, Server, Password) of - true -> - set_password_mnesia(User, Server, Password), true; - false -> false - end. - -%% Try to register using extauth; if success then cache it -try_register_external_cache(User, Server, Password) -> - case try_register_extauth(User, Server, Password) of - {atomic, ok} = R -> - set_password_mnesia(User, Server, Password), R; - _ -> {error, not_allowed} - end. - -%% @spec (User, AuthzId, Server, Password) -> true | false -check_password_mnesia(User, AuthzId, Server, Password) -> - ejabberd_auth_mnesia:check_password(User, AuthzId, Server, - Password). - -%% @spec (User, Server, Password) -> ok | {error, invalid_jid} -set_password_mnesia(User, Server, Password) -> -%% @spec (TimeLast, CacheTime) -> true | false -%% TimeLast = online | never | integer() -%% CacheTime = integer() | false - ejabberd_auth_mnesia:set_password(User, Server, - Password). - -is_fresh_enough(TimeStampLast, CacheTime) -> - Now = p1_time_compat:system_time(seconds), - TimeStampLast + CacheTime > Now. - -%% Code copied from mod_configure.erl -%% Code copied from web/ejabberd_web_admin.erl -%% TODO: Update time format to XEP-0202: Entity Time --spec(get_last_access(User::binary(), Server::binary()) -> (online | never | mod_last_required | integer())). -get_last_access(User, Server) -> - case ejabberd_sm:get_user_resources(User, Server) of - [] -> - case get_last_info(User, Server) of - mod_last_required -> mod_last_required; - not_found -> never; - {ok, Timestamp, _Status} -> Timestamp - end; - _ -> online - end. -%% @spec (User, Server) -> {ok, Timestamp, Status} | not_found | mod_last_required - -get_last_info(User, Server) -> - case get_mod_last_enabled(Server) of - mod_last -> mod_last:get_last_info(User, Server); - no_mod_last -> mod_last_required - end. - -%% @spec (Server) -> mod_last | no_mod_last -get_mod_last_enabled(Server) -> - case gen_mod:is_loaded(Server, mod_last) of - true -> mod_last; - false -> no_mod_last - end. - -get_mod_last_configured(Server) -> - case is_configured(Server, mod_last) of - true -> mod_last; - false -> no_mod_last - end. - -is_configured(Host, Module) -> - Os = ejabberd_config:get_local_option({modules, Host}, - fun(M) when is_list(M) -> M end), - lists:keymember(Module, 1, Os). - -opt_type(extauth_cache) -> - fun (false) -> undefined; - (I) when is_integer(I), I >= 0 -> I - end; -opt_type(extauth_program) -> - fun (V) -> binary_to_list(iolist_to_binary(V)) end; -opt_type(_) -> [extauth_cache, extauth_program]. +-spec failure(binary(), binary(), atom(), any()) -> {nocache, {error, db_failure}}. +failure(User, Server, Fun, Reason) -> + ?ERROR_MSG("External authentication program failed when calling " + "'~ts' for ~ts@~ts: ~p", [Fun, User, Server, Reason]), + {nocache, {error, db_failure}}. diff --git a/src/ejabberd_auth_jwt.erl b/src/ejabberd_auth_jwt.erl new file mode 100644 index 000000000..7fac3e4f7 --- /dev/null +++ b/src/ejabberd_auth_jwt.erl @@ -0,0 +1,154 @@ +%%%---------------------------------------------------------------------- +%%% File : ejabberd_auth_jwt.erl +%%% Author : Mickael Remond +%%% Purpose : Authentication using JWT tokens +%%% Created : 16 Mar 2019 by Mickael Remond +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + +-module(ejabberd_auth_jwt). + +-author('mremond@process-one.net'). + +-behaviour(ejabberd_auth). + +-export([start/1, stop/1, check_password/4, + store_type/1, plain_password_required/1, + user_exists/2, use_cache/1 + ]). +%% 'ejabberd_hooks' callback: +-export([check_decoded_jwt/5]). + +-include_lib("xmpp/include/xmpp.hrl"). +-include("logger.hrl"). + +%%%---------------------------------------------------------------------- +%%% API +%%%---------------------------------------------------------------------- +start(Host) -> + %% We add our default JWT verifier with hook priority 100. + %% So if you need to check or verify your custom JWT before the + %% default verifier, It's better to use this hook with priority + %% little than 100 and return bool() or {stop, bool()} in your own + %% callback function. + ejabberd_hooks:add(check_decoded_jwt, Host, ?MODULE, check_decoded_jwt, 100), + case ejabberd_option:jwt_key(Host) of + undefined -> + ?ERROR_MSG("Option jwt_key is not configured for ~ts: " + "JWT authentication won't work", [Host]); + _ -> + ok + end. + +stop(Host) -> + ejabberd_hooks:delete(check_decoded_jwt, Host, ?MODULE, check_decoded_jwt, 100). + +plain_password_required(_Host) -> true. + +store_type(_Host) -> external. + +-spec check_password(binary(), binary(), binary(), binary()) -> {ets_cache:tag(), boolean() | {stop, boolean()}}. +check_password(User, AuthzId, Server, Token) -> + %% MREMOND: Should we move the AuthzId check at a higher level in + %% the call stack? + if AuthzId /= <<>> andalso AuthzId /= User -> + {nocache, false}; + true -> + if Token == <<"">> -> {nocache, false}; + true -> + Res = check_jwt_token(User, Server, Token), + Rule = ejabberd_option:jwt_auth_only_rule(Server), + case acl:match_rule(Server, Rule, + jid:make(User, Server, <<"">>)) of + deny -> + {nocache, Res}; + allow -> + {nocache, {stop, Res}} + end + end + end. + +user_exists(User, Host) -> + %% Checking that the user has an active session + %% If the session was negociated by the JWT auth method then we define that the user exists + %% Any other cases will return that the user doesn't exist + {nocache, case ejabberd_sm:get_user_info(User, Host) of + [{_, Info}] -> proplists:get_value(auth_module, Info) == ejabberd_auth_jwt; + _ -> false + end}. + +use_cache(_) -> + false. + +%%%---------------------------------------------------------------------- +%%% 'ejabberd_hooks' callback +%%%---------------------------------------------------------------------- +check_decoded_jwt(true, Fields, _Signature, Server, User) -> + JidField = ejabberd_option:jwt_jid_field(Server), + case maps:find(JidField, Fields) of + {ok, SJid} when is_binary(SJid) -> + try + JID = jid:decode(SJid), + JID#jid.luser == User andalso JID#jid.lserver == Server + catch error:{bad_jid, _} -> + false + end; + _ -> % error | {ok, _UnknownType} + false + end; +check_decoded_jwt(Acc, _, _, _, _) -> + Acc. + +%%%---------------------------------------------------------------------- +%%% Internal functions +%%%---------------------------------------------------------------------- +check_jwt_token(User, Server, Token) -> + JWK = ejabberd_option:jwt_key(Server), + try jose_jwt:verify(JWK, Token) of + {true, {jose_jwt, Fields}, Signature} -> + Now = erlang:system_time(second), + ?DEBUG("jwt verify at system timestamp ~p: ~p - ~p~n", [Now, Fields, Signature]), + case maps:find(<<"exp">>, Fields) of + error -> + %% No expiry in token => We consider token invalid: + false; + {ok, Exp} -> + if + Exp > Now -> + ejabberd_hooks:run_fold( + check_decoded_jwt, + Server, + true, + [Fields, Signature, Server, User] + ); + true -> + %% return false, if token has expired + false + end + end; + {false, _, _} -> + false + catch + A:B -> + ?DEBUG("jose_jwt:verify failed ~n for account ~p@~p~n " + " JWK and token: ~p~n with error: ~p", + [User, Server, {JWK, Token}, {A, B}]), + false + end. diff --git a/src/ejabberd_auth_ldap.erl b/src/ejabberd_auth_ldap.erl index 3cb04b596..091e567a8 100644 --- a/src/ejabberd_auth_ldap.erl +++ b/src/ejabberd_auth_ldap.erl @@ -1,11 +1,11 @@ %%%---------------------------------------------------------------------- %%% File : ejabberd_auth_ldap.erl %%% Author : Alexey Shchepin -%%% Purpose : Authentification via LDAP +%%% Purpose : Authentication via LDAP %%% Created : 12 Dec 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -25,8 +25,6 @@ -module(ejabberd_auth_ldap). --behaviour(ejabberd_config). - -author('alexey@process-one.net'). -behaviour(gen_server). @@ -37,16 +35,11 @@ handle_cast/2, terminate/2, code_change/3]). -export([start/1, stop/1, start_link/1, set_password/3, - check_password/4, check_password/6, try_register/3, - dirty_get_registered_users/0, get_vh_registered_users/1, - get_vh_registered_users/2, - get_vh_registered_users_number/1, - get_vh_registered_users_number/2, get_password/2, - get_password_s/2, is_user_exists/2, remove_user/2, - remove_user/3, store_type/0, plain_password_required/0, - opt_type/1]). + check_password/4, user_exists/2, + get_users/2, count_users/2, + store_type/1, plain_password_required/1, + reload/1]). --include("ejabberd.hrl"). -include("logger.hrl"). -include("eldap.hrl"). @@ -65,16 +58,19 @@ uids = [] :: [{binary()} | {binary(), binary()}], ufilter = <<"">> :: binary(), sfilter = <<"">> :: binary(), - lfilter :: {any(), any()}, deref_aliases = never :: never | searching | finding | always, - dn_filter :: binary(), + dn_filter :: binary() | undefined, dn_filter_attrs = [] :: [binary()]}). -handle_cast(_Request, State) -> {noreply, State}. +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), + {noreply, State}. code_change(_OldVsn, State, _Extra) -> {ok, State}. -handle_info(_Info, State) -> {noreply, State}. +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), + {noreply, State}. -define(LDAP_SEARCH_TIMEOUT, 5). @@ -86,13 +82,14 @@ start(Host) -> Proc = gen_mod:get_module_proc(Host, ?MODULE), ChildSpec = {Proc, {?MODULE, start_link, [Host]}, transient, 1000, worker, [?MODULE]}, - supervisor:start_child(ejabberd_sup, ChildSpec). + supervisor:start_child(ejabberd_backend_sup, ChildSpec). stop(Host) -> Proc = gen_mod:get_module_proc(Host, ?MODULE), - gen_server:call(Proc, stop), - supervisor:terminate_child(ejabberd_sup, Proc), - supervisor:delete_child(ejabberd_sup, Proc). + case supervisor:terminate_child(ejabberd_backend_sup, Proc) of + ok -> supervisor:delete_child(ejabberd_backend_sup, Proc); + Err -> Err + end. start_link(Host) -> Proc = gen_mod:get_module_proc(Host, ?MODULE), @@ -101,6 +98,7 @@ start_link(Host) -> terminate(_Reason, _State) -> ok. init(Host) -> + process_flag(trap_exit, true), State = parse_options(Host), eldap_pool:start_link(State#state.eldap_id, State#state.servers, State#state.backups, @@ -112,77 +110,53 @@ init(Host) -> State#state.password, State#state.tls_options), {ok, State}. -plain_password_required() -> true. +reload(Host) -> + stop(Host), + start(Host). -store_type() -> external. +plain_password_required(_) -> true. + +store_type(_) -> external. check_password(User, AuthzId, Server, Password) -> if AuthzId /= <<>> andalso AuthzId /= User -> - false; + {nocache, false}; + Password == <<"">> -> + {nocache, false}; true -> - if Password == <<"">> -> false; - true -> - case catch check_password_ldap(User, Server, Password) of - {'EXIT', _} -> false; - Result -> Result - end + case catch check_password_ldap(User, Server, Password) of + {'EXIT', _} -> {nocache, false}; + Result -> {cache, Result} end end. -check_password(User, AuthzId, Server, Password, _Digest, - _DigestGen) -> - check_password(User, AuthzId, Server, Password). - set_password(User, Server, Password) -> {ok, State} = eldap_utils:get_state(Server, ?MODULE), case find_user_dn(User, State) of - false -> {error, user_not_found}; + false -> {cache, {error, db_failure}}; DN -> - eldap_pool:modify_passwd(State#state.eldap_id, DN, - Password) + case eldap_pool:modify_passwd(State#state.eldap_id, DN, + Password) of + ok -> {cache, {ok, Password}}; + _Err -> {nocache, {error, db_failure}} + end end. -%% @spec (User, Server, Password) -> {error, not_allowed} -try_register(_User, _Server, _Password) -> - {error, not_allowed}. - -dirty_get_registered_users() -> - Servers = ejabberd_config:get_vh_by_auth_method(ldap), - lists:flatmap(fun (Server) -> - get_vh_registered_users(Server) - end, - Servers). - -get_vh_registered_users(Server) -> - case catch get_vh_registered_users_ldap(Server) of +get_users(Server, []) -> + case catch get_users_ldap(Server) of {'EXIT', _} -> []; Result -> Result end. -get_vh_registered_users(Server, _) -> - get_vh_registered_users(Server). +count_users(Server, Opts) -> + length(get_users(Server, Opts)). -get_vh_registered_users_number(Server) -> - length(get_vh_registered_users(Server)). - -get_vh_registered_users_number(Server, _) -> - get_vh_registered_users_number(Server). - -get_password(_User, _Server) -> false. - -get_password_s(_User, _Server) -> <<"">>. - -%% @spec (User, Server) -> true | false | {error, Error} -is_user_exists(User, Server) -> - case catch is_user_exists_ldap(User, Server) of - {'EXIT', Error} -> {error, Error}; - Result -> Result +user_exists(User, Server) -> + case catch user_exists_ldap(User, Server) of + {'EXIT', _Error} -> {nocache, {error, db_failure}}; + Result -> {cache, Result} end. -remove_user(_User, _Server) -> {error, not_allowed}. - -remove_user(_User, _Server, _Password) -> not_allowed. - %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- @@ -199,7 +173,7 @@ check_password_ldap(User, Server, Password) -> end end. -get_vh_registered_users_ldap(Server) -> +get_users_ldap(Server) -> {ok, State} = eldap_utils:get_state(Server, ?MODULE), UIDs = State#state.uids, Eldap_ID = State#state.eldap_id, @@ -248,7 +222,7 @@ get_vh_registered_users_ldap(Server) -> _ -> [] end. -is_user_exists_ldap(User, Server) -> +user_exists_ldap(User, Server) -> {ok, State} = eldap_utils:get_state(Server, ?MODULE), case find_user_dn(User, State) of false -> false; @@ -259,8 +233,9 @@ handle_call(get_state, _From, State) -> {reply, {ok, State}, State}; handle_call(stop, _From, State) -> {stop, normal, ok, State}; -handle_call(_Request, _From, State) -> - {reply, bad_request, State}. +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), + {noreply, State}. find_user_dn(User, State) -> ResAttrs = result_attrs(State), @@ -277,19 +252,12 @@ find_user_dn(User, State) -> [#eldap_entry{attributes = Attrs, object_name = DN} | _]} -> - dn_filter(DN, Attrs, State); + is_valid_dn(DN, Attrs, State); _ -> false end; _ -> false end. -%% apply the dn filter and the local filter: -dn_filter(DN, Attrs, State) -> - case check_local_filter(Attrs, State) of - false -> false; - true -> is_valid_dn(DN, Attrs, State) - end. - %% Check that the DN is valid, based on the dn filter is_valid_dn(DN, _, #state{dn_filter = undefined}) -> DN; is_valid_dn(DN, Attrs, State) -> @@ -325,30 +293,6 @@ is_valid_dn(DN, Attrs, State) -> _ -> false end. -%% The local filter is used to check an attribute in ejabberd -%% and not in LDAP to limit the load on the LDAP directory. -%% A local rule can be either: -%% {equal, {"accountStatus",["active"]}} -%% {notequal, {"accountStatus",["disabled"]}} -%% {ldap_local_filter, {notequal, {"accountStatus",["disabled"]}}} -check_local_filter(_Attrs, - #state{lfilter = undefined}) -> - true; -check_local_filter(Attrs, - #state{lfilter = LocalFilter}) -> - {Operation, FilterMatch} = LocalFilter, - local_filter(Operation, Attrs, FilterMatch). - -local_filter(equal, Attrs, FilterMatch) -> - {Attr, Value} = FilterMatch, - case lists:keysearch(Attr, 1, Attrs) of - false -> false; - {value, {Attr, Value}} -> true; - _ -> false - end; -local_filter(notequal, Attrs, FilterMatch) -> - not local_filter(equal, Attrs, FilterMatch). - result_attrs(#state{uids = UIDs, dn_filter_attrs = DNFilterAttrs}) -> lists:foldl(fun ({UID}, Acc) -> [UID | Acc]; @@ -360,50 +304,21 @@ result_attrs(#state{uids = UIDs, %%% Auxiliary functions %%%---------------------------------------------------------------------- parse_options(Host) -> - Cfg = eldap_utils:get_config(Host, []), - Eldap_ID = jlib:atom_to_binary(gen_mod:get_module_proc(Host, ?MODULE)), - Bind_Eldap_ID = jlib:atom_to_binary( + Cfg = ?eldap_config(ejabberd_option, Host), + Eldap_ID = misc:atom_to_binary(gen_mod:get_module_proc(Host, ?MODULE)), + Bind_Eldap_ID = misc:atom_to_binary( gen_mod:get_module_proc(Host, bind_ejabberd_auth_ldap)), - UIDsTemp = gen_mod:get_opt( - {ldap_uids, Host}, [], - fun(Us) -> - lists:map( - fun({U, P}) -> - {iolist_to_binary(U), - iolist_to_binary(P)}; - ({U}) -> - {iolist_to_binary(U)}; - (U) -> - {iolist_to_binary(U)} - end, lists:flatten(Us)) - end, [{<<"uid">>, <<"%u">>}]), + UIDsTemp = ejabberd_option:ldap_uids(Host), UIDs = eldap_utils:uids_domain_subst(Host, UIDsTemp), SubFilter = eldap_utils:generate_subfilter(UIDs), - UserFilter = case gen_mod:get_opt( - {ldap_filter, Host}, [], - fun check_filter/1, <<"">>) of + UserFilter = case ejabberd_option:ldap_filter(Host) of <<"">> -> SubFilter; F -> <<"(&", SubFilter/binary, F/binary, ")">> end, - SearchFilter = eldap_filter:do_sub(UserFilter, - [{<<"%u">>, <<"*">>}]), - {DNFilter, DNFilterAttrs} = - gen_mod:get_opt({ldap_dn_filter, Host}, [], - fun([{DNF, DNFA}]) -> - NewDNFA = case DNFA of - undefined -> - []; - _ -> - [iolist_to_binary(A) - || A <- DNFA] - end, - NewDNF = check_filter(DNF), - {NewDNF, NewDNFA} - end, {undefined, []}), - LocalFilter = gen_mod:get_opt( - {ldap_local_filter, Host}, [], fun(V) -> V end), + SearchFilter = eldap_filter:do_sub(UserFilter, [{<<"%u">>, <<"*">>}]), + {DNFilter, DNFilterAttrs} = ejabberd_option:ldap_dn_filter(Host), #state{host = Host, eldap_id = Eldap_ID, bind_eldap_id = Bind_Eldap_ID, servers = Cfg#eldap_config.servers, @@ -415,34 +330,5 @@ parse_options(Host) -> base = Cfg#eldap_config.base, deref_aliases = Cfg#eldap_config.deref_aliases, uids = UIDs, ufilter = UserFilter, - sfilter = SearchFilter, lfilter = LocalFilter, + sfilter = SearchFilter, dn_filter = DNFilter, dn_filter_attrs = DNFilterAttrs}. - -check_filter(F) -> - NewF = iolist_to_binary(F), - {ok, _} = eldap_filter:parse(NewF), - NewF. - -opt_type(ldap_dn_filter) -> - fun ([{DNF, DNFA}]) -> - NewDNFA = case DNFA of - undefined -> []; - _ -> [iolist_to_binary(A) || A <- DNFA] - end, - NewDNF = check_filter(DNF), - {NewDNF, NewDNFA} - end; -opt_type(ldap_filter) -> fun check_filter/1; -opt_type(ldap_local_filter) -> fun (V) -> V end; -opt_type(ldap_uids) -> - fun (Us) -> - lists:map(fun ({U, P}) -> - {iolist_to_binary(U), iolist_to_binary(P)}; - ({U}) -> {iolist_to_binary(U)}; - (U) -> {iolist_to_binary(U)} - end, - lists:flatten(Us)) - end; -opt_type(_) -> - [ldap_dn_filter, ldap_filter, ldap_local_filter, - ldap_uids]. diff --git a/src/ejabberd_auth_mnesia.erl b/src/ejabberd_auth_mnesia.erl index dee3774db..996dd620f 100644 --- a/src/ejabberd_auth_mnesia.erl +++ b/src/ejabberd_auth_mnesia.erl @@ -1,11 +1,11 @@ %%%---------------------------------------------------------------------- %%% File : ejabberd_auth_mnesia.erl %%% Author : Alexey Shchepin -%%% Purpose : Authentification via mnesia +%%% Purpose : Authentication via mnesia %%% Created : 12 Dec 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -25,56 +25,45 @@ -module(ejabberd_auth_mnesia). --compile([{parse_transform, ejabberd_sql_pt}]). - --behaviour(ejabberd_config). - -author('alexey@process-one.net'). -behaviour(ejabberd_auth). --export([start/1, set_password/3, check_password/4, - check_password/6, try_register/3, - dirty_get_registered_users/0, get_vh_registered_users/1, - get_vh_registered_users/2, init_db/0, - get_vh_registered_users_number/1, - get_vh_registered_users_number/2, get_password/2, - get_password_s/2, is_user_exists/2, remove_user/2, - remove_user/3, store_type/0, export/1, import/2, - plain_password_required/0, opt_type/1]). +-export([start/1, stop/1, set_password_multiple/3, try_register_multiple/3, + get_users/2, init_db/0, + count_users/2, get_password/2, + remove_user/2, store_type/1, import/2, + plain_password_required/1, use_cache/1, drop_password_type/2, set_password_instance/3]). +-export([need_transform/1, transform/1]). --include("ejabberd.hrl"). -include("logger.hrl"). --include("ejabberd_sql_pt.hrl"). - --record(passwd, {us = {<<"">>, <<"">>} :: {binary(), binary()} | '$1', - password = <<"">> :: binary() | scram() | '_'}). +-include_lib("xmpp/include/scram.hrl"). +-include("ejabberd_auth.hrl"). -record(reg_users_counter, {vhost = <<"">> :: binary(), count = 0 :: integer() | '$1'}). --define(SALT_LENGTH, 16). - %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start(Host) -> init_db(), - update_table(), update_reg_users_counter_table(Host), - maybe_alert_password_scrammed_without_option(), + ok. + +stop(_Host) -> ok. init_db() -> - mnesia:create_table(passwd, - [{disc_copies, [node()]}, + ejabberd_mnesia:create(?MODULE, passwd, + [{disc_only_copies, [node()]}, {attributes, record_info(fields, passwd)}]), - mnesia:create_table(reg_users_counter, + ejabberd_mnesia:create(?MODULE, reg_users_counter, [{ram_copies, [node()]}, {attributes, record_info(fields, reg_users_counter)}]). update_reg_users_counter_table(Server) -> - Set = get_vh_registered_users(Server), + Set = get_users(Server, []), Size = length(Set), LServer = jid:nameprep(Server), F = fun () -> @@ -83,422 +72,226 @@ update_reg_users_counter_table(Server) -> end, mnesia:sync_dirty(F). -plain_password_required() -> - is_scrammed(). - -store_type() -> - case is_scrammed() of - false -> plain; %% allows: PLAIN DIGEST-MD5 SCRAM - true -> scram %% allows: PLAIN SCRAM +use_cache(Host) -> + case mnesia:table_info(passwd, storage_type) of + disc_only_copies -> + ejabberd_option:auth_use_cache(Host); + _ -> + false end. -check_password(User, AuthzId, Server, Password) -> - if AuthzId /= <<>> andalso AuthzId /= User -> - false; - true -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - US = {LUser, LServer}, - case catch mnesia:dirty_read({passwd, US}) of - [#passwd{password = Password}] when is_binary(Password) -> - Password /= <<"">>; - [#passwd{password = Scram}] when is_record(Scram, scram) -> - is_password_scram_valid(Password, Scram); - _ -> false - end - end. +plain_password_required(Server) -> + store_type(Server) == scram. -check_password(User, AuthzId, Server, Password, Digest, - DigestGen) -> - if AuthzId /= <<>> andalso AuthzId /= User -> - false; - true -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - US = {LUser, LServer}, - case catch mnesia:dirty_read({passwd, US}) of - [#passwd{password = Passwd}] when is_binary(Passwd) -> - DigRes = if Digest /= <<"">> -> - Digest == DigestGen(Passwd); - true -> false - end, - if DigRes -> true; - true -> (Passwd == Password) and (Password /= <<"">>) - end; - [#passwd{password = Scram}] when is_record(Scram, scram) -> - Passwd = jlib:decode_base64(Scram#scram.storedkey), - DigRes = if Digest /= <<"">> -> - Digest == DigestGen(Passwd); - true -> false - end, - if DigRes -> true; - true -> (Passwd == Password) and (Password /= <<"">>) - end; - _ -> false - end - end. +store_type(Server) -> + ejabberd_auth:password_format(Server). -%% @spec (User::string(), Server::string(), Password::string()) -> -%% ok | {error, invalid_jid} -set_password(User, Server, Password) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - US = {LUser, LServer}, - if (LUser == error) or (LServer == error) -> - {error, invalid_jid}; - true -> - F = fun () -> - Password2 = case is_scrammed() and is_binary(Password) - of - true -> password_to_scram(Password); - false -> Password - end, - mnesia:write(#passwd{us = US, password = Password2}) - end, - {atomic, ok} = mnesia:transaction(F), - ok - end. - -%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, invalid_jid} | {error, not_allowed} | {error, Reason} -try_register(User, Server, PasswordList) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - Password = if is_list(PasswordList); is_binary(PasswordList) -> - iolist_to_binary(PasswordList); - true -> PasswordList - end, - US = {LUser, LServer}, - if (LUser == error) or (LServer == error) -> - {error, invalid_jid}; - true -> - F = fun () -> - case mnesia:read({passwd, US}) of - [] -> - Password2 = case is_scrammed() and - is_binary(Password) - of - true -> password_to_scram(Password); - false -> Password - end, - mnesia:write(#passwd{us = US, - password = Password2}), - mnesia:dirty_update_counter(reg_users_counter, - LServer, 1), - ok; - [_E] -> exists - end - end, - mnesia:transaction(F) - end. - -%% Get all registered users in Mnesia -dirty_get_registered_users() -> - mnesia:dirty_all_keys(passwd). - -get_vh_registered_users(Server) -> - LServer = jid:nameprep(Server), - mnesia:dirty_select(passwd, - [{#passwd{us = '$1', _ = '_'}, - [{'==', {element, 2, '$1'}, LServer}], ['$1']}]). - -get_vh_registered_users(Server, - [{from, Start}, {to, End}]) - when is_integer(Start) and is_integer(End) -> - get_vh_registered_users(Server, - [{limit, End - Start + 1}, {offset, Start}]); -get_vh_registered_users(Server, - [{limit, Limit}, {offset, Offset}]) - when is_integer(Limit) and is_integer(Offset) -> - case get_vh_registered_users(Server) of - [] -> []; - Users -> - Set = lists:keysort(1, Users), - L = length(Set), - Start = if Offset < 1 -> 1; - Offset > L -> L; - true -> Offset - end, - lists:sublist(Set, Start, Limit) - end; -get_vh_registered_users(Server, [{prefix, Prefix}]) - when is_binary(Prefix) -> - Set = [{U, S} - || {U, S} <- get_vh_registered_users(Server), - str:prefix(Prefix, U)], - lists:keysort(1, Set); -get_vh_registered_users(Server, - [{prefix, Prefix}, {from, Start}, {to, End}]) - when is_binary(Prefix) and is_integer(Start) and - is_integer(End) -> - get_vh_registered_users(Server, - [{prefix, Prefix}, {limit, End - Start + 1}, - {offset, Start}]); -get_vh_registered_users(Server, - [{prefix, Prefix}, {limit, Limit}, {offset, Offset}]) - when is_binary(Prefix) and is_integer(Limit) and - is_integer(Offset) -> - case [{U, S} - || {U, S} <- get_vh_registered_users(Server), - str:prefix(Prefix, U)] - of - [] -> []; - Users -> - Set = lists:keysort(1, Users), - L = length(Set), - Start = if Offset < 1 -> 1; - Offset > L -> L; - true -> Offset - end, - lists:sublist(Set, Start, Limit) - end; -get_vh_registered_users(Server, _) -> - get_vh_registered_users(Server). - -get_vh_registered_users_number(Server) -> - LServer = jid:nameprep(Server), - Query = mnesia:dirty_select(reg_users_counter, - [{#reg_users_counter{vhost = LServer, - count = '$1'}, - [], ['$1']}]), - case Query of - [Count] -> Count; - _ -> 0 - end. - -get_vh_registered_users_number(Server, - [{prefix, Prefix}]) - when is_binary(Prefix) -> - Set = [{U, S} - || {U, S} <- get_vh_registered_users(Server), - str:prefix(Prefix, U)], - length(Set); -get_vh_registered_users_number(Server, _) -> - get_vh_registered_users_number(Server). - -get_password(User, Server) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - US = {LUser, LServer}, - case catch mnesia:dirty_read(passwd, US) of - [#passwd{password = Password}] - when is_binary(Password) -> - Password; - [#passwd{password = Scram}] - when is_record(Scram, scram) -> - {jlib:decode_base64(Scram#scram.storedkey), - jlib:decode_base64(Scram#scram.serverkey), - jlib:decode_base64(Scram#scram.salt), - Scram#scram.iterationcount}; - _ -> false - end. - -get_password_s(User, Server) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - US = {LUser, LServer}, - case catch mnesia:dirty_read(passwd, US) of - [#passwd{password = Password}] - when is_binary(Password) -> - Password; - [#passwd{password = Scram}] - when is_record(Scram, scram) -> - <<"">>; - _ -> <<"">> - end. - -%% @spec (User, Server) -> true | false | {error, Error} -is_user_exists(User, Server) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - US = {LUser, LServer}, - case catch mnesia:dirty_read({passwd, US}) of - [] -> false; - [_] -> true; - Other -> {error, Other} - end. - -%% @spec (User, Server) -> ok -%% @doc Remove user. -%% Note: it returns ok even if there was some problem removing the user. -remove_user(User, Server) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - US = {LUser, LServer}, - F = fun () -> - mnesia:delete({passwd, US}), - mnesia:dirty_update_counter(reg_users_counter, LServer, - -1) - end, - mnesia:transaction(F), - ok. - -%% @spec (User, Server, Password) -> ok | not_exists | not_allowed | bad_request -%% @doc Remove user if the provided password is correct. -remove_user(User, Server, Password) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - US = {LUser, LServer}, - F = fun () -> - case mnesia:read({passwd, US}) of - [#passwd{password = Password}] - when is_binary(Password) -> - mnesia:delete({passwd, US}), - mnesia:dirty_update_counter(reg_users_counter, LServer, - -1), - ok; - [#passwd{password = Scram}] - when is_record(Scram, scram) -> - case is_password_scram_valid(Password, Scram) of - true -> - mnesia:delete({passwd, US}), - mnesia:dirty_update_counter(reg_users_counter, - LServer, -1), - ok; - false -> not_allowed - end; - _ -> not_exists - end +set_password_multiple(User, Server, Passwords) -> + F = fun() -> + lists:foreach( + fun(#scram{hash = Hash} = Password) -> + mnesia:write(#passwd{us = {User, Server, Hash}, password = Password}); + (Plain) -> + mnesia:write(#passwd{us = {User, Server, plain}, password = Plain}) + end, Passwords) end, case mnesia:transaction(F) of - {atomic, ok} -> ok; - {atomic, Res} -> Res; - _ -> bad_request + {atomic, ok} -> + {cache, {ok, Passwords}}; + {aborted, Reason} -> + ?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]), + {nocache, {error, db_failure}} end. -update_table() -> - Fields = record_info(fields, passwd), - case mnesia:table_info(passwd, attributes) of - Fields -> - convert_to_binary(Fields), - maybe_scram_passwords(), - ok; - _ -> - ?INFO_MSG("Recreating passwd table", []), - mnesia:transform_table(passwd, ignore, Fields) +set_password_instance(User, Server, Password) -> + F = fun() -> + case Password of + #scram{hash = Hash} = Password -> + mnesia:write(#passwd{us = {User, Server, Hash}, password = Password}); + Plain -> + mnesia:write(#passwd{us = {User, Server, plain}, password = Plain}) + end + end, + case mnesia:transaction(F) of + {atomic, ok} -> + ok; + {aborted, Reason} -> + ?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]), + {error, db_failure} end. -convert_to_binary(Fields) -> - ejabberd_config:convert_table_to_binary( - passwd, Fields, set, - fun(#passwd{us = {U, _}}) -> U end, - fun(#passwd{us = {U, S}, password = Pass} = R) -> - NewUS = {iolist_to_binary(U), iolist_to_binary(S)}, - NewPass = case Pass of - #scram{storedkey = StoredKey, - serverkey = ServerKey, - salt = Salt} -> - Pass#scram{ - storedkey = iolist_to_binary(StoredKey), - serverkey = iolist_to_binary(ServerKey), - salt = iolist_to_binary(Salt)}; - _ -> - iolist_to_binary(Pass) - end, - R#passwd{us = NewUS, password = NewPass} - end). - -%%% -%%% SCRAM -%%% - -%% The passwords are stored scrammed in the table either if the option says so, -%% or if at least the first password is scrammed. -is_scrammed() -> - OptionScram = is_option_scram(), - FirstElement = mnesia:dirty_read(passwd, - mnesia:dirty_first(passwd)), - case {OptionScram, FirstElement} of - {true, _} -> true; - {false, [#passwd{password = Scram}]} - when is_record(Scram, scram) -> - true; - _ -> false +try_register_multiple(User, Server, Passwords) -> + F = fun() -> + case mnesia:select(passwd, [{{'_', {'$1', '$2', '_'}, '$3'}, + [{'==', '$1', User}, + {'==', '$2', Server}], + ['$3']}]) of + [] -> + lists:foreach( + fun(#scram{hash = Hash} = Password) -> + mnesia:write(#passwd{us = {User, Server, Hash}, password = Password}); + (Plain) -> + mnesia:write(#passwd{us = {User, Server, plain}, password = Plain}) + end, Passwords), + mnesia:dirty_update_counter(reg_users_counter, Server, 1), + {ok, Passwords}; + [_] -> + {error, exists} + end + end, + case mnesia:transaction(F) of + {atomic, Res} -> + {cache, Res}; + {aborted, Reason} -> + ?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]), + {nocache, {error, db_failure}} end. -is_option_scram() -> - scram == - ejabberd_config:get_option({auth_password_format, ?MYNAME}, - fun(V) -> V end). +get_users(Server, []) -> + Users = mnesia:dirty_select(passwd, + [{#passwd{us = '$1', _ = '_'}, + [{'==', {element, 2, '$1'}, Server}], ['$1']}]), + misc:lists_uniq([{U, S} || {U, S, _} <- Users]); +get_users(Server, [{from, Start}, {to, End}]) + when is_integer(Start) and is_integer(End) -> + get_users(Server, [{limit, End - Start + 1}, {offset, Start}]); +get_users(Server, [{limit, Limit}, {offset, Offset}]) + when is_integer(Limit) and is_integer(Offset) -> + case get_users(Server, []) of + [] -> + []; + Users -> + Set = lists:keysort(1, Users), + L = length(Set), + Start = if Offset < 1 -> 1; + Offset > L -> L; + true -> Offset + end, + lists:sublist(Set, Start, Limit) + end; +get_users(Server, [{prefix, Prefix}]) when is_binary(Prefix) -> + Set = [{U, S} || {U, S} <- get_users(Server, []), str:prefix(Prefix, U)], + lists:keysort(1, Set); +get_users(Server, [{prefix, Prefix}, {from, Start}, {to, End}]) + when is_binary(Prefix) and is_integer(Start) and is_integer(End) -> + get_users(Server, [{prefix, Prefix}, {limit, End - Start + 1}, + {offset, Start}]); +get_users(Server, [{prefix, Prefix}, {limit, Limit}, {offset, Offset}]) + when is_binary(Prefix) and is_integer(Limit) and is_integer(Offset) -> + case [{U, S} || {U, S} <- get_users(Server, []), str:prefix(Prefix, U)] of + [] -> + []; + Users -> + Set = lists:keysort(1, Users), + L = length(Set), + Start = if Offset < 1 -> 1; + Offset > L -> L; + true -> Offset + end, + lists:sublist(Set, Start, Limit) + end; +get_users(Server, _) -> + get_users(Server, []). -maybe_alert_password_scrammed_without_option() -> - case is_scrammed() andalso not is_option_scram() of - true -> - ?ERROR_MSG("Some passwords were stored in the database " - "as SCRAM, but 'auth_password_format' " - "is not configured 'scram'. The option " - "will now be considered to be 'scram'.", - []); - false -> ok +count_users(Server, []) -> + case mnesia:dirty_select( + reg_users_counter, + [{#reg_users_counter{vhost = Server, count = '$1'}, + [], ['$1']}]) of + [Count] -> Count; + _ -> 0 + end; +count_users(Server, [{prefix, Prefix}]) when is_binary(Prefix) -> + Set = [{U, S} || {U, S} <- get_users(Server, []), str:prefix(Prefix, U)], + length(Set); +count_users(Server, _) -> + count_users(Server, []). + +get_password(User, Server) -> + case mnesia:dirty_select(passwd, [{{'_', {'$1', '$2', '_'}, '$3'}, + [{'==', '$1', User}, + {'==', '$2', Server}], + ['$3']}]) of + [_|_] = List -> + List2 = lists:map( + fun({scram, SK, SEK, Salt, IC}) -> + #scram{storedkey = SK, serverkey = SEK, + salt = Salt, hash = sha, iterationcount = IC}; + (Other) -> Other + end, List), + {cache, {ok, List2}}; + _ -> + {cache, error} end. -maybe_scram_passwords() -> - case is_scrammed() of - true -> scram_passwords(); - false -> ok +drop_password_type(Server, Hash) -> + F = fun() -> + Keys = mnesia:select(passwd, [{{'_', '$1', '_'}, + [{'==', {element, 3, '$1'}, Hash}, + {'==', {element, 2, '$1'}, Server}], + ['$1']}]), + lists:foreach(fun(Key) -> mnesia:delete({passwd, Key}) end, Keys), + ok + end, + case mnesia:transaction(F) of + {atomic, ok} -> + ok; + {aborted, Reason} -> + ?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]), + {error, db_failure} end. -scram_passwords() -> - ?INFO_MSG("Converting the stored passwords into " - "SCRAM bits", - []), - Fun = fun (#passwd{password = Password} = P) -> - Scram = password_to_scram(Password), - P#passwd{password = Scram} - end, - Fields = record_info(fields, passwd), - mnesia:transform_table(passwd, Fun, Fields). +remove_user(User, Server) -> + F = fun () -> + Keys = mnesia:select(passwd, [{{'_', '$1', '_'}, + [{'==', {element, 1, '$1'}, User}, + {'==', {element, 2, '$1'}, Server}], + ['$1']}]), + lists:foreach(fun(Key) -> mnesia:delete({passwd, Key}) end, Keys), + mnesia:dirty_update_counter(reg_users_counter, Server, -1), + ok + end, + case mnesia:transaction(F) of + {atomic, ok} -> + ok; + {aborted, Reason} -> + ?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]), + {error, db_failure} + end. -password_to_scram(Password) -> - password_to_scram(Password, - ?SCRAM_DEFAULT_ITERATION_COUNT). +need_transform(#reg_users_counter{}) -> + false; +need_transform({passwd, {_U, _S, _T}, _Pass}) -> + false; +need_transform({passwd, {_U, _S}, _Pass}) -> + true. -password_to_scram(Password, IterationCount) -> - Salt = randoms:bytes(?SALT_LENGTH), - SaltedPassword = scram:salted_password(Password, Salt, - IterationCount), - StoredKey = - scram:stored_key(scram:client_key(SaltedPassword)), - ServerKey = scram:server_key(SaltedPassword), - #scram{storedkey = jlib:encode_base64(StoredKey), - serverkey = jlib:encode_base64(ServerKey), - salt = jlib:encode_base64(Salt), - iterationcount = IterationCount}. - -is_password_scram_valid(Password, Scram) -> - IterationCount = Scram#scram.iterationcount, - Salt = jlib:decode_base64(Scram#scram.salt), - SaltedPassword = scram:salted_password(Password, Salt, - IterationCount), - StoredKey = - scram:stored_key(scram:client_key(SaltedPassword)), - jlib:decode_base64(Scram#scram.storedkey) == StoredKey. - -export(_Server) -> - [{passwd, - fun(Host, #passwd{us = {LUser, LServer}, password = Password}) - when LServer == Host, - is_binary(Password) -> - [?SQL("delete from users where username=%(LUser)s;"), - ?SQL("insert into users(username, password) " - "values (%(LUser)s, %(Password)s);")]; - (Host, #passwd{us = {LUser, LServer}, password = #scram{} = Scram}) - when LServer == Host -> - StoredKey = Scram#scram.storedkey, - ServerKey = Scram#scram.serverkey, - Salt = Scram#scram.salt, - IterationCount = Scram#scram.iterationcount, - [?SQL("delete from users where username=%(LUser)s;"), - ?SQL("insert into users(username, password, serverkey, salt, " - "iterationcount) " - "values (%(LUser)s, %(StoredKey)s, %(ServerKey)s," - " %(Salt)s, %(IterationCount)d);")]; - (_Host, _R) -> - [] - end}]. +transform({passwd, {U, S}, Pass}) + when is_list(U) orelse is_list(S) orelse is_list(Pass) -> + NewUS = {iolist_to_binary(U), iolist_to_binary(S)}, + NewPass = case Pass of + #scram{storedkey = StoredKey, + serverkey = ServerKey, + salt = Salt} -> + Pass#scram{ + storedkey = iolist_to_binary(StoredKey), + serverkey = iolist_to_binary(ServerKey), + salt = iolist_to_binary(Salt)}; + _ -> + iolist_to_binary(Pass) + end, + transform(#passwd{us = NewUS, password = NewPass}); +transform(#passwd{us = {U, S}, password = Password} = P) + when is_binary(Password) -> + P#passwd{us = {U, S, plain}, password = Password}; +transform({passwd, {U, S}, {scram, SK, SEK, Salt, IC}}) -> + #passwd{us = {U, S, sha}, + password = #scram{storedkey = SK, serverkey = SEK, + salt = Salt, hash = sha, iterationcount = IC}}; +transform(#passwd{us = {U, S}, password = #scram{hash = Hash}} = P) -> + P#passwd{us = {U, S, Hash}}; +transform(Other) -> Other. import(LServer, [LUser, Password, _TimeStamp]) -> mnesia:dirty_write( #passwd{us = {LUser, LServer}, password = Password}). - -opt_type(auth_password_format) -> fun (V) -> V end; -opt_type(_) -> [auth_password_format]. diff --git a/src/ejabberd_auth_pam.erl b/src/ejabberd_auth_pam.erl index fa4b9f078..d795b0d6f 100644 --- a/src/ejabberd_auth_pam.erl +++ b/src/ejabberd_auth_pam.erl @@ -5,7 +5,7 @@ %%% Created : 5 Jul 2007 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -24,107 +24,56 @@ %%%------------------------------------------------------------------- -module(ejabberd_auth_pam). --behaviour(ejabberd_config). - -author('xram@jabber.ru'). -behaviour(ejabberd_auth). --export([start/1, set_password/3, check_password/4, - check_password/6, try_register/3, - dirty_get_registered_users/0, get_vh_registered_users/1, - get_vh_registered_users/2, - get_vh_registered_users_number/1, - get_vh_registered_users_number/2, get_password/2, - get_password_s/2, is_user_exists/2, remove_user/2, - remove_user/3, store_type/0, plain_password_required/0, - opt_type/1]). +-export([start/1, stop/1, check_password/4, + user_exists/2, store_type/1, plain_password_required/1]). start(_Host) -> - ejabberd:start_app(p1_pam). + ejabberd:start_app(epam). -set_password(_User, _Server, _Password) -> - {error, not_allowed}. - -check_password(User, AuthzId, Server, Password, _Digest, - _DigestGen) -> - check_password(User, AuthzId, Server, Password). +stop(_Host) -> + ok. check_password(User, AuthzId, Host, Password) -> if AuthzId /= <<>> andalso AuthzId /= User -> - false; - true -> - Service = get_pam_service(Host), - UserInfo = case get_pam_userinfotype(Host) of - username -> User; - jid -> <> - end, - case catch epam:authenticate(Service, UserInfo, - Password) - of - true -> true; - _ -> false - end + false; + true -> + Service = get_pam_service(Host), + UserInfo = case get_pam_userinfotype(Host) of + username -> User; + jid -> <> + end, + case catch epam:authenticate(Service, UserInfo, Password) of + true -> {cache, true}; + false -> {cache, false}; + _ -> {nocache, false} + end end. -try_register(_User, _Server, _Password) -> - {error, not_allowed}. - -dirty_get_registered_users() -> []. - -get_vh_registered_users(_Host) -> []. - -get_vh_registered_users(_Host, _) -> []. - -get_vh_registered_users_number(_Host) -> 0. - -get_vh_registered_users_number(_Host, _) -> 0. - -get_password(_User, _Server) -> false. - -get_password_s(_User, _Server) -> <<"">>. - -%% @spec (User, Server) -> true | false | {error, Error} -%% TODO: Improve this function to return an error instead of 'false' when connection to PAM failed -is_user_exists(User, Host) -> +user_exists(User, Host) -> Service = get_pam_service(Host), UserInfo = case get_pam_userinfotype(Host) of username -> User; jid -> <> end, case catch epam:acct_mgmt(Service, UserInfo) of - true -> true; - _ -> false + true -> {cache, true}; + false -> {cache, false}; + _Err -> {nocache, {error, db_failure}} end. -remove_user(_User, _Server) -> {error, not_allowed}. +plain_password_required(_) -> true. -remove_user(_User, _Server, _Password) -> not_allowed. - -plain_password_required() -> true. - -store_type() -> external. +store_type(_) -> external. %%==================================================================== %% Internal functions %%==================================================================== get_pam_service(Host) -> - ejabberd_config:get_option( - {pam_service, Host}, - fun iolist_to_binary/1, - <<"ejabberd">>). + ejabberd_option:pam_service(Host). get_pam_userinfotype(Host) -> - ejabberd_config:get_option( - {pam_userinfotype, Host}, - fun(username) -> username; - (jid) -> jid - end, - username). - -opt_type(pam_service) -> fun iolist_to_binary/1; -opt_type(pam_userinfotype) -> - fun (username) -> username; - (jid) -> jid - end; -opt_type(_) -> [pam_service, pam_userinfotype]. + ejabberd_option:pam_userinfotype(Host). diff --git a/src/ejabberd_auth_riak.erl b/src/ejabberd_auth_riak.erl deleted file mode 100644 index 51571c4e2..000000000 --- a/src/ejabberd_auth_riak.erl +++ /dev/null @@ -1,311 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : ejabberd_auth_riak.erl -%%% Author : Evgeniy Khramtsov -%%% Purpose : Authentification via Riak -%%% Created : 12 Nov 2012 by Evgeniy Khramtsov -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne -%%% -%%% This program is free software; you can redistribute it and/or -%%% modify it under the terms of the GNU General Public License as -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License along -%%% with this program; if not, write to the Free Software Foundation, Inc., -%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -%%% -%%%---------------------------------------------------------------------- - --module(ejabberd_auth_riak). - --compile([{parse_transform, ejabberd_sql_pt}]). - --behaviour(ejabberd_config). - --author('alexey@process-one.net'). - --behaviour(ejabberd_auth). - -%% External exports --export([start/1, set_password/3, check_password/4, - check_password/6, try_register/3, - dirty_get_registered_users/0, get_vh_registered_users/1, - get_vh_registered_users/2, - get_vh_registered_users_number/1, - get_vh_registered_users_number/2, get_password/2, - get_password_s/2, is_user_exists/2, remove_user/2, - remove_user/3, store_type/0, export/1, import/2, - plain_password_required/0, opt_type/1]). --export([passwd_schema/0]). - --include("ejabberd.hrl"). --include("ejabberd_sql_pt.hrl"). - --record(passwd, {us = {<<"">>, <<"">>} :: {binary(), binary()} | '$1', - password = <<"">> :: binary() | scram() | '_'}). - --define(SALT_LENGTH, 16). - -start(_Host) -> - ok. - -plain_password_required() -> - case is_scrammed() of - false -> false; - true -> true - end. - -store_type() -> - case is_scrammed() of - false -> plain; %% allows: PLAIN DIGEST-MD5 SCRAM - true -> scram %% allows: PLAIN SCRAM - end. - -passwd_schema() -> - {record_info(fields, passwd), #passwd{}}. - -check_password(User, AuthzId, Server, Password) -> - if AuthzId /= <<>> andalso AuthzId /= User -> - false; - true -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of - {ok, #passwd{password = Password}} when is_binary(Password) -> - Password /= <<"">>; - {ok, #passwd{password = Scram}} when is_record(Scram, scram) -> - is_password_scram_valid(Password, Scram); - _ -> - false - end - end. - -check_password(User, AuthzId, Server, Password, Digest, - DigestGen) -> - if AuthzId /= <<>> andalso AuthzId /= User -> - false; - true -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of - {ok, #passwd{password = Passwd}} when is_binary(Passwd) -> - DigRes = if Digest /= <<"">> -> - Digest == DigestGen(Passwd); - true -> false - end, - if DigRes -> true; - true -> (Passwd == Password) and (Password /= <<"">>) - end; - {ok, #passwd{password = Scram}} - when is_record(Scram, scram) -> - Passwd = jlib:decode_base64(Scram#scram.storedkey), - DigRes = if Digest /= <<"">> -> - Digest == DigestGen(Passwd); - true -> false - end, - if DigRes -> true; - true -> (Passwd == Password) and (Password /= <<"">>) - end; - _ -> false - end - end. - -set_password(User, Server, Password) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - US = {LUser, LServer}, - if (LUser == error) or (LServer == error) -> - {error, invalid_jid}; - true -> - Password2 = case is_scrammed() and is_binary(Password) - of - true -> password_to_scram(Password); - false -> Password - end, - ok = ejabberd_riak:put(#passwd{us = US, password = Password2}, - passwd_schema(), - [{'2i', [{<<"host">>, LServer}]}]) - end. - -try_register(User, Server, PasswordList) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - Password = if is_list(PasswordList); is_binary(PasswordList) -> - iolist_to_binary(PasswordList); - true -> PasswordList - end, - US = {LUser, LServer}, - if (LUser == error) or (LServer == error) -> - {error, invalid_jid}; - true -> - case ejabberd_riak:get(passwd, passwd_schema(), US) of - {error, notfound} -> - Password2 = case is_scrammed() and - is_binary(Password) - of - true -> password_to_scram(Password); - false -> Password - end, - {atomic, ejabberd_riak:put( - #passwd{us = US, - password = Password2}, - passwd_schema(), - [{'2i', [{<<"host">>, LServer}]}])}; - {ok, _} -> - exists; - Err -> - {atomic, Err} - end - end. - -dirty_get_registered_users() -> - lists:flatmap( - fun(Server) -> - get_vh_registered_users(Server) - end, ejabberd_config:get_vh_by_auth_method(riak)). - -get_vh_registered_users(Server) -> - LServer = jid:nameprep(Server), - case ejabberd_riak:get_keys_by_index(passwd, <<"host">>, LServer) of - {ok, Users} -> - Users; - _ -> - [] - end. - -get_vh_registered_users(Server, _) -> - get_vh_registered_users(Server). - -get_vh_registered_users_number(Server) -> - LServer = jid:nameprep(Server), - case ejabberd_riak:count_by_index(passwd, <<"host">>, LServer) of - {ok, N} -> - N; - _ -> - 0 - end. - -get_vh_registered_users_number(Server, _) -> - get_vh_registered_users_number(Server). - -get_password(User, Server) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of - {ok, #passwd{password = Password}} - when is_binary(Password) -> - Password; - {ok, #passwd{password = Scram}} - when is_record(Scram, scram) -> - {jlib:decode_base64(Scram#scram.storedkey), - jlib:decode_base64(Scram#scram.serverkey), - jlib:decode_base64(Scram#scram.salt), - Scram#scram.iterationcount}; - _ -> false - end. - -get_password_s(User, Server) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of - {ok, #passwd{password = Password}} - when is_binary(Password) -> - Password; - {ok, #passwd{password = Scram}} - when is_record(Scram, scram) -> - <<"">>; - _ -> <<"">> - end. - -is_user_exists(User, Server) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of - {error, notfound} -> false; - {ok, _} -> true; - Err -> Err - end. - -remove_user(User, Server) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - ejabberd_riak:delete(passwd, {LUser, LServer}), - ok. - -remove_user(User, Server, Password) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of - {ok, #passwd{password = Password}} - when is_binary(Password) -> - ejabberd_riak:delete(passwd, {LUser, LServer}), - ok; - {ok, #passwd{password = Scram}} - when is_record(Scram, scram) -> - case is_password_scram_valid(Password, Scram) of - true -> - ejabberd_riak:delete(passwd, {LUser, LServer}), - ok; - false -> not_allowed - end; - _ -> not_exists - end. - -%%% -%%% SCRAM -%%% - -is_scrammed() -> - scram == - ejabberd_config:get_local_option({auth_password_format, ?MYNAME}, - fun(V) -> V end). - -password_to_scram(Password) -> - password_to_scram(Password, - ?SCRAM_DEFAULT_ITERATION_COUNT). - -password_to_scram(Password, IterationCount) -> - Salt = randoms:bytes(?SALT_LENGTH), - SaltedPassword = scram:salted_password(Password, Salt, - IterationCount), - StoredKey = - scram:stored_key(scram:client_key(SaltedPassword)), - ServerKey = scram:server_key(SaltedPassword), - #scram{storedkey = jlib:encode_base64(StoredKey), - serverkey = jlib:encode_base64(ServerKey), - salt = jlib:encode_base64(Salt), - iterationcount = IterationCount}. - -is_password_scram_valid(Password, Scram) -> - IterationCount = Scram#scram.iterationcount, - Salt = jlib:decode_base64(Scram#scram.salt), - SaltedPassword = scram:salted_password(Password, Salt, - IterationCount), - StoredKey = - scram:stored_key(scram:client_key(SaltedPassword)), - jlib:decode_base64(Scram#scram.storedkey) == StoredKey. - -export(_Server) -> - [{passwd, - fun(Host, #passwd{us = {LUser, LServer}, password = Password}) - when LServer == Host -> - [?SQL("delete from users where username=%(LUser)s;"), - ?SQL("insert into users(username, password) " - "values (%(LUser)s, %(Password)s);")]; - (_Host, _R) -> - [] - end}]. - -import(LServer, [LUser, Password, _TimeStamp]) -> - Passwd = #passwd{us = {LUser, LServer}, password = Password}, - ejabberd_riak:put(Passwd, passwd_schema(), [{'2i', [{<<"host">>, LServer}]}]). - -opt_type(auth_password_format) -> fun (V) -> V end; -opt_type(_) -> [auth_password_format]. diff --git a/src/ejabberd_auth_sql.erl b/src/ejabberd_auth_sql.erl index 93dac4f4f..8ce78bc18 100644 --- a/src/ejabberd_auth_sql.erl +++ b/src/ejabberd_auth_sql.erl @@ -1,11 +1,11 @@ %%%---------------------------------------------------------------------- %%% File : ejabberd_auth_sql.erl %%% Author : Alexey Shchepin -%%% Purpose : Authentification via ODBC +%%% Purpose : Authentication via ODBC %%% Created : 12 Dec 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -25,467 +25,422 @@ -module(ejabberd_auth_sql). --compile([{parse_transform, ejabberd_sql_pt}]). - --behaviour(ejabberd_config). -author('alexey@process-one.net'). -behaviour(ejabberd_auth). --export([start/1, set_password/3, check_password/4, - check_password/6, try_register/3, - dirty_get_registered_users/0, get_vh_registered_users/1, - get_vh_registered_users/2, - get_vh_registered_users_number/1, - get_vh_registered_users_number/2, get_password/2, - get_password_s/2, is_user_exists/2, remove_user/2, - remove_user/3, store_type/0, plain_password_required/0, - convert_to_scram/1, opt_type/1]). +-export([start/1, stop/1, set_password_multiple/3, try_register_multiple/3, + get_users/2, count_users/2, get_password/2, + remove_user/2, store_type/1, plain_password_required/1, + export/1, which_users_exists/2, drop_password_type/2, set_password_instance/3]). +-export([sql_schemas/0]). --include("ejabberd.hrl"). +-include_lib("xmpp/include/scram.hrl"). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). - --define(SALT_LENGTH, 16). +-include("ejabberd_auth.hrl"). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- -start(_Host) -> ok. +start(Host) -> + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), + ok. -plain_password_required() -> - case is_scrammed() of - false -> false; - true -> true +sql_schemas() -> + [ + #sql_schema{ + version = 2, + tables = + [#sql_table{ + name = <<"users">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"type">>, type = smallint}, + #sql_column{name = <<"password">>, type = text}, + #sql_column{name = <<"serverkey">>, type = {text, 128}, + default = true}, + #sql_column{name = <<"salt">>, type = {text, 128}, + default = true}, + #sql_column{name = <<"iterationcount">>, type = integer, + default = true}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>, <<"type">>], + unique = true}]}], + update = [ + {add_column, <<"users">>, <<"type">>}, + {update_primary_key,<<"users">>, + [<<"server_host">>, <<"username">>, <<"type">>]} + ]}, + #sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"users">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"password">>, type = text}, + #sql_column{name = <<"serverkey">>, type = {text, 128}, + default = true}, + #sql_column{name = <<"salt">>, type = {text, 128}, + default = true}, + #sql_column{name = <<"iterationcount">>, type = integer, + default = true}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>], + unique = true}]}]}]. + +stop(_Host) -> ok. + +plain_password_required(Server) -> + store_type(Server) == scram. + +store_type(Server) -> + ejabberd_auth:password_format(Server). + +hash_to_num(plain) -> 1; +hash_to_num(sha) -> 2; +hash_to_num(sha256) -> 3; +hash_to_num(sha512) -> 4. + +num_to_hash(2) -> sha; +num_to_hash(3) -> sha256; +num_to_hash(4) -> sha512. + +set_password_instance(User, Server, #scram{hash = Hash, storedkey = SK, serverkey = SEK, + salt = Salt, iterationcount = IC}) -> + F = fun() -> + set_password_scram_t(User, Server, Hash, + SK, SEK, Salt, IC) + end, + case ejabberd_sql:sql_transaction(Server, F) of + {atomic, _} -> + ok; + {aborted, _} -> + {error, db_failure} + end; +set_password_instance(User, Server, Plain) -> + F = fun() -> + set_password_t(User, Server, Plain) + end, + case ejabberd_sql:sql_transaction(Server, F) of + {atomic, _} -> + ok; + {aborted, _} -> + {error, db_failure} end. -store_type() -> - case is_scrammed() of - false -> plain; %% allows: PLAIN DIGEST-MD5 SCRAM - true -> scram %% allows: PLAIN SCRAM +set_password_multiple(User, Server, Passwords) -> + F = + fun() -> + ejabberd_sql:sql_query_t( + ?SQL("delete from users where username=%(User)s and %(Server)H")), + lists:foreach( + fun(#scram{hash = Hash, storedkey = SK, serverkey = SEK, + salt = Salt, iterationcount = IC}) -> + set_password_scram_t( + User, Server, Hash, + SK, SEK, Salt, IC); + (Plain) -> + set_password_t(User, Server, Plain) + end, Passwords) + end, + case ejabberd_sql:sql_transaction(Server, F) of + {atomic, _} -> + {cache, {ok, Passwords}}; + {aborted, _} -> + {nocache, {error, db_failure}} end. -%% @spec (User, AuthzId, Server, Password) -> true | false | {error, Error} -check_password(User, AuthzId, Server, Password) -> - if AuthzId /= <<>> andalso AuthzId /= User -> - false; - true -> - LServer = jid:nameprep(Server), - LUser = jid:nodeprep(User), - if (LUser == error) or (LServer == error) -> - false; - (LUser == <<>>) or (LServer == <<>>) -> - false; - true -> - case is_scrammed() of - true -> - try sql_queries:get_password_scram(LServer, LUser) of - {selected, - [{StoredKey, ServerKey, Salt, IterationCount}]} -> - Scram = - #scram{storedkey = StoredKey, - serverkey = ServerKey, - salt = Salt, - iterationcount = IterationCount}, - is_password_scram_valid_stored(Password, Scram, LUser, LServer); - {selected, []} -> - false; %% Account does not exist - {error, _Error} -> - false %% Typical error is that table doesn't exist - catch - _:_ -> - false %% Typical error is database not accessible - end; - false -> - try sql_queries:get_password(LServer, LUser) of - {selected, [{Password}]} -> - Password /= <<"">>; - {selected, [{_Password2}]} -> - false; %% Password is not correct - {selected, []} -> - false; %% Account does not exist - {error, _Error} -> - false %% Typical error is that table doesn't exist - catch - _:_ -> - false %% Typical error is database not accessible - end - end - end +try_register_multiple(User, Server, Passwords) -> + F = + fun() -> + case ejabberd_sql:sql_query_t( + ?SQL("select @(count(*))d from users where username=%(User)s and %(Server)H")) of + {selected, [{0}]} -> + lists:foreach( + fun(#scram{hash = Hash, storedkey = SK, serverkey = SEK, + salt = Salt, iterationcount = IC}) -> + set_password_scram_t( + User, Server, Hash, + SK, SEK, Salt, IC); + (Plain) -> + set_password_t(User, Server, Plain) + end, Passwords), + {cache, {ok, Passwords}}; + {selected, _} -> + {nocache, {error, exists}}; + _ -> + {nocache, {error, db_failure}} + end + end, + case ejabberd_sql:sql_transaction(Server, F) of + {atomic, Res} -> + Res; + {aborted, _} -> + {nocache, {error, db_failure}} end. -%% @spec (User, AuthzId, Server, Password, Digest, DigestGen) -> true | false | {error, Error} -check_password(User, AuthzId, Server, Password, Digest, - DigestGen) -> - if AuthzId /= <<>> andalso AuthzId /= User -> - false; - true -> - LServer = jid:nameprep(Server), - LUser = jid:nodeprep(User), - if (LUser == error) or (LServer == error) -> - false; - (LUser == <<>>) or (LServer == <<>>) -> - false; - true -> - case is_scrammed() of - false -> - try sql_queries:get_password(LServer, LUser) of - %% Account exists, check if password is valid - {selected, [{Passwd}]} -> - DigRes = if Digest /= <<"">> -> - Digest == DigestGen(Passwd); - true -> false - end, - if DigRes -> true; - true -> (Passwd == Password) and (Password /= <<"">>) - end; - {selected, []} -> - false; %% Account does not exist - {error, _Error} -> - false %% Typical error is that table doesn't exist - catch - _:_ -> - false %% Typical error is database not accessible - end; - true -> - false - end - end +get_users(Server, Opts) -> + case list_users(Server, Opts) of + {selected, Res} -> + [{U, Server} || {U} <- Res]; + _ -> [] end. -%% @spec (User::string(), Server::string(), Password::string()) -> -%% ok | {error, invalid_jid} -set_password(User, Server, Password) -> - LServer = jid:nameprep(Server), - LUser = jid:nodeprep(User), - if (LUser == error) or (LServer == error) -> - {error, invalid_jid}; - (LUser == <<>>) or (LServer == <<>>) -> - {error, invalid_jid}; - true -> - case is_scrammed() of - true -> - Scram = password_to_scram(Password), - case catch sql_queries:set_password_scram_t( - LServer, - LUser, - Scram#scram.storedkey, - Scram#scram.serverkey, - Scram#scram.salt, - Scram#scram.iterationcount - ) - of - {atomic, ok} -> ok; - Other -> {error, Other} - end; - false -> - case catch sql_queries:set_password_t(LServer, - LUser, Password) - of - {atomic, ok} -> ok; - Other -> {error, Other} - end - end - end. - -%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, invalid_jid} -try_register(User, Server, Password) -> - LServer = jid:nameprep(Server), - LUser = jid:nodeprep(User), - if (LUser == error) or (LServer == error) -> - {error, invalid_jid}; - (LUser == <<>>) or (LServer == <<>>) -> - {error, invalid_jid}; - true -> - case is_scrammed() of - true -> - Scram = password_to_scram(Password), - case catch sql_queries:add_user_scram( - LServer, - LUser, - Scram#scram.storedkey, - Scram#scram.serverkey, - Scram#scram.salt, - Scram#scram.iterationcount - ) of - {updated, 1} -> {atomic, ok}; - _ -> {atomic, exists} - end; - false -> - case catch sql_queries:add_user(LServer, LUser, - Password) of - {updated, 1} -> {atomic, ok}; - _ -> {atomic, exists} - end - end - end. - -dirty_get_registered_users() -> - Servers = ejabberd_config:get_vh_by_auth_method(sql), - lists:flatmap(fun (Server) -> - get_vh_registered_users(Server) - end, - Servers). - -get_vh_registered_users(Server) -> - case jid:nameprep(Server) of - error -> []; - <<>> -> []; - LServer -> - case catch sql_queries:list_users(LServer) of - {selected, Res} -> - [{U, LServer} || {U} <- Res]; - _ -> [] - end - end. - -get_vh_registered_users(Server, Opts) -> - case jid:nameprep(Server) of - error -> []; - <<>> -> []; - LServer -> - case catch sql_queries:list_users(LServer, Opts) of - {selected, Res} -> - [{U, LServer} || {U} <- Res]; - _ -> [] - end - end. - -get_vh_registered_users_number(Server) -> - case jid:nameprep(Server) of - error -> 0; - <<>> -> 0; - LServer -> - case catch sql_queries:users_number(LServer) of - {selected, [{Res}]} -> - Res; - _ -> 0 - end - end. - -get_vh_registered_users_number(Server, Opts) -> - case jid:nameprep(Server) of - error -> 0; - <<>> -> 0; - LServer -> - case catch sql_queries:users_number(LServer, Opts) of - {selected, [{Res}]} -> - Res; - _Other -> 0 - end +count_users(Server, Opts) -> + case users_number(Server, Opts) of + {selected, [{Res}]} -> + Res; + _Other -> 0 end. get_password(User, Server) -> - LServer = jid:nameprep(Server), - LUser = jid:nodeprep(User), - if (LUser == error) or (LServer == error) -> - false; - (LUser == <<>>) or (LServer == <<>>) -> - false; - true -> - case is_scrammed() of - true -> - case catch sql_queries:get_password_scram( - LServer, LUser) of - {selected, - [{StoredKey, ServerKey, Salt, IterationCount}]} -> - {jlib:decode_base64(StoredKey), - jlib:decode_base64(ServerKey), - jlib:decode_base64(Salt), - IterationCount}; - _ -> false - end; - false -> - case catch sql_queries:get_password(LServer, LUser) - of - {selected, [{Password}]} -> Password; - _ -> false - end - end + case get_password_scram(Server, User) of + {selected, []} -> + {cache, error}; + {selected, Passwords} -> + Converted = lists:map( + fun({0, Password, <<>>, <<>>, 0}) -> + update_password_type(User, Server, 1), + Password; + ({_, Password, <<>>, <<>>, 0}) -> + Password; + ({0, StoredKey, ServerKey, Salt, IterationCount}) -> + {Hash, SK} = case StoredKey of + <<"sha256:", Rest/binary>> -> + update_password_type(User, Server, 3, Rest), + {sha256, Rest}; + <<"sha512:", Rest/binary>> -> + update_password_type(User, Server, 4, Rest), + {sha512, Rest}; + Other -> + update_password_type(User, Server, 2), + {sha, Other} + end, + #scram{storedkey = SK, + serverkey = ServerKey, + salt = Salt, + hash = Hash, + iterationcount = IterationCount}; + ({Type, StoredKey, ServerKey, Salt, IterationCount}) -> + Hash = num_to_hash(Type), + #scram{storedkey = StoredKey, + serverkey = ServerKey, + salt = Salt, + hash = Hash, + iterationcount = IterationCount} + end, Passwords), + {cache, {ok, Converted}}; + _ -> + {nocache, error} end. -get_password_s(User, Server) -> - LServer = jid:nameprep(Server), - LUser = jid:nodeprep(User), - if (LUser == error) or (LServer == error) -> - <<"">>; - (LUser == <<>>) or (LServer == <<>>) -> - <<"">>; - true -> - case is_scrammed() of - false -> - case catch sql_queries:get_password(LServer, LUser) of - {selected, [{Password}]} -> Password; - _ -> <<"">> - end; - true -> <<"">> - end - end. - -%% @spec (User, Server) -> true | false | {error, Error} -is_user_exists(User, Server) -> - LServer = jid:nameprep(Server), - LUser = jid:nodeprep(User), - if (LUser == error) or (LServer == error) -> - false; - (LUser == <<>>) or (LServer == <<>>) -> - false; - true -> - try sql_queries:get_password(LServer, LUser) of - {selected, [{_Password}]} -> - true; %% Account exists - {selected, []} -> - false; %% Account does not exist - {error, Error} -> {error, Error} - catch - _:B -> {error, B} - end - end. - -%% @spec (User, Server) -> ok | error -%% @doc Remove user. -%% Note: it may return ok even if there was some problem removing the user. remove_user(User, Server) -> - LServer = jid:nameprep(Server), - LUser = jid:nodeprep(User), - if (LUser == error) or (LServer == error) -> - error; - (LUser == <<>>) or (LServer == <<>>) -> - error; - true -> - catch sql_queries:del_user(LServer, LUser), - ok + case del_user(Server, User) of + {updated, _} -> + ok; + _ -> + {error, db_failure} end. -%% @spec (User, Server, Password) -> ok | error | not_exists | not_allowed -%% @doc Remove user if the provided password is correct. -remove_user(User, Server, Password) -> - LServer = jid:nameprep(Server), - LUser = jid:nodeprep(User), - if (LUser == error) or (LServer == error) -> - error; - (LUser == <<>>) or (LServer == <<>>) -> - error; - true -> - case is_scrammed() of - true -> - case check_password(User, <<"">>, Server, Password) of - true -> - remove_user(User, Server), - ok; - false -> not_allowed - end; - false -> - F = fun () -> - Result = sql_queries:del_user_return_password( - LServer, LUser, Password), - case Result of - {selected, [{Password}]} -> ok; - {selected, []} -> not_exists; - _ -> not_allowed - end - end, - {atomic, Result} = sql_queries:sql_transaction( - LServer, F), - Result - end - end. +drop_password_type(LServer, Hash) -> + Type = hash_to_num(Hash), + ejabberd_sql:sql_query( + LServer, + ?SQL("delete from users" + " where type=%(Type)d and %(LServer)H")). -%%% -%%% SCRAM -%%% - -is_scrammed() -> - scram == - ejabberd_config:get_option({auth_password_format, ?MYNAME}, - fun(V) -> V end). - -password_to_scram(Password) -> - password_to_scram(Password, - ?SCRAM_DEFAULT_ITERATION_COUNT). - -password_to_scram(Password, IterationCount) -> - Salt = randoms:bytes(?SALT_LENGTH), - SaltedPassword = scram:salted_password(Password, Salt, - IterationCount), - StoredKey = - scram:stored_key(scram:client_key(SaltedPassword)), - ServerKey = scram:server_key(SaltedPassword), - #scram{storedkey = jlib:encode_base64(StoredKey), - serverkey = jlib:encode_base64(ServerKey), - salt = jlib:encode_base64(Salt), - iterationcount = IterationCount}. - -is_password_scram_valid_stored(Pass, {scram,Pass,<<>>,<<>>,0}, LUser, LServer) -> - ?INFO_MSG("Apparently, SQL auth method and scram password formatting are " - "enabled, but the password of user '~s' in the 'users' table is not " - "scrammed. You may want to execute this command: " - "ejabberdctl convert_to_scram ~s", [LUser, LServer]), - false; -is_password_scram_valid_stored(Password, Scram, _, _) -> - is_password_scram_valid(Password, Scram). - -is_password_scram_valid(Password, Scram) -> - IterationCount = Scram#scram.iterationcount, - Salt = jlib:decode_base64(Scram#scram.salt), - SaltedPassword = scram:salted_password(Password, Salt, - IterationCount), - StoredKey = - scram:stored_key(scram:client_key(SaltedPassword)), - jlib:decode_base64(Scram#scram.storedkey) == StoredKey. - --define(BATCH_SIZE, 1000). - -set_password_scram_t(LUser, +set_password_scram_t(LUser, LServer, Hash, StoredKey, ServerKey, Salt, IterationCount) -> + Type = hash_to_num(Hash), ?SQL_UPSERT_T( "users", ["!username=%(LUser)s", + "!server_host=%(LServer)s", + "!type=%(Type)d", "password=%(StoredKey)s", "serverkey=%(ServerKey)s", "salt=%(Salt)s", "iterationcount=%(IterationCount)d"]). -convert_to_scram(Server) -> - LServer = jid:nameprep(Server), - if - LServer == error; - LServer == <<>> -> - {error, {incorrect_server_name, Server}}; - true -> - F = fun () -> - BatchSize = ?BATCH_SIZE, - case ejabberd_sql:sql_query_t( - ?SQL("select @(username)s, @(password)s" - " from users" - " where iterationcount=0" - " limit %(BatchSize)d")) of - {selected, []} -> - ok; - {selected, Rs} -> - lists:foreach( - fun({LUser, Password}) -> - Scram = password_to_scram(Password), - set_password_scram_t( - LUser, - Scram#scram.storedkey, - Scram#scram.serverkey, - Scram#scram.salt, - Scram#scram.iterationcount - ) - end, Rs), - continue; - Err -> {bad_reply, Err} - end - end, - case sql_queries:sql_transaction(LServer, F) of - {atomic, ok} -> ok; - {atomic, continue} -> convert_to_scram(Server); - {atomic, Error} -> {error, Error}; - Error -> Error +set_password_t(LUser, LServer, Password) -> + ?SQL_UPSERT_T( + "users", + ["!username=%(LUser)s", + "!server_host=%(LServer)s", + "!type=1", + "password=%(Password)s", + "serverkey=''", + "salt=''", + "iterationcount=0"]). + +update_password_type(LUser, LServer, Type, Password) -> + ejabberd_sql:sql_query( + LServer, + ?SQL("update users set type=%(Type)d, password=%(Password)s" + " where username=%(LUser)s and type=0 and %(LServer)H")). + +update_password_type(LUser, LServer, Type) -> + ejabberd_sql:sql_query( + LServer, + ?SQL("update users set type=%(Type)d" + " where username=%(LUser)s and type=0 and %(LServer)H")). + +get_password_scram(LServer, LUser) -> + ejabberd_sql:sql_query( + LServer, + ?SQL("select @(type)d, @(password)s, @(serverkey)s, @(salt)s, @(iterationcount)d" + " from users" + " where username=%(LUser)s and %(LServer)H")). + +del_user(LServer, LUser) -> + ejabberd_sql:sql_query( + LServer, + ?SQL("delete from users where username=%(LUser)s and %(LServer)H")). + +list_users(LServer, []) -> + ejabberd_sql:sql_query( + LServer, + ?SQL("select @(distinct username)s from users where %(LServer)H")); +list_users(LServer, [{from, Start}, {to, End}]) + when is_integer(Start) and is_integer(End) -> + list_users(LServer, + [{limit, End - Start + 1}, {offset, Start - 1}]); +list_users(LServer, + [{prefix, Prefix}, {from, Start}, {to, End}]) + when is_binary(Prefix) and is_integer(Start) and + is_integer(End) -> + list_users(LServer, + [{prefix, Prefix}, {limit, End - Start + 1}, + {offset, Start - 1}]); +list_users(LServer, [{limit, Limit}, {offset, Offset}]) + when is_integer(Limit) and is_integer(Offset) -> + ejabberd_sql:sql_query( + LServer, + ?SQL("select @(distinct username)s from users " + "where %(LServer)H " + "order by username " + "limit %(Limit)d offset %(Offset)d")); +list_users(LServer, + [{prefix, Prefix}, {limit, Limit}, {offset, Offset}]) + when is_binary(Prefix) and is_integer(Limit) and + is_integer(Offset) -> + SPrefix = ejabberd_sql:escape_like_arg(Prefix), + SPrefix2 = <>, + ejabberd_sql:sql_query( + LServer, + ?SQL("select @(distinct username)s from users " + "where username like %(SPrefix2)s %ESCAPE and %(LServer)H " + "order by username " + "limit %(Limit)d offset %(Offset)d")). + +users_number(LServer) -> + ejabberd_sql:sql_query( + LServer, + fun(pgsql, _) -> + case + ejabberd_option:pgsql_users_number_estimate(LServer) of + true -> + ejabberd_sql:sql_query_t( + ?SQL("select @(reltuples :: bigint)d from pg_class" + " where oid = 'users'::regclass::oid")); + _ -> + ejabberd_sql:sql_query_t( + ?SQL("select @(count(distinct username))d from users where %(LServer)H")) + end; + (_Type, _) -> + ejabberd_sql:sql_query_t( + ?SQL("select @(count(distinct username))d from users where %(LServer)H")) + end). + +users_number(LServer, [{prefix, Prefix}]) + when is_binary(Prefix) -> + SPrefix = ejabberd_sql:escape_like_arg(Prefix), + SPrefix2 = <>, + ejabberd_sql:sql_query( + LServer, + ?SQL("select @(count(distinct username))d from users " + "where username like %(SPrefix2)s %ESCAPE and %(LServer)H")); +users_number(LServer, []) -> + users_number(LServer). + +which_users_exists(LServer, LUsers) when length(LUsers) =< 100 -> + try ejabberd_sql:sql_query( + LServer, + ?SQL("select @(distinct 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. -opt_type(auth_password_format) -> fun (V) -> V end; -opt_type(_) -> [auth_password_format]. +export(_Server) -> + [{passwd, + fun(Host, #passwd{us = {LUser, LServer, plain}, password = Password}) + when LServer == Host, + is_binary(Password) -> + [?SQL("delete from users where username=%(LUser)s and type=1 and %(LServer)H;"), + ?SQL_INSERT( + "users", + ["username=%(LUser)s", + "server_host=%(LServer)s", + "type=1", + "password=%(Password)s"])]; + (Host, {passwd, {LUser, LServer, _}, + {scram, StoredKey, ServerKey, Salt, IterationCount}}) + when LServer == Host -> + Hash = sha, + Type = hash_to_num(Hash), + [?SQL("delete from users where username=%(LUser)s and type=%(Type)d and %(LServer)H;"), + ?SQL_INSERT( + "users", + ["username=%(LUser)s", + "server_host=%(LServer)s", + "type=%(Type)d", + "password=%(StoredKey)s", + "serverkey=%(ServerKey)s", + "salt=%(Salt)s", + "iterationcount=%(IterationCount)d"])]; + (Host, #passwd{us = {LUser, LServer, _}, password = #scram{} = Scram}) + when LServer == Host -> + StoredKey = Scram#scram.storedkey, + ServerKey = Scram#scram.serverkey, + Salt = Scram#scram.salt, + IterationCount = Scram#scram.iterationcount, + Type = hash_to_num(Scram#scram.hash), + [?SQL("delete from users where username=%(LUser)s and type=%(Type)d and %(LServer)H;"), + ?SQL_INSERT( + "users", + ["username=%(LUser)s", + "server_host=%(LServer)s", + "type=%(Type)d", + "password=%(StoredKey)s", + "serverkey=%(ServerKey)s", + "salt=%(Salt)s", + "iterationcount=%(IterationCount)d"])]; + (_Host, _R) -> + [] + end}]. diff --git a/src/ejabberd_backend_sup.erl b/src/ejabberd_backend_sup.erl new file mode 100644 index 000000000..1b3495e36 --- /dev/null +++ b/src/ejabberd_backend_sup.erl @@ -0,0 +1,46 @@ +%%%------------------------------------------------------------------- +%%% Created : 24 Feb 2017 by Evgeny Khramtsov +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%------------------------------------------------------------------- +-module(ejabberd_backend_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +%%%=================================================================== +%%% API functions +%%%=================================================================== +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%%%=================================================================== +%%% Supervisor callbacks +%%%=================================================================== +init([]) -> + {ok, {{one_for_one, 10, 1}, []}}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== diff --git a/src/ejabberd_batch.erl b/src/ejabberd_batch.erl new file mode 100644 index 000000000..5a907c74b --- /dev/null +++ b/src/ejabberd_batch.erl @@ -0,0 +1,205 @@ +%%%---------------------------------------------------------------------- +%%% File : ejabberd_batch.erl +%%% Author : Paweł Chmielowski +%%% Purpose : Batch tasks manager +%%% Created : 8 mar 2022 by Paweł Chmielowski +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + +-module(ejabberd_batch). +-author("pawel@process-one.net"). + +-behaviour(gen_server). + +-include("logger.hrl"). + +%% API +-export([start_link/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, + code_change/3]). +-export([register_task/5, task_status/1, abort_task/1]). + +-define(SERVER, ?MODULE). + +-record(state, {tasks = #{}}). +-record(task, {state = not_started, pid, steps, done_steps}). + +%%%=================================================================== +%%% API +%%%=================================================================== + +%% @doc Spawns the server and registers the local name (unique) +-spec(start_link() -> + {ok, Pid :: pid()} | ignore | {error, Reason :: term()}). +start_link() -> + gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). + +register_task(Type, Steps, Rate, JobState, JobFun) -> + gen_server:call(?MODULE, {register_task, Type, Steps, Rate, JobState, JobFun}). + +task_status(Type) -> + gen_server:call(?MODULE, {task_status, Type}). + +abort_task(Type) -> + gen_server:call(?MODULE, {abort_task, Type}). + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== + +%% @private +%% @doc Initializes the server +-spec(init(Args :: term()) -> + {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} | + {stop, Reason :: term()} | ignore). +init([]) -> + {ok, #state{}}. + +%% @private +%% @doc Handling call messages +-spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()}, + State :: #state{}) -> + {reply, Reply :: term(), NewState :: #state{}} | + {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} | + {noreply, NewState :: #state{}} | + {noreply, NewState :: #state{}, timeout() | hibernate} | + {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} | + {stop, Reason :: term(), NewState :: #state{}}). +handle_call({register_task, Type, Steps, Rate, JobState, JobFun}, _From, #state{tasks = Tasks} = State) -> + case maps:get(Type, Tasks, #task{}) of + #task{state = S} when S == completed; S == not_started; S == aborted; S == failed -> + Pid = spawn(fun() -> work_loop(Type, JobState, JobFun, Rate, erlang:monotonic_time(second), 0) end), + Tasks2 = maps:put(Type, #task{state = working, pid = Pid, steps = Steps, done_steps = 0}, Tasks), + {reply, ok, #state{tasks = Tasks2}}; + #task{state = working} -> + {reply, {error, in_progress}, State} + end; +handle_call({task_status, Type}, _From, #state{tasks = Tasks} = State) -> + case maps:get(Type, Tasks, none) of + none -> + {reply, not_started, State}; + #task{state = not_started} -> + {reply, not_started, State}; + #task{state = failed, done_steps = Steps, pid = Error} -> + {reply, {failed, Steps, Error}, State}; + #task{state = aborted, done_steps = Steps} -> + {reply, {aborted, Steps}, State}; + #task{state = working, done_steps = Steps} -> + {reply, {working, Steps}, State}; + #task{state = completed, done_steps = Steps} -> + {reply, {completed, Steps}, State} + end; +handle_call({abort_task, Type}, _From, #state{tasks = Tasks} = State) -> + case maps:get(Type, Tasks, none) of + #task{state = working, pid = Pid} = T -> + Pid ! abort, + Tasks2 = maps:put(Type, T#task{state = aborted, pid = none}, Tasks), + {reply, aborted, State#state{tasks = Tasks2}}; + _ -> + {reply, not_started, State} + end; +handle_call(_Request, _From, State = #state{}) -> + {reply, ok, State}. + +%% @private +%% @doc Handling cast messages +-spec(handle_cast(Request :: term(), State :: #state{}) -> + {noreply, NewState :: #state{}} | + {noreply, NewState :: #state{}, timeout() | hibernate} | + {stop, Reason :: term(), NewState :: #state{}}). +handle_cast({task_finished, Type, Pid}, #state{tasks = Tasks} = State) -> + case maps:get(Type, Tasks, none) of + #task{state = working, pid = Pid2} = T when Pid == Pid2 -> + Tasks2 = maps:put(Type, T#task{state = completed, pid = none}, Tasks), + {noreply, State#state{tasks = Tasks2}}; + _ -> + {noreply, State} + end; +handle_cast({task_progress, Type, Pid, Count}, #state{tasks = Tasks} = State) -> + case maps:get(Type, Tasks, none) of + #task{state = working, pid = Pid2, done_steps = Steps} = T when Pid == Pid2 -> + Tasks2 = maps:put(Type, T#task{done_steps = Steps + Count}, Tasks), + {noreply, State#state{tasks = Tasks2}}; + _ -> + {noreply, State} + end; +handle_cast({task_error, Type, Pid, Error}, #state{tasks = Tasks} = State) -> + case maps:get(Type, Tasks, none) of + #task{state = working, pid = Pid2} = T when Pid == Pid2 -> + Tasks2 = maps:put(Type, T#task{state = failed, pid = Error}, Tasks), + {noreply, State#state{tasks = Tasks2}}; + _ -> + {noreply, State} + end; +handle_cast(_Request, State = #state{}) -> + {noreply, State}. + +%% @private +%% @doc Handling all non call/cast messages +-spec(handle_info(Info :: timeout() | term(), State :: #state{}) -> + {noreply, NewState :: #state{}} | + {noreply, NewState :: #state{}, timeout() | hibernate} | + {stop, Reason :: term(), NewState :: #state{}}). +handle_info(_Info, State = #state{}) -> + {noreply, State}. + +%% @private +%% @doc This function is called by a gen_server when it is about to +%% terminate. It should be the opposite of Module:init/1 and do any +%% necessary cleaning up. When it returns, the gen_server terminates +%% with Reason. The return value is ignored. +-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()), + State :: #state{}) -> term()). +terminate(_Reason, _State = #state{}) -> + ok. + +%% @private +%% @doc Convert process state when code is changed +-spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{}, + Extra :: term()) -> + {ok, NewState :: #state{}} | {error, Reason :: term()}). +code_change(_OldVsn, State = #state{}, _Extra) -> + {ok, State}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== + +work_loop(Task, JobState, JobFun, Rate, StartDate, CurrentProgress) -> + try JobFun(JobState) of + {ok, _NewState, 0} -> + gen_server:cast(?MODULE, {task_finished, Task, self()}); + {ok, NewState, Count} -> + gen_server:cast(?MODULE, {task_progress, Task, self(), Count}), + NewProgress = CurrentProgress + Count, + TimeSpent = erlang:monotonic_time(second) - StartDate, + SleepTime = max(0, NewProgress/Rate*60 - TimeSpent), + receive + abort -> ok + after round(SleepTime*1000) -> + work_loop(Task, NewState, JobFun, Rate, StartDate, NewProgress) + end; + {error, Error} -> + gen_server:cast(?MODULE, {task_error, Task, self(), Error}) + catch _:_ -> + gen_server:cast(?MODULE, {task_error, Task, self(), internal_error}) + end. diff --git a/src/ejabberd_bosh.erl b/src/ejabberd_bosh.erl index b94184167..8d1dbd595 100644 --- a/src/ejabberd_bosh.erl +++ b/src/ejabberd_bosh.erl @@ -5,7 +5,7 @@ %%% Created : 20 Jul 2011 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -23,22 +23,18 @@ %%% %%%------------------------------------------------------------------- -module(ejabberd_bosh). - --protocol({xep, 124, '1.11'}). --protocol({xep, 206, '1.4'}). - --define(GEN_FSM, p1_fsm). - --behaviour(?GEN_FSM). +-behaviour(xmpp_socket). +-behaviour(p1_fsm). +-protocol({xep, 124, '1.11', '16.12', "complete", ""}). +-protocol({xep, 206, '1.4', '16.12', "complete", ""}). %% API -export([start/2, start/3, start_link/3]). -export([send_xml/2, setopts/2, controlling_process/2, - migrate/3, custom_receiver/1, become_controller/2, - reset_stream/1, change_shaper/2, monitor/1, close/1, + reset_stream/1, change_shaper/2, close/1, sockname/1, peername/1, process_request/3, send/2, - change_controller/2]). + get_transport/1, get_owner/1]). %% gen_fsm callbacks -export([init/1, wait_for_session/2, wait_for_session/3, @@ -46,13 +42,9 @@ handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). --include("ejabberd.hrl"). -include("logger.hrl"). - --include("jlib.hrl"). - +-include_lib("xmpp/include/xmpp.hrl"). -include("ejabberd_http.hrl"). - -include("bosh.hrl"). %%-define(DBGFSM, true). @@ -73,16 +65,12 @@ -define(NS_HTTP_BIND, <<"http://jabber.org/protocol/httpbind">>). --define(DEFAULT_MAXPAUSE, 120). - -define(DEFAULT_WAIT, 300). -define(DEFAULT_HOLD, 1). -define(DEFAULT_POLLING, 2). --define(DEFAULT_INACTIVITY, 30). - -define(MAX_SHAPED_REQUESTS_QUEUE_LEN, 1000). -define(SEND_TIMEOUT, 15000). @@ -96,22 +84,22 @@ -record(state, {host = <<"">> :: binary(), sid = <<"">> :: binary(), - el_ibuf = buf_new() :: ?TQUEUE, - el_obuf = buf_new() :: ?TQUEUE, - shaper_state = none :: shaper:shaper(), - c2s_pid :: pid(), + el_ibuf :: p1_queue:queue(), + el_obuf :: p1_queue:queue(), + shaper_state = none :: ejabberd_shaper:shaper(), + c2s_pid :: pid() | undefined, xmpp_ver = <<"">> :: binary(), - inactivity_timer :: reference(), - wait_timer :: reference(), - wait_timeout = ?DEFAULT_WAIT :: timeout(), - inactivity_timeout = ?DEFAULT_INACTIVITY :: timeout(), + inactivity_timer :: reference() | undefined, + wait_timer :: reference() | undefined, + wait_timeout = ?DEFAULT_WAIT :: pos_integer(), + inactivity_timeout :: pos_integer(), prev_rid = 0 :: non_neg_integer(), prev_key = <<"">> :: binary(), - prev_poll :: erlang:timestamp(), + prev_poll :: erlang:timestamp() | undefined, max_concat = unlimited :: unlimited | non_neg_integer(), - responses = gb_trees:empty() :: ?TGB_TREE, - receivers = gb_trees:empty() :: ?TGB_TREE, - shaped_receivers = queue:new() :: ?TQUEUE, + responses = gb_trees:empty() :: gb_trees:tree(), + receivers = gb_trees:empty() :: gb_trees:tree(), + shaped_receivers :: p1_queue:queue(), ip :: inet:ip_address(), max_requests = 1 :: non_neg_integer()}). @@ -123,7 +111,7 @@ start(#body{attrs = Attrs} = Body, IP, SID) -> XMPPDomain = get_attr(to, Attrs), - SupervisorProc = gen_mod:get_module_proc(XMPPDomain, ?PROCNAME), + SupervisorProc = gen_mod:get_module_proc(XMPPDomain, mod_bosh), case catch supervisor:start_child(SupervisorProc, [Body, IP, SID]) of @@ -137,18 +125,18 @@ start(#body{attrs = Attrs} = Body, IP, SID) -> end. start(StateName, State) -> - (?GEN_FSM):start_link(?MODULE, [StateName, State], + p1_fsm:start_link(?MODULE, [StateName, State], ?FSMOPTS). start_link(Body, IP, SID) -> - (?GEN_FSM):start_link(?MODULE, [Body, IP, SID], + p1_fsm:start_link(?MODULE, [Body, IP, SID], ?FSMOPTS). send({http_bind, FsmRef, IP}, Packet) -> send_xml({http_bind, FsmRef, IP}, Packet). send_xml({http_bind, FsmRef, _IP}, Packet) -> - case catch (?GEN_FSM):sync_send_all_state_event(FsmRef, + case catch p1_fsm:sync_send_all_state_event(FsmRef, {send_xml, Packet}, ?SEND_TIMEOUT) of @@ -160,12 +148,12 @@ send_xml({http_bind, FsmRef, _IP}, Packet) -> setopts({http_bind, FsmRef, _IP}, Opts) -> case lists:member({active, once}, Opts) of true -> - (?GEN_FSM):send_all_state_event(FsmRef, + p1_fsm:send_all_state_event(FsmRef, {activate, self()}); _ -> case lists:member({active, false}, Opts) of true -> - case catch (?GEN_FSM):sync_send_all_state_event(FsmRef, + case catch p1_fsm:sync_send_all_state_event(FsmRef, deactivate_socket) of {'EXIT', _} -> {error, einval}; @@ -177,37 +165,25 @@ setopts({http_bind, FsmRef, _IP}, Opts) -> controlling_process(_Socket, _Pid) -> ok. -custom_receiver({http_bind, FsmRef, _IP}) -> - {receiver, ?MODULE, FsmRef}. - -become_controller(FsmRef, C2SPid) -> - (?GEN_FSM):send_all_state_event(FsmRef, - {become_controller, C2SPid}). - -change_controller({http_bind, FsmRef, _IP}, C2SPid) -> - become_controller(FsmRef, C2SPid). - -reset_stream({http_bind, _FsmRef, _IP}) -> ok. +reset_stream({http_bind, _FsmRef, _IP} = Socket) -> + Socket. change_shaper({http_bind, FsmRef, _IP}, Shaper) -> - (?GEN_FSM):send_all_state_event(FsmRef, - {change_shaper, Shaper}). - -monitor({http_bind, FsmRef, _IP}) -> - erlang:monitor(process, FsmRef). + p1_fsm:send_all_state_event(FsmRef, {change_shaper, Shaper}). close({http_bind, FsmRef, _IP}) -> - catch (?GEN_FSM):sync_send_all_state_event(FsmRef, + catch p1_fsm:sync_send_all_state_event(FsmRef, close). sockname(_Socket) -> {ok, {{0, 0, 0, 0}, 0}}. peername({http_bind, _FsmRef, IP}) -> {ok, IP}. -migrate(FsmRef, Node, After) when node(FsmRef) == node() -> - catch erlang:send_after(After, FsmRef, {migrate, Node}); -migrate(_FsmRef, _Node, _After) -> - ok. +get_transport(_Socket) -> + http_bind. + +get_owner({http_bind, FsmRef, _IP}) -> + FsmRef. process_request(Data, IP, Type) -> Opts1 = ejabberd_c2s_config:get_c2s_limits(), @@ -269,7 +245,7 @@ process_request(Data, IP, Type) -> end. process_request(Pid, Req, _IP, Type) -> - case catch (?GEN_FSM):sync_send_event(Pid, Req, + case catch p1_fsm:sync_send_event(Pid, Req, infinity) of #body{} = Resp -> bosh_response(Resp, Type); @@ -294,61 +270,53 @@ init([#body{attrs = Attrs}, IP, SID]) -> Opts1 = ejabberd_c2s_config:get_c2s_limits(), Opts2 = [{xml_socket, true} | Opts1], Shaper = none, - ShaperState = shaper:new(Shaper), + ShaperState = ejabberd_shaper:new(Shaper), Socket = make_socket(self(), IP), XMPPVer = get_attr('xmpp:version', Attrs), XMPPDomain = get_attr(to, Attrs), - {InBuf, Opts} = case gen_mod:get_module_opt( - XMPPDomain, - mod_bosh, prebind, - fun(B) when is_boolean(B) -> B end, - false) of + {InBuf, Opts} = case mod_bosh_opt:prebind(XMPPDomain) of true -> JID = make_random_jid(XMPPDomain), - {buf_new(), [{jid, JID} | Opts2]}; + {buf_new(XMPPDomain), [{jid, JID} | Opts2]}; false -> {buf_in([make_xmlstreamstart(XMPPDomain, XMPPVer)], - buf_new()), + buf_new(XMPPDomain)), Opts2} end, - ejabberd_socket:start(ejabberd_c2s, ?MODULE, Socket, - Opts), - Inactivity = gen_mod:get_module_opt(XMPPDomain, - mod_bosh, max_inactivity, - fun(I) when is_integer(I), I>0 -> I end, - ?DEFAULT_INACTIVITY), - MaxConcat = gen_mod:get_module_opt(XMPPDomain, mod_bosh, max_concat, - fun(unlimited) -> unlimited; - (N) when is_integer(N), N>0 -> N - end, unlimited), - State = #state{host = XMPPDomain, sid = SID, ip = IP, - xmpp_ver = XMPPVer, el_ibuf = InBuf, - max_concat = MaxConcat, el_obuf = buf_new(), - inactivity_timeout = Inactivity, - shaper_state = ShaperState}, - NewState = restart_inactivity_timer(State), - mod_bosh:open_session(SID, self()), - {ok, wait_for_session, NewState}; -init([StateName, State]) -> - mod_bosh:open_session(State#state.sid, self()), - case State#state.c2s_pid of - C2SPid when is_pid(C2SPid) -> - NewSocket = make_socket(self(), State#state.ip), - C2SPid ! {change_socket, NewSocket}, - NewState = restart_inactivity_timer(State), - {ok, StateName, NewState}; - _ -> {stop, normal} + case ejabberd_c2s:start(?MODULE, Socket, [{receiver, self()}|Opts]) of + {ok, C2SPid} -> + ejabberd_c2s:accept(C2SPid), + Inactivity = mod_bosh_opt:max_inactivity(XMPPDomain) div 1000, + MaxConcat = mod_bosh_opt:max_concat(XMPPDomain), + ShapedReceivers = buf_new(XMPPDomain, ?MAX_SHAPED_REQUESTS_QUEUE_LEN), + State = #state{host = XMPPDomain, sid = SID, ip = IP, + xmpp_ver = XMPPVer, el_ibuf = InBuf, + max_concat = MaxConcat, el_obuf = buf_new(XMPPDomain), + inactivity_timeout = Inactivity, + shaped_receivers = ShapedReceivers, + shaper_state = ShaperState}, + NewState = restart_inactivity_timer(State), + case mod_bosh:open_session(SID, self()) of + ok -> + {ok, wait_for_session, NewState}; + {error, Reason} -> + {stop, Reason} + end; + {error, Reason} -> + {stop, Reason}; + ignore -> + ignore end. -wait_for_session(_Event, State) -> - ?ERROR_MSG("unexpected event in 'wait_for_session': ~p", - [_Event]), +wait_for_session(Event, State) -> + ?ERROR_MSG("Unexpected event in 'wait_for_session': ~p", + [Event]), {next_state, wait_for_session, State}. wait_for_session(#body{attrs = Attrs} = Req, From, State) -> RID = get_attr(rid, Attrs), - ?DEBUG("got request:~n** RequestID: ~p~n** Request: " + ?DEBUG("Got request:~n** RequestID: ~p~n** Request: " "~p~n** From: ~p~n** State: ~p", [RID, Req, From, State]), Wait = min(get_attr(wait, Attrs, undefined), @@ -358,14 +326,11 @@ wait_for_session(#body{attrs = Attrs} = Req, From, NewKey = get_attr(newkey, Attrs), Type = get_attr(type, Attrs), Requests = Hold + 1, - {PollTime, Polling} = if Wait == 0, Hold == 0 -> - {p1_time_compat:timestamp(), [{polling, ?DEFAULT_POLLING}]}; - true -> {undefined, []} - end, - MaxPause = gen_mod:get_module_opt(State#state.host, - mod_bosh, max_pause, - fun(I) when is_integer(I), I>0 -> I end, - ?DEFAULT_MAXPAUSE), + PollTime = if + Wait == 0, Hold == 0 -> erlang:timestamp(); + true -> undefined + end, + MaxPause = mod_bosh_opt:max_pause(State#state.host) div 1000, Resp = #body{attrs = [{sid, State#state.sid}, {wait, Wait}, {ver, ?BOSH_VERSION}, {polling, ?DEFAULT_POLLING}, @@ -373,10 +338,9 @@ wait_for_session(#body{attrs = Attrs} = Req, From, {hold, Hold}, {'xmpp:restartlogic', true}, {requests, Requests}, {secure, true}, {maxpause, MaxPause}, {'xmlns:xmpp', ?NS_BOSH}, - {'xmlns:stream', ?NS_STREAM}, {from, State#state.host} - | Polling]}, + {'xmlns:stream', ?NS_STREAM}, {from, State#state.host}]}, {ShaperState, _} = - shaper:update(State#state.shaper_state, Req#body.size), + ejabberd_shaper:update(State#state.shaper_state, Req#body.size), State1 = State#state{wait_timeout = Wait, prev_rid = RID, prev_key = NewKey, prev_poll = PollTime, shaper_state = ShaperState, @@ -386,46 +350,53 @@ wait_for_session(#body{attrs = Attrs} = Req, From, {State3, RespEls} = get_response_els(State2), State4 = stop_inactivity_timer(State3), case RespEls of - [] -> - State5 = restart_wait_timer(State4), - Receivers = gb_trees:insert(RID, {From, Resp}, - State5#state.receivers), - {next_state, active, - State5#state{receivers = Receivers}}; - _ -> - reply_next_state(State4, Resp#body{els = RespEls}, RID, - From) + [{xmlstreamstart, _, _} = El1] -> + OutBuf = buf_in([El1], State4#state.el_obuf), + State5 = restart_wait_timer(State4), + Receivers = gb_trees:insert(RID, {From, Resp}, + State5#state.receivers), + {next_state, active, + State5#state{receivers = Receivers, el_obuf = OutBuf}}; + [] -> + State5 = restart_wait_timer(State4), + Receivers = gb_trees:insert(RID, {From, Resp}, + State5#state.receivers), + {next_state, active, + State5#state{receivers = Receivers}}; + _ -> + reply_next_state(State4, Resp#body{els = RespEls}, RID, + From) end; -wait_for_session(_Event, _From, State) -> - ?ERROR_MSG("unexpected sync event in 'wait_for_session': ~p", - [_Event]), +wait_for_session(Event, _From, State) -> + ?ERROR_MSG("Unexpected sync event in 'wait_for_session': ~p", + [Event]), {reply, {error, badarg}, wait_for_session, State}. active({#body{} = Body, From}, State) -> active1(Body, From, State); -active(_Event, State) -> - ?ERROR_MSG("unexpected event in 'active': ~p", - [_Event]), +active(Event, State) -> + ?ERROR_MSG("Unexpected event in 'active': ~p", + [Event]), {next_state, active, State}. active(#body{attrs = Attrs, size = Size} = Req, From, State) -> - ?DEBUG("got request:~n** Request: ~p~n** From: " + ?DEBUG("Got request:~n** Request: ~p~n** From: " "~p~n** State: ~p", [Req, From, State]), {ShaperState, Pause} = - shaper:update(State#state.shaper_state, Size), + ejabberd_shaper:update(State#state.shaper_state, Size), State1 = State#state{shaper_state = ShaperState}, if Pause > 0 -> - QLen = queue:len(State1#state.shaped_receivers), - if QLen < (?MAX_SHAPED_REQUESTS_QUEUE_LEN) -> - TRef = start_shaper_timer(Pause), - Q = queue:in({TRef, From, Req}, - State1#state.shaped_receivers), - State2 = stop_inactivity_timer(State1), - {next_state, active, - State2#state{shaped_receivers = Q}}; - true -> + TRef = start_shaper_timer(Pause), + try p1_queue:in({TRef, From, Req}, + State1#state.shaped_receivers) of + Q -> + State2 = stop_inactivity_timer(State1), + {next_state, active, + State2#state{shaped_receivers = Q}} + catch error:full -> + misc:cancel_timer(TRef), RID = get_attr(rid, Attrs), reply_stop(State1, #body{http_reason = <<"Too many requests">>, @@ -437,9 +408,9 @@ active(#body{attrs = Attrs, size = Size} = Req, From, end; true -> active1(Req, From, State1) end; -active(_Event, _From, State) -> - ?ERROR_MSG("unexpected sync event in 'active': ~p", - [_Event]), +active(Event, _From, State) -> + ?ERROR_MSG("Unexpected sync event in 'active': ~p", + [Event]), {reply, {error, badarg}, active, State}. active1(#body{attrs = Attrs} = Req, From, State) -> @@ -469,7 +440,7 @@ active1(#body{attrs = Attrs} = Req, From, State) -> {next_state, active, do_reply(State, From, PrevBody, RID)}; none -> - State1 = drop_holding_receiver(State), + State1 = drop_holding_receiver(State, RID), State2 = stop_inactivity_timer(State1), State3 = restart_wait_timer(State2), Receivers = gb_trees:insert(RID, {From, Req}, @@ -508,7 +479,7 @@ active1(#body{attrs = Attrs} = Req, From, State) -> Pause = get_attr(pause, Attrs, undefined), NewPoll = case State#state.prev_poll of undefined -> undefined; - _ -> p1_time_compat:timestamp() + _ -> erlang:timestamp() end, State5 = State4#state{prev_poll = NewPoll, prev_key = NewKey}, @@ -539,18 +510,16 @@ active1(#body{attrs = Attrs} = Req, From, State) -> end end. -handle_event({become_controller, C2SPid}, StateName, +handle_event({activate, C2SPid}, StateName, State) -> State1 = route_els(State#state{c2s_pid = C2SPid}), {next_state, StateName, State1}; handle_event({change_shaper, Shaper}, StateName, State) -> - NewShaperState = shaper:new(Shaper), - {next_state, StateName, - State#state{shaper_state = NewShaperState}}; -handle_event(_Event, StateName, State) -> - ?ERROR_MSG("unexpected event in '~s': ~p", - [StateName, _Event]), + {next_state, StateName, State#state{shaper_state = Shaper}}; +handle_event(Event, StateName, State) -> + ?ERROR_MSG("Unexpected event in '~ts': ~p", + [StateName, Event]), {next_state, StateName, State}. handle_sync_event({send_xml, @@ -572,11 +541,11 @@ handle_sync_event({send_xml, El}, _From, StateName, reply(State2, Body#body{els = Els}, State2#state.prev_rid, From)}; none -> - State2 = case queue:out(State1#state.shaped_receivers) + State2 = case p1_queue:out(State1#state.shaped_receivers) of {{value, {TRef, From, Body}}, Q} -> - cancel_timer(TRef), - (?GEN_FSM):send_event(self(), {Body, From}), + misc:cancel_timer(TRef), + p1_fsm:send_event(self(), {Body, From}), State1#state{shaped_receivers = Q}; _ -> State1 end, @@ -588,22 +557,23 @@ handle_sync_event(deactivate_socket, _From, StateName, StateData) -> {reply, ok, StateName, StateData#state{c2s_pid = undefined}}; -handle_sync_event(_Event, _From, StateName, State) -> - ?ERROR_MSG("unexpected sync event in '~s': ~p", - [StateName, _Event]), +handle_sync_event(Event, _From, StateName, State) -> + ?ERROR_MSG("Unexpected sync event in '~ts': ~p", + [StateName, Event]), {reply, {error, badarg}, StateName, State}. handle_info({timeout, TRef, wait_timeout}, StateName, #state{wait_timer = TRef} = State) -> - {next_state, StateName, drop_holding_receiver(State)}; + State2 = State#state{wait_timer = undefined}, + {next_state, StateName, drop_holding_receiver(State2)}; handle_info({timeout, TRef, inactive}, _StateName, #state{inactivity_timer = TRef} = State) -> {stop, normal, State}; handle_info({timeout, TRef, shaper_timeout}, StateName, State) -> - case queue:out(State#state.shaped_receivers) of + case p1_queue:out(State#state.shaped_receivers) of {{value, {TRef, From, Req}}, Q} -> - (?GEN_FSM):send_event(self(), {Req, From}), + p1_fsm:send_event(self(), {Req, From}), {next_state, StateName, State#state{shaped_receivers = Q}}; {{value, _}, _} -> @@ -613,29 +583,16 @@ handle_info({timeout, TRef, shaper_timeout}, StateName, {stop, normal, State}; _ -> {next_state, StateName, State} end; -handle_info({migrate, Node}, StateName, State) -> - if Node /= node() -> - NewState = bounce_receivers(State, migrated), - {migrate, NewState, - {Node, ?MODULE, start, [StateName, NewState]}, 0}; - true -> {next_state, StateName, State} - end; -handle_info(_Info, StateName, State) -> - ?ERROR_MSG("unexpected info:~n** Msg: ~p~n** StateName: ~p", - [_Info, StateName]), +handle_info(Info, StateName, State) -> + ?ERROR_MSG("Unexpected info:~n** Msg: ~p~n** StateName: ~p", + [Info, StateName]), {next_state, StateName, State}. -terminate({migrated, ClonePid}, _StateName, State) -> - ?INFO_MSG("Migrating session \"~s\" (c2s_pid = " - "~p) to ~p on node ~p", - [State#state.sid, State#state.c2s_pid, ClonePid, - node(ClonePid)]), - mod_bosh:close_session(State#state.sid); terminate(_Reason, _StateName, State) -> mod_bosh:close_session(State#state.sid), case State#state.c2s_pid of C2SPid when is_pid(C2SPid) -> - (?GEN_FSM):send_event(C2SPid, closed); + p1_fsm:send_event(C2SPid, closed); _ -> ok end, bounce_receivers(State, closed), @@ -646,15 +603,19 @@ code_change(_OldVsn, StateName, State, _Extra) -> print_state(State) -> State. -route_els(#state{el_ibuf = Buf} = State) -> - route_els(State#state{el_ibuf = buf_new()}, - buf_to_list(Buf)). +route_els(#state{el_ibuf = Buf, c2s_pid = C2SPid} = State) -> + NewBuf = p1_queue:dropwhile( + fun(El) -> + p1_fsm:send_event(C2SPid, El), + true + end, Buf), + State#state{el_ibuf = NewBuf}. route_els(State, Els) -> case State#state.c2s_pid of C2SPid when is_pid(C2SPid) -> lists:foreach(fun (El) -> - (?GEN_FSM):send_event(C2SPid, El) + p1_fsm:send_event(C2SPid, El) end, Els), State; @@ -677,7 +638,7 @@ reply(State, Body, RID, From) -> case catch gb_trees:take_smallest(Receivers) of {NextRID, {From1, Req}, Receivers1} when NextRID == RID + 1 -> - (?GEN_FSM):send_event(self(), {Req, From1}), + p1_fsm:send_event(self(), {Req, From1}), State2#state{receivers = Receivers1}; _ -> State2#state{receivers = Receivers} end. @@ -701,22 +662,24 @@ reply_stop(State, Body, From, RID) -> {stop, normal, do_reply(State, From, Body, RID)}. drop_holding_receiver(State) -> - RID = State#state.prev_rid, + drop_holding_receiver(State, State#state.prev_rid). +drop_holding_receiver(State, RID) -> case gb_trees:lookup(RID, State#state.receivers) of - {value, {From, Body}} -> - State1 = restart_inactivity_timer(State), - Receivers = gb_trees:delete_any(RID, - State1#state.receivers), - State2 = State1#state{receivers = Receivers}, - do_reply(State2, From, Body, RID); - none -> State + {value, {From, Body}} -> + State1 = restart_inactivity_timer(State), + Receivers = gb_trees:delete_any(RID, + State1#state.receivers), + State2 = State1#state{receivers = Receivers}, + do_reply(State2, From, Body, RID); + none -> + restart_inactivity_timer(State) end. do_reply(State, From, Body, RID) -> - ?DEBUG("send reply:~n** RequestID: ~p~n** Reply: " + ?DEBUG("Send reply:~n** RequestID: ~p~n** Reply: " "~p~n** To: ~p~n** State: ~p", [RID, Body, From, State]), - (?GEN_FSM):reply(From, Body), + p1_fsm:reply(From, Body), Responses = gb_trees:delete_any(RID, State#state.responses), Responses1 = case gb_trees:size(Responses) of @@ -727,59 +690,53 @@ do_reply(State, From, Body, RID) -> Responses2 = gb_trees:insert(RID, Body, Responses1), State#state{responses = Responses2}. -bounce_receivers(State, Reason) -> +bounce_receivers(State, _Reason) -> Receivers = gb_trees:to_list(State#state.receivers), ShapedReceivers = lists:map(fun ({_, From, #body{attrs = Attrs} = Body}) -> RID = get_attr(rid, Attrs), {RID, {From, Body}} end, - queue:to_list(State#state.shaped_receivers)), - lists:foldl(fun ({RID, {From, Body}}, AccState) -> - NewBody = if Reason == closed -> - #body{http_reason = - <<"Session closed">>, - attrs = - [{type, <<"terminate">>}, - {condition, - <<"other-request">>}]}; - Reason == migrated -> - Body#body{http_reason = - <<"Session migrated">>} - end, + p1_queue:to_list(State#state.shaped_receivers)), + lists:foldl(fun ({RID, {From, _Body}}, AccState) -> + NewBody = #body{http_reason = + <<"Session closed">>, + attrs = + [{type, <<"terminate">>}, + {condition, + <<"other-request">>}]}, do_reply(AccState, From, NewBody, RID) end, State, Receivers ++ ShapedReceivers). bounce_els_from_obuf(State) -> - lists:foreach(fun ({xmlstreamelement, El}) -> - case El of - #xmlel{name = Name, attrs = Attrs} - when Name == <<"presence">>; - Name == <<"message">>; - Name == <<"iq">> -> - FromS = fxml:get_attr_s(<<"from">>, Attrs), - ToS = fxml:get_attr_s(<<"to">>, Attrs), - case {jid:from_string(FromS), - jid:from_string(ToS)} - of - {#jid{} = From, #jid{} = To} -> - ejabberd_router:route(From, To, El); - _ -> ok - end; - _ -> ok - end; - (_) -> ok - end, - buf_to_list(State#state.el_obuf)). + Opts = ejabberd_config:codec_options(), + p1_queue:foreach( + fun({xmlstreamelement, El}) -> + try xmpp:decode(El, ?NS_CLIENT, Opts) of + Pkt when ?is_stanza(Pkt) -> + case {xmpp:get_from(Pkt), xmpp:get_to(Pkt)} of + {#jid{}, #jid{}} -> + ejabberd_router:route(Pkt); + _ -> + ok + end; + _ -> + ok + catch _:{xmpp_codec, _} -> + ok + end; + (_) -> + ok + end, State#state.el_obuf). is_valid_key(<<"">>, <<"">>) -> true; is_valid_key(PrevKey, Key) -> - p1_sha:sha(Key) == PrevKey. + str:sha(Key) == PrevKey. is_overactivity(undefined) -> false; is_overactivity(PrevPoll) -> - PollPeriod = timer:now_diff(p1_time_compat:timestamp(), PrevPoll) div + PollPeriod = timer:now_diff(erlang:timestamp(), PrevPoll) div 1000000, if PollPeriod < (?DEFAULT_POLLING) -> true; true -> false @@ -931,7 +888,9 @@ decode_body(Data, Size, Type) -> end. decode(Data, xml) -> - fxml_stream:parse_element(Data). + fxml_stream:parse_element(Data); +decode(Data, json) -> + Data. attrs_to_body_attrs(Attrs) -> lists:foldl(fun (_, {error, Reason}) -> {error, Reason}; @@ -990,7 +949,7 @@ bosh_response(Body, Type) -> encode_body(Body, Type)}. bosh_response_with_msg(Body, Type, RcvBody) -> - ?DEBUG("send error reply:~p~n** Receiced body: ~p", + ?DEBUG("Send error reply:~p~n** Receiced body: ~p", [Body, RcvBody]), bosh_response(Body, Type). @@ -1001,7 +960,7 @@ http_error(Status, Reason, Type) -> end, {Status, Reason, ?HEADER(CType), <<"">>}. -make_sid() -> p1_sha:sha(randoms:get_string()). +make_sid() -> str:sha(p1_rand:get_string()). -compile({no_auto_import, [{min, 2}]}). @@ -1031,32 +990,30 @@ get_attr(Attr, Attrs, Default) -> _ -> Default end. -buf_new() -> queue:new(). +buf_new(Host) -> + buf_new(Host, unlimited). + +buf_new(Host, Limit) -> + QueueType = mod_bosh_opt:queue_type(Host), + p1_queue:new(QueueType, Limit). buf_in(Xs, Buf) -> - lists:foldl(fun (X, Acc) -> queue:in(X, Acc) end, Buf, - Xs). + lists:foldl(fun p1_queue:in/2, Buf, Xs). buf_out(Buf, Num) when is_integer(Num), Num > 0 -> buf_out(Buf, Num, []); -buf_out(Buf, _) -> {queue:to_list(Buf), buf_new()}. +buf_out(Buf, _) -> {p1_queue:to_list(Buf), p1_queue:clear(Buf)}. buf_out(Buf, 0, Els) -> {lists:reverse(Els), Buf}; buf_out(Buf, I, Els) -> - case queue:out(Buf) of + case p1_queue:out(Buf) of {{value, El}, NewBuf} -> buf_out(NewBuf, I - 1, [El | Els]); {empty, _} -> buf_out(Buf, 0, Els) end. -buf_to_list(Buf) -> queue:to_list(Buf). - -cancel_timer(TRef) when is_reference(TRef) -> - (?GEN_FSM):cancel_timer(TRef); -cancel_timer(_) -> false. - restart_timer(TRef, Timeout, Msg) -> - cancel_timer(TRef), + misc:cancel_timer(TRef), erlang:start_timer(timer:seconds(Timeout), self(), Msg). restart_inactivity_timer(#state{inactivity_timeout = @@ -1073,7 +1030,7 @@ restart_inactivity_timer(#state{inactivity_timer = stop_inactivity_timer(#state{inactivity_timer = TRef} = State) -> - cancel_timer(TRef), + misc:cancel_timer(TRef), State#state{inactivity_timer = undefined}. restart_wait_timer(#state{wait_timer = TRef, @@ -1083,13 +1040,13 @@ restart_wait_timer(#state{wait_timer = TRef, State#state{wait_timer = NewTRef}. stop_wait_timer(#state{wait_timer = TRef} = State) -> - cancel_timer(TRef), State#state{wait_timer = undefined}. + misc:cancel_timer(TRef), State#state{wait_timer = undefined}. start_shaper_timer(Timeout) -> erlang:start_timer(Timeout, self(), shaper_timeout). make_random_jid(Host) -> - User = randoms:get_string(), - jid:make(User, Host, randoms:get_string()). + User = p1_rand:get_string(), + jid:make(User, Host, p1_rand:get_string()). make_socket(Pid, IP) -> {http_bind, Pid, IP}. diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 6d84d8d93..ef9312ef5 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -1,11 +1,8 @@ -%%%---------------------------------------------------------------------- -%%% File : ejabberd_c2s.erl -%%% Author : Alexey Shchepin -%%% Purpose : Serve C2S connection -%%% Created : 16 Nov 2002 by Alexey Shchepin +%%%------------------------------------------------------------------- +%%% Created : 8 Dec 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -21,1998 +18,997 @@ %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% -%%%---------------------------------------------------------------------- - +%%%------------------------------------------------------------------- -module(ejabberd_c2s). +-behaviour(xmpp_stream_in). +-behaviour(ejabberd_listener). --behaviour(ejabberd_config). +-protocol({rfc, 3920}). +-protocol({rfc, 3921}). +-protocol({rfc, 6120}). +-protocol({rfc, 6121}). +-protocol({xep, 138, '2.1', '1.1.0', "complete", ""}). --author('alexey@process-one.net'). +%% ejabberd_listener callbacks +-export([start/3, start_link/3, accept/1, listen_opt_type/1, listen_options/0]). +%% xmpp_stream_in callbacks +-export([init/1, handle_call/3, handle_cast/2, + handle_info/2, terminate/2, code_change/3]). +-export([tls_options/1, tls_required/1, tls_enabled/1, + allow_unencrypted_sasl2/1, compress_methods/1, bind/2, + sasl_mechanisms/2, get_password_fun/2, check_password_fun/2, + check_password_digest_fun/2, unauthenticated_stream_features/1, + authenticated_stream_features/1, handle_stream_start/2, + handle_stream_end/2, handle_unauthenticated_packet/2, + handle_authenticated_packet/2, handle_auth_success/4, + handle_auth_failure/4, handle_send/3, handle_recv/3, handle_cdata/2, + handle_unbinded_packet/2, inline_stream_features/1, + handle_sasl2_inline/2, handle_sasl2_inline_post/3, + handle_bind2_inline/2, handle_bind2_inline_post/3, sasl_options/1, + handle_sasl2_task_next/4, handle_sasl2_task_data/3, + get_fast_tokens_fun/2, fast_mechanisms/1]). +%% Hooks +-export([handle_unexpected_cast/2, handle_unexpected_call/3, + process_auth_result/3, c2s_handle_bind/1, + reject_unauthenticated_packet/2, process_closed/2, + process_terminated/2, process_info/2]). +%% API +-export([get_presence/1, set_presence/2, resend_presence/1, resend_presence/2, + open_session/1, call/3, cast/2, send/2, close/1, close/2, stop_async/1, + reply/2, copy_state/2, set_timeout/2, route/2, format_reason/2, + host_up/1, host_down/1, send_ws_ping/1, bounce_message_queue/2, + reset_vcard_xupdate_resend_presence/1]). --protocol({xep, 78, '2.5'}). --protocol({xep, 138, '2.0'}). --protocol({xep, 198, '1.3'}). --protocol({xep, 356, '7.1'}). - --update_info({update, 0}). - --define(GEN_FSM, p1_fsm). - --behaviour(?GEN_FSM). - -%% External exports --export([start/2, - stop/1, - start_link/2, - close/1, - send_text/2, - send_element/2, - socket_type/0, - get_presence/1, - get_last_presence/1, - get_aux_field/2, - set_aux_field/3, - del_aux_field/2, - get_subscription/2, - get_queued_stanzas/1, - get_csi_state/1, - set_csi_state/2, - get_resume_timeout/1, - set_resume_timeout/2, - send_filtered/5, - broadcast/4, - get_subscribed/1, - transform_listen_option/2]). - --export([init/1, wait_for_stream/2, wait_for_auth/2, - wait_for_feature_request/2, wait_for_bind/2, - wait_for_sasl_response/2, - wait_for_resume/2, session_established/2, - handle_event/3, handle_sync_event/4, code_change/4, - handle_info/3, terminate/3, print_state/1, opt_type/1]). - --include("ejabberd.hrl"). +-include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). - --include("xmpp.hrl"). -%%-include("legacy.hrl"). - --include("mod_privacy.hrl"). +-include("mod_roster.hrl"). +-include("translate.hrl"). -define(SETS, gb_sets). --define(DICT, dict). -%% pres_a contains all the presence available send (either through roster mechanism or directed). -%% Directed presence unavailable remove user from pres_a. --record(state, {socket, - sockmod, - socket_monitor, - xml_socket, - streamid, - sasl_state, - access, - shaper, - zlib = false, - tls = false, - tls_required = false, - tls_enabled = false, - tls_options = [], - authenticated = false, - jid, - user = <<"">>, server = <<"">>, resource = <<"">>, - sid, - pres_t = ?SETS:new(), - pres_f = ?SETS:new(), - pres_a = ?SETS:new(), - pres_last, - pres_timestamp, - privacy_list = #userlist{}, - conn = unknown, - auth_module = unknown, - ip, - aux_fields = [], - csi_state = active, - mgmt_state, - mgmt_xmlns, - mgmt_queue, - mgmt_max_queue, - mgmt_pending_since, - mgmt_timeout, - mgmt_max_timeout, - mgmt_ack_timeout, - mgmt_ack_timer, - mgmt_resend, - mgmt_stanzas_in = 0, - mgmt_stanzas_out = 0, - mgmt_stanzas_req = 0, - ask_offline = true, - lang = <<"">>}). - --type state_name() :: wait_for_stream | wait_for_auth | - wait_for_feature_request | wait_for_bind | - wait_for_sasl_response | wait_for_resume | - session_established. --type state() :: #state{}. --type fsm_stop() :: {stop, normal, state()}. --type fsm_next() :: {next_state, state_name(), state(), non_neg_integer()}. --type fsm_reply() :: {reply, any(), state_name(), state(), non_neg_integer()}. --type fsm_transition() :: fsm_stop() | fsm_next(). +-type state() :: xmpp_stream_in:state(). -export_type([state/0]). -%-define(DBGFSM, true). +%%%=================================================================== +%%% ejabberd_listener API +%%%=================================================================== +start(SockMod, Socket, Opts) -> + xmpp_stream_in:start(?MODULE, [{SockMod, Socket}, Opts], + ejabberd_config:fsm_limit_opts(Opts)). --ifdef(DBGFSM). +start_link(SockMod, Socket, Opts) -> + xmpp_stream_in:start_link(?MODULE, [{SockMod, Socket}, Opts], + ejabberd_config:fsm_limit_opts(Opts)). --define(FSMOPTS, [{debug, [trace]}]). +accept(Ref) -> + xmpp_stream_in:accept(Ref). --else. +%%%=================================================================== +%%% Common API +%%%=================================================================== +-spec call(pid(), term(), non_neg_integer() | infinity) -> term(). +call(Ref, Msg, Timeout) -> + xmpp_stream_in:call(Ref, Msg, Timeout). --define(FSMOPTS, []). +-spec cast(pid(), term()) -> ok. +cast(Ref, Msg) -> + xmpp_stream_in:cast(Ref, Msg). --endif. +reply(Ref, Reply) -> + xmpp_stream_in:reply(Ref, Reply). -%% This is the timeout to apply between event when starting a new -%% session: --define(C2S_OPEN_TIMEOUT, 60000). +-spec get_presence(pid()) -> presence(). +get_presence(Ref) -> + call(Ref, get_presence, 1000). --define(C2S_HIBERNATE_TIMEOUT, ejabberd_config:get_option(c2s_hibernate, fun(X) when is_integer(X); X == hibernate-> X end, 90000)). +-spec set_presence(pid(), presence()) -> ok. +set_presence(Ref, Pres) -> + call(Ref, {set_presence, Pres}, 1000). --define(STREAM_HEADER, - <<"">>). +-spec resend_presence(pid()) -> boolean(). +resend_presence(Pid) -> + resend_presence(Pid, undefined). --define(STREAM_TRAILER, <<"">>). +-spec resend_presence(pid(), jid() | undefined) -> boolean(). +resend_presence(Pid, To) -> + route(Pid, {resend_presence, To}). -%% XEP-0198: +-spec reset_vcard_xupdate_resend_presence(pid()) -> boolean(). +reset_vcard_xupdate_resend_presence(Pid) -> + route(Pid, reset_vcard_xupdate_resend_presence). --define(IS_STREAM_MGMT_PACKET(Pkt), - is_record(Pkt, sm_enable) or - is_record(Pkt, sm_resume) or - is_record(Pkt, sm_a) or - is_record(Pkt, sm_r)). +-spec close(pid()) -> ok; + (state()) -> state(). +close(Ref) -> + xmpp_stream_in:close(Ref). -%%%---------------------------------------------------------------------- -%%% API -%%%---------------------------------------------------------------------- -start(SockData, Opts) -> - ?GEN_FSM:start(ejabberd_c2s, - [SockData, Opts], - fsm_limit_opts(Opts) ++ ?FSMOPTS). +-spec close(pid(), atom()) -> ok. +close(Ref, Reason) -> + xmpp_stream_in:close(Ref, Reason). -start_link(SockData, Opts) -> - (?GEN_FSM):start_link(ejabberd_c2s, - [SockData, Opts], - fsm_limit_opts(Opts) ++ ?FSMOPTS). +-spec stop_async(pid()) -> ok. +stop_async(Pid) -> + xmpp_stream_in:stop_async(Pid). -socket_type() -> xml_stream. - -%% Return Username, Resource and presence information -get_presence(FsmRef) -> - (?GEN_FSM):sync_send_all_state_event(FsmRef, - {get_presence}, 1000). -get_last_presence(FsmRef) -> - (?GEN_FSM):sync_send_all_state_event(FsmRef, - {get_last_presence}, 1000). - --spec get_aux_field(any(), state()) -> {ok, any()} | error. -get_aux_field(Key, #state{aux_fields = Opts}) -> - case lists:keyfind(Key, 1, Opts) of - {_, Val} -> {ok, Val}; - false -> error +-spec send(pid(), xmpp_element()) -> ok; + (state(), xmpp_element()) -> state(). +send(Pid, Pkt) when is_pid(Pid) -> + xmpp_stream_in:send(Pid, Pkt); +send(#{lserver := LServer} = State, Pkt) -> + Pkt1 = fix_from_to(Pkt, State), + case ejabberd_hooks:run_fold(c2s_filter_send, LServer, {Pkt1, State}, []) of + {drop, State1} -> State1; + {Pkt2, State1} -> xmpp_stream_in:send(State1, Pkt2) end. --spec set_aux_field(any(), any(), state()) -> state(). -set_aux_field(Key, Val, - #state{aux_fields = Opts} = State) -> - Opts1 = lists:keydelete(Key, 1, Opts), - State#state{aux_fields = [{Key, Val} | Opts1]}. - --spec del_aux_field(any(), state()) -> state(). -del_aux_field(Key, #state{aux_fields = Opts} = State) -> - Opts1 = lists:keydelete(Key, 1, Opts), - State#state{aux_fields = Opts1}. - --spec get_subscription(jid() | ljid(), state()) -> both | from | to | none. -get_subscription(From = #jid{}, StateData) -> - get_subscription(jid:tolower(From), StateData); -get_subscription(LFrom, StateData) -> - LBFrom = setelement(3, LFrom, <<"">>), - F = (?SETS):is_element(LFrom, StateData#state.pres_f) - orelse - (?SETS):is_element(LBFrom, StateData#state.pres_f), - T = (?SETS):is_element(LFrom, StateData#state.pres_t) - orelse - (?SETS):is_element(LBFrom, StateData#state.pres_t), - if F and T -> both; - F -> from; - T -> to; - true -> none +-spec send_error(state(), xmpp_element(), stanza_error()) -> state(). +send_error(#{lserver := LServer} = State, Pkt, Err) -> + case ejabberd_hooks:run_fold(c2s_filter_send, LServer, {Pkt, State}, []) of + {drop, State1} -> State1; + {Pkt1, State1} -> xmpp_stream_in:send_error(State1, Pkt1, Err) end. -get_queued_stanzas(#state{mgmt_queue = Queue} = StateData) -> - lists:map(fun({_N, Time, El}) -> - add_resent_delay_info(StateData, El, Time) - end, queue:to_list(Queue)). +-spec send_ws_ping(pid()) -> ok; + (state()) -> state(). +send_ws_ping(Ref) -> + xmpp_stream_in:send_ws_ping(Ref). -get_csi_state(#state{csi_state = CsiState}) -> - CsiState. +-spec route(pid(), term()) -> boolean(). +route(Pid, Term) -> + ejabberd_cluster:send(Pid, Term). -set_csi_state(#state{} = StateData, CsiState) -> - StateData#state{csi_state = CsiState}; -set_csi_state(FsmRef, CsiState) -> - FsmRef ! {set_csi_state, CsiState}. +-spec set_timeout(state(), timeout()) -> state(). +set_timeout(State, Timeout) -> + xmpp_stream_in:set_timeout(State, Timeout). -get_resume_timeout(#state{mgmt_timeout = Timeout}) -> - Timeout. +-spec host_up(binary()) -> ok. +host_up(Host) -> + ejabberd_hooks:add(c2s_closed, Host, ?MODULE, process_closed, 100), + ejabberd_hooks:add(c2s_terminated, Host, ?MODULE, + process_terminated, 100), + ejabberd_hooks:add(c2s_handle_bind, Host, ?MODULE, c2s_handle_bind, 100), + ejabberd_hooks:add(c2s_unauthenticated_packet, Host, ?MODULE, + reject_unauthenticated_packet, 100), + ejabberd_hooks:add(c2s_handle_info, Host, ?MODULE, + process_info, 100), + ejabberd_hooks:add(c2s_auth_result, Host, ?MODULE, + process_auth_result, 100), + ejabberd_hooks:add(c2s_handle_cast, Host, ?MODULE, + handle_unexpected_cast, 100), + ejabberd_hooks:add(c2s_handle_call, Host, ?MODULE, + handle_unexpected_call, 100). -set_resume_timeout(#state{} = StateData, Timeout) -> - StateData#state{mgmt_timeout = Timeout}; -set_resume_timeout(FsmRef, Timeout) -> - FsmRef ! {set_resume_timeout, Timeout}. +-spec host_down(binary()) -> ok. +host_down(Host) -> + ejabberd_hooks:delete(c2s_closed, Host, ?MODULE, process_closed, 100), + ejabberd_hooks:delete(c2s_terminated, Host, ?MODULE, + process_terminated, 100), + ejabberd_hooks:delete(c2s_handle_bind, Host, ?MODULE, c2s_handle_bind, 100), + ejabberd_hooks:delete(c2s_unauthenticated_packet, Host, ?MODULE, + reject_unauthenticated_packet, 100), + ejabberd_hooks:delete(c2s_handle_info, Host, ?MODULE, + process_info, 100), + ejabberd_hooks:delete(c2s_auth_result, Host, ?MODULE, + process_auth_result, 100), + ejabberd_hooks:delete(c2s_handle_cast, Host, ?MODULE, + handle_unexpected_cast, 100), + ejabberd_hooks:delete(c2s_handle_call, Host, ?MODULE, + handle_unexpected_call, 100). --spec send_filtered(pid(), binary(), jid(), jid(), stanza()) -> any(). -send_filtered(FsmRef, Feature, From, To, Packet) -> - FsmRef ! {send_filtered, Feature, From, To, Packet}. - --spec broadcast(pid(), any(), jid(), stanza()) -> any(). -broadcast(FsmRef, Type, From, Packet) -> - FsmRef ! {broadcast, Type, From, Packet}. - --spec stop(pid()) -> any(). -stop(FsmRef) -> (?GEN_FSM):send_event(FsmRef, stop). - --spec close(pid()) -> any(). -%% What is the difference between stop and close??? -close(FsmRef) -> (?GEN_FSM):send_event(FsmRef, closed). - -%%%---------------------------------------------------------------------- -%%% Callback functions from gen_fsm -%%%---------------------------------------------------------------------- - -init([{SockMod, Socket}, Opts]) -> - Access = gen_mod:get_opt(access, Opts, - fun acl:access_rules_validator/1, all), - Shaper = gen_mod:get_opt(shaper, Opts, - fun acl:shaper_rules_validator/1, none), - XMLSocket = case lists:keysearch(xml_socket, 1, Opts) of - {value, {_, XS}} -> XS; - _ -> false - end, - Zlib = proplists:get_bool(zlib, Opts), - StartTLS = proplists:get_bool(starttls, Opts), - StartTLSRequired = proplists:get_bool(starttls_required, Opts), - TLSEnabled = proplists:get_bool(tls, Opts), - TLS = StartTLS orelse - StartTLSRequired orelse TLSEnabled, - TLSOpts1 = lists:filter(fun ({certfile, _}) -> true; - ({ciphers, _}) -> true; - ({dhfile, _}) -> true; - (_) -> false - end, - Opts), - TLSOpts2 = case lists:keysearch(protocol_options, 1, Opts) of - {value, {_, O}} -> - [_|ProtocolOptions] = lists:foldl( - fun(X, Acc) -> X ++ Acc end, [], - [["|" | binary_to_list(Opt)] || Opt <- O, is_binary(Opt)] - ), - [{protocol_options, iolist_to_binary(ProtocolOptions)} | TLSOpts1]; - _ -> TLSOpts1 - end, - TLSOpts3 = case proplists:get_bool(tls_compression, Opts) of - false -> [compression_none | TLSOpts2]; - true -> TLSOpts2 - end, - TLSOpts = [verify_none | TLSOpts3], - StreamMgmtEnabled = proplists:get_value(stream_management, Opts, true), - StreamMgmtState = if StreamMgmtEnabled -> inactive; - true -> disabled - end, - MaxAckQueue = case proplists:get_value(max_ack_queue, Opts) of - Limit when is_integer(Limit), Limit > 0 -> Limit; - infinity -> infinity; - _ -> 1000 - end, - ResumeTimeout = case proplists:get_value(resume_timeout, Opts) of - RTimeo when is_integer(RTimeo), RTimeo >= 0 -> RTimeo; - _ -> 300 - end, - MaxResumeTimeout = case proplists:get_value(max_resume_timeout, Opts) of - Max when is_integer(Max), Max >= ResumeTimeout -> Max; - _ -> ResumeTimeout - end, - AckTimeout = case proplists:get_value(ack_timeout, Opts) of - ATimeo when is_integer(ATimeo), ATimeo > 0 -> ATimeo * 1000; - infinity -> undefined; - _ -> 60000 - end, - ResendOnTimeout = case proplists:get_value(resend_on_timeout, Opts) of - Resend when is_boolean(Resend) -> Resend; - if_offline -> if_offline; - _ -> false - end, - IP = peerip(SockMod, Socket), - Socket1 = if TLSEnabled andalso - SockMod /= ejabberd_frontend_socket -> - SockMod:starttls(Socket, TLSOpts); - true -> Socket - end, - SocketMonitor = SockMod:monitor(Socket1), - StateData = #state{socket = Socket1, sockmod = SockMod, - socket_monitor = SocketMonitor, - xml_socket = XMLSocket, zlib = Zlib, tls = TLS, - tls_required = StartTLSRequired, - tls_enabled = TLSEnabled, tls_options = TLSOpts, - sid = ejabberd_sm:make_sid(), streamid = new_id(), - access = Access, shaper = Shaper, ip = IP, - mgmt_state = StreamMgmtState, - mgmt_max_queue = MaxAckQueue, - mgmt_timeout = ResumeTimeout, - mgmt_max_timeout = MaxResumeTimeout, - mgmt_ack_timeout = AckTimeout, - mgmt_resend = ResendOnTimeout}, - {ok, wait_for_stream, StateData, ?C2S_OPEN_TIMEOUT}. - --spec get_subscribed(pid()) -> [ljid()]. -%% Return list of all available resources of contacts, -get_subscribed(FsmRef) -> - (?GEN_FSM):sync_send_all_state_event(FsmRef, - get_subscribed, 1000). - -wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) -> - try xmpp:decode(#xmlel{name = Name, attrs = Attrs}) of - #stream_start{xmlns = NS_CLIENT, stream_xmlns = NS_STREAM, - version = Version, lang = Lang} - when NS_CLIENT /= ?NS_CLIENT; NS_STREAM /= ?NS_STREAM -> - send_header(StateData, ?MYNAME, Version, Lang), - send_element(StateData, xmpp:serr_invalid_namespace()), - {stop, normal, StateData}; - #stream_start{lang = Lang, version = Version} when byte_size(Lang) > 35 -> - %% As stated in BCP47, 4.4.1: - %% Protocols or specifications that specify limited buffer sizes for - %% language tags MUST allow for language tags of at least 35 characters. - %% Do not store long language tag to avoid possible DoS/flood attacks - send_header(StateData, ?MYNAME, Version, ?MYLANG), - Txt = <<"Too long value of 'xml:lang' attribute">>, - send_element(StateData, - xmpp:serr_policy_violation(Txt, ?MYLANG)), - {stop, normal, StateData}; - #stream_start{to = undefined, lang = Lang, version = Version} -> - Txt = <<"Missing 'to' attribute">>, - send_header(StateData, ?MYNAME, Version, Lang), - send_element(StateData, - xmpp:serr_improper_addressing(Txt, Lang)), - {stop, normal, StateData}; - #stream_start{to = #jid{lserver = To}, lang = Lang, - version = Version} -> - Server = case StateData#state.server of - <<"">> -> To; - S -> S - end, - StreamVersion = case Version of - {1,0} -> {1,0}; - _ -> undefined - end, - IsBlacklistedIP = is_ip_blacklisted(StateData#state.ip, Lang), - case lists:member(Server, ?MYHOSTS) of - true when IsBlacklistedIP == false -> - change_shaper(StateData, jid:make(<<"">>, Server, <<"">>)), - case StreamVersion of - {1,0} -> - send_header(StateData, Server, {1,0}, ?MYLANG), - case StateData#state.authenticated of - false -> - TLS = StateData#state.tls, - TLSEnabled = StateData#state.tls_enabled, - TLSRequired = StateData#state.tls_required, - SASLState = cyrsasl:server_new( - <<"jabber">>, Server, <<"">>, [], - fun (U) -> - ejabberd_auth:get_password_with_authmodule( - U, Server) - end, - fun(U, AuthzId, P) -> - ejabberd_auth:check_password_with_authmodule( - U, AuthzId, Server, P) - end, - fun(U, AuthzId, P, D, DG) -> - ejabberd_auth:check_password_with_authmodule( - U, AuthzId, Server, P, D, DG) - end), - Mechs = - case TLSEnabled or not TLSRequired of - true -> - [#sasl_mechanisms{list = cyrsasl:listmech(Server)}]; - false -> - [] - end, - SockMod = - (StateData#state.sockmod):get_sockmod(StateData#state.socket), - Zlib = StateData#state.zlib, - CompressFeature = case Zlib andalso - ((SockMod == gen_tcp) orelse (SockMod == fast_tls)) of - true -> - [#compression{methods = [<<"zlib">>]}]; - _ -> - [] - end, - TLSFeature = - case (TLS == true) andalso - (TLSEnabled == false) andalso - (SockMod == gen_tcp) of - true -> - [#starttls{required = TLSRequired}]; - false -> - [] - end, - StreamFeatures1 = TLSFeature ++ CompressFeature ++ Mechs, - StreamFeatures = ejabberd_hooks:run_fold(c2s_stream_features, - Server, StreamFeatures1, [Server]), - send_element(StateData, - #stream_features{sub_els = StreamFeatures}), - fsm_next_state(wait_for_feature_request, - StateData#state{server = Server, - sasl_state = SASLState, - lang = Lang}); - _ -> - case StateData#state.resource of - <<"">> -> - RosterVersioningFeature = - ejabberd_hooks:run_fold(roster_get_versioning_feature, - Server, [], - [Server]), - StreamManagementFeature = - case stream_mgmt_enabled(StateData) of - true -> - [#feature_sm{xmlns = ?NS_STREAM_MGMT_2}, - #feature_sm{xmlns = ?NS_STREAM_MGMT_3}]; - false -> - [] - end, - SockMod = - (StateData#state.sockmod):get_sockmod( - StateData#state.socket), - Zlib = StateData#state.zlib, - CompressFeature = - case Zlib andalso - ((SockMod == gen_tcp) orelse (SockMod == fast_tls)) of - true -> - [#compression{methods = [<<"zlib">>]}]; - _ -> - [] - end, - StreamFeatures1 = - [#bind{}, #xmpp_session{optional = true}] - ++ - RosterVersioningFeature ++ - StreamManagementFeature ++ - CompressFeature ++ - ejabberd_hooks:run_fold(c2s_post_auth_features, - Server, [], [Server]), - StreamFeatures = ejabberd_hooks:run_fold(c2s_stream_features, - Server, StreamFeatures1, [Server]), - send_element(StateData, - #stream_features{sub_els = StreamFeatures}), - fsm_next_state(wait_for_bind, - StateData#state{server = Server, lang = Lang}); - _ -> - send_element(StateData, #stream_features{}), - fsm_next_state(session_established, - StateData#state{server = Server, lang = Lang}) - end - end; - _ -> - send_header(StateData, Server, StreamVersion, ?MYLANG), - if not StateData#state.tls_enabled and - StateData#state.tls_required -> - send_element( - StateData, - xmpp:serr_policy_violation( - <<"Use of STARTTLS required">>, Lang)), - {stop, normal, StateData}; - true -> - fsm_next_state(wait_for_auth, - StateData#state{server = Server, - lang = Lang}) - end - end; - true -> - IP = StateData#state.ip, - {true, LogReason, ReasonT} = IsBlacklistedIP, - ?INFO_MSG("Connection attempt from blacklisted IP ~s: ~s", - [jlib:ip_to_list(IP), LogReason]), - send_header(StateData, Server, StreamVersion, ?MYLANG), - send_element(StateData, xmpp:serr_policy_violation(ReasonT, Lang)), - {stop, normal, StateData}; - _ -> - send_header(StateData, ?MYNAME, StreamVersion, ?MYLANG), - send_element(StateData, xmpp:serr_host_unknown()), - {stop, normal, StateData} - end; - _ -> - send_header(StateData, ?MYNAME, {1,0}, ?MYLANG), - send_element(StateData, xmpp:serr_invalid_xml()), - {stop, normal, StateData} - catch _:{xmpp_codec, Why} -> - Txt = xmpp:format_error(Why), - send_header(StateData, ?MYNAME, {1,0}, ?MYLANG), - send_element(StateData, xmpp:serr_invalid_xml(Txt, ?MYLANG)), - {stop, normal, StateData} - end; -wait_for_stream(timeout, StateData) -> - {stop, normal, StateData}; -wait_for_stream({xmlstreamelement, _}, StateData) -> - send_element(StateData, xmpp:serr_not_well_formed()), - {stop, normal, StateData}; -wait_for_stream({xmlstreamend, _}, StateData) -> - send_element(StateData, xmpp:serr_not_well_formed()), - {stop, normal, StateData}; -wait_for_stream({xmlstreamerror, _}, StateData) -> - send_header(StateData, ?MYNAME, {1,0}, <<"">>), - send_element(StateData, xmpp:serr_not_well_formed()), - {stop, normal, StateData}; -wait_for_stream(closed, StateData) -> - {stop, normal, StateData}; -wait_for_stream(stop, StateData) -> - {stop, normal, StateData}. - -wait_for_auth({xmlstreamelement, #xmlel{} = El}, StateData) -> - decode_element(El, wait_for_auth, StateData); -wait_for_auth(Pkt, StateData) when ?IS_STREAM_MGMT_PACKET(Pkt) -> - fsm_next_state(wait_for_auth, dispatch_stream_mgmt(Pkt, StateData)); -wait_for_auth(#iq{type = get, sub_els = [#legacy_auth{}]} = IQ, StateData) -> - Auth = #legacy_auth{username = <<>>, password = <<>>, resource = <<>>}, - Res = case ejabberd_auth:plain_password_required(StateData#state.server) of - false -> - xmpp:make_iq_result(IQ, Auth#legacy_auth{digest = <<>>}); - true -> - xmpp:make_iq_result(IQ, Auth) - end, - send_element(StateData, Res), - fsm_next_state(wait_for_auth, StateData); -wait_for_auth(#iq{type = set, sub_els = [#legacy_auth{resource = <<"">>}]} = IQ, - StateData) -> - Lang = StateData#state.lang, - Txt = <<"No resource provided">>, - Err = xmpp:make_error(IQ, xmpp:err_not_acceptable(Txt, Lang)), - send_element(StateData, Err), - fsm_next_state(wait_for_auth, StateData); -wait_for_auth(#iq{type = set, sub_els = [#legacy_auth{username = U, - password = P0, - digest = D0, - resource = R}]} = IQ, - StateData) when is_binary(U), is_binary(R) -> - JID = jid:make(U, StateData#state.server, R), - case (JID /= error) andalso - acl:access_matches(StateData#state.access, - #{usr => jid:split(JID), ip => StateData#state.ip}, - StateData#state.server) == allow of - true -> - DGen = fun (PW) -> - p1_sha:sha(<<(StateData#state.streamid)/binary, PW/binary>>) - end, - P = if is_binary(P0) -> P0; true -> <<>> end, - D = if is_binary(D0) -> D0; true -> <<>> end, - case ejabberd_auth:check_password_with_authmodule( - U, U, StateData#state.server, P, D, DGen) of - {true, AuthModule} -> - ?INFO_MSG("(~w) Accepted legacy authentication for ~s by ~p from ~s", - [StateData#state.socket, - jid:to_string(JID), AuthModule, - ejabberd_config:may_hide_data(jlib:ip_to_list(StateData#state.ip))]), - ejabberd_hooks:run(c2s_auth_result, StateData#state.server, - [true, U, StateData#state.server, - StateData#state.ip]), - Conn = get_conn_type(StateData), - Info = [{ip, StateData#state.ip}, {conn, Conn}, - {auth_module, AuthModule}], - Res = xmpp:make_iq_result(IQ), - send_element(StateData, Res), - ejabberd_sm:open_session(StateData#state.sid, U, - StateData#state.server, R, - Info), - change_shaper(StateData, JID), - {Fs, Ts} = ejabberd_hooks:run_fold( - roster_get_subscription_lists, - StateData#state.server, - {[], []}, - [U, StateData#state.server]), - LJID = jid:tolower(jid:remove_resource(JID)), - Fs1 = [LJID | Fs], - Ts1 = [LJID | Ts], - PrivList = ejabberd_hooks:run_fold(privacy_get_user_list, - StateData#state.server, - #userlist{}, - [U, StateData#state.server]), - NewStateData = StateData#state{ - user = U, - resource = R, - jid = JID, - conn = Conn, - auth_module = AuthModule, - pres_f = (?SETS):from_list(Fs1), - pres_t = (?SETS):from_list(Ts1), - privacy_list = PrivList}, - fsm_next_state(session_established, NewStateData); - _ -> - ?INFO_MSG("(~w) Failed legacy authentication for ~s from ~s", - [StateData#state.socket, - jid:to_string(JID), - ejabberd_config:may_hide_data(jlib:ip_to_list(StateData#state.ip))]), - ejabberd_hooks:run(c2s_auth_result, StateData#state.server, - [false, U, StateData#state.server, - StateData#state.ip]), - Lang = StateData#state.lang, - Txt = <<"Legacy authentication failed">>, - Err = xmpp:make_error(IQ, xmpp:err_not_authorized(Txt, Lang)), - send_element(StateData, Err), - fsm_next_state(wait_for_auth, StateData) - end; - false when JID == error -> - ?INFO_MSG("(~w) Forbidden legacy authentication " - "for username '~s' with resource '~s'", - [StateData#state.socket, U, R]), - Err = xmpp:make_error(IQ, xmpp:err_jid_malformed()), - send_element(StateData, Err), - fsm_next_state(wait_for_auth, StateData); - false -> - ?INFO_MSG("(~w) Forbidden legacy authentication for ~s from ~s", - [StateData#state.socket, - jid:to_string(JID), - ejabberd_config:may_hide_data(jlib:ip_to_list(StateData#state.ip))]), - ejabberd_hooks:run(c2s_auth_result, StateData#state.server, - [false, U, StateData#state.server, - StateData#state.ip]), - Lang = StateData#state.lang, - Txt = <<"Legacy authentication forbidden">>, - Err = xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)), - send_element(StateData, Err), - fsm_next_state(wait_for_auth, StateData) - end; -wait_for_auth(timeout, StateData) -> - {stop, normal, StateData}; -wait_for_auth({xmlstreamend, _Name}, StateData) -> - {stop, normal, StateData}; -wait_for_auth({xmlstreamerror, _}, StateData) -> - send_element(StateData, xmpp:serr_not_well_formed()), - {stop, normal, StateData}; -wait_for_auth(closed, StateData) -> - {stop, normal, StateData}; -wait_for_auth(stop, StateData) -> - {stop, normal, StateData}; -wait_for_auth(Pkt, StateData) -> - process_unauthenticated_stanza(StateData, Pkt), - fsm_next_state(wait_for_auth, StateData). - -wait_for_feature_request({xmlstreamelement, El}, StateData) -> - decode_element(El, wait_for_feature_request, StateData); -wait_for_feature_request(Pkt, StateData) when ?IS_STREAM_MGMT_PACKET(Pkt) -> - fsm_next_state(wait_for_feature_request, - dispatch_stream_mgmt(Pkt, StateData)); -wait_for_feature_request(#sasl_auth{mechanism = Mech, - text = ClientIn}, - #state{tls_enabled = TLSEnabled, - tls_required = TLSRequired} = StateData) - when TLSEnabled or not TLSRequired -> - case cyrsasl:server_start(StateData#state.sasl_state, Mech, ClientIn) of - {ok, Props} -> - (StateData#state.sockmod):reset_stream(StateData#state.socket), - U = identity(Props), - AuthModule = proplists:get_value(auth_module, Props, undefined), - ?INFO_MSG("(~w) Accepted authentication for ~s by ~p from ~s", - [StateData#state.socket, U, AuthModule, - ejabberd_config:may_hide_data(jlib:ip_to_list(StateData#state.ip))]), - ejabberd_hooks:run(c2s_auth_result, StateData#state.server, - [true, U, StateData#state.server, - StateData#state.ip]), - send_element(StateData, #sasl_success{}), - fsm_next_state(wait_for_stream, - StateData#state{streamid = new_id(), - authenticated = true, - auth_module = AuthModule, - sasl_state = undefined, - user = U}); - {continue, ServerOut, NewSASLState} -> - send_element(StateData, #sasl_challenge{text = ServerOut}), - fsm_next_state(wait_for_sasl_response, - StateData#state{sasl_state = NewSASLState}); - {error, Error, Username} -> - ?INFO_MSG("(~w) Failed authentication for ~s@~s from ~s", - [StateData#state.socket, - Username, StateData#state.server, - ejabberd_config:may_hide_data(jlib:ip_to_list(StateData#state.ip))]), - ejabberd_hooks:run(c2s_auth_result, StateData#state.server, - [false, Username, StateData#state.server, - StateData#state.ip]), - send_element(StateData, #sasl_failure{reason = Error}), - fsm_next_state(wait_for_feature_request, StateData); - {error, Error} -> - send_element(StateData, #sasl_failure{reason = Error}), - fsm_next_state(wait_for_feature_request, StateData) - end; -wait_for_feature_request(#starttls{}, - #state{tls = true, tls_enabled = false} = StateData) -> - case (StateData#state.sockmod):get_sockmod(StateData#state.socket) of - gen_tcp -> - TLSOpts = case ejabberd_config:get_option( - {domain_certfile, StateData#state.server}, - fun iolist_to_binary/1) of - undefined -> - StateData#state.tls_options; - CertFile -> - lists:keystore(certfile, 1, - StateData#state.tls_options, - {certfile, CertFile}) - end, - Socket = StateData#state.socket, - BProceed = fxml:element_to_binary(xmpp:encode(#starttls_proceed{})), - TLSSocket = (StateData#state.sockmod):starttls(Socket, TLSOpts, BProceed), - fsm_next_state(wait_for_stream, - StateData#state{socket = TLSSocket, - streamid = new_id(), - tls_enabled = true}); - _ -> - Lang = StateData#state.lang, - Txt = <<"Unsupported TLS transport">>, - send_element(StateData, xmpp:serr_policy_violation(Txt, Lang)), - {stop, normal, StateData} - end; -wait_for_feature_request(#compress{} = Comp, StateData) -> - Zlib = StateData#state.zlib, - SockMod = (StateData#state.sockmod):get_sockmod(StateData#state.socket), - if Zlib == true, (SockMod == gen_tcp) or (SockMod == fast_tls) -> - process_compression_request(Comp, wait_for_feature_request, StateData); - true -> - send_element(StateData, #compress_failure{reason = 'setup-failed'}), - fsm_next_state(wait_for_feature_request, StateData) - end; -wait_for_feature_request(timeout, StateData) -> - {stop, normal, StateData}; -wait_for_feature_request({xmlstreamend, _Name}, - StateData) -> - {stop, normal, StateData}; -wait_for_feature_request({xmlstreamerror, _}, - StateData) -> - send_element(StateData, xmpp:serr_not_well_formed()), - {stop, normal, StateData}; -wait_for_feature_request(closed, StateData) -> - {stop, normal, StateData}; -wait_for_feature_request(stop, StateData) -> - {stop, normal, StateData}; -wait_for_feature_request(_Pkt, - #state{tls_required = TLSRequired, - tls_enabled = TLSEnabled} = StateData) - when TLSRequired and not TLSEnabled -> - Lang = StateData#state.lang, - Txt = <<"Use of STARTTLS required">>, - send_element(StateData, xmpp:serr_policy_violation(Txt, Lang)), - {stop, normal, StateData}; -wait_for_feature_request(Pkt, StateData) -> - process_unauthenticated_stanza(StateData, Pkt), - fsm_next_state(wait_for_feature_request, StateData). - -wait_for_sasl_response({xmlstreamelement, El}, StateData) -> - decode_element(El, wait_for_sasl_response, StateData); -wait_for_sasl_response(Pkt, StateData) when ?IS_STREAM_MGMT_PACKET(Pkt) -> - fsm_next_state(wait_for_sasl_response, - dispatch_stream_mgmt(Pkt, StateData)); -wait_for_sasl_response(#sasl_response{text = ClientIn}, StateData) -> - case cyrsasl:server_step(StateData#state.sasl_state, ClientIn) of - {ok, Props} -> - catch (StateData#state.sockmod):reset_stream(StateData#state.socket), - U = identity(Props), - AuthModule = proplists:get_value(auth_module, Props, <<>>), - ?INFO_MSG("(~w) Accepted authentication for ~s by ~p from ~s", - [StateData#state.socket, U, AuthModule, - ejabberd_config:may_hide_data(jlib:ip_to_list(StateData#state.ip))]), - ejabberd_hooks:run(c2s_auth_result, StateData#state.server, - [true, U, StateData#state.server, - StateData#state.ip]), - send_element(StateData, #sasl_success{}), - fsm_next_state(wait_for_stream, - StateData#state{streamid = new_id(), - authenticated = true, - auth_module = AuthModule, - sasl_state = undefined, - user = U}); - {ok, Props, ServerOut} -> - (StateData#state.sockmod):reset_stream(StateData#state.socket), - U = identity(Props), - AuthModule = proplists:get_value(auth_module, Props, undefined), - ?INFO_MSG("(~w) Accepted authentication for ~s by ~p from ~s", - [StateData#state.socket, U, AuthModule, - ejabberd_config:may_hide_data(jlib:ip_to_list(StateData#state.ip))]), - ejabberd_hooks:run(c2s_auth_result, StateData#state.server, - [true, U, StateData#state.server, - StateData#state.ip]), - send_element(StateData, #sasl_success{text = ServerOut}), - fsm_next_state(wait_for_stream, - StateData#state{streamid = new_id(), - authenticated = true, - auth_module = AuthModule, - sasl_state = undefined, - user = U}); - {continue, ServerOut, NewSASLState} -> - send_element(StateData, #sasl_challenge{text = ServerOut}), - fsm_next_state(wait_for_sasl_response, - StateData#state{sasl_state = NewSASLState}); - {error, Error, Username} -> - ?INFO_MSG("(~w) Failed authentication for ~s@~s from ~s", - [StateData#state.socket, - Username, StateData#state.server, - ejabberd_config:may_hide_data(jlib:ip_to_list(StateData#state.ip))]), - ejabberd_hooks:run(c2s_auth_result, StateData#state.server, - [false, Username, StateData#state.server, - StateData#state.ip]), - send_element(StateData, #sasl_failure{reason = Error}), - fsm_next_state(wait_for_feature_request, StateData); - {error, Error} -> - send_element(StateData, #sasl_failure{reason = Error}), - fsm_next_state(wait_for_feature_request, StateData) - end; -wait_for_sasl_response(timeout, StateData) -> - {stop, normal, StateData}; -wait_for_sasl_response({xmlstreamend, _Name}, - StateData) -> - {stop, normal, StateData}; -wait_for_sasl_response({xmlstreamerror, _}, - StateData) -> - send_element(StateData, xmpp:serr_not_well_formed()), - {stop, normal, StateData}; -wait_for_sasl_response(closed, StateData) -> - {stop, normal, StateData}; -wait_for_sasl_response(stop, StateData) -> - {stop, normal, StateData}; -wait_for_sasl_response(Pkt, StateData) -> - process_unauthenticated_stanza(StateData, Pkt), - fsm_next_state(wait_for_feature_request, StateData). - --spec resource_conflict_action(binary(), binary(), binary()) -> - {accept_resource, binary()} | closenew. -resource_conflict_action(U, S, R) -> - OptionRaw = case ejabberd_sm:is_existing_resource(U, S, R) of - true -> - ejabberd_config:get_option( - {resource_conflict, S}, - fun(setresource) -> setresource; - (closeold) -> closeold; - (closenew) -> closenew; - (acceptnew) -> acceptnew - end); - false -> - acceptnew - end, - Option = case OptionRaw of - setresource -> setresource; - closeold -> - acceptnew; %% ejabberd_sm will close old session - closenew -> closenew; - acceptnew -> acceptnew; - _ -> acceptnew %% default ejabberd behavior +%% Copies content of one c2s state to another. +%% This is needed for session migration from one pid to another. +-spec copy_state(state(), state()) -> state(). +copy_state(NewState, + #{jid := JID, resource := Resource, auth_module := AuthModule, + lserver := LServer, pres_a := PresA} = OldState) -> + State1 = case OldState of + #{pres_last := Pres, pres_timestamp := PresTS} -> + NewState#{pres_last => Pres, pres_timestamp => PresTS}; + _ -> + NewState end, - case Option of - acceptnew -> {accept_resource, R}; - closenew -> closenew; - setresource -> - Rnew = new_uniq_id(), - {accept_resource, Rnew} - end. + Conn = get_conn_type(State1), + State2 = State1#{jid => JID, resource => Resource, + conn => Conn, + auth_module => AuthModule, + pres_a => PresA}, + ejabberd_hooks:run_fold(c2s_copy_session, LServer, State2, [OldState]). --spec decode_element(xmlel(), state_name(), state()) -> fsm_transition(). -decode_element(#xmlel{} = El, StateName, StateData) -> - try case xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of - #iq{sub_els = [_], type = T} = Pkt when T == set; T == get -> - NewPkt = xmpp:decode_els( - Pkt, ?NS_CLIENT, - fun(SubEl) when StateName == session_established -> - case xmpp:get_ns(SubEl) of - ?NS_PRIVACY -> true; - ?NS_BLOCKING -> true; - _ -> false - end; - (SubEl) -> - xmpp:is_known_tag(SubEl) - end), - ?MODULE:StateName(NewPkt, StateData); - Pkt -> - ?MODULE:StateName(Pkt, StateData) - end - catch error:{xmpp_codec, Why} -> - NS = xmpp:get_ns(El), - fsm_next_state( - StateName, - case xmpp:is_stanza(El) of - true -> - Lang = xmpp:get_lang(El), - Txt = xmpp:format_error(Why), - send_error(StateData, El, xmpp:err_bad_request(Txt, Lang)); - false when NS == ?NS_STREAM_MGMT_2; NS == ?NS_STREAM_MGMT_3 -> - Err = #sm_failed{reason = 'bad-request', xmlns = NS}, - send_element(StateData, Err), - StateData; - false -> - StateData - end) - end. +-spec open_session(state()) -> {ok, state()} | state(). +open_session(#{user := U, server := S, resource := R, + sid := SID, ip := IP, auth_module := AuthModule} = State) -> + JID = jid:make(U, S, R), + State1 = change_shaper(State), + Conn = get_conn_type(State1), + State2 = State1#{conn => Conn, resource => R, jid => JID}, + Prio = case maps:get(pres_last, State, undefined) of + undefined -> undefined; + Pres -> get_priority_from_presence(Pres) + end, + Info = [{ip, IP}, {conn, Conn}, {auth_module, AuthModule}], + case State of + #{bind2_session_id := Tag} -> + ejabberd_sm:open_session(SID, U, S, R, Prio, Info, Tag); + _ -> + ejabberd_sm:open_session(SID, U, S, R, Prio, Info) + end, + xmpp_stream_in:establish(State2). -wait_for_bind({xmlstreamelement, El}, StateData) -> - decode_element(El, wait_for_bind, StateData); -wait_for_bind(#sm_resume{} = Pkt, StateData) -> - case handle_resume(StateData, Pkt) of - {ok, ResumedState} -> - fsm_next_state(session_established, ResumedState); - error -> - fsm_next_state(wait_for_bind, StateData) +%%%=================================================================== +%%% Hooks +%%%=================================================================== +process_info(#{lserver := LServer} = State, {route, Packet}) -> + {Pass, State1} = case Packet of + #presence{} -> + process_presence_in(State, Packet); + #message{} -> + process_message_in(State, Packet); + #iq{} -> + process_iq_in(State, Packet) + end, + if Pass -> + {Packet1, State2} = ejabberd_hooks:run_fold( + user_receive_packet, LServer, + {Packet, State1}, []), + case Packet1 of + drop -> State2; + _ -> send(State2, Packet1) + end; + true -> + State1 end; -wait_for_bind(Pkt, StateData) when ?IS_STREAM_MGMT_PACKET(Pkt) -> - fsm_next_state(wait_for_bind, dispatch_stream_mgmt(Pkt, StateData)); -wait_for_bind(#iq{type = set, - sub_els = [#bind{resource = R0}]} = IQ, StateData) -> - U = StateData#state.user, - R = case R0 of - <<>> -> new_uniq_id(); - _ -> R0 +process_info(State, reset_vcard_xupdate_resend_presence) -> + case maps:get(pres_last, State, error) of + error -> State; + Pres -> + Pres2 = xmpp:remove_subtag(Pres, #vcard_xupdate{}), + process_self_presence(State#{pres_last => Pres2}, Pres2) + end; +process_info(#{jid := JID} = State, {resend_presence, To}) -> + case maps:get(pres_last, State, error) of + error -> State; + Pres when To == undefined -> + process_self_presence(State, Pres); + Pres when To#jid.luser == JID#jid.luser andalso + To#jid.lserver == JID#jid.lserver andalso + To#jid.lresource == <<"">> -> + process_self_presence(State, Pres); + Pres -> + process_presence_out(State, xmpp:set_to(Pres, To)) + end; +process_info(State, Info) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), + State. + +handle_unexpected_call(State, From, Msg) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Msg]), + State. + +handle_unexpected_cast(State, Msg) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), + State. + +c2s_handle_bind({<<"">>, {ok, State}}) -> + {new_uniq_id(), {ok, State}}; +c2s_handle_bind(Acc) -> + Acc. + +reject_unauthenticated_packet(State, _Pkt) -> + Err = xmpp:serr_not_authorized(), + send(State, Err). + +process_auth_result(#{sasl_mech := Mech, auth_module := AuthModule, + socket := Socket, ip := IP, lserver := LServer} = State, + true, User) -> + misc:set_proc_label({?MODULE, User, LServer}), + ?INFO_MSG("(~ts) Accepted c2s ~ts authentication for ~ts@~ts by ~ts backend from ~ts", + [xmpp_socket:pp(Socket), Mech, User, LServer, + ejabberd_auth:backend_type(AuthModule), + ejabberd_config:may_hide_data(misc:ip_to_list(IP))]), + State; +process_auth_result(#{sasl_mech := Mech, + socket := Socket, ip := IP, lserver := LServer} = State, + {false, Reason}, User) -> + ?WARNING_MSG("(~ts) Failed c2s ~ts authentication ~tsfrom ~ts: ~ts", + [xmpp_socket:pp(Socket), Mech, + if User /= <<"">> -> ["for ", User, "@", LServer, " "]; + true -> "" + end, + ejabberd_config:may_hide_data(misc:ip_to_list(IP)), Reason]), + State. + +process_closed(State, Reason) -> + stop_async(self()), + State#{stop_reason => Reason}. + +process_terminated(#{sid := SID, jid := JID, user := U, server := S, resource := R} = State, + Reason) -> + Status = format_reason(State, Reason), + ?INFO_MSG("(~ts) Closing c2s session for ~ts: ~ts", + [case maps:find(socket, State) of + {ok, Socket} -> xmpp_socket:pp(Socket); + _ -> <<"unknown">> + end, jid:encode(JID), Status]), + Pres = #presence{type = unavailable, + from = JID, + to = jid:remove_resource(JID)}, + State1 = case maps:is_key(pres_last, State) of + true -> + ejabberd_sm:close_session_unset_presence(SID, U, S, R, + Status), + broadcast_presence_unavailable(State, Pres, true); + false -> + ejabberd_sm:close_session(SID, U, S, R), + broadcast_presence_unavailable(State, Pres, false) + end, + bounce_message_queue(SID, JID), + State1; +process_terminated(#{stop_reason := {tls, _}} = State, Reason) -> + ?WARNING_MSG("(~ts) Failed to secure c2s connection: ~ts", + [case maps:find(socket, State) of + {ok, Socket} -> xmpp_socket:pp(Socket); + _ -> <<"unknown">> + end, format_reason(State, Reason)]), + State; +process_terminated(State, _Reason) -> + State. + +%%%=================================================================== +%%% xmpp_stream_in callbacks +%%%=================================================================== +tls_options(#{lserver := LServer, tls_options := DefaultOpts, + stream_encrypted := Encrypted}) -> + TLSOpts1 = case {Encrypted, proplists:get_value(certfile, DefaultOpts)} of + {true, CertFile} when CertFile /= undefined -> DefaultOpts; + {_, _} -> + case ejabberd_pkix:get_certfile(LServer) of + error -> DefaultOpts; + {ok, CertFile} -> + lists:keystore(certfile, 1, DefaultOpts, + {certfile, CertFile}) + end + end, + TLSOpts2 = case ejabberd_option:c2s_ciphers(LServer) of + undefined -> TLSOpts1; + Ciphers -> lists:keystore(ciphers, 1, TLSOpts1, + {ciphers, Ciphers}) + end, + TLSOpts3 = case ejabberd_option:c2s_protocol_options(LServer) of + undefined -> TLSOpts2; + ProtoOpts -> lists:keystore(protocol_options, 1, TLSOpts2, + {protocol_options, ProtoOpts}) + end, + TLSOpts4 = case ejabberd_option:c2s_dhfile(LServer) of + undefined -> TLSOpts3; + DHFile -> lists:keystore(dhfile, 1, TLSOpts3, + {dhfile, DHFile}) + end, + TLSOpts5 = case ejabberd_option:c2s_cafile(LServer) of + undefined -> TLSOpts4; + CAFile -> lists:keystore(cafile, 1, TLSOpts4, + {cafile, CAFile}) + end, + case ejabberd_option:c2s_tls_compression(LServer) of + undefined -> TLSOpts5; + false -> [compression_none | TLSOpts5]; + true -> lists:delete(compression_none, TLSOpts5) + end. + +tls_required(#{tls_required := TLSRequired}) -> + TLSRequired. + +tls_enabled(#{tls_enabled := TLSEnabled, + tls_required := TLSRequired, + tls_verify := TLSVerify}) -> + TLSEnabled or TLSRequired or TLSVerify. + +allow_unencrypted_sasl2(#{allow_unencrypted_sasl2 := AllowUnencryptedSasl2}) -> + AllowUnencryptedSasl2. + +compress_methods(#{zlib := true}) -> + [<<"zlib">>]; +compress_methods(_) -> + []. + +unauthenticated_stream_features(#{lserver := LServer}) -> + ejabberd_hooks:run_fold(c2s_pre_auth_features, LServer, [], [LServer]). + +authenticated_stream_features(#{lserver := LServer}) -> + ejabberd_hooks:run_fold(c2s_post_auth_features, LServer, [], [LServer]). + +inline_stream_features(#{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_inline_features, LServer, {[], [], []}, [LServer, State]). + +sasl_mechanisms(Mechs, #{lserver := LServer, stream_encrypted := Encrypted} = State) -> + Type = ejabberd_auth:store_type(LServer), + Mechs1 = ejabberd_option:disable_sasl_mechanisms(LServer), + + {Digest, ShaAv, Sha256Av, Sha512Av} = + case ejabberd_option:auth_stored_password_types(LServer) of + [] -> + ScramHash = ejabberd_option:auth_scram_hash(LServer), + {Type == plain, + Type == plain orelse (Type == scram andalso ScramHash == sha), + Type == plain orelse (Type == scram andalso ScramHash == sha256), + Type == plain orelse (Type == scram andalso ScramHash == sha512)}; + Methods -> + HasPlain = lists:member(plain, Methods), + {HasPlain, + HasPlain orelse lists:member(scram_sha1, Methods), + HasPlain orelse lists:member(scram_sha256, Methods), + HasPlain orelse lists:member(scram_sha512, Methods)} end, - case resource_conflict_action(U, StateData#state.server, R) of - closenew -> - Err = xmpp:make_error(IQ, xmpp:err_conflict()), - send_element(StateData, Err), - fsm_next_state(wait_for_bind, StateData); - {accept_resource, R2} -> - JID = jid:make(U, StateData#state.server, R2), - StateData2 = StateData#state{resource = R2, jid = JID}, - case open_session(StateData2) of - {ok, StateData3} -> - Res = xmpp:make_iq_result(IQ, #bind{jid = JID}), - try - send_element(StateData3, Res) - catch - exit:normal -> close(self()) - end, - fsm_next_state_pack(session_established,StateData3); - {error, Error} -> - Err = xmpp:make_error(IQ, Error), - send_element(StateData, Err), - fsm_next_state(wait_for_bind, StateData) + %% I re-created it from cyrsasl ets magic, but I think it's wrong + %% TODO: need to check before 18.09 release + Mechs2 = lists:filter( + fun(<<"ANONYMOUS">>) -> + ejabberd_auth_anonymous:is_sasl_anonymous_enabled(LServer); + (<<"DIGEST-MD5">>) -> Digest; + (<<"SCRAM-SHA-1">>) -> ShaAv; + (<<"SCRAM-SHA-1-PLUS">>) -> ShaAv andalso Encrypted; + (<<"SCRAM-SHA-256">>) -> Sha256Av; + (<<"SCRAM-SHA-256-PLUS">>) -> Sha256Av andalso Encrypted; + (<<"SCRAM-SHA-512">>) -> Sha512Av; + (<<"SCRAM-SHA-512-PLUS">>) -> Sha512Av andalso Encrypted; + (<<"PLAIN">>) -> true; + (<<"X-OAUTH2">>) -> [ejabberd_auth_anonymous] /= ejabberd_auth:auth_modules(LServer); + (<<"EXTERNAL">>) -> maps:get(tls_verify, State, false); + (_) -> false + end, Mechs -- Mechs1), + case ejabberd_option:auth_password_types_hidden_in_sasl1() of + [] -> Mechs2; + List -> + Mechs3 = lists:foldl( + fun(plain, Acc) -> Acc -- [<<"PLAIN">>]; + (scram_sha1, Acc) -> Acc -- [<<"SCRAM-SHA-1">>, <<"SCRAM-SHA-1-PLUS">>]; + (scram_sha256, Acc) -> Acc -- [<<"SCRAM-SHA-256">>, <<"SCRAM-SHA-256-PLUS">>]; + (scram_sha512, Acc) -> Acc -- [<<"SCRAM-SHA-512">>, <<"SCRAM-SHA-512-PLUS">>] + end, Mechs2, List), + {Mechs3, Mechs2} + end. + +sasl_options(#{lserver := LServer}) -> + case ejabberd_option:disable_sasl_scram_downgrade_protection(LServer) of + true -> [{scram_downgrade_protection, false}]; + _ -> [] + end. + +get_password_fun(_Mech, #{lserver := LServer}) -> + fun(U) -> + ejabberd_auth:get_password_with_authmodule(U, LServer) + end. + +check_password_fun(<<"X-OAUTH2">>, #{lserver := LServer}) -> + fun(User, _AuthzId, Token) -> + case ejabberd_oauth:check_token( + User, LServer, [<<"sasl_auth">>], Token) of + true -> {true, ejabberd_oauth}; + _ -> {false, ejabberd_oauth} end end; -wait_for_bind(#compress{} = Comp, StateData) -> - Zlib = StateData#state.zlib, - SockMod = (StateData#state.sockmod):get_sockmod(StateData#state.socket), - if Zlib == true, (SockMod == gen_tcp) or (SockMod == fast_tls) -> - process_compression_request(Comp, wait_for_bind, StateData); - true -> - send_element(StateData, #compress_failure{reason = 'setup-failed'}), - fsm_next_state(wait_for_bind, StateData) - end; -wait_for_bind(timeout, StateData) -> - {stop, normal, StateData}; -wait_for_bind({xmlstreamend, _Name}, StateData) -> - {stop, normal, StateData}; -wait_for_bind({xmlstreamerror, _}, StateData) -> - send_element(StateData, xmpp:serr_not_well_formed()), - {stop, normal, StateData}; -wait_for_bind(closed, StateData) -> - {stop, normal, StateData}; -wait_for_bind(stop, StateData) -> - {stop, normal, StateData}; -wait_for_bind(Pkt, StateData) -> - fsm_next_state( - wait_for_bind, - case xmpp:is_stanza(Pkt) of - true -> - send_error(StateData, Pkt, xmpp:err_not_acceptable()); - false -> - StateData - end). - --spec open_session(state()) -> {ok, state()} | {error, stanza_error()}. -open_session(StateData) -> - U = StateData#state.user, - R = StateData#state.resource, - JID = StateData#state.jid, - Lang = StateData#state.lang, - IP = StateData#state.ip, - case acl:access_matches(StateData#state.access, - #{usr => jid:split(JID), ip => IP}, - StateData#state.server) of - allow -> - ?INFO_MSG("(~w) Opened session for ~s", - [StateData#state.socket, jid:to_string(JID)]), - change_shaper(StateData, JID), - {Fs, Ts} = ejabberd_hooks:run_fold( - roster_get_subscription_lists, - StateData#state.server, - {[], []}, - [U, StateData#state.server]), - LJID = jid:tolower(jid:remove_resource(JID)), - Fs1 = [LJID | Fs], - Ts1 = [LJID | Ts], - PrivList = - ejabberd_hooks:run_fold( - privacy_get_user_list, - StateData#state.server, - #userlist{}, - [U, StateData#state.server]), - Conn = get_conn_type(StateData), - Info = [{ip, StateData#state.ip}, {conn, Conn}, - {auth_module, StateData#state.auth_module}], - ejabberd_sm:open_session( - StateData#state.sid, U, StateData#state.server, R, Info), - UpdatedStateData = - StateData#state{ - conn = Conn, - pres_f = ?SETS:from_list(Fs1), - pres_t = ?SETS:from_list(Ts1), - privacy_list = PrivList}, - {ok, UpdatedStateData}; - _ -> - ejabberd_hooks:run(forbidden_session_hook, - StateData#state.server, [JID]), - ?INFO_MSG("(~w) Forbidden session for ~s", - [StateData#state.socket, jid:to_string(JID)]), - Txt = <<"Denied by ACL">>, - {error, xmpp:err_not_allowed(Txt, Lang)} +check_password_fun(_Mech, #{lserver := LServer}) -> + fun(U, AuthzId, P) -> + ejabberd_auth:check_password_with_authmodule(U, AuthzId, LServer, P) end. -session_established({xmlstreamelement, El}, StateData) -> - decode_element(El, session_established, StateData); -session_established(Pkt, StateData) when ?IS_STREAM_MGMT_PACKET(Pkt) -> - fsm_next_state(session_established, dispatch_stream_mgmt(Pkt, StateData)); -session_established(#csi{type = active}, StateData) -> - NewStateData = csi_flush_queue(StateData), - fsm_next_state(session_established, NewStateData#state{csi_state = active}); -session_established(#csi{type = inactive}, StateData) -> - fsm_next_state(session_established, StateData#state{csi_state = inactive}); -%% We hibernate the process to reduce memory consumption after a -%% configurable activity timeout -session_established(timeout, StateData) -> - Options = [], - proc_lib:hibernate(?GEN_FSM, enter_loop, - [?MODULE, Options, session_established, StateData]), - fsm_next_state(session_established, StateData); -session_established({xmlstreamend, _Name}, StateData) -> - {stop, normal, StateData}; -session_established({xmlstreamerror, - <<"XML stanza is too big">> = E}, - StateData) -> - send_element(StateData, - xmpp:serr_policy_violation(E, StateData#state.lang)), - {stop, normal, StateData}; -session_established({xmlstreamerror, _}, StateData) -> - send_element(StateData, xmpp:serr_not_well_formed()), - {stop, normal, StateData}; -session_established(closed, #state{mgmt_state = active} = StateData) -> - catch (StateData#state.sockmod):close(StateData#state.socket), - fsm_next_state(wait_for_resume, StateData); -session_established(closed, StateData) -> - {stop, normal, StateData}; -session_established(stop, StateData) -> - {stop, normal, StateData}; -session_established(Pkt, StateData) when ?is_stanza(Pkt) -> - FromJID = StateData#state.jid, - case check_from(Pkt, FromJID) of - 'invalid-from' -> - send_element(StateData, xmpp:serr_invalid_from()), - {stop, normal, StateData}; - _ -> - NewStateData = update_num_stanzas_in(StateData, Pkt), - session_established2(Pkt, NewStateData) - end; -session_established(_Pkt, StateData) -> - fsm_next_state(session_established, StateData). +check_password_digest_fun(_Mech, #{lserver := LServer}) -> + fun(U, AuthzId, P, D, DG) -> + ejabberd_auth:check_password_with_authmodule(U, AuthzId, LServer, P, D, DG) + end. --spec session_established2(xmpp_element(), state()) -> fsm_next(). -%% Process packets sent by user (coming from user on c2s XMPP connection) -session_established2(Pkt, StateData) -> - User = StateData#state.user, - Server = StateData#state.server, - FromJID = StateData#state.jid, - ToJID = case xmpp:get_to(Pkt) of - undefined -> jid:make(User, Server, <<"">>); - J -> J - end, - Lang = case xmpp:get_lang(Pkt) of - <<"">> -> StateData#state.lang; - L -> L - end, - NewPkt = xmpp:set_lang(Pkt, Lang), - NewState = - case NewPkt of - #presence{} -> - Presence0 = ejabberd_hooks:run_fold( - c2s_update_presence, Server, NewPkt, - [User, Server]), - Presence = ejabberd_hooks:run_fold( - user_send_packet, Server, Presence0, - [StateData, FromJID, ToJID]), - case ToJID of - #jid{user = User, server = Server, resource = <<"">>} -> - ?DEBUG("presence_update(~p,~n\t~p,~n\t~p)", - [FromJID, Presence, StateData]), - presence_update(FromJID, Presence, - StateData); - _ -> - presence_track(FromJID, ToJID, Presence, - StateData) - end; - #iq{type = T, sub_els = [El]} when T == set; T == get -> - NS = xmpp:get_ns(El), - if NS == ?NS_BLOCKING; NS == ?NS_PRIVACY -> - IQ = xmpp:set_from_to(Pkt, FromJID, ToJID), - process_privacy_iq(IQ, StateData); - NS == ?NS_SESSION -> - Res = xmpp:make_iq_result(Pkt), - send_stanza(StateData, Res); - true -> - NewPkt0 = ejabberd_hooks:run_fold( - user_send_packet, Server, NewPkt, - [StateData, FromJID, ToJID]), - check_privacy_route(FromJID, StateData, FromJID, - ToJID, NewPkt0) - end; - _ -> - NewPkt0 = ejabberd_hooks:run_fold( - user_send_packet, Server, NewPkt, - [StateData, FromJID, ToJID]), - check_privacy_route(FromJID, StateData, FromJID, - ToJID, NewPkt0) - end, - ejabberd_hooks:run(c2s_loop_debug, - [{xmlstreamelement, Pkt}]), - fsm_next_state(session_established, NewState). +get_fast_tokens_fun(_Mech, #{lserver := LServer}) -> + fun(User, UA) -> + case gen_mod:is_loaded(LServer, mod_auth_fast) of + false -> false; + _ -> mod_auth_fast:get_tokens(LServer, User, UA) + end + end. -wait_for_resume({xmlstreamelement, _El} = Event, StateData) -> - Result = session_established(Event, StateData), - fsm_next_state(wait_for_resume, element(3, Result)); -wait_for_resume(timeout, StateData) -> - ?DEBUG("Timed out waiting for resumption of stream for ~s", - [jid:to_string(StateData#state.jid)]), - {stop, normal, StateData#state{mgmt_state = timeout}}; -wait_for_resume(Event, StateData) -> - ?DEBUG("Ignoring event while waiting for resumption: ~p", [Event]), - fsm_next_state(wait_for_resume, StateData). +fast_mechanisms(#{lserver := LServer}) -> + case gen_mod:is_loaded(LServer, mod_auth_fast) of + false -> []; + _ -> mod_auth_fast:get_mechanisms(LServer) + end. -handle_event(_Event, StateName, StateData) -> - fsm_next_state(StateName, StateData). - -handle_sync_event({get_presence}, _From, StateName, - StateData) -> - User = StateData#state.user, - PresLast = StateData#state.pres_last, - Show = get_showtag(PresLast), - Status = get_statustag(PresLast), - Resource = StateData#state.resource, - Reply = {User, Resource, Show, Status}, - fsm_reply(Reply, StateName, StateData); -handle_sync_event({get_last_presence}, _From, StateName, - StateData) -> - User = StateData#state.user, - Server = StateData#state.server, - PresLast = StateData#state.pres_last, - Resource = StateData#state.resource, - Reply = {User, Server, Resource, PresLast}, - fsm_reply(Reply, StateName, StateData); - -handle_sync_event(get_subscribed, _From, StateName, - StateData) -> - Subscribed = (?SETS):to_list(StateData#state.pres_f), - {reply, Subscribed, StateName, StateData}; -handle_sync_event({resume_session, Time}, _From, _StateName, - StateData) when element(1, StateData#state.sid) == Time -> - %% The old session should be closed before the new one is opened, so we do - %% this here instead of leaving it to the terminate callback - ejabberd_sm:close_session(StateData#state.sid, - StateData#state.user, - StateData#state.server, - StateData#state.resource), - {stop, normal, {resume, StateData}, StateData#state{mgmt_state = resumed}}; -handle_sync_event({resume_session, _Time}, _From, StateName, - StateData) -> - {reply, {error, <<"Previous session not found">>}, StateName, StateData}; -handle_sync_event(_Event, _From, StateName, - StateData) -> - Reply = ok, fsm_reply(Reply, StateName, StateData). - -code_change(_OldVsn, StateName, StateData, _Extra) -> - {ok, StateName, StateData}. - -handle_info({send_text, Text}, StateName, StateData) -> - send_text(StateData, Text), - ejabberd_hooks:run(c2s_loop_debug, [Text]), - fsm_next_state(StateName, StateData); -handle_info(replaced, StateName, StateData) -> - Lang = StateData#state.lang, - Pkt = xmpp:serr_conflict(<<"Replaced by new connection">>, Lang), - handle_info({kick, replaced, Pkt}, StateName, StateData); -handle_info(kick, StateName, StateData) -> - Lang = StateData#state.lang, - Pkt = xmpp:serr_policy_violation(<<"has been kicked">>, Lang), - handle_info({kick, kicked_by_admin, Pkt}, StateName, StateData); -handle_info({kick, Reason, Pkt}, _StateName, StateData) -> - send_element(StateData, Pkt), - {stop, normal, - StateData#state{authenticated = Reason}}; -handle_info({route, _From, _To, {broadcast, Data}}, - StateName, StateData) -> - ?DEBUG("broadcast~n~p~n", [Data]), - case Data of - {item, IJID, ISubscription} -> - fsm_next_state(StateName, - roster_change(IJID, ISubscription, StateData)); - {exit, Reason} -> - Lang = StateData#state.lang, - send_element(StateData, xmpp:serr_conflict(Reason, Lang)), - {stop, normal, StateData}; - {privacy_list, PrivList, PrivListName} -> - case ejabberd_hooks:run_fold(privacy_updated_list, - StateData#state.server, - false, - [StateData#state.privacy_list, - PrivList]) of - false -> - fsm_next_state(StateName, StateData); - NewPL -> - PrivPushIQ = - #iq{type = set, - from = jid:remove_resource(StateData#state.jid), - to = StateData#state.jid, - id = <<"push", (randoms:get_string())/binary>>, - sub_els = [#privacy_query{ - lists = [#privacy_list{ - name = PrivListName}]}]}, - NewState = send_stanza(StateData, PrivPushIQ), - fsm_next_state(StateName, - NewState#state{privacy_list = NewPL}) +bind( + R, + #{ + user := U, + server := S, + lserver := LServer, + access := Access, + lang := Lang, + socket := Socket, + ip := IP + }=State +) -> + case ejabberd_hooks:run_fold(c2s_handle_bind, LServer, {R, {ok, State}}, []) of + {R2, {ok, State2}} -> + case resource_conflict_action(U, S, R2) of + closenew -> + {error, xmpp:err_conflict(), State2}; + {accept_resource, Resource} -> + JID = jid:make(U, S, Resource), + case acl:match_rule(LServer, Access, #{usr => jid:split(JID), ip => IP}) of + allow -> + State3 = open_session( + State2#{resource => Resource, sid => ejabberd_sm:make_sid()} + ), + State4 = ejabberd_hooks:run_fold( + c2s_session_opened, LServer, State3, [] + ), + ?INFO_MSG( + "(~ts) Opened c2s session for ~ts", [xmpp_socket:pp(Socket), jid:encode(JID)] + ), + {ok, State4}; + deny -> + ejabberd_hooks:run(forbidden_session_hook, LServer, [JID]), + ?WARNING_MSG( + "(~ts) Forbidden c2s session for ~ts", + [xmpp_socket:pp(Socket), jid:encode(JID)] + ), + Txt = ?T("Access denied by service policy"), + {error, xmpp:err_not_allowed(Txt, Lang), State2} + end end; - {blocking, What} -> - NewState = route_blocking(What, StateData), - fsm_next_state(StateName, NewState); - _ -> - fsm_next_state(StateName, StateData) - end; -%% Process Packets that are to be send to the user -handle_info({route, From, To, Packet}, StateName, StateData) when ?is_stanza(Packet) -> - {Pass, NewState} = - case Packet of - #presence{type = T} -> - State = ejabberd_hooks:run_fold(c2s_presence_in, - StateData#state.server, - StateData, - [{From, To, Packet}]), - case T of - probe -> - LFrom = jid:tolower(From), - LBFrom = jid:remove_resource(LFrom), - NewStateData = - case (?SETS):is_element(LFrom, State#state.pres_a) - orelse (?SETS):is_element(LBFrom, State#state.pres_a) of - true -> State; - false -> - case (?SETS):is_element(LFrom, State#state.pres_f) of - true -> - A = (?SETS):add_element(LFrom, State#state.pres_a), - State#state{pres_a = A}; - false -> - case (?SETS):is_element(LBFrom, State#state.pres_f) of - true -> - A = (?SETS):add_element(LBFrom, State#state.pres_a), - State#state{pres_a = A}; - false -> - State - end - end - end, - process_presence_probe(From, To, NewStateData), - {false, NewStateData}; - error -> - NewA = ?SETS:del_element(jid:tolower(From), State#state.pres_a), - {true, State#state{pres_a = NewA}}; - subscribe -> - SRes = is_privacy_allow(State, From, To, Packet, in), - {SRes, State}; - subscribed -> - SRes = is_privacy_allow(State, From, To, Packet, in), - {SRes, State}; - unsubscribe -> - SRes = is_privacy_allow(State, From, To, Packet, in), - {SRes, State}; - unsubscribed -> - SRes = is_privacy_allow(State, From, To, Packet, in), - {SRes, State}; - _ -> - case privacy_check_packet(State, From, To, Packet, in) of - allow -> - LFrom = jid:tolower(From), - LBFrom = jid:remove_resource(LFrom), - case (?SETS):is_element(LFrom, State#state.pres_a) - orelse (?SETS):is_element(LBFrom, State#state.pres_a) of - true -> - {true, State}; - false -> - case (?SETS):is_element(LFrom, State#state.pres_f) of - true -> - A = (?SETS):add_element(LFrom, State#state.pres_a), - {true, State#state{pres_a = A}}; - false -> - case (?SETS):is_element(LBFrom, - State#state.pres_f) of - true -> - A = (?SETS):add_element( - LBFrom, - State#state.pres_a), - {true, State#state{pres_a = A}}; - false -> - {true, State} - end - end - end; - deny -> {false, State} - end - end; - #iq{type = T} -> - case xmpp:has_subtag(Packet, #last{}) of - true when T == get; T == set -> - LFrom = jid:tolower(From), - LBFrom = jid:remove_resource(LFrom), - HasFromSub = ((?SETS):is_element(LFrom, StateData#state.pres_f) - orelse (?SETS):is_element(LBFrom, StateData#state.pres_f)) - andalso is_privacy_allow(StateData, To, From, #presence{}, out), - case HasFromSub of - true -> - case privacy_check_packet( - StateData, From, To, Packet, in) of - allow -> - {true, StateData}; - deny -> - ejabberd_router:route_error( - To, From, Packet, - xmpp:err_service_unavailable()), - {false, StateData} - end; - _ -> - ejabberd_router:route_error( - To, From, Packet, xmpp:err_forbidden()), - {false, StateData} - end; - _ -> - case privacy_check_packet(StateData, From, To, Packet, in) of - allow -> - {true, StateData}; - deny -> - ejabberd_router:route_error( - To, From, Packet, xmpp:err_service_unavailable()), - {false, StateData} - end - end; - #message{type = T} -> - case privacy_check_packet(StateData, From, To, Packet, in) of - allow -> - {true, StateData}; - deny -> - case T of - groupchat -> ok; - headline -> ok; - _ -> - case xmpp:has_subtag(Packet, #muc_user{}) of - true -> - ok; - false -> - ejabberd_router:route_error( - To, From, Packet, xmpp:err_service_unavailable()) - end - end, - {false, StateData} - end - end, - if Pass -> - FixedPacket0 = xmpp:set_from_to(Packet, From, To), - FixedPacket = ejabberd_hooks:run_fold( - user_receive_packet, - NewState#state.server, - FixedPacket0, - [NewState, NewState#state.jid, From, To]), - SentStateData = send_packet(NewState, FixedPacket), - ejabberd_hooks:run(c2s_loop_debug, [{route, From, To, Packet}]), - fsm_next_state(StateName, SentStateData); + {R2, {error, XmppErr, _State2}=Err} -> + case XmppErr of + #stanza_error{reason = 'not-allowed'} -> + JID = jid:make(U, S, R2), + ejabberd_hooks:run(forbidden_session_hook, LServer, [JID]), + ?WARNING_MSG( + "(~ts) Forbidden c2s session for ~ts", + [xmpp_socket:pp(Socket), jid:encode(JID)] + ); + _ -> + ok + end, + Err + end. + +handle_stream_start(StreamStart, #{lserver := LServer} = State) -> + case ejabberd_router:is_my_host(LServer) of + false -> + send(State#{lserver => ejabberd_config:get_myname()}, xmpp:serr_host_unknown()); true -> - ejabberd_hooks:run(c2s_loop_debug, [{route, From, To, Packet}]), - fsm_next_state(StateName, NewState) - end; -handle_info({'DOWN', Monitor, _Type, _Object, _Info}, - _StateName, StateData) - when Monitor == StateData#state.socket_monitor -> - if StateData#state.mgmt_state == active; - StateData#state.mgmt_state == pending -> - fsm_next_state(wait_for_resume, StateData); + State1 = change_shaper(State), + Opts = ejabberd_config:codec_options(), + State2 = State1#{codec_options => Opts}, + ejabberd_hooks:run_fold( + c2s_stream_started, LServer, State2, [StreamStart]) + end. + +handle_stream_end(Reason, #{lserver := LServer} = State) -> + State1 = State#{stop_reason => Reason}, + ejabberd_hooks:run_fold(c2s_closed, LServer, State1, [Reason]). + +handle_auth_success(User, _Mech, AuthModule, + #{lserver := LServer} = State) -> + State1 = State#{auth_module => AuthModule}, + ejabberd_hooks:run_fold(c2s_auth_result, LServer, State1, [true, User]). + +handle_auth_failure(User, _Mech, Reason, + #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_auth_result, LServer, State, [{false, Reason}, User]). + +handle_unbinded_packet(Pkt, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_unbinded_packet, LServer, State, [Pkt]). + +handle_unauthenticated_packet(Pkt, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_unauthenticated_packet, LServer, State, [Pkt]). + +handle_authenticated_packet(Pkt, #{lserver := LServer} = State) when not ?is_stanza(Pkt) -> + ejabberd_hooks:run_fold(c2s_authenticated_packet, + LServer, State, [Pkt]); +handle_authenticated_packet(Pkt, #{lserver := LServer, jid := JID, + ip := {IP, _}} = State) -> + Pkt1 = xmpp:put_meta(Pkt, ip, IP), + State1 = ejabberd_hooks:run_fold(c2s_authenticated_packet, + LServer, State, [Pkt1]), + #jid{luser = LUser} = JID, + {Pkt2, State2} = ejabberd_hooks:run_fold( + user_send_packet, LServer, {Pkt1, State1}, []), + case Pkt2 of + drop -> + State2; + #iq{type = set, sub_els = [_]} -> + try xmpp:try_subtag(Pkt2, #xmpp_session{}) of + #xmpp_session{} -> + % It seems that some client are expecting to have response + % to session request be sent from server jid, let's make + % sure it is that. + Pkt3 = xmpp:set_to(Pkt2, jid:make(<<>>, LServer, <<>>)), + send(State2, xmpp:make_iq_result(Pkt3)); + _ -> + check_privacy_then_route(State2, Pkt2) + catch _:{xmpp_codec, Why} -> + Txt = xmpp:io_format_error(Why), + Lang = maps:get(lang, State), + Err = xmpp:err_bad_request(Txt, Lang), + send_error(State2, Pkt2, Err) + end; + #presence{to = #jid{luser = LUser, lserver = LServer, + lresource = <<"">>}} -> + process_self_presence(State2, Pkt2); + #presence{} -> + process_presence_out(State2, Pkt2); + _ -> + check_privacy_then_route(State2, Pkt2) + end. + +handle_cdata(Data, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_handle_cdata, LServer, + State, [Data]). + +handle_sasl2_inline(Els, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_handle_sasl2_inline, LServer, + {State, Els, []}, []). + +handle_sasl2_inline_post(Els, Results, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_handle_sasl2_inline_post, LServer, + State, [Els, Results]). + +handle_bind2_inline(Els, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_handle_bind2_inline, LServer, + {State, Els, []}, []). + +handle_bind2_inline_post(Els, Results, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_handle_bind2_inline_post, LServer, + State, [Els, Results]). + +handle_sasl2_task_next(Task, Els, InlineEls, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_handle_sasl2_task_next, LServer, + {abort, State}, [Task, Els, InlineEls]). + +handle_sasl2_task_data(Els, InlineEls, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_handle_sasl2_task_data, LServer, + {abort, State}, [Els, InlineEls]). + +handle_recv(El, Pkt, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_handle_recv, LServer, State, [El, Pkt]). + +handle_send(Pkt, Result, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_handle_send, LServer, State, [Pkt, Result]). + +init([State, Opts]) -> + Access = proplists:get_value(access, Opts, all), + Shaper = proplists:get_value(shaper, Opts, none), + TLSOpts1 = lists:filter( + fun({certfile, _}) -> true; + ({ciphers, _}) -> true; + ({dhfile, _}) -> true; + ({cafile, _}) -> true; + ({protocol_options, _}) -> true; + (_) -> false + end, Opts), + TLSOpts2 = case proplists:get_bool(tls_compression, Opts) of + false -> [compression_none | TLSOpts1]; + true -> TLSOpts1 + end, + TLSEnabled = proplists:get_bool(starttls, Opts), + TLSRequired = proplists:get_bool(starttls_required, Opts), + TLSVerify = proplists:get_bool(tls_verify, Opts), + AllowUnencryptedSasl2 = proplists:get_bool(allow_unencrypted_sasl2, Opts), + Zlib = proplists:get_bool(zlib, Opts), + Timeout = ejabberd_option:negotiation_timeout(), + State1 = State#{tls_options => TLSOpts2, + tls_required => TLSRequired, + tls_enabled => TLSEnabled, + tls_verify => TLSVerify, + allow_unencrypted_sasl2 => AllowUnencryptedSasl2, + pres_a => ?SETS:new(), + zlib => Zlib, + lang => ejabberd_option:language(), + server => ejabberd_config:get_myname(), + lserver => ejabberd_config:get_myname(), + access => Access, + shaper => Shaper}, + State2 = xmpp_stream_in:set_timeout(State1, Timeout), + misc:set_proc_label({?MODULE, init_state}), + ejabberd_hooks:run_fold(c2s_init, {ok, State2}, [Opts]). + +handle_call(get_presence, From, #{jid := JID} = State) -> + Pres = case maps:get(pres_last, State, error) of + error -> + BareJID = jid:remove_resource(JID), + #presence{from = JID, to = BareJID, type = unavailable}; + P -> P + end, + reply(From, Pres), + State; +handle_call({set_presence, Pres}, From, State) -> + reply(From, ok), + process_self_presence(State, Pres); +handle_call(Request, From, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold( + c2s_handle_call, LServer, State, [Request, From]). + +handle_cast(Msg, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_handle_cast, LServer, State, [Msg]). + +handle_info(Info, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_handle_info, LServer, State, [Info]). + +terminate(Reason, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_terminated, LServer, State, [Reason]). + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +-spec process_iq_in(state(), iq()) -> {boolean(), state()}. +process_iq_in(State, #iq{} = IQ) -> + case privacy_check_packet(State, IQ, in) of + allow -> + {true, State}; + deny -> + ejabberd_router:route_error(IQ, xmpp:err_service_unavailable()), + {false, State} + end. + +-spec process_message_in(state(), message()) -> {boolean(), state()}. +process_message_in(State, #message{type = T} = Msg) -> + %% This function should be as simple as process_iq_in/2, + %% however, we don't route errors to MUC rooms in order + %% to avoid kicking us, because having a MUC room's JID blocked + %% most likely means having only some particular participant + %% blocked, i.e. room@conference.server.org/participant. + case privacy_check_packet(State, Msg, in) of + allow -> + {true, State}; + deny when T == groupchat; T == headline -> + {false, State}; + deny -> + case xmpp:has_subtag(Msg, #muc_user{}) of + true -> + ok; + false -> + ejabberd_router:route_error( + Msg, xmpp:err_service_unavailable()) + end, + {false, State} + end. + +-spec process_presence_in(state(), presence()) -> {boolean(), state()}. +process_presence_in(#{lserver := LServer, pres_a := PresA} = State0, + #presence{from = From, type = T} = Pres) -> + State = ejabberd_hooks:run_fold(c2s_presence_in, LServer, State0, [Pres]), + case T of + probe -> + route_probe_reply(From, State), + {false, State}; + error -> + A = ?SETS:del_element(jid:tolower(From), PresA), + {true, State#{pres_a => A}}; + _ -> + case privacy_check_packet(State, Pres, in) of + allow -> + {true, State}; + deny -> + {false, State} + end + end. + +-spec route_probe_reply(jid(), state()) -> ok. +route_probe_reply(From, #{jid := To, + pres_last := LastPres, + pres_timestamp := TS} = State) -> + {LUser, LServer, LResource} = jid:tolower(To), + IsAnotherResource = case jid:tolower(From) of + {LUser, LServer, R} when R /= LResource -> true; + _ -> false + end, + Subscription = get_subscription(To, From), + if IsAnotherResource orelse + Subscription == both orelse Subscription == from -> + Packet = xmpp:set_from_to(LastPres, To, From), + Packet2 = misc:add_delay_info(Packet, To, TS), + case privacy_check_packet(State, Packet2, out) of + deny -> + ok; + allow -> + ejabberd_hooks:run(presence_probe_hook, + LServer, + [From, To, self()]), + ejabberd_router:route(Packet2) + end; true -> - {stop, normal, StateData} + ok end; -handle_info(system_shutdown, StateName, StateData) -> - case StateName of - wait_for_stream -> - send_header(StateData, ?MYNAME, {1,0}, <<"en">>), - send_element(StateData, xmpp:serr_system_shutdown()), - ok; - _ -> - send_element(StateData, xmpp:serr_system_shutdown()), - ok - end, - {stop, normal, StateData}; -handle_info({route_xmlstreamelement, El}, _StateName, StateData) -> - {next_state, NStateName, NStateData, _Timeout} = - session_established({xmlstreamelement, El}, StateData), - fsm_next_state(NStateName, NStateData); -handle_info({force_update_presence, LUser, LServer}, StateName, - #state{jid = #jid{luser = LUser, lserver = LServer}} = StateData) -> - NewStateData = case StateData#state.pres_last of - #presence{} -> - Presence = - ejabberd_hooks:run_fold(c2s_update_presence, - LServer, - StateData#state.pres_last, - [LUser, LServer]), - StateData2 = StateData#state{pres_last = Presence}, - presence_update(StateData2#state.jid, Presence, - StateData2), - StateData2; - undefined -> StateData - end, - fsm_next_state(StateName, NewStateData); -handle_info({send_filtered, Feature, From, To, Packet}, StateName, StateData) -> - Drop = ejabberd_hooks:run_fold(c2s_filter_packet, StateData#state.server, - true, [StateData#state.server, StateData, - Feature, To, Packet]), - NewStateData = if Drop -> - ?DEBUG("Dropping packet from ~p to ~p", - [jid:to_string(From), - jid:to_string(To)]), - StateData; - true -> - FinalPacket = xmpp:set_from_to(Packet, From, To), - case StateData#state.jid of - To -> - case privacy_check_packet(StateData, From, To, - FinalPacket, in) of - deny -> - StateData; - allow -> - send_stanza(StateData, FinalPacket) - end; - _ -> - ejabberd_router:route(From, To, FinalPacket), - StateData - end - end, - fsm_next_state(StateName, NewStateData); -handle_info({broadcast, Type, From, Packet}, StateName, StateData) -> - Recipients = ejabberd_hooks:run_fold( - c2s_broadcast_recipients, StateData#state.server, - [], - [StateData#state.server, StateData, Type, From, Packet]), - lists:foreach( - fun(USR) -> - ejabberd_router:route( - From, jid:make(USR), Packet) - end, lists:usort(Recipients)), - fsm_next_state(StateName, StateData); -handle_info({set_csi_state, CsiState}, StateName, StateData) -> - fsm_next_state(StateName, StateData#state{csi_state = CsiState}); -handle_info({set_resume_timeout, Timeout}, StateName, StateData) -> - fsm_next_state(StateName, StateData#state{mgmt_timeout = Timeout}); -handle_info(dont_ask_offline, StateName, StateData) -> - fsm_next_state(StateName, StateData#state{ask_offline = false}); -handle_info(close, StateName, StateData) -> - ?DEBUG("Timeout waiting for stream management acknowledgement of ~s", - [jid:to_string(StateData#state.jid)]), - close(self()), - fsm_next_state(StateName, StateData#state{mgmt_ack_timer = undefined}); -handle_info({_Ref, {resume, OldStateData}}, StateName, StateData) -> - %% This happens if the resume_session/1 request timed out; the new session - %% now receives the late response. - ?DEBUG("Received old session state for ~s after failed resumption", - [jid:to_string(OldStateData#state.jid)]), - handle_unacked_stanzas(OldStateData#state{mgmt_resend = false}), - fsm_next_state(StateName, StateData); -handle_info(Info, StateName, StateData) -> - ?ERROR_MSG("Unexpected info: ~p", [Info]), - fsm_next_state(StateName, StateData). - --spec print_state(state()) -> state(). -print_state(State = #state{pres_t = T, pres_f = F, pres_a = A}) -> - State#state{pres_t = {pres_t, (?SETS):size(T)}, - pres_f = {pres_f, (?SETS):size(F)}, - pres_a = {pres_a, (?SETS):size(A)}}. - -terminate(_Reason, StateName, StateData) -> - case StateData#state.mgmt_state of - resumed -> - ?INFO_MSG("Closing former stream of resumed session for ~s", - [jid:to_string(StateData#state.jid)]); - _ -> - if StateName == session_established; - StateName == wait_for_resume -> - case StateData#state.authenticated of - replaced -> - ?INFO_MSG("(~w) Replaced session for ~s", - [StateData#state.socket, - jid:to_string(StateData#state.jid)]), - From = StateData#state.jid, - Lang = StateData#state.lang, - Status = <<"Replaced by new connection">>, - Packet = #presence{ - type = unavailable, - status = xmpp:mk_text(Status, Lang)}, - ejabberd_sm:close_session_unset_presence(StateData#state.sid, - StateData#state.user, - StateData#state.server, - StateData#state.resource, - Status), - presence_broadcast(StateData, From, - StateData#state.pres_a, Packet); - _ -> - ?INFO_MSG("(~w) Close session for ~s", - [StateData#state.socket, - jid:to_string(StateData#state.jid)]), - EmptySet = (?SETS):new(), - case StateData of - #state{pres_last = undefined, pres_a = EmptySet} -> - ejabberd_sm:close_session(StateData#state.sid, - StateData#state.user, - StateData#state.server, - StateData#state.resource); - _ -> - From = StateData#state.jid, - Packet = #presence{type = unavailable}, - ejabberd_sm:close_session_unset_presence(StateData#state.sid, - StateData#state.user, - StateData#state.server, - StateData#state.resource, - <<"">>), - presence_broadcast(StateData, From, - StateData#state.pres_a, Packet) - end, - case StateData#state.mgmt_state of - timeout -> - Info = [{num_stanzas_in, - StateData#state.mgmt_stanzas_in}], - ejabberd_sm:set_offline_info(StateData#state.sid, - StateData#state.user, - StateData#state.server, - StateData#state.resource, - Info); - _ -> - ok - end - end, - handle_unacked_stanzas(StateData), - bounce_messages(); - true -> - ok - end - end, - catch send_trailer(StateData), - (StateData#state.sockmod):close(StateData#state.socket), +route_probe_reply(_, _) -> ok. -%%%---------------------------------------------------------------------- -%%% Internal functions -%%%---------------------------------------------------------------------- --spec change_shaper(state(), jid()) -> ok. -change_shaper(StateData, JID) -> - Shaper = acl:access_matches(StateData#state.shaper, - #{usr => jid:split(JID), ip => StateData#state.ip}, - StateData#state.server), - (StateData#state.sockmod):change_shaper(StateData#state.socket, - Shaper). - --spec send_text(state(), iodata()) -> ok | {error, any()}. -send_text(StateData, Text) when StateData#state.mgmt_state == pending -> - ?DEBUG("Cannot send text while waiting for resumption: ~p", [Text]); -send_text(StateData, Text) when StateData#state.xml_socket -> - ?DEBUG("Send Text on stream = ~p", [Text]), - (StateData#state.sockmod):send_xml(StateData#state.socket, - {xmlstreamraw, Text}); -send_text(StateData, Text) when StateData#state.mgmt_state == active -> - ?DEBUG("Send XML on stream = ~p", [Text]), - case catch (StateData#state.sockmod):send(StateData#state.socket, Text) of - {'EXIT', _} -> - (StateData#state.sockmod):close(StateData#state.socket), - {error, closed}; - _ -> - ok - end; -send_text(StateData, Text) -> - ?DEBUG("Send XML on stream = ~p", [Text]), - (StateData#state.sockmod):send(StateData#state.socket, Text). - --spec send_element(state(), xmlel() | xmpp_element()) -> ok | {error, any()}. -send_element(StateData, El) when StateData#state.mgmt_state == pending -> - ?DEBUG("Cannot send element while waiting for resumption: ~p", [El]); -send_element(StateData, #xmlel{} = El) when StateData#state.xml_socket -> - ?DEBUG("Send XML on stream = ~p", [fxml:element_to_binary(El)]), - (StateData#state.sockmod):send_xml(StateData#state.socket, - {xmlstreamelement, El}); -send_element(StateData, #xmlel{} = El) -> - send_text(StateData, fxml:element_to_binary(El)); -send_element(StateData, Pkt) -> - send_element(StateData, xmpp:encode(Pkt, ?NS_CLIENT)). - --spec send_error(state(), xmlel() | stanza(), stanza_error()) -> state(). -send_error(StateData, Stanza, Error) -> - Type = xmpp:get_type(Stanza), - if Type == error; Type == result; - Type == <<"error">>; Type == <<"result">> -> - StateData; - true -> - send_stanza(StateData, xmpp:make_error(Stanza, Error)) - end. - --spec send_stanza(state(), xmpp_element()) -> state(). -send_stanza(StateData, Stanza) when StateData#state.csi_state == inactive -> - csi_filter_stanza(StateData, Stanza); -send_stanza(StateData, Stanza) when StateData#state.mgmt_state == pending -> - mgmt_queue_add(StateData, Stanza); -send_stanza(StateData, Stanza) when StateData#state.mgmt_state == active -> - NewStateData = mgmt_queue_add(StateData, Stanza), - mgmt_send_stanza(NewStateData, Stanza); -send_stanza(StateData, Stanza) -> - send_element(StateData, Stanza), - StateData. - --spec send_packet(state(), xmpp_element()) -> state(). -send_packet(StateData, Packet) -> - case xmpp:is_stanza(Packet) of - true -> - send_stanza(StateData, Packet); - false -> - send_element(StateData, Packet), - StateData - end. - --spec send_header(state(), binary(), binary(), binary()) -> ok | {error, any()}. -send_header(StateData, Server, Version, Lang) -> - Header = #xmlel{name = Name, attrs = Attrs} = - xmpp:encode(#stream_start{version = Version, - lang = Lang, - xmlns = ?NS_CLIENT, - stream_xmlns = ?NS_STREAM, - id = StateData#state.streamid, - from = jid:make(Server)}), - if StateData#state.xml_socket -> - (StateData#state.sockmod):send_xml(StateData#state.socket, - {xmlstreamstart, Name, Attrs}); - true -> - send_text(StateData, fxml:element_to_header(Header)) - end. - --spec send_trailer(state()) -> ok | {error, any()}. -send_trailer(StateData) - when StateData#state.mgmt_state == pending -> - ?DEBUG("Cannot send stream trailer while waiting for resumption", []); -send_trailer(StateData) - when StateData#state.xml_socket -> - (StateData#state.sockmod):send_xml(StateData#state.socket, - {xmlstreamend, <<"stream:stream">>}); -send_trailer(StateData) -> - send_text(StateData, ?STREAM_TRAILER). - --spec new_id() -> binary(). -new_id() -> randoms:get_string(). - --spec new_uniq_id() -> binary(). -new_uniq_id() -> - iolist_to_binary([randoms:get_string(), - integer_to_binary(p1_time_compat:unique_integer([positive]))]). - --spec get_conn_type(state()) -> c2s | c2s_tls | c2s_compressed | websocket | - c2s_compressed_tls | http_bind. -get_conn_type(StateData) -> - case (StateData#state.sockmod):get_transport(StateData#state.socket) of - tcp -> c2s; - tls -> c2s_tls; - tcp_zlib -> c2s_compressed; - tls_zlib -> c2s_compressed_tls; - http_bind -> http_bind; - websocket -> websocket - end. - --spec process_presence_probe(jid(), jid(), state()) -> ok. -process_presence_probe(From, To, StateData) -> - LFrom = jid:tolower(From), - LBFrom = setelement(3, LFrom, <<"">>), - case StateData#state.pres_last of - undefined -> - ok; - _ -> - Cond = ((?SETS):is_element(LFrom, StateData#state.pres_f) - orelse - ((LFrom /= LBFrom) andalso - (?SETS):is_element(LBFrom, StateData#state.pres_f))), - if Cond -> - %% To is the one sending the presence (the probe target) - Packet = xmpp_util:add_delay_info( - StateData#state.pres_last, To, - StateData#state.pres_timestamp), - case privacy_check_packet(StateData, To, From, Packet, out) of - deny -> - ok; - allow -> - Pid=element(2, StateData#state.sid), - ejabberd_hooks:run(presence_probe_hook, StateData#state.server, [From, To, Pid]), - %% Don't route a presence probe to oneself - case From == To of - false -> - ejabberd_router:route(To, From, Packet); - true -> - ok - end +-spec process_presence_out(state(), presence()) -> state(). +process_presence_out(#{lserver := LServer, jid := JID, + lang := Lang, pres_a := PresA} = State0, + #presence{from = From, to = To, type = Type} = Pres) -> + State1 = + if Type == subscribe; Type == subscribed; + Type == unsubscribe; Type == unsubscribed -> + Access = mod_roster_opt:access(LServer), + MyBareJID = jid:remove_resource(JID), + case acl:match_rule(LServer, Access, MyBareJID) of + deny -> + AccessErrTxt = ?T("Access denied by service policy"), + AccessErr = xmpp:err_forbidden(AccessErrTxt, Lang), + send_error(State0, Pres, AccessErr); + allow -> + ejabberd_hooks:run(roster_out_subscription, LServer, [Pres]), + State0 + end; + true -> + State0 + end, + case privacy_check_packet(State1, Pres, out) of + deny -> + PrivErrTxt = ?T("Your active privacy list has denied " + "the routing of this stanza."), + PrivErr = xmpp:err_not_acceptable(PrivErrTxt, Lang), + send_error(State1, Pres, PrivErr); + allow when Type == subscribe; Type == subscribed; + Type == unsubscribe; Type == unsubscribed -> + BareFrom = jid:remove_resource(From), + ejabberd_router:route(xmpp:set_from_to(Pres, BareFrom, To)), + State1; + allow when Type == error; Type == probe -> + ejabberd_router:route(Pres), + State1; + allow -> + ejabberd_router:route(Pres), + LTo = jid:tolower(To), + LBareTo = jid:remove_resource(LTo), + LBareFrom = jid:remove_resource(jid:tolower(From)), + if LBareTo /= LBareFrom -> + Subscription = get_subscription(From, To), + if Subscription /= both andalso Subscription /= from -> + A = case Type of + available -> ?SETS:add_element(LTo, PresA); + unavailable -> ?SETS:del_element(LTo, PresA) + end, + State1#{pres_a => A}; + true -> + State1 end; - true -> - ok + true -> + State1 end end. -%% User updates his presence (non-directed presence packet) --spec presence_update(jid(), presence(), state()) -> state(). -presence_update(From, Packet, StateData) -> - #presence{type = Type} = Packet, - case Type of - unavailable -> - Status = xmpp:get_text(Packet#presence.status), - Info = [{ip, StateData#state.ip}, - {conn, StateData#state.conn}, - {auth_module, StateData#state.auth_module}], - ejabberd_sm:unset_presence(StateData#state.sid, - StateData#state.user, - StateData#state.server, - StateData#state.resource, Status, Info), - presence_broadcast(StateData, From, - StateData#state.pres_a, Packet), - StateData#state{pres_last = undefined, - pres_timestamp = undefined, pres_a = (?SETS):new()}; - error -> StateData; - probe -> StateData; - subscribe -> StateData; - subscribed -> StateData; - unsubscribe -> StateData; - unsubscribed -> StateData; - _ -> - OldPriority = case StateData#state.pres_last of - undefined -> 0; - OldPresence -> get_priority_from_presence(OldPresence) - end, - NewPriority = get_priority_from_presence(Packet), - update_priority(NewPriority, Packet, StateData), - FromUnavail = (StateData#state.pres_last == undefined), - ?DEBUG("from unavail = ~p~n", [FromUnavail]), - NewStateData = StateData#state{pres_last = Packet, - pres_timestamp = p1_time_compat:timestamp()}, - NewState = if FromUnavail -> - ejabberd_hooks:run(user_available_hook, - NewStateData#state.server, - [NewStateData#state.jid]), - ResentStateData = if NewPriority >= 0 -> - resend_offline_messages(NewStateData), - resend_subscription_requests(NewStateData); - true -> NewStateData - end, - presence_broadcast_first(From, ResentStateData, - Packet); - true -> - presence_broadcast_to_trusted(NewStateData, From, - NewStateData#state.pres_f, - NewStateData#state.pres_a, - Packet), - if OldPriority < 0, NewPriority >= 0 -> - resend_offline_messages(NewStateData); - true -> ok - end, - NewStateData - end, - NewState - end. +-spec process_self_presence(state(), presence()) -> state(). +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), + _ = 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, true), + maps:remove(pres_last, maps:remove(pres_timestamp, State2)); +process_self_presence(#{lserver := LServer} = State, + #presence{type = available} = Pres) -> + PreviousPres = maps:get(pres_last, State, undefined), + _ = update_priority(State, Pres), + {Pres1, State1} = ejabberd_hooks:run_fold( + c2s_self_presence, LServer, {Pres, State}, []), + State2 = State1#{pres_last => Pres1, + pres_timestamp => erlang:timestamp()}, + FromUnavailable = PreviousPres == undefined, + broadcast_presence_available(State2, Pres1, FromUnavailable); +process_self_presence(State, _Pres) -> + State. -%% User sends a directed presence packet --spec presence_track(jid(), jid(), presence(), state()) -> state(). -presence_track(From, To, Packet, StateData) -> - #presence{type = Type} = Packet, - LTo = jid:tolower(To), - User = StateData#state.user, - Server = StateData#state.server, - Lang = StateData#state.lang, - case privacy_check_packet(StateData, From, To, Packet, out) of - deny -> - ErrText = <<"Your active privacy list has denied " - "the routing of this stanza.">>, - Err = xmpp:err_not_acceptable(ErrText, Lang), - send_error(StateData, xmpp:set_from_to(Packet, From, To), Err); - allow when Type == subscribe; Type == subscribed; - Type == unsubscribe; Type == unsubscribed -> - Access = gen_mod:get_module_opt(Server, mod_roster, access, - fun(A) when is_atom(A) -> A end, - all), - MyBareJID = jid:make(User, Server, <<"">>), - case acl:match_rule(Server, Access, MyBareJID) of - deny -> - ErrText = <<"Denied by ACL">>, - Err = xmpp:err_forbidden(ErrText, Lang), - send_error(StateData, xmpp:set_from_to(Packet, From, To), Err); - allow -> - ejabberd_hooks:run(roster_out_subscription, - Server, - [User, Server, To, Type]), - ejabberd_router:route(jid:remove_resource(From), To, Packet), - StateData - end; - allow when Type == error; Type == probe -> - ejabberd_router:route(From, To, Packet), - StateData; - allow -> - ejabberd_router:route(From, To, Packet), - A = case Type of - available -> - ?SETS:add_element(LTo, StateData#state.pres_a); - unavailable -> - ?SETS:del_element(LTo, StateData#state.pres_a) - end, - StateData#state{pres_a = A} - end. +-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), + ejabberd_sm:set_presence(SID, U, S, R, Priority, Pres). --spec check_privacy_route(jid(), state(), jid(), jid(), stanza()) -> state(). -check_privacy_route(From, StateData, FromRoute, To, - Packet) -> - case privacy_check_packet(StateData, From, To, Packet, - out) of +-spec broadcast_presence_unavailable(state(), presence(), boolean()) -> state(). +broadcast_presence_unavailable(#{jid := JID, pres_a := PresA} = State, Pres, + BroadcastToRoster) -> + #jid{luser = LUser, lserver = LServer} = JID, + BareJID = jid:tolower(jid:remove_resource(JID)), + Items1 = case BroadcastToRoster of + true -> + Roster = ejabberd_hooks:run_fold(roster_get, LServer, + [], [{LUser, LServer}]), + lists:foldl( + fun(#roster_item{jid = ItemJID, subscription = Sub}, Acc) + when Sub == both; Sub == from -> + maps:put(jid:tolower(ItemJID), 1, Acc); + (_, Acc) -> + Acc + end, #{BareJID => 1}, Roster); + _ -> + #{BareJID => 1} + end, + Items2 = ?SETS:fold( + fun(LJID, Acc) -> + maps:put(LJID, 1, Acc) + end, Items1, PresA), + + JIDs = lists:filtermap( + fun(LJid) -> + To = jid:make(LJid), + P = xmpp:set_to(Pres, To), + case privacy_check_packet(State, P, out) of + allow -> {true, To}; + deny -> false + end + end, maps:keys(Items2)), + route_multiple(State, JIDs, Pres), + State#{pres_a => ?SETS:new()}. + +-spec broadcast_presence_available(state(), presence(), boolean()) -> state(). +broadcast_presence_available(#{jid := JID} = State, + Pres, _FromUnavailable = true) -> + Probe = #presence{from = JID, type = probe}, + #jid{luser = LUser, lserver = LServer} = JID, + BareJID = jid:remove_resource(JID), + Items = ejabberd_hooks:run_fold(roster_get, LServer, + [], [{LUser, LServer}]), + {FJIDs, TJIDs} = + lists:foldl( + fun(#roster_item{jid = To, subscription = Sub}, {F, T}) -> + F1 = if Sub == both orelse Sub == from -> + Pres1 = xmpp:set_to(Pres, To), + case privacy_check_packet(State, Pres1, out) of + allow -> [To|F]; + deny -> F + end; + true -> F + end, + T1 = if Sub == both orelse Sub == to -> + Probe1 = xmpp:set_to(Probe, To), + case privacy_check_packet(State, Probe1, out) of + allow -> [To|T]; + deny -> T + end; + true -> T + end, + {F1, T1} + end, {[BareJID], [BareJID]}, Items), + route_multiple(State, TJIDs, Probe), + route_multiple(State, FJIDs, Pres), + State; +broadcast_presence_available(#{jid := JID} = State, + Pres, _FromUnavailable = false) -> + #jid{luser = LUser, lserver = LServer} = JID, + BareJID = jid:remove_resource(JID), + Items = ejabberd_hooks:run_fold( + roster_get, LServer, [], [{LUser, LServer}]), + JIDs = lists:foldl( + fun(#roster_item{jid = To, subscription = Sub}, Tos) + when Sub == both orelse Sub == from -> + P = xmpp:set_to(Pres, To), + case privacy_check_packet(State, P, out) of + allow -> [To|Tos]; + deny -> Tos + end; + (_, Tos) -> + Tos + end, [BareJID], Items), + route_multiple(State, JIDs, Pres), + State. + +-spec check_privacy_then_route(state(), stanza()) -> state(). +check_privacy_then_route(#{lang := Lang} = State, Pkt) -> + case privacy_check_packet(State, Pkt, out) of deny -> - Lang = StateData#state.lang, - ErrText = <<"Your active privacy list has denied " - "the routing of this stanza.">>, + ErrText = ?T("Your active privacy list has denied " + "the routing of this stanza."), Err = xmpp:err_not_acceptable(ErrText, Lang), - send_error(StateData, xmpp:set_from_to(Packet, From, To), Err); + send_error(State, Pkt, Err); allow -> - ejabberd_router:route(FromRoute, To, Packet), - StateData + ejabberd_router:route(Pkt), + State end. -%% Check if privacy rules allow this delivery --spec privacy_check_packet(state(), jid(), jid(), stanza(), in | out) -> allow | deny. -privacy_check_packet(StateData, From, To, Packet, - Dir) -> - ejabberd_hooks:run_fold(privacy_check_packet, - StateData#state.server, allow, - [StateData#state.user, StateData#state.server, - StateData#state.privacy_list, {From, To, Packet}, - Dir]). - --spec is_privacy_allow(state(), jid(), jid(), stanza(), in | out) -> boolean(). -is_privacy_allow(StateData, From, To, Packet, Dir) -> - allow == - privacy_check_packet(StateData, From, To, Packet, Dir). - -%% Send presence when disconnecting --spec presence_broadcast(state(), jid(), ?SETS:set(), presence()) -> ok. -presence_broadcast(StateData, From, JIDSet, Packet) -> - JIDs = ?SETS:to_list(JIDSet), - JIDs2 = format_and_check_privacy(From, StateData, Packet, JIDs, out), - Server = StateData#state.server, - send_multiple(From, Server, JIDs2, Packet). - --spec presence_broadcast_to_trusted( - state(), jid(), ?SETS:set(), ?SETS:set(), presence()) -> ok. -%% Send presence when updating presence -presence_broadcast_to_trusted(StateData, From, Trusted, JIDSet, Packet) -> - JIDs = ?SETS:to_list(?SETS:intersection(Trusted, JIDSet)), - JIDs2 = format_and_check_privacy(From, StateData, Packet, JIDs, out), - Server = StateData#state.server, - send_multiple(From, Server, JIDs2, Packet). - -%% Send presence when connecting --spec presence_broadcast_first(jid(), state(), presence()) -> state(). -presence_broadcast_first(From, StateData, Packet) -> - JIDsProbe = - ?SETS:fold( - fun(JID, L) -> [JID | L] end, - [], - StateData#state.pres_t), - PacketProbe = #presence{type = probe}, - JIDs2Probe = format_and_check_privacy(From, StateData, PacketProbe, JIDsProbe, out), - Server = StateData#state.server, - send_multiple(From, Server, JIDs2Probe, PacketProbe), - {As, JIDs} = - ?SETS:fold( - fun(JID, {A, JID_list}) -> - {?SETS:add_element(JID, A), JID_list++[JID]} - end, - {StateData#state.pres_a, []}, - StateData#state.pres_f), - JIDs2 = format_and_check_privacy(From, StateData, Packet, JIDs, out), - send_multiple(From, Server, JIDs2, Packet), - StateData#state{pres_a = As}. - --spec format_and_check_privacy( - jid(), state(), stanza(), [ljid()], in | out) -> [jid()]. -format_and_check_privacy(From, StateData, Packet, JIDs, Dir) -> - FJIDs = [jid:make(JID) || JID <- JIDs], - lists:filter( - fun(FJID) -> - case ejabberd_hooks:run_fold( - privacy_check_packet, StateData#state.server, - allow, - [StateData#state.user, - StateData#state.server, - StateData#state.privacy_list, - {From, FJID, Packet}, - Dir]) of - deny -> false; - allow -> true - end - end, - FJIDs). - --spec send_multiple(jid(), binary(), [jid()], stanza()) -> ok. -send_multiple(From, Server, JIDs, Packet) -> - ejabberd_router_multicast:route_multicast(From, Server, JIDs, Packet). - --spec roster_change(jid(), both | from | none | remove | to, state()) -> state(). -roster_change(IJID, ISubscription, StateData) -> - LIJID = jid:tolower(IJID), - IsFrom = (ISubscription == both) or (ISubscription == from), - IsTo = (ISubscription == both) or (ISubscription == to), - OldIsFrom = (?SETS):is_element(LIJID, StateData#state.pres_f), - FSet = if - IsFrom -> (?SETS):add_element(LIJID, StateData#state.pres_f); - true -> ?SETS:del_element(LIJID, StateData#state.pres_f) - end, - TSet = if - IsTo -> (?SETS):add_element(LIJID, StateData#state.pres_t); - true -> ?SETS:del_element(LIJID, StateData#state.pres_t) - end, - case StateData#state.pres_last of - undefined -> - StateData#state{pres_f = FSet, pres_t = TSet}; - P -> - ?DEBUG("roster changed for ~p~n", - [StateData#state.user]), - From = StateData#state.jid, - To = jid:make(IJID), - Cond1 = IsFrom andalso not OldIsFrom, - Cond2 = not IsFrom andalso OldIsFrom andalso - ((?SETS):is_element(LIJID, StateData#state.pres_a)), - if Cond1 -> - ?DEBUG("C1: ~p~n", [LIJID]), - case privacy_check_packet(StateData, From, To, P, out) - of - deny -> ok; - allow -> ejabberd_router:route(From, To, P) - end, - A = (?SETS):add_element(LIJID, StateData#state.pres_a), - StateData#state{pres_a = A, pres_f = FSet, - pres_t = TSet}; - Cond2 -> - ?DEBUG("C2: ~p~n", [LIJID]), - PU = #presence{type = unavailable}, - case privacy_check_packet(StateData, From, To, PU, out) - of - deny -> ok; - allow -> ejabberd_router:route(From, To, PU) - end, - A = ?SETS:del_element(LIJID, StateData#state.pres_a), - StateData#state{pres_a = A, pres_f = FSet, - pres_t = TSet}; - true -> StateData#state{pres_f = FSet, pres_t = TSet} - end - end. - --spec update_priority(integer(), presence(), state()) -> ok. -update_priority(Priority, Packet, StateData) -> - Info = [{ip, StateData#state.ip}, {conn, StateData#state.conn}, - {auth_module, StateData#state.auth_module}], - ejabberd_sm:set_presence(StateData#state.sid, - StateData#state.user, StateData#state.server, - StateData#state.resource, Priority, Packet, Info). +-spec privacy_check_packet(state(), stanza(), in | out) -> allow | deny. +privacy_check_packet(#{lserver := LServer} = State, Pkt, Dir) -> + ejabberd_hooks:run_fold(privacy_check_packet, LServer, allow, [State, Pkt, Dir]). -spec get_priority_from_presence(presence()) -> integer(). get_priority_from_presence(#presence{priority = Prio}) -> @@ -2021,817 +1017,148 @@ get_priority_from_presence(#presence{priority = Prio}) -> _ -> Prio end. --spec process_privacy_iq(iq(), state()) -> state(). -process_privacy_iq(#iq{from = From, to = To, - type = Type, lang = Lang} = IQ, StateData) -> - Txt = <<"No module is handling this query">>, - {Res, NewStateData} = - case Type of - get -> - R = ejabberd_hooks:run_fold( - privacy_iq_get, - StateData#state.server, - {error, xmpp:err_feature_not_implemented(Txt, Lang)}, - [IQ, StateData#state.privacy_list]), - {R, StateData}; - set -> - case ejabberd_hooks:run_fold( - privacy_iq_set, - StateData#state.server, - {error, xmpp:err_feature_not_implemented(Txt, Lang)}, - [IQ, StateData#state.privacy_list]) - of - {result, R, NewPrivList} -> - {{result, R}, - StateData#state{privacy_list = - NewPrivList}}; - R -> {R, StateData} - end - end, - IQRes = case Res of - {result, Result} -> - xmpp:make_iq_result(IQ, Result); - {error, Error} -> - xmpp:make_error(IQ, Error) - end, - ejabberd_router:route(To, From, IQRes), - NewStateData. +-spec route_multiple(state(), [jid()], stanza()) -> ok. +route_multiple(#{lserver := LServer}, JIDs, Pkt) -> + From = xmpp:get_from(Pkt), + ejabberd_router_multicast:route_multicast(From, LServer, JIDs, Pkt, false). --spec resend_offline_messages(state()) -> ok. -resend_offline_messages(#state{ask_offline = true} = StateData) -> - case ejabberd_hooks:run_fold(resend_offline_messages_hook, - StateData#state.server, [], - [StateData#state.user, StateData#state.server]) - of - Rs -> %%when is_list(Rs) -> - lists:foreach(fun ({route, From, To, Packet}) -> - Pass = case privacy_check_packet(StateData, - From, To, - Packet, in) - of - allow -> true; - deny -> false - end, - if Pass -> - ejabberd_router:route(From, To, Packet); - true -> ok - end - end, - Rs) - end; -resend_offline_messages(_StateData) -> - ok. +get_subscription(#jid{luser = LUser, lserver = LServer}, JID) -> + {Subscription, _, _} = ejabberd_hooks:run_fold( + roster_get_jid_info, LServer, {none, none, []}, + [LUser, LServer, JID]), + Subscription. --spec resend_subscription_requests(state()) -> state(). -resend_subscription_requests(#state{user = User, - server = Server} = StateData) -> - PendingSubscriptions = - ejabberd_hooks:run_fold(resend_subscription_requests_hook, - Server, [], [User, Server]), - lists:foldl(fun (XMLPacket, AccStateData) -> - send_packet(AccStateData, XMLPacket) +-spec resource_conflict_action(binary(), binary(), binary()) -> + {accept_resource, binary()} | closenew. +resource_conflict_action(U, S, R) -> + OptionRaw = case ejabberd_sm:is_existing_resource(U, S, R) of + true -> + ejabberd_option:resource_conflict(S); + false -> + acceptnew end, - StateData, - PendingSubscriptions). - --spec get_showtag(undefined | presence()) -> binary(). -get_showtag(undefined) -> <<"unavailable">>; -get_showtag(#presence{show = undefined}) -> <<"available">>; -get_showtag(#presence{show = Show}) -> atom_to_binary(Show, utf8). - --spec get_statustag(undefined | presence()) -> binary(). -get_statustag(#presence{status = Status}) -> xmpp:get_text(Status); -get_statustag(undefined) -> <<"">>. - --spec process_unauthenticated_stanza(state(), iq()) -> ok | {error, any()}. -process_unauthenticated_stanza(StateData, #iq{type = T, lang = L} = IQ) - when T == set; T == get -> - Lang = if L == undefined; L == <<"">> -> StateData#state.lang; - true -> L - end, - NewIQ = IQ#iq{lang = Lang}, - Res = ejabberd_hooks:run_fold(c2s_unauthenticated_iq, - StateData#state.server, empty, - [StateData#state.server, NewIQ, - StateData#state.ip]), - case Res of - empty -> - Txt = <<"Authentication required">>, - Err0 = xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)), - Err1 = Err0#iq{from = jid:make(<<>>, StateData#state.server, <<>>), - to = undefined}, - send_element(StateData, Err1); - _ -> - send_element(StateData, Res) - end; -process_unauthenticated_stanza(_StateData, _) -> - %% Drop any stanza, which isn't IQ stanza - ok. - --spec peerip(ejabberd_socket:sockmod(), - ejabberd_socket:socket()) -> - {inet:ip_address(), non_neg_integer()} | undefined. -peerip(SockMod, Socket) -> - IP = case SockMod of - gen_tcp -> inet:peername(Socket); - _ -> SockMod:peername(Socket) - end, - case IP of - {ok, IPOK} -> IPOK; - _ -> undefined + Option = case OptionRaw of + setresource -> setresource; + closeold -> acceptnew; %% ejabberd_sm will close old session + closenew -> closenew; + acceptnew -> acceptnew + end, + case Option of + acceptnew -> {accept_resource, R}; + closenew -> closenew; + setresource -> + Rnew = new_uniq_id(), + {accept_resource, Rnew} end. -%% fsm_next_state_pack: Pack the StateData structure to improve -%% sharing. --spec fsm_next_state_pack(state_name(), state()) -> fsm_transition(). -fsm_next_state_pack(StateName, StateData) -> - fsm_next_state_gc(StateName, pack(StateData)). - --spec fsm_next_state_gc(state_name(), state()) -> fsm_transition(). -%% fsm_next_state_gc: Garbage collect the process heap to make use of -%% the newly packed StateData structure. -fsm_next_state_gc(StateName, PackedStateData) -> - erlang:garbage_collect(), - fsm_next_state(StateName, PackedStateData). - -%% fsm_next_state: Generate the next_state FSM tuple with different -%% timeout, depending on the future state --spec fsm_next_state(state_name(), state()) -> fsm_transition(). -fsm_next_state(session_established, #state{mgmt_max_queue = exceeded} = - StateData) -> - ?WARNING_MSG("ACK queue too long, terminating session for ~s", - [jid:to_string(StateData#state.jid)]), - Err = xmpp:serr_policy_violation(<<"Too many unacked stanzas">>, - StateData#state.lang), - send_element(StateData, Err), - {stop, normal, StateData#state{mgmt_resend = false}}; -fsm_next_state(session_established, #state{mgmt_state = pending} = StateData) -> - fsm_next_state(wait_for_resume, StateData); -fsm_next_state(session_established, StateData) -> - {next_state, session_established, StateData, - ?C2S_HIBERNATE_TIMEOUT}; -fsm_next_state(wait_for_resume, #state{mgmt_timeout = 0} = StateData) -> - {stop, normal, StateData}; -fsm_next_state(wait_for_resume, #state{mgmt_pending_since = undefined, - sid = SID, jid = JID, ip = IP, - conn = Conn, auth_module = AuthModule, - server = Host} = StateData) -> - case StateData of - #state{mgmt_ack_timer = undefined} -> - ok; - #state{mgmt_ack_timer = Timer} -> - erlang:cancel_timer(Timer) - end, - ?INFO_MSG("Waiting for resumption of stream for ~s", - [jid:to_string(JID)]), - Info = [{ip, IP}, {conn, Conn}, {auth_module, AuthModule}], - NewStateData = ejabberd_hooks:run_fold(c2s_session_pending, Host, StateData, - [SID, JID, Info]), - {next_state, wait_for_resume, - NewStateData#state{mgmt_state = pending, - mgmt_pending_since = os:timestamp()}, - NewStateData#state.mgmt_timeout}; -fsm_next_state(wait_for_resume, StateData) -> - Diff = timer:now_diff(os:timestamp(), StateData#state.mgmt_pending_since), - Timeout = max(StateData#state.mgmt_timeout - Diff div 1000, 1), - {next_state, wait_for_resume, StateData, Timeout}; -fsm_next_state(StateName, StateData) -> - {next_state, StateName, StateData, ?C2S_OPEN_TIMEOUT}. - -%% fsm_reply: Generate the reply FSM tuple with different timeout, -%% depending on the future state --spec fsm_reply(_, state_name(), state()) -> fsm_reply(). -fsm_reply(Reply, session_established, StateData) -> - {reply, Reply, session_established, StateData, - ?C2S_HIBERNATE_TIMEOUT}; -fsm_reply(Reply, wait_for_resume, StateData) -> - Diff = timer:now_diff(os:timestamp(), StateData#state.mgmt_pending_since), - Timeout = max(StateData#state.mgmt_timeout - Diff div 1000, 1), - {reply, Reply, wait_for_resume, StateData, Timeout}; -fsm_reply(Reply, StateName, StateData) -> - {reply, Reply, StateName, StateData, ?C2S_OPEN_TIMEOUT}. - -%% Used by c2s blacklist plugins --spec is_ip_blacklisted(undefined | {inet:ip_address(), non_neg_integer()}, - binary()) -> false | {true, binary(), binary()}. -is_ip_blacklisted(undefined, _Lang) -> false; -is_ip_blacklisted({IP, _Port}, Lang) -> - ejabberd_hooks:run_fold(check_bl_c2s, false, [IP, Lang]). - -%% Check from attributes -%% returns invalid-from|NewElement --spec check_from(stanza(), jid()) -> 'invalid-from' | stanza(). -check_from(Pkt, FromJID) -> - JID = xmpp:get_from(Pkt), - case JID of - undefined -> - Pkt; - #jid{} -> - if - (JID#jid.luser == FromJID#jid.luser) and - (JID#jid.lserver == FromJID#jid.lserver) and - (JID#jid.lresource == FromJID#jid.lresource) -> - Pkt; - (JID#jid.luser == FromJID#jid.luser) and - (JID#jid.lserver == FromJID#jid.lserver) and - (JID#jid.lresource == <<"">>) -> - Pkt; - true -> - 'invalid-from' +-spec bounce_message_queue(ejabberd_sm:sid(), jid:jid()) -> ok. +bounce_message_queue({_, Pid} = SID, JID) -> + {U, S, R} = jid:tolower(JID), + SIDs = ejabberd_sm:get_session_sids(U, S, R), + case lists:member(SID, SIDs) of + true -> + ?WARNING_MSG("The session for ~ts@~ts/~ts is supposed to " + "be unregistered, but session identifier ~p " + "still presents in the 'session' table", + [U, S, R, Pid]); + false -> + receive {route, Pkt} -> + ejabberd_router:route(Pkt), + bounce_message_queue(SID, JID) + after 100 -> + ok end end. -fsm_limit_opts(Opts) -> - case lists:keysearch(max_fsm_queue, 1, Opts) of - {value, {_, N}} when is_integer(N) -> [{max_queue, N}]; - _ -> - case ejabberd_config:get_option( - max_fsm_queue, - fun(I) when is_integer(I), I > 0 -> I end) of - undefined -> []; - N -> [{max_queue, N}] - end +-spec new_uniq_id() -> binary(). +new_uniq_id() -> + iolist_to_binary( + [p1_rand:get_string(), + integer_to_binary(erlang:unique_integer([positive]))]). + +-spec get_conn_type(state()) -> c2s | c2s_tls | c2s_compressed | websocket | + c2s_compressed_tls | http_bind. +get_conn_type(State) -> + case xmpp_stream_in:get_transport(State) of + tcp -> c2s; + tls -> c2s_tls; + tcp_zlib -> c2s_compressed; + tls_zlib -> c2s_compressed_tls; + http_bind -> http_bind; + websocket -> websocket end. --spec bounce_messages() -> ok. -bounce_messages() -> - receive - {route, From, To, El} -> - ejabberd_router:route(From, To, El), bounce_messages() - after 0 -> ok - end. - --spec process_compression_request(compress(), state_name(), state()) -> fsm_next(). -process_compression_request(#compress{methods = []}, StateName, StateData) -> - send_element(StateData, #compress_failure{reason = 'setup-failed'}), - fsm_next_state(StateName, StateData); -process_compression_request(#compress{methods = Ms}, StateName, StateData) -> - case lists:member(<<"zlib">>, Ms) of - true -> - Socket = StateData#state.socket, - BCompressed = fxml:element_to_binary(xmpp:encode(#compressed{})), - ZlibSocket = (StateData#state.sockmod):compress(Socket, BCompressed), - fsm_next_state(wait_for_stream, - StateData#state{socket = ZlibSocket, - streamid = new_id()}); - false -> - send_element(StateData, - #compress_failure{reason = 'unsupported-method'}), - fsm_next_state(StateName, StateData) - end. - -%%%---------------------------------------------------------------------- -%%% XEP-0191 -%%%---------------------------------------------------------------------- - --spec route_blocking( - {block, [jid()]} | {unblock, [jid()]} | unblock_all, state()) -> state(). -route_blocking(What, StateData) -> - SubEl = case What of - {block, JIDs} -> - #block{items = JIDs}; - {unblock, JIDs} -> - #unblock{items = JIDs}; - unblock_all -> - #unblock{} - end, - PrivPushIQ = #iq{type = set, id = <<"push">>, sub_els = [SubEl], - from = jid:remove_resource(StateData#state.jid), - to = StateData#state.jid}, - %% No need to replace active privacy list here, - %% blocking pushes are always accompanied by - %% Privacy List pushes - send_stanza(StateData, PrivPushIQ). - -%%%---------------------------------------------------------------------- -%%% XEP-0198 -%%%---------------------------------------------------------------------- --spec stream_mgmt_enabled(state()) -> boolean(). -stream_mgmt_enabled(#state{mgmt_state = disabled}) -> - false; -stream_mgmt_enabled(_StateData) -> - true. - --spec dispatch_stream_mgmt(xmpp_element(), state()) -> state(). -dispatch_stream_mgmt(El, #state{mgmt_state = MgmtState} = StateData) - when MgmtState == active; - MgmtState == pending -> - perform_stream_mgmt(El, StateData); -dispatch_stream_mgmt(El, StateData) -> - negotiate_stream_mgmt(El, StateData). - --spec negotiate_stream_mgmt(xmpp_element(), state()) -> state(). -negotiate_stream_mgmt(_El, #state{resource = <<"">>} = StateData) -> - %% XEP-0198 says: "For client-to-server connections, the client MUST NOT - %% attempt to enable stream management until after it has completed Resource - %% Binding unless it is resuming a previous session". However, it also - %% says: "Stream management errors SHOULD be considered recoverable", so we - %% won't bail out. - send_element(StateData, #sm_failed{reason = 'unexpected-request', - xmlns = ?NS_STREAM_MGMT_3}), - StateData; -negotiate_stream_mgmt(Pkt, StateData) -> - Xmlns = xmpp:get_ns(Pkt), - case stream_mgmt_enabled(StateData) of - true -> - case Pkt of - #sm_enable{} -> - handle_enable(StateData#state{mgmt_xmlns = Xmlns}, Pkt); - _ -> - Res = if is_record(Pkt, sm_a); - is_record(Pkt, sm_r); - is_record(Pkt, sm_resume) -> - #sm_failed{reason = 'unexpected-request', - xmlns = Xmlns}; - true -> - #sm_failed{reason = 'bad-request', - xmlns = Xmlns} - end, - send_element(StateData, Res), - StateData - end; - false -> - send_element(StateData, - #sm_failed{reason = 'service-unavailable', - xmlns = Xmlns}), - StateData - end. - --spec perform_stream_mgmt(xmpp_element(), state()) -> state(). -perform_stream_mgmt(Pkt, StateData) -> - case xmpp:get_ns(Pkt) of - Xmlns when Xmlns == StateData#state.mgmt_xmlns -> - case Pkt of - #sm_r{} -> - handle_r(StateData); - #sm_a{} -> - handle_a(StateData, Pkt); - _ -> - Res = if is_record(Pkt, sm_enable); - is_record(Pkt, sm_resume) -> - #sm_failed{reason = 'unexpected-request', - xmlns = Xmlns}; - true -> - #sm_failed{reason = 'bad-request', - xmlns = Xmlns} - end, - send_element(StateData, Res), - StateData - end; - _ -> - send_element(StateData, - #sm_failed{reason = 'unsupported-version', - xmlns = StateData#state.mgmt_xmlns}) - end. - --spec handle_enable(state(), sm_enable()) -> state(). -handle_enable(#state{mgmt_timeout = DefaultTimeout, - mgmt_max_timeout = MaxTimeout} = StateData, - #sm_enable{resume = Resume, max = Max}) -> - Timeout = if Resume == false -> - 0; - Max /= undefined, Max > 0, Max =< MaxTimeout -> - Max; - true -> - DefaultTimeout - end, - Res = if Timeout > 0 -> - ?INFO_MSG("Stream management with resumption enabled for ~s", - [jid:to_string(StateData#state.jid)]), - #sm_enabled{xmlns = StateData#state.mgmt_xmlns, - id = make_resume_id(StateData), - resume = true, - max = Timeout}; - true -> - ?INFO_MSG("Stream management without resumption enabled for ~s", - [jid:to_string(StateData#state.jid)]), - #sm_enabled{xmlns = StateData#state.mgmt_xmlns} - end, - send_element(StateData, Res), - StateData#state{mgmt_state = active, - mgmt_queue = queue:new(), - mgmt_timeout = Timeout * 1000}. - --spec handle_r(state()) -> state(). -handle_r(StateData) -> - Res = #sm_a{xmlns = StateData#state.mgmt_xmlns, - h = StateData#state.mgmt_stanzas_in}, - send_element(StateData, Res), - StateData. - --spec handle_a(state(), sm_a()) -> state(). -handle_a(StateData, #sm_a{h = H}) -> - NewStateData = check_h_attribute(StateData, H), - maybe_renew_ack_request(NewStateData). - --spec handle_resume(state(), sm_resume()) -> {ok, state()} | error. -handle_resume(StateData, #sm_resume{h = H, previd = PrevID, xmlns = Xmlns}) -> - R = case stream_mgmt_enabled(StateData) of - true -> - case inherit_session_state(StateData, PrevID) of - {ok, InheritedState, Info} -> - {ok, InheritedState, Info, H}; - {error, Err, InH} -> - {error, #sm_failed{reason = 'item-not-found', - h = InH, xmlns = Xmlns}, Err}; - {error, Err} -> - {error, #sm_failed{reason = 'item-not-found', - xmlns = Xmlns}, Err} - end; - false -> - {error, #sm_failed{reason = 'service-unavailable', - xmlns = Xmlns}, - <<"XEP-0198 disabled">>} - end, - case R of - {ok, ResumedState, ResumedInfo, NumHandled} -> - NewState = check_h_attribute(ResumedState, NumHandled), - AttrXmlns = NewState#state.mgmt_xmlns, - AttrId = make_resume_id(NewState), - AttrH = NewState#state.mgmt_stanzas_in, - send_element(NewState, #sm_resumed{xmlns = AttrXmlns, - h = AttrH, - previd = AttrId}), - SendFun = fun(_F, _T, El, Time) -> - NewEl = add_resent_delay_info(NewState, El, Time), - send_element(NewState, NewEl) +-spec fix_from_to(xmpp_element(), state()) -> stanza() | xmpp_element(). +fix_from_to(Pkt, #{jid := JID}) when ?is_stanza(Pkt) -> + #jid{luser = U, lserver = S, lresource = R} = JID, + case xmpp:get_from(Pkt) of + undefined -> + Pkt; + From -> + From1 = case jid:tolower(From) of + {U, S, R} -> JID; + {U, S, _} -> jid:replace_resource(JID, From#jid.resource); + _ -> From end, - handle_unacked_stanzas(NewState, SendFun), - send_element(NewState, #sm_r{xmlns = AttrXmlns}), - NewState1 = csi_flush_queue(NewState), - NewState2 = ejabberd_hooks:run_fold(c2s_session_resumed, - StateData#state.server, - NewState1, - [NewState1#state.sid, - NewState1#state.jid, - ResumedInfo]), - ?INFO_MSG("Resumed session for ~s", - [jid:to_string(NewState2#state.jid)]), - {ok, NewState2}; - {error, El, Msg} -> - send_element(StateData, El), - ?INFO_MSG("Cannot resume session for ~s@~s: ~s", - [StateData#state.user, StateData#state.server, Msg]), - error - end. - --spec check_h_attribute(state(), non_neg_integer()) -> state(). -check_h_attribute(#state{mgmt_stanzas_out = NumStanzasOut} = StateData, H) - when H > NumStanzasOut -> - ?DEBUG("~s acknowledged ~B stanzas, but only ~B were sent", - [jid:to_string(StateData#state.jid), H, NumStanzasOut]), - mgmt_queue_drop(StateData#state{mgmt_stanzas_out = H}, NumStanzasOut); -check_h_attribute(#state{mgmt_stanzas_out = NumStanzasOut} = StateData, H) -> - ?DEBUG("~s acknowledged ~B of ~B stanzas", - [jid:to_string(StateData#state.jid), H, NumStanzasOut]), - mgmt_queue_drop(StateData, H). - --spec update_num_stanzas_in(state(), xmpp_element()) -> state(). -update_num_stanzas_in(#state{mgmt_state = MgmtState} = StateData, El) - when MgmtState == active; - MgmtState == pending -> - NewNum = case {xmpp:is_stanza(El), StateData#state.mgmt_stanzas_in} of - {true, 4294967295} -> - 0; - {true, Num} -> - Num + 1; - {false, Num} -> - Num - end, - StateData#state{mgmt_stanzas_in = NewNum}; -update_num_stanzas_in(StateData, _El) -> - StateData. - -mgmt_send_stanza(StateData, Stanza) -> - case send_element(StateData, Stanza) of - ok -> - maybe_request_ack(StateData); - _ -> - StateData#state{mgmt_state = pending} - end. - -maybe_request_ack(#state{mgmt_ack_timer = undefined} = StateData) -> - request_ack(StateData); -maybe_request_ack(StateData) -> - StateData. - -request_ack(#state{mgmt_xmlns = Xmlns, - mgmt_ack_timeout = AckTimeout} = StateData) -> - AckReq = #sm_r{xmlns = Xmlns}, - case {send_element(StateData, AckReq), AckTimeout} of - {ok, undefined} -> - ok; - {ok, Timeout} -> - Timer = erlang:send_after(Timeout, self(), close), - StateData#state{mgmt_ack_timer = Timer, - mgmt_stanzas_req = StateData#state.mgmt_stanzas_out}; - _ -> - StateData#state{mgmt_state = pending} - end. - -maybe_renew_ack_request(#state{mgmt_ack_timer = undefined} = StateData) -> - StateData; -maybe_renew_ack_request(#state{mgmt_ack_timer = Timer, - mgmt_queue = Queue, - mgmt_stanzas_out = NumStanzasOut, - mgmt_stanzas_req = NumStanzasReq} = StateData) -> - erlang:cancel_timer(Timer), - case NumStanzasReq < NumStanzasOut andalso not queue:is_empty(Queue) of - true -> - request_ack(StateData#state{mgmt_ack_timer = undefined}); - false -> - StateData#state{mgmt_ack_timer = undefined} - end. - --spec mgmt_queue_add(state(), xmpp_element()) -> state(). -mgmt_queue_add(StateData, El) -> - NewNum = case StateData#state.mgmt_stanzas_out of - 4294967295 -> - 0; - Num -> - Num + 1 - end, - NewQueue = queue:in({NewNum, p1_time_compat:timestamp(), El}, StateData#state.mgmt_queue), - NewState = StateData#state{mgmt_queue = NewQueue, - mgmt_stanzas_out = NewNum}, - check_queue_length(NewState). - --spec mgmt_queue_drop(state(), non_neg_integer()) -> state(). -mgmt_queue_drop(StateData, NumHandled) -> - NewQueue = jlib:queue_drop_while(fun({N, _T, _E}) -> N =< NumHandled end, - StateData#state.mgmt_queue), - StateData#state{mgmt_queue = NewQueue}. - --spec check_queue_length(state()) -> state(). -check_queue_length(#state{mgmt_max_queue = Limit} = StateData) - when Limit == infinity; - Limit == exceeded -> - StateData; -check_queue_length(#state{mgmt_queue = Queue, - mgmt_max_queue = Limit} = StateData) -> - case queue:len(Queue) > Limit of - true -> - StateData#state{mgmt_max_queue = exceeded}; - false -> - StateData - end. - --spec handle_unacked_stanzas(state(), fun((_, _, _, _) -> _)) -> ok. -handle_unacked_stanzas(#state{mgmt_state = MgmtState} = StateData, F) - when MgmtState == active; - MgmtState == pending; - MgmtState == timeout -> - Queue = StateData#state.mgmt_queue, - case queue:len(Queue) of - 0 -> - ok; - N -> - ?DEBUG("~B stanza(s) were not acknowledged by ~s", - [N, jid:to_string(StateData#state.jid)]), - lists:foreach( - fun({_, Time, Pkt}) -> - From = xmpp:get_from(Pkt), - To = xmpp:get_to(Pkt), - case {From, To} of - {#jid{}, #jid{}} -> - F(From, To, Pkt, Time); - {_, _} -> - ?DEBUG("Dropping stanza due to invalid JID(s)", []) - end - end, queue:to_list(Queue)) + To1 = case xmpp:get_to(Pkt) of + #jid{lresource = <<>>} = To2 -> To2; + _ -> JID + end, + xmpp:set_from_to(Pkt, From1, To1) end; -handle_unacked_stanzas(_StateData, _F) -> - ok. +fix_from_to(Pkt, _State) -> + Pkt. --spec handle_unacked_stanzas(state()) -> ok. -handle_unacked_stanzas(#state{mgmt_state = MgmtState} = StateData) - when MgmtState == active; - MgmtState == pending; - MgmtState == timeout -> - ResendOnTimeout = - case StateData#state.mgmt_resend of - Resend when is_boolean(Resend) -> - Resend; - if_offline -> - Resource = StateData#state.resource, - case ejabberd_sm:get_user_resources(StateData#state.user, - StateData#state.server) of - [Resource] -> % Same resource opened new session - true; - [] -> - true; - _ -> - false - end - end, - Lang = StateData#state.lang, - ReRoute = case ResendOnTimeout of - true -> - fun(From, To, El, Time) -> - NewEl = add_resent_delay_info(StateData, El, Time), - ejabberd_router:route(From, To, NewEl) - end; - false -> - fun(From, To, El, _Time) -> - Txt = <<"User session terminated">>, - ejabberd_router:route_error( - To, From, El, xmpp:err_service_unavailable(Txt, Lang)) - end - end, - F = fun(From, _To, #presence{}, _Time) -> - ?DEBUG("Dropping presence stanza from ~s", - [jid:to_string(From)]); - (From, To, #iq{} = El, _Time) -> - Txt = <<"User session terminated">>, - ejabberd_router:route_error( - To, From, El, xmpp:err_service_unavailable(Txt, Lang)); - (From, _To, #message{meta = #{carbon_copy := true}}, _Time) -> - %% XEP-0280 says: "When a receiving server attempts to deliver a - %% forked message, and that message bounces with an error for - %% any reason, the receiving server MUST NOT forward that error - %% back to the original sender." Resending such a stanza could - %% easily lead to unexpected results as well. - ?DEBUG("Dropping forwarded message stanza from ~s", - [jid:to_string(From)]); - (From, To, El, Time) -> - case ejabberd_hooks:run_fold(message_is_archived, - StateData#state.server, false, - [StateData, From, - StateData#state.jid, El]) of - true -> - ?DEBUG("Dropping archived message stanza from ~p", - [jid:to_string(xmpp:get_from(El))]); - false -> - ReRoute(From, To, El, Time) - end - end, - handle_unacked_stanzas(StateData, F); -handle_unacked_stanzas(_StateData) -> - ok. +-spec change_shaper(state()) -> state(). +change_shaper(#{shaper := ShaperName, ip := {IP, _}, lserver := LServer, + user := U, server := S, resource := R} = State) -> + JID = jid:make(U, S, R), + Shaper = ejabberd_shaper:match(LServer, ShaperName, + #{usr => jid:split(JID), ip => IP}), + xmpp_stream_in:change_shaper(State, ejabberd_shaper:new(Shaper)). --spec inherit_session_state(state(), binary()) -> {ok, state()} | - {error, binary()} | - {error, binary(), non_neg_integer()}. -inherit_session_state(#state{user = U, server = S} = StateData, ResumeID) -> - case jlib:base64_to_term(ResumeID) of - {term, {R, Time}} -> - case ejabberd_sm:get_session_pid(U, S, R) of - none -> - case ejabberd_sm:get_offline_info(Time, U, S, R) of - none -> - {error, <<"Previous session PID not found">>}; - Info -> - case proplists:get_value(num_stanzas_in, Info) of - undefined -> - {error, <<"Previous session timed out">>}; - H -> - {error, <<"Previous session timed out">>, H} - end - end; - OldPID -> - OldSID = {Time, OldPID}, - case catch resume_session(OldSID) of - {resume, OldStateData} -> - NewSID = {Time, self()}, % Old time, new PID - Priority = case OldStateData#state.pres_last of - undefined -> - 0; - Presence -> - get_priority_from_presence(Presence) - end, - Conn = get_conn_type(StateData), - Info = [{ip, StateData#state.ip}, {conn, Conn}, - {auth_module, StateData#state.auth_module}], - ejabberd_sm:open_session(NewSID, U, S, R, - Priority, Info), - {ok, StateData#state{conn = Conn, - sid = NewSID, - jid = OldStateData#state.jid, - resource = OldStateData#state.resource, - pres_t = OldStateData#state.pres_t, - pres_f = OldStateData#state.pres_f, - pres_a = OldStateData#state.pres_a, - pres_last = OldStateData#state.pres_last, - pres_timestamp = OldStateData#state.pres_timestamp, - privacy_list = OldStateData#state.privacy_list, - aux_fields = OldStateData#state.aux_fields, - mgmt_xmlns = OldStateData#state.mgmt_xmlns, - mgmt_queue = OldStateData#state.mgmt_queue, - mgmt_timeout = OldStateData#state.mgmt_timeout, - mgmt_stanzas_in = OldStateData#state.mgmt_stanzas_in, - mgmt_stanzas_out = OldStateData#state.mgmt_stanzas_out, - mgmt_state = active, - csi_state = active}, Info}; - {error, Msg} -> - {error, Msg}; - _ -> - {error, <<"Cannot grab session state">>} - end - end; - _ -> - {error, <<"Invalid 'previd' value">>} - end. +-spec format_reason(state(), term()) -> binary(). +format_reason(#{stop_reason := Reason}, _) -> + xmpp_stream_in:format_error(Reason); +format_reason(_, normal) -> + <<"unknown reason">>; +format_reason(_, shutdown) -> + <<"stopped by supervisor">>; +format_reason(_, {shutdown, _}) -> + <<"stopped by supervisor">>; +format_reason(_, _) -> + <<"internal server error">>. --spec resume_session({integer(), pid()}) -> any(). -resume_session({Time, PID}) -> - (?GEN_FSM):sync_send_all_state_event(PID, {resume_session, Time}, 15000). +listen_opt_type(starttls) -> + econf:bool(); +listen_opt_type(starttls_required) -> + econf:bool(); +listen_opt_type(allow_unencrypted_sasl2) -> + econf:bool(); +listen_opt_type(tls_verify) -> + econf:bool(); +listen_opt_type(zlib) -> + econf:and_then( + econf:bool(), + fun(false) -> false; + (true) -> + ejabberd:start_app(ezlib), + true + end). --spec make_resume_id(state()) -> binary(). -make_resume_id(StateData) -> - {Time, _} = StateData#state.sid, - jlib:term_to_base64({StateData#state.resource, Time}). - --spec add_resent_delay_info(state(), stanza(), erlang:timestamp()) -> stanza(). -add_resent_delay_info(_State, #iq{} = El, _Time) -> - El; -add_resent_delay_info(#state{server = From}, El, Time) -> - xmpp_util:add_delay_info(El, jid:make(From), Time, <<"Resent">>). - -%%%---------------------------------------------------------------------- -%%% XEP-0352 -%%%---------------------------------------------------------------------- --spec csi_filter_stanza(state(), stanza()) -> state(). -csi_filter_stanza(#state{csi_state = CsiState, jid = JID, server = Server} = - StateData, Stanza) -> - {StateData1, Stanzas} = ejabberd_hooks:run_fold(csi_filter_stanza, Server, - {StateData, [Stanza]}, - [Server, JID, Stanza]), - StateData2 = lists:foldl(fun(CurStanza, AccState) -> - send_stanza(AccState, CurStanza) - end, StateData1#state{csi_state = active}, - Stanzas), - StateData2#state{csi_state = CsiState}. - --spec csi_flush_queue(state()) -> state(). -csi_flush_queue(#state{csi_state = CsiState, jid = JID, server = Server} = - StateData) -> - {StateData1, Stanzas} = ejabberd_hooks:run_fold(csi_flush_queue, Server, - {StateData, []}, - [Server, JID]), - StateData2 = lists:foldl(fun(CurStanza, AccState) -> - send_stanza(AccState, CurStanza) - end, StateData1#state{csi_state = active}, - Stanzas), - StateData2#state{csi_state = CsiState}. - -%%%---------------------------------------------------------------------- -%%% JID Set memory footprint reduction code -%%%---------------------------------------------------------------------- - -%% Try to reduce the heap footprint of the four presence sets -%% by ensuring that we re-use strings and Jids wherever possible. --spec pack(state()) -> state(). -pack(S = #state{pres_a = A, pres_f = F, - pres_t = T}) -> - {NewA, Pack2} = pack_jid_set(A, gb_trees:empty()), - {NewF, Pack3} = pack_jid_set(F, Pack2), - {NewT, _Pack4} = pack_jid_set(T, Pack3), - S#state{pres_a = NewA, pres_f = NewF, - pres_t = NewT}. - -pack_jid_set(Set, Pack) -> - Jids = (?SETS):to_list(Set), - {PackedJids, NewPack} = pack_jids(Jids, Pack, []), - {(?SETS):from_list(PackedJids), NewPack}. - -pack_jids([], Pack, Acc) -> {Acc, Pack}; -pack_jids([{U, S, R} = Jid | Jids], Pack, Acc) -> - case gb_trees:lookup(Jid, Pack) of - {value, PackedJid} -> - pack_jids(Jids, Pack, [PackedJid | Acc]); - none -> - {NewU, Pack1} = pack_string(U, Pack), - {NewS, Pack2} = pack_string(S, Pack1), - {NewR, Pack3} = pack_string(R, Pack2), - NewJid = {NewU, NewS, NewR}, - NewPack = gb_trees:insert(NewJid, NewJid, Pack3), - pack_jids(Jids, NewPack, [NewJid | Acc]) - end. - -pack_string(String, Pack) -> - case gb_trees:lookup(String, Pack) of - {value, PackedString} -> {PackedString, Pack}; - none -> {String, gb_trees:insert(String, String, Pack)} - end. - -transform_listen_option(Opt, Opts) -> - [Opt|Opts]. - --spec identity([{atom(), binary()}]) -> binary(). -identity(Props) -> - case proplists:get_value(authzid, Props, <<>>) of - <<>> -> proplists:get_value(username, Props, <<>>); - AuthzId -> AuthzId - end. - -opt_type(domain_certfile) -> fun iolist_to_binary/1; -opt_type(max_fsm_queue) -> - fun (I) when is_integer(I), I > 0 -> I end; -opt_type(resource_conflict) -> - fun (setresource) -> setresource; - (closeold) -> closeold; - (closenew) -> closenew; - (acceptnew) -> acceptnew - end; -opt_type(_) -> - [domain_certfile, max_fsm_queue, resource_conflict]. +listen_options() -> + [{access, all}, + {shaper, none}, + {ciphers, undefined}, + {dhfile, undefined}, + {cafile, undefined}, + {protocol_options, undefined}, + {tls, false}, + {tls_compression, false}, + {starttls, false}, + {starttls_required, false}, + {allow_unencrypted_sasl2, false}, + {tls_verify, false}, + {zlib, false}, + {max_stanza_size, infinity}, + {max_fsm_queue, 10000}]. diff --git a/src/ejabberd_c2s_config.erl b/src/ejabberd_c2s_config.erl index 3384e3387..0c80ebec9 100644 --- a/src/ejabberd_c2s_config.erl +++ b/src/ejabberd_c2s_config.erl @@ -6,7 +6,7 @@ %%% Created : 2 Nov 2007 by Mickael Remond %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -26,45 +26,27 @@ -module(ejabberd_c2s_config). --behaviour(ejabberd_config). - -author('mremond@process-one.net'). --export([get_c2s_limits/0, opt_type/1]). +-export([get_c2s_limits/0]). %% Get first c2s configuration limitations to apply it to other c2s %% connectors. get_c2s_limits() -> - case ejabberd_config:get_option(listen, fun(V) -> V end) of - undefined -> []; - C2SFirstListen -> - case lists:keysearch(ejabberd_c2s, 2, C2SFirstListen) of - false -> []; - {value, {_Port, ejabberd_c2s, Opts}} -> - select_opts_values(Opts) - end + C2SFirstListen = ejabberd_option:listen(), + case lists:keysearch(ejabberd_c2s, 2, C2SFirstListen) of + false -> []; + {value, {_Port, ejabberd_c2s, Opts}} -> + select_opts_values(Opts) end. + %% Only get access, shaper and max_stanza_size values - select_opts_values(Opts) -> - select_opts_values(Opts, []). - -select_opts_values([], SelectedValues) -> - SelectedValues; -select_opts_values([{access, Value} | Opts], - SelectedValues) -> - select_opts_values(Opts, - [{access, Value} | SelectedValues]); -select_opts_values([{shaper, Value} | Opts], - SelectedValues) -> - select_opts_values(Opts, - [{shaper, Value} | SelectedValues]); -select_opts_values([{max_stanza_size, Value} | Opts], - SelectedValues) -> - select_opts_values(Opts, - [{max_stanza_size, Value} | SelectedValues]); -select_opts_values([_Opt | Opts], SelectedValues) -> - select_opts_values(Opts, SelectedValues). - -opt_type(listen) -> fun (V) -> V end; -opt_type(_) -> [listen]. + maps:fold( + fun(Opt, Val, Acc) when Opt == access; + Opt == shaper; + Opt == max_stanza_size -> + [{Opt, Val}|Acc]; + (_, _, Acc) -> + Acc + end, [], Opts). diff --git a/src/ejabberd_captcha.erl b/src/ejabberd_captcha.erl index f959d7f30..d1d62e59b 100644 --- a/src/ejabberd_captcha.erl +++ b/src/ejabberd_captcha.erl @@ -5,7 +5,7 @@ %%% Created : 26 Apr 2008 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -25,9 +25,8 @@ -module(ejabberd_captcha). --behaviour(ejabberd_config). - --protocol({xep, 158, '1.0'}). +-protocol({xep, 158, '1.0', '2.1.0', "complete", ""}). +-protocol({xep, 231, '1.0', '2.1.0', "complete", ""}). -behaviour(gen_server). @@ -41,22 +40,26 @@ -export([create_captcha/6, build_captcha_html/2, check_captcha/2, process_reply/1, process/2, is_feature_available/0, create_captcha_x/5, - opt_type/1]). + host_up/1, host_down/1, + config_reloaded/0, process_iq/1]). --include("xmpp.hrl"). --include("ejabberd.hrl"). +-include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). -include("ejabberd_http.hrl"). +-include("translate.hrl"). -define(CAPTCHA_LIFETIME, 120000). -define(LIMIT_PERIOD, 60*1000*1000). -type image_error() :: efbig | enodata | limit | malformed_image | timeout. +-type priority() :: neg_integer(). +-type callback() :: fun((captcha_succeed | captcha_failed) -> any()). --record(state, {limits = treap:empty() :: treap:treap()}). +-record(state, {limits = treap:empty() :: treap:treap(), + enabled = false :: boolean()}). -record(captcha, {id :: binary(), - pid :: pid(), + pid :: pid() | undefined, key :: binary(), tref :: reference(), args :: any()}). @@ -65,94 +68,78 @@ start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). --spec captcha_text(undefined | binary()) -> binary(). +-spec captcha_text(binary()) -> binary(). captcha_text(Lang) -> - translate:translate(Lang, <<"Enter the text you see">>). + translate:translate(Lang, ?T("Enter the text you see")). --spec mk_ocr_field(binary() | undefined, binary(), binary()) -> xdata_field(). +-spec mk_ocr_field(binary(), binary(), binary()) -> xdata_field(). mk_ocr_field(Lang, CID, Type) -> URI = #media_uri{type = Type, uri = <<"cid:", CID/binary>>}, - #xdata_field{var = <<"ocr">>, - type = 'text-single', - label = captcha_text(Lang), - required = true, - sub_els = [#media{uri = [URI]}]}. + [_, F] = captcha_form:encode([{ocr, <<>>}], Lang, [ocr]), + xmpp:set_els(F, [#media{uri = [URI]}]). -mk_field(Type, Var, Value) -> - #xdata_field{type = Type, var = Var, values = [Value]}. +update_captcha_key(_Id, Key, Key) -> + ok; +update_captcha_key(Id, _Key, Key2) -> + true = ets:update_element(captcha, Id, [{4, Key2}]). -spec create_captcha(binary(), jid(), jid(), - binary(), any(), any()) -> {error, image_error()} | - {ok, binary(), [text()], [xmlel()]}. - + binary(), any(), + callback() | term()) -> {error, image_error()} | + {ok, binary(), [text()], [xmpp_element()]}. create_captcha(SID, From, To, Lang, Limiter, Args) -> case create_image(Limiter) of {ok, Type, Key, Image} -> - Id = <<(randoms:get_string())/binary>>, - JID = jid:to_string(From), - CID = <<"sha1+", (p1_sha:sha(Image))/binary, "@bob.xmpp.org">>, - Data = #bob_data{cid = CID, 'max-age' = 0, type = Type, - data = Image}, - Fs = [mk_field(hidden, <<"FORM_TYPE">>, ?NS_CAPTCHA), - mk_field(hidden, <<"from">>, jid:to_string(To)), - mk_field(hidden, <<"challenge">>, Id), - mk_field(hidden, <<"sid">>, SID), - mk_ocr_field(Lang, CID, Type)], - X = #xdata{type = form, fields = Fs}, - Captcha = #xcaptcha{xdata = X}, - BodyString1 = translate:translate(Lang, - <<"Your messages to ~s are being blocked. " - "To unblock them, visit ~s">>), - BodyString = (str:format(BodyString1, - [JID, get_url(Id)])), - Body = xmpp:mk_text(BodyString, Lang), - OOB = #oob_x{url = get_url(Id)}, - Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, - {remove_id, Id}), - ets:insert(captcha, - #captcha{id = Id, pid = self(), key = Key, tref = Tref, - args = Args}), - {ok, Id, Body, [OOB, Captcha, Data]}; - Err -> Err + Id = <<(p1_rand:get_string())/binary>>, + JID = jid:encode(From), + CID = <<"sha1+", (str:sha(Image))/binary, "@bob.xmpp.org">>, + Data = #bob_data{cid = CID, 'max-age' = 0, type = Type, data = Image}, + Fs = captcha_form:encode( + [{from, To}, {challenge, Id}, {sid, SID}, + mk_ocr_field(Lang, CID, Type)], + Lang, [challenge]), + X = #xdata{type = form, fields = Fs}, + Captcha = #xcaptcha{xdata = X}, + BodyString = {?T("Your subscription request and/or messages to ~s have been blocked. " + "To unblock your subscription request, visit ~s"), [JID, get_url(Id)]}, + Body = xmpp:mk_text(BodyString, Lang), + OOB = #oob_x{url = get_url(Id)}, + Hint = #hint{type = 'no-store'}, + Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, {remove_id, Id}), + ets:insert(captcha, + #captcha{id = Id, pid = self(), key = Key, tref = Tref, + args = Args}), + {ok, Id, Body, [Hint, OOB, Captcha, Data]}; + Err -> Err end. -spec create_captcha_x(binary(), jid(), binary(), any(), xdata()) -> - {ok, xdata()} | {error, image_error()}. - + {ok, [xmpp_element()]} | {error, image_error()}. create_captcha_x(SID, To, Lang, Limiter, #xdata{fields = Fs} = X) -> case create_image(Limiter) of {ok, Type, Key, Image} -> - Id = <<(randoms:get_string())/binary>>, - CID = <<"sha1+", (p1_sha:sha(Image))/binary, "@bob.xmpp.org">>, - Data = #bob_data{cid = CID, 'max-age' = 0, type = Type, data = Image}, - HelpTxt = translate:translate(Lang, - <<"If you don't see the CAPTCHA image here, " - "visit the web page.">>), - Imageurl = get_url(<>), - NewFs = [mk_field(hidden, <<"FORM_TYPE">>, ?NS_CAPTCHA)|Fs] ++ - [#xdata_field{type = fixed, values = [HelpTxt]}, - #xdata_field{type = hidden, var = <<"captchahidden">>, - values = [<<"workaround-for-psi">>]}, - #xdata_field{type = 'text-single', var = <<"url">>, - label = translate:translate( - Lang, <<"CAPTCHA web page">>), - values = [Imageurl]}, - mk_field(hidden, <<"from">>, jid:to_string(To)), - mk_field(hidden, <<"challenge">>, Id), - mk_field(hidden, <<"sid">>, SID), - mk_ocr_field(Lang, CID, Type)], - Captcha = X#xdata{type = form, fields = NewFs}, - Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, - {remove_id, Id}), - ets:insert(captcha, - #captcha{id = Id, key = Key, tref = Tref}), - {ok, [Captcha, Data]}; - Err -> Err + Id = <<(p1_rand:get_string())/binary>>, + CID = <<"sha1+", (str:sha(Image))/binary, "@bob.xmpp.org">>, + Data = #bob_data{cid = CID, 'max-age' = 0, type = Type, data = Image}, + HelpTxt = translate:translate( + Lang, ?T("If you don't see the CAPTCHA image here, visit the web page.")), + Imageurl = get_url(<>), + [H|T] = captcha_form:encode( + [{'captcha-fallback-text', HelpTxt}, + {'captcha-fallback-url', Imageurl}, + {from, To}, {challenge, Id}, {sid, SID}, + mk_ocr_field(Lang, CID, Type)], + Lang, [challenge]), + Captcha = X#xdata{type = form, fields = [H|Fs ++ T]}, + Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, {remove_id, Id}), + ets:insert(captcha, #captcha{id = Id, key = Key, tref = Tref}), + {ok, [Captcha, Data]}; + Err -> Err end. -spec build_captcha_html(binary(), binary()) -> captcha_not_found | {xmlel(), - {xmlel(), xmlel(), + {xmlel(), cdata(), xmlel(), xmlel()}}. build_captcha_html(Id, Lang) -> @@ -162,7 +149,7 @@ build_captcha_html(Id, Lang) -> attrs = [{<<"src">>, get_url(<>)}], children = []}, - TextEl = {xmlcdata, captcha_text(Lang)}, + Text = {xmlcdata, captcha_text(Lang)}, IdEl = #xmlel{name = <<"input">>, attrs = [{<<"type">>, <<"hidden">>}, {<<"name">>, <<"id">>}, @@ -182,7 +169,7 @@ build_captcha_html(Id, Lang) -> [ImgEl, #xmlel{name = <<"br">>, attrs = [], children = []}, - TextEl, + Text, #xmlel{name = <<"br">>, attrs = [], children = []}, IdEl, KeyEl, @@ -192,24 +179,32 @@ build_captcha_html(Id, Lang) -> attrs = [{<<"type">>, <<"submit">>}, {<<"name">>, <<"enter">>}, - {<<"value">>, <<"OK">>}], + {<<"value">>, ?T("OK")}], children = []}]}, - {FormEl, {ImgEl, TextEl, IdEl, KeyEl}}; + {FormEl, {ImgEl, Text, IdEl, KeyEl}}; _ -> captcha_not_found end. -spec process_reply(xmpp_element()) -> ok | {error, bad_match | not_found | malformed}. process_reply(#xdata{} = X) -> - case {xmpp_util:get_xdata_values(<<"challenge">>, X), - xmpp_util:get_xdata_values(<<"ocr">>, X)} of - {[Id], [OCR]} -> + Required = [<<"challenge">>, <<"ocr">>], + Fs = lists:filter( + fun(#xdata_field{var = Var}) -> + lists:member(Var, [<<"FORM_TYPE">>|Required]) + end, X#xdata.fields), + try captcha_form:decode(Fs, [?NS_CAPTCHA], Required) of + Props -> + Id = proplists:get_value(challenge, Props), + OCR = proplists:get_value(ocr, Props), case check_captcha(Id, OCR) of captcha_valid -> ok; captcha_non_valid -> {error, bad_match}; captcha_not_found -> {error, not_found} - end; - _ -> + end + catch _:{captcha_form, Why} -> + ?WARNING_MSG("Malformed CAPTCHA form: ~ts", + [captcha_form:format_error(Why)]), {error, malformed} end; process_reply(#xcaptcha{xdata = #xdata{} = X}) -> @@ -217,11 +212,30 @@ process_reply(#xcaptcha{xdata = #xdata{} = X}) -> process_reply(_) -> {error, malformed}. +-spec process_iq(iq()) -> iq(). +process_iq(#iq{type = set, lang = Lang, sub_els = [#xcaptcha{} = El]} = IQ) -> + case process_reply(El) of + ok -> + xmpp:make_iq_result(IQ); + {error, malformed} -> + Txt = ?T("Incorrect CAPTCHA submit"), + xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)); + {error, _} -> + Txt = ?T("The CAPTCHA verification has failed"), + xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)) + end; +process_iq(#iq{type = get, lang = Lang} = IQ) -> + Txt = ?T("Value 'get' 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)). + process(_Handlers, #request{method = 'GET', lang = Lang, path = [_, Id]}) -> case build_captcha_html(Id, Lang) of - {FormEl, _} when is_tuple(FormEl) -> + {FormEl, _} -> Form = #xmlel{name = <<"div">>, attrs = [{<<"align">>, <<"center">>}], children = [FormEl]}, @@ -235,7 +249,8 @@ process(_Handlers, case lookup_captcha(Id) of {ok, #captcha{key = Key}} -> case create_image(Addr, Key) of - {ok, Type, _, Img} -> + {ok, Type, Key2, Img} -> + update_captcha_key(Id, Key, Key2), {200, [{<<"Content-Type">>, Type}, {<<"Cache-Control">>, <<"no-cache">>}, @@ -256,7 +271,7 @@ process(_Handlers, children = [{xmlcdata, translate:translate(Lang, - <<"The CAPTCHA is valid.">>)}]}, + ?T("The CAPTCHA is valid."))}]}, ejabberd_web:make_xhtml([Form]); captcha_non_valid -> ejabberd_web:error(not_allowed); captcha_not_found -> ejabberd_web:error(not_found) @@ -264,12 +279,29 @@ process(_Handlers, process(_Handlers, _Request) -> ejabberd_web:error(not_found). +host_up(Host) -> + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_CAPTCHA, + ?MODULE, process_iq). + +host_down(Host) -> + gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_CAPTCHA). + +config_reloaded() -> + gen_server:call(?MODULE, config_reloaded, timer:minutes(1)). + init([]) -> - mnesia:delete_table(captcha), - ets:new(captcha, - [named_table, public, {keypos, #captcha.id}]), - check_captcha_setup(), - {ok, #state{}}. + _ = mnesia:delete_table(captcha), + _ = ets:new(captcha, [named_table, public, {keypos, #captcha.id}]), + case check_captcha_setup() of + true -> + register_handlers(), + ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 70), + {ok, #state{enabled = true}}; + false -> + {ok, #state{enabled = false}}; + {error, Reason} -> + {stop, Reason} + end. handle_call({is_limited, Limiter, RateLimit}, _From, State) -> @@ -288,43 +320,98 @@ handle_call({is_limited, Limiter, RateLimit}, _From, Limits), {reply, false, State#state{limits = NewLimits}} end; -handle_call(_Request, _From, State) -> - {reply, bad_request, State}. +handle_call(config_reloaded, _From, #state{enabled = Enabled} = State) -> + State1 = case is_feature_available() of + true when not Enabled -> + case check_captcha_setup() of + true -> + register_handlers(), + State#state{enabled = true}; + _ -> + State + end; + false when Enabled -> + unregister_handlers(), + State#state{enabled = false}; + _ -> + State + end, + {reply, ok, State1}; +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), + {noreply, State}. -handle_cast(_Msg, State) -> {noreply, State}. +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), + {noreply, State}. handle_info({remove_id, Id}, State) -> - ?DEBUG("captcha ~p timed out", [Id]), + ?DEBUG("CAPTCHA ~p timed out", [Id]), case ets:lookup(captcha, Id) of - [#captcha{args = Args, pid = Pid}] -> - if is_pid(Pid) -> Pid ! {captcha_failed, Args}; - true -> ok - end, - ets:delete(captcha, Id); - _ -> ok + [#captcha{args = Args, pid = Pid}] -> + callback(captcha_failed, Pid, Args), + ets:delete(captcha, Id); + _ -> ok end, {noreply, State}; -handle_info(_Info, State) -> {noreply, State}. +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), + {noreply, State}. -terminate(_Reason, _State) -> ok. +terminate(_Reason, #state{enabled = Enabled}) -> + if Enabled -> unregister_handlers(); + true -> ok + end, + ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 70). + +register_handlers() -> + ejabberd_hooks:add(host_up, ?MODULE, host_up, 50), + ejabberd_hooks:add(host_down, ?MODULE, host_down, 50), + lists:foreach(fun host_up/1, ejabberd_option:hosts()). + +unregister_handlers() -> + ejabberd_hooks:delete(host_up, ?MODULE, host_up, 50), + ejabberd_hooks:delete(host_down, ?MODULE, host_down, 50), + lists:foreach(fun host_down/1, ejabberd_option:hosts()). code_change(_OldVsn, State, _Extra) -> {ok, State}. -create_image() -> create_image(undefined). +-spec create_image() -> {ok, binary(), binary(), binary()} | + {error, image_error()}. +create_image() -> + create_image(undefined). +-spec create_image(term()) -> {ok, binary(), binary(), binary()} | + {error, image_error()}. create_image(Limiter) -> - Key = str:substr(randoms:get_string(), 1, 6), + Key = str:substr(p1_rand:get_string(), 1, 6), create_image(Limiter, Key). +-spec create_image(term(), binary()) -> {ok, binary(), binary(), binary()} | + {error, image_error()}. create_image(Limiter, Key) -> case is_limited(Limiter) of - true -> {error, limit}; - false -> do_create_image(Key) + true -> {error, limit}; + false -> do_create_image(Key) end. +-spec do_create_image(binary()) -> {ok, binary(), binary(), binary()} | + {error, image_error()}. do_create_image(Key) -> FileName = get_prog_name(), - Cmd = lists:flatten(io_lib:format("~s ~s", [FileName, Key])), + case length(binary:split(FileName, <<"/">>)) == 1 of + true -> + do_create_image(Key, misc:binary_to_atom(FileName)); + false -> + do_create_image(Key, FileName) + end. + +do_create_image(Key, Module) when is_atom(Module) -> + Function = create_image, + erlang:apply(Module, Function, [Key]); + +do_create_image(Key, FileName) when is_binary(FileName) -> + Cmd = lists:flatten(io_lib:format("~ts ~ts", [FileName, Key])), case cmd(Cmd) of {ok, <<137, $P, $N, $G, $\r, $\n, 26, $\n, _/binary>> = @@ -336,102 +423,155 @@ do_create_image(Key) -> when X == $7; X == $9 -> {ok, <<"image/gif">>, Key, Img}; {error, enodata = Reason} -> - ?ERROR_MSG("Failed to process output from \"~s\". " + ?ERROR_MSG("Failed to process output from \"~ts\". " "Maybe ImageMagick's Convert program " "is not installed.", [Cmd]), {error, Reason}; {error, Reason} -> - ?ERROR_MSG("Failed to process an output from \"~s\": ~p", + ?ERROR_MSG("Failed to process an output from \"~ts\": ~p", [Cmd, Reason]), {error, Reason}; _ -> Reason = malformed_image, - ?ERROR_MSG("Failed to process an output from \"~s\": ~p", + ?ERROR_MSG("Failed to process an output from \"~ts\": ~p", [Cmd, Reason]), {error, Reason} end. get_prog_name() -> - case ejabberd_config:get_option( - captcha_cmd, - fun(FileName) -> - F = iolist_to_binary(FileName), - if F /= <<"">> -> F end - end) of + case ejabberd_option:captcha_cmd() of undefined -> - ?DEBUG("The option captcha_cmd is not configured, " + ?WARNING_MSG("The option captcha_cmd is not configured, " "but some module wants to use the CAPTCHA " "feature.", []), false; FileName -> + maybe_warning_norequesthandler(), FileName end. -get_url(Str) -> - CaptchaHost = ejabberd_config:get_option( - captcha_host, - fun iolist_to_binary/1, - <<"">>), - case str:tokens(CaptchaHost, <<":">>) of - [Host] -> - <<"http://", Host/binary, "/captcha/", Str/binary>>; - [<<"http", _/binary>> = TransferProt, Host] -> - <>; - [Host, PortString] -> - TransferProt = - iolist_to_binary(atom_to_list(get_transfer_protocol(PortString))), - <>; - [TransferProt, Host, PortString] -> - <>; - _ -> - <<"http://", (?MYNAME)/binary, "/captcha/", Str/binary>> +maybe_warning_norequesthandler() -> + Host = hd(ejabberd_option:hosts()), + AutoURL = get_auto_url(any, ?MODULE, Host), + ManualURL = ejabberd_option:captcha_url(), + case (AutoURL == undefined) and not is_binary(ManualURL) of + true -> + ?CRITICAL_MSG("The option captcha_cmd is configured " + "and captcha_url is set to auto, " + "but I couldn't find a request_handler in listen option " + "configured with ejabberd_captcha and integer port. " + "Please setup the URL with option captcha_url, see " + "https://docs.ejabberd.im/admin/configuration/basic/#captcha", + []); + _ -> + ok end. +-spec get_url(binary()) -> binary(). +get_url(Str) -> + case ejabberd_option:captcha_url() of + auto -> + Host = ejabberd_config:get_myname(), + URL = get_auto_url(any, ?MODULE, Host), + <>; + undefined -> + URL = parse_captcha_host(), + <>; + URL -> + <> + end. + +-spec parse_captcha_host() -> binary(). +parse_captcha_host() -> + CaptchaHost = ejabberd_option:captcha_host(), + case str:tokens(CaptchaHost, <<":">>) of + [Host] -> + <<"http://", Host/binary>>; + [<<"http", _/binary>> = TransferProt, Host] -> + <>; + [Host, PortString] -> + TransferProt = atom_to_binary(get_transfer_protocol(PortString), latin1), + <>; + [TransferProt, Host, PortString] -> + <>; + _ -> + <<"http://", (ejabberd_config:get_myname())/binary>> + end. + +get_auto_url(Tls, Module, Host) -> + case find_handler_port_path(Tls, Module) of + [] -> undefined; + TPPs -> + {ThisTls, Port, Path} = case lists:keyfind(true, 1, TPPs) of + false -> + lists:keyfind(false, 1, TPPs); + TPP -> + TPP + end, + Protocol = case ThisTls of + false -> <<"http">>; + true -> <<"https">> + end, + <>))/binary>> + end. + +find_handler_port_path(Tls, Module) -> + lists:filtermap( + fun({{Port, _, _}, + ejabberd_http, + #{tls := ThisTls, request_handlers := Handlers}}) + when is_integer(Port) and ((Tls == any) or (Tls == ThisTls)) -> + case lists:keyfind(Module, 2, Handlers) of + false -> false; + {Path, Module} -> {true, {ThisTls, Port, Path}} + end; + (_) -> false + end, ets:tab2list(ejabberd_listener)). + get_transfer_protocol(PortString) -> PortNumber = binary_to_integer(PortString), PortListeners = get_port_listeners(PortNumber), get_captcha_transfer_protocol(PortListeners). get_port_listeners(PortNumber) -> - AllListeners = ejabberd_config:get_option(listen, fun(V) -> V end), - lists:filter(fun (Listener) when is_list(Listener) -> - case proplists:get_value(port, Listener) of - PortNumber -> true; - _ -> false - end; - (_) -> false - end, - AllListeners). + AllListeners = ejabberd_option:listen(), + lists:filter( + fun({{Port, _IP, _Transport}, _Module, _Opts}) -> + Port == PortNumber + end, AllListeners). get_captcha_transfer_protocol([]) -> throw(<<"The port number mentioned in captcha_host " "is not a ejabberd_http listener with " "'captcha' option. Change the port number " "or specify http:// in that option.">>); -get_captcha_transfer_protocol([Listener | Listeners]) when is_list(Listener) -> - case proplists:get_value(module, Listener) == ejabberd_http andalso - proplists:get_bool(captcha, Listener) of - true -> - case proplists:get_bool(tls, Listener) of - true -> https; - false -> http - end; - false -> get_captcha_transfer_protocol(Listeners) +get_captcha_transfer_protocol([{_, ejabberd_http, Opts} | Listeners]) -> + Handlers = maps:get(request_handlers, Opts, []), + case lists:any( + fun({_, ?MODULE}) -> true; + ({_, _}) -> false + end, Handlers) of + true -> + case maps:get(tls, Opts) of + true -> https; + false -> http + end; + false -> + get_captcha_transfer_protocol(Listeners) end; get_captcha_transfer_protocol([_ | Listeners]) -> get_captcha_transfer_protocol(Listeners). is_limited(undefined) -> false; is_limited(Limiter) -> - case ejabberd_config:get_option( - captcha_limit, - fun(I) when is_integer(I), I > 0 -> I end) of - undefined -> false; + case ejabberd_option:captcha_limit() of + infinity -> false; Int -> case catch gen_server:call(?MODULE, {is_limited, Limiter, Int}, 5000) @@ -446,12 +586,14 @@ is_limited(Limiter) -> -define(MAX_FILE_SIZE, 64 * 1024). +-spec cmd(string()) -> {ok, binary()} | {error, image_error()}. cmd(Cmd) -> Port = open_port({spawn, Cmd}, [stream, eof, binary]), TRef = erlang:start_timer(?CMD_TIMEOUT, self(), timeout), recv_data(Port, TRef, <<>>). +-spec recv_data(port(), reference(), binary()) -> {ok, binary()} | {error, image_error()}. recv_data(Port, TRef, Buf) -> receive {Port, {data, Bytes}} -> @@ -468,39 +610,40 @@ recv_data(Port, TRef, Buf) -> return(Port, TRef, {error, timeout}) end. +-spec return(port(), reference(), {ok, binary()} | {error, image_error()}) -> + {ok, binary()} | {error, image_error()}. return(Port, TRef, Result) -> - case erlang:cancel_timer(TRef) of - false -> - receive {timeout, TRef, _} -> ok after 0 -> ok end; - _ -> ok - end, + misc:cancel_timer(TRef), catch port_close(Port), Result. is_feature_available() -> case get_prog_name() of - Prog when is_binary(Prog) -> true; + PathOrModule when is_binary(PathOrModule) -> true; false -> false end. check_captcha_setup() -> case is_feature_available() of - true -> - case create_image() of - {ok, _, _, _} -> ok; - _Err -> - ?CRITICAL_MSG("Captcha is enabled in the option captcha_cmd, " - "but it can't generate images.", - []), - throw({error, captcha_cmd_enabled_but_fails}) - end; - false -> ok + true -> + case create_image() of + {ok, _, _, _} -> + true; + Err -> + ?CRITICAL_MSG("Captcha is enabled in the option captcha_cmd, " + "but it can't generate images.", + []), + Err + end; + false -> + false end. +-spec lookup_captcha(binary()) -> {ok, #captcha{}} | {error, enoent}. lookup_captcha(Id) -> case ets:lookup(captcha, Id) of [C] -> {ok, C}; - _ -> {error, enoent} + [] -> {error, enoent} end. -spec check_captcha(binary(), binary()) -> captcha_not_found | @@ -508,25 +651,22 @@ lookup_captcha(Id) -> captcha_non_valid. check_captcha(Id, ProvidedKey) -> - case ets:lookup(captcha, Id) of - [#captcha{pid = Pid, args = Args, key = ValidKey, - tref = Tref}] -> - ets:delete(captcha, Id), - erlang:cancel_timer(Tref), - if ValidKey == ProvidedKey -> - if is_pid(Pid) -> Pid ! {captcha_succeed, Args}; - true -> ok - end, - captcha_valid; - true -> - if is_pid(Pid) -> Pid ! {captcha_failed, Args}; - true -> ok - end, - captcha_non_valid - end; - _ -> captcha_not_found + case lookup_captcha(Id) of + {ok, #captcha{pid = Pid, args = Args, key = ValidKey, tref = Tref}} -> + ets:delete(captcha, Id), + misc:cancel_timer(Tref), + if ValidKey == ProvidedKey -> + callback(captcha_succeed, Pid, Args), + captcha_valid; + true -> + callback(captcha_failed, Pid, Args), + captcha_non_valid + end; + {error, _} -> + captcha_not_found end. +-spec clean_treap(treap:treap(), priority()) -> treap:treap(). clean_treap(Treap, CleanPriority) -> case treap:is_empty(Treap) of true -> Treap; @@ -538,16 +678,16 @@ clean_treap(Treap, CleanPriority) -> end end. -now_priority() -> - -p1_time_compat:system_time(micro_seconds). +-spec callback(captcha_succeed | captcha_failed, + pid() | undefined, + callback() | term()) -> any(). +callback(Result, _Pid, F) when is_function(F) -> + F(Result); +callback(Result, Pid, Args) when is_pid(Pid) -> + Pid ! {Result, Args}; +callback(_, _, _) -> + ok. -opt_type(captcha_cmd) -> - fun (FileName) -> - F = iolist_to_binary(FileName), if F /= <<"">> -> F end - end; -opt_type(captcha_host) -> fun iolist_to_binary/1; -opt_type(captcha_limit) -> - fun (I) when is_integer(I), I > 0 -> I end; -opt_type(listen) -> fun (V) -> V end; -opt_type(_) -> - [captcha_cmd, captcha_host, captcha_limit, listen]. +-spec now_priority() -> priority(). +now_priority() -> + -erlang:system_time(microsecond). diff --git a/src/ejabberd_cluster.erl b/src/ejabberd_cluster.erl index 1e3f02a9e..38a378d30 100644 --- a/src/ejabberd_cluster.erl +++ b/src/ejabberd_cluster.erl @@ -1,11 +1,9 @@ -%%%---------------------------------------------------------------------- -%%% File : ejabberd_cluster.erl -%%% Author : Christophe Romain -%%% Purpose : Ejabberd clustering management -%%% Created : 7 Oct 2015 by Christophe Romain +%%%------------------------------------------------------------------- +%%% Author : Evgeny Khramtsov +%%% Created : 5 Jul 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -21,84 +19,209 @@ %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% -%%%---------------------------------------------------------------------- - +%%%------------------------------------------------------------------- -module(ejabberd_cluster). +-behaviour(gen_server). %% API --export([get_nodes/0, call/4, multicall/3, multicall/4]). --export([join/1, leave/1]). +-export([start_link/0, call/4, call/5, multicall/3, multicall/4, multicall/5, + eval_everywhere/3, eval_everywhere/4]). +%% Backend dependent API +-export([get_nodes/0, get_known_nodes/0, join/1, leave/1, subscribe/0, + subscribe/1, node_id/0, get_node_by_id/1, send/2, wait_for_sync/1]). +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). +%% hooks +-export([set_ticktime/0]). --include("ejabberd.hrl"). -include("logger.hrl"). --spec get_nodes() -> [node()]. +-type dst() :: pid() | atom() | {atom(), node()}. -get_nodes() -> - mnesia:system_info(running_db_nodes). +-callback init() -> ok | {error, any()}. +-callback get_nodes() -> [node()]. +-callback get_known_nodes() -> [node()]. +-callback join(node()) -> ok | {error, any()}. +-callback leave(node()) -> ok | {error, any()}. +-callback node_id() -> binary(). +-callback get_node_by_id(binary()) -> node(). +-callback send({atom(), node()}, term()) -> boolean(). +-callback wait_for_sync(timeout()) -> ok | {error, any()}. +-callback subscribe(dst()) -> ok. + +-record(state, {}). + +%%%=================================================================== +%%% API +%%%=================================================================== +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). -spec call(node(), module(), atom(), [any()]) -> any(). - call(Node, Module, Function, Args) -> - rpc:call(Node, Module, Function, Args, 5000). + call(Node, Module, Function, Args, rpc_timeout()). + +-spec call(node(), module(), atom(), [any()], timeout()) -> any(). +call(Node, Module, Function, Args, Timeout) -> + rpc:call(Node, Module, Function, Args, Timeout). -spec multicall(module(), atom(), [any()]) -> {list(), [node()]}. - multicall(Module, Function, Args) -> multicall(get_nodes(), Module, Function, Args). -spec multicall([node()], module(), atom(), list()) -> {list(), [node()]}. - multicall(Nodes, Module, Function, Args) -> - rpc:multicall(Nodes, Module, Function, Args, 5000). + multicall(Nodes, Module, Function, Args, rpc_timeout()). + +-spec multicall([node()], module(), atom(), list(), timeout()) -> {list(), [node()]}. +multicall(Nodes, Module, Function, Args, Timeout) -> + rpc:multicall(Nodes, Module, Function, Args, Timeout). + +-spec eval_everywhere(module(), atom(), [any()]) -> ok. +eval_everywhere(Module, Function, Args) -> + eval_everywhere(get_nodes(), Module, Function, Args), + ok. + +-spec eval_everywhere([node()], module(), atom(), [any()]) -> ok. +eval_everywhere(Nodes, Module, Function, Args) -> + rpc:eval_everywhere(Nodes, Module, Function, Args), + ok. + +%%%=================================================================== +%%% Backend dependent API +%%%=================================================================== +-spec get_nodes() -> [node()]. +get_nodes() -> + Mod = get_mod(), + Mod:get_nodes(). + +-spec get_known_nodes() -> [node()]. +get_known_nodes() -> + Mod = get_mod(), + Mod:get_known_nodes(). -spec join(node()) -> ok | {error, any()}. - join(Node) -> - case {node(), net_adm:ping(Node)} of - {Node, _} -> - {error, {not_master, Node}}; - {_, pong} -> - application:stop(ejabberd), - application:stop(mnesia), - mnesia:delete_schema([node()]), - application:start(mnesia), - mnesia:change_config(extra_db_nodes, [Node]), - mnesia:change_table_copy_type(schema, node(), disc_copies), - spawn(fun() -> - lists:foreach(fun(Table) -> - Type = call(Node, mnesia, table_info, [Table, storage_type]), - mnesia:add_table_copy(Table, node(), Type) - end, mnesia:system_info(tables)--[schema]) - end), - application:start(ejabberd); - _ -> - {error, {no_ping, Node}} - end. + Mod = get_mod(), + Mod:join(Node). -spec leave(node()) -> ok | {error, any()}. - leave(Node) -> - case {node(), net_adm:ping(Node)} of - {Node, _} -> - Cluster = get_nodes()--[Node], - leave(Cluster, Node); - {_, pong} -> - rpc:call(Node, ?MODULE, leave, [Node], 10000); - {_, pang} -> - case mnesia:del_table_copy(schema, Node) of - {atomic, ok} -> ok; - {aborted, Reason} -> {error, Reason} - end + Mod = get_mod(), + Mod:leave(Node). + +-spec node_id() -> binary(). +node_id() -> + Mod = get_mod(), + Mod:node_id(). + +-spec get_node_by_id(binary()) -> node(). +get_node_by_id(ID) -> + Mod = get_mod(), + Mod:get_node_by_id(ID). + +%% Note that false positive returns are possible, while false negatives are not. +%% In other words: positive return value (i.e. 'true') doesn't guarantee +%% successful delivery, while negative return value ('false') means +%% the delivery has definitely failed. +-spec send(dst(), term()) -> boolean(). +send({Name, Node}, Msg) when Node == node() -> + send(Name, Msg); +send(undefined, _Msg) -> + false; +send(Name, Msg) when is_atom(Name) -> + send(whereis(Name), Msg); +send(Pid, Msg) when is_pid(Pid) andalso node(Pid) == node() -> + case erlang:is_process_alive(Pid) of + true -> + erlang:send(Pid, Msg), + true; + false -> + false + end; +send(Dst, Msg) -> + Mod = get_mod(), + Mod:send(Dst, Msg). + +-spec wait_for_sync(timeout()) -> ok | {error, any()}. +wait_for_sync(Timeout) -> + Mod = get_mod(), + Mod:wait_for_sync(Timeout). + +-spec subscribe() -> ok. +subscribe() -> + subscribe(self()). + +-spec subscribe(dst()) -> ok. +subscribe(Proc) -> + Mod = get_mod(), + Mod:subscribe(Proc). + +%%%=================================================================== +%%% Hooks +%%%=================================================================== +set_ticktime() -> + Ticktime = ejabberd_option:net_ticktime() div 1000, + case net_kernel:set_net_ticktime(Ticktime) of + {ongoing_change_to, Time} when Time /= Ticktime -> + ?ERROR_MSG("Failed to set new net_ticktime because " + "the net kernel is busy changing it to the " + "previously configured value. Please wait for " + "~B seconds and retry", [Time]); + _ -> + ok end. -leave([], Node) -> - {error, {no_cluster, Node}}; -leave([Master|_], Node) -> - application:stop(ejabberd), - application:stop(mnesia), - call(Master, mnesia, del_table_copy, [schema, Node]), - spawn(fun() -> - mnesia:delete_schema([node()]), - erlang:halt(0) - end), - ok. + +%%%=================================================================== +%%% gen_server API +%%%=================================================================== +init([]) -> + set_ticktime(), + Nodes = ejabberd_option:cluster_nodes(), + lists:foreach(fun(Node) -> + net_kernel:connect_node(Node) + end, Nodes), + Mod = get_mod(), + case Mod:init() of + ok -> + ejabberd_hooks:add(config_reloaded, ?MODULE, set_ticktime, 50), + Mod:subscribe(?MODULE), + {ok, #state{}}; + {error, Reason} -> + {stop, Reason} + end. + +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), + {noreply, State}. + +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), + {noreply, State}. + +handle_info({node_up, Node}, State) -> + ?INFO_MSG("Node ~ts has joined", [Node]), + {noreply, State}; +handle_info({node_down, Node}, State) -> + ?INFO_MSG("Node ~ts has left", [Node]), + {noreply, State}; +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), + {noreply, State}. + +terminate(_Reason, _State) -> + ejabberd_hooks:delete(config_reloaded, ?MODULE, set_ticktime, 50). + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +get_mod() -> + Backend = ejabberd_option:cluster_backend(), + list_to_existing_atom("ejabberd_cluster_" ++ atom_to_list(Backend)). + +rpc_timeout() -> + ejabberd_option:rpc_timeout(). diff --git a/src/ejabberd_cluster_mnesia.erl b/src/ejabberd_cluster_mnesia.erl new file mode 100644 index 000000000..ada0703be --- /dev/null +++ b/src/ejabberd_cluster_mnesia.erl @@ -0,0 +1,154 @@ +%%%---------------------------------------------------------------------- +%%% File : ejabberd_cluster_mnesia.erl +%%% Author : Christophe Romain +%%% Purpose : ejabberd clustering management via Mnesia +%%% Created : 7 Oct 2015 by Christophe Romain +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + +-module(ejabberd_cluster_mnesia). +-behaviour(ejabberd_cluster). + +%% API +-export([init/0, get_nodes/0, join/1, leave/1, + get_known_nodes/0, node_id/0, get_node_by_id/1, + send/2, wait_for_sync/1, subscribe/1]). + +-include("logger.hrl"). + +-spec init() -> ok. +init() -> + ok. + +-spec get_nodes() -> [node()]. + +get_nodes() -> + mnesia:system_info(running_db_nodes). + +-spec get_known_nodes() -> [node()]. + +get_known_nodes() -> + lists:usort(mnesia:system_info(db_nodes) + ++ mnesia:system_info(extra_db_nodes)). + +-spec join(node()) -> ok | {error, any()}. + +join(Node) -> + case {node(), net_adm:ping(Node)} of + {Node, _} -> + {error, {not_master, Node}}; + {_, pong} -> + application:stop(ejabberd), + application:stop(mnesia), + mnesia:delete_schema([node()]), + application:start(mnesia), + case mnesia:change_config(extra_db_nodes, [Node]) of + {ok, _} -> + replicate_database(Node), + wait_for_sync(infinity), + application:stop(mnesia), + application:start(ejabberd); + {error, Reason} -> + {error, Reason} + end; + _ -> + {error, {no_ping, Node}} + end. + +-spec leave(node()) -> ok | {error, any()}. + +leave(Node) -> + case {node(), net_adm:ping(Node)} of + {Node, _} -> + Cluster = get_nodes()--[Node], + leave(Cluster, Node); + {_, pong} -> + rpc:call(Node, ?MODULE, leave, [Node], 10000); + {_, pang} -> + case mnesia:del_table_copy(schema, Node) of + {atomic, ok} -> ok; + {aborted, Reason} -> {error, Reason} + end + end. +leave([], Node) -> + {error, {no_cluster, Node}}; +leave([Master|_], Node) -> + application:stop(ejabberd), + application:stop(mnesia), + spawn(fun() -> + rpc:call(Master, mnesia, del_table_copy, [schema, Node]), + mnesia:delete_schema([node()]), + erlang:halt(0) + end), + ok. + +-spec node_id() -> binary(). +node_id() -> + integer_to_binary(erlang:phash2(node())). + +-spec get_node_by_id(binary()) -> node(). +get_node_by_id(Hash) -> + try binary_to_integer(Hash) of + I -> match_node_id(I) + catch _:_ -> + node() + end. + +-spec send({atom(), node()}, term()) -> boolean(). +send(Dst, Msg) -> + case erlang:send(Dst, Msg, [nosuspend, noconnect]) of + ok -> true; + _ -> false + end. + +-spec wait_for_sync(timeout()) -> ok. +wait_for_sync(Timeout) -> + ?INFO_MSG("Waiting for Mnesia synchronization to complete", []), + mnesia:wait_for_tables(mnesia:system_info(local_tables), Timeout), + ok. + +-spec subscribe(_) -> ok. +subscribe(_) -> + ok. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== + +replicate_database(Node) -> + mnesia:change_table_copy_type(schema, node(), disc_copies), + lists:foreach( + fun(Table) -> + Type = rpc:call(Node, mnesia, table_info, [Table, storage_type]), + mnesia:add_table_copy(Table, node(), Type) + end, mnesia:system_info(tables)--[schema]). + +-spec match_node_id(integer()) -> node(). +match_node_id(I) -> + match_node_id(I, get_nodes()). + +-spec match_node_id(integer(), [node()]) -> node(). +match_node_id(I, [Node|Nodes]) -> + case erlang:phash2(Node) of + I -> Node; + _ -> match_node_id(I, Nodes) + end; +match_node_id(_I, []) -> + node(). diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl index 6172b18ed..f1e724da3 100644 --- a/src/ejabberd_commands.erl +++ b/src/ejabberd_commands.erl @@ -5,7 +5,7 @@ %%% Created : 20 May 2008 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -23,228 +23,45 @@ %%% %%%---------------------------------------------------------------------- -%%% @headerfile "ejabberd_commands.hrl" - -%%% @doc Management of ejabberd commands. -%%% -%%% An ejabberd command is an abstract function identified by a name, -%%% with a defined number and type of calling arguments and type of -%%% result, that can be defined in any Erlang module and executed -%%% using any valid frontend. -%%% -%%% -%%% == Define a new ejabberd command == -%%% -%%% ejabberd commands can be defined and registered in -%%% any Erlang module. -%%% -%%% Some commands are procedures; and their purpose is to perform an -%%% action in the server, so the command result is only some result -%%% code or result tuple. Other commands are inspectors, and their -%%% purpose is to gather some information about the server and return -%%% a detailed response: it can be integer, string, atom, tuple, list -%%% or a mix of those ones. -%%% -%%% The arguments and result of an ejabberd command are strictly -%%% defined. The number and format of the arguments provided when -%%% calling an ejabberd command must match the definition of that -%%% command. The format of the result provided by an ejabberd command -%%% must be exactly its definition. For example, if a command is said -%%% to return an integer, it must always return an integer (except in -%%% case of a crash). -%%% -%%% If you are developing an Erlang module that will run inside -%%% ejabberd and you want to provide a new ejabberd command to -%%% administer some task related to your module, you only need to: -%%% implement a function, define the command, and register it. -%%% -%%% -%%% === Define a new ejabberd command === -%%% -%%% An ejabberd command is defined using the Erlang record -%%% 'ejabberd_commands'. This record has several elements that you -%%% must define. Note that 'tags', 'desc' and 'longdesc' are optional. -%%% -%%% For example let's define an ejabberd command 'pow' that gets the -%%% integers 'base' and 'exponent'. Its result will be an integer -%%% 'power': -%%% -%%%
#ejabberd_commands{name = pow, tags = [test],
-%%%                 desc = "Return the power of base for exponent",
-%%%                 longdesc = "This is an example command. The formula is:\n"
-%%%                 "  power = base ^ exponent",
-%%%                 module = ?MODULE, function = pow,
-%%%                 args = [{base, integer}, {exponent, integer}],
-%%%                 result = {power, integer}}
-%%% -%%% -%%% === Implement the function associated to the command === -%%% -%%% Now implement a function in your module that matches the arguments -%%% and result of the ejabberd command. -%%% -%%% For example the function calc_power gets two integers Base and -%%% Exponent. It calculates the power and rounds to an integer: -%%% -%%%
calc_power(Base, Exponent) ->
-%%%    PowFloat = math:pow(Base, Exponent),
-%%%    round(PowFloat).
-%%% -%%% Since this function will be called by ejabberd_commands, it must -%%% be exported. -%%% Add to your module: -%%%
-export([calc_power/2]).
-%%% -%%% Only some types of result formats are allowed. -%%% If the format is defined as 'rescode', then your function must return: -%%% ok | true | atom() -%%% where the atoms ok and true as considered positive answers, -%%% and any other response atom is considered negative. -%%% -%%% If the format is defined as 'restuple', then the command must return: -%%% {rescode(), string()} -%%% -%%% If the format is defined as '{list, something()}', then the command -%%% must return a list of something(). -%%% -%%% -%%% === Register the command === -%%% -%%% Define this function and put inside the #ejabberd_command you -%%% defined in the beginning: -%%% -%%%
commands() ->
-%%%    [
-%%%
-%%%    ].
-%%% -%%% You need to include this header file in order to use the record: -%%% -%%%
-include("ejabberd_commands.hrl").
-%%% -%%% When your module is initialized or started, register your commands: -%%% -%%%
ejabberd_commands:register_commands(commands()),
-%%% -%%% And when your module is stopped, unregister your commands: -%%% -%%%
ejabberd_commands:unregister_commands(commands()),
-%%% -%%% That's all! Now when your module is started, the command will be -%%% registered and any frontend can access it. For example: -%%% -%%%
$ ejabberdctl help pow
-%%%
-%%%   Command Name: pow
-%%%
-%%%   Arguments: base::integer
-%%%              exponent::integer
-%%%
-%%%   Returns: power::integer
-%%%
-%%%   Tags: test
-%%%
-%%%   Description: Return the power of base for exponent
-%%%
-%%% This is an example command. The formula is:
-%%%  power = base ^ exponent
-%%%
-%%% $ ejabberdctl pow 3 4
-%%% 81
-%%% 
-%%% -%%% -%%% == Execute an ejabberd command == -%%% -%%% ejabberd commands are mean to be executed using any valid -%%% frontend. An ejabberd commands is implemented in a regular Erlang -%%% function, so it is also possible to execute this function in any -%%% Erlang module, without dealing with the associated ejabberd -%%% commands. -%%% -%%% -%%% == Frontend to ejabberd commands == -%%% -%%% Currently there are two frontends to ejabberd commands: the shell -%%% script {@link ejabberd_ctl. ejabberdctl}, and the XML-RPC server -%%% ejabberd_xmlrpc. -%%% -%%% -%%% === ejabberdctl as a frontend to ejabberd commands === -%%% -%%% It is possible to use ejabberdctl to get documentation of any -%%% command. But ejabberdctl does not support all the argument types -%%% allowed in ejabberd commands, so there are some ejabberd commands -%%% that cannot be executed using ejabberdctl. -%%% -%%% Also note that the ejabberdctl shell administration script also -%%% manages ejabberdctl commands, which are unrelated to ejabberd -%%% commands and can only be executed using ejabberdctl. -%%% -%%% -%%% === ejabberd_xmlrpc as a frontend to ejabberd commands === -%%% -%%% ejabberd_xmlrpc provides an XML-RPC server to execute ejabberd commands. -%%% ejabberd_xmlrpc is a contributed module published in ejabberd-modules SVN. -%%% -%%% Since ejabberd_xmlrpc does not provide any method to get documentation -%%% of the ejabberd commands, please use ejabberdctl to know which -%%% commands are available, and their usage. -%%% -%%% The number and format of the arguments provided when calling an -%%% ejabberd command must match the definition of that command. Please -%%% make sure the XML-RPC call provides the required arguments, with -%%% the specified format. The order of the arguments in an XML-RPC -%%% call is not important, because all the data is tagged and will be -%%% correctly prepared by ejabberd_xmlrpc before executing the ejabberd -%%% command. - -%%% TODO: consider this feature: -%%% All commands are catched. If an error happens, return the restuple: -%%% {error, flattened error string} -%%% This means that ecomm call APIs (ejabberd_ctl, ejabberd_xmlrpc) -%%% need to allows this. And ejabberd_xmlrpc must be prepared to -%%% handle such an unexpected response. - - -module(ejabberd_commands). -author('badlop@process-one.net'). +-behaviour(gen_server). + -define(DEFAULT_VERSION, 1000000). --export([init/0, +-export([start_link/0, list_commands/0, list_commands/1, + list_commands/2, get_command_format/1, get_command_format/2, get_command_format/3, - get_command_policy_and_scope/1, get_command_definition/1, get_command_definition/2, get_tags_commands/0, get_tags_commands/1, - get_exposed_commands/0, register_commands/1, + register_commands/2, + register_commands/3, unregister_commands/1, - expose_commands/1, - execute_command/2, - execute_command/3, - execute_command/4, - execute_command/5, - execute_command/6, - opt_type/1, + unregister_commands/3, get_commands_spec/0, get_commands_definition/0, get_commands_definition/1, execute_command2/3, execute_command2/4]). +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). -include("ejabberd_commands.hrl"). --include("ejabberd.hrl"). -include("logger.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). --define(POLICY_ACCESS, '$policy'). +-type auth() :: {binary(), binary(), binary() | {oauth, binary()}, boolean()} | map(). + +-record(state, {}). get_commands_spec() -> [ @@ -257,9 +74,9 @@ get_commands_spec() -> "documentation should be stored", "Regexp matching names of commands or modules " "that will be included inside generated document", - "Comma separated list of languages (choosen from java, perl, xmlrpc, json)" + "Comma separated list of languages (chosen from `java`, `perl`, `xmlrpc`, `json`) " "that will have example invocation include in markdown document"], - result_desc = "0 if command failed, 1 when succedded", + result_desc = "0 if command failed, 1 when succeeded", args_example = ["/home/me/docs/api.html", "mod_admin", "java,json"], result_example = ok}, #ejabberd_commands{name = gen_markdown_doc_for_commands, tags = [documentation], @@ -270,108 +87,149 @@ get_commands_spec() -> args_desc = ["Path to file where generated " "documentation should be stored", "Regexp matching names of commands or modules " - "that will be included inside generated document", - "Comma separated list of languages (choosen from java, perl, xmlrpc, json)" + "that will be included inside generated document, " + "or `runtime` to get commands registered at runtime", + "Comma separated list of languages (chosen from `java`, `perl`, `xmlrpc`, `json`) " "that will have example invocation include in markdown document"], - result_desc = "0 if command failed, 1 when succedded", + result_desc = "0 if command failed, 1 when succeeded", args_example = ["/home/me/docs/api.html", "mod_admin", "java,json"], + result_example = ok}, + #ejabberd_commands{name = gen_markdown_doc_for_tags, tags = [documentation], + desc = "Generates markdown documentation for ejabberd_commands", + note = "added in 21.12", + module = ejabberd_commands_doc, function = generate_tags_md, + args = [{file, binary}], + result = {res, rescode}, + args_desc = ["Path to file where generated " + "documentation should be stored"], + result_desc = "0 if command failed, 1 when succeeded", + args_example = ["/home/me/docs/tags.md"], result_example = ok}]. -init() -> + +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +init([]) -> try mnesia:transform_table(ejabberd_commands, ignore, record_info(fields, ejabberd_commands)) catch exit:{aborted, {no_exists, _}} -> ok end, - mnesia:create_table(ejabberd_commands, + ejabberd_mnesia:create(?MODULE, ejabberd_commands, [{ram_copies, [node()]}, {local_content, true}, {attributes, record_info(fields, ejabberd_commands)}, {type, bag}]), - mnesia:add_table_copy(ejabberd_commands, node(), ram_copies), register_commands(get_commands_spec()), - ejabberd_access_permissions:register_permission_addon(?MODULE, fun permission_addon/0). + {ok, #state{}}. + +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), + {noreply, State}. + +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), + {noreply, State}. + +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. -spec register_commands([ejabberd_commands()]) -> ok. -%% @doc Register ejabberd commands. -%% If a command is already registered, a warning is printed and the -%% old command is preserved. -%% A registered command is not directly available to be called through -%% ejabberd ReST API. It need to be exposed to be available through API. register_commands(Commands) -> + register_commands(unknown, Commands). + +-spec register_commands(atom(), [ejabberd_commands()]) -> ok. + +register_commands(Definer, Commands) -> + ExistingCommands = list_commands(), lists:foreach( fun(Command) -> - %% XXX check if command exists - mnesia:dirty_write(Command) - %% ?DEBUG("This command is already defined:~n~p", [Command]) + Name = Command#ejabberd_commands.name, + case lists:keyfind(Name, 1, ExistingCommands) of + false -> + mnesia:dirty_write(register_command_prepare(Command, Definer)); + _ -> + OtherCommandDef = get_command_definition(Name), + ?CRITICAL_MSG("Error trying to define a command: another one already exists with the same name:~n Existing: ~p~n New: ~p", [OtherCommandDef, Command]) + end end, Commands), ejabberd_access_permissions:invalidate(), ok. +-spec register_commands(binary(), atom(), [ejabberd_commands()]) -> ok. + +register_commands(Host, Definer, Commands) -> + case gen_mod:is_loaded_elsewhere(Host, Definer) of + false -> + register_commands(Definer, Commands); + true -> + ok + end. + +register_command_prepare(Command, Definer) -> + Tags1 = Command#ejabberd_commands.tags, + Tags2 = case Command#ejabberd_commands.version of + 0 -> Tags1; + Version -> Tags1 ++ [list_to_atom("v"++integer_to_list(Version))] + end, + Command#ejabberd_commands{definer = Definer, tags = Tags2}. + + -spec unregister_commands([ejabberd_commands()]) -> ok. -%% @doc Unregister ejabberd commands. unregister_commands(Commands) -> lists:foreach( fun(Command) -> - mnesia:dirty_delete_object(Command) + mnesia:dirty_delete(ejabberd_commands, Command#ejabberd_commands.name) end, Commands), - ejabberd_access_permissions:invalidate(), - ok. + ejabberd_access_permissions:invalidate(). -%% @doc Expose command through ejabberd ReST API. -%% Pass a list of command names or policy to expose. --spec expose_commands([ejabberd_commands()|atom()|open|user|admin|restricted]) -> ok | {error, atom()}. +-spec unregister_commands(binary(), atom(), [ejabberd_commands()]) -> ok. -expose_commands(Commands) -> - Names = lists:map(fun(#ejabberd_commands{name = Name}) -> - Name; - (Name) when is_atom(Name) -> - Name - end, - Commands), - - case ejabberd_config:add_local_option(commands, [{add_commands, Names}]) of - {aborted, Reason} -> - {error, Reason}; - {atomic, Result} -> - Result +unregister_commands(Host, Definer, Commands) -> + case gen_mod:is_loaded_elsewhere(Host, Definer) of + false -> + unregister_commands(Commands); + true -> + ok end. -spec list_commands() -> [{atom(), [aterm()], string()}]. -%% @doc Get a list of all the available commands, arguments and description. list_commands() -> list_commands(?DEFAULT_VERSION). -spec list_commands(integer()) -> [{atom(), [aterm()], string()}]. -%% @doc Get a list of all the available commands, arguments and -%% description in a given API verion. list_commands(Version) -> Commands = get_commands_definition(Version), [{Name, Args, Desc} || #ejabberd_commands{name = Name, args = Args, - desc = Desc} <- Commands]. + tags = Tags, + desc = Desc} <- Commands, + not lists:member(internal, Tags)]. +-spec list_commands(integer(), map()) -> [{atom(), [aterm()], string()}]. --spec list_commands_policy(integer()) -> - [{atom(), [aterm()], string(), atom()}]. +list_commands(Version, CallerInfo) -> + lists:filter( + fun({Name, _Args, _Desc}) -> + allow == ejabberd_access_permissions:can_access(Name, CallerInfo) + end, + list_commands(Version) + ). -%% @doc Get a list of all the available commands, arguments, -%% description, and policy in a given API version. -list_commands_policy(Version) -> - Commands = get_commands_definition(Version), - [{Name, Args, Desc, Policy} || - #ejabberd_commands{name = Name, - args = Args, - desc = Desc, - policy = Policy} <- Commands]. +-spec get_command_format(atom()) -> {[aterm()], [{atom(),atom()}], rterm()}. --spec get_command_format(atom()) -> {[aterm()], rterm()}. - -%% @doc Get the format of arguments and result of a command. get_command_format(Name) -> get_command_format(Name, noauth, ?DEFAULT_VERSION). get_command_format(Name, Version) when is_integer(Version) -> @@ -379,52 +237,29 @@ get_command_format(Name, Version) when is_integer(Version) -> get_command_format(Name, Auth) -> get_command_format(Name, Auth, ?DEFAULT_VERSION). --spec get_command_format(atom(), - {binary(), binary(), binary(), boolean()} | - noauth | admin, - integer()) -> - {[aterm()], rterm()}. - +-spec get_command_format(atom(), noauth | admin | auth(), integer()) -> {[aterm()], [{atom(),atom()}], rterm()}. get_command_format(Name, Auth, Version) -> Admin = is_admin(Name, Auth, #{}), #ejabberd_commands{args = Args, result = Result, + args_rename = Rename, policy = Policy} = get_command_definition(Name, Version), case Policy of user when Admin; Auth == noauth -> - {[{user, binary}, {server, binary} | Args], Result}; + {[{user, binary}, {host, binary} | Args], Rename, Result}; _ -> - {Args, Result} + {Args, Rename, Result} end. --spec get_command_policy_and_scope(atom()) -> {ok, open|user|admin|restricted, [oauth_scope()]} | {error, command_not_found}. - -%% @doc return command policy. -get_command_policy_and_scope(Name) -> - case get_command_definition(Name) of - #ejabberd_commands{policy = Policy} = Cmd -> - {ok, Policy, cmd_scope(Cmd)}; - command_not_found -> - {error, command_not_found} - end. - -%% The oauth scopes for a command are the command name itself, -%% also might include either 'ejabberd:user' or 'ejabberd:admin' -cmd_scope(#ejabberd_commands{policy = Policy, name = Name}) -> - [erlang:atom_to_binary(Name,utf8)] ++ [<<"ejabberd:user">> || Policy == user] ++ [<<"ejabberd:admin">> || Policy == admin]. - - -spec get_command_definition(atom()) -> ejabberd_commands(). -%% @doc Get the definition record of a command. get_command_definition(Name) -> get_command_definition(Name, ?DEFAULT_VERSION). -spec get_command_definition(atom(), integer()) -> ejabberd_commands(). -%% @doc Get the definition record of a command in a given API version. get_command_definition(Name, Version) -> case lists:reverse( lists:sort( @@ -444,7 +279,6 @@ get_commands_definition() -> -spec get_commands_definition(integer()) -> [ejabberd_commands()]. -% @doc Returns all commands for a given API version get_commands_definition(Version) -> L = lists:reverse( lists:sort( @@ -468,158 +302,39 @@ execute_command2(Name, Arguments, CallerInfo) -> execute_command2(Name, Arguments, CallerInfo, Version) -> Command = get_command_definition(Name, Version), - case ejabberd_access_permissions:can_access(Name, CallerInfo) of - allow -> + FrontedCalledInternal = + maps:get(caller_module, CallerInfo, none) /= ejabberd_web_admin + andalso lists:member(internal, Command#ejabberd_commands.tags), + case {ejabberd_access_permissions:can_access(Name, CallerInfo), + FrontedCalledInternal} of + {allow, false} -> do_execute_command(Command, Arguments); - _ -> + {_, true} -> + throw({error, frontend_cannot_call_an_internal_command}); + {deny, false} -> throw({error, access_rules_unauthorized}) end. -%% @spec (Name::atom(), Arguments) -> ResultTerm -%% where -%% Arguments = [any()] -%% @doc Execute a command. -%% Can return the following exceptions: -%% command_unknown | account_unprivileged | invalid_account_data | -%% no_auth_provided | access_rules_unauthorized -execute_command(Name, Arguments) -> - execute_command(Name, Arguments, ?DEFAULT_VERSION). - --spec execute_command(atom(), - [any()], - integer() | - {binary(), binary(), binary(), boolean()} | - noauth | admin - ) -> any(). - -%% @spec (Name::atom(), Arguments, integer() | Auth) -> ResultTerm -%% where -%% Auth = {User::string(), Server::string(), Password::string(), -%% Admin::boolean()} -%% | noauth -%% | admin -%% Arguments = [any()] -%% -%% @doc Execute a command in a given API version -%% Can return the following exceptions: -%% command_unknown | account_unprivileged | invalid_account_data | -%% no_auth_provided -execute_command(Name, Arguments, Version) when is_integer(Version) -> - execute_command([], noauth, Name, Arguments, Version); -execute_command(Name, Arguments, Auth) -> - execute_command([], Auth, Name, Arguments, ?DEFAULT_VERSION). - -%% @spec (AccessCommands, Auth, Name::atom(), Arguments) -> -%% ResultTerm | {error, Error} -%% where -%% AccessCommands = [{Access, CommandNames, Arguments}] | undefined -%% Auth = {User::string(), Server::string(), Password::string(), Admin::boolean()} -%% | noauth -%% | admin -%% Arguments = [any()] -%% -%% @doc Execute a command -%% Can return the following exceptions: -%% command_unknown | account_unprivileged | invalid_account_data | no_auth_provided -execute_command(AccessCommands, Auth, Name, Arguments) -> - execute_command(AccessCommands, Auth, Name, Arguments, ?DEFAULT_VERSION). - --spec execute_command([{atom(), [atom()], [any()]}] | undefined, - {binary(), binary(), binary(), boolean()} | - noauth | admin, - atom(), - [any()], - integer() - ) -> any(). - -%% @spec (AccessCommands, Auth, Name::atom(), Arguments, integer()) -> ResultTerm -%% where -%% AccessCommands = [{Access, CommandNames, Arguments}] | undefined -%% Auth = {User::string(), Server::string(), Password::string(), Admin::boolean()} -%% | noauth -%% | admin -%% Arguments = [any()] -%% -%% @doc Execute a command in a given API version -%% Can return the following exceptions: -%% command_unknown | account_unprivileged | invalid_account_data | no_auth_provided | access_rules_unauthorized -execute_command(AccessCommands1, Auth1, Name, Arguments, Version) -> - execute_command(AccessCommands1, Auth1, Name, Arguments, Version, #{}). - -execute_command(AccessCommands1, Auth1, Name, Arguments, Version, CallerInfo) -> - Auth = case is_admin(Name, Auth1, CallerInfo) of - true -> admin; - false -> Auth1 - end, - TokenJID = oauth_token_user(Auth1), - Command = get_command_definition(Name, Version), - AccessCommands = get_all_access_commands(AccessCommands1), - - case check_access_commands(AccessCommands, Auth, Name, Command, Arguments, CallerInfo) of - ok -> execute_check_policy(Auth, TokenJID, Command, Arguments) - end. - - -execute_check_policy( - _Auth, _JID, #ejabberd_commands{policy = open} = Command, Arguments) -> - do_execute_command(Command, Arguments); -execute_check_policy( - noauth, _JID, Command, Arguments) -> - do_execute_command(Command, Arguments); -execute_check_policy( - _Auth, _JID, #ejabberd_commands{policy = restricted} = Command, Arguments) -> - do_execute_command(Command, Arguments); -execute_check_policy( - _Auth, JID, #ejabberd_commands{policy = admin} = Command, Arguments) -> - execute_check_access(JID, Command, Arguments); -execute_check_policy( - admin, JID, #ejabberd_commands{policy = user} = Command, Arguments) -> - execute_check_access(JID, Command, Arguments); -execute_check_policy( - {User, Server, _, _}, JID, #ejabberd_commands{policy = user} = Command, Arguments) -> - execute_check_access(JID, Command, [User, Server | Arguments]). - -execute_check_access(_FromJID, #ejabberd_commands{access = []} = Command, Arguments) -> - do_execute_command(Command, Arguments); -execute_check_access(undefined, _Command, _Arguments) -> - throw({error, access_rules_unauthorized}); -execute_check_access(FromJID, #ejabberd_commands{access = AccessRefs} = Command, Arguments) -> - %% TODO Review: Do we have smarter / better way to check rule on other Host than global ? - Host = global, - Rules = lists:map(fun({Mod, AccessName, Default}) -> - gen_mod:get_module_opt(Host, Mod, - AccessName, fun(A) -> A end, Default); - (Default) -> - Default - end, AccessRefs), - case acl:any_rules_allowed(Host, Rules, FromJID) of - true -> - do_execute_command(Command, Arguments); - false -> - throw({error, access_rules_unauthorized}) - end. do_execute_command(Command, Arguments) -> Module = Command#ejabberd_commands.module, Function = Command#ejabberd_commands.function, ?DEBUG("Executing command ~p:~p with Args=~p", [Module, Function, Arguments]), + ejabberd_hooks:run(api_call, [Module, Function, Arguments]), apply(Module, Function, Arguments). -spec get_tags_commands() -> [{string(), [string()]}]. -%% @spec () -> [{Tag::string(), [CommandName::string()]}] -%% @doc Get all the tags and associated commands. get_tags_commands() -> get_tags_commands(?DEFAULT_VERSION). -spec get_tags_commands(integer()) -> [{string(), [string()]}]. -%% @spec (integer) -> [{Tag::string(), [CommandName::string()]}] -%% @doc Get all the tags and associated commands in a given API version get_tags_commands(Version) -> CommandTags = [{Name, Tags} || #ejabberd_commands{name = Name, tags = Tags} - <- get_commands_definition(Version)], + <- get_commands_definition(Version), + not lists:member(internal, Tags)], Dict = lists:foldl( fun({CommandNameAtom, CTags}, D) -> CommandName = atom_to_list(CommandNameAtom), @@ -643,233 +358,12 @@ get_tags_commands(Version) -> %% ----------------------------- %% Access verification %% ----------------------------- - -%% @spec (AccessCommands, Auth, Method, Command, Arguments) -> ok -%% where -%% AccessCommands = [ {Access, CommandNames, Arguments} ] -%% Auth = {User::string(), Server::string(), Password::string()} | noauth -%% Method = atom() -%% Arguments = [any()] -%% @doc Check access is allowed to that command. -%% At least one AccessCommand must be satisfied. -%% It may throw {error, Error} where: -%% Error = account_unprivileged | invalid_account_data -check_access_commands([], _Auth, _Method, _Command, _Arguments, _CallerInfo) -> - ok; -check_access_commands(AccessCommands, Auth, Method, Command1, Arguments, CallerInfo) -> - Command = - case {Command1#ejabberd_commands.policy, Auth} of - {user, {_, _, _, _}} -> - Command1; - {user, _} -> - Command1#ejabberd_commands{ - args = [{user, binary}, {server, binary} | - Command1#ejabberd_commands.args]}; - _ -> - Command1 - end, - AccessCommandsAllowed = - lists:filter( - fun({Access, Commands, ArgumentRestrictions}) -> - case check_access(Command, Access, Auth, CallerInfo) of - true -> - check_access_command(Commands, Command, - ArgumentRestrictions, - Method, Arguments); - false -> - false - end; - ({Access, Commands}) -> - ArgumentRestrictions = [], - case check_access(Command, Access, Auth, CallerInfo) of - true -> - check_access_command(Commands, Command, - ArgumentRestrictions, - Method, Arguments); - false -> - false - end - end, - AccessCommands), - case AccessCommandsAllowed of - [] -> throw({error, account_unprivileged}); - L when is_list(L) -> ok - end. - --spec check_auth(ejabberd_commands(), noauth) -> noauth_provided; - (ejabberd_commands(), - {binary(), binary(), binary(), boolean()}) -> - {ok, binary(), binary()}. - -check_auth(_Command, noauth) -> - no_auth_provided; -check_auth(Command, {User, Server, {oauth, Token}, _}) -> - ScopeList = cmd_scope(Command), - case ejabberd_oauth:check_token(User, Server, ScopeList, Token) of - true -> - {ok, User, Server}; - _ -> - throw({error, invalid_account_data}) - end; -check_auth(_Command, {User, Server, Password, _}) when is_binary(Password) -> - %% Check the account exists and password is valid - case ejabberd_auth:check_password(User, <<"">>, Server, Password) of - true -> {ok, User, Server}; - _ -> throw({error, invalid_account_data}) - end. - -check_access(Command, ?POLICY_ACCESS, _, _) - when Command#ejabberd_commands.policy == open -> - true; -check_access(_Command, _Access, admin, _) -> - true; -check_access(_Command, _Access, {_User, _Server, _, true}, _) -> - false; -check_access(Command, Access, Auth, CallerInfo) - when Access =/= ?POLICY_ACCESS; - Command#ejabberd_commands.policy == open; - Command#ejabberd_commands.policy == user -> - case check_auth(Command, Auth) of - {ok, User, Server} -> - check_access2(Access, CallerInfo#{usr => jid:split(jid:make(User, Server, <<>>))}, Server); - no_auth_provided -> - case Command#ejabberd_commands.policy of - user -> - false; - _ -> - check_access2(Access, CallerInfo, global) - end; - _ -> - false - end; -check_access(_Command, _Access, _Auth, _CallerInfo) -> - false. - -check_access2(?POLICY_ACCESS, _CallerInfo, _Server) -> - true; -check_access2(Access, AccessInfo, Server) -> - %% Check this user has access permission - case acl:access_matches(Access, AccessInfo, Server) of - allow -> true; - deny -> false - end. - -check_access_command(Commands, Command, ArgumentRestrictions, - Method, Arguments) -> - case Commands==all orelse lists:member(Method, Commands) of - true -> check_access_arguments(Command, ArgumentRestrictions, - Arguments); - false -> false - end. - -check_access_arguments(Command, ArgumentRestrictions, Arguments) -> - ArgumentsTagged = tag_arguments(Command#ejabberd_commands.args, Arguments), - lists:all( - fun({ArgName, ArgAllowedValue}) -> - %% If the call uses the argument, check the value is acceptable - case lists:keysearch(ArgName, 1, ArgumentsTagged) of - {value, {ArgName, ArgValue}} -> ArgValue == ArgAllowedValue; - false -> true - end - end, ArgumentRestrictions). - -tag_arguments(ArgsDefs, Args) -> - lists:zipwith( - fun({ArgName, _ArgType}, ArgValue) -> - {ArgName, ArgValue} - end, - ArgsDefs, - Args). - - -%% Get commands for all version -get_all_access_commands(AccessCommands) -> - get_access_commands(AccessCommands, ?DEFAULT_VERSION). - -get_access_commands(undefined, Version) -> - Cmds = get_exposed_commands(Version), - [{?POLICY_ACCESS, Cmds, []}]; -get_access_commands(AccessCommands, _Version) -> - AccessCommands. - -get_exposed_commands() -> - get_exposed_commands(?DEFAULT_VERSION). -get_exposed_commands(Version) -> - Opts0 = ejabberd_config:get_option( - commands, - fun(V) when is_list(V) -> V end, - []), - Opts = lists:map(fun(V) when is_tuple(V) -> [V]; (V) -> V end, Opts0), - CommandsList = list_commands_policy(Version), - OpenCmds = [N || {N, _, _, open} <- CommandsList], - RestrictedCmds = [N || {N, _, _, restricted} <- CommandsList], - AdminCmds = [N || {N, _, _, admin} <- CommandsList], - UserCmds = [N || {N, _, _, user} <- CommandsList], - Cmds = - lists:foldl( - fun([{add_commands, L}], Acc) -> - Cmds = expand_commands(L, OpenCmds, UserCmds, AdminCmds, RestrictedCmds), - lists:usort(Cmds ++ Acc); - ([{remove_commands, L}], Acc) -> - Cmds = expand_commands(L, OpenCmds, UserCmds, AdminCmds, RestrictedCmds), - Acc -- Cmds; - (_, Acc) -> Acc - end, [], Opts), - Cmds. - -%% This is used to allow mixing command policy (like open, user, admin, restricted), with command entry -expand_commands(L, OpenCmds, UserCmds, AdminCmds, RestrictedCmds) when is_list(L) -> - lists:foldl(fun(open, Acc) -> OpenCmds ++ Acc; - (user, Acc) -> UserCmds ++ Acc; - (admin, Acc) -> AdminCmds ++ Acc; - (restricted, Acc) -> RestrictedCmds ++ Acc; - (Command, Acc) when is_atom(Command) -> - [Command|Acc] - end, [], L). - -oauth_token_user(noauth) -> - undefined; -oauth_token_user(admin) -> - undefined; -oauth_token_user({User, Server, _, _}) -> - jid:make(User, Server, <<>>). - +-spec is_admin(atom(), admin | noauth | auth(), map()) -> boolean(). is_admin(_Name, admin, _Extra) -> true; is_admin(_Name, {_User, _Server, _, false}, _Extra) -> false; is_admin(_Name, Map, _extra) when is_map(Map) -> true; -is_admin(Name, Auth, Extra) -> - {ACLInfo, Server} = case Auth of - {U, S, _, _} -> - {Extra#{usr=>jid:split(jid:make(U, S, <<>>))}, S}; - _ -> - {Extra, global} - end, - AdminAccess = ejabberd_config:get_option( - commands_admin_access, - fun(V) -> V end, - none), - case acl:access_matches(AdminAccess, ACLInfo, Server) of - allow -> - case catch check_auth(get_command_definition(Name), Auth) of - {ok, _, _} -> true; - no_auth_provided -> true; - _ -> false - end; - deny -> false - end. - -permission_addon() -> - [{<<"'commands' option compatibility shim">>, - {[], - [{access, ejabberd_config:get_option(commands_admin_access, - fun(V) -> V end, - none)}], - {get_exposed_commands(), []}}}]. - -opt_type(commands_admin_access) -> fun acl:access_rules_validator/1; -opt_type(commands) -> - fun(V) when is_list(V) -> V end; -opt_type(_) -> [commands, commands_admin_access]. +is_admin(_Name, _Auth, _Extra) -> + false. diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index bb519a600..79bfe6147 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -5,7 +5,7 @@ %%% Created : 20 May 2008 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -28,14 +28,16 @@ -export([generate_html_output/3]). -export([generate_md_output/3]). +-export([generate_tags_md/1]). -include("ejabberd_commands.hrl"). --include("ejabberd.hrl"). -define(RAW(V), if HTMLOutput -> fxml:crypt(iolist_to_binary(V)); true -> iolist_to_binary(V) end). --define(TAG(N), if HTMLOutput -> [<<"<", ??N, "/>">>]; true -> md_tag(N, <<"">>) end). --define(TAG(N, V), if HTMLOutput -> [<<"<", ??N, ">">>, V, <<"">>]; true -> md_tag(N, V) end). --define(TAG(N, C, V), if HTMLOutput -> [<<"<", ??N, " class='", C, "'>">>, V, <<"">>]; true -> md_tag(N, V) end). +-define(TAG_BIN(N), (atom_to_binary(N, latin1))/binary). +-define(TAG_STR(N), atom_to_list(N)). +-define(TAG(N), if HTMLOutput -> [<<"<", ?TAG_BIN(N), "/>">>]; true -> md_tag(N, <<"">>) end). +-define(TAG(N, V), if HTMLOutput -> [<<"<", ?TAG_BIN(N), ">">>, V, <<"">>]; true -> md_tag(N, V) end). +-define(TAG(N, C, V), if HTMLOutput -> [<<"<", ?TAG_BIN(N), " class='", C, "'>">>, V, <<"">>]; true -> md_tag(N, V) end). -define(TAG_R(N, V), ?TAG(N, ?RAW(V))). -define(TAG_R(N, C, V), ?TAG(N, C, ?RAW(V))). -define(SPAN(N, V), ?TAG_R(span, ??N, V)). @@ -69,9 +71,9 @@ list_join_with([El|Tail], M) -> end, [El], Tail)). md_tag(dt, V) -> - [<<"\n">>, V, <<"\n">>]; + [<<"- ">>, V]; md_tag(dd, V) -> - [<<"\n: ">>, V, <<"\n">>]; + [<<" : ">>, V, <<"\n">>]; md_tag(li, V) -> [<<"- ">>, V, <<"\n">>]; md_tag(pre, V) -> @@ -81,20 +83,14 @@ md_tag(p, V) -> md_tag(h1, V) -> [<<"\n\n## ">>, V, <<"\n">>]; md_tag(h2, V) -> - [<<"\n\n### ">>, V, <<"\n">>]; + [<<"\n__">>, V, <<"__\n\n">>]; md_tag(strong, V) -> [<<"*">>, V, <<"*">>]; +md_tag('div', V) -> + [<<"*Note* about this command: ">>, V, <<".">>]; md_tag(_, V) -> V. - -%% rescode_to_int(ok) -> -%% 0; -%% rescode_to_int(true) -> -%% 0; -%% rescode_to_int(_) -> -%% 1. - perl_gen({Name, integer}, Int, _Indent, HTMLOutput) -> [?ARG(Name), ?OP_L(" => "), ?NUM(Int)]; perl_gen({Name, string}, Str, _Indent, HTMLOutput) -> @@ -241,7 +237,7 @@ json_gen({_Name, {list, ElDesc}}, List, Indent, HTMLOutput) -> [?OP_L("["), ?BR, Indent2, list_join_with(Res, [?OP_L(","), ?BR, Indent2]), ?BR, Indent, ?OP_L("]")]. json_call(Name, ArgsDesc, Values, ResultDesc, Result, HTMLOutput) -> - {Indent, Preamble} = if HTMLOutput -> {<<"">>, []}; true -> {<<" ">>, <<"~~~ json\n">>} end, + {Indent, Preamble} = if HTMLOutput -> {<<"">>, []}; true -> {<<"">>, <<"~~~ json\n">>} end, {Code, ResultStr} = case {ResultDesc, Result} of {{_, rescode}, V} when V == true; V == ok -> {200, [?STR_L("")]}; @@ -251,13 +247,8 @@ json_call(Name, ArgsDesc, Values, ResultDesc, Result, HTMLOutput) -> {200, [?STR(Text1)]}; {{_, restuple}, {_, Text2}} -> {500, [?STR(Text2)]}; - {{_, {list, _}}, _} -> - {200, json_gen(ResultDesc, Result, Indent, HTMLOutput)}; - {{_, {tuple, _}}, _} -> - {200, json_gen(ResultDesc, Result, Indent, HTMLOutput)}; - {{Name0, _}, _} -> - {200, [Indent, ?OP_L("{"), ?STR_A(Name0), ?OP_L(": "), - json_gen(ResultDesc, Result, Indent, HTMLOutput), Indent, ?OP_L("}")]} + {{_, _}, _} -> + {200, json_gen(ResultDesc, Result, Indent, HTMLOutput)} end, CodeStr = case Code of 200 -> <<" 200 OK">>; @@ -324,74 +315,151 @@ gen_calls(#ejabberd_commands{args_example=Values, args=ArgsDesc, case lists:member(<<"xmlrpc">>, Langs) of true -> ?TAG(li, ?TAG(pre, XML)); _ -> [] end, case lists:member(<<"json">>, Langs) of true -> ?TAG(li, ?TAG(pre, JSON)); _ -> [] end])]; true -> - [<<"\n">>, case lists:member(<<"java">>, Langs) of true -> <<"* Java\n">>; _ -> [] end, - case lists:member(<<"perl">>, Langs) of true -> <<"* Perl\n">>; _ -> [] end, - case lists:member(<<"xmlrpc">>, Langs) of true -> <<"* XmlRPC\n">>; _ -> [] end, - case lists:member(<<"json">>, Langs) of true -> <<"* JSON\n">>; _ -> [] end, - <<"{: .code-samples-labels}\n">>, - case lists:member(<<"java">>, Langs) of true -> [<<"\n* ">>, ?TAG(pre, Java), <<"~~~\n">>]; _ -> [] end, - case lists:member(<<"perl">>, Langs) of true -> [<<"\n* ">>, ?TAG(pre, Perl), <<"~~~\n">>]; _ -> [] end, - case lists:member(<<"xmlrpc">>, Langs) of true -> [<<"\n* ">>, ?TAG(pre, XML), <<"~~~\n">>]; _ -> [] end, - case lists:member(<<"json">>, Langs) of true -> [<<"\n* ">>, ?TAG(pre, JSON), <<"~~~\n">>]; _ -> [] end, - <<"{: .code-samples-tabs}\n\n">>] + case Langs of + Val when length(Val) == 0 orelse length(Val) == 1 -> + [case lists:member(<<"java">>, Langs) of true -> [<<"\n">>, ?TAG(pre, Java), <<"~~~\n">>]; _ -> [] end, + case lists:member(<<"perl">>, Langs) of true -> [<<"\n">>, ?TAG(pre, Perl), <<"~~~\n">>]; _ -> [] end, + case lists:member(<<"xmlrpc">>, Langs) of true -> [<<"\n">>, ?TAG(pre, XML), <<"~~~\n">>]; _ -> [] end, + case lists:member(<<"json">>, Langs) of true -> [<<"\n">>, ?TAG(pre, JSON), <<"~~~\n">>]; _ -> [] end, + <<"\n\n">>]; + _ -> + [<<"\n">>, case lists:member(<<"java">>, Langs) of true -> <<"* Java\n">>; _ -> [] end, + case lists:member(<<"perl">>, Langs) of true -> <<"* Perl\n">>; _ -> [] end, + case lists:member(<<"xmlrpc">>, Langs) of true -> <<"* XmlRPC\n">>; _ -> [] end, + case lists:member(<<"json">>, Langs) of true -> <<"* JSON\n">>; _ -> [] end, + <<"{: .code-samples-labels}\n">>, + case lists:member(<<"java">>, Langs) of true -> [<<"\n* ">>, ?TAG(pre, Java), <<"~~~\n">>]; _ -> [] end, + case lists:member(<<"perl">>, Langs) of true -> [<<"\n* ">>, ?TAG(pre, Perl), <<"~~~\n">>]; _ -> [] end, + case lists:member(<<"xmlrpc">>, Langs) of true -> [<<"\n* ">>, ?TAG(pre, XML), <<"~~~\n">>]; _ -> [] end, + case lists:member(<<"json">>, Langs) of true -> [<<"\n* ">>, ?TAG(pre, JSON), <<"~~~\n">>]; _ -> [] end, + <<"{: .code-samples-tabs}\n\n">>] + end end. -gen_doc(#ejabberd_commands{name=Name, tags=_Tags, desc=Desc, longdesc=LongDesc, - args=Args, args_desc=ArgsDesc, - result=Result, result_desc=ResultDesc}=Cmd, HTMLOutput, Langs) -> - LDesc = case LongDesc of - "" -> Desc; - _ -> LongDesc - end, - ArgsText = case ArgsDesc of - none -> - [?TAG(ul, "args-list", lists:map(fun({AName, Type}) -> - [?TAG(li, [?TAG_R(strong, atom_to_list(AName)), <<" :: ">>, - ?RAW(io_lib:format("~p", [Type]))])] - end, Args))]; - _ -> - [?TAG(dl, "args-list", lists:map(fun({{AName, Type}, ADesc}) -> - [?TAG(dt, [?TAG_R(strong, atom_to_list(AName)), <<" :: ">>, - ?RAW(io_lib:format("~p", [Type]))]), - ?TAG(dd, ?RAW(ADesc))] - end, lists:zip(Args, ArgsDesc)))] - end, - ResultText = case ResultDesc of - none -> - [?RAW(io_lib:format("~p", [Result]))]; - _ -> - [?TAG(dl, [ - ?TAG(dt, io_lib:format("~p", [Result])), - ?TAG_R(dd, ResultDesc)])] - end, +format_type({list, {_, {tuple, Els}}}) -> + io_lib:format("[~ts]", [format_type({tuple, Els})]); +format_type({list, El}) -> + io_lib:format("[~ts]", [format_type(El)]); +format_type({tuple, Els}) -> + Args = [format_type(El) || El <- Els], + io_lib:format("{~ts}", [string:join(Args, ", ")]); +format_type({Name, Type}) -> + io_lib:format("~ts::~ts", [Name, format_type(Type)]); +format_type(binary) -> + "string"; +format_type(atom) -> + "string"; +format_type(Type) -> + io_lib:format("~p", [Type]). - [?TAG(h1, [?TAG(strong, atom_to_list(Name)), <<" - ">>, ?RAW(Desc)]), - ?TAG(p, ?RAW(LDesc)), - ?TAG(h2, <<"Arguments:">>), - ArgsText, - ?TAG(h2, <<"Result:">>), - ResultText, - ?TAG(h2, <<"Examples:">>), - gen_calls(Cmd, HTMLOutput, Langs)]. +gen_param(Name, Type, undefined, HTMLOutput) -> + [?TAG(li, [?TAG_R(strong, atom_to_list(Name)), <<" :: ">>, ?RAW(format_type(Type))])]; +gen_param(Name, Type, Desc, HTMLOutput) -> + [?TAG(dt, [?TAG_R(strong, atom_to_list(Name)), <<" :: ">>, ?RAW(format_type(Type))]), + ?TAG(dd, ?RAW(Desc))]. + +make_tags(HTMLOutput) -> + TagsList = ejabberd_commands:get_tags_commands(1000000), + lists:map(fun(T) -> gen_tags(T, HTMLOutput) end, TagsList). + +-dialyzer({no_match, gen_tags/2}). +gen_tags({TagName, Commands}, HTMLOutput) -> + [?TAG(h1, TagName) | [?TAG(p, ?RAW("* _`"++C++"`_")) || C <- Commands]]. + +gen_doc(#ejabberd_commands{name=Name, tags=Tags, desc=Desc, longdesc=LongDesc, + args=Args, args_desc=ArgsDesc, note=Note, definer=Definer, + result=Result, result_desc=ResultDesc}=Cmd, HTMLOutput, Langs) -> + try + ArgsText = case ArgsDesc of + none -> + [?TAG(ul, "args-list", [gen_param(AName, Type, undefined, HTMLOutput) + || {AName, Type} <- Args])]; + _ -> + [?TAG(dl, "args-list", [gen_param(AName, Type, ADesc, HTMLOutput) + || {{AName, Type}, ADesc} <- lists:zip(Args, ArgsDesc)])] + end, + ResultText = case Result of + {res,rescode} -> + [?TAG(dl, [gen_param(res, integer, + "Status code (`0` on success, `1` otherwise)", + HTMLOutput)])]; + {res,restuple} -> + [?TAG(dl, [gen_param(res, string, + "Raw result string", + HTMLOutput)])]; + {RName, Type} -> + case ResultDesc of + none -> + [?TAG(ul, [gen_param(RName, Type, undefined, HTMLOutput)])]; + _ -> + [?TAG(dl, [gen_param(RName, Type, ResultDesc, HTMLOutput)])] + end + end, + TagsText = ?RAW(string:join(["_`"++atom_to_list(Tag)++"`_" || Tag <- Tags], ", ")), + IsDefinerMod = case Definer of + unknown -> false; + _ -> lists:member(gen_mod, lists:flatten(proplists:get_all_values(behaviour, Definer:module_info(attributes)))) + end, + ModuleText = case IsDefinerMod of + true -> + [?TAG(h2, <<"Module:">>), ?TAG(p, ?RAW("_`"++atom_to_list(Definer)++"`_"))]; + false -> + [] + end, + NoteEl = case Note of + "" -> []; + _ -> ?TAG('div', "note-down", ?RAW(Note)) + end, + {NotePre, NotePost} = + if HTMLOutput -> {[], NoteEl}; + true -> {NoteEl, []} + end, + + [?TAG(h1, make_command_name(Name, Note)), + NotePre, + ?TAG(p, ?RAW(Desc)), + case LongDesc of + "" -> []; + _ -> ?TAG(p, ?RAW(LongDesc)) + end, + NotePost, + ?TAG(h2, <<"Arguments:">>), ArgsText, + ?TAG(h2, <<"Result:">>), ResultText, + ?TAG(h2, <<"Tags:">>), ?TAG(p, TagsText)] + ++ ModuleText ++ [ + ?TAG(h2, <<"Examples:">>), gen_calls(Cmd, HTMLOutput, Langs)] + catch + _:Ex -> + throw(iolist_to_binary(io_lib:format( + <<"Error when generating documentation for command '~p': ~p">>, + [Name, Ex]))) + end. + +get_version_mark("") -> + ""; +get_version_mark(Note) -> + [XX, YY | _] = string:tokens(binary_to_list(ejabberd_option:version()), "."), + XXYY = string:join([XX, YY], "."), + case string:find(Note, XXYY) of + nomatch -> ""; + _ -> " 🟤" + end. + +make_command_name(Name, Note) -> + atom_to_list(Name) ++ get_version_mark(Note). find_commands_definitions() -> - case code:lib_dir(ejabberd, ebin) of - {error, _} -> - lists:map(fun({N, _, _}) -> - ejabberd_commands:get_command_definition(N) - end, ejabberd_commands:list_commands()); - Path -> - lists:flatmap(fun(P) -> - Mod = list_to_atom(filename:rootname(P)), - code:ensure_loaded(Mod), - case erlang:function_exported(Mod, get_commands_spec, 0) of - true -> - apply(Mod, get_commands_spec, []); - _ -> - [] - end - end, filelib:wildcard("*.beam", Path)) - end. + lists:flatmap( + fun(Mod) -> + code:ensure_loaded(Mod), + Cs = case erlang:function_exported(Mod, get_commands_spec, 0) of + true -> + apply(Mod, get_commands_spec, []); + _ -> + [] + end, + [C#ejabberd_commands{definer = Mod} || C <- Cs] + end, ejabberd_config:beams(all)). generate_html_output(File, RegExp, Languages) -> Cmds = find_commands_definitions(), @@ -403,15 +471,30 @@ generate_html_output(File, RegExp, Languages) -> Cmds3 = lists:sort(fun(#ejabberd_commands{name=N1}, #ejabberd_commands{name=N2}) -> N1 =< N2 end, Cmds2), + Cmds4 = [maybe_add_policy_arguments(Cmd) || Cmd <- Cmds3], Langs = binary:split(Languages, <<",">>, [global]), - Out = lists:map(fun(C) -> gen_doc(C, true, Langs) end, Cmds3), + Out = lists:map(fun(C) -> gen_doc(C, true, Langs) end, Cmds4), {ok, Fh} = file:open(File, [write]), - io:format(Fh, "~s", [[html_pre(), Out, html_post()]]), + io:format(Fh, "~ts", [[html_pre(), Out, html_post()]]), file:close(Fh), ok. +maybe_add_policy_arguments(#ejabberd_commands{args=Args1, policy=user}=Cmd) -> + Args2 = [{user, binary}, {host, binary} | Args1], + Cmd#ejabberd_commands{args = Args2}; +maybe_add_policy_arguments(Cmd) -> + Cmd. + +generate_md_output(File, <<"runtime">>, Languages) -> + Cmds = lists:map(fun({N, _, _}) -> + ejabberd_commands:get_command_definition(N) + end, ejabberd_commands:list_commands()), + generate_md_output(File, <<".">>, Languages, Cmds); generate_md_output(File, RegExp, Languages) -> Cmds = find_commands_definitions(), + generate_md_output(File, RegExp, Languages, Cmds). + +generate_md_output(File, RegExp, Languages, Cmds) -> {ok, RE} = re:compile(RegExp), Cmds2 = lists:filter(fun(#ejabberd_commands{name=Name, module=Module}) -> re:run(atom_to_list(Name), RE, [{capture, none}]) == match orelse @@ -420,11 +503,25 @@ generate_md_output(File, RegExp, Languages) -> Cmds3 = lists:sort(fun(#ejabberd_commands{name=N1}, #ejabberd_commands{name=N2}) -> N1 =< N2 end, Cmds2), + Cmds4 = [maybe_add_policy_arguments(Cmd) || Cmd <- Cmds3], Langs = binary:split(Languages, <<",">>, [global]), - Header = <<"---\ntitle: Administration API reference\nbodyclass: nocomment\n---">>, - Out = lists:map(fun(C) -> gen_doc(C, false, Langs) end, Cmds3), - {ok, Fh} = file:open(File, [write]), - io:format(Fh, "~s~s", [Header, Out]), + Version = binary_to_list(ejabberd_config:version()), + Header = ["# API Reference\n\n" + "This section describes API commands of ejabberd ", Version, ". " + "The commands that changed in this version are marked with 🟤.\n\n"], + Out = lists:map(fun(C) -> gen_doc(C, false, Langs) end, Cmds4), + {ok, Fh} = file:open(File, [write, {encoding, utf8}]), + io:format(Fh, "~ts~ts", [Header, Out]), + file:close(Fh), + ok. + +generate_tags_md(File) -> + Version = binary_to_list(ejabberd_config:version()), + Header = ["# API Tags\n\n" + "This section enumerates the API tags of ejabberd ", Version, ". \n\n"], + Tags = make_tags(false), + {ok, Fh} = file:open(File, [write, {encoding, utf8}]), + io:format(Fh, "~ts~ts", [Header, Tags]), file:close(Fh), ok. diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index af26767f8..11173f9e8 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -5,7 +5,7 @@ %%% Created : 14 Dec 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -22,203 +22,332 @@ %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- - -module(ejabberd_config). --author('alexey@process-one.net'). --export([start/0, load_file/1, reload_file/0, read_file/1, - add_global_option/2, add_local_option/2, - get_global_option/2, get_local_option/2, - get_global_option/3, get_local_option/3, - get_option/2, get_option/3, add_option/2, has_option/1, - get_vh_by_auth_method/1, is_file_readable/1, - get_version/0, get_myhosts/0, get_mylang/0, - get_ejabberd_config_path/0, is_using_elixir_config/0, - prepare_opt_val/4, convert_table_to_binary/5, - transform_options/1, collect_options/1, default_db/2, - convert_to_yaml/1, convert_to_yaml/2, v_db/2, - env_binary_to_list/2, opt_type/1, may_hide_data/1, - is_elixir_enabled/0, v_dbs/1, v_dbs_mods/1]). +%% API +-export([get_option/1]). +-export([load/0, reload/0, format_error/1, path/0]). +-export([env_binary_to_list/2]). +-export([get_myname/0, get_uri/0, get_copyright/0]). +-export([get_shared_key/0, get_node_start/0]). +-export([fsm_limit_opts/1]). +-export([codec_options/0]). +-export([version/0]). +-export([default_db/2, default_db/3, default_ram_db/2, default_ram_db/3]). +-export([beams/1, validators/1, globals/0, may_hide_data/1]). +-export([dump/0, dump/1, convert_to_yaml/1, convert_to_yaml/2]). +-export([callback_modules/1]). +-export([set_option/2]). +-export([get_defined_keywords/1, get_predefined_keywords/1, replace_keywords/2, replace_keywords/3]). +-export([resolve_host_alias/1]). --export([start/2]). +%% Deprecated functions +-export([get_option/2]). +-export([get_version/0, get_myhosts/0]). +-export([get_mylang/0, get_lang/1]). +-deprecated([{get_option, 2}, + {get_version, 0}, + {get_myhosts, 0}, + {get_mylang, 0}, + {get_lang, 1}]). --include("ejabberd.hrl"). -include("logger.hrl"). --include("ejabberd_config.hrl"). --include_lib("kernel/include/file.hrl"). --callback opt_type(atom()) -> function() | [atom()]. -%% @type macro() = {macro_key(), macro_value()} +-type option() :: atom() | {atom(), global | binary()}. +-type error_reason() :: {merge_conflict, atom(), binary()} | + {old_config, file:filename_all(), term()} | + {write_file, file:filename_all(), term()} | + {exception, term(), term(), term()}. +-type error_return() :: {error, econf:error_reason(), term()} | + {error, error_reason()}. +-type host_config() :: #{{atom(), binary() | global} => term()}. -%% @type macro_key() = atom(). -%% The atom must have all characters in uppercase. +-callback opt_type(atom()) -> econf:validator(). +-callback options() -> [atom() | {atom(), term()}]. +-callback globals() -> [atom()]. +-callback doc() -> any(). -%% @type macro_value() = term(). +-optional_callbacks([globals/0]). -start() -> - mnesia_init(), - Config = get_ejabberd_config_path(), - State0 = read_file(Config), - State1 = hosts_to_start(State0), - State2 = validate_opts(State1), - %% This start time is used by mod_last: - UnixTime = p1_time_compat:system_time(seconds), - SharedKey = case erlang:get_cookie() of - nocookie -> - p1_sha:sha(randoms:get_string()); - Cookie -> - p1_sha:sha(jlib:atom_to_binary(Cookie)) - end, - State3 = set_option({node_start, global}, UnixTime, State2), - State4 = set_option({shared_key, global}, SharedKey, State3), - set_opts(State4). +-ifndef(OTP_BELOW_28). +-dialyzer([no_opaque_union]). +-endif. -%% When starting ejabberd for testing, we sometimes want to start a -%% subset of hosts from the one define in the config file. -%% This function override the host list read from config file by the -%% one we provide. -%% Hosts to start are defined in an ejabberd application environment -%% variable 'hosts' to make it easy to ignore some host in config -%% file. -hosts_to_start(State) -> - case application:get_env(ejabberd, hosts) of - undefined -> - %% Start all hosts as defined in config file - State; - {ok, Hosts} -> - set_hosts_in_options(Hosts, State) +%%%=================================================================== +%%% API +%%%=================================================================== +-spec load() -> ok | error_return(). +load() -> + load(path()). + +-spec load(file:filename_all()) -> ok | error_return(). +load(Path) -> + ConfigFile = unicode:characters_to_binary(Path), + UnixTime = erlang:monotonic_time(second), + ?INFO_MSG("Loading configuration from ~ts", [ConfigFile]), + _ = ets:new(ejabberd_options, + [named_table, public, {read_concurrency, true}]), + case load_file(ConfigFile) of + ok -> + set_shared_key(), + set_node_start(UnixTime), + ?INFO_MSG("Configuration loaded successfully", []); + Err -> + Err end. -%% @private -%% At the moment, these functions are mainly used to setup unit tests. --spec start(Hosts :: [binary()], Opts :: [acl:acl() | local_config()]) -> ok. -start(Hosts, Opts) -> - mnesia_init(), - set_opts(set_hosts_in_options(Hosts, #state{opts = Opts})). +-spec reload() -> ok | error_return(). +reload() -> + ejabberd_systemd:reloading(), + ConfigFile = path(), + ?INFO_MSG("Reloading configuration from ~ts", [ConfigFile]), + OldHosts = get_myhosts(), + Res = case load_file(ConfigFile) of + ok -> + NewHosts = get_myhosts(), + AddHosts = NewHosts -- OldHosts, + DelHosts = OldHosts -- NewHosts, + lists:foreach( + fun(Host) -> + ejabberd_hooks:run(host_up, [Host]) + end, AddHosts), + lists:foreach( + fun(Host) -> + ejabberd_hooks:run(host_down, [Host]) + end, DelHosts), + ejabberd_hooks:run(config_reloaded, []), + % logger is started too early to be able to use hooks, so + % we need to call it separately + ejabberd_logger:config_reloaded(), + delete_host_options(DelHosts), + ?INFO_MSG("Configuration reloaded successfully", []); + Err -> + ?ERROR_MSG("Configuration reload aborted: ~ts", + [format_error(Err)]), + Err + end, + ejabberd_systemd:ready(), + Res. -mnesia_init() -> - case catch mnesia:table_info(local_config, storage_type) of - disc_copies -> - mnesia:delete_table(local_config); - _ -> - ok - end, - mnesia:create_table(local_config, - [{ram_copies, [node()]}, - {local_content, true}, - {attributes, record_info(fields, local_config)}]), - mnesia:add_table_copy(local_config, node(), ram_copies). +-spec dump() -> ok | error_return(). +dump() -> + dump(stdout). -%% @doc Get the filename of the ejabberd configuration file. -%% The filename can be specified with: erl -config "/path/to/ejabberd.yml". -%% It can also be specified with the environtment variable EJABBERD_CONFIG_PATH. -%% If not specified, the default value 'ejabberd.yml' is assumed. -%% @spec () -> string() -get_ejabberd_config_path() -> - case get_env_config() of - {ok, Path} -> Path; - undefined -> - case os:getenv("EJABBERD_CONFIG_PATH") of - false -> - ?CONFIG_PATH; - Path -> - Path +-spec dump(stdout | file:filename_all()) -> ok | error_return(). +dump(Output) -> + Y = get_option(yaml_config), + dump(Y, Output). + +-spec dump(term(), stdout | file:filename_all()) -> ok | error_return(). +dump(Y, Output) -> + Data = fast_yaml:encode(Y), + case Output of + stdout -> + io:format("~ts~n", [Data]); + FileName -> + try + ok = filelib:ensure_dir(FileName), + ok = file:write_file(FileName, Data) + catch _:{badmatch, {error, Reason}} -> + {error, {write_file, FileName, Reason}} end end. --spec get_env_config() -> {ok, string()} | undefined. -get_env_config() -> - %% First case: the filename can be specified with: erl -config "/path/to/ejabberd.yml". - case application:get_env(config) of - R = {ok, _Path} -> R; - undefined -> - %% Second case for embbeding ejabberd in another app, for example for Elixir: - %% config :ejabberd, - %% file: "config/ejabberd.yml" - application:get_env(ejabberd, file) +-spec get_option(option(), term()) -> term(). +get_option(Opt, Default) -> + try get_option(Opt) + catch _:badarg -> Default end. -%% @doc Read the ejabberd configuration file. -%% It also includes additional configuration files and replaces macros. -%% This function will crash if finds some error in the configuration file. -%% @spec (File::string()) -> #state{} -read_file(File) -> - read_file(File, [{replace_macros, true}, - {include_files, true}, - {include_modules_configs, true}]). +-spec get_option(option()) -> term(). +get_option(Opt) when is_atom(Opt) -> + get_option({Opt, global}); +get_option({O, Host} = Opt) -> + Tab = case get_tmp_config() of + undefined -> ejabberd_options; + T -> T + end, + try ets:lookup_element(Tab, Opt, 2) + catch + error:badarg:StackTrace when Host /= global -> + Val = get_option({O, global}), + ?DEBUG("Option '~ts' is not defined for virtual host '~ts'. " + "This is a bug, please report it with the following " + "stacktrace included:~n** ~ts", + [O, Host, misc:format_exception(2, error, badarg, StackTrace)]), + Val + end. -read_file(File, Opts) -> - Terms1 = case is_elixir_enabled() of - true -> - case 'Elixir.Ejabberd.ConfigUtil':is_elixir_config(File) of - true -> - 'Elixir.Ejabberd.Config':init(File), - 'Elixir.Ejabberd.Config':get_ejabberd_opts(); - false -> - get_plain_terms_file(File, Opts) - end; - false -> - get_plain_terms_file(File, Opts) - end, - Terms_macros = case proplists:get_bool(replace_macros, Opts) of - true -> replace_macros(Terms1); - false -> Terms1 - end, - Terms = transform_terms(Terms_macros), - State = lists:foldl(fun search_hosts/2, #state{}, Terms), - {Head, Tail} = lists:partition( - fun({host_config, _}) -> false; - ({append_host_config, _}) -> false; - (_) -> true - end, Terms), - State1 = lists:foldl(fun process_term/2, State, Head ++ Tail), - State1#state{opts = compact(State1#state.opts)}. +-spec set_option(option(), term()) -> ok. +set_option(Opt, Val) when is_atom(Opt) -> + set_option({Opt, global}, Val); +set_option(Opt, Val) -> + Tab = case get_tmp_config() of + undefined -> ejabberd_options; + T -> T + end, + ets:insert(Tab, {Opt, Val}), + ok. --spec load_file(string()) -> ok. +-spec get_version() -> binary(). +get_version() -> + get_option(version). -load_file(File) -> - State0 = read_file(File), - State = validate_opts(State0), - set_opts(State). +-spec get_myhosts() -> [binary(), ...]. +get_myhosts() -> + get_option(hosts). --spec reload_file() -> ok. +-spec get_myname() -> binary(). +get_myname() -> + get_option(host). -reload_file() -> - Config = get_ejabberd_config_path(), - load_file(Config). +-spec get_mylang() -> binary(). +get_mylang() -> + get_lang(global). --spec convert_to_yaml(file:filename()) -> ok | {error, any()}. +-spec get_lang(global | binary()) -> binary(). +get_lang(Host) -> + get_option({language, Host}). -convert_to_yaml(File) -> - convert_to_yaml(File, stdout). +-spec get_uri() -> binary(). +get_uri() -> + <<"https://www.process-one.net/ejabberd/">>. --spec convert_to_yaml(file:filename(), - stdout | file:filename()) -> ok | {error, any()}. +-spec get_copyright() -> binary(). +get_copyright() -> + <<"Copyright (c) ProcessOne">>. -convert_to_yaml(File, Output) -> - State = read_file(File, [{include_files, false}]), - Opts = [{K, V} || #local_config{key = K, value = V} <- State#state.opts], - {GOpts, HOpts} = split_by_hosts(Opts), - NewOpts = GOpts ++ lists:map( - fun({Host, Opts1}) -> - {host_config, [{Host, Opts1}]} - end, HOpts), - Data = fast_yaml:encode(lists:reverse(NewOpts)), - case Output of - stdout -> - io:format("~s~n", [Data]); - FileName -> - file:write_file(FileName, Data) +-spec get_shared_key() -> binary(). +get_shared_key() -> + get_option(shared_key). + +-spec get_node_start() -> integer(). +get_node_start() -> + get_option(node_start). + +-spec fsm_limit_opts([proplists:property()]) -> [{max_queue, pos_integer()}]. +fsm_limit_opts(Opts) -> + case lists:keyfind(max_fsm_queue, 1, Opts) of + {_, I} when is_integer(I), I>0 -> + [{max_queue, I}]; + false -> + case get_option(max_fsm_queue) of + undefined -> []; + N -> [{max_queue, N}] + end + end. + +-spec codec_options() -> [xmpp:decode_option()]. +codec_options() -> + case get_option(validate_stream) of + true -> []; + false -> [ignore_els] + end. + +%% Do not use this function in runtime: +%% It's slow and doesn't read 'version' option from the config. +%% Use ejabberd_option:version() instead. +-spec version() -> binary(). +version() -> + case application:get_env(ejabberd, custom_vsn) of + {ok, Vsn0} when is_list(Vsn0) -> + list_to_binary(Vsn0); + {ok, Vsn1} when is_binary(Vsn1) -> + Vsn1; + _ -> + case application:get_key(ejabberd, vsn) of + undefined -> <<"">>; + {ok, Vsn} -> list_to_binary(Vsn) + end + end. + +-spec default_db(binary() | global, module()) -> atom(). +default_db(Host, Module) -> + default_db(default_db, db_type, Host, Module, mnesia). + +-spec default_db(binary() | global, module(), atom()) -> atom(). +default_db(Host, Module, Default) -> + default_db(default_db, db_type, Host, Module, Default). + +-spec default_ram_db(binary() | global, module()) -> atom(). +default_ram_db(Host, Module) -> + default_db(default_ram_db, ram_db_type, Host, Module, mnesia). + +-spec default_ram_db(binary() | global, module(), atom()) -> atom(). +default_ram_db(Host, Module, Default) -> + default_db(default_ram_db, ram_db_type, Host, Module, Default). + +-spec default_db(default_db | default_ram_db, db_type | ram_db_type, binary() | global, module(), atom()) -> atom(). +default_db(Opt, ModOpt, Host, Mod, Default) -> + Type = get_option({Opt, Host}), + DBMod = list_to_atom(atom_to_list(Mod) ++ "_" ++ atom_to_list(Type)), + case code:ensure_loaded(DBMod) of + {module, _} -> Type; + {error, _} -> + ?WARNING_MSG("Module ~ts doesn't support database '~ts' " + "defined in toplevel option '~ts': will use the value " + "set in ~ts option '~ts', or '~ts' as fallback", + [Mod, Type, Opt, Mod, ModOpt, Default]), + Default + end. + +-spec beams(local | external | all) -> [module()]. +beams(local) -> + {ok, Mods} = application:get_key(ejabberd, modules), + Mods; +beams(external) -> + ExtMods = [Name || {Name, _Details} <- ext_mod:installed()], + lists:foreach( + fun(ExtMod) -> + ExtModPath = ext_mod:module_ebin_dir(ExtMod), + case lists:member(ExtModPath, code:get_path()) of + true -> ok; + false -> code:add_patha(ExtModPath) + end + end, ExtMods), + case application:get_env(ejabberd, external_beams) of + {ok, Path0} -> + Paths = case Path0 of + [L|_] = V when is_list(L) -> V; + L -> [L] + end, + CustMods = lists:foldl( + fun(Path, CM) -> + case lists:member(Path, code:get_path()) of + true -> ok; + false -> code:add_patha(Path) + end, + Beams = filelib:wildcard(filename:join(Path, "*\.beam")), + CM ++ [list_to_atom(filename:rootname(filename:basename(Beam))) + || Beam <- Beams] + end, [], Paths), + CustMods ++ ExtMods; + _ -> + ExtMods + end; +beams(all) -> + beams(local) ++ beams(external). + +-spec may_hide_data(term()) -> term(). +may_hide_data(Data) -> + case get_option(hide_sensitive_log_data) of + false -> Data; + true -> "hidden_by_ejabberd" end. %% Some Erlang apps expects env parameters to be list and not binary. %% For example, Mnesia is not able to start if mnesia dir is passed as a binary. %% However, binary is most common on Elixir, so it is easy to make a setup mistake. --spec env_binary_to_list(atom(), atom()) -> {ok, any()}|undefined. +-spec env_binary_to_list(atom(), atom()) -> {ok, any()} | undefined. env_binary_to_list(Application, Parameter) -> %% Application need to be loaded to allow setting parameters - application:load(Application), + case proplists:is_defined(Application, application:loaded_applications()) of + true -> + ok; + false -> + application:load(Application) + end, case application:get_env(Application, Parameter) of {ok, Val} when is_binary(Val) -> BVal = binary_to_list(Val), @@ -228,1196 +357,628 @@ env_binary_to_list(Application, Parameter) -> Other end. -%% @doc Read an ejabberd configuration file and return the terms. -%% Input is an absolute or relative path to an ejabberd config file. -%% Returns a list of plain terms, -%% in which the options 'include_config_file' were parsed -%% and the terms in those files were included. -%% @spec(iolist()) -> [term()] -get_plain_terms_file(File, Opts) when is_binary(File) -> - get_plain_terms_file(binary_to_list(File), Opts); -get_plain_terms_file(File1, Opts) -> - File = get_absolute_path(File1), - DontStopOnError = lists:member(dont_halt_on_error, Opts), - case consult(File) of - {ok, Terms} -> - BinTerms1 = strings_to_binary(Terms), - ModInc = case proplists:get_bool(include_modules_configs, Opts) of - true -> - Files = [{filename:rootname(filename:basename(F)), F} - || F <- filelib:wildcard(ext_mod:config_dir() ++ "/*.{yml,yaml}") - ++ filelib:wildcard(ext_mod:modules_dir() ++ "/*/conf/*.{yml,yaml}")], - [proplists:get_value(F,Files) || F <- proplists:get_keys(Files)]; - _ -> - [] - end, - BinTerms = BinTerms1 ++ [{include_config_file, list_to_binary(V)} || V <- ModInc], - case proplists:get_bool(include_files, Opts) of - true -> - include_config_files(BinTerms); - false -> - BinTerms - end; - {error, enoent, Reason} -> - case DontStopOnError of - true -> - ?WARNING_MSG(Reason, []), - []; - _ -> - ?ERROR_MSG(Reason, []), - exit_or_halt(Reason) - end; - {error, Reason} -> - ?ERROR_MSG(Reason, []), - case DontStopOnError of - true -> []; - _ -> exit_or_halt(Reason) - end +%% ejabberd_options calls this function when parsing options inside host_config +-spec validators([atom()]) -> {econf:validators(), [atom()]}. +validators(Disallowed) -> + Host = global, + DefinedKeywords = get_defined_keywords(Host), + validators(Disallowed, DefinedKeywords). + +%% validate/1 calls this function when parsing toplevel options +-spec validators([atom()], [any()]) -> {econf:validators(), [atom()]}. +validators(Disallowed, DK) -> + Modules = callback_modules(all), + Validators = lists:foldl( + fun(M, Vs) -> + maps:merge(Vs, validators(M, Disallowed, DK)) + end, #{}, Modules), + Required = lists:flatmap( + fun(M) -> + [O || O <- M:options(), is_atom(O)] + end, Modules), + {Validators, Required}. + +-spec convert_to_yaml(file:filename()) -> ok | error_return(). +convert_to_yaml(File) -> + convert_to_yaml(File, stdout). + +-spec convert_to_yaml(file:filename(), + stdout | file:filename()) -> ok | error_return(). +convert_to_yaml(File, Output) -> + case read_erlang_file(File, []) of + {ok, Y} -> + dump(Y, Output); + Err -> + Err end. -consult(File) -> - case filename:extension(File) of - Ex when (Ex == ".yml") or (Ex == ".yaml") -> - case fast_yaml:decode_from_file(File, [plain_as_atom]) of - {ok, []} -> - {ok, []}; - {ok, [Document|_]} -> - {ok, parserl(Document)}; - {error, Err} -> - Msg1 = "Cannot load " ++ File ++ ": ", - Msg2 = fast_yaml:format_error(Err), - case Err of - enoent -> - {error, enoent, Msg1 ++ Msg2}; - _ -> - {error, Msg1 ++ Msg2} - end - end; - _ -> - case file:consult(File) of - {ok, Terms} -> - {ok, Terms}; - {error, enoent} -> - {error, enoent}; - {error, {LineNumber, erl_parse, _ParseMessage} = Reason} -> - {error, describe_config_problem(File, Reason, LineNumber)}; - {error, Reason} -> - case Reason of - enoent -> - {error, enoent, describe_config_problem(File, Reason)}; - _ -> - {error, describe_config_problem(File, Reason)} +-spec format_error(error_return()) -> string(). +format_error({error, Reason, Ctx}) -> + econf:format_error(Reason, Ctx); +format_error({error, {merge_conflict, Opt, Host}}) -> + lists:flatten( + io_lib:format( + "Cannot merge value of option '~ts' defined in append_host_config " + "for virtual host ~ts: only options of type list or map are allowed " + "in append_host_config. Hint: specify the option in host_config", + [Opt, Host])); +format_error({error, {old_config, Path, Reason}}) -> + lists:flatten( + io_lib:format( + "Failed to read configuration from '~ts': ~ts~ts", + [Path, + case Reason of + {_, _, _} -> "at line "; + _ -> "" + end, file:format_error(Reason)])); +format_error({error, {write_file, Path, Reason}}) -> + lists:flatten( + io_lib:format( + "Failed to write to '~ts': ~ts", + [Path, + file:format_error(Reason)])); +format_error({error, {exception, Class, Reason, St}}) -> + lists:flatten( + io_lib:format( + "Exception occurred during configuration processing. " + "This is most likely due to faulty/incompatible validator in " + "third-party code. If you are not running any third-party " + "code, please report the bug with ejabberd configuration " + "file attached and the following stacktrace included:~n** ~ts", + [misc:format_exception(2, Class, Reason, St)])). + +%% @format-begin + +replace_keywords(Host, Value) -> + Keywords = get_defined_keywords(Host) ++ get_predefined_keywords(Host), + replace_keywords(Host, Value, Keywords). + +replace_keywords(Host, List, Keywords) when is_list(List) -> + [replace_keywords(Host, Element, Keywords) || Element <- List]; +replace_keywords(Host, Atom, Keywords) when is_atom(Atom) -> + Str = atom_to_list(Atom), + Bin = iolist_to_binary(Str), + case Str == string:uppercase(Str) of + false -> + BinaryReplaced = replace_keywords(Host, Bin, Keywords), + binary_to_atom(BinaryReplaced, utf8); + true -> + case proplists:get_value(Bin, Keywords) of + undefined -> + Atom; + Replacement -> + Replacement end - end - end. - -parserl(<<"> ", Term/binary>>) -> - {ok, A2, _} = erl_scan:string(binary_to_list(Term)), - {ok, A3} = erl_parse:parse_term(A2), - A3; -parserl({A, B}) -> - {parserl(A), parserl(B)}; -parserl([El|Tail]) -> - [parserl(El) | parserl(Tail)]; -parserl(Other) -> - Other. - -%% @doc Convert configuration filename to absolute path. -%% Input is an absolute or relative path to an ejabberd configuration file. -%% And returns an absolute path to the configuration file. -%% @spec (string()) -> string() -get_absolute_path(File) -> - case filename:pathtype(File) of - absolute -> - File; - relative -> - {ok, Dir} = file:get_cwd(), - filename:absname_join(Dir, File); - volumerelative -> - filename:absname(File) - end. - - -search_hosts(Term, State) -> - case Term of - {host, Host} -> - if - State#state.hosts == [] -> - set_hosts_in_options([Host], State); - true -> - ?ERROR_MSG("Can't load config file: " - "too many hosts definitions", []), - exit("too many hosts definitions") - end; - {hosts, Hosts} -> - if - State#state.hosts == [] -> - set_hosts_in_options(Hosts, State); - true -> - ?ERROR_MSG("Can't load config file: " - "too many hosts definitions", []), - exit("too many hosts definitions") - end; - _ -> - State - end. - -set_hosts_in_options(Hosts, State) -> - PrepHosts = normalize_hosts(Hosts), - NewOpts = lists:filter(fun({local_config,{hosts,global},_}) -> false; - (_) -> true - end, State#state.opts), - set_option({hosts, global}, PrepHosts, State#state{hosts = PrepHosts, opts = NewOpts}). - -normalize_hosts(Hosts) -> - normalize_hosts(Hosts,[]). -normalize_hosts([], PrepHosts) -> - lists:reverse(PrepHosts); -normalize_hosts([Host|Hosts], PrepHosts) -> - case jid:nodeprep(iolist_to_binary(Host)) of - error -> - ?ERROR_MSG("Can't load config file: " - "invalid host name [~p]", [Host]), - exit("invalid hostname"); - PrepHost -> - normalize_hosts(Hosts, [PrepHost|PrepHosts]) - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% Errors reading the config file - -describe_config_problem(Filename, Reason) -> - Text1 = lists:flatten("Problem loading ejabberd config file " ++ Filename), - Text2 = lists:flatten(" : " ++ file:format_error(Reason)), - ExitText = Text1 ++ Text2, - ExitText. - -describe_config_problem(Filename, Reason, LineNumber) -> - Text1 = lists:flatten("Problem loading ejabberd config file " ++ Filename), - Text2 = lists:flatten(" approximately in the line " - ++ file:format_error(Reason)), - ExitText = Text1 ++ Text2, - Lines = get_config_lines(Filename, LineNumber, 10, 3), - ?ERROR_MSG("The following lines from your configuration file might be" - " relevant to the error: ~n~s", [Lines]), - ExitText. - -get_config_lines(Filename, TargetNumber, PreContext, PostContext) -> - {ok, Fd} = file:open(Filename, [read]), - LNumbers = lists:seq(TargetNumber-PreContext, TargetNumber+PostContext), - NextL = io:get_line(Fd, no_prompt), - R = get_config_lines2(Fd, NextL, 1, LNumbers, []), - file:close(Fd), - R. - -get_config_lines2(_Fd, eof, _CurrLine, _LNumbers, R) -> - lists:reverse(R); -get_config_lines2(_Fd, _NewLine, _CurrLine, [], R) -> - lists:reverse(R); -get_config_lines2(Fd, Data, CurrLine, [NextWanted | LNumbers], R) when is_list(Data) -> - NextL = io:get_line(Fd, no_prompt), - if - CurrLine >= NextWanted -> - Line2 = [integer_to_list(CurrLine), ": " | Data], - get_config_lines2(Fd, NextL, CurrLine+1, LNumbers, [Line2 | R]); - true -> - get_config_lines2(Fd, NextL, CurrLine+1, [NextWanted | LNumbers], R) - end. - -%% If ejabberd isn't yet running in this node, then halt the node -exit_or_halt(ExitText) -> - case [Vsn || {ejabberd, _Desc, Vsn} <- application:which_applications()] of - [] -> - timer:sleep(1000), - halt(string:substr(ExitText, 1, 199)); - [_] -> - exit(ExitText) - end. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% Support for 'include_config_file' - -get_config_option_key(Name, Val) -> - if Name == listen -> - case Val of - {{Port, IP, Trans}, _Mod, _Opts} -> - {Port, IP, Trans}; - {{Port, Trans}, _Mod, _Opts} when Trans == tcp; Trans == udp -> - {Port, {0,0,0,0}, Trans}; - {{Port, IP}, _Mod, _Opts} -> - {Port, IP, tcp}; - {Port, _Mod, _Opts} -> - {Port, {0,0,0,0}, tcp}; - V when is_list(V) -> - lists:foldl( - fun({port, Port}, {_, IP, T}) -> - {Port, IP, T}; - ({ip, IP}, {Port, _, T}) -> - {Port, IP, T}; - ({transport, T}, {Port, IP, _}) -> - {Port, IP, T}; - (_, Res) -> - Res - end, {5222, {0,0,0,0}, tcp}, Val) - end; - is_tuple(Val) -> - element(1, Val); - true -> - Val - end. - -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, Map, Res) when is_map(Map) -> - [{Name, maps:values(Map)} | Res]; - (Name, Val, Res) -> - [{Name, Val} | Res] - end, [], IMap). - -merge_configs(Terms, ResMap) -> - lists:foldl(fun({Name, Val}, Map) when is_list(Val), Name =/= auth_method -> - Old = maps:get(Name, Map, #{}), - 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)}; - true -> - SVal - end, - maps:put(get_config_option_key(Name, SVal), NVal, OMap) - end, Old, Val), - maps:put(Name, New, Map); - ({Name, Val}, Map) -> - maps:put(Name, Val, Map) - end, ResMap, Terms). - -%% @doc Include additional configuration files in the list of terms. -%% @spec ([term()]) -> [term()] -include_config_files(Terms) -> - {FileOpts, Terms1} = - lists:mapfoldl( - fun({include_config_file, _} = T, Ts) -> - {[transform_include_option(T)], Ts}; - ({include_config_file, _, _} = T, Ts) -> - {[transform_include_option(T)], Ts}; - (T, Ts) -> - {[], [T|Ts]} - end, [], Terms), - Terms2 = lists:flatmap( - fun({File, Opts}) -> - include_config_file(File, Opts) - end, lists:flatten(FileOpts)), - - M1 = merge_configs(Terms1, #{}), - M2 = merge_configs(Terms2, M1), - maps_to_lists(M2). - -transform_include_option({include_config_file, File}) when is_list(File) -> - case is_string(File) of - true -> {File, []}; - false -> File end; -transform_include_option({include_config_file, Filename}) -> - {Filename, []}; -transform_include_option({include_config_file, Filename, Options}) -> - {Filename, Options}. +replace_keywords(_Host, Binary, Keywords) when is_binary(Binary) -> + lists:foldl(fun ({Key, Replacement}, V) when is_binary(Replacement) -> + misc:expand_keyword(<<"@", Key/binary, "@">>, V, Replacement); + ({_, _}, V) -> + V + end, + Binary, + Keywords); +replace_keywords(Host, {Element1, Element2}, Keywords) -> + {Element1, replace_keywords(Host, Element2, Keywords)}; +replace_keywords(_Host, Value, _DK) -> + Value. -include_config_file(Filename, Options) -> - Included_terms = get_plain_terms_file(Filename, [{include_files, true}, dont_halt_on_error]), - Disallow = proplists:get_value(disallow, Options, []), - Included_terms2 = delete_disallowed(Disallow, Included_terms), - Allow_only = proplists:get_value(allow_only, Options, all), - keep_only_allowed(Allow_only, Included_terms2). +get_defined_keywords(Host) -> + Tab = case get_tmp_config() of + undefined -> + ejabberd_options; + T -> + T + end, + get_defined_keywords(Tab, Host). -%% @doc Filter from the list of terms the disallowed. -%% Returns a sublist of Terms without the ones which first element is -%% included in Disallowed. -%% @spec (Disallowed::[atom()], Terms::[term()]) -> [term()] -delete_disallowed(Disallowed, Terms) -> - lists:foldl( - fun(Dis, Ldis) -> - delete_disallowed2(Dis, Ldis) - end, - Terms, - Disallowed). +get_defined_keywords(Tab, Host) -> + KeysHost = + case ets:lookup(Tab, {define_keyword, Host}) of + [{_, List}] -> + List; + _ -> + [] + end, + KeysGlobal = + case Host /= global andalso ets:lookup(Tab, {define_keyword, global}) of + [{_, ListG}] -> + ListG; + _ -> + [] + end, + %% Trying to get defined keywords in host_config when starting ejabberd, + %% the options are not yet stored in ets + KeysTemp = + case not is_atom(Tab) andalso KeysHost == [] andalso KeysGlobal == [] of + true -> + get_defined_keywords_yaml_config(ets:lookup_element(Tab, {yaml_config, global}, 2)); + false -> + [] + end, + lists:reverse(KeysTemp ++ KeysGlobal ++ KeysHost). -delete_disallowed2(Disallowed, [H|T]) -> - case element(1, H) of - Disallowed -> - ?WARNING_MSG("The option '~p' is disallowed, " - "and will not be accepted", [Disallowed]), - delete_disallowed2(Disallowed, T); - _ -> - [H|delete_disallowed2(Disallowed, T)] - end; -delete_disallowed2(_, []) -> - []. +get_defined_keywords_yaml_config(Y) -> + [{erlang:atom_to_binary(KwAtom, latin1), KwValue} + || {KwAtom, KwValue} <- proplists:get_value(define_keyword, Y, [])]. -%% @doc Keep from the list only the allowed terms. -%% Returns a sublist of Terms with only the ones which first element is -%% included in Allowed. -%% @spec (Allowed::[atom()], Terms::[term()]) -> [term()] -keep_only_allowed(all, Terms) -> - Terms; -keep_only_allowed(Allowed, Terms) -> - {As, NAs} = lists:partition( - fun(Term) -> - lists:member(element(1, Term), Allowed) - end, - Terms), - [?WARNING_MSG("This option is not allowed, " - "and will not be accepted:~n~p", [NA]) - || NA <- NAs], - As. +get_predefined_keywords(Host) -> + HostList = + case Host of + global -> + []; + _ -> + [{<<"HOST">>, Host}, {<<"HOST_URL_ENCODE">>, misc:url_encode(Host)}] + end, + Home = misc:get_home(), + ConfigDirPath = + iolist_to_binary(filename:dirname( + ejabberd_config:path())), + LogDirPath = + iolist_to_binary(filename:dirname( + ejabberd_logger:get_log_path())), + HostList + ++ [{<<"HOME">>, list_to_binary(Home)}, + {<<"CONFIG_PATH">>, ConfigDirPath}, + {<<"LOG_PATH">>, LogDirPath}, + {<<"SEMVER">>, ejabberd_option:version()}, + {<<"VERSION">>, + misc:semver_to_xxyy( + ejabberd_option:version())}]. +resolve_host_alias(Host) -> + case lists:member(Host, ejabberd_option:hosts()) of + true -> + Host; + false -> + resolve_host_alias2(Host) + end. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% Support for Macro +resolve_host_alias2(Host) -> + Result = + lists:filter(fun({Alias1, _Vhost}) -> is_glob_match(Host, Alias1) end, + ejabberd_option:hosts_alias()), + case Result of + [{_, Vhost} | _] when is_binary(Vhost) -> + ?DEBUG("(~p) Alias host '~s' resolved into vhost '~s'", [self(), Host, Vhost]), + Vhost; + [] -> + ?DEBUG("(~p) Request sent to host '~s', which isn't a vhost or an alias", + [self(), Host]), + Host + end. -%% @doc Replace the macros with their defined values. -%% @spec (Terms::[term()]) -> [term()] -replace_macros(Terms) -> - {TermsOthers, Macros} = split_terms_macros(Terms), - replace(TermsOthers, Macros). +%% Copied from ejabberd-2.0.0/src/acl.erl +is_regexp_match(String, RegExp) -> + case ejabberd_regexp:run(String, RegExp) of + nomatch -> + false; + match -> + true; + {error, ErrDesc} -> + io:format("Wrong regexp ~p in ACL: ~p", [RegExp, ErrDesc]), + false + end. -%% @doc Split Terms into normal terms and macro definitions. -%% @spec (Terms) -> {Terms, Macros} -%% Terms = [term()] -%% Macros = [macro()] -split_terms_macros(Terms) -> - lists:foldl( - fun(Term, {TOs, Ms}) -> - case Term of - {define_macro, Key, Value} -> - case is_correct_macro({Key, Value}) of - true -> - {TOs, Ms++[{Key, Value}]}; - false -> - exit({macro_not_properly_defined, Term}) - end; - {define_macro, KeyVals} -> - case lists:all(fun is_correct_macro/1, KeyVals) of - true -> - {TOs, Ms ++ KeyVals}; - false -> - exit({macros_not_properly_defined, Term}) - end; - Term -> - {TOs ++ [Term], Ms} +is_glob_match(String, <<"!", Glob/binary>>) -> + not is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)); +is_glob_match(String, Glob) -> + is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)). +%% @format-end + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +-spec path() -> binary(). +path() -> + unicode:characters_to_binary( + case get_env_config() of + {ok, Path} -> + Path; + undefined -> + case os:getenv("EJABBERD_CONFIG_PATH") of + false -> + "ejabberd.yml"; + Path -> + Path end - end, - {[], []}, - Terms). + end). -is_correct_macro({Key, _Val}) -> - is_atom(Key) and is_all_uppercase(Key); -is_correct_macro(_) -> - false. +-spec get_env_config() -> {ok, string()} | undefined. +get_env_config() -> + %% First case: the filename can be specified with: erl -config "/path/to/ejabberd.yml". + case application:get_env(ejabberd, config) of + R = {ok, _Path} -> R; + undefined -> + %% Second case for embbeding ejabberd in another app, for example for Elixir: + %% config :ejabberd, + %% file: "config/ejabberd.yml" + application:get_env(ejabberd, file) + end. -%% @doc Recursively replace in Terms macro usages with the defined value. -%% @spec (Terms, Macros) -> Terms -%% Terms = [term()] -%% Macros = [macro()] -replace([], _) -> - []; -replace([Term|Terms], Macros) -> - [replace_term(Term, Macros) | replace(Terms, Macros)]; -replace(Term, Macros) -> - replace_term(Term, Macros). +-spec create_tmp_config() -> ok. +create_tmp_config() -> + T = ets:new(options, [private]), + put(ejabberd_options, T), + ok. -replace_term(Key, Macros) when is_atom(Key) -> - case is_all_uppercase(Key) of - true -> - case proplists:get_value(Key, Macros) of - undefined -> exit({undefined_macro, Key}); - Value -> Value +-spec get_tmp_config() -> ets:tid() | undefined. +get_tmp_config() -> + get(ejabberd_options). + +-spec delete_tmp_config() -> ok. +delete_tmp_config() -> + case get_tmp_config() of + undefined -> + ok; + T -> + erase(ejabberd_options), + ets:delete(T), + ok + end. + +-spec callback_modules(local | external | all) -> [module()]. +callback_modules(local) -> + [ejabberd_options]; +callback_modules(external) -> + lists:filter( + fun(M) -> + case code:ensure_loaded(M) of + {module, _} -> + erlang:function_exported(M, options, 0) + andalso erlang:function_exported(M, opt_type, 1); + {error, _} -> + false + end + end, beams(external)); +callback_modules(all) -> + misc:lists_uniq(callback_modules(local) ++ callback_modules(external)). + +-spec validators(module(), [atom()], [any()]) -> econf:validators(). +validators(Mod, Disallowed, DK) -> + Keywords = DK ++ get_predefined_keywords(global), + maps:from_list( + lists:filtermap( + fun(O) -> + case lists:member(O, Disallowed) of + true -> false; + false -> + Type = + try Mod:opt_type(O) + catch _:_ -> + ejabberd_options:opt_type(O) + end, + TypeProcessed = + econf:and_then( + fun(B) -> + replace_keywords(global, B, Keywords) + end, + Type), + {true, {O, TypeProcessed}} + end + end, proplists:get_keys(Mod:options()))). + +read_file(File) -> + read_file(File, [replace_macros, include_files, include_modules_configs]). + +read_file(File, Opts) -> + {Opts1, Opts2} = proplists:split(Opts, [replace_macros, include_files]), + Ret = case filename:extension(File) of + Ex when Ex == <<".yml">> orelse Ex == <<".yaml">> -> + Files = case proplists:get_bool(include_modules_configs, Opts2) of + true -> ext_mod:modules_configs(); + false -> [] + end, + lists:foreach( + fun(F) -> + ?INFO_MSG("Loading third-party configuration from ~ts", [F]) + end, Files), + read_yaml_files([File|Files], lists:flatten(Opts1)); + _ -> + read_erlang_file(File, lists:flatten(Opts1)) + end, + case Ret of + {ok, Y} -> + InstalledModules = maybe_install_contrib_modules(Y), + ValResult = validate(Y), + case InstalledModules of + [] -> ok; + _ -> spawn(fun() -> timer:sleep(5000), ?MODULE:reload() end) + end, + ValResult; + Err -> + Err + end. + +get_additional_macros() -> + MacroStrings = lists:foldl(fun([$E, $J, $A, $B, $B, $E, $R, $D, $_, $M, $A, $C, $R, $O, $_ | MacroString], Acc) -> + [parse_macro_string(MacroString) | Acc]; + (_, Acc) -> + Acc + end, + [], + os:getenv()), + {additional_macros, MacroStrings}. + +parse_macro_string(MacroString) -> + [NameString, ValueString] = string:split(MacroString, "="), + {ok, [ValueDecoded]} = fast_yaml:decode(ValueString), + {list_to_atom(NameString), ValueDecoded}. + +read_yaml_files(Files, Opts) -> + ParseOpts = [plain_as_atom, get_additional_macros() | lists:flatten(Opts)], + lists:foldl( + fun(File, {ok, Y1}) -> + case econf:parse(File, #{'_' => econf:any()}, ParseOpts) of + {ok, Y2} -> {ok, Y1 ++ Y2}; + Err -> Err + end; + (_, Err) -> + Err + end, {ok, []}, Files). + +read_erlang_file(File, _) -> + case ejabberd_old_config:read_file(File) of + {ok, Y} -> + econf:replace_macros(Y); + Err -> + Err + end. + +-spec maybe_install_contrib_modules(term()) -> [atom()]. +maybe_install_contrib_modules(Options) -> + case {lists:keysearch(allow_contrib_modules, 1, Options), + lists:keysearch(install_contrib_modules, 1, Options)} of + {Allow, {value, {_, InstallContribModules}}} + when (Allow == false) or + (Allow == {value, {allow_contrib_modules, true}}) -> + ext_mod:install_contrib_modules(InstallContribModules, Options); + _ -> + [] + end. + +-spec validate(term()) -> {ok, [{atom(), term()}]} | error_return(). +validate(Y1) -> + case pre_validate(Y1) of + {ok, Y2} -> + set_loglevel(proplists:get_value(loglevel, Y2, info)), + ejabberd_logger:set_modules_fully_logged(proplists:get_value(log_modules_fully, Y2, [])), + case ejabberd_config_transformer:map_reduce(Y2) of + {ok, Y3} -> + Hosts = proplists:get_value(hosts, Y3), + Version = proplists:get_value(version, Y3, version()), + DK = get_defined_keywords_yaml_config(Y3), + create_tmp_config(), + set_option(hosts, Hosts), + set_option(host, hd(Hosts)), + set_option(version, Version), + set_option(yaml_config, Y3), + {Validators, Required} = validators([], DK), + Validator = econf:options(Validators, + [{required, Required}, + unique]), + econf:validate(Validator, Y3); + Err -> + Err end; - false -> - Key - end; -replace_term({use_macro, Key, Value}, Macros) -> - proplists:get_value(Key, Macros, Value); -replace_term(Term, Macros) when is_list(Term) -> - replace(Term, Macros); -replace_term(Term, Macros) when is_tuple(Term) -> - List = tuple_to_list(Term), - List2 = replace(List, Macros), - list_to_tuple(List2); -replace_term(Term, _) -> - Term. - -is_all_uppercase(Atom) -> - String = erlang:atom_to_list(Atom), - lists:all(fun(C) when C >= $a, C =< $z -> false; - (_) -> true - end, String). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% Process terms - -process_term(Term, State) -> - case Term of - {host_config, HostTerms} -> - lists:foldl( - fun({Host, Terms}, AccState) -> - lists:foldl(fun(T, S) -> - process_host_term(T, Host, S, set) - end, AccState, Terms) - end, State, HostTerms); - {append_host_config, HostTerms} -> - lists:foldl( - fun({Host, Terms}, AccState) -> - lists:foldl(fun(T, S) -> - process_host_term(T, Host, S, append) - end, AccState, Terms) - end, State, HostTerms); - _ -> - process_host_term(Term, global, State, set) + Err -> + Err end. -process_host_term(Term, Host, State, Action) -> - case Term of - {modules, Modules} when Action == set -> - set_option({modules, Host}, replace_modules(Modules), State); - {modules, Modules} when Action == append -> - append_option({modules, Host}, replace_modules(Modules), State); - {host, _} -> - State; - {hosts, _} -> - State; - {Opt, Val} when Action == set -> - set_option({rename_option(Opt), Host}, change_val(Opt, Val), State); - {Opt, Val} when Action == append -> - append_option({rename_option(Opt), Host}, change_val(Opt, Val), State); - Opt -> - ?WARNING_MSG("Ignore invalid (outdated?) option ~p", [Opt]), - State +-spec pre_validate(term()) -> {ok, [{atom(), term()}]} | error_return(). +pre_validate(Y1) -> + econf:validate( + econf:and_then( + econf:options( + #{hosts => ejabberd_options:opt_type(hosts), + loglevel => ejabberd_options:opt_type(loglevel), + version => ejabberd_options:opt_type(version), + '_' => econf:any()}, + [{required, [hosts]}]), + fun econf:group_dups/1), Y1). + +-spec load_file(binary()) -> ok | error_return(). +load_file(File) -> + try + case read_file(File) of + {ok, Terms} -> + case set_host_config(Terms) of + {ok, Map} -> + T = get_tmp_config(), + Hosts = get_myhosts(), + apply_defaults(T, Hosts, Map), + case validate_modules(Hosts) of + {ok, ModOpts} -> + ets:insert(T, ModOpts), + set_option(host, hd(Hosts)), + commit(), + set_fqdn(); + Err -> + abort(Err) + end; + Err -> + abort(Err) + end; + Err -> + abort(Err) + end + catch + Class:Reason:Stack -> + {error, {exception, Class, Reason, Stack}} end. -rename_option(Option) when is_atom(Option) -> - case atom_to_list(Option) of - "odbc_" ++ T -> - NewOption = list_to_atom("sql_" ++ T), - ?WARNING_MSG("Option '~s' is obsoleted, use '~s' instead", - [Option, NewOption]), - NewOption; - _ -> - Option - end; -rename_option(Option) -> - Option. +-spec commit() -> ok. +commit() -> + T = get_tmp_config(), + NewOpts = ets:tab2list(T), + ets:insert(ejabberd_options, NewOpts), + delete_tmp_config(). -change_val(auth_method, Val) -> - prepare_opt_val(auth_method, Val, - fun(V) -> - L = if is_list(V) -> V; - true -> [V] - end, - lists:map( - fun(odbc) -> sql; - (internal) -> mnesia; - (A) when is_atom(A) -> A - end, L) - end, [mnesia]); -change_val(_Opt, Val) -> +-spec abort(error_return()) -> error_return(). +abort(Err) -> + delete_tmp_config(), + try ets:lookup_element(ejabberd_options, {loglevel, global}, 2) of + Level -> set_loglevel(Level) + catch _:badarg -> + ok + end, + Err. + +-spec set_host_config([{atom(), term()}]) -> {ok, host_config()} | error_return(). +set_host_config(Opts) -> + Map1 = lists:foldl( + fun({Opt, Val}, M) when Opt /= host_config, + Opt /= append_host_config -> + maps:put({Opt, global}, Val, M); + (_, M) -> + M + end, #{}, Opts), + HostOpts = proplists:get_value(host_config, Opts, []), + AppendHostOpts = proplists:get_value(append_host_config, Opts, []), + Map2 = lists:foldl( + fun({Host, Opts1}, M1) -> + lists:foldl( + fun({Opt, Val}, M2) -> + maps:put({Opt, Host}, Val, M2) + end, M1, Opts1) + end, Map1, HostOpts), + Map3 = lists:foldl( + fun(_, {error, _} = Err) -> + Err; + ({Host, Opts1}, M1) -> + lists:foldl( + fun(_, {error, _} = Err) -> + Err; + ({Opt, L1}, M2) when is_list(L1) -> + L2 = try maps:get({Opt, Host}, M2) + catch _:{badkey, _} -> + maps:get({Opt, global}, M2, []) + end, + L3 = L2 ++ L1, + maps:put({Opt, Host}, L3, M2); + ({Opt, _}, _) -> + {error, {merge_conflict, Opt, Host}} + end, M1, Opts1) + end, Map2, AppendHostOpts), + case Map3 of + {error, _} -> Map3; + _ -> {ok, Map3} + end. + +-spec apply_defaults(ets:tid(), [binary()], host_config()) -> ok. +apply_defaults(Tab, Hosts, Map) -> + Defaults1 = defaults(), + apply_defaults(Tab, global, Map, Defaults1), + {_, Defaults2} = proplists:split(Defaults1, globals()), + lists:foreach( + fun(Host) -> + set_option(host, Host), + apply_defaults(Tab, Host, Map, Defaults2) + end, Hosts). + +-spec apply_defaults(ets:tid(), global | binary(), + host_config(), + [atom() | {atom(), term()}]) -> ok. +apply_defaults(Tab, Host, Map, Defaults) -> + lists:foreach( + fun({Opt, Default}) -> + try maps:get({Opt, Host}, Map) of + Val -> + ets:insert(Tab, {{Opt, Host}, Val}) + catch _:{badkey, _} when Host == global -> + Default1 = compute_default(Default, Host), + ets:insert(Tab, {{Opt, Host}, Default1}); + _:{badkey, _} -> + try maps:get({Opt, global}, Map) of + V -> ets:insert(Tab, {{Opt, Host}, V}) + catch _:{badkey, _} -> + Default1 = compute_default(Default, Host), + ets:insert(Tab, {{Opt, Host}, Default1}) + end + end; + (Opt) when Host == global -> + Val = maps:get({Opt, Host}, Map), + ets:insert(Tab, {{Opt, Host}, Val}); + (_) -> + ok + end, Defaults). + +-spec defaults() -> [atom() | {atom(), term()}]. +defaults() -> + lists:foldl( + fun(Mod, Acc) -> + lists:foldl( + fun({Opt, Val}, Acc1) -> + lists:keystore(Opt, 1, Acc1, {Opt, Val}); + (Opt, Acc1) -> + case lists:member(Opt, Acc1) of + true -> Acc1; + false -> [Opt|Acc1] + end + end, Acc, Mod:options()) + end, ejabberd_options:options(), callback_modules(external)). + +-spec globals() -> [atom()]. +globals() -> + lists:usort( + lists:flatmap( + fun(Mod) -> + case erlang:function_exported(Mod, globals, 0) of + true -> Mod:globals(); + false -> [] + end + end, callback_modules(all))). + +%% The module validator depends on virtual host, so we have to +%% validate modules in this separate function. +-spec validate_modules([binary()]) -> {ok, list()} | error_return(). +validate_modules(Hosts) -> + lists:foldl( + fun(Host, {ok, Acc}) -> + set_option(host, Host), + ModOpts = get_option({modules, Host}), + case gen_mod:validate(Host, ModOpts) of + {ok, ModOpts1} -> + {ok, [{{modules, Host}, ModOpts1}|Acc]}; + Err -> + Err + end; + (_, Err) -> + Err + end, {ok, []}, Hosts). + +-spec delete_host_options([binary()]) -> ok. +delete_host_options(Hosts) -> + lists:foreach( + fun(Host) -> + ets:match_delete(ejabberd_options, {{'_', Host}, '_'}) + end, Hosts). + +-spec compute_default(fun((global | binary()) -> T) | T, global | binary()) -> T. +compute_default(F, Host) when is_function(F, 1) -> + F(Host); +compute_default(Val, _) -> Val. -set_option(Opt, Val, State) -> - State#state{opts = [#local_config{key = Opt, value = Val} | - State#state.opts]}. - -append_option({Opt, Host}, Val, State) -> - GlobalVals = lists:flatmap( - fun(#local_config{key = {O, global}, value = V}) - when O == Opt -> - if is_list(V) -> V; - true -> [V] - end; - (_) -> - [] - end, State#state.opts), - NewVal = if is_list(Val) -> Val ++ GlobalVals; - true -> [Val|GlobalVals] - end, - set_option({Opt, Host}, NewVal, State). - -set_opts(State) -> - Opts = State#state.opts, - F = fun() -> - lists:foreach(fun(R) -> - mnesia:write(R) - end, Opts) - end, - case mnesia:transaction(F) of - {atomic, _} -> ok; - {aborted,{no_exists,Table}} -> - MnesiaDirectory = mnesia:system_info(directory), - ?ERROR_MSG("Error reading Mnesia database spool files:~n" - "The Mnesia database couldn't read the spool file for the table '~p'.~n" - "ejabberd needs read and write access in the directory:~n ~s~n" - "Maybe the problem is a change in the computer hostname,~n" - "or a change in the Erlang node name, which is currently:~n ~p~n" - "Check the ejabberd guide for details about changing the~n" - "computer hostname or Erlang node name.~n", - [Table, MnesiaDirectory, node()]), - exit("Error reading Mnesia database") - end. - -add_global_option(Opt, Val) -> - add_option(Opt, Val). - -add_local_option(Opt, Val) -> - add_option(Opt, Val). - -add_option(Opt, Val) when is_atom(Opt) -> - add_option({Opt, global}, Val); -add_option(Opt, Val) -> - mnesia:transaction(fun() -> - mnesia:write(#local_config{key = Opt, - value = Val}) - end). - --spec prepare_opt_val(any(), any(), check_fun(), any()) -> any(). - -prepare_opt_val(Opt, Val, F, Default) -> - Call = case F of - {Mod, Fun} -> - fun() -> Mod:Fun(Val) end; - _ -> - fun() -> F(Val) end - end, - try Call() of - Res -> - Res - catch {replace_with, NewRes} -> - NewRes; - {invalid_syntax, Error} -> - ?WARNING_MSG("incorrect value '~s' of option '~s', " - "using '~s' as fallback: ~s", - [format_term(Val), - format_term(Opt), - format_term(Default), - Error]), - Default; - _:_ -> - ?WARNING_MSG("incorrect value '~s' of option '~s', " - "using '~s' as fallback", - [format_term(Val), - format_term(Opt), - format_term(Default)]), - Default - end. - --type check_fun() :: fun((any()) -> any()) | {module(), atom()}. - --spec get_global_option(any(), check_fun()) -> any(). - -get_global_option(Opt, F) -> - get_option(Opt, F, undefined). - --spec get_global_option(any(), check_fun(), any()) -> any(). - -get_global_option(Opt, F, Default) -> - get_option(Opt, F, Default). - --spec get_local_option(any(), check_fun()) -> any(). - -get_local_option(Opt, F) -> - get_option(Opt, F, undefined). - --spec get_local_option(any(), check_fun(), any()) -> any(). - -get_local_option(Opt, F, Default) -> - get_option(Opt, F, Default). - --spec get_option(any(), check_fun()) -> any(). - -get_option(Opt, F) -> - get_option(Opt, F, undefined). - --spec get_option(any(), check_fun(), any()) -> any(). - -get_option(Opt, F, Default) when is_atom(Opt) -> - get_option({Opt, global}, F, Default); -get_option(Opt, F, Default) -> - case Opt of - {O, global} when is_atom(O) -> ok; - {O, H} when is_atom(O), is_binary(H) -> ok; - _ -> ?WARNING_MSG("Option ~p has invalid (outdated?) format. " - "This is likely a bug", [Opt]) - end, - case ets:lookup(local_config, Opt) of - [#local_config{value = Val}] -> - prepare_opt_val(Opt, Val, F, Default); - _ -> - case Opt of - {Key, Host} when Host /= global -> - get_option({Key, global}, F, Default); - _ -> - Default - end - end. - --spec has_option(atom() | {atom(), global | binary()}) -> any(). -has_option(Opt) -> - get_option(Opt, fun(_) -> true end, false). - -init_module_db_table(Modules) -> - catch ets:new(module_db, [named_table, public, bag]), - %% Dirty hack for mod_pubsub - ets:insert(module_db, {mod_pubsub, mnesia}), - ets:insert(module_db, {mod_pubsub, sql}), - lists:foreach( - fun(M) -> - case re:split(atom_to_list(M), "_", [{return, list}]) of - [_] -> - ok; - Parts -> - [Suffix|T] = lists:reverse(Parts), - BareMod = string:join(lists:reverse(T), "_"), - ets:insert(module_db, {list_to_atom(BareMod), - list_to_atom(Suffix)}) - end - end, Modules). - --spec v_db(module(), atom()) -> atom(). - -v_db(Mod, internal) -> v_db(Mod, mnesia); -v_db(Mod, odbc) -> v_db(Mod, sql); -v_db(Mod, Type) -> - case ets:match_object(module_db, {Mod, Type}) of - [_|_] -> Type; - [] -> erlang:error(badarg) - end. - --spec v_dbs(module()) -> [atom()]. - -v_dbs(Mod) -> - lists:flatten(ets:match(module_db, {Mod, '$1'})). - --spec v_dbs_mods(module()) -> [module()]. - -v_dbs_mods(Mod) -> - lists:map(fun([M]) -> - binary_to_atom(<<(atom_to_binary(Mod, utf8))/binary, "_", - (atom_to_binary(M, utf8))/binary>>, utf8) - end, ets:match(module_db, {Mod, '$1'})). - --spec default_db(binary(), module()) -> atom(). - -default_db(Host, Module) -> - case ejabberd_config:get_option( - {default_db, Host}, fun(T) when is_atom(T) -> T end) of - undefined -> - mnesia; - DBType -> - try - v_db(Module, DBType) - catch error:badarg -> - ?WARNING_MSG("Module '~s' doesn't support database '~s' " - "defined in option 'default_db', using " - "'mnesia' as fallback", [Module, DBType]), - mnesia - end - end. - -get_modules_with_options() -> - {ok, Mods} = application:get_key(ejabberd, modules), - ExtMods = [Name || {Name, _Details} <- ext_mod:installed()], - AllMods = [?MODULE|ExtMods++Mods], - init_module_db_table(AllMods), - lists:foldl( - fun(Mod, D) -> - case catch Mod:opt_type('') of - Opts when is_list(Opts) -> - lists:foldl( - fun(Opt, Acc) -> - dict:append(Opt, Mod, Acc) - end, D, Opts); - {'EXIT', {undef, _}} -> - D - end - end, dict:new(), AllMods). - -validate_opts(#state{opts = Opts} = State) -> - ModOpts = get_modules_with_options(), - NewOpts = lists:filtermap( - fun(#local_config{key = {Opt, _Host}, value = Val} = In) -> - case dict:find(Opt, ModOpts) of - {ok, [Mod|_]} -> - VFun = Mod:opt_type(Opt), - try VFun(Val) of - _ -> - true - catch {replace_with, NewVal} -> - {true, In#local_config{value = NewVal}}; - {invalid_syntax, Error} -> - ?ERROR_MSG("ignoring option '~s' with " - "invalid value: ~p: ~s", - [Opt, Val, Error]), - false; - _:_ -> - ?ERROR_MSG("ignoring option '~s' with " - "invalid value: ~p", - [Opt, Val]), - false - end; - _ -> - ?ERROR_MSG("unknown option '~s' will be likely" - " ignored", [Opt]), - true - end - end, Opts), - State#state{opts = NewOpts}. - --spec get_vh_by_auth_method(atom()) -> [binary()]. - -%% Return the list of hosts with a given auth method -get_vh_by_auth_method(AuthMethod) -> - Cfgs = mnesia:dirty_match_object(local_config, - #local_config{key = {auth_method, '_'}, - _ = '_'}), - lists:flatmap( - fun(#local_config{key = {auth_method, Host}, value = M}) -> - Methods = if not is_list(M) -> [M]; - true -> M - end, - case lists:member(AuthMethod, Methods) of - true when Host == global -> - get_myhosts(); - true -> - [Host]; - false -> - [] - end - end, Cfgs). - -%% @spec (Path::string()) -> true | false -is_file_readable(Path) -> - case file:read_file_info(Path) of - {ok, FileInfo} -> - case {FileInfo#file_info.type, FileInfo#file_info.access} of - {regular, read} -> true; - {regular, read_write} -> true; - _ -> false - end; - {error, _Reason} -> - false - end. - -get_version() -> - case application:get_key(ejabberd, vsn) of - undefined -> ""; - {ok, Vsn} -> list_to_binary(Vsn) - end. - --spec get_myhosts() -> [binary()]. - -get_myhosts() -> - get_option(hosts, fun(V) -> V end). - --spec get_mylang() -> binary(). - -get_mylang() -> - get_option( - language, - fun iolist_to_binary/1, - <<"en">>). - -replace_module(mod_announce_odbc) -> {mod_announce, sql}; -replace_module(mod_blocking_odbc) -> {mod_blocking, sql}; -replace_module(mod_caps_odbc) -> {mod_caps, sql}; -replace_module(mod_irc_odbc) -> {mod_irc, sql}; -replace_module(mod_last_odbc) -> {mod_last, sql}; -replace_module(mod_muc_odbc) -> {mod_muc, sql}; -replace_module(mod_offline_odbc) -> {mod_offline, sql}; -replace_module(mod_privacy_odbc) -> {mod_privacy, sql}; -replace_module(mod_private_odbc) -> {mod_private, sql}; -replace_module(mod_roster_odbc) -> {mod_roster, sql}; -replace_module(mod_shared_roster_odbc) -> {mod_shared_roster, sql}; -replace_module(mod_vcard_odbc) -> {mod_vcard, sql}; -replace_module(mod_vcard_xupdate_odbc) -> {mod_vcard_xupdate, sql}; -replace_module(mod_pubsub_odbc) -> {mod_pubsub, sql}; -replace_module(Module) -> - case is_elixir_module(Module) of - true -> expand_elixir_module(Module); - false -> Module - end. - -replace_modules(Modules) -> - lists:map( - fun({Module, Opts}) -> - case replace_module(Module) of - {NewModule, DBType} -> - emit_deprecation_warning(Module, NewModule, DBType), - NewOpts = [{db_type, DBType} | - lists:keydelete(db_type, 1, Opts)], - {NewModule, transform_module_options(Module, NewOpts)}; - NewModule -> - if Module /= NewModule -> - emit_deprecation_warning(Module, NewModule); - true -> - ok - end, - {NewModule, transform_module_options(Module, Opts)} - end - end, Modules). - -%% Elixir module naming -%% ==================== - --ifdef(ELIXIR_ENABLED). -is_elixir_enabled() -> - true. --else. -is_elixir_enabled() -> - false. --endif. - -is_using_elixir_config() -> - case is_elixir_enabled() of - true -> - Config = get_ejabberd_config_path(), - 'Elixir.Ejabberd.ConfigUtil':is_elixir_config(Config); - false -> - false - end. - -%% If module name start with uppercase letter, this is an Elixir module: -is_elixir_module(Module) -> - case atom_to_list(Module) of - [H|_] when H >= 65, H =< 90 -> true; - _ ->false - end. - -%% We assume we know this is an elixir module -expand_elixir_module(Module) -> - case atom_to_list(Module) of - %% Module name already specified as an Elixir from Erlang module name - "Elixir." ++ _ -> Module; - %% if start with uppercase letter, this is an Elixir module: Append 'Elixir.' to module name. - ModuleString -> - list_to_atom("Elixir." ++ ModuleString) - end. - -strings_to_binary([]) -> - []; -strings_to_binary(L) when is_list(L) -> - case is_string(L) of - true -> - list_to_binary(L); - false -> - strings_to_binary1(L) - end; -strings_to_binary({A, B, C, D}) when - is_integer(A), is_integer(B), is_integer(C), is_integer(D) -> - {A, B, C ,D}; -strings_to_binary(T) when is_tuple(T) -> - list_to_tuple(strings_to_binary1(tuple_to_list(T))); -strings_to_binary(X) -> - X. - -strings_to_binary1([El|L]) -> - [strings_to_binary(El)|strings_to_binary1(L)]; -strings_to_binary1([]) -> - []; -strings_to_binary1(T) -> - T. - -is_string([C|T]) when (C >= 0) and (C =< 255) -> - is_string(T); -is_string([]) -> - true; -is_string(_) -> - false. - -binary_to_strings(B) when is_binary(B) -> - binary_to_list(B); -binary_to_strings([H|T]) -> - [binary_to_strings(H)|binary_to_strings(T)]; -binary_to_strings(T) when is_tuple(T) -> - list_to_tuple(binary_to_strings(tuple_to_list(T))); -binary_to_strings(T) -> - T. - -format_term(Bin) when is_binary(Bin) -> - io_lib:format("\"~s\"", [Bin]); -format_term(S) when is_list(S), S /= [] -> - case lists:all(fun(C) -> (C>=0) and (C=<255) end, S) of - true -> - io_lib:format("\"~s\"", [S]); - false -> - io_lib:format("~p", [binary_to_strings(S)]) - end; -format_term(T) -> - io_lib:format("~p", [binary_to_strings(T)]). - -transform_terms(Terms) -> - %% We could check all ejabberd beams, but this - %% slows down start-up procedure :( - Mods = [mod_register, - mod_last, - ejabberd_s2s, - ejabberd_listener, - ejabberd_sql_sup, - shaper, - ejabberd_s2s_out, - acl, - ejabberd_config], - collect_options(transform_terms(Mods, Terms)). - -transform_terms([Mod|Mods], Terms) -> - case catch Mod:transform_options(Terms) of - {'EXIT', _} = Err -> - ?ERROR_MSG("Failed to transform terms by ~p: ~p", [Mod, Err]), - transform_terms(Mods, Terms); - NewTerms -> - transform_terms(Mods, NewTerms) - end; -transform_terms([], NewTerms) -> - NewTerms. - -transform_module_options(Module, Opts) -> - Opts1 = gen_iq_handler:transform_module_options(Opts), - try - Module:transform_module_options(Opts1) - catch error:undef -> - Opts1 - end. - -compact(Cfg) -> - Opts = [{K, V} || #local_config{key = K, value = V} <- Cfg], - {GOpts, HOpts} = split_by_hosts(Opts), - [#local_config{key = {O, global}, value = V} || {O, V} <- GOpts] ++ - lists:flatmap( - fun({Host, OptVal}) -> - case lists:member(OptVal, GOpts) of - true -> - []; - false -> - [#local_config{key = {Opt, Host}, value = Val} - || {Opt, Val} <- OptVal] - end - end, lists:flatten(HOpts)). - -split_by_hosts(Opts) -> - Opts1 = orddict:to_list( - lists:foldl( - fun({{Opt, Host}, Val}, D) -> - orddict:append(Host, {Opt, Val}, D) - end, orddict:new(), Opts)), - case lists:keytake(global, 1, Opts1) of - {value, {global, GlobalOpts}, HostOpts} -> - {GlobalOpts, HostOpts}; - _ -> - {[], Opts1} - end. - -collect_options(Opts) -> - {D, InvalidOpts} = - lists:foldl( - fun({K, V}, {D, Os}) when is_list(V) -> - {orddict:append_list(K, V, D), Os}; - ({K, V}, {D, Os}) -> - {orddict:store(K, V, D), Os}; - (Opt, {D, Os}) -> - {D, [Opt|Os]} - end, {orddict:new(), []}, Opts), - InvalidOpts ++ orddict:to_list(D). - -transform_options(Opts) -> - Opts1 = lists:foldl(fun transform_options/2, [], Opts), - {HOpts, Opts2} = lists:mapfoldl( - fun({host_config, O}, Os) -> - {[O], Os}; - (O, Os) -> - {[], [O|Os]} - end, [], Opts1), - {AHOpts, Opts3} = lists:mapfoldl( - fun({append_host_config, O}, Os) -> - {[O], Os}; - (O, Os) -> - {[], [O|Os]} - end, [], Opts2), - HOpts1 = case collect_options(lists:flatten(HOpts)) of - [] -> - []; - HOs -> - [{host_config, - [{H, transform_terms(O)} || {H, O} <- HOs]}] - end, - AHOpts1 = case collect_options(lists:flatten(AHOpts)) of - [] -> - []; - AHOs -> - [{append_host_config, - [{H, transform_terms(O)} || {H, O} <- AHOs]}] - end, - HOpts1 ++ AHOpts1 ++ Opts3. - -transform_options({domain_certfile, Domain, CertFile}, Opts) -> - ?WARNING_MSG("Option 'domain_certfile' now should be defined " - "per virtual host or globally. The old format is " - "still supported but it is better to fix your config", []), - [{host_config, [{Domain, [{domain_certfile, CertFile}]}]}|Opts]; -transform_options(Opt, Opts) when Opt == override_global; - Opt == override_local; - Opt == override_acls -> - ?WARNING_MSG("Ignoring '~s' option which has no effect anymore", [Opt]), - Opts; -transform_options({host_config, Host, HOpts}, Opts) -> - {AddOpts, HOpts1} = - lists:mapfoldl( - fun({{add, Opt}, Val}, Os) -> - ?WARNING_MSG("Option 'add' is deprecated. " - "The option is still supported " - "but it is better to fix your config: " - "use 'append_host_config' instead.", []), - {[{Opt, Val}], Os}; - (O, Os) -> - {[], [O|Os]} - end, [], HOpts), - [{append_host_config, [{Host, lists:flatten(AddOpts)}]}, - {host_config, [{Host, HOpts1}]}|Opts]; -transform_options({define_macro, Macro, Val}, Opts) -> - [{define_macro, [{Macro, Val}]}|Opts]; -transform_options({include_config_file, _} = Opt, Opts) -> - [{include_config_file, [transform_include_option(Opt)]} | Opts]; -transform_options({include_config_file, _, _} = Opt, Opts) -> - [{include_config_file, [transform_include_option(Opt)]} | Opts]; -transform_options(Opt, Opts) -> - [Opt|Opts]. - --spec convert_table_to_binary(atom(), [atom()], atom(), - fun(), fun()) -> ok. - -convert_table_to_binary(Tab, Fields, Type, DetectFun, ConvertFun) -> - case is_table_still_list(Tab, DetectFun) of - true -> - ?INFO_MSG("Converting '~s' table from strings to binaries.", [Tab]), - TmpTab = list_to_atom(atom_to_list(Tab) ++ "_tmp_table"), - catch mnesia:delete_table(TmpTab), - case mnesia:create_table(TmpTab, - [{disc_only_copies, [node()]}, - {type, Type}, - {local_content, true}, - {record_name, Tab}, - {attributes, Fields}]) of - {atomic, ok} -> - mnesia:transform_table(Tab, ignore, Fields), - case mnesia:transaction( - fun() -> - mnesia:write_lock_table(TmpTab), - mnesia:foldl( - fun(R, _) -> - NewR = ConvertFun(R), - mnesia:dirty_write(TmpTab, NewR) - end, ok, Tab) - end) of - {atomic, ok} -> - mnesia:clear_table(Tab), - case mnesia:transaction( - fun() -> - mnesia:write_lock_table(Tab), - mnesia:foldl( - fun(R, _) -> - mnesia:dirty_write(R) - end, ok, TmpTab) - end) of - {atomic, ok} -> - mnesia:delete_table(TmpTab); - Err -> - report_and_stop(Tab, Err) - end; - Err -> - report_and_stop(Tab, Err) - end; - Err -> - report_and_stop(Tab, Err) - end; - false -> - ok - end. - -is_table_still_list(Tab, DetectFun) -> - is_table_still_list(Tab, DetectFun, mnesia:dirty_first(Tab)). - -is_table_still_list(_Tab, _DetectFun, '$end_of_table') -> - false; -is_table_still_list(Tab, DetectFun, Key) -> - Rs = mnesia:dirty_read(Tab, Key), - Res = lists:foldl(fun(_, true) -> - true; - (_, false) -> - false; - (R, _) -> - case DetectFun(R) of - '$next' -> - '$next'; - El -> - is_list(El) - end - end, '$next', Rs), - case Res of - true -> - true; - false -> - false; - '$next' -> - is_table_still_list(Tab, DetectFun, mnesia:dirty_next(Tab, Key)) - end. - -report_and_stop(Tab, Err) -> - ErrTxt = lists:flatten( - io_lib:format( - "Failed to convert '~s' table to binary: ~p", - [Tab, Err])), - ?CRITICAL_MSG(ErrTxt, []), - timer:sleep(1000), - halt(string:substr(ErrTxt, 1, 199)). - -emit_deprecation_warning(Module, NewModule, DBType) -> - ?WARNING_MSG("Module ~s is deprecated, use ~s with 'db_type: ~s'" - " instead", [Module, NewModule, DBType]). - -emit_deprecation_warning(Module, NewModule) -> - case is_elixir_module(NewModule) of - %% Do not emit deprecation warning for Elixir - true -> ok; - false -> - ?WARNING_MSG("Module ~s is deprecated, use ~s instead", - [Module, NewModule]) - end. - -opt_type(hide_sensitive_log_data) -> - fun (H) when is_boolean(H) -> H end; -opt_type(hosts) -> - fun(L) when is_list(L) -> - lists:map( - fun(H) -> - iolist_to_binary(H) - end, L) - end; -opt_type(language) -> - fun iolist_to_binary/1; -opt_type(_) -> - [hide_sensitive_log_data, hosts, language]. - --spec may_hide_data(string()) -> string(); - (binary()) -> binary(). - -may_hide_data(Data) -> - case ejabberd_config:get_option( - hide_sensitive_log_data, - fun(false) -> false; - (true) -> true - end, - false) of - false -> - Data; - true -> - "hidden_by_ejabberd" - end. +-spec set_fqdn() -> ok. +set_fqdn() -> + FQDNs = get_option(fqdn), + xmpp:set_config([{fqdn, FQDNs}]). + +-spec set_shared_key() -> ok. +set_shared_key() -> + Key = case erlang:get_cookie() of + nocookie -> + str:sha(p1_rand:get_string()); + Cookie -> + str:sha(erlang:atom_to_binary(Cookie, latin1)) + end, + set_option(shared_key, Key). + +-spec set_node_start(integer()) -> ok. +set_node_start(UnixTime) -> + set_option(node_start, UnixTime). + +-spec set_loglevel(logger:level()) -> ok. +set_loglevel(Level) -> + ejabberd_logger:set(Level). diff --git a/src/ejabberd_config_transformer.erl b/src/ejabberd_config_transformer.erl new file mode 100644 index 000000000..1aed7c6a8 --- /dev/null +++ b/src/ejabberd_config_transformer.erl @@ -0,0 +1,644 @@ +%%%---------------------------------------------------------------------- +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- +-module(ejabberd_config_transformer). + +%% API +-export([map_reduce/1]). + +-include("logger.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +map_reduce(Y) -> + F = + fun(Y1) -> + Y2 = (validator())(Y1), + Y3 = transform(Y2), + case application:get_env(ejabberd, custom_config_transformer) of + {ok, TransMod} when is_atom(TransMod) -> + TransMod:transform(Y3); + _ -> + Y3 + end + end, + econf:validate(F, Y). + +%%%=================================================================== +%%% Transformer +%%%=================================================================== +transform(Y) -> + {Y1, Acc1} = transform(global, Y, #{}), + {Y2, Acc2} = update(Y1, Acc1), + filter(global, Y2, Acc2). + +transform(Host, Y, Acc) -> + filtermapfoldr( + fun({Opt, HostOpts}, Acc1) when (Opt == host_config orelse + Opt == append_host_config) + andalso Host == global -> + case filtermapfoldr( + fun({Host1, Opts}, Acc2) -> + case transform(Host1, Opts, Acc2) of + {[], Acc3} -> + {false, Acc3}; + {Opts1, Acc3} -> + {{true, {Host1, Opts1}}, Acc3} + end + end, Acc1, HostOpts) of + {[], Acc4} -> + {false, Acc4}; + {HostOpts1, Acc4} -> + {{true, {Opt, HostOpts1}}, Acc4} + end; + ({Opt, Val}, Acc1) -> + transform(Host, Opt, Val, Acc1) + end, Acc, Y). + +transform(Host, modules, ModOpts, Acc) -> + {ModOpts1, Acc2} = + lists:mapfoldr( + fun({Mod, Opts}, Acc1) -> + Opts1 = transform_module_options(Opts), + transform_module(Host, Mod, Opts1, Acc1) + end, Acc, ModOpts), + {{true, {modules, ModOpts1}}, Acc2}; +transform(global, listen, Listeners, Acc) -> + {Listeners1, Acc2} = + lists:mapfoldr( + fun(Opts, Acc1) -> + transform_listener(Opts, Acc1) + end, Acc, Listeners), + {{true, {listen, Listeners1}}, Acc2}; +transform(_Host, Opt, CertFile, Acc) when (Opt == domain_certfile) orelse + (Opt == c2s_certfile) orelse + (Opt == s2s_certfile) -> + ?WARNING_MSG("Option '~ts' is deprecated and was automatically " + "appended to 'certfiles' option. ~ts", + [Opt, adjust_hint()]), + CertFiles = maps:get(certfiles, Acc, []), + Acc1 = maps:put(certfiles, CertFiles ++ [CertFile], Acc), + {false, Acc1}; +transform(_Host, certfiles, CertFiles1, Acc) -> + CertFiles2 = maps:get(certfiles, Acc, []), + Acc1 = maps:put(certfiles, CertFiles1 ++ CertFiles2, Acc), + {true, Acc1}; +transform(_Host, acme, ACME, Acc) -> + ACME1 = lists:map( + fun({ca_url, URL} = Opt) -> + case misc:uri_parse(URL) of + {ok, _, _, "acme-v01.api.letsencrypt.org", _, _, _} -> + NewURL = ejabberd_acme:default_directory_url(), + ?WARNING_MSG("ACME directory URL ~ts defined in " + "option acme->ca_url is deprecated " + "and was automatically replaced " + "with ~ts. ~ts", + [URL, NewURL, adjust_hint()]), + {ca_url, NewURL}; + _ -> + Opt + end; + (Opt) -> + Opt + end, ACME), + {{true, {acme, ACME1}}, Acc}; +transform(Host, s2s_use_starttls, required_trusted, Acc) -> + ?WARNING_MSG("The value 'required_trusted' of option " + "'s2s_use_starttls' is deprecated and was " + "automatically replaced with value 'required'. " + "The module 'mod_s2s_dialback' has also " + "been automatically removed from the configuration. ~ts", + [adjust_hint()]), + Hosts = maps:get(remove_s2s_dialback, Acc, []), + Acc1 = maps:put(remove_s2s_dialback, [Host|Hosts], Acc), + {{true, {s2s_use_starttls, required}}, Acc1}; +transform(Host, define_macro, Macro, Acc) when is_binary(Host) -> + ?WARNING_MSG("The option 'define_macro' is not supported inside 'host_config'. " + "Consequently those macro definitions for host '~ts' are unused: ~ts", + [Host, io_lib:format("~p", [Macro])]), + {true, Acc}; +transform(_Host, _Opt, _Val, Acc) -> + {true, Acc}. + +update(Y, Acc) -> + set_certfiles(Y, Acc). + +filter(Host, Y, Acc) -> + lists:filtermap( + fun({Opt, HostOpts}) when (Opt == host_config orelse + Opt == append_host_config) + andalso Host == global -> + HostOpts1 = lists:map( + fun({Host1, Opts1}) -> + {Host1, filter(Host1, Opts1, Acc)} + end, HostOpts), + {true, {Opt, HostOpts1}}; + ({Opt, Val}) -> + filter(Host, Opt, Val, Acc) + end, Y). + +filter(_Host, log_rotate_date, _, _) -> + warn_removed_option(log_rotate_date), + false; +filter(_Host, log_rate_limit, _, _) -> + warn_removed_option(log_rate_limit), + false; +filter(_Host, ca_path, _, _) -> + warn_removed_option(ca_path, ca_file), + false; +filter(_Host, iqdisc, _, _) -> + warn_removed_option(iqdisc), + false; +filter(_Host, access, _, _) -> + warn_removed_option(access, access_rules), + false; +filter(_Host, commands, _, _) -> + warn_removed_option(commands, api_permissions), + false; +filter(_Host, ejabberdctl_access_commands, _, _) -> + warn_removed_option(ejabberdctl_access_commands, api_permissions), + false; +filter(_Host, commands_admin_access, _, _) -> + warn_removed_option(commands_admin_access, api_permissions), + false; +filter(_Host, ldap_group_cache_size, _, _) -> + warn_removed_option(ldap_group_cache_size, cache_size), + false; +filter(_Host, ldap_user_cache_size, _, _) -> + warn_removed_option(ldap_user_cache_size, cache_size), + false; +filter(_Host, ldap_group_cache_validity, _, _) -> + warn_removed_option(ldap_group_cache_validity, cache_life_time), + false; +filter(_Host, ldap_user_cache_validity, _, _) -> + warn_removed_option(ldap_user_cache_validity, cache_life_time), + false; +filter(_Host, ldap_local_filter, _, _) -> + warn_removed_option(ldap_local_filter), + false; +filter(_Host, deref_aliases, Val, _) -> + warn_replaced_option(deref_aliases, ldap_deref_aliases), + {true, {ldap_deref_aliases, Val}}; +filter(_Host, default_db, internal, _) -> + {true, {default_db, mnesia}}; +filter(_Host, default_db, odbc, _) -> + {true, {default_db, sql}}; +filter(_Host, auth_method, Ms, _) -> + Ms1 = lists:map( + fun(internal) -> mnesia; + (odbc) -> sql; + (M) -> M + end, Ms), + {true, {auth_method, Ms1}}; +filter(_Host, default_ram_db, internal, _) -> + {true, {default_ram_db, mnesia}}; +filter(_Host, default_ram_db, odbc, _) -> + {true, {default_ram_db, sql}}; +filter(_Host, extauth_cache, _, _) -> + ?WARNING_MSG("Option 'extauth_cache' is deprecated " + "and has no effect, use authentication " + "or global cache configuration options: " + "auth_use_cache, auth_cache_life_time, " + "use_cache, cache_life_time, and so on", []), + false; +filter(_Host, extauth_instances, Val, _) -> + warn_replaced_option(extauth_instances, extauth_pool_size), + {true, {extauth_pool_size, Val}}; +filter(_Host, Opt, Val, _) when Opt == outgoing_s2s_timeout; + Opt == s2s_dns_timeout -> + warn_huge_timeout(Opt, Val), + true; +filter(_Host, captcha_host, _, _) -> + warn_deprecated_option(captcha_host, captcha_url), + true; +filter(_Host, route_subdomains, _, _) -> + warn_removed_option(route_subdomains, s2s_access), + false; +filter(_Host, auth_password_types_hidden_in_scram1, Val, _) -> + {true, {auth_password_types_hidden_in_sasl1, Val}}; +filter(Host, modules, ModOpts, State) -> + NoDialbackHosts = maps:get(remove_s2s_dialback, State, []), + ModOpts1 = lists:filter( + fun({mod_s2s_dialback, _}) -> + not lists:member(Host, NoDialbackHosts); + ({mod_echo, _}) -> + warn_removed_module(mod_echo), + false; + (_) -> + true + end, ModOpts), + {true, {modules, ModOpts1}}; +filter(_, _, _, _) -> + true. + +%%%=================================================================== +%%% Listener transformers +%%%=================================================================== +transform_listener(Opts, Acc) -> + Opts1 = transform_request_handlers(Opts), + Opts2 = transform_turn_ip(Opts1), + Opts3 = remove_inet_options(Opts2), + collect_listener_certfiles(Opts3, Acc). + +transform_request_handlers(Opts) -> + case lists:keyfind(module, 1, Opts) of + {_, ejabberd_http} -> + replace_request_handlers(Opts); + {_, ejabberd_xmlrpc} -> + remove_xmlrpc_access_commands(Opts); + _ -> + Opts + end. + +transform_turn_ip(Opts) -> + case lists:keyfind(module, 1, Opts) of + {_, ejabberd_stun} -> + replace_turn_ip(Opts); + _ -> + Opts + end. + +replace_request_handlers(Opts) -> + Handlers = proplists:get_value(request_handlers, Opts, []), + Handlers1 = + lists:foldl( + fun({captcha, IsEnabled}, Acc) -> + Handler = {<<"/captcha">>, ejabberd_captcha}, + warn_replaced_handler(captcha, Handler, IsEnabled), + [Handler|Acc]; + ({register, IsEnabled}, Acc) -> + Handler = {<<"/register">>, mod_register_web}, + warn_replaced_handler(register, Handler, IsEnabled), + [Handler|Acc]; + ({web_admin, IsEnabled}, Acc) -> + Handler = {<<"/admin">>, ejabberd_web_admin}, + warn_replaced_handler(web_admin, Handler, IsEnabled), + [Handler|Acc]; + ({http_bind, IsEnabled}, Acc) -> + Handler = {<<"/bosh">>, mod_bosh}, + warn_replaced_handler(http_bind, Handler, IsEnabled), + [Handler|Acc]; + ({xmlrpc, IsEnabled}, Acc) -> + Handler = {<<"/">>, ejabberd_xmlrpc}, + warn_replaced_handler(xmlrpc, Handler, IsEnabled), + Acc ++ [Handler]; + (_, Acc) -> + Acc + end, Handlers, Opts), + Handlers2 = lists:map( + fun({Path, mod_http_bind}) -> + warn_replaced_module(mod_http_bind, mod_bosh), + {Path, mod_bosh}; + (PathMod) -> + PathMod + end, Handlers1), + Opts1 = lists:filtermap( + fun({captcha, _}) -> false; + ({register, _}) -> false; + ({web_admin, _}) -> false; + ({http_bind, _}) -> false; + ({xmlrpc, _}) -> false; + ({http_poll, _}) -> + ?WARNING_MSG("Listening option 'http_poll' is " + "ignored: HTTP Polling support was " + "removed in ejabberd 15.04. ~ts", + [adjust_hint()]), + false; + ({request_handlers, _}) -> + false; + (_) -> true + end, Opts), + case Handlers2 of + [] -> Opts1; + _ -> [{request_handlers, Handlers2}|Opts1] + end. + +remove_xmlrpc_access_commands(Opts) -> + lists:filter( + fun({access_commands, _}) -> + warn_removed_option(access_commands, api_permissions), + false; + (_) -> + true + end, Opts). + +replace_turn_ip(Opts) -> + lists:filtermap( + fun({turn_ip, Val}) -> + warn_replaced_option(turn_ip, turn_ipv4_address), + {true, {turn_ipv4_address, Val}}; + (_) -> + true + end, Opts). + +remove_inet_options(Opts) -> + lists:filter( + fun({Opt, _}) when Opt == inet; Opt == inet6 -> + warn_removed_option(Opt, ip), + false; + (_) -> + true + end, Opts). + +collect_listener_certfiles(Opts, Acc) -> + Mod = proplists:get_value(module, Opts), + if Mod == ejabberd_http; + Mod == ejabberd_c2s; + Mod == ejabberd_s2s_in -> + case lists:keyfind(certfile, 1, Opts) of + {_, CertFile} -> + ?WARNING_MSG("Listening option 'certfile' of module ~ts " + "is deprecated and was automatically " + "appended to global 'certfiles' option. ~ts", + [Mod, adjust_hint()]), + CertFiles = maps:get(certfiles, Acc, []), + {proplists:delete(certfile, Opts), + maps:put(certfiles, [CertFile|CertFiles], Acc)}; + false -> + {Opts, Acc} + end; + true -> + {Opts, Acc} + end. + +%%%=================================================================== +%%% Module transformers +%%% NOTE: transform_module_options/1 is called before transform_module/4 +%%%=================================================================== +transform_module_options(Opts) -> + lists:filtermap( + fun({Opt, internal}) when Opt == db_type; + Opt == ram_db_type -> + {true, {Opt, mnesia}}; + ({Opt, odbc}) when Opt == db_type; + Opt == ram_db_type -> + {true, {Opt, sql}}; + ({deref_aliases, Val}) -> + warn_replaced_option(deref_aliases, ldap_deref_aliases), + {true, {ldap_deref_aliases, Val}}; + ({ldap_group_cache_size, _}) -> + warn_removed_option(ldap_group_cache_size, cache_size), + false; + ({ldap_user_cache_size, _}) -> + warn_removed_option(ldap_user_cache_size, cache_size), + false; + ({ldap_group_cache_validity, _}) -> + warn_removed_option(ldap_group_cache_validity, cache_life_time), + false; + ({ldap_user_cache_validity, _}) -> + warn_removed_option(ldap_user_cache_validity, cache_life_time), + false; + ({iqdisc, _}) -> + warn_removed_option(iqdisc), + false; + (_) -> + true + end, Opts). + +transform_module(Host, mod_http_bind, Opts, Acc) -> + warn_replaced_module(mod_http_bind, mod_bosh), + transform_module(Host, mod_bosh, Opts, Acc); +transform_module(Host, mod_vcard_xupdate_odbc, Opts, Acc) -> + warn_replaced_module(mod_vcard_xupdate_odbc, mod_vcard_xupdate), + transform_module(Host, mod_vcard_xupdate, Opts, Acc); +transform_module(Host, mod_vcard_ldap, Opts, Acc) -> + warn_replaced_module(mod_vcard_ldap, mod_vcard, ldap), + transform_module(Host, mod_vcard, [{db_type, ldap}|Opts], Acc); +transform_module(Host, M, Opts, Acc) when (M == mod_announce_odbc orelse + M == mod_blocking_odbc orelse + M == mod_caps_odbc orelse + M == mod_last_odbc orelse + M == mod_muc_odbc orelse + M == mod_offline_odbc orelse + M == mod_privacy_odbc orelse + M == mod_private_odbc orelse + M == mod_pubsub_odbc orelse + M == mod_roster_odbc orelse + M == mod_shared_roster_odbc orelse + M == mod_vcard_odbc) -> + M1 = strip_odbc_suffix(M), + warn_replaced_module(M, M1, sql), + transform_module(Host, M1, [{db_type, sql}|Opts], Acc); +transform_module(_Host, mod_blocking, Opts, Acc) -> + Opts1 = lists:filter( + fun({db_type, _}) -> + warn_removed_module_option(db_type, mod_blocking), + false; + (_) -> + true + end, Opts), + {{mod_blocking, Opts1}, Acc}; +transform_module(_Host, mod_carboncopy, Opts, Acc) -> + Opts1 = lists:filter( + fun({Opt, _}) when Opt == ram_db_type; + Opt == use_cache; + Opt == cache_size; + Opt == cache_missed; + Opt == cache_life_time -> + warn_removed_module_option(Opt, mod_carboncopy), + false; + (_) -> + true + end, Opts), + {{mod_carboncopy, Opts1}, Acc}; +transform_module(_Host, mod_http_api, Opts, Acc) -> + Opts1 = lists:filter( + fun({admin_ip_access, _}) -> + warn_removed_option(admin_ip_access, api_permissions), + false; + (_) -> + true + end, Opts), + {{mod_http_api, Opts1}, Acc}; +transform_module(_Host, mod_http_upload, Opts, Acc) -> + Opts1 = lists:filter( + fun({service_url, _}) -> + warn_deprecated_option(service_url, external_secret), + true; + (_) -> + true + end, Opts), + {{mod_http_upload, Opts1}, Acc}; +transform_module(_Host, mod_pubsub, Opts, Acc) -> + Opts1 = lists:map( + fun({plugins, Plugins}) -> + {plugins, + lists:filter( + fun(Plugin) -> + case lists:member( + Plugin, + [<<"buddy">>, <<"club">>, <<"dag">>, + <<"dispatch">>, <<"hometree">>, <<"mb">>, + <<"mix">>, <<"online">>, <<"private">>, + <<"public">>]) of + true -> + ?WARNING_MSG( + "Plugin '~ts' of mod_pubsub is not " + "supported anymore and has been " + "automatically removed from 'plugins' " + "option. ~ts", + [Plugin, adjust_hint()]), + false; + false -> + true + end + end, Plugins)}; + (Opt) -> + Opt + end, Opts), + {{mod_pubsub, Opts1}, Acc}; +transform_module(_Host, Mod, Opts, Acc) -> + {{Mod, Opts}, Acc}. + +strip_odbc_suffix(M) -> + [_|T] = lists:reverse(string:tokens(atom_to_list(M), "_")), + list_to_atom(string:join(lists:reverse(T), "_")). + +%%%=================================================================== +%%% Aux +%%%=================================================================== +filtermapfoldr(Fun, Init, List) -> + lists:foldr( + fun(X, {Ret, Acc}) -> + case Fun(X, Acc) of + {true, Acc1} -> {[X|Ret], Acc1}; + {{true, X1}, Acc1} -> {[X1|Ret], Acc1}; + {false, Acc1} -> {Ret, Acc1} + end + end, {[], Init}, List). + +set_certfiles(Y, #{certfiles := CertFiles} = Acc) -> + {lists:keystore(certfiles, 1, Y, {certfiles, CertFiles}), Acc}; +set_certfiles(Y, Acc) -> + {Y, Acc}. + +%%%=================================================================== +%%% Warnings +%%%=================================================================== +warn_replaced_module(From, To) -> + ?WARNING_MSG("Module ~ts is deprecated and was automatically " + "replaced by ~ts. ~ts", + [From, To, adjust_hint()]). + +warn_replaced_module(From, To, Type) -> + ?WARNING_MSG("Module ~ts is deprecated and was automatically " + "replaced by ~ts with db_type: ~ts. ~ts", + [From, To, Type, adjust_hint()]). + +warn_removed_module(Mod) -> + ?WARNING_MSG("Module ~ts is deprecated and was automatically " + "removed from the configuration. ~ts", [Mod, adjust_hint()]). + +warn_replaced_handler(Opt, {Path, Module}, false) -> + ?WARNING_MSG("Listening option '~ts' is deprecated, " + "please use instead the " + "HTTP request handler: \"~ts\" -> ~ts. ~ts", + [Opt, Path, Module, adjust_hint()]); +warn_replaced_handler(Opt, {Path, Module}, true) -> + ?WARNING_MSG("Listening option '~ts' is deprecated " + "and was automatically replaced by " + "HTTP request handler: \"~ts\" -> ~ts. ~ts", + [Opt, Path, Module, adjust_hint()]). + +warn_deprecated_option(OldOpt, NewOpt) -> + ?WARNING_MSG("Option '~ts' is deprecated. Use option '~ts' instead.", + [OldOpt, NewOpt]). + +warn_replaced_option(OldOpt, NewOpt) -> + ?WARNING_MSG("Option '~ts' is deprecated and was automatically " + "replaced by '~ts'. ~ts", + [OldOpt, NewOpt, adjust_hint()]). + +warn_removed_option(Opt) -> + ?WARNING_MSG("Option '~ts' is deprecated and has no effect anymore. " + "Please remove it from the configuration.", [Opt]). + +warn_removed_option(OldOpt, NewOpt) -> + ?WARNING_MSG("Option '~ts' is deprecated and has no effect anymore. " + "Use option '~ts' instead.", [OldOpt, NewOpt]). + +warn_removed_module_option(Opt, Mod) -> + ?WARNING_MSG("Option '~ts' of module ~ts is deprecated " + "and has no effect anymore. ~ts", + [Opt, Mod, adjust_hint()]). + +warn_huge_timeout(Opt, T) when is_integer(T), T >= 1000 -> + ?WARNING_MSG("Value '~B' of option '~ts' is too big, " + "are you sure you have set seconds?", + [T, Opt]); +warn_huge_timeout(_, _) -> + ok. + +adjust_hint() -> + "Please adjust your configuration file accordingly. " + "Hint: run `ejabberdctl dump-config` command to view current " + "configuration as it is seen by ejabberd.". + +%%%=================================================================== +%%% Very raw validator: just to make sure we get properly typed terms +%%% Expand it if you need to transform more options, but don't +%%% abuse complex types: simple and composite types are preferred +%%%=================================================================== +validator() -> + Validators = + #{s2s_use_starttls => econf:atom(), + certfiles => econf:list(econf:any()), + c2s_certfile => econf:binary(), + s2s_certfile => econf:binary(), + domain_certfile => econf:binary(), + default_db => econf:atom(), + default_ram_db => econf:atom(), + auth_method => econf:list_or_single(econf:atom()), + acme => econf:options( + #{ca_url => econf:binary(), + '_' => econf:any()}, + [unique]), + listen => + econf:list( + econf:options( + #{captcha => econf:bool(), + register => econf:bool(), + web_admin => econf:bool(), + http_bind => econf:bool(), + http_poll => econf:bool(), + xmlrpc => econf:bool(), + module => econf:atom(), + certfile => econf:binary(), + request_handlers => + econf:map(econf:binary(), econf:atom()), + '_' => econf:any()}, + [])), + modules => + econf:options( + #{'_' => + econf:options( + #{db_type => econf:atom(), + plugins => econf:list(econf:binary()), + '_' => econf:any()}, + [])}, + []), + '_' => econf:any()}, + econf:options( + Validators#{host_config => + econf:map(econf:binary(), + econf:options(Validators, [])), + append_host_config => + econf:map(econf:binary(), + econf:options(Validators, []))}, + []). diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 63adcdf69..075854e40 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -5,7 +5,7 @@ %%% Created : 11 Jan 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -23,48 +23,34 @@ %%% %%%---------------------------------------------------------------------- -%%% @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(ejabberd_config). +-behaviour(gen_server). -author('alexey@process-one.net'). --export([start/0, init/0, process/1, process2/2, - register_commands/3, unregister_commands/3, - opt_type/1]). +-export([start/0, start_link/0, process/1, process/2, process2/2]). +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). +-export([get_commands_spec/0, format_arg/2, + get_usage_command/4]). -include("ejabberd_ctl.hrl"). -include("ejabberd_commands.hrl"). --include("ejabberd.hrl"). +-include("ejabberd_http.hrl"). -include("logger.hrl"). + -define(DEFAULT_VERSION, 1000000). +-record(state, {}). %%----------------------------- %% Module %%----------------------------- start() -> + disable_logging(), [SNode, Timeout, Args] = case init:get_plain_arguments() of [SNode2, "--no-timeout" | Args2] -> [SNode2, infinity, Args2]; @@ -89,7 +75,7 @@ start() -> end end, Node = list_to_atom(SNode1), - Status = case rpc:call(Node, ?MODULE, process, [Args], Timeout) of + Status = case ejabberd_cluster:call(Node, ?MODULE, process, [Args], Timeout) of {badrpc, Reason} -> print("Failed RPC connection to the node ~p: ~p~n", [Node, Reason]), @@ -103,41 +89,71 @@ start() -> end, halt(Status). -init() -> - ets:new(ejabberd_ctl_cmds, [named_table, set, public]), - ets:new(ejabberd_ctl_host_cmds, [named_table, set, public]). +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). +init([]) -> + ejabberd_commands:register_commands(?MODULE, get_commands_spec()), + {ok, #state{}}. -%%----------------------------- -%% ejabberdctl Command managment -%%----------------------------- +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), + {noreply, State}. -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), +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), + {noreply, State}. + +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), + {noreply, State}. + +terminate(_Reason, _State) -> + ejabberd_commands:unregister_commands(get_commands_spec()), ok. +code_change(_OldVsn, State, _Extra) -> + {ok, State}. %%----------------------------- -%% Process +%% Process http %%----------------------------- +-spec process_http([binary()], tuple()) -> {non_neg_integer(), [{binary(), binary()}], string()}. + +process_http([_Call], #request{data = Data, path = [<<"ctl">> | _]}) -> + Args = [binary_to_list(E) || E <- misc:json_decode(Data)], + process_http2(Args, ?DEFAULT_VERSION). + +process_http2(["--version", Arg | Args], _) -> + Version = + try + list_to_integer(Arg) + catch _:_ -> + throw({invalid_version, Arg}) + end, + process_http2(Args, Version); + +process_http2(Args, Version) -> + {String, Code} = process2(Args, [], Version), + String2 = case String of + [] -> String; + _ -> [String, "\n"] + end, + {200, [{<<"status-code">>, integer_to_binary(Code)}], String2}. + +%%----------------------------- +%% Process command line +%%----------------------------- -spec process([string()]) -> non_neg_integer(). process(Args) -> process(Args, ?DEFAULT_VERSION). +-spec process([string() | binary()], non_neg_integer() | tuple()) -> non_neg_integer(). --spec process([string()], non_neg_integer()) -> non_neg_integer(). +process([Call], Request) when is_binary(Call) and is_record(Request, request) -> + process_http([Call], Request); %% The commands status, stop and restart are defined here to ensure %% they are usable even if ejabberd is completely stopped. @@ -145,41 +161,27 @@ process(["status"], _Version) -> {InternalStatus, ProvidedStatus} = init:get_status(), print("The node ~p is ~p with status: ~p~n", [node(), InternalStatus, ProvidedStatus]), - case lists:keysearch(ejabberd, 1, application:which_applications()) of + case lists:keymember(ejabberd, 1, application:which_applications()) of false -> EjabberdLogPath = ejabberd_logger:get_log_path(), print("ejabberd is not running in that node~n" - "Check for error messages: ~s~n" + "Check for error messages: ~ts~n" "or other files in that directory.~n", [EjabberdLogPath]), ?STATUS_ERROR; - {value, {_, _, Version}} -> - print("ejabberd ~s is running in that node~n", [Version]), + true -> + print("ejabberd ~ts is running in that node~n", [ejabberd_option:version()]), ?STATUS_SUCCESS end; -process(["stop"], _Version) -> - %%ejabberd_cover:stop(), - init:stop(), - ?STATUS_SUCCESS; - -process(["restart"], _Version) -> - init:restart(), - ?STATUS_SUCCESS; - -process(["mnesia"], _Version) -> - print("~p~n", [mnesia:system_info(all)]), - ?STATUS_SUCCESS; - -process(["mnesia", "info"], _Version) -> +%% TODO: Mnesia operations should not be hardcoded in ejabberd_ctl module. +%% For now, I leave them there to avoid breaking those commands for people that +%% may be using it (as format of response is going to change). +process(["mnesia_info_ctl"], _Version) -> mnesia:info(), ?STATUS_SUCCESS; -process(["mnesia", Arg], _Version) -> - case catch mnesia:system_info(list_to_atom(Arg)) of - {'EXIT', Error} -> print("Error: ~p~n", [Error]); - Return -> print("~p~n", [Return]) - end, - ?STATUS_SUCCESS; +process(["print_sql_schema", DBType, DBVersion, NewSchema], _Version) -> + ejabberd_sql_schema:print_schema(DBType, DBVersion, NewSchema); %% The arguments --long and --dual are not documented because they are %% automatically selected depending in the number of columns of the shell @@ -187,27 +189,38 @@ process(["help" | Mode], Version) -> {MaxC, ShCode} = get_shell_info(), case Mode of [] -> - print_usage(dual, MaxC, ShCode, Version), - ?STATUS_USAGE; + print_usage_help(MaxC, ShCode), + ?STATUS_SUCCESS; ["--dual"] -> print_usage(dual, MaxC, ShCode, Version), ?STATUS_USAGE; ["--long"] -> print_usage(long, MaxC, ShCode, Version), ?STATUS_USAGE; - ["--tags"] -> + ["tags"] -> print_usage_tags(MaxC, ShCode, Version), ?STATUS_SUCCESS; - ["--tags", Tag] -> + ["--tags"] -> % deprecated in favor of "tags" + print_usage_tags(MaxC, ShCode, Version), + ?STATUS_SUCCESS; + ["commands"] -> + print_usage_tags_long(MaxC, ShCode, Version), + ?STATUS_SUCCESS; + ["--tags", Tag] -> % deprecated in favor of simply "Tag" print_usage_tags(Tag, MaxC, ShCode, Version), ?STATUS_SUCCESS; - ["help"] -> - print_usage_help(MaxC, ShCode), - ?STATUS_SUCCESS; - [CmdString | _] -> - CmdStringU = ejabberd_regexp:greplace( - list_to_binary(CmdString), <<"-">>, <<"_">>), - print_usage_commands2(binary_to_list(CmdStringU), MaxC, ShCode, Version), + [String | _] -> + case determine_string_type(String, Version) of + no_idea -> + io:format("No tag or command matches '~ts'~n", [String]); + both -> + print_usage_tags(String, MaxC, ShCode, Version), + print_usage_commands2(String, MaxC, ShCode, Version); + tag -> + print_usage_tags(String, MaxC, ShCode, Version); + command -> + print_usage_commands2(String, MaxC, ShCode, Version) + end, ?STATUS_SUCCESS end; @@ -221,20 +234,19 @@ process(["--version", Arg | Args], _) -> process(Args, Version); process(Args, Version) -> - AccessCommands = get_accesscommands(), - {String, Code} = process2(Args, AccessCommands, Version), + {String, Code} = process2(Args, [], Version), case String of [] -> ok; _ -> - io:format("~s~n", [String]) + io:format("~ts~n", [String]) end, Code. -%% @spec (Args::[string()], AccessCommands) -> {String::string(), Code::integer()} +-spec process2(Args::[string()], AccessCommands::any()) -> + {String::string(), Code::integer()}. process2(Args, AccessCommands) -> process2(Args, AccessCommands, ?DEFAULT_VERSION). -%% @spec (Args::[string()], AccessCommands, Version) -> {String::string(), Code::integer()} process2(["--auth", User, Server, Pass | Args], AccessCommands, Version) -> process2(Args, AccessCommands, {list_to_binary(User), list_to_binary(Server), list_to_binary(Pass), true}, Version); @@ -250,7 +262,7 @@ process2(Args, AccessCommands, Auth, Version) -> io:format(lists:flatten(["\n" | String]++["\n"])), [CommandString | _] = Args, process(["help" | [CommandString]], Version), - {lists:flatten(String), ?STATUS_ERROR}; + {lists:flatten(String), ?STATUS_USAGE}; {String, Code} when is_list(String) and is_integer(Code) -> {lists:flatten(String), Code}; @@ -264,22 +276,32 @@ process2(Args, AccessCommands, Auth, Version) -> {"Erroneous result: " ++ io_lib:format("~p", [Other]), ?STATUS_ERROR} end. -get_accesscommands() -> - ejabberd_config:get_option(ejabberdctl_access_commands, - fun(V) when is_list(V) -> V end, []). +determine_string_type(String, Version) -> + TagsCommands = ejabberd_commands:get_tags_commands(Version), + CommandsNames = case lists:keysearch(String, 1, TagsCommands) of + {value, {String, CNs}} -> CNs; + false -> [] + end, + AllCommandsNames = [atom_to_list(Name) || {Name, _, _} <- ejabberd_commands:list_commands(Version)], + Cmds = filter_commands(AllCommandsNames, String), + case {CommandsNames, Cmds} of + {[], []} -> no_idea; + {[], _} -> command; + {_, []} -> tag; + {_, _} -> both + end. %%----------------------------- %% Command calling %%----------------------------- -%% @spec (Args::[string()], Auth, AccessCommands) -> string() | integer() | {string(), integer()} try_run_ctp(Args, Auth, AccessCommands, Version) -> try ejabberd_hooks:run_fold(ejabberd_ctl_process, false, [Args]) of false when Args /= [] -> try_call_command(Args, Auth, AccessCommands, Version); false -> print_usage(Version), - {"", ?STATUS_USAGE}; + {"", ?STATUS_BADRPC}; Status -> {"", Status} catch @@ -293,54 +315,60 @@ try_run_ctp(Args, Auth, AccessCommands, Version) -> {io_lib:format("Error in ejabberd ctl process: '~p' ~p", [Error, Why]), ?STATUS_USAGE} end. -%% @spec (Args::[string()], Auth, AccessCommands) -> string() | integer() | {string(), integer()} try_call_command(Args, Auth, AccessCommands, Version) -> try call_command(Args, Auth, AccessCommands, Version) of - {error, command_unknown} -> - {io_lib:format("Error: command ~p not known.", [hd(Args)]), ?STATUS_ERROR}; - {error, wrong_command_arguments} -> - {"Error: wrong arguments", ?STATUS_ERROR}; + {Reason, wrong_command_arguments} -> + {Reason, ?STATUS_USAGE}; Res -> Res catch + throw:{error, unknown_command} -> + KnownCommands = [Cmd || {Cmd, _, _} <- ejabberd_commands:list_commands(Version)], + UnknownCommand = list_to_atom(hd(Args)), + {io_lib:format( + "Error: unknown command '~ts'. Did you mean '~ts'?", + [hd(Args), misc:best_match(UnknownCommand, KnownCommands)]), + ?STATUS_ERROR}; throw:Error -> {io_lib:format("~p", [Error]), ?STATUS_ERROR}; - A:Why -> - Stack = erlang:get_stacktrace(), - {io_lib:format("Problem '~p ~p' occurred executing the command.~nStacktrace: ~p", [A, Why, Stack]), ?STATUS_ERROR} + A:Why:StackTrace -> + {io_lib:format("Unhandled exception occurred executing the command:~n** ~ts", + [misc:format_exception(2, A, Why, StackTrace)]), + ?STATUS_ERROR} end. -%% @spec (Args::[string()], Auth, AccessCommands) -> string() | integer() | {string(), integer()} | {error, ErrorType} +-spec call_command(Args::[string()], + Auth::noauth | {binary(), binary(), binary(), true}, + AccessCommands::[any()], + Version::integer()) -> + string() | integer() | {string(), integer()} | {error, ErrorType::any()}. call_command([CmdString | Args], Auth, _AccessCommands, Version) -> CmdStringU = ejabberd_regexp:greplace( list_to_binary(CmdString), <<"-">>, <<"_">>), Command = list_to_atom(binary_to_list(CmdStringU)), - case ejabberd_commands:get_command_format(Command, Auth, Version) of - {error, command_unknown} -> - {error, command_unknown}; - {ArgsFormat, ResultFormat} -> - case (catch format_args(Args, ArgsFormat)) of - ArgsFormatted when is_list(ArgsFormatted) -> - CI = case Auth of - {U, S, _, _} -> #{usr => {U, S, <<"">>}, caller_host => S}; - _ -> #{} - end, - CI2 = CI#{caller_module => ?MODULE}, - Result = ejabberd_commands:execute_command2(Command, - ArgsFormatted, - CI2, - Version), - format_result(Result, ResultFormat); - {'EXIT', {function_clause,[{lists,zip,[A1, A2], _} | _]}} -> - {NumCompa, TextCompa} = - case {length(A1), length(A2)} of - {L1, L2} when L1 < L2 -> {L2-L1, "less argument"}; - {L1, L2} when L1 > L2 -> {L1-L2, "more argument"} - end, - {io_lib:format("Error: the command ~p requires ~p ~s.", - [CmdString, NumCompa, TextCompa]), - wrong_command_arguments} - end + {ArgsFormat, _, ResultFormat} = ejabberd_commands:get_command_format(Command, Auth, Version), + case (catch format_args(Args, ArgsFormat)) of + ArgsFormatted when is_list(ArgsFormatted) -> + CI = case Auth of + {U, S, _, _} -> #{usr => {U, S, <<"">>}, caller_host => S}; + _ -> #{} + end, + CI2 = CI#{caller_module => ?MODULE}, + Result = ejabberd_commands:execute_command2(Command, + ArgsFormatted, + CI2, + Version), + format_result_preliminary(Result, ResultFormat, Version); + {'EXIT', {function_clause,[{lists,zip,[A1,A2|_], _} | _]}} -> + {NumCompa, TextCompa} = + case {length(A1), length(A2)} of + {L1, L2} when L1 < L2 -> {L2-L1, "less argument"}; + {L1, L2} when L1 > L2 -> {L1-L2, "more argument"} + end, + process(["help" | [CmdString]], Version), + {io_lib:format("Error: the command '~ts' requires ~p ~ts.", + [CmdString, NumCompa, TextCompa]), + wrong_command_arguments} end. @@ -366,7 +394,18 @@ format_arg("", string) -> format_arg(Arg, string) -> NumChars = integer_to_list(length(Arg)), Parse = "~" ++ NumChars ++ "c", - format_arg2(Arg, Parse). + format_arg2(Arg, Parse); +format_arg(Arg, {list, {_ArgName, ArgFormat}}) -> + [format_arg(string:trim(Element), ArgFormat) || Element <- string:tokens(Arg, ",")]; +format_arg(Arg, {list, ArgFormat}) -> + [format_arg(string:trim(Element), ArgFormat) || Element <- string:tokens(Arg, ",")]; +format_arg(Arg, {tuple, Elements}) -> + Args = string:tokens(Arg, ":"), + list_to_tuple(format_args(Args, Elements)); +format_arg(Arg, Format) -> + S = unicode:characters_to_binary(Arg, utf8), + JSON = misc:json_decode(S), + mod_http_api:format_arg(JSON, Format). format_arg2(Arg, Parse)-> {ok, [Arg2], _RemainingArguments} = io_lib:fread(Parse, Arg), @@ -376,52 +415,77 @@ format_arg2(Arg, Parse)-> %% Format result %%----------------------------- -format_result({error, ErrorAtom}, _) -> +format_result_preliminary(Result, {A, {list, B}}, Version) -> + format_result(Result, {A, {top_result_list, B}}, Version); +format_result_preliminary(Result, ResultFormat, Version) -> + format_result(Result, ResultFormat, Version). + +format_result({error, ErrorAtom}, _, _Version) -> {io_lib:format("Error: ~p", [ErrorAtom]), make_status(error)}; %% An error should always be allowed to return extended error to help with API. %% Extended error is of the form: %% {error, type :: atom(), code :: int(), Desc :: string()} -format_result({error, ErrorAtom, Code, _Msg}, _) -> - {io_lib:format("Error: ~p", [ErrorAtom]), make_status(Code)}; +format_result({error, ErrorAtom, Code, Msg}, _, _Version) -> + {io_lib:format("Error: ~p: ~s", [ErrorAtom, Msg]), make_status(Code)}; -format_result(Atom, {_Name, atom}) -> +format_result(Atom, {_Name, atom}, _Version) -> io_lib:format("~p", [Atom]); -format_result(Int, {_Name, integer}) -> +format_result(Int, {_Name, integer}, _Version) -> io_lib:format("~p", [Int]); -format_result([A|_]=String, {_Name, string}) when is_list(String) and is_integer(A) -> - io_lib:format("~s", [String]); +format_result([A|_]=String, {_Name, string}, _Version) when is_list(String) and is_integer(A) -> + io_lib:format("~ts", [String]); -format_result(Binary, {_Name, string}) when is_binary(Binary) -> - io_lib:format("~s", [binary_to_list(Binary)]); +format_result(Binary, {_Name, binary}, _Version) when is_binary(Binary) -> + io_lib:format("~ts", [Binary]); -format_result(Atom, {_Name, string}) when is_atom(Atom) -> - io_lib:format("~s", [atom_to_list(Atom)]); +format_result(String, {_Name, binary}, _Version) when is_list(String) -> + io_lib:format("~ts", [String]); -format_result(Integer, {_Name, string}) when is_integer(Integer) -> - io_lib:format("~s", [integer_to_list(Integer)]); +format_result(Binary, {_Name, string}, _Version) when is_binary(Binary) -> + io_lib:format("~ts", [Binary]); -format_result(Other, {_Name, string}) -> +format_result(Atom, {_Name, string}, _Version) when is_atom(Atom) -> + io_lib:format("~ts", [atom_to_list(Atom)]); + +format_result(Integer, {_Name, string}, _Version) when is_integer(Integer) -> + io_lib:format("~ts", [integer_to_list(Integer)]); + +format_result(Other, {_Name, string}, _Version) -> io_lib:format("~p", [Other]); -format_result(Code, {_Name, rescode}) -> +format_result(Code, {_Name, rescode}, _Version) -> make_status(Code); -format_result({Code, Text}, {_Name, restuple}) -> - {io_lib:format("~s", [Text]), make_status(Code)}; +format_result({Code, Text}, {_Name, restuple}, _Version) -> + {io_lib:format("~ts", [Text]), make_status(Code)}; + +format_result([], {_Name, {top_result_list, _ElementsDef}}, _Version) -> + ""; +format_result([FirstElement | Elements], {_Name, {top_result_list, ElementsDef}}, Version) -> + [format_result(FirstElement, ElementsDef, Version) | + lists:map( + fun(Element) -> + ["\n" | format_result(Element, ElementsDef, Version)] + end, + Elements)]; %% The result is a list of something: [something()] -format_result([], {_Name, {list, _ElementsDef}}) -> +format_result([], {_Name, {list, _ElementsDef}}, _Version) -> ""; -format_result([FirstElement | Elements], {_Name, {list, ElementsDef}}) -> +format_result([FirstElement | Elements], {_Name, {list, ElementsDef}}, Version) -> + Separator = case Version of + 0 -> ";"; + _ -> "," + end, %% Start formatting the first element - [format_result(FirstElement, ElementsDef) | + [format_result(FirstElement, ElementsDef, Version) | %% If there are more elements, put always first a newline character lists:map( fun(Element) -> - ["\n" | format_result(Element, ElementsDef)] + [Separator | format_result(Element, ElementsDef, Version)] end, Elements)]; @@ -429,33 +493,31 @@ format_result([FirstElement | Elements], {_Name, {list, ElementsDef}}) -> %% NOTE: the elements in the tuple are separated with tabular characters, %% if a string is empty, it will be difficult to notice in the shell, %% maybe a different separation character should be used, like ;;? -format_result(ElementsTuple, {_Name, {tuple, ElementsDef}}) -> +format_result(ElementsTuple, {_Name, {tuple, ElementsDef}}, Version) -> ElementsList = tuple_to_list(ElementsTuple), [{FirstE, FirstD} | ElementsAndDef] = lists:zip(ElementsList, ElementsDef), - [format_result(FirstE, FirstD) | + [format_result(FirstE, FirstD, Version) | lists:map( fun({Element, ElementDef}) -> - ["\t" | format_result(Element, ElementDef)] + ["\t" | format_result(Element, ElementDef, Version)] end, ElementsAndDef)]; -format_result(404, {_Name, _}) -> +format_result(404, {_Name, _}, _Version) -> make_status(not_found). make_status(ok) -> ?STATUS_SUCCESS; make_status(true) -> ?STATUS_SUCCESS; make_status(Code) when is_integer(Code), Code > 255 -> ?STATUS_ERROR; make_status(Code) when is_integer(Code), Code > 0 -> Code; -make_status(_Error) -> ?STATUS_ERROR. +make_status(Error) -> + io:format("Error: ~p~n", [Error]), + ?STATUS_ERROR. get_list_commands(Version) -> try ejabberd_commands:list_commands(Version) of Commands -> - [tuple_command_help(Command) - || {N,_,_}=Command <- Commands, - %% Don't show again those commands, because they are already - %% announced by ejabberd_ctl itself - N /= status, N /= stop, N /= restart] + [tuple_command_help(Command) || Command <- Commands] catch exit:_ -> [] @@ -463,71 +525,66 @@ get_list_commands(Version) -> %% Return: {string(), [string()], string()} tuple_command_help({Name, _Args, Desc}) -> - {Args, _} = ejabberd_commands:get_command_format(Name, admin), + {Args, _, _} = ejabberd_commands:get_command_format(Name, admin), Arguments = [atom_to_list(ArgN) || {ArgN, _ArgF} <- Args], - Prepend = case is_supported_args(Args) of - true -> ""; - false -> "*" - end, CallString = atom_to_list(Name), - {CallString, Arguments, Prepend ++ Desc}. + {CallString, Arguments, Desc}. -is_supported_args(Args) -> - lists:all( - fun({_Name, Format}) -> - (Format == integer) - or (Format == string) - or (Format == binary) +has_tuple_args(Args) -> + lists:any( + fun({_Name, tuple}) -> true; + ({_Name, {tuple, _}}) -> true; + ({_Name, {list, SubArg}}) -> + has_tuple_args([SubArg]); + (_) -> false end, Args). -get_list_ctls() -> - case catch ets:tab2list(ejabberd_ctl_cmds) of - {'EXIT', _} -> []; - Cs -> [{NameArgs, [], Desc} || {NameArgs, Desc} <- Cs] - end. - +has_list_args(Args) -> + lists:any( + fun({_Name, list}) -> true; + ({_Name, {list, _}}) -> true; + (_) -> false + end, + Args). %%----------------------------- %% Print help %%----------------------------- -%% Bold +%% Commands are Bold -define(B1, "\e[1m"). -define(B2, "\e[22m"). --define(B(S), case ShCode of true -> [?B1, S, ?B2]; false -> S end). +-define(C(S), case ShCode of true -> [?B1, S, ?B2]; false -> S end). -%% Underline +%% Arguments are Dim +-define(D1, "\e[2m"). +-define(D2, "\e[22m"). +-define(A(S), case ShCode of true -> [?D1, S, ?D2]; false -> S end). + +%% Tags are Underline -define(U1, "\e[4m"). -define(U2, "\e[24m"). --define(U(S), case ShCode of true -> [?U1, S, ?U2]; false -> S end). +-define(G(S), case ShCode of true -> [?U1, S, ?U2]; false -> S end). + +%% B are Nothing +-define(N1, "\e[0m"). +-define(N2, "\e[0m"). +-define(B(S), case ShCode of true -> [?N1, S, ?N2]; false -> S end). print_usage(Version) -> {MaxC, ShCode} = get_shell_info(), print_usage(dual, MaxC, ShCode, Version). print_usage(HelpMode, MaxC, ShCode, Version) -> - AllCommands = - [ - {"status", [], "Get ejabberd status"}, - {"stop", [], "Stop ejabberd"}, - {"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(), + AllCommands = get_list_commands(Version), print( - ["Usage: ", ?B("ejabberdctl"), " [--no-timeout] [--node ", ?U("nodename"), "] [--version ", ?U("api_version"), "] ", - ?U("command"), " [", ?U("options"), "]\n" + ["Usage: ", "ejabberdctl", " [--no-timeout] [--node ", ?A("name"), "] [--version ", ?A("apiv"), "] ", + "[--auth ", ?A("user host pass"), "] ", + ?C("command"), " [", ?A("arguments"), "]\n" "\n" "Available commands in this ejabberd node:\n"], []), - print_usage_commands(HelpMode, MaxC, ShCode, AllCommands), - print( - ["\n" - "Examples:\n" - " ejabberdctl restart\n" - " ejabberdctl --node ejabberd@host restart\n"], - []). + print_usage_commands(HelpMode, MaxC, ShCode, AllCommands). print_usage_commands(HelpMode, MaxC, ShCode, Commands) -> CmdDescsSorted = lists:keysort(1, Commands), @@ -573,6 +630,14 @@ get_shell_info() -> %% Split this command description in several lines of proper length prepare_description(DescInit, MaxC, Desc) -> + case string:find(Desc, "\n") of + nomatch -> + prepare_description2(DescInit, MaxC, Desc); + _ -> + Desc + end. + +prepare_description2(DescInit, MaxC, Desc) -> Words = string:tokens(Desc, " "), prepare_long_line(DescInit, MaxC, Words). @@ -619,21 +684,27 @@ format_command_lines(CALD, MaxCmdLen, MaxC, ShCode, dual) %% If the space available for descriptions is too narrow, enforce long help mode format_command_lines(CALD, MaxCmdLen, MaxC, ShCode, long); +format_command_lines(CALD, _MaxCmdLen, _MaxC, ShCode, short) -> + lists:map( + fun({Cmd, Args, _CmdArgsL, _Desc}) -> + [" ", ?C(Cmd), [[" ", ?A(Arg)] || Arg <- Args], "\n"] + end, CALD); + format_command_lines(CALD, MaxCmdLen, MaxC, ShCode, dual) -> lists:map( fun({Cmd, Args, CmdArgsL, Desc}) -> DescFmt = prepare_description(MaxCmdLen+4, MaxC, Desc), - [" ", ?B(Cmd), " ", [[?U(Arg), " "] || Arg <- Args], - string:chars($\s, MaxCmdLen - CmdArgsL + 1), + [" ", ?C(Cmd), [[" ", ?A(Arg)] || Arg <- Args], + lists:duplicate(MaxCmdLen - CmdArgsL + 1, $\s), DescFmt, "\n"] end, CALD); format_command_lines(CALD, _MaxCmdLen, MaxC, ShCode, long) -> lists:map( fun({Cmd, Args, _CmdArgsL, Desc}) -> - DescFmt = prepare_description(8, MaxC, Desc), - ["\n ", ?B(Cmd), " ", [[?U(Arg), " "] || Arg <- Args], "\n", " ", - DescFmt, "\n"] + DescFmt = prepare_description(13, MaxC, Desc), + [" ", ?C(Cmd), [[" ", ?A(Arg)] || Arg <- Args], "\n", + " ", DescFmt, "\n"] end, CALD). @@ -642,20 +713,42 @@ format_command_lines(CALD, _MaxCmdLen, MaxC, ShCode, long) -> %%----------------------------- print_usage_tags(MaxC, ShCode, Version) -> - print("Available tags and commands:", []), + print("Available tags and list of commands:", []), TagsCommands = ejabberd_commands:get_tags_commands(Version), lists:foreach( fun({Tag, Commands} = _TagCommands) -> - print(["\n\n ", ?B(Tag), "\n "], []), + print(["\n\n ", ?G(Tag), "\n "], []), Words = lists:sort(Commands), Desc = prepare_long_line(5, MaxC, Words), - print(Desc, []) + print(?C(Desc), []) end, TagsCommands), print("\n\n", []). +print_usage_tags_long(MaxC, ShCode, Version) -> + print("Available tags and commands details:", []), + TagsCommands = ejabberd_commands:get_tags_commands(Version), + print("\n", []), + lists:foreach( + fun({Tag, CommandsNames} = _TagCommands) -> + print(["\n ", ?G(Tag), "\n"], []), + CommandsList = lists:map( + fun(NameString) -> + C = ejabberd_commands:get_command_definition( + list_to_atom(NameString), Version), + #ejabberd_commands{name = Name, + args = Args, + desc = Desc} = C, + tuple_command_help({Name, Args, Desc}) + end, + CommandsNames), + print_usage_commands(short, MaxC, ShCode, CommandsList) + end, + TagsCommands), + print("\n", []). + print_usage_tags(Tag, MaxC, ShCode, Version) -> - print(["Available commands with tag ", ?B(Tag), ":", "\n"], []), + print(["Available commands with tag ", ?G(Tag), ":", "\n", "\n"], []), HelpMode = long, TagsCommands = ejabberd_commands:get_tags_commands(Version), CommandsNames = case lists:keysearch(Tag, 1, TagsCommands) of @@ -682,62 +775,74 @@ print_usage_tags(Tag, MaxC, ShCode, Version) -> print_usage_help(MaxC, ShCode) -> LongDesc = - ["The special 'help' ejabberdctl command provides help of ejabberd commands.\n\n" - "The format is:\n ", ?B("ejabberdctl"), " ", ?B("help"), " [", ?B("--tags"), " ", ?U("[tag]"), " | ", ?U("com?*"), "]\n\n" + ["This special ", ?C("help"), " command provides help of ejabberd commands.\n\n" + "The format is:\n ", ?B("ejabberdctl"), " ", ?C("help"), + " [", ?A("tags"), " | ", ?A("commands"), " | ", ?G("tag"), " | ", ?C("command"), " | ", ?C("com?*"), "]\n\n" "The optional arguments:\n" - " ",?B("--tags")," Show all tags and the names of commands in each tag\n" - " ",?B("--tags"), " ", ?U("tag")," Show description of commands in this tag\n" - " ",?U("command")," Show detailed description of the command\n" - " ",?U("com?*")," Show detailed description of commands that match this glob.\n" - " You can use ? to match a simple character,\n" - " and * to match several characters.\n" + " ",?A("tags")," Show all tags and commands names in each tag\n" + " ",?A("commands")," Show all tags and commands details in each tag\n" + " ",?G("tag")," Show commands related to this tag\n" + " ",?C("command")," Show detailed description of this command\n" + " ",?C("com?*")," Show commands that match this glob.\n" + " (? will match a simple character, and\n" + " * will match several characters)\n" "\n", "Some example usages:\n", - " ejabberdctl help\n", - " ejabberdctl help --tags\n", - " ejabberdctl help --tags accounts\n", - " ejabberdctl help register\n", - " ejabberdctl help regist*\n", + " ejabberdctl ", ?C("help"), "\n", + " ejabberdctl ", ?C("help"), " ", ?A("tags"), "\n", + " ejabberdctl ", ?C("help"), " ", ?A("commands"), "\n", + " ejabberdctl ", ?C("help"), " ", ?G("accounts"), "\n", + " ejabberdctl ", ?C("help"), " ", ?C("register"), "\n", + " ejabberdctl ", ?C("help"), " ", ?C("regist*"), "\n", "\n", - "Please note that 'ejabberdctl help' shows all ejabberd commands,\n", - "even those that cannot be used in the shell with ejabberdctl.\n", - "Those commands can be identified because the description starts with: *"], + "Some command arguments are lists or tuples, like add_rosteritem and create_room_with_opts.\n", + "Separate the elements in a list with the , character.\n", + "Separate the elements in a tuple with the : character.\n", + "\n", + "Some commands results are lists or tuples, like get_roster and get_user_subscriptions.\n", + "The elements in a list are separated with a , character.\n", + "The elements in a tuple are separated with a tabular character.\n"], ArgsDef = [], C = #ejabberd_commands{ - desc = "Show help of ejabberd commands", - longdesc = lists:flatten(LongDesc), - args = ArgsDef, - result = {help, string}}, - print_usage_command("help", C, MaxC, ShCode). + name = help, + desc = "Show help of ejabberd commands", + longdesc = lists:flatten(LongDesc), + args = ArgsDef, + result = {help, string}}, + print(get_usage_command2("help", C, MaxC, ShCode), []). %%----------------------------- %% Print usage command %%----------------------------- -%% @spec (CmdSubString::string(), MaxC::integer(), ShCode::boolean()) -> ok +-spec print_usage_commands2(CmdSubString::string(), MaxC::integer(), + ShCode::boolean(), Version::integer()) -> ok. print_usage_commands2(CmdSubString, MaxC, ShCode, Version) -> %% Get which command names match this substring AllCommandsNames = [atom_to_list(Name) || {Name, _, _} <- ejabberd_commands:list_commands(Version)], Cmds = filter_commands(AllCommandsNames, CmdSubString), case Cmds of - [] -> io:format("Error: no command found that match: ~p~n", [CmdSubString]); + [] -> io:format("Error: no command found that match '~ts'~n", [CmdSubString]); _ -> print_usage_commands3(lists:sort(Cmds), MaxC, ShCode, Version) end. +print_usage_commands3([Cmd], MaxC, ShCode, Version) -> + print_usage_command(Cmd, MaxC, ShCode, Version); print_usage_commands3(Cmds, MaxC, ShCode, Version) -> - %% Then for each one print it - lists:mapfoldl( - fun(Cmd, Remaining) -> - print_usage_command(Cmd, MaxC, ShCode, Version), - case Remaining > 1 of - true -> print([" ", lists:duplicate(MaxC, 126), " \n"], []); - false -> ok - end, - {ok, Remaining-1} - end, - length(Cmds), - Cmds). + CommandsList = lists:map( + fun(NameString) -> + C = ejabberd_commands:get_command_definition( + list_to_atom(NameString), Version), + #ejabberd_commands{name = Name, + args = Args, + desc = Desc} = C, + tuple_command_help({Name, Args, Desc}) + end, + Cmds), + + print_usage_commands(long, MaxC, ShCode, CommandsList), %% que aqui solo muestre un par de lineas + ok. filter_commands(All, SubString) -> case lists:member(SubString, All) of @@ -758,29 +863,42 @@ filter_commands_regexp(All, Glob) -> end, All). -%% @spec (Cmd::string(), MaxC::integer(), ShCode::boolean()) -> ok -print_usage_command(Cmd, MaxC, ShCode, Version) -> - Name = list_to_atom(Cmd), - case ejabberd_commands:get_command_definition(Name, Version) of - command_not_found -> - io:format("Error: command ~p not known.~n", [Cmd]); - C -> - print_usage_command2(Cmd, C, MaxC, ShCode) - end. +maybe_add_policy_arguments(Args, user) -> + [{user, binary}, {host, binary} | Args]; +maybe_add_policy_arguments(Args, _) -> + Args. -print_usage_command2(Cmd, C, MaxC, ShCode) -> +-spec print_usage_command(Cmd::string(), MaxC::integer(), + ShCode::boolean(), Version::integer()) -> ok. +print_usage_command(Cmd, MaxC, ShCode, Version) -> + print(get_usage_command(Cmd, MaxC, ShCode, Version), []). + +get_usage_command(Cmd, MaxC, ShCode, Version) -> + Name = list_to_atom(Cmd), + C = ejabberd_commands:get_command_definition(Name, Version), + get_usage_command2(Cmd, C, MaxC, ShCode). + +get_usage_command2(Cmd, C, MaxC, ShCode) -> #ejabberd_commands{ tags = TagsAtoms, + definer = Definer, desc = Desc, + args = ArgsDefPreliminary, + args_desc = ArgsDesc, + args_example = ArgsExample, + result_example = ResultExample, + policy = Policy, longdesc = LongDesc, + note = Note, result = ResultDef} = C, - NameFmt = [" ", ?B("Command Name"), ": ", Cmd, "\n"], + NameFmt = [" ", ?B("Command Name"), ": ", ?C(Cmd), "\n"], %% Initial indentation of result is 13 = length(" Arguments: ") - {ArgsDef, _} = ejabberd_commands:get_command_format( - C#ejabberd_commands.name, admin), - Args = [format_usage_ctype(ArgDef, 13) || ArgDef <- ArgsDef], + ArgsDef = maybe_add_policy_arguments(ArgsDefPreliminary, Policy), + ArgsDetailed = add_args_desc(ArgsDef, ArgsDesc), + Args = [format_usage_ctype1(ArgDetailed, 13, ShCode) || ArgDetailed <- ArgsDetailed], + ArgsMargin = lists:duplicate(13, $\s), ArgsListFmt = case Args of [] -> "\n"; @@ -790,32 +908,104 @@ print_usage_command2(Cmd, C, MaxC, ShCode) -> %% Initial indentation of result is 11 = length(" Returns: ") ResultFmt = format_usage_ctype(ResultDef, 11), - ReturnsFmt = [" ",?B("Returns"),": ", ResultFmt], + ReturnsFmt = [" ",?B("Result"),": ", ResultFmt], - XmlrpcFmt = "", %%+++ [" ",?B("XML-RPC"),": ", format_usage_xmlrpc(ArgsDef, ResultDef), "\n\n"], + ExampleMargin = lists:duplicate(11, $\s), + Example = format_usage_example(Cmd, ArgsExample, ResultExample, ExampleMargin), + ExampleFmt = case Example of + [] -> + ""; + _ -> + ExampleListFmt = [ [Ex, "\n", ExampleMargin] || Ex <- Example], + [" ",?B("Example"),": ", ExampleListFmt, "\n"] + end, - TagsFmt = [" ",?B("Tags"),": ", prepare_long_line(8, MaxC, [atom_to_list(TagA) || TagA <- TagsAtoms])], + TagsFmt = [" ",?B("Tags"),":", prepare_long_line(8, MaxC, [?G(atom_to_list(TagA)) || TagA <- TagsAtoms])], - DescFmt = [" ",?B("Description"),": ", prepare_description(15, MaxC, Desc)], + IsDefinerMod = case Definer of + unknown -> true; + _ -> lists:member([gen_mod], proplists:get_all_values(behaviour, Definer:module_info(attributes))) + end, + ModuleFmt = case IsDefinerMod of + true -> [" ",?B("Module"),": ", atom_to_list(Definer), "\n\n"]; + false -> [] + end, + + NoteFmt = case Note of + "" -> []; + _ -> [" ",?B("Note"),": ", Note, "\n\n"] + end, + + DescFmt = [" ",?B("Description"),":", prepare_description(15, MaxC, Desc)], LongDescFmt = case LongDesc of "" -> ""; _ -> ["", prepare_description(0, MaxC, LongDesc), "\n\n"] end, - NoteEjabberdctl = case is_supported_args(ArgsDef) of - true -> ""; - false -> [" ", ?B("Note:"), " This command cannot be executed using ejabberdctl. Try ejabberd_xmlrpc.\n\n"] + NoteEjabberdctlList = case has_list_args(ArgsDefPreliminary) of + true -> [" ", ?B("Note:"), " In a list argument, separate the elements using the , character for example: one,two,three\n\n"]; + false -> "" + end, + NoteEjabberdctlTuple = case has_tuple_args(ArgsDefPreliminary) of + true -> [" ", ?B("Note:"), " In a tuple argument, separate the elements using the : character for example: members_only:true\n\n"]; + false -> "" end, - print(["\n", NameFmt, "\n", ArgsFmt, "\n", ReturnsFmt, "\n\n", XmlrpcFmt, TagsFmt, "\n\n", DescFmt, "\n\n", LongDescFmt, NoteEjabberdctl], []). + First = case Cmd of + "help" -> ""; + _ -> [NameFmt, "\n", ArgsFmt, "\n", ReturnsFmt, + "\n\n", ExampleFmt, TagsFmt, "\n\n", ModuleFmt, NoteFmt, DescFmt, "\n\n"] + end, + [First, LongDescFmt, NoteEjabberdctlList, NoteEjabberdctlTuple]. + +%%----------------------------- +%% Format Arguments Help +%%----------------------------- + +add_args_desc(Definitions, none) -> + Descriptions = lists:duplicate(length(Definitions), ""), + add_args_desc(Definitions, Descriptions); +add_args_desc(Definitions, Descriptions) -> + lists:zipwith(fun({Name, Type}, Description) -> + {Name, Type, Description} end, + Definitions, + Descriptions). + +format_usage_ctype1({_Name, _Type} = Definition, Indentation, ShCode) -> + [Arg] = add_args_desc([Definition], none), + format_usage_ctype1(Arg, Indentation, ShCode); +format_usage_ctype1({Name, Type, Description}, Indentation, ShCode) -> + TypeString = case Type of + {list, ElementDef} -> + NameFmt = atom_to_list(Name), + Indentation2 = Indentation + length(NameFmt) + 4, + ElementFmt = format_usage_ctype1(ElementDef, Indentation2, ShCode), + io_lib:format("[ ~s ]", [lists:flatten(ElementFmt)]); + {tuple, ElementsDef} -> + NameFmt = atom_to_list(Name), + Indentation2 = Indentation + length(NameFmt) + 4, + ElementsFmt = format_usage_tuple(ElementsDef, Indentation2), + io_lib:format("{ ~s }", [lists:flatten(ElementsFmt)]); + _ -> + Type + end, + DescriptionText = case Description of + "" -> ""; + Description -> " : "++Description + end, + io_lib:format("~p::~s~s", [Name, TypeString, DescriptionText]). + format_usage_ctype(Type, _Indentation) - when (Type==atom) or (Type==integer) or (Type==string) or (Type==binary) or (Type==rescode) or (Type==restuple)-> + when (Type==atom) or (Type==integer) or (Type==string) or (Type==binary) + or (Type==rescode) or (Type==restuple) -> io_lib:format("~p", [Type]); format_usage_ctype({Name, Type}, _Indentation) - when (Type==atom) or (Type==integer) or (Type==string) or (Type==binary) or (Type==rescode) or (Type==restuple)-> + when (Type==atom) or (Type==integer) or (Type==string) or (Type==binary) + or (Type==rescode) or (Type==restuple) + or (Type==any) -> io_lib:format("~p::~p", [Name, Type]); format_usage_ctype({Name, {list, ElementDef}}, Indentation) -> @@ -828,12 +1018,13 @@ format_usage_ctype({Name, {tuple, ElementsDef}}, Indentation) -> NameFmt = atom_to_list(Name), Indentation2 = Indentation + length(NameFmt) + 4, ElementsFmt = format_usage_tuple(ElementsDef, Indentation2), - [NameFmt, "::{ " | ElementsFmt]. + [NameFmt, "::{ "] ++ ElementsFmt ++ [" }"]. + format_usage_tuple([], _Indentation) -> []; format_usage_tuple([ElementDef], Indentation) -> - [format_usage_ctype(ElementDef, Indentation) , " }"]; + format_usage_ctype(ElementDef, Indentation); format_usage_tuple([ElementDef | ElementsDef], Indentation) -> ElementFmt = format_usage_ctype(ElementDef, Indentation), MarginString = lists:duplicate(Indentation, $\s), % Put spaces @@ -842,16 +1033,144 @@ format_usage_tuple([ElementDef | ElementsDef], Indentation) -> print(Format, Args) -> io:format(lists:flatten(Format), Args). +-ifdef(LAGER). +disable_logging() -> + ok. +-else. +disable_logging() -> + logger:set_primary_config(level, none). +-endif. + %%----------------------------- -%% Command managment +%% Format Example Help %%----------------------------- -%%+++ -%% Struct(Integer res) create_account(Struct(String user, String server, String password)) -%%format_usage_xmlrpc(ArgsDef, ResultDef) -> -%% ["aaaa bbb ccc"]. +format_usage_example(_Cmd, none, _ResultExample, _Indentation) -> + ""; +format_usage_example(Cmd, ArgsExample, ResultExample, Indentation) -> + Arguments = format_usage_arguments(ArgsExample, []), + Result = format_usage_result([ResultExample], [], Indentation), + [lists:join(" ", ["ejabberdctl", Cmd] ++ Arguments) | Result]. +format_usage_arguments([], R) -> + lists:reverse(R); -opt_type(ejabberdctl_access_commands) -> - fun (V) when is_list(V) -> V end; -opt_type(_) -> [ejabberdctl_access_commands]. +format_usage_arguments([Argument | Arguments], R) + when is_integer(Argument) -> + format_usage_arguments(Arguments, [integer_to_list(Argument) | R]); + +format_usage_arguments([[Integer|_] = Argument | Arguments], R) + when is_list(Argument) and is_integer(Integer) -> + Result = case contains_more_than_letters(Argument) of + true -> ["\"", Argument, "\""]; + false -> [Argument] + end, + format_usage_arguments(Arguments, [Result | R]); + +format_usage_arguments([[Element | _] = Argument | Arguments], R) + when is_list(Argument) and is_tuple(Element) -> + ArgumentFmt = format_usage_arguments(Argument, []), + format_usage_arguments(Arguments, [lists:join(",", ArgumentFmt) | R]); + +format_usage_arguments([Argument | Arguments], R) + when is_list(Argument) -> + Result = format_usage_arguments(Argument, []), + format_usage_arguments(Arguments, [lists:join(",", Result) | R]); + +format_usage_arguments([Argument | Arguments], R) + when is_tuple(Argument) -> + Result = format_usage_arguments(tuple_to_list(Argument), []), + format_usage_arguments(Arguments, [lists:join(":", Result) | R]); + +format_usage_arguments([Argument | Arguments], R) + when is_binary(Argument) -> + Result = case contains_more_than_letters(binary_to_list(Argument)) of + true -> ["\"", Argument, "\""]; + false -> [Argument] + end, + format_usage_arguments(Arguments, [Result | R]); + +format_usage_arguments([Argument | Arguments], R) -> + format_usage_arguments(Arguments, [Argument | R]). + +format_usage_result([none], _R, _Indentation) -> + ""; +format_usage_result([], R, _Indentation) -> + lists:reverse(R); + +format_usage_result([{Code, Text} | Arguments], R, Indentation) + when is_atom(Code) and is_binary(Text) -> + format_usage_result(Arguments, [Text | R], Indentation); + +format_usage_result([Argument | Arguments], R, Indentation) + when is_atom(Argument) -> + format_usage_result(Arguments, [["\'", atom_to_list(Argument), "\'"] | R], Indentation); + +format_usage_result([Argument | Arguments], R, Indentation) + when is_integer(Argument) -> + format_usage_result(Arguments, [integer_to_list(Argument) | R], Indentation); + +format_usage_result([[Integer|_] = Argument | Arguments], R, Indentation) + when is_list(Argument) and is_integer(Integer) -> + format_usage_result(Arguments, [Argument | R], Indentation); + +format_usage_result([[Element | _] = Argument | Arguments], R, Indentation) + when is_list(Argument) and is_tuple(Element) -> + ArgumentFmt = format_usage_result(Argument, [], Indentation), + format_usage_result(Arguments, [lists:join("\n"++Indentation, ArgumentFmt) | R], Indentation); + +format_usage_result([Argument | Arguments], R, Indentation) + when is_list(Argument) -> + format_usage_result(Arguments, [lists:join("\n"++Indentation, Argument) | R], Indentation); + +format_usage_result([Argument | Arguments], R, Indentation) + when is_tuple(Argument) -> + Result = format_usage_result(tuple_to_list(Argument), [], Indentation), + format_usage_result(Arguments, [lists:join("\t", Result) | R], Indentation); + +format_usage_result([Argument | Arguments], R, Indentation) -> + format_usage_result(Arguments, [Argument | R], Indentation). + +contains_more_than_letters(Argument) -> + lists:any(fun(I) when (I < $A) -> true; + (I) when (I > $z) -> true; + (_) -> false end, + Argument). + +%%----------------------------- +%% Register commands +%%----------------------------- + +get_commands_spec() -> + [ + #ejabberd_commands{name = help, tags = [ejabberdctl], + desc = "Get list of commands, or help of a command (only ejabberdctl)", + longdesc = "This command is exclusive for the ejabberdctl command-line script, " + "don't attempt to execute it using any other API frontend."}, + #ejabberd_commands{name = mnesia_change, tags = [ejabberdctl, mnesia], + desc = "Change the erlang node name in the mnesia database (only ejabberdctl)", + longdesc = "This command internally calls the _`mnesia_change_nodename`_ API. " + "This is a special command that starts and stops ejabberd several times: " + "do not attempt to run this command when ejabberd is running. " + "This command is exclusive for the ejabberdctl command-line script, " + "don't attempt to execute it using any other API frontend.", + note = "added in 25.08", + args = [{old_node_name, string}], + args_desc = ["Old erlang node name"], + args_example = ["ejabberd@oldmachine"]}, + #ejabberd_commands{name = mnesia_info_ctl, tags = [ejabberdctl, mnesia], + desc = "Show information of Mnesia system (only ejabberdctl)", + note = "renamed in 24.02", + longdesc = "This command is exclusive for the ejabberdctl command-line script, " + "don't attempt to execute it using any other API frontend."}, + #ejabberd_commands{name = print_sql_schema, tags = [ejabberdctl, sql], + desc = "Print SQL schema for the given RDBMS (only ejabberdctl)", + longdesc = "This command is exclusive for the ejabberdctl command-line script, " + "don't attempt to execute it using any other API frontend.", + note = "added in 24.02", + args = [{db_type, string}, {db_version, string}, {new_schema, string}], + args_desc = ["Database type: pgsql | mysql | sqlite", + "Your database version: 16.1, 8.2.0...", + "Use new schema: 0, false, 1 or true"], + args_example = ["pgsql", "16.1", "true"]} + ]. diff --git a/src/ejabberd_db_sup.erl b/src/ejabberd_db_sup.erl new file mode 100644 index 000000000..192c355c3 --- /dev/null +++ b/src/ejabberd_db_sup.erl @@ -0,0 +1,46 @@ +%%%------------------------------------------------------------------- +%%% Created : 13 June 2019 by Evgeny Khramtsov +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%------------------------------------------------------------------- +-module(ejabberd_db_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +%%%=================================================================== +%%% API functions +%%%=================================================================== +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%%%=================================================================== +%%% Supervisor callbacks +%%%=================================================================== +init([]) -> + {ok, {{one_for_one, 10, 1}, []}}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== diff --git a/src/ejabberd_doc.erl b/src/ejabberd_doc.erl new file mode 100644 index 000000000..c2bf33922 --- /dev/null +++ b/src/ejabberd_doc.erl @@ -0,0 +1,515 @@ +%%%---------------------------------------------------------------------- +%%% File : ejabberd_doc.erl +%%% Purpose : Options documentation generator +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- +-module(ejabberd_doc). + +%% API +-export([man/0, man/1, have_a2x/0]). + +-include("ejabberd_commands.hrl"). +-include("translate.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +man() -> + man(<<"en">>). + +man(Lang) when is_list(Lang) -> + man(list_to_binary(Lang)); +man(Lang) -> + {ModDoc, SubModDoc} = + lists:foldl( + fun(M, {Mods, SubMods} = Acc) -> + case lists:prefix("mod_", atom_to_list(M)) orelse + lists:prefix("Elixir.Mod", atom_to_list(M)) of + true -> + try M:mod_doc() of + #{desc := Descr} = Map -> + DocOpts = maps:get(opts, Map, []), + Example = maps:get(example, Map, []), + Note = maps:get(note, Map, []), + Apitags = get_module_apitags(M), + {[{M, Descr, DocOpts, #{example => Example, note => Note, apitags => Apitags}}|Mods], SubMods}; + #{opts := DocOpts} -> + {ParentMod, Backend} = strip_backend_suffix(M), + {Mods, dict:append(ParentMod, {M, Backend, DocOpts}, SubMods)}; + #{} -> + warn("module ~s is not properly documented", [M]), + Acc + catch _:undef -> + case erlang:function_exported( + M, mod_options, 1) of + true -> + warn("module ~s is not documented", [M]); + false -> + ok + end, + Acc + end; + false -> + Acc + end + end, {[], dict:new()}, ejabberd_config:beams(all)), + Doc = lists:flatmap( + fun(M) -> + try M:doc() + catch _:undef -> [] + end + end, ejabberd_config:callback_modules(all)), + Version = binary_to_list(ejabberd_config:version()), + Options = + ["TOP LEVEL OPTIONS", + "-----------------", + "This section describes top level options of ejabberd " ++ Version ++ ".", + "The options that changed in this version are marked with 🟤.", + io_lib:nl()] ++ + lists:flatmap( + fun(Opt) -> + opt_to_man(Lang, Opt, 1) + end, lists:keysort(1, Doc)), + ModDoc1 = lists:map( + fun({M, Descr, DocOpts, Ex}) -> + case dict:find(M, SubModDoc) of + {ok, Backends} -> + {M, Descr, DocOpts, Backends, Ex}; + error -> + {M, Descr, DocOpts, [], Ex} + end + end, ModDoc), + ModOptions = + [io_lib:nl(), + "MODULES", + "-------", + "[[modules]]", + "This section describes modules options of ejabberd " ++ Version ++ ".", + "The modules that changed in this version are marked with 🟤.", + io_lib:nl()] ++ + lists:flatmap( + fun({M, Descr, DocOpts, Backends, Example}) -> + ModName = atom_to_list(M), + VersionMark = get_version_mark(Example), + [io_lib:nl(), + lists:flatten([ModName, VersionMark]), + lists:duplicate(length(atom_to_list(M)), $~), + "[[" ++ ModName ++ "]]", + io_lib:nl()] ++ + format_versions(Lang, Example) ++ [io_lib:nl()] ++ + tr_multi(Lang, Descr) ++ [io_lib:nl()] ++ + opts_to_man(Lang, [{M, '', DocOpts}|Backends]) ++ + format_example(0, Lang, Example) ++ [io_lib:nl()] ++ + format_apitags(Lang, Example) + end, lists:keysort(1, ModDoc1)), + ListenOptions = + [io_lib:nl(), + "LISTENERS", + "-------", + "[[listeners]]", + "This section describes listeners options of ejabberd " ++ Version ++ ".", + io_lib:nl(), + "TODO"], + AsciiData = + [[unicode:characters_to_binary(Line), io_lib:nl()] + || Line <- man_header(Lang) ++ Options ++ [io_lib:nl()] + ++ ModOptions ++ ListenOptions ++ man_footer(Lang)], + warn_undocumented_modules(ModDoc1), + warn_undocumented_options(Doc), + write_man(AsciiData). + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +opts_to_man(Lang, [{_, _, []}]) -> + Text = tr(Lang, ?T("The module has no options.")), + [Text, io_lib:nl()]; +opts_to_man(Lang, Backends) -> + lists:flatmap( + fun({_, Backend, DocOpts}) when DocOpts /= [] -> + Text = if Backend == '' -> + tr(Lang, ?T("Available options")); + true -> + lists:flatten( + io_lib:format( + tr(Lang, ?T("Available options for '~s' backend")), + [Backend])) + end, + [Text ++ ":", lists:duplicate(length(Text)+1, $^)| + lists:flatmap( + fun(Opt) -> opt_to_man(Lang, Opt, 1) end, + lists:keysort(1, DocOpts))] ++ [io_lib:nl()]; + (_) -> + [] + end, Backends). + +opt_to_man(Lang, {Option, Options}, Level) -> + [format_option(Lang, Option, Options)|format_versions(Lang, Options)++format_desc(Lang, Options)] ++ + format_example(Level, Lang, Options); +opt_to_man(Lang, {Option, Options, Children}, Level) -> + [format_option(Lang, Option, Options)|format_desc(Lang, Options)] ++ + lists:append( + [[H ++ ":"|T] + || [H|T] <- lists:map( + fun(Opt) -> opt_to_man(Lang, Opt, Level+1) end, + lists:keysort(1, Children))]) ++ + [io_lib:nl()|format_example(Level, Lang, Options)]. + +get_version_mark(#{note := Note}) -> + [XX, YY | _] = string:tokens(binary_to_list(ejabberd_option:version()), "."), + XXYY = string:join([XX, YY], "."), + case string:find(Note, XXYY) of + nomatch -> ""; + _ -> " 🟤" + end; +get_version_mark(_) -> + "". + +format_option(Lang, Option, #{value := Val} = Options) -> + VersionMark = get_version_mark(Options), + "*" ++ atom_to_list(Option) ++ VersionMark ++ "*: 'pass:[" ++ + tr(Lang, Val) ++ "]'::"; +format_option(_Lang, Option, #{}) -> + "*" ++ atom_to_list(Option) ++ "*::". + +format_versions(_Lang, #{note := Note}) when Note /= [] -> + ["_Note_ about this option: " ++ Note ++ ". "]; +format_versions(_, _) -> + []. + +%% @format-begin +get_module_apitags(M) -> + AllCommands = ejabberd_commands:get_commands_definition(), + Tags = [C#ejabberd_commands.tags || C <- AllCommands, C#ejabberd_commands.module == M], + TagsClean = + lists:sort( + misc:lists_uniq( + lists:flatten(Tags))), + TagsStrings = [atom_to_list(C) || C <- TagsClean], + TagFiltering = + fun ("internal") -> + false; + ([$v | Rest]) -> + {error, no_integer} == string:to_integer(Rest); + (_) -> + true + end, + TagsFiltered = lists:filter(TagFiltering, TagsStrings), + TagsUrls = + [["_`../../developer/ejabberd-api/admin-tags.md#", C, "|", C, "`_"] || C <- TagsFiltered], + lists:join(", ", TagsUrls). + +format_apitags(_Lang, #{apitags := TagsString}) when TagsString /= "" -> + ["**API Tags:** ", TagsString]; +format_apitags(_, _) -> + []. +%% @format-end + +format_desc(Lang, #{desc := Desc}) -> + tr_multi(Lang, Desc). + +format_example(Level, Lang, #{example := [_|_] = Example}) -> + case lists:all(fun is_list/1, Example) of + true -> + if Level == 0 -> + ["*Example*:", + "^^^^^^^^^^"]; + true -> + ["+", "*Example*:", "+"] + end ++ format_yaml(Example); + false when Level == 0 -> + ["Examples:", + "^^^^^^^^^"] ++ + lists:flatmap( + fun({Text, Lines}) -> + [tr(Lang, Text)] ++ format_yaml(Lines) + end, Example); + false -> + lists:flatmap( + fun(Block) -> + ["+", "*Examples*:", "+"|Block] + end, + lists:map( + fun({Text, Lines}) -> + [tr(Lang, Text), "+"] ++ format_yaml(Lines) + end, Example)) + end; +format_example(_, _, _) -> + []. + +format_yaml(Lines) -> + ["==========================", + "[source,yaml]", + "----"|Lines] ++ + ["----", + "=========================="]. + +man_header(Lang) -> + ["ejabberd.yml(5)", + "===============", + ":doctype: manpage", + ":version: " ++ binary_to_list(ejabberd_config:version()), + io_lib:nl(), + "NAME", + "----", + "ejabberd.yml - " ++ tr(Lang, ?T("main configuration file for ejabberd.")), + io_lib:nl(), + "SYNOPSIS", + "--------", + "ejabberd.yml", + io_lib:nl(), + "DESCRIPTION", + "-----------", + tr(Lang, ?T("The configuration file is written in " + "https://en.wikipedia.org/wiki/YAML[YAML] language.")), + io_lib:nl(), + tr(Lang, ?T("WARNING: YAML is indentation sensitive, so make sure you respect " + "indentation, or otherwise you will get pretty cryptic " + "configuration errors.")), + io_lib:nl(), + tr(Lang, ?T("Logically, configuration options are split into 3 main categories: " + "'Modules', 'Listeners' and everything else called 'Top Level' options. " + "Thus this document is split into 3 main chapters describing each " + "category separately. So, the contents of ejabberd.yml will typically " + "look like this:")), + io_lib:nl(), + "==========================", + "[source,yaml]", + "----", + "hosts:", + " - example.com", + " - domain.tld", + "loglevel: info", + "...", + "listen:", + " -", + " port: 5222", + " module: ejabberd_c2s", + " ...", + "modules:", + " mod_roster: {}", + " ...", + "----", + "==========================", + io_lib:nl(), + tr(Lang, ?T("Any configuration error (such as syntax error, unknown option " + "or invalid option value) is fatal in the sense that ejabberd will " + "refuse to load the whole configuration file and will not start or will " + "abort configuration reload.")), + io_lib:nl(), + tr(Lang, ?T("All options can be changed in runtime by running 'ejabberdctl " + "reload-config' command. Configuration reload is atomic: either all options " + "are accepted and applied simultaneously or the new configuration is " + "refused without any impact on currently running configuration.")), + io_lib:nl(), + tr(Lang, ?T("Some options can be specified for particular virtual host(s) only " + "using 'host_config' or 'append_host_config' options. Such options " + "are called 'local'. Examples are 'modules', 'auth_method' and 'default_db'. " + "The options that cannot be defined per virtual host are called 'global'. " + "Examples are 'loglevel', 'certfiles' and 'listen'. It is a configuration " + "mistake to put 'global' options under 'host_config' or 'append_host_config' " + "section - ejabberd will refuse to load such configuration.")), + io_lib:nl(), + str:format( + tr(Lang, ?T("It is not recommended to write ejabberd.yml from scratch. Instead it is " + "better to start from \"default\" configuration file available at ~s. " + "Once you get ejabberd running you can start changing configuration " + "options to meet your requirements.")), + [default_config_url()]), + io_lib:nl(), + str:format( + tr(Lang, ?T("Note that this document is intended to provide comprehensive description of " + "all configuration options that can be consulted to understand the meaning " + "of a particular option, its format and possible values. It will be quite " + "hard to understand how to configure ejabberd by reading this document only " + "- for this purpose the reader is recommended to read online Configuration " + "Guide available at ~s.")), + [configuration_guide_url()]), + io_lib:nl()]. + +man_footer(Lang) -> + {Year, _, _} = date(), + [io_lib:nl(), + "AUTHOR", + "------", + "https://www.process-one.net[ProcessOne].", + io_lib:nl(), + "VERSION", + "-------", + str:format( + tr(Lang, ?T("This document describes the configuration file of ejabberd ~ts. " + "Configuration options of other ejabberd versions " + "may differ significantly.")), + [ejabberd_config:version()]), + io_lib:nl(), + "REPORTING BUGS", + "--------------", + tr(Lang, ?T("Report bugs to ")), + io_lib:nl(), + "SEE ALSO", + "---------", + tr(Lang, ?T("Default configuration file")) ++ ": " ++ default_config_url(), + io_lib:nl(), + tr(Lang, ?T("Main site")) ++ ": ", + io_lib:nl(), + tr(Lang, ?T("Documentation")) ++ ": ", + io_lib:nl(), + tr(Lang, ?T("Configuration Guide")) ++ ": " ++ configuration_guide_url(), + io_lib:nl(), + tr(Lang, ?T("Source code")) ++ ": ", + io_lib:nl(), + "COPYING", + "-------", + "Copyright (c) 2002-" ++ integer_to_list(Year) ++ + " https://www.process-one.net[ProcessOne]."]. + +tr(Lang, {Format, Args}) -> + unicode:characters_to_list( + str:format( + translate:translate(Lang, iolist_to_binary(Format)), + Args)); +tr(Lang, Txt) -> + unicode:characters_to_list(translate:translate(Lang, iolist_to_binary(Txt))). + +tr_multi(Lang, Txt) when is_binary(Txt) -> + tr_multi(Lang, [Txt]); +tr_multi(Lang, {Format, Args}) -> + tr_multi(Lang, [{Format, Args}]); +tr_multi(Lang, Lines) when is_list(Lines) -> + [tr(Lang, Txt) || Txt <- Lines]. + +write_man(AsciiData) -> + case file:get_cwd() of + {ok, Cwd} -> + AsciiDocFile = filename:join(Cwd, "ejabberd.yml.5.txt"), + ManPage = filename:join(Cwd, "ejabberd.yml.5"), + case file:write_file(AsciiDocFile, AsciiData) of + ok -> + Ret = run_a2x(Cwd, AsciiDocFile), + %%file:delete(AsciiDocFile), + case Ret of + ok -> + {ok, lists:flatten( + io_lib:format( + "The manpage saved as ~ts", [ManPage]))}; + {error, Error} -> + {error, lists:flatten( + io_lib:format( + "Failed to generate manpage: ~ts", [Error]))} + end; + {error, Reason} -> + {error, lists:flatten( + io_lib:format( + "Failed to write to ~ts: ~s", + [AsciiDocFile, file:format_error(Reason)]))} + end; + {error, Reason} -> + {error, lists:flatten( + io_lib:format("Failed to get current directory: ~s", + [file:format_error(Reason)]))} + end. + +have_a2x() -> + case os:find_executable("a2x") of + false -> false; + Path -> {true, Path} + end. + +run_a2x(Cwd, AsciiDocFile) -> + case have_a2x() of + false -> + {error, "a2x was not found: do you have 'asciidoc' installed?"}; + {true, Path} -> + Cmd = lists:flatten( + io_lib:format("~ts --no-xmllint -f manpage ~ts -D ~ts", + [Path, AsciiDocFile, Cwd])), + case os:cmd(Cmd) of + "" -> ok; + Ret -> {error, Ret} + end + end. + +warn_undocumented_modules(Docs) -> + lists:foreach( + fun({M, _, DocOpts, Backends, _}) -> + warn_undocumented_module(M, DocOpts), + lists:foreach( + fun({SubM, _, SubOpts}) -> + warn_undocumented_module(SubM, SubOpts) + end, Backends) + end, Docs). + +warn_undocumented_module(M, DocOpts) -> + try M:mod_options(ejabberd_config:get_myname()) of + Defaults -> + lists:foreach( + fun(OptDefault) -> + Opt = case OptDefault of + O when is_atom(O) -> O; + {O, _} -> O + end, + case lists:keymember(Opt, 1, DocOpts) of + false -> + warn("~s: option ~s is not documented", + [M, Opt]); + true -> + ok + end + end, Defaults) + catch _:undef -> + ok + end. + +warn_undocumented_options(Docs) -> + Opts = lists:flatmap( + fun(M) -> + try M:options() of + Defaults -> + lists:map( + fun({O, _}) -> O; + (O) when is_atom(O) -> O + end, Defaults) + catch _:undef -> + [] + end + end, ejabberd_config:callback_modules(all)), + lists:foreach( + fun(Opt) -> + case lists:keymember(Opt, 1, Docs) of + false -> + warn("option ~s is not documented", [Opt]); + true -> + ok + end + end, Opts). + +warn(Format, Args) -> + io:format(standard_error, "Warning: " ++ Format ++ "~n", Args). + +strip_backend_suffix(M) -> + [H|T] = lists:reverse(string:tokens(atom_to_list(M), "_")), + {list_to_atom(string:join(lists:reverse(T), "_")), list_to_atom(H)}. + +default_config_url() -> + "". + +configuration_guide_url() -> + "". diff --git a/src/ejabberd_frontend_socket.erl b/src/ejabberd_frontend_socket.erl deleted file mode 100644 index ab5b6a701..000000000 --- a/src/ejabberd_frontend_socket.erl +++ /dev/null @@ -1,261 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : ejabberd_frontend_socket.erl -%%% Author : Alexey Shchepin -%%% Purpose : Frontend socket with zlib and TLS support library -%%% Created : 23 Aug 2006 by Alexey Shchepin -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne -%%% -%%% This program is free software; you can redistribute it and/or -%%% modify it under the terms of the GNU General Public License as -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License along -%%% with this program; if not, write to the Free Software Foundation, Inc., -%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -%%% -%%%---------------------------------------------------------------------- - --module(ejabberd_frontend_socket). - --author('alexey@process-one.net'). - --behaviour(gen_server). - -%% API --export([start/4, - start_link/5, - %connect/3, - starttls/2, - starttls/3, - compress/1, - compress/2, - reset_stream/1, - send/2, - change_shaper/2, - monitor/1, - get_sockmod/1, - get_transport/1, - get_peer_certificate/1, - get_verify_result/1, - close/1, - sockname/1, peername/1]). - -%% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, - handle_info/2, terminate/2, code_change/3]). - --record(state, {sockmod, socket, receiver}). - --define(HIBERNATE_TIMEOUT, 90000). - -%%==================================================================== -%% API -%%==================================================================== -start_link(Module, SockMod, Socket, Opts, Receiver) -> - gen_server:start_link(?MODULE, - [Module, SockMod, Socket, Opts, Receiver], []). - -start(Module, SockMod, Socket, Opts) -> - case Module:socket_type() of - xml_stream -> - MaxStanzaSize = case lists:keysearch(max_stanza_size, 1, - Opts) - of - {value, {_, Size}} -> Size; - _ -> infinity - end, - Receiver = ejabberd_receiver:start(Socket, SockMod, - none, MaxStanzaSize), - case SockMod:controlling_process(Socket, Receiver) of - ok -> ok; - {error, _Reason} -> SockMod:close(Socket) - end, - supervisor:start_child(ejabberd_frontend_socket_sup, - [Module, SockMod, Socket, Opts, Receiver]); - raw -> - %{ok, Pid} = Module:start({SockMod, Socket}, Opts), - %case SockMod:controlling_process(Socket, Pid) of - % ok -> - % ok; - % {error, _Reason} -> - % SockMod:close(Socket) - %end - todo - end. - -starttls(FsmRef, _TLSOpts) -> - %% TODO: Frontend improvements planned by Aleksey - %%gen_server:call(FsmRef, {starttls, TLSOpts}), - FsmRef. - -starttls(FsmRef, TLSOpts, Data) -> - gen_server:call(FsmRef, {starttls, TLSOpts, Data}), - FsmRef. - -compress(FsmRef) -> compress(FsmRef, undefined). - -compress(FsmRef, Data) -> - gen_server:call(FsmRef, {compress, Data}), FsmRef. - -reset_stream(FsmRef) -> - gen_server:call(FsmRef, reset_stream). - -send(FsmRef, Data) -> - gen_server:call(FsmRef, {send, Data}). - -change_shaper(FsmRef, Shaper) -> - gen_server:call(FsmRef, {change_shaper, Shaper}). - -monitor(FsmRef) -> erlang:monitor(process, FsmRef). - -get_sockmod(FsmRef) -> - gen_server:call(FsmRef, get_sockmod). - -get_transport(FsmRef) -> - gen_server:call(FsmRef, get_transport). - -get_peer_certificate(FsmRef) -> - gen_server:call(FsmRef, get_peer_certificate). - -get_verify_result(FsmRef) -> - gen_server:call(FsmRef, get_verify_result). - -close(FsmRef) -> gen_server:call(FsmRef, close). - -sockname(FsmRef) -> gen_server:call(FsmRef, sockname). - -peername(_FsmRef) -> - %% TODO: Frontend improvements planned by Aleksey - %%gen_server:call(FsmRef, peername). - {ok, {{0, 0, 0, 0}, 0}}. - -%%==================================================================== -%% gen_server callbacks -%%==================================================================== - -init([Module, SockMod, Socket, Opts, Receiver]) -> - Node = ejabberd_node_groups:get_closest_node(backend), - {SockMod2, Socket2} = check_starttls(SockMod, Socket, Receiver, Opts), - {ok, Pid} = - rpc:call(Node, Module, start, [{?MODULE, self()}, Opts]), - ejabberd_receiver:become_controller(Receiver, Pid), - {ok, #state{sockmod = SockMod2, - socket = Socket2, - receiver = Receiver}}. - -handle_call({starttls, TLSOpts}, _From, State) -> - {ok, TLSSocket} = fast_tls:tcp_to_tls(State#state.socket, TLSOpts), - ejabberd_receiver:starttls(State#state.receiver, TLSSocket), - Reply = ok, - {reply, Reply, State#state{socket = TLSSocket, sockmod = fast_tls}, - ?HIBERNATE_TIMEOUT}; - -handle_call({starttls, TLSOpts, Data}, _From, State) -> - {ok, TLSSocket} = fast_tls:tcp_to_tls(State#state.socket, TLSOpts), - ejabberd_receiver:starttls(State#state.receiver, TLSSocket), - catch (State#state.sockmod):send( - State#state.socket, Data), - Reply = ok, - {reply, Reply, - State#state{socket = TLSSocket, sockmod = fast_tls}, - ?HIBERNATE_TIMEOUT}; -handle_call({compress, Data}, _From, State) -> - {ok, ZlibSocket} = - ejabberd_receiver:compress(State#state.receiver, Data), - Reply = ok, - {reply, Reply, - State#state{socket = ZlibSocket, sockmod = ezlib}, - ?HIBERNATE_TIMEOUT}; -handle_call(reset_stream, _From, State) -> - ejabberd_receiver:reset_stream(State#state.receiver), - Reply = ok, - {reply, Reply, State, ?HIBERNATE_TIMEOUT}; -handle_call({send, Data}, _From, State) -> - catch (State#state.sockmod):send(State#state.socket, Data), - Reply = ok, - {reply, Reply, State, ?HIBERNATE_TIMEOUT}; -handle_call({change_shaper, Shaper}, _From, State) -> - ejabberd_receiver:change_shaper(State#state.receiver, - Shaper), - Reply = ok, - {reply, Reply, State, ?HIBERNATE_TIMEOUT}; -handle_call(get_sockmod, _From, State) -> - Reply = State#state.sockmod, - {reply, Reply, State, ?HIBERNATE_TIMEOUT}; -handle_call(get_transport, _From, State) -> - Reply = case State#state.sockmod of - gen_tcp -> tcp; - fast_tls -> tls; - ezlib -> - case ezlib:get_sockmod(State#state.socket) of - tcp -> tcp_zlib; - tls -> tls_zlib - end; - ejabberd_http_bind -> http_bind; - ejabberd_http_ws -> websocket - end, - {reply, Reply, State, ?HIBERNATE_TIMEOUT}; -handle_call(get_peer_certificate, _From, State) -> - Reply = fast_tls:get_peer_certificate(State#state.socket), - {reply, Reply, State, ?HIBERNATE_TIMEOUT}; -handle_call(get_verify_result, _From, State) -> - Reply = fast_tls:get_verify_result(State#state.socket), - {reply, Reply, State, ?HIBERNATE_TIMEOUT}; -handle_call(close, _From, State) -> - ejabberd_receiver:close(State#state.receiver), - Reply = ok, - {stop, normal, Reply, State}; -handle_call(sockname, _From, State) -> - #state{sockmod = SockMod, socket = Socket} = State, - Reply = - case SockMod of - gen_tcp -> - inet:sockname(Socket); - _ -> - SockMod:sockname(Socket) - end, - {reply, Reply, State, ?HIBERNATE_TIMEOUT}; -handle_call(peername, _From, State) -> - #state{sockmod = SockMod, socket = Socket} = State, - Reply = case SockMod of - gen_tcp -> inet:peername(Socket); - _ -> SockMod:peername(Socket) - end, - {reply, Reply, State, ?HIBERNATE_TIMEOUT}; -handle_call(_Request, _From, State) -> - Reply = ok, {reply, Reply, State, ?HIBERNATE_TIMEOUT}. - -handle_cast(_Msg, State) -> - {noreply, State, ?HIBERNATE_TIMEOUT}. - -handle_info(timeout, State) -> - proc_lib:hibernate(gen_server, enter_loop, - [?MODULE, [], State]), - {noreply, State, ?HIBERNATE_TIMEOUT}; -handle_info(_Info, State) -> - {noreply, State, ?HIBERNATE_TIMEOUT}. - -terminate(_Reason, _State) -> ok. - -code_change(_OldVsn, State, _Extra) -> {ok, State}. - -check_starttls(SockMod, Socket, Receiver, Opts) -> - TLSEnabled = proplists:get_bool(tls, Opts), - TLSOpts = lists:filter(fun({certfile, _}) -> true; - (_) -> false - end, Opts), - if TLSEnabled -> - {ok, TLSSocket} = fast_tls:tcp_to_tls(Socket, TLSOpts), - ejabberd_receiver:starttls(Receiver, TLSSocket), - {fast_tls, TLSSocket}; - true -> - {SockMod, Socket} - end. diff --git a/src/ejabberd_hooks.erl b/src/ejabberd_hooks.erl index c1daa4c0e..8f378aa48 100644 --- a/src/ejabberd_hooks.erl +++ b/src/ejabberd_hooks.erl @@ -5,7 +5,7 @@ %%% Created : 8 Aug 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -22,10 +22,8 @@ %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- - -module(ejabberd_hooks). -author('alexey@process-one.net'). - -behaviour(gen_server). %% External exports @@ -33,21 +31,17 @@ add/3, add/4, add/5, - add_dist/5, - add_dist/6, delete/3, delete/4, delete/5, - delete_dist/5, - delete_dist/6, + subscribe/4, + subscribe/5, + unsubscribe/4, + unsubscribe/5, run/2, run/3, run_fold/3, - run_fold/4, - get_handlers/2]). - --export([delete_all_hooks/0]). - + run_fold/4]). %% gen_server callbacks -export([init/1, handle_call/3, @@ -56,25 +50,37 @@ handle_info/2, terminate/2]). + +-export( + [ + get_tracing_options/3, + trace_off/3, + trace_on/5,human_readable_time_string/1 + ] +). + -include("logger.hrl"). --record(state, {}). --type local_hook() :: { Seq :: integer(), Module :: atom(), Function :: atom()}. --type distributed_hook() :: { Seq :: integer(), Node :: atom(), Module :: atom(), Function :: atom()}. +-record(state, {}). +-type subscriber() :: {Module :: atom(), Function :: atom(), InitArg :: any()}. +-type subscriber_event() :: before | 'after' | before_callback | after_callback. +-type hook() :: {Seq :: integer(), Module :: atom(), Function :: atom() | fun()}. + +-define(TRACE_HOOK_KEY, '$trace_hook'). +-define(TIMING_KEY, '$trace_hook_timer'). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start_link() -> - gen_server:start_link({local, ejabberd_hooks}, ejabberd_hooks, [], []). - --spec add(atom(), fun(), number()) -> ok. + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). +-spec add(atom(), fun(), integer()) -> ok. %% @doc See add/4. add(Hook, Function, Seq) when is_function(Function) -> add(Hook, global, undefined, Function, Seq). --spec add(atom(), HostOrModule :: binary() | atom(), fun() | atom() , number()) -> ok. +-spec add(atom(), HostOrModule :: binary() | atom(), fun() | atom(), integer()) -> ok. add(Hook, Host, Function, Seq) when is_function(Function) -> add(Hook, Host, undefined, Function, Seq); @@ -83,29 +89,44 @@ add(Hook, Host, Function, Seq) when is_function(Function) -> add(Hook, Module, Function, Seq) -> add(Hook, global, Module, Function, Seq). --spec add(atom(), binary() | global, atom(), atom() | fun(), number()) -> ok. - +-spec add(atom(), binary() | global, atom(), atom() | fun(), integer()) -> ok. add(Hook, Host, Module, Function, Seq) -> - gen_server:call(ejabberd_hooks, {add, Hook, Host, Module, Function, Seq}). + gen_server:call(?MODULE, {add, Hook, Host, Module, Function, Seq}). --spec add_dist(atom(), atom(), atom(), atom() | fun(), number()) -> ok. +-spec subscribe(atom(), atom(), atom(), any()) -> ok. +%% @doc Add a subscriber to this hook. +%% +%% Before running any hook callback, the subscriber will be called in form of +%% Module:Function(InitArg, 'before', Host :: binary() | global, Hook, HookArgs) +%% Above function should return new state. +%% +%% Before running each callback, the subscriber will be called in form of +%% Module:Function(State, 'before_callback', Host :: binary() | global, Hook, {CallbackMod, CallbackArg, Seq, HookArgs}) +%% Above function should return new state. +%% +%% After running each callback, the subscriber will be called in form of +%% Module:Function(State, 'after_callback', Host :: binary() | global, Hook, {CallbackMod, CallbackArg, Seq, HookArgs}) +%% Above function should return new state. +%% +%% After running any hook callback, the subscriber will be called in form of +%% Module:Function(State, 'after', Host :: binary() | global, Hook, HookArgs) +%% Return value of this function call will be dropped. +%% +%% For every ejabberd_hooks:[run|run_fold] for every subscriber above functions will be called and the hook runner +%% maintains State in above four calls. +subscribe(Hook, Module, Function, InitArg) -> + subscribe(Hook, global, Module, Function, InitArg). -add_dist(Hook, Node, Module, Function, Seq) -> - gen_server:call(ejabberd_hooks, {add, Hook, global, Node, Module, Function, Seq}). - --spec add_dist(atom(), binary() | global, atom(), atom(), atom() | fun(), number()) -> ok. - -add_dist(Hook, Host, Node, Module, Function, Seq) -> - gen_server:call(ejabberd_hooks, {add, Hook, Host, Node, Module, Function, Seq}). - --spec delete(atom(), fun(), number()) -> ok. +-spec subscribe(atom(), binary() | global, atom(), atom(), any()) -> ok. +subscribe(Hook, Host, Module, Function, InitArg) -> + gen_server:call(?MODULE, {subscribe, Hook, Host, Module, Function, InitArg}). +-spec delete(atom(), fun(), integer()) -> ok. %% @doc See del/4. delete(Hook, Function, Seq) when is_function(Function) -> delete(Hook, global, undefined, Function, Seq). --spec delete(atom(), binary() | atom(), atom() | fun(), number()) -> ok. - +-spec delete(atom(), binary() | atom(), atom() | fun(), integer()) -> ok. delete(Hook, Host, Function, Seq) when is_function(Function) -> delete(Hook, Host, undefined, Function, Seq); @@ -114,181 +135,244 @@ delete(Hook, Host, Function, Seq) when is_function(Function) -> delete(Hook, Module, Function, Seq) -> delete(Hook, global, Module, Function, Seq). --spec delete(atom(), binary() | global, atom(), atom() | fun(), number()) -> ok. - +-spec delete(atom(), binary() | global, atom(), atom() | fun(), integer()) -> ok. delete(Hook, Host, Module, Function, Seq) -> - gen_server:call(ejabberd_hooks, {delete, Hook, Host, Module, Function, Seq}). + gen_server:call(?MODULE, {delete, Hook, Host, Module, Function, Seq}). --spec delete_dist(atom(), atom(), atom(), atom() | fun(), number()) -> ok. -delete_dist(Hook, Node, Module, Function, Seq) -> - delete_dist(Hook, global, Node, Module, Function, Seq). --spec delete_dist(atom(), binary() | global, atom(), atom(), atom() | fun(), number()) -> ok. +-spec unsubscribe(atom(), atom(), atom(), any()) -> ok. +%% @doc Removes a subscriber from this hook. +unsubscribe(Hook, Module, Function, InitArg) -> + unsubscribe(Hook, global, Module, Function, InitArg). -delete_dist(Hook, Host, Node, Module, Function, Seq) -> - gen_server:call(ejabberd_hooks, {delete, Hook, Host, Node, Module, Function, Seq}). +-spec unsubscribe(atom(), binary() | global, atom(), atom(), any()) -> ok. +unsubscribe(Hook, Host, Module, Function, InitArg) -> + gen_server:call(?MODULE, {unsubscribe, Hook, Host, Module, Function, InitArg}). --spec delete_all_hooks() -> true. - -%% @doc Primarily for testing / instrumentation -delete_all_hooks() -> - gen_server:call(ejabberd_hooks, {delete_all}). - --spec get_handlers(atom(), binary() | global) -> [local_hook() | distributed_hook()]. -%% @doc Returns currently set handler for hook name -get_handlers(Hookname, Host) -> - gen_server:call(ejabberd_hooks, {get_handlers, Hookname, Host}). -spec run(atom(), list()) -> ok. - -%% @doc Run the calls of this hook in order, don't care about function results. +%% @doc Run the calls (and subscribers) of this hook in order, don't care about function results. %% If a call returns stop, no more calls are performed. run(Hook, Args) -> run(Hook, global, Args). -spec run(atom(), binary() | global, list()) -> ok. - run(Hook, Host, Args) -> - case ets:lookup(hooks, {Hook, Host}) of - [{_, Ls}] -> - run1(Ls, Hook, Args); + try ets:lookup(hooks, {Hook, Host}) of + [{_, Ls, Subs}] -> + case erlang:get(?TRACE_HOOK_KEY) of + undefined when Subs == [] -> + run1(Ls, Hook, Args); + undefined -> + Subs2 = call_subscriber_list(Subs, Host, Hook, Args, before, []), + Subs3 = run1(Ls, Hook, Args, Host, Subs2), + _Subs4 = call_subscriber_list(Subs3, Host, Hook, Args, 'after', []), + ok; + TracingHooksOpts -> + case do_get_tracing_options(Hook, Host, TracingHooksOpts) of + undefined -> + Subs2 = call_subscriber_list(Subs, Host, Hook, Args, before, []), + Subs3 = run1(Ls, Hook, Args, Host, Subs2), + _Subs4 = call_subscriber_list(Subs3, Host, Hook, Args, 'after', []), + ok; + TracingOpts -> + foreach_start_hook_tracing(TracingOpts, Hook, Host, Args), + Subs2 = call_subscriber_list(Subs, Host, Hook, Args, before, []), + Subs3 = run2(Ls, Hook, Args, Host, TracingOpts, Subs2), + _Subs4 = call_subscriber_list(Subs3, Host, Hook, Args, 'after', []), + ok + end + end; [] -> ok + catch _:badarg -> + ok end. --spec run_fold(atom(), any(), list()) -> any(). - -%% @doc Run the calls of this hook in order. +-spec run_fold(atom(), T, list()) -> T. +%% @doc Run the calls (and subscribers) of this hook in order. %% The arguments passed to the function are: [Val | Args]. %% The result of a call is used as Val for the next call. -%% If a call returns 'stop', no more calls are performed and 'stopped' is returned. +%% If a call returns 'stop', no more calls are performed. %% If a call returns {stop, NewVal}, no more calls are performed and NewVal is returned. run_fold(Hook, Val, Args) -> run_fold(Hook, global, Val, Args). --spec run_fold(atom(), binary() | global, any(), list()) -> any(). - +-spec run_fold(atom(), binary() | global, T, list()) -> T. run_fold(Hook, Host, Val, Args) -> - case ets:lookup(hooks, {Hook, Host}) of - [{_, Ls}] -> - run_fold1(Ls, Hook, Val, Args); + try ets:lookup(hooks, {Hook, Host}) of + [{_, Ls, Subs}] -> + case erlang:get(?TRACE_HOOK_KEY) of + undefined when Subs == [] -> + run_fold1(Ls, Hook, Val, Args); + undefined -> + Subs2 = call_subscriber_list(Subs, Host, Hook, [Val | Args], before, []), + {Val2, Subs3} = run_fold1(Ls, Hook, Val, Args, Host, Subs2), + _Subs4 = call_subscriber_list(Subs3, Host, Hook, [Val2 | Args], 'after', []), + Val2; + TracingHooksOpts -> + case do_get_tracing_options(Hook, Host, TracingHooksOpts) of + undefined -> + Subs2 = call_subscriber_list(Subs, Host, Hook, [Val | Args], before, []), + {Val2, Subs3} = run_fold1(Ls, Hook, Val, Args, Host, Subs2), + _Subs4 = call_subscriber_list(Subs3, Host, Hook, [Val2 | Args], 'after', []), + Val2; + TracingOpts -> + fold_start_hook_tracing(TracingOpts, Hook, Host, [Val | Args]), + Subs2 = call_subscriber_list(Subs, Host, Hook, [Val | Args], before, []), + {Val2, Subs3} = run_fold2(Ls, Hook, Val, Args, Host, TracingOpts, Subs2), + _Subs4 = call_subscriber_list(Subs3, Host, Hook, [Val2 | Args], 'after', []), + Val2 + end + end; [] -> Val + catch _:badarg -> + Val + end. + +get_tracing_options(Hook, Host, Pid) when Pid == erlang:self() -> + do_get_tracing_options(Hook, Host, erlang:get(?TRACE_HOOK_KEY)); +get_tracing_options(Hook, Host, Pid) when erlang:is_pid(Pid) -> + case erlang:process_info(Pid, dictionary) of + {_, DictPropList} -> + case lists:keyfind(?TRACE_HOOK_KEY, 1, DictPropList) of + {_, TracingHooksOpts} -> + do_get_tracing_options(Hook, Host, TracingHooksOpts); + _ -> + undefined + end; + _ -> + undefined + end. + +trace_on(Hook, Host, Pid, #{}=Opts, Timeout) when Pid == erlang:self() -> + do_trace_on(Hook, Host, Opts, Timeout); +trace_on(Hook, Host, Proc, #{}=Opts, Timeout) -> + try sys:replace_state( + Proc, + fun(State) -> + do_trace_on(Hook, Host, Opts, Timeout), + State + end, + 15000 + ) of + _ -> % process state + ok + catch + _:Reason -> + {error, Reason} + end. + +trace_off(Hook, Host, Pid) when Pid == erlang:self() -> + do_trace_off(Hook, Host); +trace_off(Hook, Host, Proc) -> + try sys:replace_state( + Proc, + fun(State) -> + do_trace_off(Hook, Host), + State + end, + 15000 + ) of + _ -> % process state + ok + catch + _:Reason -> + {error, Reason} end. %%%---------------------------------------------------------------------- %%% Callback functions from gen_server %%%---------------------------------------------------------------------- - -%%---------------------------------------------------------------------- -%% Func: init/1 -%% Returns: {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} -%%---------------------------------------------------------------------- init([]) -> - ets:new(hooks, [named_table]), + _ = ets:new(hooks, [named_table, {read_concurrency, true}]), {ok, #state{}}. -%%---------------------------------------------------------------------- -%% Func: handle_call/3 -%% Returns: {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | (terminate/2 is called) -%% {stop, Reason, State} (terminate/2 is called) -%%---------------------------------------------------------------------- handle_call({add, Hook, Host, Module, Function, Seq}, _From, State) -> HookFormat = {Seq, Module, Function}, Reply = handle_add(Hook, Host, HookFormat), {reply, Reply, State}; -handle_call({add, Hook, Host, Node, Module, Function, Seq}, _From, State) -> - HookFormat = {Seq, Node, Module, Function}, - Reply = handle_add(Hook, Host, HookFormat), - {reply, Reply, State}; - handle_call({delete, Hook, Host, Module, Function, Seq}, _From, State) -> HookFormat = {Seq, Module, Function}, Reply = handle_delete(Hook, Host, HookFormat), {reply, Reply, State}; -handle_call({delete, Hook, Host, Node, Module, Function, Seq}, _From, State) -> - HookFormat = {Seq, Node, Module, Function}, - Reply = handle_delete(Hook, Host, HookFormat), +handle_call({subscribe, Hook, Host, Module, Function, InitArg}, _From, State) -> + SubscriberFormat = {Module, Function, InitArg}, + Reply = handle_subscribe(Hook, Host, SubscriberFormat), {reply, Reply, State}; - -handle_call({get_handlers, Hook, Host}, _From, State) -> - Reply = case ets:lookup(hooks, {Hook, Host}) of - [{_, Handlers}] -> Handlers; - [] -> [] - end, +handle_call({unsubscribe, Hook, Host, Module, Function, InitArg}, _From, State) -> + SubscriberFormat = {Module, Function, InitArg}, + Reply = handle_unsubscribe(Hook, Host, SubscriberFormat), {reply, Reply, State}; +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), + {noreply, State}. -handle_call({delete_all}, _From, State) -> - Reply = ets:delete_all_objects(hooks), - {reply, Reply, State}; - -handle_call(_Request, _From, State) -> - Reply = ok, - {reply, Reply, State}. - --spec handle_add(atom(), atom(), local_hook() | distributed_hook()) -> ok. -%% in-memory storage operation: Handle adding hook in ETS table +-spec handle_add(atom(), atom(), hook()) -> ok. handle_add(Hook, Host, El) -> case ets:lookup(hooks, {Hook, Host}) of - [{_, Ls}] -> + [{_, Ls, Subs}] -> case lists:member(El, Ls) of true -> ok; false -> NewLs = lists:merge(Ls, [El]), - ets:insert(hooks, {{Hook, Host}, NewLs}), + ets:insert(hooks, {{Hook, Host}, NewLs, Subs}), ok end; [] -> NewLs = [El], - ets:insert(hooks, {{Hook, Host}, NewLs}), + ets:insert(hooks, {{Hook, Host}, NewLs, []}), ok end. - --spec handle_delete(atom(), atom(), local_hook() | distributed_hook()) -> ok. -%% in-memory storage operation: Handle deleting hook from ETS table +-spec handle_delete(atom(), atom(), hook()) -> ok. handle_delete(Hook, Host, El) -> case ets:lookup(hooks, {Hook, Host}) of - [{_, Ls}] -> + [{_, Ls, Subs}] -> NewLs = lists:delete(El, Ls), - ets:insert(hooks, {{Hook, Host}, NewLs}), + ets:insert(hooks, {{Hook, Host}, NewLs, Subs}), ok; [] -> ok - end. + end. -%%---------------------------------------------------------------------- -%% Func: handle_cast/2 -%% Returns: {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} (terminate/2 is called) -%%---------------------------------------------------------------------- -handle_cast(_Msg, State) -> +-spec handle_subscribe(atom(), atom(), subscriber()) -> ok. +handle_subscribe(Hook, Host, El) -> + case ets:lookup(hooks, {Hook, Host}) of + [{_, Ls, Subs}] -> + case lists:member(El, Subs) of + true -> + ok; + false -> + ets:insert(hooks, {{Hook, Host}, Ls, Subs ++ [El]}), + ok + end; + [] -> + ets:insert(hooks, {{Hook, Host}, [], [El]}), + ok + end. + +-spec handle_unsubscribe(atom(), atom(), subscriber()) -> ok. +handle_unsubscribe(Hook, Host, El) -> + case ets:lookup(hooks, {Hook, Host}) of + [{_, Ls, Subs}] -> + ets:insert(hooks, {{Hook, Host}, Ls, lists:delete(El, Subs)}), + ok; + [] -> + ok + end. + +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), {noreply, State}. -%%---------------------------------------------------------------------- -%% Func: handle_info/2 -%% Returns: {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} (terminate/2 is called) -%%---------------------------------------------------------------------- -handle_info(_Info, State) -> +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), {noreply, State}. -%%---------------------------------------------------------------------- -%% Func: terminate/2 -%% Purpose: Shutdown the server -%% Returns: any (ignored by gen_server) -%%---------------------------------------------------------------------- terminate(_Reason, _State) -> ok. @@ -298,38 +382,13 @@ code_change(_OldVsn, State, _Extra) -> %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- - --spec run1([local_hook()|distributed_hook()], atom(), list()) -> ok. - +-spec run1([hook()], atom(), list()) -> ok. run1([], _Hook, _Args) -> ok; -%% Run distributed hook on target node. -%% It is not attempted again in case of failure. Next hook will be executed -run1([{_Seq, Node, Module, Function} | Ls], Hook, Args) -> - %% MR: Should we have a safe rpc, like we have a safe apply or is bad_rpc enough ? - case ejabberd_cluster:call(Node, Module, Function, Args) of - timeout -> - ?ERROR_MSG("Timeout on RPC to ~p~nrunning hook: ~p", - [Node, {Hook, Args}]), - run1(Ls, Hook, Args); - {badrpc, Reason} -> - ?ERROR_MSG("Bad RPC error to ~p: ~p~nrunning hook: ~p", - [Node, Reason, {Hook, Args}]), - run1(Ls, Hook, Args); - stop -> - ?INFO_MSG("~nThe process ~p in node ~p ran a hook in node ~p.~n" - "Stop.", [self(), node(), Node]), % debug code - ok; - Res -> - ?INFO_MSG("~nThe process ~p in node ~p ran a hook in node ~p.~n" - "The response is:~n~s", [self(), node(), Node, Res]), % debug code - run1(Ls, Hook, Args) - end; run1([{_Seq, Module, Function} | Ls], Hook, Args) -> - Res = safe_apply(Module, Function, Args), + Res = safe_apply(Hook, Module, Function, Args), case Res of - {'EXIT', Reason} -> - ?ERROR_MSG("~p~nrunning hook: ~p", [Reason, {Hook, Args}]), + 'EXIT' -> run1(Ls, Hook, Args); stop -> ok; @@ -337,47 +396,527 @@ run1([{_Seq, Module, Function} | Ls], Hook, Args) -> run1(Ls, Hook, Args) end. - +-spec run_fold1([hook()], atom(), T, list()) -> T. run_fold1([], _Hook, Val, _Args) -> Val; -run_fold1([{_Seq, Node, Module, Function} | Ls], Hook, Val, Args) -> - case ejabberd_cluster:call(Node, Module, Function, [Val | Args]) of - {badrpc, Reason} -> - ?ERROR_MSG("Bad RPC error to ~p: ~p~nrunning hook: ~p", - [Node, Reason, {Hook, Args}]), - run_fold1(Ls, Hook, Val, Args); - timeout -> - ?ERROR_MSG("Timeout on RPC to ~p~nrunning hook: ~p", - [Node, {Hook, Args}]), - run_fold1(Ls, Hook, Val, Args); - stop -> - stopped; - {stop, NewVal} -> - ?INFO_MSG("~nThe process ~p in node ~p ran a hook in node ~p.~n" - "Stop, and the NewVal is:~n~p", [self(), node(), Node, NewVal]), % debug code - NewVal; - NewVal -> - ?INFO_MSG("~nThe process ~p in node ~p ran a hook in node ~p.~n" - "The NewVal is:~n~p", [self(), node(), Node, NewVal]), % debug code - run_fold1(Ls, Hook, NewVal, Args) - end; run_fold1([{_Seq, Module, Function} | Ls], Hook, Val, Args) -> - Res = safe_apply(Module, Function, [Val | Args]), + Res = safe_apply(Hook, Module, Function, [Val | Args]), case Res of - {'EXIT', Reason} -> - ?ERROR_MSG("~p~nrunning hook: ~p", [Reason, {Hook, Args}]), + 'EXIT' -> run_fold1(Ls, Hook, Val, Args); stop -> - stopped; + Val; {stop, NewVal} -> NewVal; NewVal -> run_fold1(Ls, Hook, NewVal, Args) end. -safe_apply(Module, Function, Args) -> - if is_function(Function) -> - catch apply(Function, Args); - true -> - catch apply(Module, Function, Args) +-spec run1([hook()], atom(), list(), binary() | global, [subscriber()]) -> [subscriber()]. +run1([], _Hook, _Args, _Host, SubscriberList) -> + SubscriberList; +run1([{Seq, Module, Function} | Ls], Hook, Args, Host, SubscriberList) -> + SubscriberList2 = call_subscriber_list(SubscriberList, Host, Hook, {Module, Function, Seq, Args}, before_callback, []), + Res = safe_apply(Hook, Module, Function, Args), + SubscriberList3 = call_subscriber_list(SubscriberList2, Host, Hook, {Module, Function, Seq, Args}, after_callback, []), + case Res of + 'EXIT' -> + run1(Ls, Hook, Args, Host, SubscriberList3); + stop -> + SubscriberList3; + _ -> + run1(Ls, Hook, Args, Host, SubscriberList3) end. + +-spec run_fold1([hook()], atom(), T, list(), binary() | global, [subscriber()]) -> {T, [subscriber()]}. +run_fold1([], _Hook, Val, _Args, _Host, SubscriberList) -> + {Val, SubscriberList}; +run_fold1([{Seq, Module, Function} | Ls], Hook, Val, Args, Host, SubscriberList) -> + SubscriberList2 = call_subscriber_list(SubscriberList, Host, Hook, {Module, Function, Seq, [Val | Args]}, before_callback, []), + Res = safe_apply(Hook, Module, Function, [Val | Args]), + SubscriberList3 = call_subscriber_list(SubscriberList2, Host, Hook, {Module, Function, Seq, [Val | Args]}, after_callback, []), + case Res of + 'EXIT' -> + run_fold1(Ls, Hook, Val, Args, Host, SubscriberList3); + stop -> + {Val, SubscriberList3}; + {stop, NewVal} -> + {NewVal, SubscriberList3}; + NewVal -> + run_fold1(Ls, Hook, NewVal, Args, Host, SubscriberList3) + end. + +-spec safe_apply(atom(), atom(), atom() | fun(), list()) -> any(). +safe_apply(Hook, Module, Function, Args) -> + ?DEBUG("Running hook ~p: ~p:~p/~B", + [Hook, Module, Function, length(Args)]), + try if is_function(Function) -> + apply(Function, Args); + true -> + apply(Module, Function, Args) + end + catch + E:R:Stack when E /= exit; R /= normal -> + ?ERROR_MSG("Hook ~p crashed when running ~p:~p/~p:~n" ++ + string:join( + ["** ~ts" | [ "** Arg " ++ integer_to_list(I) ++ " = ~p" + || I <- lists:seq(1, length(Args)) ]], + "~n"), + [Hook, + Module, + Function, + length(Args), + misc:format_exception(2, E, R, Stack) | Args]), + 'EXIT' + end. + +-spec call_subscriber_list([subscriber()], binary() | global, atom(), {atom(), atom(), integer(), list()} | list(), subscriber_event(), [subscriber()]) -> any(). +call_subscriber_list([], _Host, _Hook, _CallbackOrArgs, _Event, []) -> + []; +call_subscriber_list([], _Host, _Hook, _CallbackOrArgs, _Event, Result) -> + lists:reverse(Result); +call_subscriber_list([{Mod, Func, InitArg} | SubscriberList], Host, Hook, CallbackOrArgs, Event, Result) -> + SubscriberArgs = [InitArg, Event, Host, Hook, CallbackOrArgs], + ?DEBUG("Running hook subscriber ~p: ~p:~p/~B with event ~p", + [Hook, Mod, Func, length(SubscriberArgs), Event]), + try apply(Mod, Func, SubscriberArgs) of + State -> + call_subscriber_list(SubscriberList, Host, Hook, CallbackOrArgs, Event, [{Mod, Func, State} | Result]) + catch + E:R:Stack when E /= exit; R /= normal -> + ?ERROR_MSG("Hook subscriber ~p crashed when running ~p:~p/~p:~n" ++ + string:join( + ["** ~ts" | [ "** Arg " ++ integer_to_list(I) ++ " = ~p" + || I <- lists:seq(1, length(SubscriberArgs)) ]], + "~n"), + [Hook, + Mod, + Func, + length(SubscriberArgs), + misc:format_exception(2, E, R, Stack) | SubscriberArgs]), + %% Do not append subscriber for next calls: + call_subscriber_list(SubscriberList, Host, Hook, CallbackOrArgs, Event, Result) + end. + +%%%---------------------------------------------------------------------- +%%% Internal tracing functions +%%%---------------------------------------------------------------------- + +do_trace_on(Hook, Host, Opts, Timeout) when erlang:is_list(Host) -> + do_trace_on(Hook, erlang:list_to_binary(Host), Opts, Timeout); +do_trace_on(Hook, Host, Opts, undefined) -> + case erlang:get(?TRACE_HOOK_KEY) of + _ when Hook == all andalso Host == <<"*">> -> + % Trace everything: + erlang:put(?TRACE_HOOK_KEY, #{all => #{<<"*">> => Opts}}); + #{all := #{<<"*">> := _}} -> % Already tracing everything + % Update Opts: + erlang:put(?TRACE_HOOK_KEY, #{all => #{<<"*">> => Opts}}); + #{all := HostOpts} when Hook == all -> % Already Tracing everything for some hosts + % Add/Update Host and Opts: + erlang:put(?TRACE_HOOK_KEY, #{all => HostOpts#{Host => Opts}}); + #{all := _} -> % Already tracing everything and Hook is not all + ok; + #{} when Hook == all -> + % Remove other hooks by just adding all: + erlang:put(?TRACE_HOOK_KEY, #{all => #{Host => Opts}}); + #{}=TraceHooksOpts when Host == <<"*">> -> % Want to trace a hook for all hosts + erlang:put(?TRACE_HOOK_KEY, TraceHooksOpts#{Hook => #{Host => Opts}}); + #{}=TraceHooksOpts -> + case maps:get(Hook, TraceHooksOpts, #{}) of + #{<<"*">> := _} -> % Already tracing this hook for all hosts + ok; + HostOpts -> + erlang:put(?TRACE_HOOK_KEY, TraceHooksOpts#{Hook => HostOpts#{Host => Opts}}) + end; + undefined -> + erlang:put(?TRACE_HOOK_KEY, #{Hook => #{Host => Opts}}) + end, + ok; +do_trace_on(Hook, Host, Opts, TimeoutSeconds) -> % Trace myself `Timeout` time + Timeout = timer:seconds(TimeoutSeconds), + ParentPid = erlang:self(), + try erlang:spawn( + fun() -> + MonitorRef = erlang:monitor(process, ParentPid), + receive + {_, MonitorRef, _, _, _} -> + ok + after Timeout -> + trace_off(Hook, Host, ParentPid) + end, + erlang:exit(normal) + end + ) of + _ -> + do_trace_on(Hook, Host, Opts, undefined) % ok + catch + _:Reason -> % system_limit + {error, Reason} + end. + +do_trace_off(Hook, Host) when erlang:is_list(Host) -> + do_trace_off(Hook, erlang:list_to_binary(Host)); +do_trace_off(Hook, Host) -> + case erlang:get(?TRACE_HOOK_KEY) of + _ when Hook == all andalso Host == <<"*">> -> + % Remove all tracing: + erlang:erase(?TRACE_HOOK_KEY); + #{all := HostOpts} when Hook == all -> % Already tracing all hooks + % Remove Host: + HostOpts2 = maps:remove(Host, HostOpts), + if + HostOpts2 == #{} -> + % Remove all tracing: + erlang:erase(?TRACE_HOOK_KEY); + true -> + erlang:put(?TRACE_HOOK_KEY, #{all => HostOpts2}) + end; + #{}=TraceHooksOpts when Host == <<"*">> -> + % Remove tracing of this hook for all hosts: + TraceHooksOpts2 = maps:remove(Hook, TraceHooksOpts), + if + TraceHooksOpts2 == #{} -> + % Remove all tracing: + erlang:erase(?TRACE_HOOK_KEY); + true -> + erlang:put(?TRACE_HOOK_KEY, TraceHooksOpts2) + end; + #{}=TraceHooksOpts -> + case maps:get(Hook, TraceHooksOpts, undefined) of + #{}=HostOpts -> + NewHostOpts = maps:remove(Host, HostOpts), + if + NewHostOpts == #{} -> + % Remove hook: + erlang:put(?TRACE_HOOK_KEY, maps:remove(Hook, TraceHooksOpts)); + true -> + erlang:put(?TRACE_HOOK_KEY, TraceHooksOpts#{Hook => NewHostOpts}) + end; + _ -> + ok + end; + undefined -> + ok + end, + ok. + +do_get_tracing_options(Hook, Host, MaybeMap) -> + case MaybeMap of + undefined -> + undefined; + #{all := #{<<"*">> := Opts}} -> % Tracing everything + Opts; + #{all := HostOpts} -> % Tracing all hooks for some hosts + maps:get(Host, HostOpts, undefined); + #{}=TraceHooksOpts -> + HostOpts = maps:get(Hook, TraceHooksOpts, #{}), + case maps:get(Host, HostOpts, undefined) of + undefined -> + maps:get(<<"*">>, HostOpts, undefined); + Opts -> + Opts + end + end. + +run2([], Hook, Args, Host, Opts, SubscriberList) -> + foreach_stop_hook_tracing(Opts, Hook, Host, Args, undefined), + SubscriberList; +run2([{Seq, Module, Function} | Ls], Hook, Args, Host, TracingOpts, SubscriberList) -> + foreach_start_callback_tracing(TracingOpts, Hook, Host, Module, Function, Args, Seq), + SubscriberList2 = call_subscriber_list(SubscriberList, Host, Hook, {Module, Function, Seq, Args}, before_callback, []), + Res = safe_apply(Hook, Module, Function, Args), + SubscriberList3 = call_subscriber_list(SubscriberList2, Host, Hook, {Module, Function, Seq, Args}, after_callback, []), + foreach_stop_callback_tracing(TracingOpts, Hook, Host, Module, Function, Args, Seq, Res), + case Res of + 'EXIT' -> + run2(Ls, Hook, Args, Host, TracingOpts, SubscriberList3); + stop -> + foreach_stop_hook_tracing(TracingOpts, Hook, Host, Args, {Module, Function, Seq, Ls}), + SubscriberList3; + _ -> + run2(Ls, Hook, Args, Host, TracingOpts, SubscriberList3) + end. + +run_fold2([], Hook, Val, Args, Host, Opts, SubscriberList) -> + fold_stop_hook_tracing(Opts, Hook, Host, [Val | Args], undefined), + {Val, SubscriberList}; +run_fold2([{Seq, Module, Function} | Ls], Hook, Val, Args, Host, TracingOpts, SubscriberList) -> + fold_start_callback_tracing(TracingOpts, Hook, Host, Module, Function, [Val | Args], Seq), + SubscriberList2 = call_subscriber_list(SubscriberList, Host, Hook, {Module, Function, Seq, [Val | Args]}, before_callback, []), + Res = safe_apply(Hook, Module, Function, [Val | Args]), + SubscriberList3 = call_subscriber_list(SubscriberList2, Host, Hook, {Module, Function, Seq, [Val | Args]}, after_callback, []), + fold_stop_callback_tracing(TracingOpts, Hook, Host, Module, Function, [Val | Args], Seq, Res), + case Res of + 'EXIT' -> + run_fold2(Ls, Hook, Val, Args, Host, TracingOpts, SubscriberList3); + stop -> + fold_stop_hook_tracing(TracingOpts, Hook, Host, [Val | Args], {Module, Function, Seq, {old, Val}, Ls}), + {Val, SubscriberList3}; + {stop, NewVal} -> + fold_stop_hook_tracing(TracingOpts, Hook, Host, [Val | Args], {Module, Function, Seq, {new, NewVal}, Ls}), + {NewVal, SubscriberList3}; + NewVal -> + run_fold2(Ls, Hook, NewVal, Args, Host, TracingOpts, SubscriberList3) + end. + +foreach_start_hook_tracing(TracingOpts, Hook, Host, Args) -> + run_event_handlers(TracingOpts, Hook, Host, start_hook, [Args], foreach). + +foreach_stop_hook_tracing(TracingOpts, Hook, Host, Args, BreakCallback) -> + run_event_handlers(TracingOpts, Hook, Host, stop_hook, [Args, BreakCallback], foreach). + +foreach_start_callback_tracing(TracingOpts, Hook, Host, Mod, Func, Args, Seq) -> + run_event_handlers(TracingOpts, Hook, Host, start_callback, [Mod, Func, Args, Seq], foreach). + +foreach_stop_callback_tracing(TracingOpts, Hook, Host, Mod, Func, Args, Seq, Res) -> + run_event_handlers(TracingOpts, Hook, Host, stop_callback, [Mod, Func, Args, Seq, Res], foreach). + +fold_start_hook_tracing(TracingOpts, Hook, Host, Args) -> + run_event_handlers(TracingOpts, Hook, Host, start_hook, [Args], fold). + +fold_stop_hook_tracing(TracingOpts, Hook, Host, Args, BreakCallback) -> + run_event_handlers(TracingOpts, Hook, Host, stop_hook, [Args, BreakCallback], fold). + +fold_start_callback_tracing(TracingOpts, Hook, Host, Mod, Func, Args, Seq) -> + run_event_handlers(TracingOpts, Hook, Host, start_callback, [Mod, Func, Args, Seq], fold). + +fold_stop_callback_tracing(TracingOpts, Hook, Host, Mod, Func, Args, Seq, Res) -> + run_event_handlers(TracingOpts, Hook, Host, stop_callback, [Mod, Func, Args, Seq, Res], fold). + +run_event_handlers(TracingOpts, Hook, Host, Event, EventArgs, RunType) -> + EventHandlerList = maps:get(event_handler_list, TracingOpts, default_tracing_event_handler_list()), + EventHandlerOpts = maps:get(event_handler_options, TracingOpts, #{}), + if + erlang:is_list(EventHandlerList) -> + lists:foreach( + fun(EventHandler) -> + try + if + erlang:is_function(EventHandler) -> + erlang:apply( + EventHandler, + [Event, EventArgs, RunType, Hook, Host, EventHandlerOpts, TracingOpts] + ); + true -> + EventHandler:handle_hook_tracing_event( + Event, + EventArgs, + RunType, + Hook, + Host, + EventHandlerOpts, + TracingOpts + ) + end + of + _ -> + ok + catch + E:R:Stack -> + ?ERROR_MSG( + "(~0p|~ts|~0p) Tracing event '~0p' handler exception(~0p): ~0p: ~0p", + [Hook, Host, erlang:self(), EventHandler, E, R, Stack]), + ok + end + end, + EventHandlerList + ); % ok + true -> + ?ERROR_MSG("(~0p|~ts|~0p) Bad event handler list: ~0p", [Hook, Host, erlang:self(), EventHandlerList]), + ok + end. + +default_tracing_event_handler_list() -> + [fun tracing_timing_event_handler/7]. + +tracing_timing_event_handler(start_hook, EventArgs, RunType, Hook, Host, _, TracingOpts) -> + HookStart = erlang:system_time(nanosecond), + % Generate new event: + run_event_handlers(TracingOpts, Hook, Host, start_hook_timing, EventArgs ++ [HookStart], RunType); +tracing_timing_event_handler(stop_hook, EventArgs, RunType, Hook, Host, _, TracingOpts) -> + HookStop = erlang:system_time(nanosecond), + TimingMap = #{} = erlang:get(?TIMING_KEY), + {HookStart, CallbackList} = maps:get({Hook, Host}, TimingMap), + {CallbackListTiming, CallbackListTotal} = lists:foldl( + fun({_, _, _, CallbackStart, CallbackStop}=CallbackTimingInfo, {CallbackListTimingX, Total}) -> + {CallbackListTimingX ++ [CallbackTimingInfo], Total + (CallbackStop - CallbackStart)} + end, + {[], 0}, + CallbackList + ), + % Generate new event: + run_event_handlers( + TracingOpts, + Hook, + Host, + stop_hook_timing, + EventArgs ++ [HookStart, HookStop, CallbackListTiming, CallbackListTotal], + RunType + ); +tracing_timing_event_handler(start_callback, EventArgs, RunType, Hook, Host, _, TracingOpts) -> + CallbackStart = erlang:system_time(nanosecond), + % Generate new event: + run_event_handlers(TracingOpts, Hook, Host, start_callback_timing, EventArgs ++ [CallbackStart], RunType); +tracing_timing_event_handler(stop_callback, EventArgs, RunType, Hook, Host, _, TracingOpts) -> + CallbackStop = erlang:system_time(nanosecond), + TimingMap = #{} = erlang:get(?TIMING_KEY), + {_, [{_, _, _, CallbackStart} | _]} = maps:get({Hook, Host}, TimingMap), + run_event_handlers( + TracingOpts, + Hook, + Host, + stop_callback_timing, + EventArgs ++ [CallbackStart, CallbackStop], + RunType + ), + ok; +tracing_timing_event_handler(start_hook_timing, [_, HookStart], RunType, Hook, Host, EventHandlerOpts, _) -> + tracing_output(EventHandlerOpts, "(~0p|~ts|~0p|~0p) Timing started\n", [Hook, Host, erlang:self(), RunType]), + case erlang:get(?TIMING_KEY) of + #{}=TimingMap -> + erlang:put(?TIMING_KEY, TimingMap#{{Hook, Host} => {HookStart, []}}); + _ -> + erlang:put(?TIMING_KEY, #{{Hook, Host} => {HookStart, []}}) + end, + ok; +tracing_timing_event_handler( + stop_hook_timing, + [_, _, HookStart, HookStop, CallbackListTiming, CallbackListTotal], + RunType, + Hook, + Host, + EventHandlerOpts, + _ +) -> + if + erlang:length(CallbackListTiming) < 2 -> % We don't need sorted timing result + ok; + true -> + CallbackListTimingText = + lists:foldl( + fun({Mod, Func, Arity, Diff}, CallbackListTimingText) -> + CallbackListTimingText + ++ "\n\t" + ++ mfa_string({Mod, Func, Arity}) + ++ " -> " + ++ human_readable_time_string(Diff) + end, + "", + lists:keysort( + 4, + [ + {Mod, Func, Arity, CallbackStop - CallbackStart} || + {Mod, Func, Arity, CallbackStart, CallbackStop} <- CallbackListTiming + ] + ) + ), + tracing_output( + EventHandlerOpts, + "(~0p|~ts|~0p|~0p) All callbacks took ~ts to run. Sorted running time:" + ++ CallbackListTimingText + ++ "\n", + [Hook, Host, erlang:self(), RunType, human_readable_time_string(CallbackListTotal)] + ), + tracing_output( + EventHandlerOpts, + "(~0p|~ts|~0p|~0p) Time calculations for all callbacks took ~ts\n", + [ + Hook, + Host, + erlang:self(), + RunType, + human_readable_time_string((HookStop - HookStart) - CallbackListTotal) + ] + ) + end, + tracing_output(EventHandlerOpts, "(~0p|~ts|~0p|~0p) Timing stopped\n", [Hook, Host, erlang:self(), RunType]), + TimingMap = #{} = erlang:get(?TIMING_KEY), + NewTimingMap = maps:remove({Hook, Host}, TimingMap), + if + NewTimingMap == #{} -> + erlang:erase(?TIMING_KEY); + true -> + erlang:put(?TIMING_KEY, NewTimingMap) + end, + ok; +tracing_timing_event_handler(start_callback_timing, [Mod, Func, Args, _, CallbackStart], _, Hook, Host, _, _) -> + TimingMap = #{} = erlang:get(?TIMING_KEY), + {HookStart, Callbacks} = maps:get({Hook, Host}, TimingMap), + erlang:put( + ?TIMING_KEY, + TimingMap#{ + {Hook, Host} => {HookStart, [{Mod, Func, erlang:length(Args), CallbackStart} | Callbacks]} + } + ), + ok; +tracing_timing_event_handler( + stop_callback_timing, + [Mod, Func, _, _, _, CallbackStart, CallbackStop], + RunType, + Hook, + Host, + EventHandlerOpts, + _ +) -> + TimingMap = #{} = erlang:get(?TIMING_KEY), + {HookStart, [{Mod, Func, Arity, CallbackStart} | Callbacks]} = maps:get({Hook, Host}, TimingMap), + maps:get(output_for_each_callback, maps:get(timing, EventHandlerOpts, #{}), false) andalso tracing_output( + EventHandlerOpts, + "(~0p|~ts|~0p|~0p) " + ++ mfa_string({Mod, Func, Arity}) + ++ " took " + ++ human_readable_time_string(CallbackStop - CallbackStart) + ++ "\n", + [Hook, Host, erlang:self(), RunType] + ), + erlang:put( + ?TIMING_KEY, + TimingMap#{ + {Hook, Host} => {HookStart, [{Mod, Func, Arity, CallbackStart, CallbackStop} | Callbacks]} + } + ), + ok; +tracing_timing_event_handler(_, _, _, _, _, _, _) -> + ok. + +tracing_output(#{output_function := OutputF}, Text, Args) -> + try + OutputF(Text, Args) + of + _ -> + ok + catch + E:R:Stack -> + ?ERROR_MSG("Tracing output function exception(~0p): ~0p: ~0p", [E, R, Stack]), + ok + end; +tracing_output(#{output_log_level := Output}, Text, Args) -> + if + Output == debug -> + ?DEBUG(Text, Args); + true -> % info + ?INFO_MSG(Text, Args) + end, + ok; +tracing_output(Opts, Text, Args) -> + tracing_output(Opts#{output_log_level => info}, Text, Args). + +mfa_string({_, Fun, _}) when erlang:is_function(Fun) -> + io_lib:format("~0p", [Fun]); +mfa_string({Mod, Func, Arity}) -> + erlang:atom_to_list(Mod) ++ ":" ++ erlang:atom_to_list(Func) ++ "/" ++ erlang:integer_to_list(Arity). + +human_readable_time_string(TimeNS) -> + {Time, Unit, Decimals} = + if + TimeNS >= 1000000000 -> + {TimeNS / 1000000000, "", 10}; + TimeNS >= 1000000 -> + {TimeNS / 1000000, "m", 7}; + TimeNS >= 1000 -> + {TimeNS / 1000, "μ", 4}; + true -> + {TimeNS / 1, "n", 0} + end, + erlang:float_to_list(Time, [{decimals, Decimals}, compact]) ++ Unit ++ "s". diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl index c0c7bbbd6..709585145 100644 --- a/src/ejabberd_http.erl +++ b/src/ejabberd_http.erl @@ -5,7 +5,7 @@ %%% Created : 27 Feb 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -24,25 +24,24 @@ %%%---------------------------------------------------------------------- -module(ejabberd_http). - --behaviour(ejabberd_config). +-behaviour(ejabberd_listener). -author('alexey@process-one.net'). %% External exports --export([start/2, start_link/2, become_controller/1, - socket_type/0, receive_headers/1, url_encode/1, - transform_listen_option/2]). +-export([start/3, start_link/3, + accept/1, receive_headers/1, recv_file/2, + listen_opt_type/1, listen_options/0, + apply_custom_headers/2]). --export([init/2, opt_type/1]). +-export([init/3]). --include("ejabberd.hrl"). -include("logger.hrl"). - --include("xmpp.hrl"). - +-include_lib("xmpp/include/xmpp.hrl"). -include("ejabberd_http.hrl"). +-include_lib("kernel/include/file.hrl"). + -record(state, {sockmod, socket, request_method, @@ -50,7 +49,7 @@ request_path, request_auth, request_keepalive, - request_content_length, + request_content_length = 0, request_lang = <<"en">>, %% XXX bard: request handlers are configured in %% ejabberd.cfg under the HTTP service. For example, @@ -67,8 +66,11 @@ request_headers = [], end_of_request = false, options = [], - default_host, - trail = <<>> + custom_headers, + trail = <<>>, + allow_unencrypted_sasl2, + addr_re, + sock_peer_name = none }). -define(XHTML_DOCTYPE, @@ -83,113 +85,109 @@ "org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" "">>). -start(SockData, Opts) -> +-define(RECV_BUF, 65536). +-define(SEND_BUF, 65536). +-define(MAX_POST_SIZE, 20971520). %% 20Mb + +start(SockMod, Socket, Opts) -> {ok, proc_lib:spawn(ejabberd_http, init, - [SockData, Opts])}. + [SockMod, Socket, Opts])}. -start_link(SockData, Opts) -> +start_link(SockMod, Socket, Opts) -> {ok, proc_lib:spawn_link(ejabberd_http, init, - [SockData, Opts])}. + [SockMod, Socket, Opts])}. -init({SockMod, Socket}, Opts) -> +init(SockMod, Socket, Opts) -> TLSEnabled = proplists:get_bool(tls, Opts), - TLSOpts1 = lists:filter(fun ({certfile, _}) -> true; - ({ciphers, _}) -> true; + TLSOpts1 = lists:filter(fun ({ciphers, _}) -> true; ({dhfile, _}) -> true; + ({cafile, _}) -> true; + ({protocol_options, _}) -> true; (_) -> false end, Opts), - TLSOpts2 = case lists:keysearch(protocol_options, 1, Opts) of - {value, {_, O}} -> - [_|ProtocolOptions] = lists:foldl( - fun(X, Acc) -> X ++ Acc end, [], - [["|" | binary_to_list(Opt)] || Opt <- O, is_binary(Opt)] - ), - [{protocol_options, iolist_to_binary(ProtocolOptions)} | TLSOpts1]; - _ -> TLSOpts1 - end, - TLSOpts3 = case proplists:get_bool(tls_compression, Opts) of - false -> [compression_none | TLSOpts2]; - true -> TLSOpts2 + TLSOpts2 = case proplists:get_bool(tls_compression, Opts) of + false -> [compression_none | TLSOpts1]; + true -> TLSOpts1 end, + TLSOpts3 = case ejabberd_pkix:get_certfile( + ejabberd_config:get_myname()) of + error -> TLSOpts2; + {ok, CertFile} -> [{certfile, CertFile}|TLSOpts2] + end, TLSOpts = [verify_none | TLSOpts3], {SockMod1, Socket1} = if TLSEnabled -> - inet:setopts(Socket, [{recbuf, 8192}]), + inet:setopts(Socket, [{recbuf, ?RECV_BUF}]), {ok, TLSSocket} = fast_tls:tcp_to_tls(Socket, TLSOpts), {fast_tls, TLSSocket}; true -> {SockMod, Socket} end, - Captcha = case proplists:get_bool(captcha, Opts) of - true -> [{[<<"captcha">>], ejabberd_captcha}]; - false -> [] - end, - Register = case proplists:get_bool(register, Opts) of - true -> [{[<<"register">>], mod_register_web}]; - false -> [] - end, - Admin = case proplists:get_bool(web_admin, Opts) of - true -> [{[<<"admin">>], ejabberd_web_admin}]; - false -> [] - end, - Bind = case proplists:get_bool(http_bind, Opts) of - true -> [{[<<"http-bind">>], mod_http_bind}]; - false -> [] - end, - XMLRPC = case proplists:get_bool(xmlrpc, Opts) of - true -> [{[], ejabberd_xmlrpc}]; - false -> [] - end, - DefinedHandlers = gen_mod:get_opt( - request_handlers, Opts, - fun(Hs) -> - Hs1 = lists:map(fun - ({Mod, Path}) when is_atom(Mod) -> {Path, Mod}; - ({Path, Mod}) -> {Path, Mod} - end, Hs), - - [{str:tokens( - iolist_to_binary(Path), <<"/">>), - Mod} || {Path, Mod} <- Hs1] - end, []), - RequestHandlers = DefinedHandlers ++ Captcha ++ Register ++ - Admin ++ Bind ++ XMLRPC, + SockPeer = proplists:get_value(sock_peer_name, Opts, none), + RequestHandlers = proplists:get_value(request_handlers, Opts, []), ?DEBUG("S: ~p~n", [RequestHandlers]), - DefaultHost = gen_mod:get_opt(default_host, Opts, fun(A) -> A end, undefined), + {ok, RE} = re:compile(<<"^(?:\\[(.*?)\\]|(.*?))(?::(\\d+))?$">>), - ?INFO_MSG("started: ~p", [{SockMod1, Socket1}]), + CustomHeaders = proplists:get_value(custom_headers, Opts, []), + + AllowUnencryptedSasl2 = proplists:get_bool(allow_unencrypted_sasl2, Opts), State = #state{sockmod = SockMod1, socket = Socket1, - default_host = DefaultHost, + custom_headers = CustomHeaders, options = Opts, - request_handlers = RequestHandlers}, + allow_unencrypted_sasl2 = AllowUnencryptedSasl2, + request_handlers = RequestHandlers, + sock_peer_name = SockPeer, + addr_re = RE}, try receive_headers(State) of V -> V catch {error, _} -> State end. -become_controller(_Pid) -> +accept(_Pid) -> ok. -socket_type() -> - raw. - +send_text(_State, none) -> + ok; send_text(State, Text) -> - case catch - (State#state.sockmod):send(State#state.socket, Text) - of - ok -> ok; - {error, timeout} -> - ?INFO_MSG("Timeout on ~p:send", [State#state.sockmod]), - exit(normal); - Error -> - ?DEBUG("Error in ~p:send: ~p", - [State#state.sockmod, Error]), - exit(normal) + case (State#state.sockmod):send(State#state.socket, Text) of + ok -> ok; + {error, timeout} -> + ?INFO_MSG("Timeout on ~p:send", [State#state.sockmod]), + exit(normal); + Error -> + ?DEBUG("Error in ~p:send: ~p", + [State#state.sockmod, Error]), + exit(normal) + end. + +send_file(State, Fd, Size, FileName) -> + try + case State#state.sockmod of + gen_tcp -> + {ok, _} = file:sendfile(Fd, State#state.socket, 0, Size, []), + ok; + _ -> + case file:read(Fd, ?SEND_BUF) of + {ok, Data} -> + send_text(State, Data), + send_file(State, Fd, Size, FileName); + eof -> + ok + end + end + catch _:{case_clause, {error, Why}} -> + if Why /= closed -> + ?WARNING_MSG("Failed to read ~ts: ~ts", + [FileName, file_format_error(Why)]), + exit(normal); + true -> + ok + end end. receive_headers(#state{trail = Trail} = State) -> @@ -197,7 +195,13 @@ receive_headers(#state{trail = Trail} = State) -> Socket = State#state.socket, Data = SockMod:recv(Socket, 0, 300000), case Data of - {error, _} -> ok; + {error, closed} when State#state.request_method == undefined -> + % socket closed without receiving anything in it + ok; + {error, Error} -> + ?DEBUG("Error when retrieving http headers ~p: ~p", + [State#state.sockmod, Error]), + ok; {ok, D} -> parse_headers(State#state{trail = <>}) end. @@ -244,7 +248,7 @@ process_header(State, Data) -> request_version = Version, request_path = Path, request_keepalive = KeepAlive}; {ok, {http_header, _, 'Connection' = Name, _, Conn}} -> - KeepAlive1 = case jlib:tolower(Conn) of + KeepAlive1 = case misc:tolower(Conn) of <<"keep-alive">> -> true; <<"close">> -> false; _ -> State#state.request_keepalive @@ -267,83 +271,82 @@ process_header(State, Data) -> {http_header, _, 'Accept-Language' = Name, _, Langs}} -> State#state{request_lang = parse_lang(Langs), request_headers = add_header(Name, Langs, State)}; - {ok, {http_header, _, 'Host' = Name, _, Host}} -> - State#state{request_host = Host, - request_headers = add_header(Name, Host, State)}; + {ok, {http_header, _, 'Host' = Name, _, Value}} -> + {Host, Port, TP} = get_transfer_protocol(State#state.addr_re, SockMod, Value), + State#state{request_host = ejabberd_config:resolve_host_alias(Host), + request_port = Port, + request_tp = TP, + request_headers = add_header(Name, Value, State)}; {ok, {http_header, _, Name, _, Value}} when is_binary(Name) -> State#state{request_headers = add_header(normalize_header_name(Name), Value, State)}; {ok, {http_header, _, Name, _, Value}} -> State#state{request_headers = add_header(Name, Value, State)}; - {ok, http_eoh} - when State#state.request_host == undefined -> - ?WARNING_MSG("An HTTP request without 'Host' HTTP " - "header was received.", - []), - throw(http_request_no_host_header); + {ok, http_eoh} when State#state.request_host == undefined; + State#state.request_host == error -> + {State1, Out} = process_request(State), + send_text(State1, Out), + process_header(State, {ok, {http_error, <<>>}}); {ok, http_eoh} -> - ?DEBUG("(~w) http query: ~w ~p~n", - [State#state.socket, State#state.request_method, - element(2, State#state.request_path)]), - {HostProvided, Port, TP} = - get_transfer_protocol(SockMod, - State#state.request_host), - Host = get_host_really_served(State#state.default_host, - HostProvided), - State2 = State#state{request_host = Host, - request_port = Port, request_tp = TP}, - {State3, Out} = process_request(State2), - send_text(State3, Out), - case State3#state.request_keepalive of - true -> - #state{sockmod = SockMod, socket = Socket, - trail = State3#state.trail, - options = State#state.options, - default_host = State#state.default_host, - request_handlers = State#state.request_handlers}; - _ -> - #state{end_of_request = true, - trail = State3#state.trail, - options = State#state.options, - default_host = State#state.default_host, - request_handlers = State#state.request_handlers} - end; + ?DEBUG("(~w) http query: ~w ~p~n", + [State#state.socket, State#state.request_method, + element(2, State#state.request_path)]), + {State3, Out} = process_request(State), + send_text(State3, Out), + case State3#state.request_keepalive of + true -> + #state{sockmod = SockMod, socket = Socket, + trail = State3#state.trail, + options = State#state.options, + custom_headers = State#state.custom_headers, + request_handlers = State#state.request_handlers, + addr_re = State#state.addr_re}; + _ -> + #state{end_of_request = true, + trail = State3#state.trail, + options = State#state.options, + custom_headers = State#state.custom_headers, + request_handlers = State#state.request_handlers, + addr_re = State#state.addr_re} + end; _ -> #state{end_of_request = true, options = State#state.options, - default_host = State#state.default_host, - request_handlers = State#state.request_handlers} + custom_headers = State#state.custom_headers, + request_handlers = State#state.request_handlers, + addr_re = State#state.addr_re} end. add_header(Name, Value, State)-> [{Name, Value} | State#state.request_headers]. -get_host_really_served(undefined, Provided) -> - Provided; -get_host_really_served(Default, Provided) -> - case lists:member(Provided, ?MYHOSTS) of - true -> Provided; - false -> Default - end. +get_transfer_protocol(RE, SockMod, HostPort) -> + {Proto, DefPort} = case SockMod of + gen_tcp -> {http, 80}; + fast_tls -> {https, 443} + end, + {Host, Port} = case re:run(HostPort, RE, [{capture,[1,2,3],binary}]) of + nomatch -> + {error, DefPort}; + {match, [<<>>, H, <<>>]} -> + {jid:nameprep(H), DefPort}; + {match, [H, <<>>, <<>>]} -> + {jid:nameprep(H), DefPort}; + {match, [<<>>, H, PortStr]} -> + {jid:nameprep(H), binary_to_integer(PortStr)}; + {match, [H, <<>>, PortStr]} -> + {jid:nameprep(H), binary_to_integer(PortStr)} + end, -get_transfer_protocol(SockMod, HostPort) -> - [Host | PortList] = str:tokens(HostPort, <<":">>), - case {SockMod, PortList} of - {gen_tcp, []} -> {Host, 80, http}; - {gen_tcp, [Port]} -> - {Host, binary_to_integer(Port), http}; - {fast_tls, []} -> {Host, 443, https}; - {fast_tls, [Port]} -> - {Host, binary_to_integer(Port), https} - end. + {Host, Port, Proto}. %% XXX bard: search through request handlers looking for one that %% matches the requested URL path, and pass control to it. If none is %% found, answer with HTTP 404. -process([], _, _, _, _) -> ejabberd_web:error(not_found); -process(Handlers, Request, Socket, SockMod, Trail) -> +process([], _) -> ejabberd_web:error(not_found); +process(Handlers, Request) -> {HandlerPathPrefix, HandlerModule, HandlerOpts, HandlersLeft} = case Handlers of [{Pfx, Mod} | Tail] -> @@ -361,16 +364,25 @@ process(Handlers, Request, Socket, SockMod, Trail) -> %% requested path is "/test/foo/bar", the local path is %% ["foo", "bar"] LocalPath = lists:nthtail(length(HandlerPathPrefix), Request#request.path), - R = try - HandlerModule:socket_handoff( - LocalPath, Request, Socket, SockMod, Trail, HandlerOpts) - catch error:undef -> - HandlerModule:process(LocalPath, Request) + R = case erlang:function_exported(HandlerModule, socket_handoff, 3) of + true -> + HandlerModule:socket_handoff( + LocalPath, Request, HandlerOpts); + false -> + try + HandlerModule:process(LocalPath, Request) + catch + Class:Reason:Stack -> + ?ERROR_MSG( + "HTTP handler crashed: ~s", + [misc:format_exception(2, Class, Reason, Stack)]), + erlang:raise(Class, Reason, Stack) + end end, ejabberd_hooks:run(http_request_debug, [{LocalPath, Request}]), R; false -> - process(HandlersLeft, Request, Socket, SockMod, Trail) + process(HandlersLeft, Request) end. extract_path_query(#state{request_method = Method, @@ -378,77 +390,107 @@ extract_path_query(#state{request_method = Method, when Method =:= 'GET' orelse Method =:= 'HEAD' orelse Method =:= 'DELETE' orelse Method =:= 'OPTIONS' -> - case catch url_decode_q_split(Path) of - {'EXIT', _} -> {State, false}; - {NPath, Query} -> - LPath = normalize_path([NPE - || NPE <- str:tokens(path_decode(NPath), <<"/">>)]), + case catch url_decode_q_split_normalize(Path) of + {'EXIT', Error} -> + ?DEBUG("Error decoding URL '~p': ~p", [Path, Error]), + {State, false}; + {LPath, Query} -> LQuery = case catch parse_urlencoded(Query) of {'EXIT', _Reason} -> []; LQ -> LQ end, - {State, {LPath, LQuery, <<"">>}} + {State, {LPath, LQuery, <<"">>, Path}} end; extract_path_query(#state{request_method = Method, request_path = {abs_path, Path}, request_content_length = Len, + trail = Trail, sockmod = _SockMod, socket = _Socket} = State) - when (Method =:= 'POST' orelse Method =:= 'PUT') andalso - is_integer(Len) -> - case recv_data(State, Len) of - error -> {State, false}; - {NewState, Data} -> - ?DEBUG("client data: ~p~n", [Data]), - case catch url_decode_q_split(Path) of - {'EXIT', _} -> {NewState, false}; - {NPath, _Query} -> - LPath = normalize_path([NPE - || NPE <- str:tokens(path_decode(NPath), <<"/">>)]), - LQuery = case catch parse_urlencoded(Data) of - {'EXIT', _Reason} -> []; - LQ -> LQ - end, - {NewState, {LPath, LQuery, Data}} + when (Method =:= 'POST' orelse Method =:= 'PUT') andalso Len>0 -> + case catch url_decode_q_split_normalize(Path) of + {'EXIT', Error} -> + ?DEBUG("Error decoding URL '~p': ~p", [Path, Error]), + {State, false}; + {LPath, _Query} -> + case Method of + 'PUT' -> + {State, {LPath, [], Trail, Path}}; + 'POST' -> + case recv_data(State) of + {ok, Data} -> + LQuery = case catch parse_urlencoded(Data) of + {'EXIT', _Reason} -> []; + LQ -> LQ + end, + {State, {LPath, LQuery, Data, Path}}; + error -> + {State, false} + end end end; extract_path_query(State) -> {State, false}. +process_request(#state{request_host = undefined, + custom_headers = CustomHeaders} = State) -> + {State, make_text_output(State, 400, CustomHeaders, + <<"Missing Host header">>)}; +process_request(#state{request_host = error, + custom_headers = CustomHeaders} = State) -> + {State, make_text_output(State, 400, CustomHeaders, + <<"Malformed Host header">>)}; process_request(#state{request_method = Method, request_auth = Auth, request_lang = Lang, + request_version = Version, sockmod = SockMod, socket = Socket, + sock_peer_name = SockPeer, options = Options, request_host = Host, request_port = Port, request_tp = TP, + request_content_length = Length, request_headers = RequestHeaders, request_handlers = RequestHandlers, - trail = Trail} = State) -> + custom_headers = CustomHeaders} = State) -> + case proplists:get_value(<<"Expect">>, RequestHeaders, <<>>) of + <<"100-", _/binary>> when Version == {1, 1} -> + send_text(State, <<"HTTP/1.1 100 Continue\r\n\r\n">>); + _ -> + ok + end, case extract_path_query(State) of {State2, false} -> {State2, make_bad_request(State)}; - {State2, {LPath, LQuery, Data}} -> - PeerName = - case SockMod of - gen_tcp -> - inet:peername(Socket); - _ -> - SockMod:peername(Socket) - end, + {State2, {LPath, LQuery, Data, RawPath}} -> + 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) end, XFF = proplists:get_value('X-Forwarded-For', RequestHeaders, []), - IP = analyze_ip_xff(IPHere, XFF, Host), + IP = analyze_ip_xff(IPHere, XFF), Request = #request{method = Method, path = LPath, + raw_path = RawPath, q = LQuery, auth = Auth, - data = Data, + length = Length, + sockmod = SockMod, + socket = Socket, + data = Data, lang = Lang, host = Host, port = Port, @@ -456,170 +498,154 @@ process_request(#state{request_method = Method, opts = Options, headers = RequestHeaders, ip = IP}, - Res = case process(RequestHandlers, Request, Socket, SockMod, Trail) of + RequestHandlers1 = ejabberd_hooks:run_fold( + http_request_handlers, RequestHandlers, [Host, Request]), + Res = case process(RequestHandlers1, Request) of El when is_record(El, xmlel) -> - make_xhtml_output(State, 200, [], El); + make_xhtml_output(State, 200, CustomHeaders, El); {Status, Headers, El} when is_record(El, xmlel) -> - make_xhtml_output(State, Status, Headers, El); + make_xhtml_output(State, Status, + apply_custom_headers(Headers, CustomHeaders), El); Output when is_binary(Output) or is_list(Output) -> - make_text_output(State, 200, [], Output); + make_text_output(State, 200, CustomHeaders, Output); {Status, Headers, Output} when is_binary(Output) or is_list(Output) -> - make_text_output(State, Status, Headers, Output); + make_text_output(State, Status, + apply_custom_headers(Headers, CustomHeaders), Output); + {Status, Headers, {file, FileName}} -> + make_file_output(State, Status, Headers, FileName); {Status, Reason, Headers, Output} when is_binary(Output) or is_list(Output) -> - make_text_output(State, Status, Reason, Headers, Output); + make_text_output(State, Status, Reason, + apply_custom_headers(Headers, CustomHeaders), Output); _ -> none end, - {State2, Res} + {State2#state{trail = <<>>}, Res} end. make_bad_request(State) -> - make_xhtml_output(State, 400, [], + make_xhtml_output(State, 400, State#state.custom_headers, ejabberd_web:make_xhtml([#xmlel{name = <<"h1">>, attrs = [], children = [{xmlcdata, <<"400 Bad Request">>}]}])). -analyze_ip_xff(IP, [], _Host) -> IP; -analyze_ip_xff({IPLast, Port}, XFF, Host) -> +analyze_ip_xff(IP, []) -> IP; +analyze_ip_xff({IPLast, Port}, XFF) -> [ClientIP | ProxiesIPs] = str:tokens(XFF, <<", ">>) ++ - [jlib:ip_to_list(IPLast)], - TrustedProxies = ejabberd_config:get_option( - {trusted_proxies, Host}, - fun(all) -> all; - (TPs) -> - [iolist_to_binary(TP) || TP <- TPs] - end, []), + [misc:ip_to_list(IPLast)], + TrustedProxies = ejabberd_option:trusted_proxies(), IPClient = case is_ipchain_trusted(ProxiesIPs, TrustedProxies) of true -> - {ok, IPFirst} = inet_parse:address( - binary_to_list(ClientIP)), - ?DEBUG("The IP ~w was replaced with ~w due to " - "header X-Forwarded-For: ~s", - [IPLast, IPFirst, XFF]), - IPFirst; + case inet_parse:address(binary_to_list(ClientIP)) of + {ok, IPFirst} -> + ?DEBUG("The IP ~w was replaced with ~w due to " + "header X-Forwarded-For: ~ts", + [IPLast, IPFirst, XFF]), + IPFirst; + E -> throw(E) + end; false -> IPLast end, {IPClient, Port}. +is_ipchain_trusted([], _) -> false; is_ipchain_trusted(_UserIPs, all) -> true; -is_ipchain_trusted(UserIPs, TrustedIPs) -> - [] == UserIPs -- [<<"127.0.0.1">> | TrustedIPs]. +is_ipchain_trusted(UserIPs, Masks) -> + lists:all( + fun(IP) -> + case inet:parse_address(binary_to_list(IP)) of + {ok, IP2} -> + lists:any( + fun({Mask, MaskLen}) -> + misc:match_ip_mask(IP2, Mask, MaskLen) + end, Masks); + _ -> + false + end + end, UserIPs). -recv_data(State, Len) -> recv_data(State, Len, <<>>). - -recv_data(State, 0, Acc) -> {State, Acc}; -recv_data(#state{trail = Trail} = State, Len, <<>>) when byte_size(Trail) > Len -> - <> = Trail, - {State#state{trail = Rest}, Data}; -recv_data(State, Len, Acc) -> - case State#state.trail of - <<>> -> - case (State#state.sockmod):recv(State#state.socket, - min(Len, 16#4000000), 300000) - of - {ok, Data} -> - recv_data(State, Len - byte_size(Data), <>); - Err -> - ?DEBUG("Cannot receive HTTP data: ~p", [Err]), - error +recv_data(#state{request_content_length = Len}) when Len >= ?MAX_POST_SIZE -> + error; +recv_data(#state{request_content_length = Len, trail = Trail, + sockmod = SockMod, socket = Socket}) -> + NewLen = Len - byte_size(Trail), + if NewLen > 0 -> + case SockMod:recv(Socket, NewLen, 60000) of + {ok, Data} -> {ok, <>}; + {error, _} -> error end; - _ -> - Trail = (State#state.trail), - recv_data(State#state{trail = <<>>}, - Len - byte_size(Trail), <>) + true -> + {ok, Trail} end. -make_xhtml_output(State, Status, Headers, XHTML) -> - Data = case lists:member(html, Headers) of - true -> - iolist_to_binary([?HTML_DOCTYPE, - fxml:element_to_binary(XHTML)]); - _ -> - iolist_to_binary([?XHTML_DOCTYPE, - fxml:element_to_binary(XHTML)]) - end, - Headers1 = case lists:keysearch(<<"Content-Type">>, 1, - Headers) - of - {value, _} -> - [{<<"Content-Length">>, - integer_to_binary(byte_size(Data))} - | Headers]; - _ -> - [{<<"Content-Type">>, <<"text/html; charset=utf-8">>}, - {<<"Content-Length">>, - integer_to_binary(byte_size(Data))} - | Headers] - end, - HeadersOut = case {State#state.request_version, - State#state.request_keepalive} - of - {{1, 1}, true} -> Headers1; - {_, true} -> - [{<<"Connection">>, <<"keep-alive">>} | Headers1]; - {_, false} -> - [{<<"Connection">>, <<"close">>} | Headers1] - end, - Version = case State#state.request_version of - {1, 1} -> <<"HTTP/1.1 ">>; - _ -> <<"HTTP/1.0 ">> - end, - H = lists:map(fun ({Attr, Val}) -> - [Attr, <<": ">>, Val, <<"\r\n">>]; - (_) -> [] +recv_file(#request{length = Len, data = Trail, + sockmod = SockMod, socket = Socket}, Path) -> + case file:open(Path, [write, exclusive, raw]) of + {ok, Fd} -> + Res = case file:write(Fd, Trail) of + ok -> + NewLen = max(0, Len - byte_size(Trail)), + do_recv_file(NewLen, SockMod, Socket, Fd); + {error, _} = Err -> + Err end, - HeadersOut), - SL = [Version, - integer_to_binary(Status), <<" ">>, - code_to_phrase(Status), <<"\r\n">>], - Data2 = case State#state.request_method of - 'HEAD' -> <<"">>; - _ -> Data + file:close(Fd), + case Res of + ok -> ok; + {error, _} -> file:delete(Path) end, - [SL, H, <<"\r\n">>, Data2]. + Res; + {error, _} = Err -> + Err + end. -make_text_output(State, Status, Headers, Text) -> - make_text_output(State, Status, <<"">>, Headers, Text). +do_recv_file(0, _SockMod, _Socket, _Fd) -> + ok; +do_recv_file(Len, SockMod, Socket, Fd) -> + ChunkLen = min(Len, ?RECV_BUF), + case SockMod:recv(Socket, ChunkLen, timer:seconds(30)) of + {ok, Data} -> + case file:write(Fd, Data) of + ok -> + do_recv_file(Len-size(Data), SockMod, Socket, Fd); + {error, _} = Err -> + Err + end; + {error, _} -> + {error, closed} + end. -make_text_output(State, Status, Reason, Headers, Text) -> - Data = iolist_to_binary(Text), - Headers1 = case lists:keysearch(<<"Content-Type">>, 1, - Headers) - of - {value, _} -> - [{<<"Content-Length">>, - integer_to_binary(byte_size(Data))} - | Headers]; - _ -> - [{<<"Content-Type">>, <<"text/html; charset=utf-8">>}, - {<<"Content-Length">>, - integer_to_binary(byte_size(Data))} - | Headers] +make_headers(State, Status, Reason, Headers, Data) -> + Len = if is_integer(Data) -> Data; + true -> iolist_size(Data) + end, + Headers1 = [{<<"Content-Length">>, integer_to_binary(Len)} | Headers], + Headers2 = case lists:keyfind(<<"Content-Type">>, 1, Headers) of + {_, _} -> + Headers1; + false -> + [{<<"Content-Type">>, <<"text/html; charset=utf-8">>} + | Headers1] end, HeadersOut = case {State#state.request_version, - State#state.request_keepalive} - of - {{1, 1}, true} -> Headers1; - {_, true} -> - [{<<"Connection">>, <<"keep-alive">>} | Headers1]; - {_, false} -> - [{<<"Connection">>, <<"close">>} | Headers1] + State#state.request_keepalive} of + {{1, 1}, true} -> Headers2; + {_, true} -> + [{<<"Connection">>, <<"keep-alive">>} | Headers2]; + {_, false} -> + [{<<"Connection">>, <<"close">>} | Headers2] end, Version = case State#state.request_version of - {1, 1} -> <<"HTTP/1.1 ">>; - _ -> <<"HTTP/1.0 ">> + {1, 1} -> <<"HTTP/1.1 ">>; + _ -> <<"HTTP/1.0 ">> end, - H = lists:map(fun ({Attr, Val}) -> - [Attr, <<": ">>, Val, <<"\r\n">>] - end, - HeadersOut), + H = [[Attr, <<": ">>, Val, <<"\r\n">>] || {Attr, Val} <- HeadersOut], NewReason = case Reason of <<"">> -> code_to_phrase(Status); _ -> Reason @@ -627,11 +653,55 @@ make_text_output(State, Status, Reason, Headers, Text) -> SL = [Version, integer_to_binary(Status), <<" ">>, NewReason, <<"\r\n">>], + [SL, H, <<"\r\n">>]. + +make_xhtml_output(State, Status, Headers, XHTML) -> + Data = case State#state.request_method of + 'HEAD' -> <<"">>; + _ -> + DocType = case lists:member(html, Headers) of + true -> ?HTML_DOCTYPE; + false -> ?XHTML_DOCTYPE + end, + iolist_to_binary([DocType, fxml:element_to_binary(XHTML)]) + end, + EncodedHdrs = make_headers(State, Status, <<"">>, Headers, Data), + [EncodedHdrs, Data]. + +make_text_output(State, Status, Headers, Text) -> + make_text_output(State, Status, <<"">>, Headers, Text). + +make_text_output(State, Status, Reason, Headers, Text) -> + Data = iolist_to_binary(Text), Data2 = case State#state.request_method of - 'HEAD' -> <<"">>; - _ -> Data + 'HEAD' -> <<"">>; + _ -> Data end, - [SL, H, <<"\r\n">>, Data2]. + EncodedHdrs = make_headers(State, Status, Reason, Headers, Data2), + [EncodedHdrs, Data2]. + +make_file_output(State, Status, Headers, FileName) -> + case file:read_file_info(FileName) of + {ok, #file_info{size = Size}} when State#state.request_method == 'HEAD' -> + make_headers(State, Status, <<"">>, Headers, Size); + {ok, #file_info{size = Size}} -> + case file:open(FileName, [raw, read]) of + {ok, Fd} -> + EncodedHdrs = make_headers(State, Status, <<"">>, Headers, Size), + send_text(State, EncodedHdrs), + send_file(State, Fd, Size, FileName), + file:close(Fd), + none; + {error, Why} -> + Reason = file_format_error(Why), + ?ERROR_MSG("Failed to open ~ts: ~ts", [FileName, Reason]), + make_text_output(State, 404, Reason, [], <<>>) + end; + {error, Why} -> + Reason = file_format_error(Why), + ?ERROR_MSG("Failed to read info of ~ts: ~ts", [FileName, Reason]), + make_text_output(State, 404, Reason, [], <<>>) + end. parse_lang(Langs) -> case str:tokens(Langs, <<",; ">>) of @@ -639,8 +709,20 @@ parse_lang(Langs) -> [] -> <<"en">> end. +file_format_error(Reason) -> + case file:format_error(Reason) of + "unknown POSIX error" -> atom_to_list(Reason); + Text -> Text + end. + +url_decode_q_split_normalize(Path) -> + {NPath, Query} = url_decode_q_split(Path), + LPath = normalize_path([NPE + || NPE <- str:tokens(misc:uri_decode(NPath), <<"/">>)]), + {LPath, Query}. + % Code below is taken (with some modifications) from the yaws webserver, which -% is distributed under the folowing license: +% is distributed under the following license: % % This software (the yaws webserver) is free software. % Parts of this software is Copyright (c) Claes Wikstrom @@ -664,19 +746,6 @@ url_decode_q_split(<>, Acc) when H /= 0 -> url_decode_q_split(<<>>, Ack) -> {path_norm_reverse(Ack), <<>>}. -%% @doc Decode a part of the URL and return string() -path_decode(Path) -> path_decode(Path, <<>>). - -path_decode(<<$%, Hi, Lo, Tail/binary>>, Acc) -> - Hex = hex_to_integer([Hi, Lo]), - if Hex == 0 -> exit(badurl); - true -> ok - end, - path_decode(Tail, <>); -path_decode(<>, Acc) when H /= 0 -> - path_decode(T, <>); -path_decode(<<>>, Acc) -> Acc. - path_norm_reverse(<<"/", T/binary>>) -> start_dir(0, <<"/">>, T); path_norm_reverse(T) -> start_dir(0, <<"">>, T). @@ -700,22 +769,6 @@ rest_dir(0, Path, <>) -> rest_dir(0, <>, T); rest_dir(N, Path, <<_H, T/binary>>) -> rest_dir(N, Path, T). -%% hex_to_integer - -hex_to_integer(Hex) -> - case catch list_to_integer(Hex, 16) of - {'EXIT', _} -> old_hex_to_integer(Hex); - X -> X - end. - -old_hex_to_integer(Hex) -> - DEHEX = fun (H) when H >= $a, H =< $f -> H - $a + 10; - (H) when H >= $A, H =< $F -> H - $A + 10; - (H) when H >= $0, H =< $9 -> H - $0 - end, - lists:foldl(fun (E, Acc) -> Acc * 16 + DEHEX(E) end, 0, - Hex). - code_to_phrase(100) -> <<"Continue">>; code_to_phrase(101) -> <<"Switching Protocols ">>; code_to_phrase(200) -> <<"OK">>; @@ -761,30 +814,32 @@ code_to_phrase(503) -> <<"Service Unavailable">>; code_to_phrase(504) -> <<"Gateway Timeout">>; code_to_phrase(505) -> <<"HTTP Version Not Supported">>. --spec parse_auth(binary()) -> {binary(), binary()} | {oauth, binary(), []} | undefined. +-spec parse_auth(binary()) -> {binary(), binary()} | {oauth, binary(), []} | invalid. parse_auth(<<"Basic ", Auth64/binary>>) -> - Auth = jlib:decode_base64(Auth64), - %% Auth should be a string with the format: user@server:password - %% Note that password can contain additional characters '@' and ':' - case str:chr(Auth, $:) of - 0 -> - undefined; - Pos -> - {User, <<$:, Pass/binary>>} = erlang:split_binary(Auth, Pos-1), - PassUtf8 = unicode:characters_to_binary(binary_to_list(Pass), utf8), - {User, PassUtf8} + try base64:decode(Auth64) of + Auth -> + case binary:split(Auth, <<":">>) of + [User, Pass] -> + PassUtf8 = unicode:characters_to_binary(Pass, utf8), + {User, PassUtf8}; + _ -> + invalid + end + catch _:_ -> + invalid end; parse_auth(<<"Bearer ", SToken/binary>>) -> Token = str:strip(SToken), {oauth, Token, []}; -parse_auth(<<_/binary>>) -> undefined. +parse_auth(<<_/binary>>) -> + invalid. parse_urlencoded(S) -> parse_urlencoded(S, nokey, <<>>, key). parse_urlencoded(<<$%, Hi, Lo, Tail/binary>>, Last, Cur, State) -> - Hex = hex_to_integer([Hi, Lo]), + Hex = list_to_integer([Hi, Lo], 16), parse_urlencoded(Tail, Last, <>, State); parse_urlencoded(<<$&, Tail/binary>>, _Last, Cur, key) -> [{Cur, <<"">>} | parse_urlencoded(Tail, @@ -804,40 +859,14 @@ parse_urlencoded(<<>>, Last, Cur, _State) -> [{Last, Cur}]; parse_urlencoded(undefined, _, _, _) -> []. - -url_encode(A) -> - url_encode(A, <<>>). - -url_encode(<>, Acc) when - (H >= $a andalso H =< $z) orelse - (H >= $A andalso H =< $Z) orelse - (H >= $0 andalso H =< $9) orelse - H == $_ orelse - H == $. orelse - H == $- orelse - H == $/ orelse - H == $: -> - url_encode(T, <>); -url_encode(<>, Acc) -> - case integer_to_hex(H) of - [X, Y] -> url_encode(T, <>); - [X] -> url_encode(T, <>) - end; -url_encode(<<>>, Acc) -> - Acc. - - -integer_to_hex(I) -> - case catch erlang:integer_to_list(I, 16) of - {'EXIT', _} -> old_integer_to_hex(I); - Int -> Int - end. - -old_integer_to_hex(I) when I < 10 -> integer_to_list(I); -old_integer_to_hex(I) when I < 16 -> [I - 10 + $A]; -old_integer_to_hex(I) when I >= 16 -> - N = trunc(I / 16), - old_integer_to_hex(N) ++ old_integer_to_hex(I rem 16). +apply_custom_headers(Headers, CustomHeaders) -> + {Doctype, Headers2} = case Headers -- [html] of + Headers -> {[], Headers}; + Other -> {[html], Other} + end, + M = maps:merge(maps:from_list(Headers2), + maps:from_list(CustomHeaders)), + Doctype ++ maps:to_list(M). % The following code is mostly taken from yaws_ssl.erl @@ -870,29 +899,29 @@ normalize_path([_Parent, <<"..">>|Path], Norm) -> normalize_path([Part | Path], Norm) -> normalize_path(Path, [Part|Norm]). -transform_listen_option(captcha, Opts) -> - [{captcha, true}|Opts]; -transform_listen_option(register, Opts) -> - [{register, true}|Opts]; -transform_listen_option(web_admin, Opts) -> - [{web_admin, true}|Opts]; -transform_listen_option(http_bind, Opts) -> - [{http_bind, true}|Opts]; -transform_listen_option(http_poll, Opts) -> - Opts; -transform_listen_option({request_handlers, Hs}, Opts) -> - Hs1 = lists:map( - fun({PList, Mod}) when is_list(PList) -> - Path = iolist_to_binary([[$/, P] || P <- PList]), - {Path, Mod}; - (Opt) -> - Opt - end, Hs), - [{request_handlers, Hs1} | Opts]; -transform_listen_option(Opt, Opts) -> - [Opt|Opts]. +listen_opt_type(tag) -> + econf:binary(); +listen_opt_type(allow_unencrypted_sasl2) -> + econf:bool(); +listen_opt_type(request_handlers) -> + econf:map( + econf:and_then( + econf:binary(), + fun(Path) -> str:tokens(Path, <<"/">>) end), + econf:beam([[{socket_handoff, 3}, {process, 2}]])); +listen_opt_type(custom_headers) -> + econf:map( + econf:binary(), + econf:binary()). -opt_type(trusted_proxies) -> - fun (all) -> all; - (TPs) -> [iolist_to_binary(TP) || TP <- TPs] end; -opt_type(_) -> [trusted_proxies]. +listen_options() -> + [{ciphers, undefined}, + {dhfile, undefined}, + {cafile, undefined}, + {protocol_options, undefined}, + {tls, false}, + {tls_compression, false}, + {allow_unencrypted_sasl2, false}, + {request_handlers, []}, + {tag, <<>>}, + {custom_headers, []}]. diff --git a/src/ejabberd_http_bind.erl b/src/ejabberd_http_bind.erl deleted file mode 100644 index ea64b3cdf..000000000 --- a/src/ejabberd_http_bind.erl +++ /dev/null @@ -1,1211 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : ejabberd_http_bind.erl -%%% Author : Stefan Strigler -%%% Purpose : Implements XMPP over BOSH (XEP-0206) -%%% Created : 21 Sep 2005 by Stefan Strigler -%%% Modified: may 2009 by Mickael Remond, Alexey Schepin -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne -%%% -%%% This program is free software; you can redistribute it and/or -%%% modify it under the terms of the GNU General Public License as -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License along -%%% with this program; if not, write to the Free Software Foundation, Inc., -%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -%%% -%%%---------------------------------------------------------------------- - --module(ejabberd_http_bind). - --protocol({xep, 124, '1.11'}). --protocol({xep, 206, '1.4'}). - --behaviour(gen_fsm). - -%% External exports --export([start_link/4, - init/1, - handle_event/3, - handle_sync_event/4, - code_change/4, - handle_info/3, - terminate/3, - send/2, - send_xml/2, - sockname/1, - peername/1, - setopts/2, - controlling_process/2, - become_controller/2, - custom_receiver/1, - reset_stream/1, - change_shaper/2, - monitor/1, - close/1, - start/5, - handle_session_start/8, - handle_http_put/7, - http_put/7, - http_get/2, - prepare_response/4, - process_request/3]). - --include("ejabberd.hrl"). --include("logger.hrl"). - --include("xmpp.hrl"). - --include("ejabberd_http.hrl"). - --include("http_bind.hrl"). - --record(http_bind, - {id, pid, to, hold, wait, process_delay, version}). - --define(NULL_PEER, {{0, 0, 0, 0}, 0}). - -%% http binding request --record(hbr, {rid, - key, - out}). - --record(state, {id, - rid = none, - key, - socket, - output = "", - input = queue:new(), - waiting_input = false, - shaper_state, - shaper_timer, - last_receiver, - last_poll, - http_receiver, - out_of_order_receiver = false, - wait_timer, - ctime = 0, - timer, - pause = 0, - unprocessed_req_list = [], % list of request that have been delayed for proper reordering: {Request, PID} - req_list = [], % list of requests (cache) - max_inactivity, - max_pause, - ip = ?NULL_PEER - }). - -%% Internal request format: --record(http_put, {rid, - attrs, - payload, - payload_size, - hold, - stream, - ip}). - -%%-define(DBGFSM, true). --ifdef(DBGFSM). - --define(FSMOPTS, [{debug, [trace]}]). - --else. - --define(FSMOPTS, []). - --endif. - -%% Wait 100ms before continue processing, to allow the client provide more related stanzas. --define(BOSH_VERSION, <<"1.8">>). - --define(NS_BOSH, <<"urn:xmpp:xbosh">>). - --define(NS_HTTP_BIND, - <<"http://jabber.org/protocol/httpbind">>). - --define(MAX_REQUESTS, 2). - --define(MIN_POLLING, 2000000). - --define(MAX_WAIT, 3600). - --define(MAX_INACTIVITY, 30000). - --define(MAX_PAUSE, 120). - --define(PROCESS_DELAY_DEFAULT, 100). - --define(PROCESS_DELAY_MIN, 0). - --define(PROCESS_DELAY_MAX, 1000). - --define(PROCNAME_MHB, ejabberd_mod_http_bind). - -start(XMPPDomain, Sid, Key, IP, HOpts) -> - ?DEBUG("Starting session", []), - case catch gen_fsm:start(?MODULE, - [Sid, Key, IP, HOpts], - ?FSMOPTS) - of - {ok, Pid} -> {ok, Pid}; - _ -> check_bind_module(XMPPDomain), - {error, "Cannot start HTTP bind session"} - end. - -start_link(Sid, Key, IP, HOpts) -> - gen_fsm:start_link(?MODULE, [Sid, Key, IP, HOpts], ?FSMOPTS). - -send({http_bind, FsmRef, _IP}, Packet) -> - gen_fsm:sync_send_all_state_event(FsmRef, - {send, Packet}). - -send_xml({http_bind, FsmRef, _IP}, Packet) -> - gen_fsm:sync_send_all_state_event(FsmRef, - {send_xml, Packet}). - -setopts({http_bind, FsmRef, _IP}, Opts) -> - case lists:member({active, once}, Opts) of - true -> - gen_fsm:send_all_state_event(FsmRef, {activate, self()}); - _ -> - ok - end. - -controlling_process(_Socket, _Pid) -> ok. - -custom_receiver({http_bind, FsmRef, _IP}) -> - {receiver, ?MODULE, FsmRef}. - -become_controller(FsmRef, C2SPid) -> - gen_fsm:send_all_state_event(FsmRef, - {become_controller, C2SPid}). - -reset_stream({http_bind, _FsmRef, _IP}) -> - ok. - -change_shaper({http_bind, FsmRef, _IP}, Shaper) -> - gen_fsm:send_all_state_event(FsmRef, - {change_shaper, Shaper}). - -monitor({http_bind, FsmRef, _IP}) -> - erlang:monitor(process, FsmRef). - -close({http_bind, FsmRef, _IP}) -> - catch gen_fsm:sync_send_all_state_event(FsmRef, - {stop, close}). - -sockname(_Socket) -> {ok, ?NULL_PEER}. - -peername({http_bind, _FsmRef, IP}) -> {ok, IP}. - - -%% Entry point for data coming from client through ejabberd HTTP server: -process_request(Data, IP, HOpts) -> - Opts1 = ejabberd_c2s_config:get_c2s_limits(), - Opts = [{xml_socket, true} | Opts1], - MaxStanzaSize = case lists:keysearch(max_stanza_size, 1, - Opts) - of - {value, {_, Size}} -> Size; - _ -> infinity - end, - PayloadSize = iolist_size(Data), - case catch parse_request(Data, PayloadSize, - MaxStanzaSize) - of - %% No existing session: - {ok, {<<"">>, Rid, Attrs, Payload}} -> - case fxml:get_attr_s(<<"to">>, Attrs) of - <<"">> -> - ?DEBUG("Session not created (Improper addressing)", []), - {200, ?HEADER, - <<"">>}; - XmppDomain -> - NXmppDomain = jid:nameprep(XmppDomain), - Sid = p1_sha:sha(term_to_binary({p1_time_compat:monotonic_time(), make_ref()})), - case start(NXmppDomain, Sid, <<"">>, IP, HOpts) of - {error, _} -> - {500, ?HEADER, - <<"Internal Server Error">>}; - {ok, Pid} -> - handle_session_start(Pid, NXmppDomain, Sid, Rid, Attrs, - Payload, PayloadSize, IP) - end - end; - %% Existing session - {ok, {Sid, Rid, Attrs, Payload1}} -> - StreamStart = case fxml:get_attr_s(<<"xmpp:restart">>, - Attrs) - of - <<"true">> -> true; - _ -> false - end, - Payload2 = case fxml:get_attr_s(<<"type">>, Attrs) of - <<"terminate">> -> - Payload1 ++ [{xmlstreamend, <<"stream:stream">>}]; - _ -> Payload1 - end, - handle_http_put(Sid, Rid, Attrs, Payload2, PayloadSize, - StreamStart, IP); - {size_limit, Sid} -> - case mnesia:dirty_read({http_bind, Sid}) of - {error, _} -> {404, ?HEADER, <<"">>}; - {ok, #http_bind{pid = FsmRef}} -> - gen_fsm:sync_send_all_state_event(FsmRef, - {stop, close}), - {200, ?HEADER, - <<"Request Too Large">>} - end; - _ -> - ?DEBUG("Received bad request: ~p", [Data]), - {400, ?HEADER, <<"">>} - end. - -handle_session_start(Pid, XmppDomain, Sid, Rid, Attrs, - Payload, PayloadSize, IP) -> - ?DEBUG("got pid: ~p", [Pid]), - Wait = case str:to_integer(fxml:get_attr_s(<<"wait">>, - Attrs)) - of - {error, _} -> ?MAX_WAIT; - {CWait, _} -> - if CWait > (?MAX_WAIT) -> ?MAX_WAIT; - true -> CWait - end - end, - Hold = case str:to_integer(fxml:get_attr_s(<<"hold">>, - Attrs)) - of - {error, _} -> (?MAX_REQUESTS) - 1; - {CHold, _} -> - if CHold > (?MAX_REQUESTS) - 1 -> (?MAX_REQUESTS) - 1; - true -> CHold - end - end, - Pdelay = case - str:to_integer(fxml:get_attr_s(<<"process-delay">>, - Attrs)) - of - {error, _} -> ?PROCESS_DELAY_DEFAULT; - {CPdelay, _} - when ((?PROCESS_DELAY_MIN) =< CPdelay) and - (CPdelay =< (?PROCESS_DELAY_MAX)) -> - CPdelay; - {CPdelay, _} -> - lists:max([lists:min([CPdelay, ?PROCESS_DELAY_MAX]), - ?PROCESS_DELAY_MIN]) - end, - Version = case catch - list_to_float(binary_to_list(fxml:get_attr_s(<<"ver">>, Attrs))) - of - {'EXIT', _} -> 0.0; - V -> V - end, - XmppVersion = fxml:get_attr_s(<<"xmpp:version">>, Attrs), - ?DEBUG("Create session: ~p", [Sid]), - mnesia:dirty_write( - #http_bind{id = Sid, - pid = Pid, - to = {XmppDomain, - XmppVersion}, - hold = Hold, - wait = Wait, - process_delay = Pdelay, - version = Version - }), - handle_http_put(Sid, Rid, Attrs, Payload, PayloadSize, true, IP). - -%%%---------------------------------------------------------------------- -%%% Callback functions from gen_fsm -%%%---------------------------------------------------------------------- - -init([Sid, Key, IP, HOpts]) -> - ?DEBUG("started: ~p", [{Sid, Key, IP}]), - Opts1 = ejabberd_c2s_config:get_c2s_limits(), - SOpts = lists:filtermap(fun({stream_management, _}) -> true; - ({max_ack_queue, _}) -> true; - ({ack_timeout, _}) -> true; - ({resume_timeout, _}) -> true; - ({max_resume_timeout, _}) -> true; - ({resend_on_timeout, _}) -> true; - (_) -> false - end, HOpts), - - Opts = [{xml_socket, true} | SOpts ++ Opts1], - Shaper = none, - ShaperState = shaper:new(Shaper), - Socket = {http_bind, self(), IP}, - ejabberd_socket:start(ejabberd_c2s, ?MODULE, Socket, Opts), - Timer = erlang:start_timer(?MAX_INACTIVITY, self(), []), - {ok, loop, #state{id = Sid, - key = Key, - socket = Socket, - shaper_state = ShaperState, - max_inactivity = ?MAX_INACTIVITY, - max_pause = ?MAX_PAUSE, - timer = Timer}}. - -handle_event({become_controller, C2SPid}, StateName, StateData) -> - case StateData#state.input of - cancel -> - {next_state, StateName, - StateData#state{waiting_input = C2SPid}}; - Input -> - lists:foreach(fun (Event) -> C2SPid ! Event end, - queue:to_list(Input)), - {next_state, StateName, - StateData#state{input = queue:new(), - waiting_input = C2SPid}} - end; -handle_event({change_shaper, Shaper}, StateName, - StateData) -> - NewShaperState = shaper:new(Shaper), - {next_state, StateName, - StateData#state{shaper_state = NewShaperState}}; -handle_event(_Event, StateName, StateData) -> - {next_state, StateName, StateData}. - -handle_sync_event({send_xml, Packet}, _From, StateName, - #state{http_receiver = undefined} = StateData) -> - Output = [Packet | StateData#state.output], - Reply = ok, - {reply, Reply, StateName, - StateData#state{output = Output}}; -handle_sync_event({send_xml, Packet}, _From, StateName, - #state{out_of_order_receiver = true} = StateData) -> - Output = [Packet | StateData#state.output], - Reply = ok, - {reply, Reply, StateName, - StateData#state{output = Output}}; -handle_sync_event({send_xml, Packet}, _From, StateName, - StateData) -> - Output = [Packet | StateData#state.output], - cancel_timer(StateData#state.timer), - Timer = set_inactivity_timer(StateData#state.pause, - StateData#state.max_inactivity), - HTTPReply = {ok, Output}, - gen_fsm:reply(StateData#state.http_receiver, HTTPReply), - cancel_timer(StateData#state.wait_timer), - Rid = StateData#state.rid, - ReqList = [#hbr{rid = Rid, key = StateData#state.key, - out = Output} - | [El - || El <- StateData#state.req_list, El#hbr.rid /= Rid]], - Reply = ok, - {reply, Reply, StateName, - StateData#state{output = [], http_receiver = undefined, - req_list = ReqList, wait_timer = undefined, - timer = Timer}}; - -handle_sync_event({stop,close}, _From, _StateName, StateData) -> - Reply = ok, - {stop, normal, Reply, StateData}; -handle_sync_event({stop,stream_closed}, _From, _StateName, StateData) -> - Reply = ok, - {stop, normal, Reply, StateData}; -handle_sync_event({stop,Reason}, _From, _StateName, StateData) -> - ?DEBUG("Closing bind session ~p - Reason: ~p", [StateData#state.id, Reason]), - Reply = ok, - {stop, normal, Reply, StateData}; -%% HTTP PUT: Receive packets from the client -handle_sync_event(#http_put{rid = Rid}, _From, - StateName, StateData) - when StateData#state.shaper_timer /= undefined -> - Pause = case - erlang:read_timer(StateData#state.shaper_timer) - of - false -> 0; - P -> P - end, - Reply = {wait, Pause}, - ?DEBUG("Shaper timer for RID ~p: ~p", [Rid, Reply]), - {reply, Reply, StateName, StateData}; -handle_sync_event(#http_put{payload_size = - PayloadSize} = - Request, - _From, StateName, StateData) -> - ?DEBUG("New request: ~p", [Request]), - {NewShaperState, NewShaperTimer} = - update_shaper(StateData#state.shaper_state, - PayloadSize), - handle_http_put_event(Request, StateName, - StateData#state{shaper_state = NewShaperState, - shaper_timer = NewShaperTimer}); -%% HTTP GET: send packets to the client -handle_sync_event({http_get, Rid, Wait, Hold}, From, StateName, StateData) -> - TNow = p1_time_compat:system_time(micro_seconds), - if (Hold > 0) and - ((StateData#state.output == []) or (StateData#state.rid < Rid)) and - ((TNow - StateData#state.ctime) < (Wait*1000*1000)) and - (StateData#state.rid =< Rid) and - (StateData#state.pause == 0) -> - send_receiver_reply(StateData#state.http_receiver, {ok, empty}), - cancel_timer(StateData#state.wait_timer), - WaitTimer = erlang:start_timer(Wait * 1000, self(), []), - cancel_timer(StateData#state.timer), - {next_state, StateName, StateData#state{ - http_receiver = From, - out_of_order_receiver = StateData#state.rid < Rid, - wait_timer = WaitTimer, - timer = undefined}}; - true -> - cancel_timer(StateData#state.timer), - Reply = {ok, StateData#state.output}, - ReqList = [#hbr{rid = Rid, - key = StateData#state.key, - out = StateData#state.output} - | [El - || El <- StateData#state.req_list, - El#hbr.rid /= Rid]], - if (StateData#state.http_receiver /= undefined) and - StateData#state.out_of_order_receiver -> - {reply, Reply, StateName, - StateData#state{output = [], timer = undefined, - req_list = ReqList, - out_of_order_receiver = false}}; - true -> - send_receiver_reply(StateData#state.http_receiver, {ok, empty}), - cancel_timer(StateData#state.wait_timer), - Timer = set_inactivity_timer(StateData#state.pause, - StateData#state.max_inactivity), - {reply, Reply, StateName, - StateData#state{output = [], - http_receiver = undefined, - wait_timer = undefined, - timer = Timer, - req_list = ReqList}} - end - end; -handle_sync_event(peername, _From, StateName, - StateData) -> - Reply = {ok, StateData#state.ip}, - {reply, Reply, StateName, StateData}; -handle_sync_event(_Event, _From, StateName, - StateData) -> - Reply = ok, {reply, Reply, StateName, StateData}. - -code_change(_OldVsn, StateName, StateData, _Extra) -> - {ok, StateName, StateData}. - -handle_info({timeout, Timer, _}, _StateName, - #state{id = SID, timer = Timer} = StateData) -> - ?INFO_MSG("Session timeout. Closing the HTTP bind " - "session: ~p", - [SID]), - {stop, normal, StateData}; -handle_info({timeout, WaitTimer, _}, StateName, - #state{wait_timer = WaitTimer} = StateData) -> - if StateData#state.http_receiver /= undefined -> - cancel_timer(StateData#state.timer), - Timer = set_inactivity_timer(StateData#state.pause, - StateData#state.max_inactivity), - gen_fsm:reply(StateData#state.http_receiver, - {ok, empty}), - Rid = StateData#state.rid, - ReqList = [#hbr{rid = Rid, key = StateData#state.key, - out = []} - | [El - || El <- StateData#state.req_list, El#hbr.rid /= Rid]], - {next_state, StateName, - StateData#state{http_receiver = undefined, - req_list = ReqList, wait_timer = undefined, - timer = Timer}}; - true -> {next_state, StateName, StateData} - end; -handle_info({timeout, ShaperTimer, _}, StateName, - #state{shaper_timer = ShaperTimer} = StateData) -> - {next_state, StateName, StateData#state{shaper_timer = undefined}}; - -handle_info(_, StateName, StateData) -> - {next_state, StateName, StateData}. - -terminate(_Reason, _StateName, StateData) -> - ?DEBUG("terminate: Deleting session ~s", - [StateData#state.id]), - mnesia:dirty_delete({http_bind, StateData#state.id}), - send_receiver_reply(StateData#state.http_receiver, - {ok, terminate}), - case StateData#state.waiting_input of - false -> ok; - C2SPid -> gen_fsm:send_event(C2SPid, closed) - end, - ok. - -%%%---------------------------------------------------------------------- -%%% Internal functions -%%%---------------------------------------------------------------------- - -%% PUT / Get processing: -handle_http_put_event(#http_put{rid = Rid, - attrs = Attrs, hold = Hold} = - Request, - StateName, StateData) -> - ?DEBUG("New request: ~p", [Request]), - RidAllow = rid_allow(StateData#state.rid, Rid, Attrs, - Hold, StateData#state.max_pause), - case RidAllow of - buffer -> - ?DEBUG("Buffered request: ~p", [Request]), - PendingRequests = StateData#state.unprocessed_req_list, - Requests = lists:keydelete(Rid, 2, PendingRequests), - ReqList = [#hbr{rid = Rid, key = StateData#state.key, - out = []} - | [El - || El <- StateData#state.req_list, - El#hbr.rid > Rid - 1 - Hold]], - ?DEBUG("reqlist: ~p", [ReqList]), - UnprocessedReqList = [Request | Requests], - cancel_timer(StateData#state.timer), - Timer = set_inactivity_timer(0, - StateData#state.max_inactivity), - {reply, ok, StateName, - StateData#state{unprocessed_req_list = - UnprocessedReqList, - req_list = ReqList, timer = Timer}, - hibernate}; - _ -> - process_http_put(Request, StateName, StateData, - RidAllow) - end. - -process_http_put(#http_put{rid = Rid, attrs = Attrs, - payload = Payload, hold = Hold, stream = StreamTo, - ip = IP} = - Request, - StateName, StateData, RidAllow) -> - ?DEBUG("Actually processing request: ~p", [Request]), - Key = fxml:get_attr_s(<<"key">>, Attrs), - NewKey = fxml:get_attr_s(<<"newkey">>, Attrs), - KeyAllow = case RidAllow of - repeat -> true; - false -> false; - {true, _} -> - case StateData#state.key of - <<"">> -> true; - OldKey -> - NextKey = p1_sha:sha(Key), - ?DEBUG("Key/OldKey/NextKey: ~s/~s/~s", - [Key, OldKey, NextKey]), - if OldKey == NextKey -> true; - true -> ?DEBUG("wrong key: ~s", [Key]), false - end - end - end, - TNow = p1_time_compat:system_time(micro_seconds), - LastPoll = if Payload == [] -> TNow; - true -> 0 - end, - if (Payload == []) and (Hold == 0) and - (TNow - StateData#state.last_poll < (?MIN_POLLING)) -> - Reply = {error, polling_too_frequently}, - {reply, Reply, StateName, StateData}; - KeyAllow -> - case RidAllow of - false -> - Reply = {error, not_exists}, - {reply, Reply, StateName, StateData}; - repeat -> - ?DEBUG("REPEATING ~p", [Rid]), - case [El#hbr.out - || El <- StateData#state.req_list, El#hbr.rid == Rid] - of - [] -> {error, not_exists}; - [Out | _XS] -> - if (Rid == StateData#state.rid) and - (StateData#state.http_receiver /= undefined) -> - {reply, ok, StateName, StateData}; - true -> - Reply = {repeat, lists:reverse(Out)}, - {reply, Reply, StateName, - StateData#state{last_poll = LastPoll}} - end - end; - {true, Pause} -> - SaveKey = if NewKey == <<"">> -> Key; - true -> NewKey - end, - ?DEBUG(" -- SaveKey: ~s~n", [SaveKey]), - ReqList1 = [El - || El <- StateData#state.req_list, - El#hbr.rid > Rid - 1 - Hold], - ReqList = case lists:keymember(Rid, #hbr.rid, ReqList1) - of - true -> ReqList1; - false -> - [#hbr{rid = Rid, key = StateData#state.key, - out = []} - | ReqList1] - end, - ?DEBUG("reqlist: ~p", [ReqList]), - cancel_timer(StateData#state.timer), - Timer = set_inactivity_timer(Pause, - StateData#state.max_inactivity), - case StateData#state.waiting_input of - false -> - Input = lists:foldl(fun queue:in/2, - StateData#state.input, Payload), - Reply = ok, - process_buffered_request(Reply, StateName, - StateData#state{input = Input, - rid = Rid, - key = SaveKey, - ctime = TNow, - timer = Timer, - pause = Pause, - last_poll = - LastPoll, - req_list = - ReqList, - ip = IP}); - C2SPid -> - case StreamTo of - {To, <<"">>} -> - gen_fsm:send_event(C2SPid, - {xmlstreamstart, - <<"stream:stream">>, - [{<<"to">>, To}, - {<<"xmlns">>, ?NS_CLIENT}, - {<<"xmlns:stream">>, - ?NS_STREAM}]}); - {To, Version} -> - gen_fsm:send_event(C2SPid, - {xmlstreamstart, - <<"stream:stream">>, - [{<<"to">>, To}, - {<<"xmlns">>, ?NS_CLIENT}, - {<<"version">>, Version}, - {<<"xmlns:stream">>, - ?NS_STREAM}]}); - _ -> ok - end, - MaxInactivity = get_max_inactivity(StreamTo, - StateData#state.max_inactivity), - MaxPause = get_max_inactivity(StreamTo, - StateData#state.max_pause), - ?DEBUG("really sending now: ~p", [Payload]), - lists:foreach(fun ({xmlstreamend, End}) -> - gen_fsm:send_event(C2SPid, - {xmlstreamend, - End}); - (El) -> - gen_fsm:send_event(C2SPid, - {xmlstreamelement, - El}) - end, - Payload), - Reply = ok, - process_buffered_request(Reply, StateName, - StateData#state{input = - queue:new(), - rid = Rid, - key = SaveKey, - ctime = TNow, - timer = Timer, - pause = Pause, - last_poll = - LastPoll, - req_list = - ReqList, - max_inactivity = - MaxInactivity, - max_pause = - MaxPause, - ip = IP}) - end - end; - true -> - Reply = {error, bad_key}, - {reply, Reply, StateName, StateData} - end. - -process_buffered_request(Reply, StateName, StateData) -> - Rid = StateData#state.rid, - Requests = StateData#state.unprocessed_req_list, - case lists:keysearch(Rid + 1, 2, Requests) of - {value, Request} -> - ?DEBUG("Processing buffered request: ~p", [Request]), - NewRequests = lists:keydelete(Rid + 1, 2, Requests), - handle_http_put_event(Request, StateName, - StateData#state{unprocessed_req_list = - NewRequests}); - _ -> {reply, Reply, StateName, StateData, hibernate} - end. - -handle_http_put(Sid, Rid, Attrs, Payload, PayloadSize, - StreamStart, IP) -> - case http_put(Sid, Rid, Attrs, Payload, PayloadSize, - StreamStart, IP) - of - {error, not_exists} -> - ?DEBUG("no session associated with sid: ~p", [Sid]), - {404, ?HEADER, <<"">>}; - {{error, Reason}, Sess} -> - ?DEBUG("Error on HTTP put. Reason: ~p", [Reason]), - handle_http_put_error(Reason, Sess); - {{repeat, OutPacket}, Sess} -> - ?DEBUG("http_put said 'repeat!' ...~nOutPacket: ~p", - [OutPacket]), - send_outpacket(Sess, OutPacket); - {{wait, Pause}, _Sess} -> - ?DEBUG("Trafic Shaper: Delaying request ~p", [Rid]), - timer:sleep(Pause), - handle_http_put(Sid, Rid, Attrs, Payload, PayloadSize, - StreamStart, IP); - {ok, Sess} -> - prepare_response(Sess, Rid, [], StreamStart) - end. - -http_put(Sid, Rid, Attrs, Payload, PayloadSize, - StreamStart, IP) -> - ?DEBUG("Looking for session: ~p", [Sid]), - case mnesia:dirty_read({http_bind, Sid}) of - [] -> - {error, not_exists}; - [#http_bind{pid = FsmRef, hold=Hold, - to= {To, StreamVersion}} = Sess] -> - NewStream = case StreamStart of - true -> {To, StreamVersion}; - _ -> <<"">> - end, - {gen_fsm:sync_send_all_state_event( - FsmRef, #http_put{rid = Rid, - attrs = Attrs, - payload = Payload, - payload_size = PayloadSize, - hold = Hold, - stream = NewStream, - ip = IP}, - 30000), Sess} - end. - -handle_http_put_error(Reason, - #http_bind{pid = FsmRef, version = Version}) - when Version >= 0 -> - gen_fsm:sync_send_all_state_event(FsmRef, - {stop, {put_error, Reason}}), - case Reason of - not_exists -> - {200, ?HEADER, - fxml:element_to_binary(#xmlel{name = <<"body">>, - attrs = - [{<<"xmlns">>, ?NS_HTTP_BIND}, - {<<"type">>, <<"terminate">>}, - {<<"condition">>, - <<"item-not-found">>}], - children = []})}; - bad_key -> - {200, ?HEADER, - fxml:element_to_binary(#xmlel{name = <<"body">>, - attrs = - [{<<"xmlns">>, ?NS_HTTP_BIND}, - {<<"type">>, <<"terminate">>}, - {<<"condition">>, - <<"item-not-found">>}], - children = []})}; - polling_too_frequently -> - {200, ?HEADER, - fxml:element_to_binary(#xmlel{name = <<"body">>, - attrs = - [{<<"xmlns">>, ?NS_HTTP_BIND}, - {<<"type">>, <<"terminate">>}, - {<<"condition">>, - <<"policy-violation">>}], - children = []})} - end; -handle_http_put_error(Reason, - #http_bind{pid = FsmRef}) -> - gen_fsm:sync_send_all_state_event(FsmRef, - {stop, {put_error_no_version, Reason}}), - case Reason of - not_exists -> %% bad rid - ?DEBUG("Closing HTTP bind session (Bad rid).", []), - {404, ?HEADER, <<"">>}; - bad_key -> - ?DEBUG("Closing HTTP bind session (Bad key).", []), - {404, ?HEADER, <<"">>}; - polling_too_frequently -> - ?DEBUG("Closing HTTP bind session (User polling " - "too frequently).", - []), - {403, ?HEADER, <<"">>} - end. - -%% Control RID ordering -rid_allow(none, _NewRid, _Attrs, _Hold, _MaxPause) -> - {true, 0}; -rid_allow(OldRid, NewRid, Attrs, Hold, MaxPause) -> - ?DEBUG("Previous rid / New rid: ~p/~p", - [OldRid, NewRid]), - if - %% We did not miss any packet, we can process it immediately: - NewRid == OldRid + 1 -> - case catch - binary_to_integer(fxml:get_attr_s(<<"pause">>, - Attrs)) - of - {'EXIT', _} -> {true, 0}; - Pause1 when Pause1 =< MaxPause -> - ?DEBUG("got pause: ~p", [Pause1]), {true, Pause1}; - _ -> {true, 0} - end; - %% We have missed packets, we need to cached it to process it later on: - (OldRid < NewRid) and (NewRid =< OldRid + Hold + 1) -> - buffer; - (NewRid =< OldRid) and (NewRid > OldRid - Hold - 1) -> - repeat; - true -> false - end. - -update_shaper(ShaperState, PayloadSize) -> - {NewShaperState, Pause} = shaper:update(ShaperState, - PayloadSize), - if Pause > 0 -> - ShaperTimer = erlang:start_timer(Pause, self(), - activate), - {NewShaperState, ShaperTimer}; - true -> {NewShaperState, undefined} - end. - -prepare_response(Sess, Rid, OutputEls, StreamStart) -> - receive after Sess#http_bind.process_delay -> ok end, - case catch http_get(Sess, Rid) of - {ok, cancel} -> - {200, ?HEADER, - <<"">>}; - {ok, empty} -> - {200, ?HEADER, - <<"">>}; - {ok, terminate} -> - {200, ?HEADER, - <<"">>}; - {ok, ROutPacket} -> - OutPacket = lists:reverse(ROutPacket), - ?DEBUG("OutPacket: ~p", [OutputEls ++ OutPacket]), - prepare_outpacket_response(Sess, Rid, - OutputEls ++ OutPacket, StreamStart); - {'EXIT', {shutdown, _}} -> - {200, ?HEADER, - <<"">>}; - {'EXIT', _Reason} -> - {200, ?HEADER, - <<"">>} - end. - -%% Send output payloads on establised sessions -prepare_outpacket_response(Sess, _Rid, OutPacket, - false) -> - case catch send_outpacket(Sess, OutPacket) of - {'EXIT', _Reason} -> - ?DEBUG("Error in sending packet ~p ", [_Reason]), - {200, ?HEADER, - <<"">>}; - SendRes -> SendRes - end; -%% Handle a new session along with its output payload -prepare_outpacket_response(#http_bind{id = Sid, - wait = Wait, hold = Hold, to = To} = - _Sess, - _Rid, OutPacket, true) -> - case OutPacket of - [{xmlstreamstart, _, OutAttrs} | Els] -> - AuthID = fxml:get_attr_s(<<"id">>, OutAttrs), - From = fxml:get_attr_s(<<"from">>, OutAttrs), - Version = fxml:get_attr_s(<<"version">>, OutAttrs), - OutEls = case Els of - [] -> []; - [{xmlstreamelement, - #xmlel{name = <<"stream:features">>, - attrs = StreamAttribs, children = StreamEls}} - | StreamTail] -> - TypedTail = [check_default_xmlns(OEl) - || {xmlstreamelement, OEl} <- StreamTail], - [#xmlel{name = <<"stream:features">>, - attrs = - [{<<"xmlns:stream">>, ?NS_STREAM}] ++ - StreamAttribs, - children = StreamEls}] - ++ TypedTail; - StreamTail -> - [check_default_xmlns(OEl) - || {xmlstreamelement, OEl} <- StreamTail] - end, - case OutEls of - [#xmlel{name = <<"stream:error">>}] -> - {200, ?HEADER, - <<"">>}; - _ -> - BOSH_attribs = [{<<"authid">>, AuthID}, - {<<"xmlns:xmpp">>, ?NS_BOSH}, - {<<"xmlns:stream">>, ?NS_STREAM}] - ++ - case OutEls of - [] -> []; - _ -> [{<<"xmpp:version">>, Version}] - end, - MaxInactivity = get_max_inactivity(To, ?MAX_INACTIVITY), - MaxPause = get_max_pause(To), - {200, ?HEADER, - fxml:element_to_binary(#xmlel{name = <<"body">>, - attrs = - [{<<"xmlns">>, ?NS_HTTP_BIND}, - {<<"sid">>, Sid}, - {<<"wait">>, - integer_to_binary(Wait)}, - {<<"requests">>, - integer_to_binary(Hold + 1)}, - {<<"inactivity">>, - integer_to_binary( - trunc(MaxInactivity / 1000))}, - {<<"maxpause">>, - integer_to_binary(MaxPause)}, - {<<"polling">>, - integer_to_binary( - trunc((?MIN_POLLING) / 1000000))}, - {<<"ver">>, ?BOSH_VERSION}, - {<<"from">>, From}, - {<<"secure">>, <<"true">>}] - ++ BOSH_attribs, - children = OutEls})} - end; - _ -> - {200, ?HEADER, - <<"">>} - end. - -http_get(#http_bind{pid = FsmRef, wait = Wait, - hold = Hold}, - Rid) -> - gen_fsm:sync_send_all_state_event(FsmRef, - {http_get, Rid, Wait, Hold}, - 2 * (?MAX_WAIT) * 1000). - -send_outpacket(#http_bind{pid = FsmRef}, OutPacket) -> - case OutPacket of - [] -> - {200, ?HEADER, - <<"">>}; - [{xmlstreamend, _}] -> - gen_fsm:sync_send_all_state_event(FsmRef, - {stop, stream_closed}), - {200, ?HEADER, - <<"">>}; - _ -> - AllElements = lists:all(fun ({xmlstreamelement, - #xmlel{name = <<"stream:error">>}}) -> - false; - ({xmlstreamelement, _}) -> true; - ({xmlstreamraw, _}) -> true; - (_) -> false - end, - OutPacket), - case AllElements of - true -> - TypedEls = lists:foldl(fun ({xmlstreamelement, El}, - Acc) -> - Acc ++ - [fxml:element_to_binary(check_default_xmlns(El))]; - ({xmlstreamraw, R}, Acc) -> - Acc ++ [R] - end, - [], OutPacket), - Body = <<"", - (iolist_to_binary(TypedEls))/binary, "">>, - ?DEBUG(" --- outgoing data --- ~n~s~n --- END " - "--- ~n", - [Body]), - {200, ?HEADER, Body}; - false -> - case OutPacket of - [{xmlstreamstart, _, _} | SEls] -> - OutEls = case SEls of - [{xmlstreamelement, - #xmlel{name = <<"stream:features">>, - attrs = StreamAttribs, - children = StreamEls}} - | StreamTail] -> - TypedTail = [check_default_xmlns(OEl) - || {xmlstreamelement, OEl} - <- StreamTail], - [#xmlel{name = <<"stream:features">>, - attrs = - [{<<"xmlns:stream">>, - ?NS_STREAM}] - ++ StreamAttribs, - children = StreamEls}] - ++ TypedTail; - StreamTail -> - [check_default_xmlns(OEl) - || {xmlstreamelement, OEl} <- StreamTail] - end, - {200, ?HEADER, - fxml:element_to_binary(#xmlel{name = <<"body">>, - attrs = - [{<<"xmlns">>, - ?NS_HTTP_BIND}], - children = OutEls})}; - _ -> - SErrCond = lists:filter(fun ({xmlstreamelement, - #xmlel{name = - <<"stream:error">>}}) -> - true; - (_) -> false - end, - OutPacket), - StreamErrCond = case SErrCond of - [] -> null; - [{xmlstreamelement, - #xmlel{} = StreamErrorTag} - | _] -> - [StreamErrorTag] - end, - gen_fsm:sync_send_all_state_event(FsmRef, - {stop, - {stream_error, - OutPacket}}), - case StreamErrCond of - null -> - {200, ?HEADER, - <<"">>}; - _ -> - {200, ?HEADER, - <<"", - (elements_to_string(StreamErrCond))/binary, - "">>} - end - end - end - end. - -parse_request(Data, PayloadSize, MaxStanzaSize) -> - ?DEBUG("--- incoming data --- ~n~s~n --- END " - "--- ", - [Data]), - case fxml_stream:parse_element(Data) of - #xmlel{name = <<"body">>, attrs = Attrs, - children = Els} -> - Xmlns = fxml:get_attr_s(<<"xmlns">>, Attrs), - if Xmlns /= (?NS_HTTP_BIND) -> {error, bad_request}; - true -> - case catch - binary_to_integer(fxml:get_attr_s(<<"rid">>, - Attrs)) - of - {'EXIT', _} -> {error, bad_request}; - Rid -> - FixedEls = lists:filter(fun (I) -> - case I of - #xmlel{} -> true; - _ -> false - end - end, - Els), - Sid = fxml:get_attr_s(<<"sid">>, Attrs), - if PayloadSize =< MaxStanzaSize -> - {ok, {Sid, Rid, Attrs, FixedEls}}; - true -> {size_limit, Sid} - end - end - end; - #xmlel{} -> {error, bad_request}; - {error, _Reason} -> {error, bad_request} - end. - -send_receiver_reply(undefined, _Reply) -> ok; -send_receiver_reply(Receiver, Reply) -> - gen_fsm:reply(Receiver, Reply). - -%% Cancel timer and empty message queue. -cancel_timer(undefined) -> ok; -cancel_timer(Timer) -> - erlang:cancel_timer(Timer), - receive {timeout, Timer, _} -> ok after 0 -> ok end. - -%% If client asked for a pause (pause > 0), we apply the pause value -%% as inactivity timer: -set_inactivity_timer(Pause, _MaxInactivity) - when Pause > 0 -> - erlang:start_timer(Pause * 1000, self(), []); -%% Otherwise, we apply the max_inactivity value as inactivity timer: -set_inactivity_timer(_Pause, MaxInactivity) -> - erlang:start_timer(MaxInactivity, self(), []). - -elements_to_string([]) -> []; -elements_to_string([El | Els]) -> - [fxml:element_to_binary(El) | elements_to_string(Els)]. - -%% @spec (To, Default::integer()) -> integer() -%% where To = [] | {Host::string(), Version::string()} -get_max_inactivity({Host, _}, Default) -> - case gen_mod:get_module_opt(Host, mod_http_bind, max_inactivity, - fun(I) when is_integer(I), I>0 -> I end, - undefined) - of - Seconds when is_integer(Seconds) -> Seconds * 1000; - undefined -> Default - end; -get_max_inactivity(_, Default) -> Default. - -get_max_pause({Host, _}) -> - gen_mod:get_module_opt(Host, mod_http_bind, max_pause, - fun(I) when is_integer(I), I>0 -> I end, - ?MAX_PAUSE); -get_max_pause(_) -> ?MAX_PAUSE. - -check_default_xmlns(#xmlel{name = Name, attrs = Attrs, - children = Els} = - El) -> - case fxml:get_tag_attr_s(<<"xmlns">>, El) of - <<"">> -> - #xmlel{name = Name, - attrs = [{<<"xmlns">>, ?NS_CLIENT} | Attrs], - children = Els}; - _ -> El - end; -check_default_xmlns(El) -> El. - -%% Check that mod_http_bind has been defined in config file. -%% Print a warning in log file if this is not the case. -check_bind_module(XmppDomain) -> - case gen_mod:is_loaded(XmppDomain, mod_http_bind) of - true -> true; - false -> - ?ERROR_MSG("You are trying to use BOSH (HTTP Bind) " - "in host ~p, but the module mod_http_bind " - "is not started in that host. Configure " - "your BOSH client to connect to the correct " - "host, or add your desired host to the " - "configuration, or check your 'modules' " - "section in your ejabberd configuration " - "file.", - [XmppDomain]), - false - end. diff --git a/src/ejabberd_http_ws.erl b/src/ejabberd_http_ws.erl index b92345dd4..c14ed2d58 100644 --- a/src/ejabberd_http_ws.erl +++ b/src/ejabberd_http_ws.erl @@ -5,7 +5,7 @@ %%% Created : 09-10-2010 by Eric Cestari %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -23,41 +23,34 @@ %%% %%%---------------------------------------------------------------------- -module(ejabberd_http_ws). - --behaviour(ejabberd_config). - -author('ecestari@process-one.net'). - --behaviour(gen_fsm). +-behaviour(xmpp_socket). +-behaviour(p1_fsm). -export([start/1, start_link/1, init/1, handle_event/3, handle_sync_event/4, code_change/4, handle_info/3, terminate/3, send_xml/2, setopts/2, sockname/1, - peername/1, controlling_process/2, become_controller/2, - close/1, socket_handoff/6, opt_type/1]). + peername/1, controlling_process/2, get_owner/1, + reset_stream/1, close/1, change_shaper/2, + socket_handoff/3, get_transport/1]). --include("ejabberd.hrl"). -include("logger.hrl"). --include("xmpp.hrl"). +-include_lib("xmpp/include/xmpp.hrl"). -include("ejabberd_http.hrl"). --define(PING_INTERVAL, 60). --define(WEBSOCKET_TIMEOUT, 300). - -record(state, {socket :: ws_socket(), - ping_interval = ?PING_INTERVAL :: non_neg_integer(), + ping_interval :: non_neg_integer(), ping_timer = make_ref() :: reference(), - pong_expected :: boolean(), - timeout = ?WEBSOCKET_TIMEOUT :: non_neg_integer(), + pong_expected = false :: boolean(), + timeout :: non_neg_integer(), timer = make_ref() :: reference(), input = [] :: list(), - waiting_input = false :: false | pid(), - last_receiver :: pid(), - ws :: {#ws{}, pid()}, - rfc_compilant = undefined :: boolean() | undefined}). + active = false :: boolean(), + c2s_pid :: pid(), + ws :: {#ws{}, pid()}}). %-define(DBGFSM, true). @@ -75,19 +68,25 @@ -export_type([ws_socket/0]). start(WS) -> - gen_fsm:start(?MODULE, [WS], ?FSMOPTS). + p1_fsm:start(?MODULE, [WS], ?FSMOPTS). start_link(WS) -> - gen_fsm:start_link(?MODULE, [WS], ?FSMOPTS). + p1_fsm:start_link(?MODULE, [WS], ?FSMOPTS). send_xml({http_ws, FsmRef, _IP}, Packet) -> - gen_fsm:sync_send_all_state_event(FsmRef, - {send_xml, Packet}). + case catch p1_fsm:sync_send_all_state_event(FsmRef, + {send_xml, Packet}, + 15000) + of + {'EXIT', {timeout, _}} -> {error, timeout}; + {'EXIT', _} -> {error, einval}; + Res -> Res + end. setopts({http_ws, FsmRef, _IP}, Opts) -> case lists:member({active, once}, Opts) of true -> - gen_fsm:send_all_state_event(FsmRef, + p1_fsm:send_all_state_event(FsmRef, {activate, self()}); _ -> ok end. @@ -98,16 +97,23 @@ peername({http_ws, _FsmRef, IP}) -> {ok, IP}. controlling_process(_Socket, _Pid) -> ok. -become_controller(FsmRef, C2SPid) -> - gen_fsm:send_all_state_event(FsmRef, - {become_controller, C2SPid}). - close({http_ws, FsmRef, _IP}) -> - catch gen_fsm:sync_send_all_state_event(FsmRef, close). + catch p1_fsm:sync_send_all_state_event(FsmRef, close). -socket_handoff(LocalPath, Request, Socket, SockMod, Buf, Opts) -> - ejabberd_websocket:socket_handoff(LocalPath, Request, Socket, SockMod, - Buf, Opts, ?MODULE, fun get_human_html_xmlel/0). +reset_stream({http_ws, _FsmRef, _IP} = Socket) -> + Socket. + +change_shaper({http_ws, FsmRef, _IP}, Shaper) -> + p1_fsm:send_all_state_event(FsmRef, {new_shaper, Shaper}). + +get_transport(_Socket) -> + websocket. + +get_owner({http_ws, FsmRef, _IP}) -> + FsmRef. + +socket_handoff(LocalPath, Request, Opts) -> + ejabberd_websocket:socket_handoff(LocalPath, Request, Opts, ?MODULE, fun get_human_html_xmlel/0). %%% Internal @@ -116,102 +122,85 @@ init([{#ws{ip = IP, http_opts = HOpts}, _} = WS]) -> ({max_ack_queue, _}) -> true; ({ack_timeout, _}) -> true; ({resume_timeout, _}) -> true; + ({allow_unencrypted_sasl2, _}) -> true; ({max_resume_timeout, _}) -> true; ({resend_on_timeout, _}) -> true; + ({access, _}) -> true; (_) -> false end, HOpts), - Opts = [{xml_socket, true} | ejabberd_c2s_config:get_c2s_limits() ++ SOpts], - PingInterval = ejabberd_config:get_option( - {websocket_ping_interval, ?MYNAME}, - fun(I) when is_integer(I), I>=0 -> I end, - ?PING_INTERVAL) * 1000, - WSTimeout = ejabberd_config:get_option( - {websocket_timeout, ?MYNAME}, - fun(I) when is_integer(I), I>0 -> I end, - ?WEBSOCKET_TIMEOUT) * 1000, + Opts = ejabberd_c2s_config:get_c2s_limits() ++ SOpts, + PingInterval = ejabberd_option:websocket_ping_interval(), + WSTimeout = ejabberd_option:websocket_timeout(), Socket = {http_ws, self(), IP}, ?DEBUG("Client connected through websocket ~p", [Socket]), - ejabberd_socket:start(ejabberd_c2s, ?MODULE, Socket, - Opts), - Timer = erlang:start_timer(WSTimeout, self(), []), - {ok, loop, - #state{socket = Socket, timeout = WSTimeout, - timer = Timer, ws = WS, - ping_interval = PingInterval}}. - -handle_event({activate, From}, StateName, StateData) -> - case StateData#state.input of - [] -> - {next_state, StateName, - StateData#state{waiting_input = From}}; - Input -> - Receiver = From, - lists:foreach(fun(I) when is_binary(I)-> - Receiver ! {tcp, StateData#state.socket, I}; - (I2) -> - Receiver ! {tcp, StateData#state.socket, [I2]} - end, Input), - {next_state, StateName, - StateData#state{input = [], waiting_input = false, - last_receiver = Receiver}} + case ejabberd_c2s:start(?MODULE, Socket, [{receiver, self()}|Opts]) of + {ok, C2SPid} -> + ejabberd_c2s:accept(C2SPid), + Timer = erlang:start_timer(WSTimeout, self(), []), + {ok, loop, + #state{socket = Socket, timeout = WSTimeout, + timer = Timer, ws = WS, c2s_pid = C2SPid, + ping_interval = PingInterval}}; + {error, Reason} -> + {stop, Reason}; + ignore -> + ignore end. +handle_event({activate, From}, StateName, State) -> + State1 = case State#state.input of + [] -> State#state{active = true}; + Input -> + lists:foreach( + fun(I) when is_binary(I)-> + From ! {tcp, State#state.socket, I}; + (I2) -> + From ! {tcp, State#state.socket, [I2]} + end, Input), + State#state{active = false, input = []} + end, + {next_state, StateName, State1#state{c2s_pid = From}}; +handle_event({new_shaper, Shaper}, StateName, #state{ws = {_, WsPid}} = StateData) -> + WsPid ! {new_shaper, Shaper}, + {next_state, StateName, StateData}. + handle_sync_event({send_xml, Packet}, _From, StateName, - #state{ws = {_, WsPid}, rfc_compilant = R} = StateData) -> - Packet2 = case {case R of undefined -> true; V -> V end, Packet} of - {true, {xmlstreamstart, _, Attrs}} -> - Attrs2 = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>} | - lists:keydelete(<<"xmlns">>, 1, lists:keydelete(<<"xmlns:stream">>, 1, Attrs))], - {xmlstreamelement, #xmlel{name = <<"open">>, attrs = Attrs2}}; - {true, {xmlstreamend, _}} -> - {xmlstreamelement, #xmlel{name = <<"close">>, - attrs = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>}]}}; - {true, {xmlstreamraw, <<"\r\n\r\n">>}} -> % cdata ping - skip; - {true, {xmlstreamelement, #xmlel{name=Name2} = El2}} -> - El3 = case Name2 of - <<"stream:", _/binary>> -> - fxml:replace_tag_attr(<<"xmlns:stream">>, ?NS_STREAM, El2); - _ -> - case fxml:get_tag_attr_s(<<"xmlns">>, El2) of - <<"">> -> - fxml:replace_tag_attr(<<"xmlns">>, <<"jabber:client">>, El2); - _ -> - El2 - end - end, - {xmlstreamelement , El3}; - _ -> - Packet - end, - case Packet2 of - {xmlstreamstart, Name, Attrs3} -> - B = fxml:element_to_binary(#xmlel{name = Name, attrs = Attrs3}), - WsPid ! {send, <<(binary:part(B, 0, byte_size(B)-2))/binary, ">">>}; - {xmlstreamend, Name} -> - WsPid ! {send, <<"">>}; - {xmlstreamelement, El} -> - WsPid ! {send, fxml:element_to_binary(El)}; - {xmlstreamraw, Bin} -> - WsPid ! {send, Bin}; - {xmlstreamcdata, Bin2} -> - WsPid ! {send, Bin2}; - skip -> - ok - end, - SN2 = case Packet2 of - {xmlstreamelement, #xmlel{name = <<"close">>}} -> - stream_end_sent; - _ -> - StateName - end, + #state{ws = {_, WsPid}} = StateData) -> + SN2 = case Packet of + {xmlstreamstart, _, Attrs} -> + Attrs2 = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>} | + lists:keydelete(<<"xmlns">>, 1, lists:keydelete(<<"xmlns:stream">>, 1, Attrs))], + route_el(WsPid, #xmlel{name = <<"open">>, attrs = Attrs2}), + StateName; + {xmlstreamend, _} -> + route_el(WsPid, #xmlel{name = <<"close">>, + attrs = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>}]}), + stream_end_sent; + {xmlstreamraw, <<"\r\n\r\n">>} -> + % cdata ping + StateName; + {xmlstreamelement, #xmlel{name = Name2} = El2} -> + El3 = case Name2 of + <<"stream:", _/binary>> -> + fxml:replace_tag_attr(<<"xmlns:stream">>, ?NS_STREAM, El2); + _ -> + case fxml:get_tag_attr_s(<<"xmlns">>, El2) of + <<"">> -> + fxml:replace_tag_attr(<<"xmlns">>, <<"jabber:client">>, El2); + _ -> + El2 + end + end, + route_el(WsPid, El3), + StateName + end, {reply, ok, SN2, StateData}; -handle_sync_event(close, _From, StateName, #state{ws = {_, WsPid}, rfc_compilant = true} = StateData) - when StateName /= stream_end_sent -> +handle_sync_event(close, _From, StateName, #state{ws = {_, WsPid}} = StateData) + when StateName /= stream_end_sent -> Close = #xmlel{name = <<"close">>, attrs = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>}]}, - WsPid ! {send, fxml:element_to_binary(Close)}, + route_text(WsPid, fxml:element_to_binary(Close)), {stop, normal, StateData}; handle_sync_event(close, _From, _StateName, StateData) -> {stop, normal, StateData}. @@ -220,14 +209,13 @@ handle_info(closed, _StateName, StateData) -> {stop, normal, StateData}; handle_info({received, Packet}, StateName, StateDataI) -> {StateData, Parsed} = parse(StateDataI, Packet), - SD = case StateData#state.waiting_input of + SD = case StateData#state.active of false -> - Input = StateData#state.input ++ if is_binary(Parsed) -> [Parsed]; true -> Parsed end, + Input = StateData#state.input ++ Parsed, StateData#state{input = Input}; - Receiver -> - Receiver ! {tcp, StateData#state.socket, Parsed}, - setup_timers(StateData#state{waiting_input = false, - last_receiver = Receiver}) + true -> + StateData#state.c2s_pid ! {tcp, StateData#state.socket, Parsed}, + setup_timers(StateData#state{active = false}) end, {next_state, StateName, SD}; handle_info(PingPong, StateName, StateData) when PingPong == ping orelse @@ -237,18 +225,20 @@ handle_info(PingPong, StateName, StateData) when PingPong == ping orelse StateData2#state{pong_expected = false}}; handle_info({timeout, Timer, _}, _StateName, #state{timer = Timer} = StateData) -> + ?DEBUG("Closing websocket connection from hitting inactivity timeout", []), {stop, normal, StateData}; handle_info({timeout, Timer, _}, StateName, #state{ping_timer = Timer, ws = {_, WsPid}} = StateData) -> case StateData#state.pong_expected of false -> - cancel_timer(StateData#state.ping_timer), + misc:cancel_timer(StateData#state.ping_timer), PingTimer = erlang:start_timer(StateData#state.ping_interval, self(), []), WsPid ! {ping, <<>>}, {next_state, StateName, StateData#state{ping_timer = PingTimer, pong_expected = true}}; true -> + ?DEBUG("Closing websocket connection from missing pongs", []), {stop, normal, StateData} end; handle_info(_, StateName, StateData) -> @@ -258,19 +248,13 @@ code_change(_OldVsn, StateName, StateData, _Extra) -> {ok, StateName, StateData}. terminate(_Reason, _StateName, StateData) -> - case StateData#state.waiting_input of - false -> ok; - Receiver -> - ?DEBUG("C2S Pid : ~p", [Receiver]), - Receiver ! {tcp_closed, StateData#state.socket} - end, - ok. + StateData#state.c2s_pid ! {tcp_closed, StateData#state.socket}. setup_timers(StateData) -> - cancel_timer(StateData#state.timer), + misc:cancel_timer(StateData#state.timer), Timer = erlang:start_timer(StateData#state.timeout, self(), []), - cancel_timer(StateData#state.ping_timer), + misc:cancel_timer(StateData#state.ping_timer), PingTimer = case StateData#state.ping_interval of 0 -> StateData#state.ping_timer; V -> erlang:start_timer(V, self(), []) @@ -278,12 +262,8 @@ setup_timers(StateData) -> StateData#state{timer = Timer, ping_timer = PingTimer, pong_expected = false}. -cancel_timer(Timer) -> - erlang:cancel_timer(Timer), - receive {timeout, Timer, _} -> ok after 0 -> ok end. - get_human_html_xmlel() -> - Heading = <<"ejabberd ", (jlib:atom_to_binary(?MODULE))/binary>>, + Heading = <<"ejabberd ", (misc:atom_to_binary(?MODULE))/binary>>, #xmlel{name = <<"html">>, attrs = [{<<"xmlns">>, <<"http://www.w3.org/1999/xhtml">>}], @@ -314,57 +294,29 @@ get_human_html_xmlel() -> "client that supports it.">>}]}]}]}. -parse(#state{rfc_compilant = C} = State, Data) -> - case C of - undefined -> - P = fxml_stream:new(self()), - P2 = fxml_stream:parse(P, Data), - fxml_stream:close(P2), - case parsed_items([]) of - error -> - {State#state{rfc_compilant = true}, <<"parse error">>}; - [] -> - {State#state{rfc_compilant = true}, <<"parse error">>}; - [{xmlstreamstart, <<"open">>, _} | _] -> - parse(State#state{rfc_compilant = true}, Data); - _ -> - parse(State#state{rfc_compilant = false}, Data) - end; - true -> - El = fxml_stream:parse_element(Data), - case El of - #xmlel{name = <<"open">>, attrs = Attrs} -> - Attrs2 = [{<<"xmlns:stream">>, ?NS_STREAM}, {<<"xmlns">>, <<"jabber:client">>} | - lists:keydelete(<<"xmlns">>, 1, lists:keydelete(<<"xmlns:stream">>, 1, Attrs))], - {State, [{xmlstreamstart, <<"stream:stream">>, Attrs2}]}; - #xmlel{name = <<"close">>} -> - {State, [{xmlstreamend, <<"stream:stream">>}]}; - {error, _} -> - {State, <<"parse error">>}; - _ -> - {State, [El]} - end; - false -> - {State, Data} +parse(State, Data) -> + El = fxml_stream:parse_element(Data), + case El of + #xmlel{name = <<"open">>, attrs = Attrs} -> + Attrs2 = [{<<"xmlns:stream">>, ?NS_STREAM}, {<<"xmlns">>, <<"jabber:client">>} | + lists:keydelete(<<"xmlns">>, 1, lists:keydelete(<<"xmlns:stream">>, 1, Attrs))], + {State, [{xmlstreamstart, <<"stream:stream">>, Attrs2}]}; + #xmlel{name = <<"close">>} -> + {State, [{xmlstreamend, <<"stream:stream">>}]}; + {error, _} -> + {State, [{xmlstreamerror, {4, <<"not well-formed">>}}]}; + _ -> + {State, [El]} end. -parsed_items(List) -> +-spec route_text(pid(), binary()) -> ok. +route_text(Pid, Data) -> + Pid ! {text_with_reply, Data, self()}, receive - {'$gen_event', El} - when element(1, El) == xmlel; - element(1, El) == xmlstreamstart; - element(1, El) == xmlstreamelement; - element(1, El) == xmlstreamend -> - parsed_items([El | List]); - {'$gen_event', {xmlstreamerror, _}} -> - error - after 0 -> - lists:reverse(List) + {text_reply, Pid} -> + ok end. -opt_type(websocket_ping_interval) -> - fun (I) when is_integer(I), I >= 0 -> I end; -opt_type(websocket_timeout) -> - fun (I) when is_integer(I), I > 0 -> I end; -opt_type(_) -> - [websocket_ping_interval, websocket_timeout]. +-spec route_el(pid(), xmlel() | cdata()) -> ok. +route_el(Pid, Data) -> + route_text(Pid, fxml:element_to_binary(Data)). diff --git a/src/ejabberd_idna.erl b/src/ejabberd_idna.erl deleted file mode 100644 index f889b411a..000000000 --- a/src/ejabberd_idna.erl +++ /dev/null @@ -1,224 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : ejabberd_idna.erl -%%% Author : Alexey Shchepin -%%% Purpose : Support for IDNA (RFC3490) -%%% Created : 10 Apr 2004 by Alexey Shchepin -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne -%%% -%%% This program is free software; you can redistribute it and/or -%%% modify it under the terms of the GNU General Public License as -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License along -%%% with this program; if not, write to the Free Software Foundation, Inc., -%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -%%% -%%%---------------------------------------------------------------------- - --module(ejabberd_idna). - --author('alexey@process-one.net'). - --export([domain_utf8_to_ascii/1, - domain_ucs2_to_ascii/1, - utf8_to_ucs2/1]). - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). --endif. - --spec domain_utf8_to_ascii(binary()) -> false | binary(). - -domain_utf8_to_ascii(Domain) -> - domain_ucs2_to_ascii(utf8_to_ucs2(Domain)). - -utf8_to_ucs2(S) -> - utf8_to_ucs2(binary_to_list(S), ""). - -utf8_to_ucs2([], R) -> lists:reverse(R); -utf8_to_ucs2([C | S], R) when C < 128 -> - utf8_to_ucs2(S, [C | R]); -utf8_to_ucs2([C1, C2 | S], R) when C1 < 224 -> - utf8_to_ucs2(S, [C1 band 31 bsl 6 bor C2 band 63 | R]); -utf8_to_ucs2([C1, C2, C3 | S], R) when C1 < 240 -> - utf8_to_ucs2(S, - [C1 band 15 bsl 12 bor (C2 band 63 bsl 6) bor C3 band 63 - | R]). - --spec domain_ucs2_to_ascii(list()) -> false | binary(). - -domain_ucs2_to_ascii(Domain) -> - case catch domain_ucs2_to_ascii1(Domain) of - {'EXIT', _Reason} -> false; - Res -> iolist_to_binary(Res) - end. - -domain_ucs2_to_ascii1(Domain) -> - Parts = string:tokens(Domain, - [46, 12290, 65294, 65377]), - ASCIIParts = lists:map(fun (P) -> to_ascii(P) end, - Parts), - string:strip(lists:flatmap(fun (P) -> [$. | P] end, - ASCIIParts), - left, $.). - -%% Domain names are already nameprep'ed in ejabberd, so we skiping this step -to_ascii(Name) -> - false = lists:any(fun (C) - when (0 =< C) and (C =< 44) or - (46 =< C) and (C =< 47) - or (58 =< C) and (C =< 64) - or (91 =< C) and (C =< 96) - or (123 =< C) and (C =< 127) -> - true; - (_) -> false - end, - Name), - case Name of - [H | _] when H /= $- -> true = lists:last(Name) /= $- - end, - ASCIIName = case lists:any(fun (C) -> C > 127 end, Name) - of - true -> - true = case Name of - "xn--" ++ _ -> false; - _ -> true - end, - "xn--" ++ punycode_encode(Name); - false -> Name - end, - L = length(ASCIIName), - true = (1 =< L) and (L =< 63), - ASCIIName. - -%%% PUNYCODE (RFC3492) - --define(BASE, 36). - --define(TMIN, 1). - --define(TMAX, 26). - --define(SKEW, 38). - --define(DAMP, 700). - --define(INITIAL_BIAS, 72). - --define(INITIAL_N, 128). - -punycode_encode(Input) -> - N = (?INITIAL_N), - Delta = 0, - Bias = (?INITIAL_BIAS), - Basic = lists:filter(fun (C) -> C =< 127 end, Input), - NonBasic = lists:filter(fun (C) -> C > 127 end, Input), - L = length(Input), - B = length(Basic), - SNonBasic = lists:usort(NonBasic), - Output1 = if B > 0 -> Basic ++ "-"; - true -> "" - end, - Output2 = punycode_encode1(Input, SNonBasic, B, B, L, N, - Delta, Bias, ""), - Output1 ++ Output2. - -punycode_encode1(Input, [M | SNonBasic], B, H, L, N, - Delta, Bias, Out) - when H < L -> - Delta1 = Delta + (M - N) * (H + 1), - % let n = m - {NewDelta, NewBias, NewH, NewOut} = lists:foldl(fun (C, - {ADelta, ABias, AH, - AOut}) -> - if C < M -> - {ADelta + 1, - ABias, AH, - AOut}; - C == M -> - NewOut = - punycode_encode_delta(ADelta, - ABias, - AOut), - NewBias = - adapt(ADelta, - H + - 1, - H - == - B), - {0, NewBias, - AH + 1, - NewOut}; - true -> - {ADelta, - ABias, AH, - AOut} - end - end, - {Delta1, Bias, H, Out}, - Input), - punycode_encode1(Input, SNonBasic, B, NewH, L, M + 1, - NewDelta + 1, NewBias, NewOut); -punycode_encode1(_Input, _SNonBasic, _B, _H, _L, _N, - _Delta, _Bias, Out) -> - lists:reverse(Out). - -punycode_encode_delta(Delta, Bias, Out) -> - punycode_encode_delta(Delta, Bias, Out, ?BASE). - -punycode_encode_delta(Delta, Bias, Out, K) -> - T = if K =< Bias -> ?TMIN; - K >= Bias + (?TMAX) -> ?TMAX; - true -> K - Bias - end, - if Delta < T -> [codepoint(Delta) | Out]; - true -> - C = T + (Delta - T) rem ((?BASE) - T), - punycode_encode_delta((Delta - T) div ((?BASE) - T), - Bias, [codepoint(C) | Out], K + (?BASE)) - end. - -adapt(Delta, NumPoints, FirstTime) -> - Delta1 = if FirstTime -> Delta div (?DAMP); - true -> Delta div 2 - end, - Delta2 = Delta1 + Delta1 div NumPoints, - adapt1(Delta2, 0). - -adapt1(Delta, K) -> - if Delta > ((?BASE) - (?TMIN)) * (?TMAX) div 2 -> - adapt1(Delta div ((?BASE) - (?TMIN)), K + (?BASE)); - true -> - K + - ((?BASE) - (?TMIN) + 1) * Delta div (Delta + (?SKEW)) - end. - -codepoint(C) -> - if (0 =< C) and (C =< 25) -> C + 97; - (26 =< C) and (C =< 35) -> C + 22 - end. - -%%%=================================================================== -%%% Unit tests -%%%=================================================================== --ifdef(TEST). - -acsii_test() -> - ?assertEqual(<<"test.org">>, domain_utf8_to_ascii(<<"test.org">>)). - -utf8_test() -> - ?assertEqual( - <<"xn--d1acufc.xn--p1ai">>, - domain_utf8_to_ascii( - <<208,180,208,190,208,188,208,181,208,189,46,209,128,209,132>>)). - --endif. diff --git a/src/ejabberd_iq.erl b/src/ejabberd_iq.erl new file mode 100644 index 000000000..5db539cab --- /dev/null +++ b/src/ejabberd_iq.erl @@ -0,0 +1,188 @@ +%%%------------------------------------------------------------------- +%%% File : ejabberd_iq.erl +%%% Author : Evgeny Khramtsov +%%% Purpose : +%%% Created : 10 Nov 2017 by Evgeny Khramtsov +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%------------------------------------------------------------------- + +-module(ejabberd_iq). + +-behaviour(gen_server). + +%% API +-export([start_link/0, route/4, dispatch/1]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +-include_lib("xmpp/include/xmpp.hrl"). +-include("logger.hrl"). + + +-record(state, {expire = infinity :: timeout()}). +-type state() :: #state{}. + +%%%=================================================================== +%%% API +%%%=================================================================== +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +-spec route(iq(), atom() | pid(), term(), non_neg_integer()) -> ok. +route(#iq{type = T} = IQ, Proc, Ctx, Timeout) when T == set; T == get -> + Expire = current_time() + Timeout, + Rnd = p1_rand:get_string(), + ID = encode_id(Expire, Rnd), + ets:insert(?MODULE, {{Expire, Rnd}, Proc, Ctx}), + gen_server:cast(?MODULE, {restart_timer, Expire}), + ejabberd_router:route(IQ#iq{id = ID}). + +-spec dispatch(iq()) -> boolean(). +dispatch(#iq{type = T, id = ID} = IQ) when T == error; T == result -> + case decode_id(ID) of + {ok, Expire, Rnd, Node} -> + ejabberd_cluster:send({?MODULE, Node}, {route, IQ, {Expire, Rnd}}); + error -> + false + end; +dispatch(_) -> + false. + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== +init([]) -> + _ = ets:new(?MODULE, [named_table, ordered_set, public]), + {ok, #state{}}. + +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), + noreply(State). + +handle_cast({restart_timer, Expire}, State) -> + State1 = State#state{expire = min(Expire, State#state.expire)}, + noreply(State1); +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), + noreply(State). + +handle_info({route, IQ, Key}, State) -> + case ets:lookup(?MODULE, Key) of + [{_, Proc, Ctx}] -> + callback(Proc, IQ, Ctx), + ets:delete(?MODULE, Key); + [] -> + ok + end, + noreply(State); +handle_info(timeout, State) -> + Expire = clean(ets:first(?MODULE)), + noreply(State#state{expire = Expire}); +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), + noreply(State). + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +-spec current_time() -> non_neg_integer(). +current_time() -> + erlang:system_time(millisecond). + +-spec clean({non_neg_integer(), binary()} | '$end_of_table') + -> non_neg_integer() | infinity. +clean({Expire, _} = Key) -> + case current_time() of + Time when Time >= Expire -> + case ets:lookup(?MODULE, Key) of + [{_, Proc, Ctx}] -> + callback(Proc, timeout, Ctx), + ets:delete(?MODULE, Key); + [] -> + ok + end, + clean(ets:next(?MODULE, Key)); + _ -> + Expire + end; +clean('$end_of_table') -> + infinity. + +-spec noreply(state()) -> {noreply, state()} | {noreply, state(), non_neg_integer()}. +noreply(#state{expire = Expire} = State) -> + case Expire of + infinity -> + {noreply, State}; + _ -> + Timeout = max(0, Expire - current_time()), + {noreply, State, Timeout} + end. + +-spec encode_id(non_neg_integer(), binary()) -> binary(). +encode_id(Expire, Rnd) -> + ExpireBin = integer_to_binary(Expire), + Node = ejabberd_cluster:node_id(), + CheckSum = calc_checksum(<>), + <<"rr-", ExpireBin/binary, $-, Rnd/binary, $-, CheckSum/binary, $-, Node/binary>>. + +-spec decode_id(binary()) -> {ok, non_neg_integer(), binary(), atom()} | error. +decode_id(<<"rr-", ID/binary>>) -> + try + [ExpireBin, Tail] = binary:split(ID, <<"-">>), + [Rnd, Rest] = binary:split(Tail, <<"-">>), + [CheckSum, NodeBin] = binary:split(Rest, <<"-">>), + CheckSum = calc_checksum(<>), + Node = ejabberd_cluster:get_node_by_id(NodeBin), + Expire = binary_to_integer(ExpireBin), + {ok, Expire, Rnd, Node} + catch _:{badmatch, _} -> + error + end; +decode_id(_) -> + error. + +-spec calc_checksum(binary()) -> binary(). +calc_checksum(Data) -> + Key = ejabberd_config:get_shared_key(), + base64:encode(crypto:hash(sha, <>)). + +-spec callback(atom() | pid(), #iq{} | timeout, term()) -> any(). +callback(undefined, IQRes, Fun) -> + try Fun(IQRes) + catch + Class:Reason:StackTrace -> + ?ERROR_MSG("Failed to process iq response:~n~ts~n** ~ts", + [xmpp:pp(IQRes), + misc:format_exception(2, Class, Reason, StackTrace)]) + end; +callback(Proc, IQRes, Ctx) -> + try + Proc ! {iq_reply, IQRes, Ctx} + catch _:badarg -> + ok + end. diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index a9cc441e9..d23f4f189 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -5,7 +5,7 @@ %%% Created : 16 Nov 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -24,351 +24,457 @@ %%%---------------------------------------------------------------------- -module(ejabberd_listener). - --behaviour(ejabberd_config). +-behaviour(supervisor). -author('alexey@process-one.net'). +-author('ekhramtsov@process-one.net'). --export([start_link/0, init/1, start/3, init/3, +-export([start_link/0, init/1, stop/0, start/3, init/3, start_listeners/0, start_listener/3, stop_listeners/0, - stop_listener/2, parse_listener_portip/2, - add_listener/3, delete_listener/2, transform_options/1, - validate_cfg/1, opt_type/1]). + add_listener/3, delete_listener/2, + config_reloaded/0]). +-export([listen_options/0, listen_opt_type/1, validator/0]). +-export([tls_listeners/0]). --include("ejabberd.hrl"). -include("logger.hrl"). -%% We do not block on send anymore. --define(TCP_SEND_TIMEOUT, 15000). +-type transport() :: tcp | udp. +-type endpoint() :: {inet:port_number(), inet:ip_address(), transport()}. +-type list_opts() :: [{atom(), term()}]. +-type opts() :: #{atom() => term()}. +-type listener() :: {endpoint(), module(), opts()}. +-type sockmod() :: gen_tcp. +-type socket() :: inet:socket(). +-type state() :: term(). + +-export_type([listener/0]). + +-callback start(sockmod(), socket(), state()) -> + {ok, pid()} | {error, any()} | ignore. +-callback start_link(sockmod(), socket(), state()) -> + {ok, pid()} | {error, any()} | ignore. +-callback accept(pid()) -> any(). +-callback listen_opt_type(atom()) -> econf:validator(). +-callback listen_options() -> [{atom(), term()} | atom()]. +-callback tcp_init(socket(), list_opts()) -> state(). +-callback udp_init(socket(), list_opts()) -> state(). + +-optional_callbacks([listen_opt_type/1, tcp_init/2, udp_init/2]). start_link() -> - supervisor:start_link({local, ejabberd_listeners}, ?MODULE, []). - + supervisor:start_link({local, ?MODULE}, ?MODULE, []). init(_) -> - ets:new(listen_sockets, [named_table, public]), - bind_tcp_ports(), - {ok, {{one_for_one, 10, 1}, []}}. + _ = ets:new(?MODULE, [named_table, public]), + ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50), + Listeners = ejabberd_option:listen(), + {ok, {{one_for_one, 10, 1}, listeners_childspec(Listeners)}}. -bind_tcp_ports() -> - case ejabberd_config:get_option(listen, fun validate_cfg/1) of - undefined -> - ignore; - Ls -> - lists:foreach( - fun({Port, Module, Opts}) -> - ModuleRaw = strip_frontend(Module), - case ModuleRaw:socket_type() of - independent -> ok; - _ -> - bind_tcp_port(Port, Module, Opts) - end - end, Ls) - end. +stop() -> + ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 50), + stop_listeners(), + ejabberd_sup:stop_child(?MODULE). -bind_tcp_port(PortIP, Module, RawOpts) -> - try check_listener_options(RawOpts) of - ok -> - {Port, IPT, IPS, IPV, Proto, OptsClean} = parse_listener_portip(PortIP, RawOpts), - {_Opts, SockOpts} = prepare_opts(IPT, IPV, OptsClean), - case Proto of - udp -> ok; - _ -> - ListenSocket = listen_tcp(PortIP, Module, SockOpts, Port, IPS), - ets:insert(listen_sockets, {PortIP, ListenSocket}), - ok - end - catch - throw:{error, Error} -> - ?ERROR_MSG(Error, []) - end. +-spec listeners_childspec([listener()]) -> [supervisor:child_spec()]. +listeners_childspec(Listeners) -> + lists:map( + fun({EndPoint, Module, Opts}) -> + ets:insert(?MODULE, {EndPoint, Module, Opts}), + {EndPoint, + {?MODULE, start, [EndPoint, Module, Opts]}, + transient, brutal_kill, worker, [?MODULE]} + end, Listeners). +-spec start_listeners() -> ok. start_listeners() -> - case ejabberd_config:get_option(listen, fun validate_cfg/1) of - undefined -> - ignore; - Ls -> - Ls2 = lists:map( - fun({Port, Module, Opts}) -> - case start_listener(Port, Module, Opts) of - {ok, _Pid} = R -> R; - {error, Error} -> - throw(Error) - end - end, Ls), - report_duplicated_portips(Ls), - {ok, {{one_for_one, 10, 1}, Ls2}} - end. + Listeners = ejabberd_option:listen(), + lists:foreach( + fun(Spec) -> + supervisor:start_child(?MODULE, Spec) + end, listeners_childspec(Listeners)). -report_duplicated_portips(L) -> - LKeys = [Port || {Port, _, _} <- L], - LNoDupsKeys = proplists:get_keys(L), - case LKeys -- LNoDupsKeys of - [] -> ok; - Dups -> - ?CRITICAL_MSG("In the ejabberd configuration there are duplicated " - "Port number + IP address:~n ~p", - [Dups]) - end. +-spec start(endpoint(), module(), opts()) -> term(). +start(EndPoint, Module, Opts) -> + proc_lib:start_link(?MODULE, init, [EndPoint, Module, Opts]). -start(Port, Module, Opts) -> - %% Check if the module is an ejabberd listener or an independent listener - ModuleRaw = strip_frontend(Module), - case ModuleRaw:socket_type() of - independent -> ModuleRaw:start_listener(Port, Opts); - _ -> start_dependent(Port, Module, Opts) - end. +-spec init(endpoint(), module(), opts()) -> ok. +init({_, _, Transport} = EndPoint, Module, AllOpts) -> + {ModuleOpts, SockOpts} = split_opts(Transport, AllOpts), + init(EndPoint, Module, ModuleOpts, SockOpts). -%% @spec(Port, Module, Opts) -> {ok, Pid} | {error, ErrorMessage} -start_dependent(Port, Module, Opts) -> - try check_listener_options(Opts) of - ok -> - proc_lib:start_link(?MODULE, init, [Port, Module, Opts]) - catch - throw:{error, Error} -> - ?ERROR_MSG(Error, []), - {error, Error} - end. - -init(PortIP, Module, RawOpts) -> - {Port, IPT, IPS, IPV, Proto, OptsClean} = parse_listener_portip(PortIP, RawOpts), - {Opts, SockOpts} = prepare_opts(IPT, IPV, OptsClean), - if Proto == udp -> - init_udp(PortIP, Module, Opts, SockOpts, Port, IPS); - true -> - init_tcp(PortIP, Module, Opts, SockOpts, Port, IPS) - end. - -init_udp(PortIP, Module, Opts, SockOpts, Port, IPS) -> - case gen_udp:open(Port, [binary, +-spec init(endpoint(), module(), opts(), [gen_tcp:option()]) -> ok. +init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) -> + {Port2, ExtraOpts} = case Port of + <<"unix:", Path/binary>> -> + SO = lists:keydelete(ip, 1, SockOpts), + setup_provisional_udsocket_dir(Path), + file:delete(Path), + {0, [{ip, {local, Path}} | SO]}; + _ -> + {Port, SockOpts} + end, + ExtraOpts2 = lists:keydelete(send_timeout, 1, ExtraOpts), + case {gen_udp:open(Port2, [binary, {active, false}, {reuseaddr, true} | - SockOpts]) of - {ok, Socket} -> - %% Inform my parent that this port was opened succesfully - proc_lib:init_ack({ok, self()}), - case erlang:function_exported(Module, udp_init, 2) of - false -> - udp_recv(Socket, Module, Opts); - true -> - case catch Module:udp_init(Socket, Opts) of - {'EXIT', _} = Err -> - ?ERROR_MSG("failed to process callback function " - "~p:~s(~p, ~p): ~p", - [Module, udp_init, Socket, Opts, Err]), - udp_recv(Socket, Module, Opts); - NewOpts -> - udp_recv(Socket, Module, NewOpts) - end - end; - {error, Reason} -> - socket_error(Reason, PortIP, Module, SockOpts, Port, IPS) - end. - -init_tcp(PortIP, Module, Opts, SockOpts, Port, IPS) -> - ListenSocket = listen_tcp(PortIP, Module, SockOpts, Port, IPS), - %% Inform my parent that this port was opened succesfully - proc_lib:init_ack({ok, self()}), - case erlang:function_exported(Module, tcp_init, 2) of - false -> - accept(ListenSocket, Module, Opts); - true -> - case catch Module:tcp_init(ListenSocket, Opts) of - {'EXIT', _} = Err -> - ?ERROR_MSG("failed to process callback function " - "~p:~s(~p, ~p): ~p", - [Module, tcp_init, ListenSocket, Opts, Err]), - accept(ListenSocket, Module, Opts); - NewOpts -> - accept(ListenSocket, Module, NewOpts) - end - end. - -listen_tcp(PortIP, Module, SockOpts, Port, IPS) -> - case ets:lookup(listen_sockets, PortIP) of - [{PortIP, ListenSocket}] -> - ?INFO_MSG("Reusing listening port for ~p", [PortIP]), - ets:delete(listen_sockets, PortIP), - ListenSocket; - _ -> - Res = gen_tcp:listen(Port, [binary, - {packet, 0}, - {active, false}, - {reuseaddr, true}, - {nodelay, true}, - {send_timeout, ?TCP_SEND_TIMEOUT}, - {send_timeout_close, true}, - {keepalive, true} | - SockOpts]), - case Res of - {ok, ListenSocket} -> - ListenSocket; + ExtraOpts2]), + set_definitive_udsocket(Port, Opts)} of + {{ok, Socket}, ok} -> + misc:set_proc_label({?MODULE, udp, Port}), + case inet:sockname(Socket) of + {ok, {Addr, Port1}} -> + proc_lib:init_ack({ok, self()}), + case application:ensure_started(ejabberd) of + ok -> + ?INFO_MSG("Start accepting ~ts connections at ~ts for ~p", + [format_transport(udp, Opts), + format_endpoint({Port1, Addr, udp}), Module]), + Opts1 = opts_to_list(Module, Opts), + case erlang:function_exported(Module, udp_init, 2) of + false -> + udp_recv(Socket, Module, Opts1); + true -> + State = Module:udp_init(Socket, Opts1), + udp_recv(Socket, Module, State) + end; + {error, _} -> + ok + end; {error, Reason} -> - socket_error(Reason, PortIP, Module, SockOpts, Port, IPS) - end + return_socket_error(Reason, EndPoint, Module) + end; + {{error, Reason}, _} -> + return_socket_error(Reason, EndPoint, Module); + {_, {error, Reason} } -> + return_socket_error(Reason, EndPoint, Module) + end; +init({Port, _, tcp} = EndPoint, Module, Opts, SockOpts) -> + case {listen_tcp(Port, SockOpts), + set_definitive_udsocket(Port, Opts)} of + {{ok, ListenSocket}, ok} -> + case inet:sockname(ListenSocket) of + {ok, {Addr, Port1}} -> + proc_lib:init_ack({ok, self()}), + misc:set_proc_label({?MODULE, tcp, Port}), + case application:ensure_started(ejabberd) of + ok -> + Sup = start_module_sup(Module, Opts), + Interval = maps:get(accept_interval, Opts), + Proxy = maps:get(use_proxy_protocol, Opts), + ?INFO_MSG("Start accepting ~ts connections at ~ts for ~p", + [format_transport(tcp, Opts), + format_endpoint({Port1, Addr, tcp}), Module]), + Opts1 = opts_to_list(Module, Opts), + case erlang:function_exported(Module, tcp_init, 2) of + false -> + accept(ListenSocket, Module, Opts1, Sup, Interval, Proxy); + true -> + State = Module:tcp_init(ListenSocket, Opts1), + accept(ListenSocket, Module, State, Sup, Interval, Proxy) + end; + {error, _} -> + ok + end; + {error, Reason} -> + return_socket_error(Reason, EndPoint, Module) + end; + {{error, Reason}, _} -> + return_socket_error(Reason, EndPoint, Module); + {_, {error, Reason}} -> + return_socket_error(Reason, EndPoint, Module) end. -%% @spec (PortIP, Opts) -> {Port, IPT, IPS, IPV, OptsClean} -%% where -%% PortIP = Port | {Port, IPT | IPS} -%% Port = integer() -%% IPT = tuple() -%% IPS = string() -%% IPV = inet | inet6 -%% Opts = [IPV | {ip, IPT} | atom() | tuple()] -%% OptsClean = [atom() | tuple()] -%% @doc Parse any kind of ejabberd listener specification. -%% The parsed options are returned in several formats. -%% OptsClean does not include inet/inet6 or ip options. -%% Opts can include the options inet6 and {ip, Tuple}, -%% but they are only used when no IP address was specified in the PortIP. -%% The IP version (either IPv4 or IPv6) is inferred from the IP address type, -%% so the option inet/inet6 is only used when no IP is specified at all. -parse_listener_portip(PortIP, Opts) -> - {IPOpt, Opts2} = strip_ip_option(Opts), - {IPVOpt, OptsClean} = case proplists:get_bool(inet6, Opts2) of - true -> {inet6, proplists:delete(inet6, Opts2)}; - false -> {inet, Opts2} - end, - {Port, IPT, IPS, Proto} = - case add_proto(PortIP, Opts) of - {P, Prot} -> - T = get_ip_tuple(IPOpt, IPVOpt), - S = jlib:ip_to_list(T), - {P, T, S, Prot}; - {P, T, Prot} when is_integer(P) and is_tuple(T) -> - S = jlib:ip_to_list(T), - {P, T, S, Prot}; - {P, S, Prot} when is_integer(P) and is_binary(S) -> - [S | _] = str:tokens(S, <<"/">>), - {ok, T} = inet_parse:address(binary_to_list(S)), - {P, T, S, Prot} - end, - IPV = case tuple_size(IPT) of - 4 -> inet; - 8 -> inet6 - end, - {Port, IPT, IPS, IPV, Proto, OptsClean}. - -prepare_opts(IPT, IPV, OptsClean) -> - %% The first inet|inet6 and the last {ip, _} work, - %% so overriding those in Opts - Opts = [IPV | OptsClean] ++ [{ip, IPT}], - SockOpts = lists:filter(fun({ip, _}) -> true; - (inet6) -> true; - (inet) -> true; - ({backlog, _}) -> true; - (_) -> false - end, Opts), - {Opts, SockOpts}. - -add_proto(Port, Opts) when is_integer(Port) -> - {Port, get_proto(Opts)}; -add_proto({Port, Proto}, _Opts) when is_atom(Proto) -> - {Port, normalize_proto(Proto)}; -add_proto({Port, Addr}, Opts) -> - {Port, Addr, get_proto(Opts)}; -add_proto({Port, Addr, Proto}, _Opts) -> - {Port, Addr, normalize_proto(Proto)}. - -strip_ip_option(Opts) -> - {IPL, OptsNoIP} = lists:partition( - fun({ip, _}) -> true; - (_) -> false - end, - Opts), - case IPL of - %% Only the first ip option is considered - [{ip, T1} | _] -> - {T1, OptsNoIP}; - [] -> - {no_ip_option, OptsNoIP} +-spec listen_tcp(inet:port_number(), [gen_tcp:option()]) -> + {ok, inet:socket()} | {error, system_limit | inet:posix()}. +listen_tcp(Port, SockOpts) -> + {Port2, ExtraOpts} = case Port of + <<"unix:", Path/binary>> -> + SO = lists:keydelete(ip, 1, SockOpts), + Prov = setup_provisional_udsocket_dir(Path), + file:delete(Path), + file:delete(Prov), + {0, [{ip, {local, Prov}} | SO]}; + _ -> + {Port, SockOpts} + end, + Res = gen_tcp:listen(Port2, [binary, + {packet, 0}, + {active, false}, + {reuseaddr, true}, + {nodelay, true}, + {send_timeout_close, true}, + {keepalive, true} | ExtraOpts]), + case Res of + {ok, ListenSocket} -> + {ok, ListenSocket}; + {error, _} = Err -> + Err end. -get_ip_tuple(no_ip_option, inet) -> - {0, 0, 0, 0}; -get_ip_tuple(no_ip_option, inet6) -> - {0, 0, 0, 0, 0, 0, 0, 0}; -get_ip_tuple(IPOpt, _IPVOpt) -> - IPOpt. +%%% +%%% Unix Domain Socket utility functions +%%% -accept(ListenSocket, Module, Opts) -> - IntervalOpt = - case proplists:get_value(accept_interval, Opts) of - [{linear, [I1_, T1_, T2_, I2_]}] -> - {linear, I1_, T1_, T2_, I2_}; - I_ -> I_ - end, - Interval = - case IntervalOpt of - undefined -> - 0; - I when is_integer(I), I >= 0 -> - I; - {linear, I1, T1, T2, I2} - when is_integer(I1), - is_integer(T1), - is_integer(T2), - is_integer(I2), - I1 >= 0, - I2 >= 0, - T2 > 0 -> - {MSec, Sec, _USec} = os:timestamp(), - TS = MSec * 1000000 + Sec, - {linear, I1, TS + T1, T2, I2}; - I -> - ?WARNING_MSG("There is a problem in the configuration: " - "~p is a wrong accept_interval value. " - "Using 0 as fallback", - [I]), - 0 - end, - accept(ListenSocket, Module, Opts, Interval). +setup_provisional_udsocket_dir(DefinitivePath) -> + ProvisionalPath = get_provisional_udsocket_path(DefinitivePath), + ?INFO_MSG("Creating a Unix Domain Socket provisional file at ~ts for the definitive path ~s", + [ProvisionalPath, DefinitivePath]), + ProvisionalPathAbsolute = relative_socket_to_mnesia(ProvisionalPath), + create_base_dir(ProvisionalPathAbsolute), + ProvisionalPathAbsolute. -accept(ListenSocket, Module, Opts, Interval) -> - NewInterval = check_rate_limit(Interval), +get_provisional_udsocket_path(Path) -> + ReproducibleSecret = binary:part(crypto:hash(sha, misc:atom_to_binary(erlang:get_cookie())), 1, 8), + PathBase64 = misc:term_to_base64({ReproducibleSecret, Path}), + PathBuild = filename:join(misc:get_home(), PathBase64), + DestPath = filename:join(filename:dirname(Path), PathBase64), + case {byte_size(DestPath) > 107, byte_size(PathBuild) > 107} of + {false, _} -> + DestPath; + {true, false} -> + ?INFO_MSG("The provisional Unix Domain Socket path ~ts is longer than 107, let's use home directory instead which is ~p", [DestPath, byte_size(PathBuild)]), + PathBuild; + {true, true} -> + ?ERROR_MSG("The Unix Domain Socket path ~ts is too long, " + "and I cannot create the provisional file safely. " + "Please configure a shorter path and try again.", [Path]), + throw({error_socket_path_too_long, Path}) + end. + +get_definitive_udsocket_path(<<"unix", _>> = Unix) -> + Unix; +get_definitive_udsocket_path(ProvisionalPath) -> + PathBase64 = filename:basename(ProvisionalPath), + {term, {_, Path}} = misc:base64_to_term(PathBase64), + relative_socket_to_mnesia(Path). + +-spec set_definitive_udsocket(integer() | binary(), opts()) -> ok | {error, file:posix() | badarg}. + +set_definitive_udsocket(<<"unix:", Path/binary>>, Opts) -> + Prov = get_provisional_udsocket_path(Path), + Usd = maps:get(unix_socket, Opts), + case maps:get(mode, Usd, undefined) of + undefined -> ok; + Mode -> ok = file:change_mode(Prov, Mode) + end, + case maps:get(owner, Usd, undefined) of + undefined -> ok; + Owner -> + try + ok = file:change_owner(Prov, Owner) + catch + error:{badmatch, {error, eperm}} -> + ?ERROR_MSG("Error trying to set owner ~p for socket ~p", [Owner, Prov]), + throw({error_setting_socket_owner, Owner, Prov}) + end + end, + case maps:get(group, Usd, undefined) of + undefined -> ok; + Group -> + try + ok = file:change_group(Prov, Group) + catch + error:{badmatch, {error, eperm}} -> + ?ERROR_MSG("Error trying to set group ~p for socket ~p", [Group, Prov]), + throw({error_setting_socket_group, Group, Prov}) + end + end, + FinalPath = relative_socket_to_mnesia(Path), + create_base_dir(FinalPath), + file:rename(Prov, FinalPath); +set_definitive_udsocket(Port, _Opts) when is_integer(Port) -> + ok. + +create_base_dir(Path) -> + Dirname = filename:dirname(Path), + case file:make_dir(Dirname) of + ok -> + file:change_mode(Dirname, 8#00700); + _ -> + ok + end. + +relative_socket_to_mnesia(Path1) -> + case filename:pathtype(Path1) of + absolute -> + Path1; + relative -> + MnesiaDir = mnesia:system_info(directory), + filename:join(MnesiaDir, Path1) + end. + +maybe_delete_udsocket_file(<<"unix:", Path/binary>>) -> + PathAbsolute = relative_socket_to_mnesia(Path), + file:delete(PathAbsolute); +maybe_delete_udsocket_file(_Port) -> + ok. + +%%% +%%% +%%% + +-spec split_opts(transport(), opts()) -> {opts(), [gen_tcp:option()]}. +split_opts(Transport, Opts) -> + maps:fold( + fun(Opt, Val, {ModOpts, SockOpts}) -> + case {Opt, Val} of + {ip, _} -> + {ModOpts, [{Opt, Val} | SockOpts]}; + {backlog, _} when Transport == tcp -> + {ModOpts, [{Opt, Val} | SockOpts]}; + {backlog, _} -> + {ModOpts, SockOpts}; + {send_timeout, _} -> + {ModOpts, [{Opt, Val} | SockOpts]}; + _ -> + {ModOpts#{Opt => Val}, SockOpts} + end + end, {#{}, []}, Opts). + +-spec accept(inet:socket(), module(), state(), atom(), + non_neg_integer(), boolean()) -> no_return(). +accept(ListenSocket, Module, State, Sup, Interval, Proxy) -> + Arity = case erlang:function_exported(Module, start, 3) of + true -> 3; + false -> 2 + end, + accept(ListenSocket, Module, State, Sup, Interval, Proxy, Arity). + +-spec accept(inet:socket(), module(), state(), atom(), + non_neg_integer(), boolean(), 2|3) -> no_return(). +accept(ListenSocket, Module, State, Sup, Interval, Proxy, Arity) -> + NewInterval = apply_rate_limit(Interval), case gen_tcp:accept(ListenSocket) of + {ok, Socket} when Proxy -> + case proxy_protocol:decode(gen_tcp, Socket, 10000) of + {error, Err} -> + ?ERROR_MSG("(~w) Proxy protocol parsing failed: ~ts", + [ListenSocket, format_error(Err)]), + gen_tcp:close(Socket); + {undefined, undefined} -> + gen_tcp:close(Socket); + {{Addr, Port}, {PAddr, PPort}} = SP -> + %% THIS IS WRONG + State2 = [{sock_peer_name, SP} | State], + Receiver = case start_connection(Module, Arity, Socket, State2, Sup) of + {ok, RecvPid} -> + RecvPid; + _ -> + gen_tcp:close(Socket), + none + end, + ?INFO_MSG("(~p) Accepted proxied connection ~ts -> ~ts", + [Receiver, + ejabberd_config:may_hide_data( + format_endpoint({PPort, PAddr, tcp})), + format_endpoint({Port, Addr, tcp})]) + end, + accept(ListenSocket, Module, State, Sup, NewInterval, Proxy, Arity); {ok, Socket} -> case {inet:sockname(Socket), inet:peername(Socket)} of {{ok, {Addr, Port}}, {ok, {PAddr, PPort}}} -> - ?INFO_MSG("(~w) Accepted connection ~s:~p -> ~s:~p", - [Socket, ejabberd_config:may_hide_data(inet_parse:ntoa(PAddr)), PPort, - inet_parse:ntoa(Addr), Port]); + Receiver = case start_connection(Module, Arity, Socket, State, Sup) of + {ok, RecvPid} -> + RecvPid; + _ -> + gen_tcp:close(Socket), + none + end, + case is_ctl_over_http(State) of + false -> + ?INFO_MSG("(~p) Accepted connection ~ts -> ~ts", + [Receiver, + ejabberd_config:may_hide_data( + format_endpoint({PPort, PAddr, tcp})), + format_endpoint({Port, Addr, tcp})]); + true -> + ?DEBUG("(~p) Accepted connection ~ts -> ~ts", + [Receiver, + ejabberd_config:may_hide_data( + format_endpoint({PPort, PAddr, tcp})), + format_endpoint({Port, Addr, tcp})]) + end; _ -> - ok + gen_tcp:close(Socket) end, - CallMod = case is_frontend(Module) of - true -> ejabberd_frontend_socket; - false -> ejabberd_socket - end, - CallMod:start(strip_frontend(Module), gen_tcp, Socket, Opts), - accept(ListenSocket, Module, Opts, NewInterval); + accept(ListenSocket, Module, State, Sup, NewInterval, Proxy, Arity); {error, Reason} -> - ?ERROR_MSG("(~w) Failed TCP accept: ~w", - [ListenSocket, Reason]), - accept(ListenSocket, Module, Opts, NewInterval) + ?ERROR_MSG("(~w) Failed TCP accept: ~ts", + [ListenSocket, format_error(Reason)]), + accept(ListenSocket, Module, State, Sup, NewInterval, Proxy, Arity) end. -udp_recv(Socket, Module, Opts) -> +is_ctl_over_http(State) -> + case lists:keyfind(request_handlers, 1, State) of + {request_handlers, Handlers} -> + case lists:keyfind(ejabberd_ctl, 2, Handlers) of + {_, ejabberd_ctl} -> true; + _ -> false + end; + _ -> false + end. + +-spec udp_recv(inet:socket(), module(), state()) -> no_return(). +udp_recv(Socket, Module, State) -> case gen_udp:recv(Socket, 0) of {ok, {Addr, Port, Packet}} -> - case catch Module:udp_recv(Socket, Addr, Port, Packet, Opts) of + case catch Module:udp_recv(Socket, Addr, Port, Packet, State) of {'EXIT', Reason} -> - ?ERROR_MSG("failed to process UDP packet:~n" + ?ERROR_MSG("Failed to process UDP packet:~n" "** Source: {~p, ~p}~n" "** Reason: ~p~n** Packet: ~p", [Addr, Port, Reason, Packet]), - udp_recv(Socket, Module, Opts); - NewOpts -> - udp_recv(Socket, Module, NewOpts) + udp_recv(Socket, Module, State); + NewState -> + udp_recv(Socket, Module, NewState) end; {error, Reason} -> - ?ERROR_MSG("unexpected UDP error: ~s", [format_error(Reason)]), + ?ERROR_MSG("Unexpected UDP error: ~ts", [format_error(Reason)]), throw({error, Reason}) end. -%% @spec (Port, Module, Opts) -> {ok, Pid} | {error, Error} -start_listener(Port, Module, Opts) -> - case start_listener2(Port, Module, Opts) of +-spec start_connection(module(), 2|3, inet:socket(), state(), atom()) -> + {ok, pid()} | {error, any()} | ignore. +start_connection(Module, Arity, Socket, State, Sup) -> + Res = case Sup of + undefined when Arity == 3 -> + Module:start(gen_tcp, Socket, State); + undefined -> + Module:start({gen_tcp, Socket}, State); + _ when Arity == 3 -> + supervisor:start_child(Sup, [gen_tcp, Socket, State]); + _ -> + supervisor:start_child(Sup, [{gen_tcp, Socket}, State]) + end, + case Res of + {ok, Pid, preowned_socket} -> + Module:accept(Pid), + {ok, Pid}; + {ok, Pid} -> + case gen_tcp:controlling_process(Socket, Pid) of + ok -> + Module:accept(Pid), + {ok, Pid}; + Err -> + case Sup of + undefined -> + exit(Pid, kill); + _ -> + supervisor:terminate_child(Sup, Pid) + end, + Err + end; + Err -> + Err + end. + +-spec start_listener(endpoint(), module(), opts()) -> + {ok, pid()} | {error, any()}. +start_listener(EndPoint, Module, Opts) -> + %% It is only required to start the supervisor in some cases. + %% But it doesn't hurt to attempt to start it for any listener. + %% So, it's normal (and harmless) that in most cases this + %% call returns: {error, {already_started, pid()}} + case start_listener_sup(EndPoint, Module, Opts) of {ok, _Pid} = R -> R; {error, {{'EXIT', {undef, [{M, _F, _A}|_]}}, _} = Error} -> ?ERROR_MSG("Error starting the ejabberd listener: ~p.~n" @@ -381,206 +487,140 @@ start_listener(Port, Module, Opts) -> {error, Error} end. -%% @spec (Port, Module, Opts) -> {ok, Pid} | {error, Error} -start_listener2(Port, Module, Opts) -> - %% It is only required to start the supervisor in some cases. - %% But it doesn't hurt to attempt to start it for any listener. - %% So, it's normal (and harmless) that in most cases this call returns: {error, {already_started, pid()}} - maybe_start_sip(Module), - start_module_sup(Port, Module), - start_listener_sup(Port, Module, Opts). +-spec start_module_sup(module(), opts()) -> atom(). +start_module_sup(Module, Opts) -> + case maps:get(supervisor, Opts) of + true -> + Proc = list_to_atom(atom_to_list(Module) ++ "_sup"), + ChildSpec = {Proc, {ejabberd_tmp_sup, start_link, [Proc, Module]}, + permanent, + infinity, + supervisor, + [ejabberd_tmp_sup]}, + case supervisor:start_child(ejabberd_sup, ChildSpec) of + {ok, _} -> Proc; + {error, {already_started, _}} -> Proc; + _ -> undefined + end; + false -> + undefined + end. -start_module_sup(_Port, Module) -> - Proc1 = gen_mod:get_module_proc(<<"sup">>, Module), - ChildSpec1 = - {Proc1, - {ejabberd_tmp_sup, start_link, [Proc1, strip_frontend(Module)]}, - permanent, - infinity, - supervisor, - [ejabberd_tmp_sup]}, - supervisor:start_child(ejabberd_sup, ChildSpec1). - -start_listener_sup(Port, Module, Opts) -> - ChildSpec = {Port, - {?MODULE, start, [Port, Module, Opts]}, +-spec start_listener_sup(endpoint(), module(), opts()) -> + {ok, pid()} | {error, any()}. +start_listener_sup(EndPoint, Module, Opts) -> + ChildSpec = {EndPoint, + {?MODULE, start, [EndPoint, Module, Opts]}, transient, brutal_kill, worker, [?MODULE]}, - supervisor:start_child(ejabberd_listeners, ChildSpec). + supervisor:start_child(?MODULE, ChildSpec). +-spec stop_listeners() -> ok. stop_listeners() -> - Ports = ejabberd_config:get_option(listen, fun validate_cfg/1), + Ports = ejabberd_option:listen(), lists:foreach( fun({PortIpNetp, Module, _Opts}) -> delete_listener(PortIpNetp, Module) end, Ports). -%% @spec (PortIP, Module) -> ok -%% where -%% PortIP = {Port, IPT | IPS} -%% Port = integer() -%% IPT = tuple() -%% IPS = string() -%% Module = atom() -stop_listener(PortIP, _Module) -> - supervisor:terminate_child(ejabberd_listeners, PortIP), - supervisor:delete_child(ejabberd_listeners, PortIP). +-spec stop_listener(endpoint(), module(), opts()) -> ok | {error, any()}. +stop_listener({Port, _, Transport} = EndPoint, Module, Opts) -> + case supervisor:terminate_child(?MODULE, EndPoint) of + ok -> + ?INFO_MSG("Stop accepting ~ts connections at ~ts for ~p", + [format_transport(Transport, Opts), + format_endpoint(EndPoint), Module]), + maybe_delete_udsocket_file(Port), + ets:delete(?MODULE, EndPoint), + supervisor:delete_child(?MODULE, EndPoint); + Err -> + Err + end. -%% @spec (PortIP, Module, Opts) -> {ok, Pid} | {error, Error} -%% where -%% PortIP = {Port, IPT | IPS} -%% Port = integer() -%% IPT = tuple() -%% IPS = string() -%% IPV = inet | inet6 -%% Module = atom() -%% Opts = [IPV | {ip, IPT} | atom() | tuple()] -%% @doc Add a listener and store in config if success -add_listener(PortIP, Module, Opts) -> - {Port, IPT, _, _, Proto, _} = parse_listener_portip(PortIP, Opts), - PortIP1 = {Port, IPT, Proto}, - case start_listener(PortIP1, Module, Opts) of +-spec add_listener(endpoint(), module(), opts()) -> ok | {error, any()}. +add_listener(EndPoint, Module, Opts) -> + Opts1 = apply_defaults(Module, Opts), + case start_listener(EndPoint, Module, Opts1) of {ok, _Pid} -> - Ports = case ejabberd_config:get_option( - listen, fun validate_cfg/1) of - undefined -> - []; - Ls -> - Ls - end, - Ports1 = lists:keydelete(PortIP1, 1, Ports), - Ports2 = [{PortIP1, Module, Opts} | Ports1], - Ports3 = lists:map(fun transform_option/1, Ports2), - ejabberd_config:add_option(listen, Ports3), ok; {error, {already_started, _Pid}} -> - {error, {already_started, PortIP}}; + {error, {already_started, EndPoint}}; {error, Error} -> {error, Error} end. -delete_listener(PortIP, Module) -> - delete_listener(PortIP, Module, []). - -%% @spec (PortIP, Module, Opts) -> ok -%% where -%% PortIP = {Port, IPT | IPS} -%% Port = integer() -%% IPT = tuple() -%% IPS = string() -%% Module = atom() -%% Opts = [term()] -delete_listener(PortIP, Module, Opts) -> - {Port, IPT, _, _, Proto, _} = parse_listener_portip(PortIP, Opts), - PortIP1 = {Port, IPT, Proto}, - Ports = case ejabberd_config:get_option( - listen, fun validate_cfg/1) of - undefined -> - []; - Ls -> - Ls - end, - Ports1 = lists:keydelete(PortIP1, 1, Ports), - Ports2 = lists:map(fun transform_option/1, Ports1), - ejabberd_config:add_option(listen, Ports2), - stop_listener(PortIP1, Module). - - --spec is_frontend({frontend, module} | module()) -> boolean(). - -is_frontend({frontend, _Module}) -> true; -is_frontend(_) -> false. - -%% @doc(FrontMod) -> atom() -%% where FrontMod = atom() | {frontend, atom()} --spec strip_frontend({frontend, module()} | module()) -> module(). - -strip_frontend({frontend, Module}) -> Module; -strip_frontend(Module) when is_atom(Module) -> Module. - -maybe_start_sip(esip_socket) -> - ejabberd:start_app(esip); -maybe_start_sip(_) -> - ok. - -%%% -%%% Check options -%%% - -check_listener_options(Opts) -> - case includes_deprecated_ssl_option(Opts) of - false -> ok; - true -> - Error = "There is a problem with your ejabberd configuration file: " - "the option 'ssl' for listening sockets is no longer available." - " To get SSL encryption use the option 'tls'.", - throw({error, Error}) - end, - case certfile_readable(Opts) of - true -> ok; - {false, Path} -> - ErrorText = "There is a problem in the configuration: " - "the specified file is not readable: ", - throw({error, ErrorText ++ Path}) - end, - ok. - -%% Parse the options of the socket, -%% and return if the deprecated option 'ssl' is included -%% @spec (Opts) -> true | false -includes_deprecated_ssl_option(Opts) -> - case lists:keysearch(ssl, 1, Opts) of - {value, {ssl, _SSLOpts}} -> - true; - _ -> - lists:member(ssl, Opts) +-spec delete_listener(endpoint(), module()) -> ok | {error, any()}. +delete_listener(EndPoint, Module) -> + try ets:lookup_element(?MODULE, EndPoint, 3) of + Opts -> stop_listener(EndPoint, Module, Opts) + catch _:badarg -> + ok end. -%% @spec (Opts) -> true | {false, Path::string()} -certfile_readable(Opts) -> - case proplists:lookup(certfile, Opts) of - none -> true; - {certfile, Path} -> - PathS = binary_to_list(Path), - case ejabberd_config:is_file_readable(PathS) of - true -> true; - false -> {false, PathS} - end - end. +-spec tls_listeners() -> [module()]. +tls_listeners() -> + lists:usort( + lists:filtermap( + fun({_, Module, #{tls := true}}) -> {true, Module}; + ({_, Module, #{starttls := true}}) -> {true, Module}; + (_) -> false + end, ets:tab2list(?MODULE))). -get_proto(Opts) -> - case proplists:get_value(proto, Opts) of - undefined -> - tcp; - Proto -> - normalize_proto(Proto) - end. - -normalize_proto(tcp) -> tcp; -normalize_proto(udp) -> udp; -normalize_proto(UnknownProto) -> - ?WARNING_MSG("There is a problem in the configuration: " - "~p is an unknown IP protocol. Using tcp as fallback", - [UnknownProto]), - tcp. - -socket_error(Reason, PortIP, Module, SockOpts, Port, IPS) -> - ReasonT = case Reason of - eaddrnotavail -> - "IP address not available: " ++ binary_to_list(IPS); - eaddrinuse -> - "IP address and port number already used: " - ++binary_to_list(IPS)++" "++integer_to_list(Port); +-spec config_reloaded() -> ok. +config_reloaded() -> + New = ejabberd_option:listen(), + Old = ets:tab2list(?MODULE), + lists:foreach( + fun({EndPoint, Module, Opts}) -> + case lists:keyfind(EndPoint, 1, New) of + false -> + stop_listener(EndPoint, Module, Opts); _ -> - format_error(Reason) - end, - ?ERROR_MSG("Failed to open socket:~n ~p~nReason: ~s", - [{Port, Module, SockOpts}, ReasonT]), - throw({Reason, PortIP}). + ok + end + end, Old), + lists:foreach( + fun({EndPoint, Module, Opts}) -> + case lists:keyfind(EndPoint, 1, Old) of + {_, Module, Opts} -> + ok; + {_, OldModule, OldOpts} -> + _ = stop_listener(EndPoint, OldModule, OldOpts), + case start_listener(EndPoint, Module, Opts) of + {ok, _} -> + ets:insert(?MODULE, {EndPoint, Module, Opts}); + _ -> + ok + end; + false -> + case start_listener(EndPoint, Module, Opts) of + {ok, _} -> + ets:insert(?MODULE, {EndPoint, Module, Opts}); + _ -> + ok + end + end + end, New). +-spec return_socket_error(inet:posix(), endpoint(), module()) -> no_return(). +return_socket_error(Reason, EndPoint, Module) -> + ?ERROR_MSG("Failed to open socket at ~ts for ~ts: ~ts", + [format_endpoint(EndPoint), Module, format_error(Reason)]), + return_init_error(Reason). + +-ifdef(OTP_BELOW_26). +return_init_error(Reason) -> + proc_lib:init_ack({error, Reason}). +-else. +-spec return_init_error(inet:posix()) -> no_return(). +return_init_error(Reason) -> + proc_lib:init_fail({error, Reason}, {exit, normal}). +-endif. + +-spec format_error(inet:posix() | atom()) -> string(). format_error(Reason) -> case inet:format_error(Reason) of "unknown POSIX error" -> @@ -589,7 +629,35 @@ format_error(Reason) -> ReasonStr end. -check_rate_limit(Interval) -> +-spec format_endpoint(endpoint()) -> string(). +format_endpoint({Port, IP, Transport}) -> + case Port of + <<"unix:", _/binary>> -> + Port; + <<>> when (IP == local) and (Transport == tcp) -> + "local-unix-socket-domain"; + Unix when is_binary(Unix) -> + Def = get_definitive_udsocket_path(Unix), + <<"unix:", Def/binary>>; + _ -> + IPStr = case tuple_size(IP) of + 4 -> inet:ntoa(IP); + 8 -> "[" ++ inet:ntoa(IP) ++ "]" + end, + IPStr ++ ":" ++ integer_to_list(Port) + end. + +-spec format_transport(transport(), opts()) -> string(). +format_transport(Transport, Opts) -> + case maps:get(tls, Opts, false) of + true when Transport == tcp -> "TLS"; + true when Transport == udp -> "DTLS"; + false when Transport == tcp -> "TCP"; + false when Transport == udp -> "UDP" + end. + +-spec apply_rate_limit(non_neg_integer()) -> non_neg_integer(). +apply_rate_limit(Interval) -> NewInterval = receive {rate_limit, AcceptInterval} -> AcceptInterval @@ -614,109 +682,191 @@ check_rate_limit(Interval) -> end, NewInterval. --define(IS_CHAR(C), (is_integer(C) and (C >= 0) and (C =< 255))). --define(IS_UINT(U), (is_integer(U) and (U >= 0) and (U =< 65535))). --define(IS_PORT(P), (is_integer(P) and (P > 0) and (P =< 65535))). --define(IS_TRANSPORT(T), ((T == tcp) or (T == udp))). +-spec validator() -> econf:validator(). +validator() -> + econf:and_then( + econf:list( + econf:and_then( + econf:options( + #{module => listen_opt_type(module), + transport => listen_opt_type(transport), + '_' => econf:any()}, + [{required, [module]}]), + fun(Opts) -> + M = proplists:get_value(module, Opts), + T = proplists:get_value(transport, Opts, tcp), + (validator(M, T))(Opts) + end)), + fun prepare_opts/1). -transform_option({{Port, IP, Transport}, Mod, Opts}) -> - IPStr = if is_tuple(IP) -> - list_to_binary(inet_parse:ntoa(IP)); - true -> - IP - end, - Opts1 = lists:map( - fun({ip, IPT}) when is_tuple(IPT) -> - {ip, list_to_binary(inet_parse:ntoa(IP))}; - (ssl) -> {tls, true}; - (A) when is_atom(A) -> {A, true}; - (Opt) -> Opt - end, Opts), - Opts2 = lists:foldl( - fun(Opt, Acc) -> - try - Mod:transform_listen_option(Opt, Acc) - catch error:undef -> - [Opt|Acc] - end - end, [], Opts1), - TransportOpt = if Transport == tcp -> []; - true -> [{transport, Transport}] - end, - IPOpt = if IPStr == <<"0.0.0.0">> -> []; - true -> [{ip, IPStr}] - end, - IPOpt ++ TransportOpt ++ [{port, Port}, {module, Mod} | Opts2]; -transform_option({{Port, Transport}, Mod, Opts}) - when ?IS_TRANSPORT(Transport) -> - transform_option({{Port, all_zero_ip(Opts), Transport}, Mod, Opts}); -transform_option({{Port, IP}, Mod, Opts}) -> - transform_option({{Port, IP, tcp}, Mod, Opts}); -transform_option({Port, Mod, Opts}) -> - transform_option({{Port, all_zero_ip(Opts), tcp}, Mod, Opts}); -transform_option(Opt) -> - Opt. +-spec validator(module(), transport()) -> econf:validator(). +validator(M, T) -> + Options = listen_options() ++ M:listen_options(), + Required = lists:usort([Opt || Opt <- Options, is_atom(Opt)]), + Disallowed = if T == udp -> + [backlog, use_proxy_protocol, accept_interval]; + true -> + [] + end, + Keywords = ejabberd_config:get_defined_keywords(global) ++ ejabberd_config:get_predefined_keywords(global), + Validator = maps:from_list( + lists:map( + fun(Opt) -> + Type = try M:listen_opt_type(Opt) + catch _:_ when M /= ?MODULE -> + listen_opt_type(Opt) + end, + TypeProcessed = + econf:and_then( + fun(B) -> + ejabberd_config:replace_keywords(global, B, Keywords) + end, + Type), + {Opt, TypeProcessed} + end, proplists:get_keys(Options))), + econf:options( + Validator, + [{required, Required}, {disallowed, Disallowed}, + {return, map}, unique]). -transform_options(Opts) -> - lists:foldl(fun transform_options/2, [], Opts). +-spec prepare_opts([opts()]) -> [listener()]. +prepare_opts(Listeners) -> + check_overlapping_listeners( + lists:map( + fun(Opts1) -> + {Opts2, Opts3} = partition( + fun({port, _}) -> true; + ({transport, _}) -> true; + ({module, _}) -> true; + (_) -> false + end, Opts1), + Mod = maps:get(module, Opts2), + Port = maps:get(port, Opts2), + Transport = maps:get(transport, Opts2, tcp), + IP = maps:get(ip, Opts3, {0,0,0,0}), + Opts4 = apply_defaults(Mod, Opts3), + {{Port, IP, Transport}, Mod, Opts4} + end, Listeners)). -transform_options({listen, LOpts}, Opts) -> - [{listen, lists:map(fun transform_option/1, LOpts)} | Opts]; -transform_options(Opt, Opts) -> - [Opt|Opts]. +-spec check_overlapping_listeners([listener()]) -> [listener()]. +check_overlapping_listeners(Listeners) -> + _ = lists:foldl( + fun({{Port, IP, Transport} = Key, _, _}, Acc) -> + case lists:member(Key, Acc) of + true -> + econf:fail({listener_dup, {IP, Port}}); + false -> + ZeroIP = case size(IP) of + 8 -> {0,0,0,0,0,0,0,0}; + 4 -> {0,0,0,0} + end, + Key1 = {Port, ZeroIP, Transport}, + case lists:member(Key1, Acc) of + true -> + econf:fail({listener_conflict, + {IP, Port}, {ZeroIP, Port}}); + false -> + [Key|Acc] + end + end + end, [], Listeners), + Listeners. --type transport() :: udp | tcp. --type port_ip_transport() :: inet:port_number() | - {inet:port_number(), transport()} | - {inet:port_number(), inet:ip_address()} | - {inet:port_number(), inet:ip_address(), - transport()}. --spec validate_cfg(list()) -> [{port_ip_transport(), module(), list()}]. +-spec apply_defaults(module(), opts()) -> opts(). +apply_defaults(Mod, Opts) -> + lists:foldl( + fun({Opt, Default}, M) -> + case maps:is_key(Opt, M) of + true -> M; + false -> M#{Opt => Default} + end; + (_, M) -> + M + end, Opts, Mod:listen_options() ++ listen_options()). -validate_cfg(L) -> - lists:map( - fun(LOpts) -> - lists:foldl( - fun({port, Port}, {{_, IP, T}, Mod, Opts}) -> - true = ?IS_PORT(Port), - {{Port, IP, T}, Mod, Opts}; - ({ip, IP}, {{Port, _, T}, Mod, Opts}) -> - {{Port, prepare_ip(IP), T}, Mod, Opts}; - ({transport, T}, {{Port, IP, _}, Mod, Opts}) -> - true = ?IS_TRANSPORT(T), - {{Port, IP, T}, Mod, Opts}; - ({module, Mod}, {Port, _, Opts}) -> - {Port, prepare_mod(Mod), Opts}; - (Opt, {Port, Mod, Opts}) -> - {Port, Mod, [Opt|Opts]} - end, {{5222, all_zero_ip(LOpts), tcp}, ejabberd_c2s, []}, LOpts) - end, L). +%% Convert options to list with removing defaults +-spec opts_to_list(module(), opts()) -> list_opts(). +opts_to_list(Mod, Opts) -> + Defaults = Mod:listen_options() ++ listen_options(), + maps:fold( + fun(Opt, Val, Acc) -> + case proplists:get_value(Opt, Defaults) of + Val -> Acc; + _ -> [{Opt, Val}|Acc] + end + end, [], Opts). -prepare_ip({A, B, C, D} = IP) - when ?IS_CHAR(A) and ?IS_CHAR(B) and ?IS_CHAR(C) and ?IS_CHAR(D) -> - IP; -prepare_ip({A, B, C, D, E, F, G, H} = IP) - when ?IS_UINT(A) and ?IS_UINT(B) and ?IS_UINT(C) and ?IS_UINT(D) - and ?IS_UINT(E) and ?IS_UINT(F) and ?IS_UINT(G) and ?IS_UINT(H) -> - IP; -prepare_ip(IP) when is_list(IP) -> - {ok, Addr} = inet_parse:address(IP), - Addr; -prepare_ip(IP) when is_binary(IP) -> - prepare_ip(binary_to_list(IP)). +-spec partition(fun(({atom(), term()}) -> boolean()), opts()) -> {opts(), opts()}. +partition(Fun, Opts) -> + maps:fold( + fun(Opt, Val, {True, False}) -> + case Fun({Opt, Val}) of + true -> {True#{Opt => Val}, False}; + false -> {True, False#{Opt => Val}} + end + end, {#{}, #{}}, Opts). -prepare_mod(ejabberd_sip) -> - prepare_mod(sip); -prepare_mod(sip) -> - esip_socket; -prepare_mod(Mod) when is_atom(Mod) -> - Mod. +-spec listen_opt_type(atom()) -> econf:validator(). +listen_opt_type(port) -> + econf:either( + econf:int(0, 65535), + econf:binary("^unix:.*")); +listen_opt_type(module) -> + econf:beam([[{start, 3}, {start, 2}], + [{start_link, 3}, {start_link, 2}], + {accept, 1}, {listen_options, 0}]); +listen_opt_type(ip) -> + econf:ip(); +listen_opt_type(transport) -> + econf:enum([tcp, udp]); +listen_opt_type(accept_interval) -> + econf:non_neg_int(); +listen_opt_type(backlog) -> + econf:non_neg_int(); +listen_opt_type(supervisor) -> + econf:bool(); +listen_opt_type(ciphers) -> + econf:binary(); +listen_opt_type(dhfile) -> + econf:file(); +listen_opt_type(cafile) -> + econf:pem(); +listen_opt_type(certfile) -> + econf:pem(); +listen_opt_type(protocol_options) -> + econf:and_then( + econf:list(econf:binary()), + fun(Options) -> str:join(Options, <<"|">>) end); +listen_opt_type(tls_compression) -> + econf:bool(); +listen_opt_type(tls) -> + econf:bool(); +listen_opt_type(max_stanza_size) -> + econf:pos_int(infinity); +listen_opt_type(max_fsm_queue) -> + econf:pos_int(); +listen_opt_type(send_timeout) -> + econf:timeout(second, infinity); +listen_opt_type(shaper) -> + econf:shaper(); +listen_opt_type(access) -> + econf:acl(); +listen_opt_type(unix_socket) -> + econf:options( + #{group => econf:non_neg_int(), + owner => econf:non_neg_int(), + mode => econf:octal()}, + [unique, {return, map}]); +listen_opt_type(use_proxy_protocol) -> + econf:bool(). -all_zero_ip(Opts) -> - case proplists:get_bool(inet6, Opts) of - true -> {0,0,0,0,0,0,0,0}; - false -> {0,0,0,0} - end. - -opt_type(listen) -> fun validate_cfg/1; -opt_type(_) -> [listen]. +listen_options() -> + [module, port, + {transport, tcp}, + {ip, {0,0,0,0}}, + {accept_interval, 0}, + {send_timeout, 15000}, + {backlog, 128}, + {unix_socket, #{}}, + {use_proxy_protocol, false}, + {supervisor, true}]. diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl index 3406192f7..5d9557415 100644 --- a/src/ejabberd_local.erl +++ b/src/ejabberd_local.erl @@ -5,7 +5,7 @@ %%% Created : 30 Nov 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -30,38 +30,29 @@ -behaviour(gen_server). %% API --export([start_link/0]). +-export([start/0, start_link/0]). --export([route/3, route_iq/4, route_iq/5, process_iq/3, - process_iq_reply/3, register_iq_handler/4, - register_iq_handler/5, register_iq_response_handler/4, - register_iq_response_handler/5, unregister_iq_handler/2, - unregister_iq_response_handler/2, refresh_iq_handlers/0, - bounce_resource_packet/3]). +-export([route/1, + get_features/1, + bounce_resource_packet/1, + host_up/1, host_down/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --include("ejabberd.hrl"). --include("logger.hrl"). +%% deprecated functions: use ejabberd_router:route_iq/3,4 +-export([route_iq/2, route_iq/3]). +-deprecated([{route_iq, 2}, {route_iq, 3}]). --include("xmpp.hrl"). +-include("logger.hrl"). +-include_lib("stdlib/include/ms_transform.hrl"). +-include_lib("xmpp/include/xmpp.hrl"). + +-include("translate.hrl"). -record(state, {}). --record(iq_response, {id = <<"">> :: binary(), - module :: atom(), - function :: atom() | fun(), - timer = make_ref() :: reference()}). - --define(IQTABLE, local_iqtable). - -%% This value is used in SIP and Megaco for a transaction lifetime. --define(IQ_TIMEOUT, 32000). - --type ping_timeout() :: non_neg_integer() | undefined. - %%==================================================================== %% API %%==================================================================== @@ -69,203 +60,87 @@ %% Function: start_link() -> {ok,Pid} | ignore | {error,Error} %% Description: Starts the server %%-------------------------------------------------------------------- +start() -> + ChildSpec = {?MODULE, {?MODULE, start_link, []}, + transient, 1000, worker, [?MODULE]}, + supervisor:start_child(ejabberd_sup, ChildSpec). + start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). --spec process_iq(jid(), jid(), iq()) -> any(). -process_iq(From, To, #iq{type = T, lang = Lang, sub_els = [El]} = Packet) - when T == get; T == set -> - XMLNS = xmpp:get_ns(El), - Host = To#jid.lserver, - case ets:lookup(?IQTABLE, {XMLNS, Host}) of - [{_, Module, Function}] -> - gen_iq_handler:handle(Host, Module, Function, no_queue, - From, To, Packet); - [{_, Module, Function, Opts}] -> - gen_iq_handler:handle(Host, Module, Function, Opts, - From, To, Packet); - [] -> - Txt = <<"No module is handling this query">>, - Err = xmpp:err_service_unavailable(Txt, Lang), - ejabberd_router:route_error(To, From, Packet, Err) - end; -process_iq(From, To, #iq{type = T} = Packet) when T == get; T == set -> - Err = xmpp:err_bad_request(), - ejabberd_router:route_error(To, From, Packet, Err); -process_iq(From, To, #iq{type = T} = Packet) when T == result; T == error -> - process_iq_reply(From, To, Packet). - --spec process_iq_reply(jid(), jid(), iq()) -> any(). -process_iq_reply(From, To, #iq{id = ID} = IQ) -> - case get_iq_callback(ID) of - {ok, undefined, Function} -> Function(IQ), ok; - {ok, Module, Function} -> - Module:Function(From, To, IQ), ok; - _ -> nothing +-spec route(stanza()) -> ok. +route(Packet) -> + ?DEBUG("Local route:~n~ts", [xmpp:pp(Packet)]), + Type = xmpp:get_type(Packet), + To = xmpp:get_to(Packet), + if To#jid.luser /= <<"">> -> + ejabberd_sm:route(Packet); + is_record(Packet, iq), To#jid.lresource == <<"">> -> + gen_iq_handler:handle(?MODULE, Packet); + Type == result; Type == error -> + ok; + true -> + ejabberd_hooks:run(local_send_to_resource_hook, + To#jid.lserver, [Packet]) end. --spec route(jid(), jid(), stanza()) -> any(). -route(From, To, Packet) -> - case catch do_route(From, To, Packet) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p~nwhen processing: ~p", - [Reason, {From, To, Packet}]); - _ -> ok - end. +-spec route_iq(iq(), function()) -> ok. +route_iq(IQ, Fun) -> + route_iq(IQ, Fun, undefined). --spec route_iq(jid(), jid(), iq(), function()) -> any(). -route_iq(From, To, IQ, F) -> - route_iq(From, To, IQ, F, undefined). +-spec route_iq(iq(), function(), undefined | non_neg_integer()) -> ok. +route_iq(IQ, Fun, Timeout) -> + ejabberd_router:route_iq(IQ, Fun, undefined, Timeout). --spec route_iq(jid(), jid(), iq(), function(), ping_timeout()) -> any(). -route_iq(From, To, #iq{type = Type} = IQ, F, Timeout) - when is_function(F) -> - Packet = if Type == set; Type == get -> - ID = randoms:get_string(), - Host = From#jid.lserver, - register_iq_response_handler(Host, ID, undefined, F, Timeout), - IQ#iq{id = ID}; - true -> - IQ - end, - ejabberd_router:route(From, To, Packet). - --spec register_iq_response_handler(binary(), binary(), module(), - atom() | function()) -> any(). -register_iq_response_handler(Host, ID, Module, - Function) -> - register_iq_response_handler(Host, ID, Module, Function, - undefined). - --spec register_iq_response_handler(binary(), binary(), module(), - atom() | function(), ping_timeout()) -> any(). -register_iq_response_handler(_Host, ID, Module, - Function, Timeout0) -> - Timeout = case Timeout0 of - undefined -> ?IQ_TIMEOUT; - N when is_integer(N), N > 0 -> N - end, - TRef = erlang:start_timer(Timeout, ejabberd_local, ID), - mnesia:dirty_write(#iq_response{id = ID, - module = Module, - function = Function, - timer = TRef}). - --spec register_iq_handler(binary(), binary(), module(), function()) -> any(). -register_iq_handler(Host, XMLNS, Module, Fun) -> - ejabberd_local ! - {register_iq_handler, Host, XMLNS, Module, Fun}. - --spec register_iq_handler(binary(), binary(), module(), function(), - gen_iq_handler:opts()) -> any(). -register_iq_handler(Host, XMLNS, Module, Fun, Opts) -> - ejabberd_local ! - {register_iq_handler, Host, XMLNS, Module, Fun, Opts}. - --spec unregister_iq_response_handler(binary(), binary()) -> ok. -unregister_iq_response_handler(_Host, ID) -> - catch get_iq_callback(ID), ok. - --spec unregister_iq_handler(binary(), binary()) -> any(). -unregister_iq_handler(Host, XMLNS) -> - ejabberd_local ! {unregister_iq_handler, Host, XMLNS}. - --spec refresh_iq_handlers() -> any(). -refresh_iq_handlers() -> - ejabberd_local ! refresh_iq_handlers. - --spec bounce_resource_packet(jid(), jid(), stanza()) -> stop. -bounce_resource_packet(_From, #jid{lresource = <<"">>}, #presence{}) -> +-spec bounce_resource_packet(stanza()) -> ok | stop. +bounce_resource_packet(#presence{to = #jid{lresource = <<"">>}}) -> ok; -bounce_resource_packet(_From, #jid{lresource = <<"">>}, - #message{type = headline}) -> +bounce_resource_packet(#message{to = #jid{lresource = <<"">>}, type = headline}) -> ok; -bounce_resource_packet(From, To, Packet) -> +bounce_resource_packet(Packet) -> Lang = xmpp:get_lang(Packet), - Txt = <<"No available resource found">>, + Txt = ?T("No available resource found"), Err = xmpp:err_item_not_found(Txt, Lang), - ejabberd_router:route_error(To, From, Packet, Err), + ejabberd_router:route_error(Packet, Err), stop. +-spec get_features(binary()) -> [binary()]. +get_features(Host) -> + gen_iq_handler:get_features(?MODULE, Host). + %%==================================================================== %% gen_server callbacks %%==================================================================== init([]) -> - lists:foreach(fun (Host) -> - ejabberd_router:register_route(Host, - Host, - {apply, ?MODULE, - route}), - ejabberd_hooks:add(local_send_to_resource_hook, Host, - ?MODULE, bounce_resource_packet, - 100) - end, - ?MYHOSTS), - catch ets:new(?IQTABLE, [named_table, public]), + process_flag(trap_exit, true), + lists:foreach(fun host_up/1, ejabberd_option:hosts()), + ejabberd_hooks:add(host_up, ?MODULE, host_up, 10), + ejabberd_hooks:add(host_down, ?MODULE, host_down, 100), + gen_iq_handler:start(?MODULE), update_table(), - mnesia:create_table(iq_response, - [{ram_copies, [node()]}, - {attributes, record_info(fields, iq_response)}]), - mnesia:add_table_copy(iq_response, node(), ram_copies), {ok, #state{}}. -handle_call(_Request, _From, State) -> - Reply = ok, {reply, Reply, State}. +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), + {noreply, State}. -handle_cast(_Msg, State) -> {noreply, State}. +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), + {noreply, State}. -handle_info({route, From, To, Packet}, State) -> - case catch do_route(From, To, Packet) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p~nwhen processing: ~p", - [Reason, {From, To, Packet}]); - _ -> ok - end, +handle_info({route, Packet}, State) -> + route(Packet), {noreply, State}; -handle_info({register_iq_handler, Host, XMLNS, Module, - Function}, - State) -> - ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function}), - catch mod_disco:register_feature(Host, XMLNS), - {noreply, State}; -handle_info({register_iq_handler, Host, XMLNS, Module, - Function, Opts}, - State) -> - ets:insert(?IQTABLE, - {{XMLNS, Host}, Module, Function, Opts}), - catch mod_disco:register_feature(Host, XMLNS), - {noreply, State}; -handle_info({unregister_iq_handler, Host, XMLNS}, - State) -> - case ets:lookup(?IQTABLE, {XMLNS, Host}) of - [{_, Module, Function, Opts}] -> - gen_iq_handler:stop_iq_handler(Module, Function, Opts); - _ -> ok - end, - ets:delete(?IQTABLE, {XMLNS, Host}), - catch mod_disco:unregister_feature(Host, XMLNS), - {noreply, State}; -handle_info(refresh_iq_handlers, State) -> - lists:foreach(fun (T) -> - case T of - {{XMLNS, Host}, _Module, _Function, _Opts} -> - catch mod_disco:register_feature(Host, XMLNS); - {{XMLNS, Host}, _Module, _Function} -> - catch mod_disco:register_feature(Host, XMLNS); - _ -> ok - end - end, - ets:tab2list(?IQTABLE)), - {noreply, State}; -handle_info({timeout, _TRef, ID}, State) -> - process_iq_timeout(ID), - {noreply, State}; -handle_info(_Info, State) -> +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), {noreply, State}. terminate(_Reason, _State) -> + lists:foreach(fun host_down/1, ejabberd_option:hosts()), + ejabberd_hooks:delete(host_up, ?MODULE, host_up, 10), + ejabberd_hooks:delete(host_down, ?MODULE, host_down, 100), ok. code_change(_OldVsn, State, _Extra) -> @@ -274,69 +149,25 @@ code_change(_OldVsn, State, _Extra) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- --spec do_route(jid(), jid(), stanza()) -> any(). -do_route(From, To, Packet) -> - ?DEBUG("local route~n\tfrom ~p~n\tto ~p~n\tpacket " - "~P~n", - [From, To, Packet, 8]), - Type = xmpp:get_type(Packet), - if To#jid.luser /= <<"">> -> - ejabberd_sm:route(From, To, Packet); - is_record(Packet, iq), To#jid.lresource == <<"">> -> - process_iq(From, To, Packet); - Type == result; Type == error -> - ok; - true -> - ejabberd_hooks:run(local_send_to_resource_hook, - To#jid.lserver, [From, To, Packet]) - end. - -spec update_table() -> ok. update_table() -> - case catch mnesia:table_info(iq_response, attributes) of - [id, module, function] -> - mnesia:delete_table(iq_response), - ok; - [id, module, function, timer] -> - ok; - {'EXIT', _} -> - ok - end. + catch mnesia:delete_table(iq_response), + ok. --spec get_iq_callback(binary()) -> {ok, module(), atom() | function()} | error. -get_iq_callback(ID) -> - case mnesia:dirty_read(iq_response, ID) of - [#iq_response{module = Module, timer = TRef, - function = Function}] -> - cancel_timer(TRef), - mnesia:dirty_delete(iq_response, ID), - {ok, Module, Function}; - _ -> - error - end. +host_up(Host) -> + Owner = case whereis(?MODULE) of + undefined -> self(); + Pid -> Pid + end, + ejabberd_router:register_route(Host, Host, {apply, ?MODULE, route}, Owner), + ejabberd_hooks:add(local_send_to_resource_hook, Host, + ?MODULE, bounce_resource_packet, 100). --spec process_iq_timeout(binary()) -> any(). -process_iq_timeout(ID) -> - spawn(fun process_iq_timeout/0) ! ID. - --spec process_iq_timeout() -> any(). -process_iq_timeout() -> - receive - ID -> - case get_iq_callback(ID) of - {ok, undefined, Function} -> - Function(timeout); - _ -> - ok - end - after 5000 -> - ok - end. - --spec cancel_timer(reference()) -> ok. -cancel_timer(TRef) -> - case erlang:cancel_timer(TRef) of - false -> - receive {timeout, TRef, _} -> ok after 0 -> ok end; - _ -> ok - end. +host_down(Host) -> + Owner = case whereis(?MODULE) of + undefined -> self(); + Pid -> Pid + end, + ejabberd_router:unregister_route(Host, Owner), + ejabberd_hooks:delete(local_send_to_resource_hook, Host, + ?MODULE, bounce_resource_packet, 100). diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl index 795d4f390..c002914bf 100644 --- a/src/ejabberd_logger.erl +++ b/src/ejabberd_logger.erl @@ -1,11 +1,11 @@ %%%------------------------------------------------------------------- -%%% @author Evgeniy Khramtsov -%%% @doc -%%% -%%% @end +%%% File : ejabberd_logger.erl +%%% Author : Evgeniy Khramtsov +%%% Purpose : ejabberd logger wrapper %%% Created : 12 May 2013 by Evgeniy Khramtsov %%% -%%% ejabberd, Copyright (C) 2013-2016 ProcessOne +%%% +%%% ejabberd, Copyright (C) 2013-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -23,34 +23,36 @@ %%%------------------------------------------------------------------- -module(ejabberd_logger). - --behaviour(ejabberd_config). +-compile({no_auto_import, [get/0]}). %% API --export([start/0, reopen_log/0, rotate_log/0, get/0, set/1, get_log_path/0, opt_type/1]). +-export([start/0, get/0, set/1, get_log_path/0, flush/0]). +-export([convert_loglevel/1, loglevels/0, set_modules_fully_logged/1, config_reloaded/0]). +-ifndef(LAGER). +-export([progress_filter/2]). +-endif. +%% Deprecated functions +-export([restart/0, reopen_log/0, rotate_log/0]). +-deprecated([{restart, 0}, + {reopen_log, 0}, + {rotate_log, 0}]). --include("ejabberd.hrl"). +-type loglevel() :: none | emergency | alert | critical | + error | warning | notice | info | debug. --type loglevel() :: 0 | 1 | 2 | 3 | 4 | 5. +-define(is_loglevel(L), + ((L == none) or (L == emergency) or (L == alert) + or (L == critical) or (L == error) or (L == warning) + or (L == notice) or (L == info) or (L == debug))). --spec start() -> ok. --spec get_log_path() -> string(). --spec reopen_log() -> ok. --spec rotate_log() -> ok. --spec get() -> {loglevel(), atom(), string()}. --spec set(loglevel() | {loglevel(), list()}) -> {module, module()}. +-export_type([loglevel/0]). + +-include("logger.hrl"). %%%=================================================================== %%% API %%%=================================================================== -%% @doc Returns the full path to the ejabberd log file. -%% It first checks for application configuration parameter 'log_path'. -%% If not defined it checks the environment variable EJABBERD_LOG_PATH. -%% And if that one is neither defined, returns the default value: -%% "ejabberd.log" in current directory. -%% Note: If the directory where to place the ejabberd log file to not exist, -%% it is not created and no log file will be generated. -%% @spec () -> string() +-spec get_log_path() -> string(). get_log_path() -> case ejabberd_config:env_binary_to_list(ejabberd, log_path) of {ok, Path} -> @@ -58,35 +60,48 @@ get_log_path() -> undefined -> case os:getenv("EJABBERD_LOG_PATH") of false -> - ?LOG_PATH; + "ejabberd.log"; Path -> Path end end. -opt_type(log_rotate_date) -> - fun(S) -> binary_to_list(iolist_to_binary(S)) end; -opt_type(log_rotate_size) -> - fun(I) when is_integer(I), I >= 0 -> I end; -opt_type(log_rotate_count) -> - fun(I) when is_integer(I), I >= 0 -> I end; -opt_type(log_rate_limit) -> - fun(I) when is_integer(I), I >= 0 -> I end; -opt_type(_) -> - [log_rotate_date, log_rotate_size, log_rotate_count, log_rate_limit]. +-spec loglevels() -> [loglevel(), ...]. +loglevels() -> + [none, emergency, alert, critical, error, warning, notice, info, debug]. +-spec convert_loglevel(0..5) -> loglevel(). +convert_loglevel(0) -> none; +convert_loglevel(1) -> critical; +convert_loglevel(2) -> error; +convert_loglevel(3) -> warning; +convert_loglevel(4) -> info; +convert_loglevel(5) -> debug. + +quiet_mode() -> + case application:get_env(ejabberd, quiet) of + {ok, true} -> true; + _ -> false + end. + +-spec get_integer_env(atom(), T) -> T. get_integer_env(Name, Default) -> case application:get_env(ejabberd, Name) of {ok, I} when is_integer(I), I>=0 -> I; + {ok, infinity} -> + infinity; undefined -> Default; {ok, Junk} -> - error_logger:error_msg("wrong value for ~s: ~p; " + error_logger:error_msg("wrong value for ~ts: ~p; " "using ~p as a fallback~n", [Name, Junk, Default]), Default end. + +-ifdef(LAGER). +-spec get_string_env(atom(), T) -> T. get_string_env(Name, Default) -> case application:get_env(ejabberd, Name) of {ok, L} when is_list(L) -> @@ -94,14 +109,16 @@ get_string_env(Name, Default) -> undefined -> Default; {ok, Junk} -> - error_logger:error_msg("wrong value for ~s: ~p; " + error_logger:error_msg("wrong value for ~ts: ~p; " "using ~p as a fallback~n", [Name, Junk, Default]), Default end. -%% @spec () -> ok start() -> + start(info). + +start(Level) -> StartedApps = application:which_applications(5000), case lists:keyfind(logger, 1, StartedApps) of %% Elixir logger is started. We assume everything is in place @@ -109,24 +126,23 @@ start() -> {logger, _, _} -> error_logger:info_msg("Ignoring ejabberd logger options, using Elixir Logger.", []), %% Do not start lager, we rely on Elixir Logger - do_start_for_logger(); + do_start_for_logger(Level); _ -> - do_start() + do_start(Level) end. -do_start_for_logger() -> +do_start_for_logger(Level) -> application:load(sasl), application:set_env(sasl, sasl_error_logger, false), application:load(lager), application:set_env(lager, error_logger_redirect, false), application:set_env(lager, error_logger_whitelist, ['Elixir.Logger.ErrorHandler']), application:set_env(lager, crash_log, false), - application:set_env(lager, handlers, [{elixir_logger_backend, [{level, info}]}]), + application:set_env(lager, handlers, [{elixir_logger_backend, [{level, Level}]}]), ejabberd:start_app(lager), ok. -%% Start lager -do_start() -> +do_start(Level) -> application:load(sasl), application:set_env(sasl, sasl_error_logger, false), application:load(lager), @@ -135,14 +151,25 @@ do_start() -> ErrorLog = filename:join([Dir, "error.log"]), CrashLog = filename:join([Dir, "crash.log"]), LogRotateDate = get_string_env(log_rotate_date, ""), - LogRotateSize = get_integer_env(log_rotate_size, 10*1024*1024), + LogRotateSize = case get_integer_env(log_rotate_size, 10*1024*1024) of + infinity -> 0; + V -> V + end, LogRotateCount = get_integer_env(log_rotate_count, 1), LogRateLimit = get_integer_env(log_rate_limit, 100), + ConsoleLevel0 = case quiet_mode() of + true -> critical; + _ -> Level + end, + ConsoleLevel = case get_lager_version() >= "3.6.0" of + true -> [{level, ConsoleLevel0}]; + false -> ConsoleLevel0 + end, application:set_env(lager, error_logger_hwm, LogRateLimit), application:set_env( lager, handlers, - [{lager_console_backend, info}, - {lager_file_backend, [{file, ConsoleLog}, {level, info}, {date, LogRotateDate}, + [{lager_console_backend, ConsoleLevel}, + {lager_file_backend, [{file, ConsoleLog}, {level, Level}, {date, LogRotateDate}, {count, LogRotateCount}, {size, LogRotateSize}]}, {lager_file_backend, [{file, ErrorLog}, {level, error}, {date, LogRotateDate}, {count, LogRotateCount}, {size, LogRotateSize}]}]), @@ -151,16 +178,23 @@ do_start() -> application:set_env(lager, crash_log_size, LogRotateSize), application:set_env(lager, crash_log_count, LogRotateCount), ejabberd:start_app(lager), + lists:foreach(fun(Handler) -> + lager:set_loghwm(Handler, LogRateLimit) + end, gen_event:which_handlers(lager_event)). + +restart() -> + Level = ejabberd_option:loglevel(), + application:stop(lager), + start(Level). + +config_reloaded() -> ok. -%% @spec () -> ok reopen_log() -> - %% Lager detects external log rotation automatically. ok. -%% @spec () -> ok rotate_log() -> - lager_crash_log ! rotate, + catch lager_crash_log ! rotate, lists:foreach( fun({lager_file_backend, File}) -> whereis(lager_event) ! {rotate, File}; @@ -168,53 +202,7 @@ rotate_log() -> ok end, gen_event:which_handlers(lager_event)). -%% @spec () -> {loglevel(), atom(), string()} get() -> - case get_lager_loglevel() of - none -> {0, no_log, "No log"}; - emergency -> {1, critical, "Critical"}; - alert -> {1, critical, "Critical"}; - critical -> {1, critical, "Critical"}; - error -> {2, error, "Error"}; - warning -> {3, warning, "Warning"}; - notice -> {3, warning, "Warning"}; - info -> {4, info, "Info"}; - debug -> {5, debug, "Debug"} - end. - -%% @spec (loglevel() | {loglevel(), list()}) -> {module, module()} -set(LogLevel) when is_integer(LogLevel) -> - LagerLogLevel = case LogLevel of - 0 -> none; - 1 -> critical; - 2 -> error; - 3 -> warning; - 4 -> info; - 5 -> debug; - E -> throw({wrong_loglevel, E}) - end, - case get_lager_loglevel() of - LagerLogLevel -> - ok; - _ -> - ConsoleLog = get_log_path(), - lists:foreach( - fun({lager_file_backend, File} = H) when File == ConsoleLog -> - lager:set_loglevel(H, LagerLogLevel); - (lager_console_backend = H) -> - lager:set_loglevel(H, LagerLogLevel); - (elixir_logger_backend = H) -> - lager:set_loglevel(H, LagerLogLevel); - (_) -> - ok - end, gen_event:which_handlers(lager_event)) - end, - {module, lager}; -set({_LogLevel, _}) -> - error_logger:error_msg("custom loglevels are not supported for 'lager'"), - {module, lager}. - -get_lager_loglevel() -> Handlers = get_lager_handlers(), lists:foldl(fun(lager_console_backend, _Acc) -> lager:get_loglevel(lager_console_backend); @@ -225,6 +213,31 @@ get_lager_loglevel() -> end, none, Handlers). +set(N) when is_integer(N), N>=0, N=<5 -> + set(convert_loglevel(N)); +set(Level) when ?is_loglevel(Level) -> + case get() of + Level -> + ok; + _ -> + ConsoleLog = get_log_path(), + QuietMode = quiet_mode(), + lists:foreach( + fun({lager_file_backend, File} = H) when File == ConsoleLog -> + lager:set_loglevel(H, Level); + (lager_console_backend = H) when not QuietMode -> + lager:set_loglevel(H, Level); + (elixir_logger_backend = H) -> + lager:set_loglevel(H, Level); + (_) -> + ok + end, get_lager_handlers()) + end, + case Level of + debug -> xmpp:set_config([{debug, true}]); + _ -> xmpp:set_config([{debug, false}]) + end. + get_lager_handlers() -> case catch gen_event:which_handlers(lager_event) of {'EXIT',noproc} -> @@ -232,3 +245,203 @@ get_lager_handlers() -> Result -> Result end. + +-spec get_lager_version() -> string(). +get_lager_version() -> + Apps = application:loaded_applications(), + case lists:keyfind(lager, 1, Apps) of + {_, _, Vsn} -> Vsn; + false -> "0.0.0" + end. + +set_modules_fully_logged(_) -> ok. + +flush() -> + application:stop(lager), + application:stop(sasl). + +-else. +-include_lib("kernel/include/logger.hrl"). + +-spec start() -> ok | {error, term()}. +start() -> + start(info). + +start(Level) -> + EjabberdLog = get_log_path(), + Dir = filename:dirname(EjabberdLog), + ErrorLog = filename:join([Dir, "error.log"]), + LogRotateSize = get_integer_env(log_rotate_size, 10*1024*1024), + LogRotateCount = get_integer_env(log_rotate_count, 1), + LogBurstLimitWindowTime = get_integer_env(log_burst_limit_window_time, 1000), + LogBurstLimitCount = get_integer_env(log_burst_limit_count, 500), + Config = #{max_no_bytes => LogRotateSize, + max_no_files => LogRotateCount, + filesync_repeat_interval => no_repeat, + file_check => 1000, + sync_mode_qlen => 1000, + drop_mode_qlen => 1000, + flush_qlen => 5000, + burst_limit_window_time => LogBurstLimitWindowTime, + burst_limit_max_count => LogBurstLimitCount}, + FmtConfig = #{legacy_header => false, + time_designator => $\s, + max_size => 100*1024, + single_line => false}, + FileFmtConfig = FmtConfig#{template => file_template()}, + ConsoleFmtConfig = FmtConfig#{template => console_template()}, + try + ok = logger:set_primary_config(level, Level), + DefaultHandlerId = get_default_handlerid(), + ok = logger:update_formatter_config(DefaultHandlerId, ConsoleFmtConfig), + case quiet_mode() of + true -> + ok = logger:set_handler_config(DefaultHandlerId, level, critical); + _ -> + ok + end, + case logger:add_primary_filter(progress_report, + {fun ?MODULE:progress_filter/2, stop}) of + ok -> ok; + {error, {already_exist, _}} -> ok + end, + case logger:add_handler(ejabberd_log, logger_std_h, + #{level => all, + config => Config#{file => EjabberdLog}, + formatter => {logger_formatter, FileFmtConfig}}) of + ok -> ok; + {error, {already_exist, _}} -> ok + end, + case logger:add_handler(error_log, logger_std_h, + #{level => error, + config => Config#{file => ErrorLog}, + formatter => {logger_formatter, FileFmtConfig}}) of + ok -> ok; + {error, {already_exist, _}} -> ok + end + catch _:{Tag, Err} when Tag == badmatch; Tag == case_clause -> + ?LOG_CRITICAL("Failed to set logging: ~p", [Err]), + Err + end. + +get_default_handlerid() -> + Ids = logger:get_handler_ids(), + case lists:member(default, Ids) of + true -> default; + false -> hd(Ids) + end. + +-spec restart() -> ok. +restart() -> + ok. + +-spec config_reloaded() -> ok. +config_reloaded() -> + LogRotateSize = ejabberd_option:log_rotate_size(), + LogRotateCount = ejabberd_option:log_rotate_count(), + LogBurstLimitWindowTime = ejabberd_option:log_burst_limit_window_time(), + LogBurstLimitCount = ejabberd_option:log_burst_limit_count(), + lists:foreach( + fun(Handler) -> + case logger:get_handler_config(Handler) of + {ok, #{config := Config}} -> + Config2 = Config#{ + max_no_bytes => LogRotateSize, + max_no_files => LogRotateCount, + burst_limit_window_time => LogBurstLimitWindowTime, + burst_limit_max_count => LogBurstLimitCount}, + logger:update_handler_config(Handler, config, Config2); + _ -> + ok + end + end, [ejabberd_log, error_log]). + +progress_filter(#{level:=info,msg:={report,#{label:={_,progress}}}} = Event, _) -> + case get() of + debug -> + logger_filters:progress(Event#{level => debug}, log); + _ -> + stop + end; +progress_filter(Event, _) -> + Event. + +-ifdef(ELIXIR_ENABLED). +console_template() -> + case (false /= code:is_loaded('Elixir.Logger')) + andalso + 'Elixir.System':version() >= <<"1.15">> of + true -> + {ok, DC} = logger:get_handler_config(default), + MessageFormat = case maps:get(formatter, DC) of + %% https://hexdocs.pm/logger/1.17.2/Logger.Formatter.html#module-formatting + {'Elixir.Logger.Formatter', _} -> + message; + %% https://www.erlang.org/doc/apps/kernel/logger_formatter#t:template/0 + {logger_formatter, _} -> + msg + end, + [date, " ", time, " [", level, "] ", MessageFormat, "\n"]; + false -> + [time, " [", level, "] " | msg()] + end. +msg() -> + [{logger_formatter, [[logger_formatter, title], ":", io_lib:nl()], []}, + msg, io_lib:nl()]. +-else. +console_template() -> + [time, " ", ?CLEAD, ?CDEFAULT, clevel, "[", level, "] ", ?CMID, ?CDEFAULT, ctext | msg()]. +msg() -> + [{logger_formatter, [[logger_formatter, title], ":", io_lib:nl()], []}, + msg, ?CCLEAN, io_lib:nl()]. +-endif. + +file_template() -> + [time, " [", level, "] ", pid, + {mfa, ["@", mfa, {line, [":", line], []}], []}, " " | msg()]. + +-spec reopen_log() -> ok. +reopen_log() -> + ok. + +-spec rotate_log() -> ok. +rotate_log() -> + ok. + +-spec get() -> loglevel(). +get() -> + #{level := Level} = logger:get_primary_config(), + Level. + +-spec set(0..5 | loglevel()) -> ok. +set(N) when is_integer(N), N>=0, N=<5 -> + set(convert_loglevel(N)); +set(Level) when ?is_loglevel(Level) -> + case get() of + Level -> ok; + PrevLevel -> + ?LOG_NOTICE("Changing loglevel from '~s' to '~s'", + [PrevLevel, Level]), + logger:set_primary_config(level, Level), + case Level of + debug -> xmpp:set_config([{debug, true}]); + _ -> xmpp:set_config([{debug, false}]) + end + end. + +set_modules_fully_logged(Modules) -> + logger:unset_module_level(), + logger:set_module_level(Modules, all). + +-spec flush() -> ok. +flush() -> + lists:foreach( + fun(#{id := HandlerId, module := logger_std_h}) -> + logger_std_h:filesync(HandlerId); + (#{id := HandlerId, module := logger_disk_log_h}) -> + logger_disk_log_h:filesync(HandlerId); + (_) -> + ok + end, logger:get_handler_config()). + +-endif. diff --git a/src/ejabberd_mnesia.erl b/src/ejabberd_mnesia.erl new file mode 100644 index 000000000..d9db27219 --- /dev/null +++ b/src/ejabberd_mnesia.erl @@ -0,0 +1,465 @@ +%%%---------------------------------------------------------------------- +%%% File : mnesia_mnesia.erl +%%% Author : Christophe Romain +%%% Purpose : Handle configurable mnesia schema +%%% Created : 17 Nov 2016 by Christophe Romain +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2025 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. +%%% +%%%---------------------------------------------------------------------- + +%%% This module should be used everywhere ejabberd creates a mnesia table +%%% to make the schema customizable without code change +%%% Just apply this change in ejabberd modules +%%% s/ejabberd_mnesia:create(?MODULE, /ejabberd_mnesia:create(?MODULE, / + +-module(ejabberd_mnesia). +-author('christophe.romain@process-one.net'). + +-behaviour(gen_server). + +-export([start/0, create/3, update/2, transform/2, transform/3, + dump_schema/0]). +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +-define(NEED_RESET, [local_content, type]). + +-include("logger.hrl"). + + +-record(state, {tables = #{} :: tables(), + schema = [] :: [{atom(), custom_schema()}]}). + +-type tables() :: #{atom() => {[{atom(), term()}], term()}}. +-type custom_schema() :: [{ram_copies | disc_copies | disc_only_copies, [node()]} | + {local_content, boolean()} | + {type, set | ordered_set | bag} | + {attributes, [atom()]} | + {index, [atom()]}]. + +start() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +-spec create(module(), atom(), list()) -> any(). +create(Module, Name, TabDef) -> + gen_server:call(?MODULE, {create, Module, Name, TabDef}, + %% Huge timeout is need to have enough + %% time to transform huge tables + timer:minutes(30)). + +init([]) -> + ejabberd_config:env_binary_to_list(mnesia, dir), + MyNode = node(), + DbNodes = mnesia:system_info(db_nodes), + case lists:member(MyNode, DbNodes) of + true -> + case mnesia:system_info(extra_db_nodes) of + [] -> mnesia:create_schema([node()]); + _ -> ok + end, + ejabberd:start_app(mnesia, permanent), + ?DEBUG("Waiting for Mnesia tables synchronization...", []), + mnesia:wait_for_tables(mnesia:system_info(local_tables), infinity), + Schema = read_schema_file(), + {ok, #state{schema = Schema}}; + false -> + ?CRITICAL_MSG("Erlang node name mismatch: I'm running in node [~ts], " + "but the mnesia database is owned by ~p", [MyNode, DbNodes]), + ?CRITICAL_MSG("Either set ERLANG_NODE in ejabberdctl.cfg " + "or change node name in Mnesia by running: " + "ejabberdctl mnesia_change ~ts", [hd(DbNodes)]), + {stop, node_name_mismatch} + end. + +handle_call({create, Module, Name, TabDef}, _From, State) -> + case maps:get(Name, State#state.tables, undefined) of + {TabDef, Result} -> + {reply, Result, State}; + _ -> + Result = do_create(Module, Name, TabDef, State#state.schema), + Tables = maps:put(Name, {TabDef, Result}, State#state.tables), + {reply, Result, State#state{tables = Tables}} + end; +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), + {noreply, State}. + +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), + {noreply, State}. + +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +do_create(Module, Name, TabDef, TabDefs) -> + code:ensure_loaded(Module), + Schema = schema(Name, TabDef, TabDefs), + {attributes, Attrs} = lists:keyfind(attributes, 1, Schema), + case catch mnesia:table_info(Name, attributes) of + {'EXIT', _} -> + create(Name, TabDef); + Attrs -> + case need_reset(Name, Schema) of + true -> + reset(Name, Schema); + false -> + case update(Name, Attrs, Schema) of + {atomic, ok} -> + transform(Module, Name, Attrs, Attrs); + Err -> + Err + end + end; + OldAttrs -> + transform(Module, Name, OldAttrs, Attrs) + end. + +reset(Name, TabDef) -> + ?INFO_MSG("Deleting Mnesia table '~ts'", [Name]), + mnesia_op(delete_table, [Name]), + create(Name, TabDef). + +update(Name, TabDef) -> + {attributes, Attrs} = lists:keyfind(attributes, 1, TabDef), + update(Name, Attrs, TabDef). + +update(Name, Attrs, TabDef) -> + case change_table_copy_type(Name, TabDef) of + {atomic, ok} -> + CurrIndexes = [lists:nth(N-1, Attrs) || + N <- mnesia:table_info(Name, index)], + NewIndexes = proplists:get_value(index, TabDef, []), + case delete_indexes(Name, CurrIndexes -- NewIndexes) of + {atomic, ok} -> + add_indexes(Name, NewIndexes -- CurrIndexes); + Err -> + Err + end; + Err -> + Err + end. + +change_table_copy_type(Name, TabDef) -> + CurrType = mnesia:table_info(Name, storage_type), + NewType = case lists:filter(fun is_storage_type_option/1, TabDef) of + [{Type, _}|_] -> Type; + [] -> CurrType + end, + if NewType /= CurrType -> + ?INFO_MSG("Changing Mnesia table '~ts' from ~ts to ~ts", + [Name, CurrType, NewType]), + if CurrType == unknown -> mnesia_op(add_table_copy, [Name, node(), NewType]); + true -> + mnesia_op(change_table_copy_type, [Name, node(), NewType]) + end; + true -> + {atomic, ok} + end. + +delete_indexes(Name, [Index|Indexes]) -> + ?INFO_MSG("Deleting index '~ts' from Mnesia table '~ts'", [Index, Name]), + case mnesia_op(del_table_index, [Name, Index]) of + {atomic, ok} -> + delete_indexes(Name, Indexes); + Err -> + Err + end; +delete_indexes(_Name, []) -> + {atomic, ok}. + +add_indexes(Name, [Index|Indexes]) -> + ?INFO_MSG("Adding index '~ts' to Mnesia table '~ts'", [Index, Name]), + case mnesia_op(add_table_index, [Name, Index]) of + {atomic, ok} -> + add_indexes(Name, Indexes); + Err -> + Err + end; +add_indexes(_Name, []) -> + {atomic, ok}. + +% +% utilities +% + +schema(Name, Default, Schema) -> + case lists:keyfind(Name, 1, Schema) of + {_, Custom} -> + TabDefs = merge(Custom, Default), + ?DEBUG("Using custom schema for table '~ts': ~p", + [Name, TabDefs]), + TabDefs; + false -> + Default + end. + +-spec read_schema_file() -> [{atom(), custom_schema()}]. +read_schema_file() -> + File = schema_path(), + case fast_yaml:decode_from_file(File, [plain_as_atom]) of + {ok, Y} -> + case econf:validate(validator(), lists:flatten(Y)) of + {ok, []} -> + ?WARNING_MSG("Mnesia schema file ~ts is empty", [File]), + []; + {ok, Config} -> + lists:map( + fun({Tab, Opts}) -> + {Tab, lists:map( + fun({storage_type, T}) -> {T, [node()]}; + (Other) -> Other + end, Opts)} + end, Config); + {error, Reason, Ctx} -> + ?ERROR_MSG("Failed to read Mnesia schema from ~ts: ~ts", + [File, econf:format_error(Reason, Ctx)]), + [] + end; + {error, enoent} -> + ?DEBUG("No custom Mnesia schema file found at ~ts", [File]), + []; + {error, Reason} -> + ?ERROR_MSG("Failed to read Mnesia schema file ~ts: ~ts", + [File, fast_yaml:format_error(Reason)]) + end. + +-spec validator() -> econf:validator(). +validator() -> + econf:map( + econf:atom(), + econf:options( + #{storage_type => econf:enum([ram_copies, disc_copies, disc_only_copies]), + local_content => econf:bool(), + type => econf:enum([set, ordered_set, bag]), + attributes => econf:list(econf:atom()), + index => econf:list(econf:atom())}, + [{return, orddict}, unique]), + [unique]). + +create(Name, TabDef) -> + Type = lists:foldl( + fun({ram_copies, _}, _) -> " ram "; + ({disc_copies, _}, _) -> " disc "; + ({disc_only_copies, _}, _) -> " disc_only "; + (_, Acc) -> Acc + end, " ", TabDef), + ?INFO_MSG("Creating Mnesia~tstable '~ts'", [Type, Name]), + case mnesia_op(create_table, [Name, TabDef]) of + {atomic, ok} -> + add_table_copy(Name); + Err -> + Err + end. + +%% The table MUST exist, otherwise the function would fail +add_table_copy(Name) -> + Type = mnesia:table_info(Name, storage_type), + Nodes = mnesia:table_info(Name, Type), + case lists:member(node(), Nodes) of + true -> + {atomic, ok}; + false -> + mnesia_op(add_table_copy, [Name, node(), Type]) + end. + +merge(Custom, Default) -> + NewDefault = case lists:any(fun is_storage_type_option/1, Custom) of + true -> + lists:filter( + fun(O) -> + not is_storage_type_option(O) + end, Default); + false -> + Default + end, + lists:ukeymerge(1, Custom, lists:ukeysort(1, NewDefault)). + +need_reset(Table, TabDef) -> + ValuesF = [mnesia:table_info(Table, Key) || Key <- ?NEED_RESET], + ValuesT = [proplists:get_value(Key, TabDef) || Key <- ?NEED_RESET], + lists:foldl( + fun({Val, Val}, Acc) -> Acc; + ({_, undefined}, Acc) -> Acc; + ({_, _}, _) -> true + end, false, lists:zip(ValuesF, ValuesT)). + +transform(Module, Name) -> + try mnesia:table_info(Name, attributes) of + Attrs -> + transform(Module, Name, Attrs, Attrs) + catch _:{aborted, _} = Err -> + Err + end. + +transform(Module, Name, NewAttrs) -> + try mnesia:table_info(Name, attributes) of + OldAttrs -> + transform(Module, Name, OldAttrs, NewAttrs) + catch _:{aborted, _} = Err -> + Err + end. + +transform(Module, Name, Attrs, Attrs) -> + case need_transform(Module, Name) of + true -> + ?INFO_MSG("Transforming table '~ts', this may take a while", [Name]), + transform_table(Module, Name); + false -> + {atomic, ok} + end; +transform(Module, Name, OldAttrs, NewAttrs) -> + Fun = case erlang:function_exported(Module, transform, 1) of + true -> transform_fun(Module, Name); + false -> fun(Old) -> do_transform(OldAttrs, NewAttrs, Old) end + end, + mnesia_op(transform_table, [Name, Fun, NewAttrs]). + +-spec need_transform(module(), atom()) -> boolean(). +need_transform(Module, Name) -> + case erlang:function_exported(Module, need_transform, 1) of + true -> + do_need_transform(Module, Name, mnesia:dirty_first(Name)); + false -> + false + end. + +do_need_transform(_Module, _Name, '$end_of_table') -> + false; +do_need_transform(Module, Name, Key) -> + Objs = mnesia:dirty_read(Name, Key), + case lists:foldl( + fun(_, true) -> true; + (Obj, _) -> Module:need_transform(Obj) + end, undefined, Objs) of + true -> true; + false -> false; + _ -> + do_need_transform(Module, Name, mnesia:dirty_next(Name, Key)) + end. + +do_transform(OldAttrs, Attrs, Old) -> + [Name|OldValues] = tuple_to_list(Old), + Before = lists:zip(OldAttrs, OldValues), + After = lists:foldl( + fun(Attr, Acc) -> + case lists:keyfind(Attr, 1, Before) of + false -> [{Attr, undefined}|Acc]; + Value -> [Value|Acc] + end + end, [], lists:reverse(Attrs)), + {Attrs, NewRecord} = lists:unzip(After), + list_to_tuple([Name|NewRecord]). + +transform_fun(Module, Name) -> + fun(Obj) -> + try Module:transform(Obj) + catch + Class:Reason:StackTrace -> + ?ERROR_MSG("Failed to transform Mnesia table ~ts:~n" + "** Record: ~p~n" + "** ~ts", + [Name, + Obj, + misc:format_exception(2, Class, Reason, StackTrace)]), + erlang:raise(Class, Reason, StackTrace) + end + end. + +transform_table(Module, Name) -> + Type = mnesia:table_info(Name, type), + Attrs = mnesia:table_info(Name, attributes), + TmpTab = list_to_atom(atom_to_list(Name) ++ "_backup"), + StorageType = if Type == ordered_set -> disc_copies; + true -> disc_only_copies + end, + mnesia:create_table(TmpTab, + [{StorageType, [node()]}, + {type, Type}, + {local_content, true}, + {record_name, Name}, + {attributes, Attrs}]), + mnesia:clear_table(TmpTab), + Fun = transform_fun(Module, Name), + Res = mnesia_op( + transaction, + [fun() -> do_transform_table(Name, Fun, TmpTab, mnesia:first(Name)) end]), + mnesia:delete_table(TmpTab), + Res. + +do_transform_table(Name, _Fun, TmpTab, '$end_of_table') -> + mnesia:foldl( + fun(Obj, _) -> + mnesia:write(Name, Obj, write) + end, ok, TmpTab); +do_transform_table(Name, Fun, TmpTab, Key) -> + Next = mnesia:next(Name, Key), + Objs = mnesia:read(Name, Key), + lists:foreach( + fun(Obj) -> + mnesia:write(TmpTab, Fun(Obj), write), + mnesia:delete_object(Obj) + end, Objs), + do_transform_table(Name, Fun, TmpTab, Next). + +mnesia_op(Fun, Args) -> + case apply(mnesia, Fun, Args) of + {atomic, ok} -> + {atomic, ok}; + Other -> + ?ERROR_MSG("Failure on mnesia ~ts ~p: ~p", + [Fun, Args, Other]), + Other + end. + +schema_path() -> + Dir = case os:getenv("EJABBERD_MNESIA_SCHEMA") of + false -> mnesia:system_info(directory); + Path -> Path + end, + filename:join(Dir, "ejabberd.schema"). + +is_storage_type_option({O, _}) -> + O == ram_copies orelse O == disc_copies orelse O == disc_only_copies. + +dump_schema() -> + File = schema_path(), + Schema = lists:flatmap( + fun(schema) -> + []; + (Tab) -> + [{Tab, [{storage_type, + mnesia:table_info(Tab, storage_type)}, + {local_content, + mnesia:table_info(Tab, local_content)}]}] + end, mnesia:system_info(tables)), + case file:write_file(File, [fast_yaml:encode(Schema), io_lib:nl()]) of + ok -> + io:format("Mnesia schema is written to ~ts~n", [File]); + {error, Reason} -> + io:format("Failed to write Mnesia schema to ~ts: ~ts", + [File, file:format_error(Reason)]) + end. diff --git a/src/ejabberd_node_groups.erl b/src/ejabberd_node_groups.erl deleted file mode 100644 index 352757dd8..000000000 --- a/src/ejabberd_node_groups.erl +++ /dev/null @@ -1,173 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : ejabberd_node_groups.erl -%%% Author : Alexey Shchepin -%%% Purpose : Distributed named node groups based on pg2 module -%%% Created : 1 Nov 2006 by Alexey Shchepin -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne -%%% -%%% This program is free software; you can redistribute it and/or -%%% modify it under the terms of the GNU General Public License as -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License along -%%% with this program; if not, write to the Free Software Foundation, Inc., -%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -%%% -%%%---------------------------------------------------------------------- - --module(ejabberd_node_groups). - --behaviour(ejabberd_config). --author('alexey@process-one.net'). - --behaviour(gen_server). - -%% API --export([start_link/0, - join/1, - leave/1, - get_members/1, - get_closest_node/1]). - --export([init/1, handle_call/3, handle_cast/2, - handle_info/2, terminate/2, code_change/3, opt_type/1]). - --define(PG2, pg2). - --record(state, {}). - -%%==================================================================== -%% API -%%==================================================================== -%%-------------------------------------------------------------------- -%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} -%% Description: Starts the server -%%-------------------------------------------------------------------- -start_link() -> - gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). - -join(Name) -> - PG = {?MODULE, Name}, - pg2:create(PG), - pg2:join(PG, whereis(?MODULE)). - -leave(Name) -> - PG = {?MODULE, Name}, - pg2:leave(PG, whereis(?MODULE)). - -get_members(Name) -> - PG = {?MODULE, Name}, - [node(P) || P <- pg2:get_members(PG)]. - -get_closest_node(Name) -> - PG = {?MODULE, Name}, - node(pg2:get_closest_pid(PG)). - -%%==================================================================== -%% gen_server callbacks -%%==================================================================== - -%%-------------------------------------------------------------------- -%% Function: init(Args) -> {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} -%% Description: Initiates the server -%%-------------------------------------------------------------------- -init([]) -> - {FE, BE} = - case ejabberd_config:get_option( - node_type, - fun(frontend) -> frontend; - (backend) -> backend; - (generic) -> generic - end, generic) of - frontend -> - {true, false}; - backend -> - {false, true}; - generic -> - {true, true}; - undefined -> - {true, true} - end, - if - FE -> - join(frontend); - true -> - ok - end, - if - BE -> - join(backend); - true -> - ok - end, - {ok, #state{}}. - -%%-------------------------------------------------------------------- -%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | -%% {stop, Reason, State} -%% Description: Handling call messages -%%-------------------------------------------------------------------- -handle_call(_Request, _From, State) -> - Reply = ok, - {reply, Reply, State}. - -%%-------------------------------------------------------------------- -%% Function: handle_cast(Msg, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling cast messages -%%-------------------------------------------------------------------- -handle_cast(_Msg, State) -> - {noreply, State}. - -%%-------------------------------------------------------------------- -%% Function: handle_info(Info, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling all non call/cast messages -%%-------------------------------------------------------------------- -handle_info(_Info, State) -> - {noreply, State}. - -%%-------------------------------------------------------------------- -%% Function: terminate(Reason, State) -> void() -%% Description: This function is called by a gen_server when it is about to -%% terminate. It should be the opposite of Module:init/1 and do any necessary -%% cleaning up. When it returns, the gen_server terminates with Reason. -%% The return value is ignored. -%%-------------------------------------------------------------------- -terminate(_Reason, _State) -> - ok. - -%%-------------------------------------------------------------------- -%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} -%% Description: Convert process state when code is changed -%%-------------------------------------------------------------------- -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -%%-------------------------------------------------------------------- -%%% Internal functions -%%-------------------------------------------------------------------- - -opt_type(node_type) -> - fun (frontend) -> frontend; - (backend) -> backend; - (generic) -> generic - end; -opt_type(_) -> [node_type]. diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl index 74e26e8da..a18596d46 100644 --- a/src/ejabberd_oauth.erl +++ b/src/ejabberd_oauth.erl @@ -5,7 +5,7 @@ %%% Created : 20 Mar 2015 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -32,13 +32,11 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --export([start/0, - start_link/0, +-export([start_link/0, get_client_identity/2, verify_redirection_uri/3, authenticate_user/2, authenticate_client/2, - verify_resowner_scope/3, associate_access_code/3, associate_access_token/3, associate_refresh_token/3, @@ -47,106 +45,128 @@ check_token/2, scope_in_scope_list/2, process/2, - opt_type/1]). + config_reloaded/0, + verify_resowner_scope/3]). --export([oauth_issue_token/3, oauth_list_tokens/0, oauth_revoke_token/1, oauth_list_scopes/0]). +-export([get_commands_spec/0, + oauth_issue_token/3, oauth_list_tokens/0, oauth_revoke_token/1, + oauth_add_client_password/3, + oauth_add_client_implicit/3, + oauth_remove_client/1]). --include("xmpp.hrl"). +-export([web_menu_main/2, web_page_main/2]). --include("ejabberd.hrl"). +-include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). - -include("ejabberd_http.hrl"). -include("ejabberd_web_admin.hrl"). -include("ejabberd_oauth.hrl"). - -include("ejabberd_commands.hrl"). +-include("translate.hrl"). +-callback init() -> any(). +-callback store(#oauth_token{}) -> ok | {error, any()}. +-callback lookup(binary()) -> {ok, #oauth_token{}} | error. +-callback revoke(binary()) -> ok | {error, binary()}. +-callback clean(non_neg_integer()) -> any(). + +-record(oauth_ctx, { + password :: binary() | admin_generated, + client :: #oauth_client{} | undefined + }). %% There are two ways to obtain an oauth token: %% * Using the web form/api results in the token being generated in behalf of the user providing the user/pass %% * Using the command line and oauth_issue_token command, the token is generated in behalf of ejabberd' sysadmin %% (as it has access to ejabberd command line). --define(EXPIRE, 4294967). - -start() -> - DBMod = get_db_backend(), - DBMod:init(), - MaxSize = - ejabberd_config:get_option( - oauth_cache_size, - fun(I) when is_integer(I), I>0 -> I end, - 1000), - LifeTime = - ejabberd_config:get_option( - oauth_cache_life_time, - fun(I) when is_integer(I), I>0 -> I end, - timer:hours(1) div 1000), - cache_tab:new(oauth_token, - [{max_size, MaxSize}, {life_time, LifeTime}]), - Expire = expire(), - application:set_env(oauth2, backend, ejabberd_oauth), - application:set_env(oauth2, expiry_time, Expire), - application:start(oauth2), - ChildSpec = {?MODULE, {?MODULE, start_link, []}, - transient, 1000, worker, [?MODULE]}, - supervisor:start_child(ejabberd_sup, ChildSpec), - ejabberd_commands:register_commands(get_commands_spec()), - ok. - - get_commands_spec() -> [ #ejabberd_commands{name = oauth_issue_token, tags = [oauth], - desc = "Issue an oauth token for the given jid", + desc = "Issue an OAuth token for the given jid", module = ?MODULE, function = oauth_issue_token, args = [{jid, string},{ttl, integer}, {scopes, string}], policy = restricted, - args_example = ["user@server.com", "connected_users_number;muc_online_rooms"], + args_example = ["user@server.com", 3600, "connected_users_number;muc_online_rooms"], args_desc = ["Jid for which issue token", "Time to live of generated token in seconds", "List of scopes to allow, separated by ';'"], result = {result, {tuple, [{token, string}, {scopes, string}, {expires_in, string}]}} }, + #ejabberd_commands{name = oauth_issue_token, tags = [oauth], + desc = "Issue an OAuth token for the given jid", + module = ?MODULE, function = oauth_issue_token, + version = 1, + note = "updated in 24.02", + args = [{jid, string}, {ttl, integer}, {scopes, {list, {scope, binary}}}], + policy = restricted, + args_example = ["user@server.com", 3600, ["connected_users_number", "muc_online_rooms"]], + args_desc = ["Jid for which issue token", + "Time to live of generated token in seconds", + "List of scopes to allow"], + result = {result, {tuple, [{token, string}, {scopes, {list, {scope, string}}}, {expires_in, string}]}} + }, #ejabberd_commands{name = oauth_list_tokens, tags = [oauth], - desc = "List oauth tokens, their user and scope, and how many seconds remain until expirity", + desc = "List OAuth tokens, user, scope, and seconds to expire (only Mnesia)", + longdesc = "List _`oauth.md|OAuth`_ tokens, their user and scope, and how many seconds remain until expiry", module = ?MODULE, function = oauth_list_tokens, args = [], policy = restricted, result = {tokens, {list, {token, {tuple, [{token, string}, {user, string}, {scope, string}, {expires_in, string}]}}}} }, - #ejabberd_commands{name = oauth_list_scopes, tags = [oauth], - desc = "List scopes that can be granted to tokens generated through the command line, together with the commands they allow", - module = ?MODULE, function = oauth_list_scopes, - args = [], - policy = restricted, - result = {scopes, {list, {scope, {tuple, [{scope, string}, {commands, string}]}}}} - }, #ejabberd_commands{name = oauth_revoke_token, tags = [oauth], - desc = "Revoke authorization for a token", + desc = "Revoke authorization for an OAuth token", + note = "changed in 22.05", module = ?MODULE, function = oauth_revoke_token, - args = [{token, string}], + args = [{token, binary}], policy = restricted, - result = {tokens, {list, {token, {tuple, [{token, string}, {user, string}, {scope, string}, {expires_in, string}]}}}}, - result_desc = "List of remaining tokens" + result = {res, restuple}, + result_desc = "Result code" + }, + #ejabberd_commands{name = oauth_add_client_password, tags = [oauth], + desc = "Add OAuth client_id with password grant type", + module = ?MODULE, function = oauth_add_client_password, + args = [{client_id, binary}, + {client_name, binary}, + {secret, binary}], + policy = restricted, + result = {res, restuple} + }, + #ejabberd_commands{name = oauth_add_client_implicit, tags = [oauth], + desc = "Add OAuth client_id with implicit grant type", + module = ?MODULE, function = oauth_add_client_implicit, + args = [{client_id, binary}, + {client_name, binary}, + {redirect_uri, binary}], + policy = restricted, + result = {res, restuple} + }, + #ejabberd_commands{name = oauth_remove_client, tags = [oauth], + desc = "Remove OAuth client_id", + module = ?MODULE, function = oauth_remove_client, + args = [{client_id, binary}], + policy = restricted, + result = {res, restuple} } ]. -oauth_issue_token(Jid, TTLSeconds, ScopesString) -> +oauth_issue_token(Jid, TTLSeconds, [Head|_] = ScopesString) when is_integer(Head) -> Scopes = [list_to_binary(Scope) || Scope <- string:tokens(ScopesString, ";")], - case jid:from_string(list_to_binary(Jid)) of + oauth_issue_token(Jid, TTLSeconds, Scopes); +oauth_issue_token(Jid, TTLSeconds, Scopes) -> + try jid:decode(list_to_binary(Jid)) of #jid{luser =Username, lserver = Server} -> - case oauth2:authorize_password({Username, Server}, Scopes, admin_generated) of + Ctx1 = #oauth_ctx{password = admin_generated}, + case oauth2:authorize_password({Username, Server}, Scopes, Ctx1) of {ok, {_Ctx,Authorization}} -> {ok, {_AppCtx2, Response}} = oauth2:issue_token(Authorization, [{expiry_time, TTLSeconds}]), - {ok, AccessToken} = oauth2_response:access_token(Response), - {ok, VerifiedScope} = oauth2_response:scope(Response), + {ok, AccessToken} = oauth2_response:access_token(Response), + {ok, VerifiedScope} = oauth2_response:scope(Response), {AccessToken, VerifiedScope, integer_to_list(TTLSeconds) ++ " seconds"}; - {error, Error} -> - {error, Error} - end; - error -> + {error, Error} -> + {error, Error} + end + catch _:{bad_jid, _} -> {error, "Invalid JID: " ++ Jid} end. @@ -154,72 +174,135 @@ oauth_list_tokens() -> Tokens = mnesia:dirty_match_object(#oauth_token{_ = '_'}), {MegaSecs, Secs, _MiniSecs} = os:timestamp(), TS = 1000000 * MegaSecs + Secs, - [{Token, jid:to_string(jid:make(U,S,<<>>)), Scope, integer_to_list(Expires - TS) ++ " seconds"} || + [{Token, jid:encode(jid:make(U,S)), Scope, integer_to_list(Expires - TS) ++ " seconds"} || #oauth_token{token=Token, scope=Scope, us= {U,S},expire=Expires} <- Tokens]. oauth_revoke_token(Token) -> - ok = mnesia:dirty_delete(oauth_token, list_to_binary(Token)), - oauth_list_tokens(). + DBMod = get_db_backend(), + case DBMod:revoke(Token) of + ok -> + ets_cache:delete(oauth_cache, Token, + ejabberd_cluster:get_nodes()), + {ok, ""}; + Other -> + Other + end. -oauth_list_scopes() -> - [ {Scope, string:join([atom_to_list(Cmd) || Cmd <- Cmds], ",")} || {Scope, Cmds} <- dict:to_list(get_cmd_scopes())]. +oauth_add_client_password(ClientID, ClientName, Secret) -> + DBMod = get_db_backend(), + DBMod:store_client(#oauth_client{client_id = ClientID, + client_name = ClientName, + grant_type = password, + options = [{secret, Secret}]}), + {ok, []}. +oauth_add_client_implicit(ClientID, ClientName, RedirectURI) -> + DBMod = get_db_backend(), + DBMod:store_client(#oauth_client{client_id = ClientID, + client_name = ClientName, + grant_type = implicit, + options = [{redirect_uri, RedirectURI}]}), + {ok, []}. +oauth_remove_client(Client) -> + DBMod = get_db_backend(), + DBMod:remove_client(Client), + {ok, []}. +config_reloaded() -> + DBMod = get_db_backend(), + case init_cache(DBMod) of + true -> + ets_cache:setopts(oauth_cache, cache_opts()); + false -> + ok + end. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) -> - erlang:send_after(expire() * 1000, self(), clean), + DBMod = get_db_backend(), + DBMod:init(), + init_cache(DBMod), + Expire = expire(), + application:set_env(oauth2, backend, ejabberd_oauth), + application:set_env(oauth2, expiry_time, Expire div 1000), + application:start(oauth2), + ejabberd_commands:register_commands(get_commands_spec()), + ejabberd_hooks:add(webadmin_menu_main, ?MODULE, web_menu_main, 50), + ejabberd_hooks:add(webadmin_page_main, ?MODULE, web_page_main, 50), + ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50), + erlang:send_after(expire(), self(), clean), {ok, ok}. -handle_call(_Request, _From, State) -> - {reply, bad_request, State}. +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), + {noreply, State}. -handle_cast(_Msg, State) -> {noreply, State}. +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), + {noreply, State}. handle_info(clean, State) -> {MegaSecs, Secs, MiniSecs} = os:timestamp(), TS = 1000000 * MegaSecs + Secs, DBMod = get_db_backend(), DBMod:clean(TS), - erlang:send_after(trunc(expire() * 1000 * (1 + MiniSecs / 1000000)), + erlang:send_after(trunc(expire() * (1 + MiniSecs / 1000000)), self(), clean), {noreply, State}; -handle_info(_Info, State) -> {noreply, State}. +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), + {noreply, State}. -terminate(_Reason, _State) -> ok. +terminate(_Reason, _State) -> + ejabberd_hooks:delete(webadmin_menu_main, ?MODULE, web_menu_main, 50), + ejabberd_hooks:delete(webadmin_page_main, ?MODULE, web_page_main, 50), + ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 50). code_change(_OldVsn, State, _Extra) -> {ok, State}. +get_client_identity(<<"">>, Ctx) -> + {ok, {Ctx, {client, unknown_client}}}; +get_client_identity(ClientID, Ctx) when is_binary(ClientID) -> + {ok, {Ctx, {client, ClientID}}}. -get_client_identity(Client, Ctx) -> {ok, {Ctx, {client, Client}}}. - -verify_redirection_uri(_, _, Ctx) -> {ok, Ctx}. +verify_redirection_uri(_ClientID, RedirectURI, Ctx) -> + case Ctx of + #oauth_ctx{client = #oauth_client{grant_type = implicit} = Client} -> + case get_redirect_uri(Client) of + RedirectURI -> + {ok, Ctx}; + _ -> + {error, invalid_uri} + end; + #oauth_ctx{client = #oauth_client{}} -> + {error, invalid_client}; + _ -> + {ok, Ctx} + end. authenticate_user({User, Server}, Ctx) -> - case jid:make(User, Server, <<"">>) of + case jid:make(User, Server) of #jid{} = JID -> Access = - ejabberd_config:get_option( - {oauth_access, JID#jid.lserver}, - fun(A) -> A end, - none), + ejabberd_option:oauth_access(JID#jid.lserver), case acl:match_rule(JID#jid.lserver, Access, JID) of allow -> case Ctx of - {password, Password} -> - case ejabberd_auth:check_password(User, <<"">>, Server, Password) of - true -> + #oauth_ctx{password = admin_generated} -> {ok, {Ctx, {user, User, Server}}}; - false -> - {error, badpass} - end; - admin_generated -> - {ok, {Ctx, {user, User, Server}}} + #oauth_ctx{password = Password} + when is_binary(Password) -> + case ejabberd_auth:check_password(User, <<"">>, Server, Password) of + true -> + {ok, {Ctx, {user, User, Server}}}; + false -> + {error, badpass} + end end; deny -> {error, badpass} @@ -228,14 +311,28 @@ authenticate_user({User, Server}, Ctx) -> {error, badpass} end. -authenticate_client(Client, Ctx) -> {ok, {Ctx, {client, Client}}}. +authenticate_client(ClientID, Ctx) -> + case ejabberd_option:oauth_client_id_check() of + allow -> + {ok, {Ctx, {client, ClientID}}}; + deny -> {error, not_allowed}; + db -> + DBMod = get_db_backend(), + case DBMod:lookup_client(ClientID) of + {ok, #oauth_client{} = Client} -> + {ok, {Ctx#oauth_ctx{client = Client}, {client, ClientID}}}; + _ -> + {error, not_allowed} + end + end. +-spec verify_resowner_scope({user, binary(), binary()}, [binary()], any()) -> + {ok, any(), [binary()]} | {error, any()}. verify_resowner_scope({user, _User, _Server}, Scope, Ctx) -> - Cmds = ejabberd_commands:get_exposed_commands(), - Cmds1 = ['ejabberd:user', 'ejabberd:admin', sasl_auth | Cmds], - RegisteredScope = [atom_to_binary(C, utf8) || C <- Cmds1], + Cmds = [atom_to_binary(Name, utf8) || {Name, _, _} <- ejabberd_commands:list_commands()], + AllowedScopes = [<<"ejabberd:user">>, <<"ejabberd:admin">>, <<"sasl_auth">>] ++ Cmds, case oauth2_priv_set:is_subset(oauth2_priv_set:new(Scope), - oauth2_priv_set:new(RegisteredScope)) of + oauth2_priv_set:new(AllowedScopes)) of true -> {ok, {Ctx, Scope}}; false -> @@ -244,18 +341,6 @@ verify_resowner_scope({user, _User, _Server}, Scope, Ctx) -> verify_resowner_scope(_, _, _) -> {error, badscope}. - -get_cmd_scopes() -> - ScopeMap = lists:foldl(fun(Cmd, Accum) -> - case ejabberd_commands:get_command_policy_and_scope(Cmd) of - {ok, Policy, Scopes} when Policy =/= restricted -> - lists:foldl(fun(Scope, Accum2) -> - dict:append(Scope, Cmd, Accum2) - end, Accum, Scopes); - _ -> Accum - end end, dict:new(), ejabberd_commands:get_exposed_commands()), - ScopeMap. - %% This is callback for oauth tokens generated through the command line. Only open and admin commands are %% made available. %verify_client_scope({client, ejabberd_ctl}, Scope, Ctx) -> @@ -313,6 +398,8 @@ scope_in_scope_list(Scope, ScopeList) -> oauth2_priv_set:is_member(Scope2, TokenScopeSet) end, ScopeList). +-spec check_token(binary()) -> {ok, {binary(), binary()}, [binary()]} | + {false, expired | not_found}. check_token(Token) -> case lookup(Token) of {ok, #oauth_token{us = US, @@ -377,30 +464,47 @@ check_token(ScopeList, Token) -> store(R) -> - cache_tab:insert( - oauth_token, R#oauth_token.token, R, - fun() -> - DBMod = get_db_backend(), - DBMod:store(R) - end). + DBMod = get_db_backend(), + case DBMod:store(R) of + ok -> + ets_cache:delete(oauth_cache, R#oauth_token.token, + ejabberd_cluster:get_nodes()); + {error, _} = Err -> + Err + end. lookup(Token) -> - cache_tab:lookup( - oauth_token, Token, - fun() -> - DBMod = get_db_backend(), - case DBMod:lookup(Token) of - #oauth_token{} = R -> {ok, R}; - _ -> error - end - end). + ets_cache:lookup(oauth_cache, Token, + fun() -> + DBMod = get_db_backend(), + DBMod:lookup(Token) + end). +-spec init_cache(module()) -> boolean(). +init_cache(DBMod) -> + UseCache = use_cache(DBMod), + case UseCache of + true -> + ets_cache:new(oauth_cache, cache_opts()); + false -> + ets_cache:delete(oauth_cache) + end, + UseCache. + +use_cache(DBMod) -> + case erlang:function_exported(DBMod, use_cache, 0) of + true -> DBMod:use_cache(); + false -> ejabberd_option:oauth_use_cache() + end. + +cache_opts() -> + MaxSize = ejabberd_option:oauth_cache_size(), + CacheMissed = ejabberd_option:oauth_cache_missed(), + LifeTime = ejabberd_option:oauth_cache_life_time(), + [{max_size, MaxSize}, {life_time, LifeTime}, {cache_missed, CacheMissed}]. expire() -> - ejabberd_config:get_option( - oauth_expire, - fun(I) when is_integer(I) -> I end, - ?EXPIRE). + ejabberd_option:oauth_expire(). -define(DIV(Class, Els), ?XAE(<<"div">>, [{<<"class">>, Class}], Els)). @@ -416,6 +520,10 @@ process(_Handlers, path = [_, <<"authorization_token">>]}) -> ResponseType = proplists:get_value(<<"response_type">>, Q, <<"">>), ClientId = proplists:get_value(<<"client_id">>, Q, <<"">>), + JidEls = case proplists:get_value(<<"jid">>, Q, <<"">>) of + <<"">> -> [?INPUTID(<<"email">>, <<"username">>, <<"">>)]; + Jid -> [?C(Jid), ?INPUT(<<"hidden">>, <<"username">>, Jid)] + end, RedirectURI = proplists:get_value(<<"redirect_uri">>, Q, <<"">>), Scope = proplists:get_value(<<"scope">>, Q, <<"">>), State = proplists:get_value(<<"state">>, Q, <<"">>), @@ -423,10 +531,10 @@ process(_Handlers, ?XAE(<<"form">>, [{<<"action">>, <<"authorization_token">>}, {<<"method">>, <<"post">>}], - [?LABEL(<<"username">>, [?CT(<<"User (jid)">>), ?C(<<": ">>)]), - ?INPUTID(<<"text">>, <<"username">>, <<"">>), + [?LABEL(<<"username">>, [?CT(?T("User (jid)")), ?C(<<": ">>)]) + ] ++ JidEls ++ [ ?BR, - ?LABEL(<<"password">>, [?CT(<<"Password">>), ?C(<<": ">>)]), + ?LABEL(<<"password">>, [?CT(?T("Password")), ?C(<<": ">>)]), ?INPUTID(<<"password">>, <<"password">>, <<"">>), ?INPUT(<<"hidden">>, <<"response_type">>, ResponseType), ?INPUT(<<"hidden">>, <<"client_id">>, ClientId), @@ -434,7 +542,7 @@ process(_Handlers, ?INPUT(<<"hidden">>, <<"scope">>, Scope), ?INPUT(<<"hidden">>, <<"state">>, State), ?BR, - ?LABEL(<<"ttl">>, [?CT(<<"Token TTL">>), ?CT(<<": ">>)]), + ?LABEL(<<"ttl">>, [?CT(?T("Token TTL")), ?C(<<": ">>)]), ?XAE(<<"select">>, [{<<"name">>, <<"ttl">>}], [ ?XAC(<<"option">>, [{<<"value">>, <<"3600">>}],<<"1 Hour">>), @@ -443,7 +551,7 @@ process(_Handlers, ?XAC(<<"option">>, [{<<"selected">>, <<"selected">>},{<<"value">>, <<"31536000">>}],<<"1 Year">>), ?XAC(<<"option">>, [{<<"value">>, <<"315360000">>}],<<"10 Years">>)]), ?BR, - ?INPUTT(<<"submit">>, <<"">>, <<"Accept">>) + ?INPUTT(<<"submit">>, <<"">>, ?T("Accept")) ]), Top = ?DIV(<<"section">>, @@ -487,106 +595,165 @@ process(_Handlers, RedirectURI = proplists:get_value(<<"redirect_uri">>, Q, <<"">>), SScope = proplists:get_value(<<"scope">>, Q, <<"">>), StringJID = proplists:get_value(<<"username">>, Q, <<"">>), - #jid{user = Username, server = Server} = jid:from_string(StringJID), - Password = proplists:get_value(<<"password">>, Q, <<"">>), - State = proplists:get_value(<<"state">>, Q, <<"">>), - Scope = str:tokens(SScope, <<" ">>), - TTL = proplists:get_value(<<"ttl">>, Q, <<"">>), - ExpiresIn = case TTL of - <<>> -> undefined; - _ -> binary_to_integer(TTL) - end, - case oauth2:authorize_password({Username, Server}, - ClientId, - RedirectURI, - Scope, - {password, Password}) of - {ok, {_AppContext, Authorization}} -> - {ok, {_AppContext2, Response}} = - oauth2:issue_token(Authorization, [{expiry_time, ExpiresIn} || ExpiresIn /= undefined ]), - {ok, AccessToken} = oauth2_response:access_token(Response), - {ok, Type} = oauth2_response:token_type(Response), - %%Ugly: workardound to return the correct expirity time, given than oauth2 lib doesn't really have - %%per-case expirity time. - Expires = case ExpiresIn of - undefined -> - {ok, Ex} = oauth2_response:expires_in(Response), - Ex; - _ -> - ExpiresIn - end, - {ok, VerifiedScope} = oauth2_response:scope(Response), - %oauth2_wrq:redirected_access_token_response(ReqData, - % RedirectURI, - % AccessToken, - % Type, - % Expires, - % VerifiedScope, - % State, - % Context); - {302, [{<<"Location">>, - <>))/binary, - "&state=", State/binary>> - }], - ejabberd_web:make_xhtml([?XC(<<"h1">>, <<"302 Found">>)])}; - {error, Error} when is_atom(Error) -> - %oauth2_wrq:redirected_error_response( - % ReqData, RedirectURI, Error, State, Context) - {302, [{<<"Location">>, - <>, <<"302 Found">>)])} + try jid:decode(StringJID) of + #jid{user = Username, server = Server} -> + Password = proplists:get_value(<<"password">>, Q, <<"">>), + State = proplists:get_value(<<"state">>, Q, <<"">>), + Scope = str:tokens(SScope, <<" ">>), + TTL = proplists:get_value(<<"ttl">>, Q, <<"">>), + ExpiresIn = case TTL of + <<>> -> undefined; + _ -> binary_to_integer(TTL) + end, + case oauth2:authorize_password({Username, Server}, + ClientId, + RedirectURI, + Scope, + #oauth_ctx{password = Password}) of + {ok, {_AppContext, Authorization}} -> + {ok, {_AppContext2, Response}} = + oauth2:issue_token(Authorization, [{expiry_time, ExpiresIn} || ExpiresIn /= undefined]), + {ok, AccessToken} = oauth2_response:access_token(Response), + {ok, Type} = oauth2_response:token_type(Response), + %%Ugly: workardound to return the correct expirity time, given than oauth2 lib doesn't really have + %%per-case expirity time. + Expires = case ExpiresIn of + undefined -> + {ok, Ex} = oauth2_response:expires_in(Response), + Ex; + _ -> + ExpiresIn + end, + {ok, VerifiedScope} = oauth2_response:scope(Response), + %oauth2_wrq:redirected_access_token_response(ReqData, + % RedirectURI, + % AccessToken, + % Type, + % Expires, + % VerifiedScope, + % State, + % Context); + {302, [{<<"Location">>, + <>))/binary, + "&state=", State/binary>> + }], + ejabberd_web:make_xhtml([?XC(<<"h1">>, <<"302 Found">>)])}; + {error, Error} when is_atom(Error) -> + %oauth2_wrq:redirected_error_response( + % ReqData, RedirectURI, Error, State, Context) + {302, [{<<"Location">>, + <>, <<"302 Found">>)])} + end + catch _:{bad_jid, _} -> + State = proplists:get_value(<<"state">>, Q, <<"">>), + {400, [{<<"Location">>, + <>, <<"400 Invalid request">>)])} end; process(_Handlers, #request{method = 'POST', q = Q, lang = _Lang, + auth = HTTPAuth, path = [_, <<"token">>]}) -> - case proplists:get_value(<<"grant_type">>, Q, <<"">>) of - <<"password">> -> - SScope = proplists:get_value(<<"scope">>, Q, <<"">>), - StringJID = proplists:get_value(<<"username">>, Q, <<"">>), - #jid{user = Username, server = Server} = jid:from_string(StringJID), - Password = proplists:get_value(<<"password">>, Q, <<"">>), - Scope = str:tokens(SScope, <<" ">>), - TTL = proplists:get_value(<<"ttl">>, Q, <<"">>), - ExpiresIn = case TTL of - <<>> -> undefined; - _ -> binary_to_integer(TTL) + Access = + case ejabberd_option:oauth_client_id_check() of + allow -> + case proplists:get_value(<<"grant_type">>, Q, <<"">>) of + <<"password">> -> + password; + _ -> + unsupported_grant_type + end; + deny -> + deny; + db -> + {ClientID, Secret} = + case HTTPAuth of + {ClientID1, Secret1} -> + {ClientID1, Secret1}; + _ -> + ClientID1 = proplists:get_value( + <<"client_id">>, Q, <<"">>), + Secret1 = proplists:get_value( + <<"client_secret">>, Q, <<"">>), + {ClientID1, Secret1} end, - case oauth2:authorize_password({Username, Server}, - Scope, - {password, Password}) of - {ok, {_AppContext, Authorization}} -> - {ok, {_AppContext2, Response}} = - oauth2:issue_token(Authorization, [{expiry_time, ExpiresIn} || ExpiresIn /= undefined ]), - {ok, AccessToken} = oauth2_response:access_token(Response), - {ok, Type} = oauth2_response:token_type(Response), - %%Ugly: workardound to return the correct expirity time, given than oauth2 lib doesn't really have - %%per-case expirity time. - Expires = case ExpiresIn of - undefined -> - {ok, Ex} = oauth2_response:expires_in(Response), - Ex; - _ -> - ExpiresIn - end, - {ok, VerifiedScope} = oauth2_response:scope(Response), - json_response(200, {[ - {<<"access_token">>, AccessToken}, - {<<"token_type">>, Type}, - {<<"scope">>, str:join(VerifiedScope, <<" ">>)}, - {<<"expires_in">>, Expires}]}); - {error, Error} when is_atom(Error) -> - json_error(400, <<"invalid_grant">>, Error) - end; - _OtherGrantType -> - json_error(400, <<"unsupported_grant_type">>, unsupported_grant_type) - end; + DBMod = get_db_backend(), + case DBMod:lookup_client(ClientID) of + {ok, #oauth_client{grant_type = password} = Client} -> + case get_client_secret(Client) of + Secret -> + case proplists:get_value(<<"grant_type">>, Q, <<"">>) of + <<"password">> when + Client#oauth_client.grant_type == password -> + password; + _ -> + unsupported_grant_type + end; + _ -> + deny + end; + _ -> + deny + end + end, + case Access of + password -> + SScope = proplists:get_value(<<"scope">>, Q, <<"">>), + StringJID = proplists:get_value(<<"username">>, Q, <<"">>), + try jid:decode(StringJID) of + #jid{user = Username, server = Server} -> + Password = proplists:get_value(<<"password">>, Q, <<"">>), + Scope = str:tokens(SScope, <<" ">>), + TTL = proplists:get_value(<<"ttl">>, Q, <<"">>), + ExpiresIn = case TTL of + <<>> -> undefined; + _ -> binary_to_integer(TTL) + end, + case oauth2:authorize_password({Username, Server}, + Scope, + #oauth_ctx{password = Password}) of + {ok, {_AppContext, Authorization}} -> + {ok, {_AppContext2, Response}} = + oauth2:issue_token(Authorization, [{expiry_time, ExpiresIn} || ExpiresIn /= undefined]), + {ok, AccessToken} = oauth2_response:access_token(Response), + {ok, Type} = oauth2_response:token_type(Response), + %%Ugly: workardound to return the correct expirity time, given than oauth2 lib doesn't really have + %%per-case expirity time. + Expires = case ExpiresIn of + undefined -> + {ok, Ex} = oauth2_response:expires_in(Response), + Ex; + _ -> + ExpiresIn + end, + {ok, VerifiedScope} = oauth2_response:scope(Response), + json_response(200, #{<<"access_token">> => AccessToken, + <<"token_type">> => Type, + <<"scope">> => str:join(VerifiedScope, <<" ">>), + <<"expires_in">> => Expires}); + {error, Error} when is_atom(Error) -> + json_error(400, <<"invalid_grant">>, Error) + end + catch _:{bad_jid, _} -> + json_error(400, <<"invalid_request">>, invalid_jid) + end; + unsupported_grant_type -> + json_error(400, <<"unsupported_grant_type">>, + unsupported_grant_type); + deny -> + ejabberd_web:error(not_allowed) + end; process(_Handlers, _Request) -> ejabberd_web:error(not_found). @@ -594,31 +761,35 @@ process(_Handlers, _Request) -> -spec get_db_backend() -> module(). get_db_backend() -> - DBType = ejabberd_config:get_option( - oauth_db_type, - fun(T) -> ejabberd_config:v_db(?MODULE, T) end, - mnesia), - list_to_atom("ejabberd_oauth_" ++ atom_to_list(DBType)). + DBType = ejabberd_option:oauth_db_type(), + list_to_existing_atom("ejabberd_oauth_" ++ atom_to_list(DBType)). +get_client_secret(#oauth_client{grant_type = password, options = Options}) -> + proplists:get_value(secret, Options, false). + +get_redirect_uri(#oauth_client{grant_type = implicit, options = Options}) -> + proplists:get_value(redirect_uri, Options, false). %% Headers as per RFC 6749 json_response(Code, Body) -> {Code, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}, {<<"Cache-Control">>, <<"no-store">>}, {<<"Pragma">>, <<"no-cache">>}], - jiffy:encode(Body)}. + misc:json_encode(Body)}. %% OAauth error are defined in: %% https://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-5.2 json_error(Code, Error, Reason) -> Desc = json_error_desc(Reason), - Body = {[{<<"error">>, Error}, - {<<"error_description">>, Desc}]}, + Body = #{<<"error">> => Error, + <<"error_description">> => Desc}, json_response(Code, Body). json_error_desc(access_denied) -> <<"Access denied">>; +json_error_desc(badpass) -> <<"Bad password">>; json_error_desc(unsupported_grant_type) -> <<"Unsupported grant type">>; -json_error_desc(invalid_scope) -> <<"Invalid scope">>. +json_error_desc(invalid_scope) -> <<"Invalid scope">>; +json_error_desc(invalid_jid) -> <<"Invalid JID">>. web_head() -> [?XA(<<"meta">>, [{<<"http-equiv">>, <<"X-UA-Compatible">>}, @@ -631,129 +802,43 @@ web_head() -> ]. css() -> - <<" - body { - margin: 0; - padding: 0; - - font-family: sans-serif; - color: #fff; - } - - h1 { - font-size: 3em; - color: #444; - } - - p { - line-height: 1.5em; - color: #888; - } - - a { - color: #fff; - } - a:hover, - a:active { - text-decoration: underline; - } - - em { - display: inline-block; - padding: 0 5px; - - background: #f4f4f4; - border-radius: 5px; - - font-style: normal; - font-weight: bold; - color: #444; - } - - form { - color: #444; - } - label { - display: block; - font-weight: bold; - } - - input[type=text], - input[type=password] { - margin-bottom: 1em; - padding: 0.4em; - - max-width: 330px; - width: 100%; - - border: 1px solid #c4c4c4; - border-radius: 5px; - outline: 0; - - font-size: 1.2em; - } - input[type=text]:focus, - input[type=password]:focus, - input[type=text]:active, - input[type=password]:active { - border-color: #41AFCA; - } - - input[type=submit] { - font-size: 1em; - } - - .container { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - - background: #424A55; - background-image: -webkit-linear-gradient(270deg, rgba(48,52,62,0) 24%, #30353e 100%); - background-image: linear-gradient(-180deg, rgba(48,52,62,0) 24%, #30353e 100%); - } - - .section { - padding: 3em; - } - .white.section { - background: #fff; - border-bottom: 4px solid #41AFCA; - } - - .white.section a { - text-decoration: none; - color: #41AFCA; - } - .white.section a:hover, - .white.section a:active { - text-decoration: underline; - } - - .container > .section { - background: #424A55; - } - - .block { - margin: 0 auto; - max-width: 900px; - width: 100%; - } -">>. + case misc:read_css("oauth.css") of + {ok, Data} -> Data; + {error, _} -> <<>> + end. logo() -> - <<"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAABACAYAAACgPErgAAAVtklEQVR42uydeXhU1d2ADzuyCcii4FZBZdEqVYtaUGqlikVwaRWsrWBRKIh8KgXcEAX9VECtoljcURAJICJWQLaKLAEjIHtBSNhC9oQkk2SW+36/52P+iIE5d5kzk4Fn3ud5H/7gIbnD3PPOveeeuVedygD9xLlQMh9yJ0D+GeokJAg1xcEWzOWYj0DBaQm0fXdaMCu8bROgpJVKksQj0+F84OXw/jRd7KVOdbbCFI6jdDNktlAnESVQNwSzOI6yhZBaV1UzAXiB4wj8CBtbqiRJXDIILtgL+zmOb8aoU5Xh0MMiEr5x6iSiAIZYROLo/aoayYMeocjbNlklSeKC9VAjBDM5IcEQfNZFnYq8AM8SkcJV6iSiCGYRkT1LVDWSA08Tkfy9MLaeOsUA2ot9xJHiW2KK+LW4Mvxnijg1/Pd9xQvFGiqJLUOhcT5kE5Etg0/VYI3TBOvbkyxYKURk2dpqDZb2gyEjC1SjUyBQdcQe4mtimujDHWXiD+Lr4g1iXZXkhISgBZBLZIaoU5GR0I2IVDxxkgVrNhH5z+pqDZb2g+FAJjQ4aYMFtBAfEzdils3iP8TkhYkqlMEZFuQQmcHqVGUBTOI40lLhz02TwUoGKxJAI/FxMYPYckB8Smyikvw/vlM/WHqAP4ifiHPF0fBsYyUkg5UM1okAbhV/JL5sE29TSZLBOhHJYCWDVRXgNPFVqpfXxQbJYCWDlQxWMlgRAc4VV5EYrBZ/kQxWMljJYFVLsBomdLCAS8RdJBZ7xMuSwTIcLKCO2EG8VrxdvEfsLV4F1vkJstq9BlidwN8hVleSLLgUuFzsEv6zs9g4EYIFNBAvFi87tm3WZWI7oHZ8glWnkWbb6ortCG/bsT+tDsBpcYrVpeIhzBHAHJni5XGOd5PwvnKJif0DaCh2/Pn7y/linbgFKx/qrjkWpffEHZo3qVhMBWuC2EVVA4ehay6sAYJgBeDocuh9iYqCL+F04DbxLTFNPMzxWGI6WCuBJ8Rfxi5YK34WLKC5BX3FVzj22n8S/fwcX/i9+1wcLraLVbAqr8OqgNOA31nwPLBc3HWCNU1+cbe4WBwjXhbD08A9eOcn8SNxmHij+EuxQ3iwdxP/Jr4pbsU7+2J5ehiCNuI9FkwN78sZol+0xDSYd6OH75R2BcZasALYKwZO0IWd4fd3LIS6g1XHeLBWQV1gsLgJ9wTBWgSBm1ScuBYuAvI4jrx9cOEZyiXzoUUIxhbDPm+fvFlL4PPe5oO14FslvAjnrYeXgYO4p0T8UrzJbLB2HlbCZMqbfwOjgrDD41HLCrE/LK1pKFYNxFS8sVDsK1Y5ctQeRf42HLcy3JMmNjIc6+ss+ETMRUuRH9Zd7SBS9QMwCFiDNzZBYAT4G5dTUcuC7KiClQ3dj8B6osYSAzOgoq2KMatgChF5e5RyQQXcVwL7MMNXsKOTuWAd2FBG9uhyyMMI6XPBd7GZYOUd8pP+UCn8hBEOr4KCbgYG7Ju4Z43YU0VB+LRoLu6ZpgyQAhflw2e4Y7lYS0XADzdZ8ANGsLaX88PgACHvwfLBY4Afo4T2Q9lNMZ68W0lE/J8oB5RDkxDMwDhF+XC4v6tgxZdcKLov+mBhYR4/FD8VRTRu93CE97RYTxkCuFfMxR13qSjIhoE+Tx9qPh9Mb6WqcBRqBuBli5hguQ4WUGsPvGsRKyw/hAaoGJEPy4lI4Uxlw3XHzu/XEVNKHk3QYAmWWDEecmraBqtaCE6HrCYeJpX34Jwc8eYYXp3cjHPSxWbKA9/Da3im+CeY1kBV4gNoUQILiB/6YD0JNYFP4jMoGFINwZqhNHwKLbfB5ji9/kdMBss8vk9MBcs8pasht7GLSIzHOQfFX6kYApyFuw/FFz38jveJitxBqhI3QMsS2IiQMMFaDFOJHyF45fZECdYOqA0sIX5YMPkOk8Eyz96xpoJlnvIZDgduazEfZ+SJVyoNhqO1DWcUim1dfJf2DTxTVgxFY1Ql+kOtivDYSJhgTYX7cIx1EJgmDhSvEbuK12XAiHyYCxThCKsQHmiXCMHa5+5T+BBY74qVX39vcUIhbLBwSkUO7DonBsEqBCtFfBjoHt6+34mjLfg34HO4feL8mw0HqwKsr8XRwA2E9x3xYY693jzHvWfZCAdheAZnWG6+1wecKd4oDhFHicPFvm6WI4SXRBTijOcdzi3+BefkifPAehLoI14Pqcdt/1x3YyNb/BisYUC38PvbU3wivOQhGHWw+sK5ASjAltBB8D8MQe05NXCBBZOBCmxJXwbTa1RnsDbCL4Pgx5ajeeJjYDVXEZgDNSugVwhW44j1sw0GKwjrX4El2kGzHDqVwzvO1x3d2dhMsPbMgjnaRZGroW0OjHcY1VIYeZHNMoZ0nPFP5YDwIJwp5miWinwt3u7w5z3g4lS1sc2c1Tl+Z0eTRXBkLKy1PWp7Ga4EAthSXAhHn4BQa5vX2zUIX1rRBKvU0fluYDaUt3F5xa5rNmzFlsfurs5grYP52JK7AVZcpBxSCrU3wvPOPtgzuxsI1gG3l9/90Cfg6Ihm/ONRBssn3udyceNVftiBLXNnaAZHbxcLNZs6uPXMFJdHCF84OeICluIIfQTzHI3jrI0Q7Kwc4myaxEqDtR2VC9bCCMDvOliZ0NGCCv1pwb7XlEdmQCtgOVp2bYU69aojWB/AVUEIoWcpfNlMeQD4q/0bs/GL6IK1JR3Wd/T4/lxZDJloKTgMrzXzFqyDJfBNT+WB2dD2kO1Eb9AP/zrhAAQ+xhl/d7A6fm0U977qbvPzr3YYwlkqArnQ2f4swb8SZjZXDtkG3SxsWSZ6HRt9RJ+rYP0H/omWdVNUlGyDxpm2b/i4P1VHsPbYnhrl7IQ/NlNRAPzDfqnH0o4eg1UEEy+N8mkl3e1P378e4CFYFnzRR0VBVzgP26BumqyqANQX92JPhm5FOXC2uJXoKBR7KA3AQuzZLzaMcJbwqv2pfXFL5YKV8BFaCvfCH1uqKDgKA0NOgzUAGuRp7664eaUyxD3Qpkz7u0oWxDtY/aBhFhzUD7hBN6goeRVq7IRVNkEY4y1YRx5VBiiHV9ByaK77YOW+pwxQCn9DS/4W2Fy7SgCuEEPYM1ETkXriSsxwRLxQe7RhjyV2VVW4E+ofhj1oSe2rXDAaGhTBAbQM72vo6VDzHQXrX9r7nxeXwqrLYWcNoFa0hj8F7rQiTuAV5cHIFvEM1jvQ3UJH+QJliN9Dj5B2APkWuw/Wrm3QrJ6hZyC20q/C9qfDsgbOg1WYDxe2NbRtdfTfZbXKIbW9x8ns30QISA1xEmbZLDbRzJEdwp6hqgqvwVWWdt9avVy5ZDL8Wj9VstbYwUwpdALKbIP1HxhJRAJlwPfhHWWzATeJafpz9bd/H89grYBRaHn+RmWQn2CdZtsyYWhjd8HaPVQZZKl2HV7AgvEXOw9W1pvKIDNhGFrevbVKACY6vPLWSHN0NUp8ThxnyJfECzRHWSlermaugfvRcuQu5ZLv4F60pN+lDLIZUmyDtRRmklAUPBzPYK2GT3Vf4IX+jZRBPoYniUioHA62dx6sQAVkt1MGSYXeFjoC3ZwFKyRm/14Z5Bu4OKidZwsOqjL4P8eeZSqBAMZgz0JVhQ3wOhEpLoLxZyqXHIAJRKS0CF48UxnkWehnG6wj8BUJxfK3VZQUuAiW/ntRwa+UYR6BP+gH+f6eLoK1CQprKoMchIv82itNGfc5DFY+FLUyvG0NS2E/Ecl8qcrg/w573kuwYP0Re1JVFd7RfvAWbYIU1/vJ2/AhETm6HlJqKIOshA4hKNMGK5RwwZrygYFHqC9zGqygNlgrP4vBjQW7hcCKHKy9tzh/kGrqd8ow+6CVX3tPovShKkw2PKuJx2G4tKEyyF6o4YPvicjhN1UlHD5TcGKCBetG7Nkq/ixA+doPNv9i5YFp2iuE1mJlmBxoEbR7kKqVcMGaOU1FyTpYq7tbg/NgbZ+tDLMLrrcJVi9ViTLtKfsq48E6CK392p0m/e8qTDE8RUQOHobmRoN1AGqWQZomWFMq33FE/C/2vJBgwboee/aKdVzMfXk6U3hLGyyMBysXWga1i5jTBqmchAvWY31VlGRqbyrmn64qka8NVsj4/MZg6IPgNFj7tV9iLfsRVtdSBsmATgEIaoJ/mwqzEwYTkWABbDhLGSQdGpdp12OVPF7lmQO7HU1gJxDAI9izs+oRVlAfrH8rDyzQXoAp3gxv1zJ8UaVTSDtHec9f1bqEmXQPiuUfwjdR/Se8AG0sOEpEUidVmax8n4iU58OKFoaPsJ5xE6x/wwT9pPuTRifdv4K79Nv3QZdK8yb90PLazcogH8HlIQgQkfl3ewjWpwn2yLH92JOmhFgHqwSGE5EyH7x/vjJICtyjX362s6f6Cf6hX9tSOB8Q+SJGfimmQPogZYCXoD9avh1WZYAORcvwfsogpbDeTbBmwW1oKR6mDJIGs4iIvxAeOLPSpGxn/dGYb6rhJRdPod2jn7nGQ7DWaQJSQzxLbC22iqFtxDvE7ThjRTyCNQ1uRkvqYGWQQzBH/yV3+R7zFOhhab8u8smt6iThN1AjB1brd+rnuqhKvAFX6hdz/rgBLqqpDJAHPS2w3ARrIpzth2IiEtgBM+spA2RBB/33unZ9qyrxDNTLg52a11MAS842dDrYRH/XhfxDMLyhh2AViK0jBKum+K5YKObE0CLc8WE8gvUStC7XbtuWrXBeHWWA7tDJp933dq+F8TXVHmgS1M4LFByE/q3VScB826OrwG7IqqsqcQvU3Q+70OJ7SEVJO6iVARsQHAcrTBosR8vrI5UBNsBctBQ+raowD6ag5ev3lQFmw3i0FM9SgiZYOiJ+KHMs4iUkFk+aC5aeLbAULYERygBB+BIt+8ZV3lHfQ8uRRaDqqASmF7Sxf8xV6SR1AjbBi+gphPIuUcb0aQQvwfoWBqDFKoEXuqooWAID0RLyw56OJ7j319WAhZZp/aOMVTegDC0ZfaII1sdKA9CPxOLWeAVrMgy0Hxu9ukS5NvEh+33v6Q6VT4uusCCElpnz4Ie6KgFZAWctgTS0lPjg2osiLDRtFwAfWoKHIfRrj/NCI0oAr8EqgAZ+2IOew7DkWuWBAPQvBT9ayuZo5uUWo8cH6z1FKwTXFkEOWio2Q1ndKIJVILa1XYGeGBSJbeIVrObQKBPS0VJ6ABZ7itY2eLAIO1JmqKqUwzzsWQyB9soFwGliQxUj/HCNs/tiH5hqM2hfxxarCPwPughBI80dEBwHK3wUOMDCjtKjbp5GlAn1vodxgIWeoO6hDGvhmqDtzygXA0/BN3VcPF/yAYfzO3crQRcsE+uxgIfEUqqXVUowGyw9I2EY9uRCxV0uls803AwTsacC3uisqjIEOgMV2GLlAeMgdK7NbW47Aq+Ke8UD4jsw4yyDj9ruIE6yoAxbyo/AjjOVhnehVQFk4ojQIgjeDNSNsGO3FP9mwVYEE8FqAbUOwwoccWAh+HuCVS/C9jUNwZ+BNByx9A0Hd4+ciiOOpELx3WBFumNB/RD0smARjti5FPrVNBCsQvECB9G6SvwaZ/xX/F/xf8S5YojoGRXvYM2C+j7nT8uZC9ZvI42NhdA8AAOALTgi5TkVic/gSRxjFQDzxKFiH7G7eJv4WAhWBqCc49i/Gya2US7hWPzuFf8iTrBgGVCOY4r/5HCupF8AV2wDazrwiDhEfEFcKGYRxkiwwlRAeyAb5+wQPxFHioPEseLn4gEcU7gdLm3q4PubTVw+ay9DTBGfCm/bKPFTcSfOyYe+FyrBQLAQPnfzyHdxSvg1F4hlYrGYHn5d94hNTnC/qxK8Uya2Mx8se0rhCsCHc7aCVXnfG2fB/Ao4hGPSV8O59VQkfgs1N8IiYkrBK8oFW+E5wIdndkx2eVXkeYxgPljh7bs1CCHigr8Ull7u4r26pBgKiBtb7lCCqWCFeVi5AKgtthE7iL8QGykNwGi8s0AJcQ9WmOnwIHEjlAFp5yg7noNmQCoxo2yNizmWoUG8Yon73oOzayiXAG8mZLDC/BkGlIBFTMkvg1v6eFhPc31mzKNVIf59hBJiECy/2FvFCOCcKObBesQjWNV/8aFiPxR0cbNRrcVVxITgauWAR6H+IdiNZxZPinJdyKSEDFaY5dAXyCY2HIJPeyqPvAlXANuJDT74/n4leA6WPUfFXjEKVjMxD/css/m5c7TBMsgmGBGI2VG+9T0UtvdyI7cme+EzjPPhROWACmiJp0/q8lzY/RdlgFUwDCjGGFZYXbDSb3Fx4aGzH77DKBmrIPdCFSUcWyU9B6Pk7ICMbkowFiz9fNFAZRjgGjGIOwLi1UrDBv0R1hJlmCLoFYIMs2Mj8D74m6poAIZbkGtmg/gIbmumHLID5uOK0GzIb294B7skCAssoiYLsh6FdWIk/D7Ycp7LZ7vV2Qejifo9CmYBo+Hb2oY/jQcC+4iKUAnwCmxvrgTjwdLzttjS0L7U3uOjwqYoG3ZqF19WvKVigA/aBGEaUEFUWFsh+CeTa50uCMAUC3LwRioEb1MueRw62a/DsXLE9yH4GxVD/HCjBfPEYlxxdDsUjgPrbCXMprTF/oiPnip9QnnEgnND8KwFu3DHdigfC4vaqhgBNA3CwxZsxB37xFdhUSelQRssM+wVh4iNPb7+RhwLyhHcs8PJM/+OHLv1ziaOw5cNmReoGAL8KgRvBWC/5e4IZh2EBomnqVgQgrYWDBUXApkRlhaUillgLRVfFm8AaiqPAN3ENWJ+2ExxJVjTIHQXBM9UcSQI51twLzBNXMSxQOSIeeEdchNYC469dq6HH+qd4DYpvwA+FPeIeeF/86Ch9+g0C34HTBC/FLeK2eHfkyWmiZ+LT4rXhddqxYUQ1LLgGmCMOF/8QcwKb1tOeFsXhNcu3QyWLhDmg2XPLvFZ8UpRO8iAVuL14nPiTrxRLnZVDjkC54TveJAfdjEcuUzFia/h9FLoDbwifhV+3XmVxsZ6cQ5Yj0LoChVPkI0TO4q9wLoDuFW8TrxAbGr4d9WwoAVwhni6SiCA+mIzsXn4/6S2i3/bIPzvasdw++pW2r6mYi2VIAC1wtvUPLyNdZUGz8EyTyj8s78KnzK+LL4kviPOE9eJeUTP/R5f+xlgnZEIY8OC5pXGRg2VJEkSTbBOXsaoJEmSJIN1EvC0SvJ/7dSxigEAHIDxf5FisAilbNbbWGSi5OZ7BgZPcS/hAZTNwFtYSVltit2iU/c9gxv8u75ffa/wSQ4ruR+ahySHldyZJiHJYSW3pnZIcliJHekrJDmsxMM60JQqIUkJh3WhJX1SKSTpzcN60J1udKQNfdOIaiFJLwxrTwua0Zj61PtjXfqgDtWpEJL04rCetKKBM5GUeVgnGoYkJR1Wka60o0ZIUuJhVWlLzZCk5MMqUyv0r/wCSDD/4sxS1q8AAAAASUVORK5CYII=">>. + case misc:read_img("oauth-logo.png") of + {ok, Img} -> + B64Img = base64:encode(Img), + <<"data:image/png;base64,", B64Img/binary>>; + {error, _} -> + <<>> + end. -opt_type(oauth_expire) -> - fun(I) when is_integer(I), I >= 0 -> I end; -opt_type(oauth_access) -> - fun acl:access_rules_validator/1; -opt_type(oauth_db_type) -> - fun(T) -> ejabberd_config:v_db(?MODULE, T) end; -opt_type(oauth_cache_life_time) -> - fun (I) when is_integer(I), I > 0 -> I end; -opt_type(oauth_cache_size) -> - fun (I) when is_integer(I), I > 0 -> I end; -opt_type(_) -> [oauth_expire, oauth_access, oauth_db_type]. +%%% +%%% WebAdmin +%%% + +%% @format-begin + +web_menu_main(Acc, _Lang) -> + Acc ++ [{<<"oauth">>, <<"OAuth">>}]. + +web_page_main(_, #request{path = [<<"oauth">>]} = R) -> + Head = ?H1GLraw(<<"OAuth">>, <<"developer/ejabberd-api/oauth/">>, <<"OAuth">>), + Set = [?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"token">>}], <<"Token">>), + ?XE(<<"blockquote">>, + [ejabberd_web_admin:make_command(oauth_list_tokens, R), + ejabberd_web_admin:make_command(oauth_issue_token, R), + ejabberd_web_admin:make_command(oauth_revoke_token, R)]), + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"client">>}], <<"Client">>), + ?XE(<<"blockquote">>, + [ejabberd_web_admin:make_command(oauth_add_client_implicit, R), + ejabberd_web_admin:make_command(oauth_add_client_password, R), + ejabberd_web_admin:make_command(oauth_remove_client, R)])], + {stop, Head ++ Set}; +web_page_main(Acc, _) -> + Acc. diff --git a/src/ejabberd_oauth_mnesia.erl b/src/ejabberd_oauth_mnesia.erl index a23f443ed..37fa3285c 100644 --- a/src/ejabberd_oauth_mnesia.erl +++ b/src/ejabberd_oauth_mnesia.erl @@ -5,7 +5,7 @@ %%% Created : 20 Jul 2016 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -25,33 +25,54 @@ %%%------------------------------------------------------------------- -module(ejabberd_oauth_mnesia). +-behaviour(ejabberd_oauth). -export([init/0, - store/1, - lookup/1, - clean/1]). + store/1, + lookup/1, + clean/1, + lookup_client/1, + store_client/1, + remove_client/1, + use_cache/0, revoke/1]). -include("ejabberd_oauth.hrl"). init() -> - mnesia:create_table(oauth_token, - [{disc_copies, [node()]}, + ejabberd_mnesia:create(?MODULE, oauth_token, + [{disc_only_copies, [node()]}, {attributes, record_info(fields, oauth_token)}]), - mnesia:add_table_copy(oauth_token, node(), disc_copies), + ejabberd_mnesia:create(?MODULE, oauth_client, + [{disc_copies, [node()]}, + {attributes, + record_info(fields, oauth_client)}]), ok. +use_cache() -> + case mnesia:table_info(oauth_token, storage_type) of + disc_only_copies -> + ejabberd_option:oauth_use_cache(); + _ -> + false + end. + store(R) -> mnesia:dirty_write(R). lookup(Token) -> case catch mnesia:dirty_read(oauth_token, Token) of [R] -> - R; + {ok, R}; _ -> - false + error end. + +-spec revoke(binary()) -> ok | {error, binary()}. +revoke(Token) -> + mnesia:dirty_delete(oauth_token, Token). + clean(TS) -> F = fun() -> Ts = mnesia:select( @@ -63,3 +84,16 @@ clean(TS) -> end, mnesia:async_dirty(F). +lookup_client(ClientID) -> + case catch mnesia:dirty_read(oauth_client, ClientID) of + [R] -> + {ok, R}; + _ -> + error + end. + +remove_client(ClientID) -> + mnesia:dirty_delete(oauth_client, ClientID). + +store_client(R) -> + mnesia:dirty_write(R). diff --git a/src/ejabberd_oauth_rest.erl b/src/ejabberd_oauth_rest.erl index c932d16f5..b7200872a 100644 --- a/src/ejabberd_oauth_rest.erl +++ b/src/ejabberd_oauth_rest.erl @@ -5,7 +5,7 @@ %%% Created : 26 Jul 2016 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -25,74 +25,150 @@ %%%------------------------------------------------------------------- -module(ejabberd_oauth_rest). +-behaviour(ejabberd_oauth). -export([init/0, store/1, lookup/1, clean/1, - opt_type/1]). + lookup_client/1, + store_client/1, revoke/1]). --include("ejabberd.hrl"). -include("ejabberd_oauth.hrl"). -include("logger.hrl"). --include("jid.hrl"). +-include_lib("xmpp/include/jid.hrl"). init() -> - rest:start(?MYNAME), + rest:start(ejabberd_config:get_myname()), ok. store(R) -> Path = path(<<"store">>), %% Retry 2 times, with a backoff of 500millisec {User, Server} = R#oauth_token.us, - SJID = jid:to_string({User, Server, <<"">>}), + SJID = jid:encode({User, Server, <<"">>}), case rest:with_retry( post, - [?MYNAME, Path, [], - {[{<<"token">>, R#oauth_token.token}, - {<<"user">>, SJID}, - {<<"scope">>, R#oauth_token.scope}, - {<<"expire">>, R#oauth_token.expire} - ]}], 2, 500) of + [ejabberd_config:get_myname(), Path, [], + #{<<"token">> => R#oauth_token.token, + <<"user">> => SJID, + <<"scope">> => R#oauth_token.scope, + <<"expire">> => R#oauth_token.expire + }], 2, 500) of {ok, Code, _} when Code == 200 orelse Code == 201 -> ok; Err -> - ?ERROR_MSG("failed to store oauth record ~p: ~p", [R, Err]), - {error, Err} + ?ERROR_MSG("Failed to store oauth record ~p: ~p", [R, Err]), + {error, db_failure} end. lookup(Token) -> Path = path(<<"lookup">>), - case rest:with_retry(post, [?MYNAME, Path, [], - {[{<<"token">>, Token}]}], + case rest:with_retry(post, [ejabberd_config:get_myname(), Path, [], + #{<<"token">> => Token}], 2, 500) of - {ok, 200, {Data}} -> - SJID = proplists:get_value(<<"user">>, Data, <<>>), - JID = jid:from_string(SJID), + {ok, 200, Data} -> + SJID = case maps:find(<<"user">>, Data) of + {ok, U} -> U; + error -> <<>> + end, + JID = jid:decode(SJID), US = {JID#jid.luser, JID#jid.lserver}, - Scope = proplists:get_value(<<"scope">>, Data, []), - Expire = proplists:get_value(<<"expire">>, Data, 0), - #oauth_token{token = Token, - us = US, - scope = Scope, - expire = Expire}; + Scope = case maps:find(<<"scope">>, Data) of + {ok, S} -> S; + error -> [] + end, + Expire = case maps:find(<<"expire">>, Data) of + {ok, E} -> E; + error -> 0 + end, + {ok, #oauth_token{token = Token, + us = US, + scope = Scope, + expire = Expire}}; {ok, 404, _Resp} -> - false; + error; Other -> ?ERROR_MSG("Unexpected response for oauth lookup: ~p", [Other]), - {error, rest_failed} + case ejabberd_option:oauth_cache_rest_failure_life_time() of + infinity -> error; + Time -> {cache_with_timeout, error, Time} + end end. +-spec revoke(binary()) -> ok | {error, binary()}. +revoke(_Token) -> + {error, <<"not available">>}. + clean(_TS) -> ok. path(Path) -> - Base = ejabberd_config:get_option(ext_api_path_oauth, - fun(X) -> iolist_to_binary(X) end, - <<"/oauth">>), + Base = ejabberd_option:ext_api_path_oauth(), <>. +store_client(#oauth_client{client_id = ClientID, + client_name = ClientName, + grant_type = GrantType, + options = Options} = R) -> + Path = path(<<"store_client">>), + SGrantType = + case GrantType of + password -> <<"password">>; + implicit -> <<"implicit">> + end, + SOptions = misc:term_to_base64(Options), + %% Retry 2 times, with a backoff of 500millisec + case rest:with_retry( + post, + [ejabberd_config:get_myname(), Path, [], + #{<<"client_id">> => ClientID, + <<"client_name">> => ClientName, + <<"grant_type">> => SGrantType, + <<"options">> => SOptions + }], 2, 500) of + {ok, Code, _} when Code == 200 orelse Code == 201 -> + ok; + Err -> + ?ERROR_MSG("Failed to store oauth record ~p: ~p", [R, Err]), + {error, db_failure} + end. -opt_type(ext_api_path_oauth) -> - fun (X) -> iolist_to_binary(X) end; -opt_type(_) -> [ext_api_path_oauth]. +lookup_client(ClientID) -> + Path = path(<<"lookup_client">>), + case rest:with_retry(post, [ejabberd_config:get_myname(), Path, [], + #{<<"client_id">> => ClientID}], + 2, 500) of + {ok, 200, Data} -> + ClientName = case maps:find(<<"client_name">>, Data) of + {ok, CN} -> CN; + error -> <<>> + end, + SGrantType = case maps:find(<<"grant_type">>, Data) of + {ok, GT} -> GT; + error -> <<>> + end, + GrantType = + case SGrantType of + <<"password">> -> password; + <<"implicit">> -> implicit + end, + SOptions = case maps:find(<<"options">>, Data) of + {ok, O} -> O; + error -> <<>> + end, + case misc:base64_to_term(SOptions) of + {term, Options} -> + {ok, #oauth_client{client_id = ClientID, + client_name = ClientName, + grant_type = GrantType, + options = Options}}; + _ -> + error + end; + {ok, 404, _Resp} -> + error; + Other -> + ?ERROR_MSG("Unexpected response for oauth lookup: ~p", [Other]), + error + end. diff --git a/src/ejabberd_oauth_sql.erl b/src/ejabberd_oauth_sql.erl index 3c09362c2..fe0a159ad 100644 --- a/src/ejabberd_oauth_sql.erl +++ b/src/ejabberd_oauth_sql.erl @@ -5,7 +5,7 @@ %%% Created : 27 Jul 2016 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -25,54 +25,150 @@ %%%------------------------------------------------------------------- -module(ejabberd_oauth_sql). - --compile([{parse_transform, ejabberd_sql_pt}]). +-behaviour(ejabberd_oauth). -export([init/0, - store/1, - lookup/1, - clean/1]). + store/1, + lookup/1, + clean/1, + lookup_client/1, + store_client/1, + remove_client/1, revoke/1]). +-export([sql_schemas/0]). -include("ejabberd_oauth.hrl"). --include("ejabberd.hrl"). -include("ejabberd_sql_pt.hrl"). --include("jid.hrl"). +-include_lib("xmpp/include/jid.hrl"). +-include("logger.hrl"). init() -> + ejabberd_sql_schema:update_schema( + ejabberd_config:get_myname(), ?MODULE, sql_schemas()), ok. +sql_schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"oauth_token">>, + columns = + [#sql_column{name = <<"token">>, type = text}, + #sql_column{name = <<"jid">>, type = text}, + #sql_column{name = <<"scope">>, type = text}, + #sql_column{name = <<"expire">>, type = bigint}], + indices = [#sql_index{ + columns = [<<"token">>], + unique = true}]}, + #sql_table{ + name = <<"oauth_client">>, + columns = + [#sql_column{name = <<"client_id">>, type = text}, + #sql_column{name = <<"client_name">>, type = text}, + #sql_column{name = <<"grant_type">>, type = text}, + #sql_column{name = <<"options">>, type = text}], + indices = [#sql_index{ + columns = [<<"client_id">>], + unique = true}]}]}]. + store(R) -> Token = R#oauth_token.token, {User, Server} = R#oauth_token.us, - SJID = jid:to_string({User, Server, <<"">>}), + SJID = jid:encode({User, Server, <<"">>}), Scope = str:join(R#oauth_token.scope, <<" ">>), Expire = R#oauth_token.expire, - ?SQL_UPSERT( - ?MYNAME, - "oauth_token", - ["!token=%(Token)s", - "jid=%(SJID)s", - "scope=%(Scope)s", - "expire=%(Expire)d"]). + case ?SQL_UPSERT( + ejabberd_config:get_myname(), + "oauth_token", + ["!token=%(Token)s", + "jid=%(SJID)s", + "scope=%(Scope)s", + "expire=%(Expire)d"]) of + ok -> + ok; + _ -> + {error, db_failure} + end. lookup(Token) -> case ejabberd_sql:sql_query( - ?MYNAME, + ejabberd_config:get_myname(), ?SQL("select @(jid)s, @(scope)s, @(expire)d" " from oauth_token where token=%(Token)s")) of {selected, [{SJID, Scope, Expire}]} -> - JID = jid:from_string(SJID), + JID = jid:decode(SJID), US = {JID#jid.luser, JID#jid.lserver}, - #oauth_token{token = Token, - us = US, - scope = str:tokens(Scope, <<" ">>), - expire = Expire}; + {ok, #oauth_token{token = Token, + us = US, + scope = str:tokens(Scope, <<" ">>), + expire = Expire}}; _ -> - false + error + end. + +revoke(Token) -> + case ejabberd_sql:sql_query( + ejabberd_config:get_myname(), + ?SQL("delete from oauth_token where token=%(Token)s")) of + {error, _} -> + {error, <<"db error">>}; + _ -> + ok end. clean(TS) -> ejabberd_sql:sql_query( - ?MYNAME, + ejabberd_config:get_myname(), ?SQL("delete from oauth_token where expire < %(TS)d")). +lookup_client(ClientID) -> + case ejabberd_sql:sql_query( + ejabberd_config:get_myname(), + ?SQL("select @(client_name)s, @(grant_type)s, @(options)s" + " from oauth_client where client_id=%(ClientID)s")) of + {selected, [{ClientName, SGrantType, SOptions}]} -> + GrantType = + case SGrantType of + <<"password">> -> password; + <<"implicit">> -> implicit + end, + case misc:base64_to_term(SOptions) of + {term, Options} -> + {ok, #oauth_client{client_id = ClientID, + client_name = ClientName, + grant_type = GrantType, + options = Options}}; + _ -> + error + end; + _ -> + error + end. + +store_client(#oauth_client{client_id = ClientID, + client_name = ClientName, + grant_type = GrantType, + options = Options}) -> + SGrantType = + case GrantType of + password -> <<"password">>; + implicit -> <<"implicit">> + end, + SOptions = misc:term_to_base64(Options), + case ?SQL_UPSERT( + ejabberd_config:get_myname(), + "oauth_client", + ["!client_id=%(ClientID)s", + "client_name=%(ClientName)s", + "grant_type=%(SGrantType)s", + "options=%(SOptions)s"]) of + ok -> + ok; + _ -> + {error, db_failure} + end. + +remove_client(Client) -> + ejabberd_sql:sql_query( + ejabberd_config:get_myname(), + ?SQL("delete from oauth_client where client=%(Client)s")). diff --git a/src/ejabberd_old_config.erl b/src/ejabberd_old_config.erl new file mode 100644 index 000000000..670dd7158 --- /dev/null +++ b/src/ejabberd_old_config.erl @@ -0,0 +1,643 @@ +%%%---------------------------------------------------------------------- +%%% Purpose: Transform old-style Erlang config to YAML config +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- +-module(ejabberd_old_config). + +%% API +-export([read_file/1]). + +-include("logger.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +read_file(File) -> + case consult(File) of + {ok, Terms1} -> + ?INFO_MSG("Converting from old configuration format", []), + Terms2 = strings_to_binary(Terms1), + Terms3 = transform(Terms2), + Terms4 = transform_certfiles(Terms3), + Terms5 = transform_host_config(Terms4), + {ok, collect_options(Terms5)}; + {error, Reason} -> + {error, {old_config, File, Reason}} + end. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +collect_options(Opts) -> + {D, InvalidOpts} = + lists:foldl( + fun({K, V}, {D, Os}) when is_list(V) -> + {orddict:append_list(K, V, D), Os}; + ({K, V}, {D, Os}) -> + {orddict:store(K, V, D), Os}; + (Opt, {D, Os}) -> + {D, [Opt|Os]} + end, {orddict:new(), []}, Opts), + InvalidOpts ++ orddict:to_list(D). + +transform(Opts) -> + Opts1 = transform_register(Opts), + Opts2 = transform_s2s(Opts1), + Opts3 = transform_listeners(Opts2), + Opts5 = transform_sql(Opts3), + Opts6 = transform_shaper(Opts5), + Opts7 = transform_s2s_out(Opts6), + Opts8 = transform_acl(Opts7), + Opts9 = transform_modules(Opts8), + Opts10 = transform_globals(Opts9), + collect_options(Opts10). + +%%%=================================================================== +%%% mod_register +%%%=================================================================== +transform_register(Opts) -> + try + {value, {modules, ModOpts}, Opts1} = lists:keytake(modules, 1, Opts), + {value, {?MODULE, RegOpts}, ModOpts1} = lists:keytake(?MODULE, 1, ModOpts), + {value, {ip_access, L}, RegOpts1} = lists:keytake(ip_access, 1, RegOpts), + true = is_list(L), + ?WARNING_MSG("Old 'ip_access' format detected. " + "The old format is still supported " + "but it is better to fix your config: " + "use access rules instead.", []), + ACLs = lists:flatmap( + fun({Action, S}) -> + ACLName = misc:binary_to_atom( + iolist_to_binary( + ["ip_", S])), + [{Action, ACLName}, + {acl, ACLName, {ip, S}}] + end, L), + Access = {access, mod_register_networks, + [{Action, ACLName} || {Action, ACLName} <- ACLs]}, + [ACL || {acl, _, _} = ACL <- ACLs] ++ + [Access, + {modules, + [{mod_register, + [{ip_access, mod_register_networks}|RegOpts1]} + | ModOpts1]}|Opts1] + catch error:{badmatch, false} -> + Opts + end. + +%%%=================================================================== +%%% ejabberd_s2s +%%%=================================================================== +transform_s2s(Opts) -> + lists:foldl(fun transform_s2s/2, [], Opts). + +transform_s2s({{s2s_host, Host}, Action}, Opts) -> + ?WARNING_MSG("Option 's2s_host' is deprecated.", []), + ACLName = misc:binary_to_atom( + iolist_to_binary(["s2s_access_", Host])), + [{acl, ACLName, {server, Host}}, + {access, s2s, [{Action, ACLName}]}, + {s2s_access, s2s} | + Opts]; +transform_s2s({s2s_default_policy, Action}, Opts) -> + ?WARNING_MSG("Option 's2s_default_policy' is deprecated. " + "The option is still supported but it is better to " + "fix your config: " + "use 's2s_access' with an access rule.", []), + [{access, s2s, [{Action, all}]}, + {s2s_access, s2s} | + Opts]; +transform_s2s(Opt, Opts) -> + [Opt|Opts]. + +%%%=================================================================== +%%% ejabberd_s2s_out +%%%=================================================================== +transform_s2s_out(Opts) -> + lists:foldl(fun transform_s2s_out/2, [], Opts). + +transform_s2s_out({outgoing_s2s_options, Families, Timeout}, Opts) -> + ?WARNING_MSG("Option 'outgoing_s2s_options' is deprecated. " + "The option is still supported " + "but it is better to fix your config: " + "use 'outgoing_s2s_timeout' and " + "'outgoing_s2s_families' instead.", []), + [{outgoing_s2s_families, Families}, + {outgoing_s2s_timeout, Timeout} + | Opts]; +transform_s2s_out({s2s_dns_options, S2SDNSOpts}, AllOpts) -> + ?WARNING_MSG("Option 's2s_dns_options' is deprecated. " + "The option is still supported " + "but it is better to fix your config: " + "use 's2s_dns_timeout' and " + "'s2s_dns_retries' instead", []), + lists:foldr( + fun({timeout, T}, AccOpts) -> + [{s2s_dns_timeout, T}|AccOpts]; + ({retries, R}, AccOpts) -> + [{s2s_dns_retries, R}|AccOpts]; + (_, AccOpts) -> + AccOpts + end, AllOpts, S2SDNSOpts); +transform_s2s_out(Opt, Opts) -> + [Opt|Opts]. + +%%%=================================================================== +%%% ejabberd_listener +%%%=================================================================== +transform_listeners(Opts) -> + lists:foldl(fun transform_listeners/2, [], Opts). + +transform_listeners({listen, LOpts}, Opts) -> + [{listen, lists:map(fun transform_listener/1, LOpts)} | Opts]; +transform_listeners(Opt, Opts) -> + [Opt|Opts]. + +transform_listener({{Port, IP, Transport}, Mod, Opts}) -> + IPStr = if is_tuple(IP) -> + list_to_binary(inet_parse:ntoa(IP)); + true -> + IP + end, + Opts1 = lists:map( + fun({ip, IPT}) when is_tuple(IPT) -> + {ip, list_to_binary(inet_parse:ntoa(IP))}; + (ssl) -> {tls, true}; + (A) when is_atom(A) -> {A, true}; + (Opt) -> Opt + end, Opts), + Opts2 = lists:foldl( + fun(Opt, Acc) -> + transform_listen_option(Mod, Opt, Acc) + end, [], Opts1), + TransportOpt = if Transport == tcp -> []; + true -> [{transport, Transport}] + end, + IPOpt = if IPStr == <<"0.0.0.0">> -> []; + true -> [{ip, IPStr}] + end, + IPOpt ++ TransportOpt ++ [{port, Port}, {module, Mod} | Opts2]; +transform_listener({{Port, Transport}, Mod, Opts}) + when Transport == tcp orelse Transport == udp -> + transform_listener({{Port, all_zero_ip(Opts), Transport}, Mod, Opts}); +transform_listener({{Port, IP}, Mod, Opts}) -> + transform_listener({{Port, IP, tcp}, Mod, Opts}); +transform_listener({Port, Mod, Opts}) -> + transform_listener({{Port, all_zero_ip(Opts), tcp}, Mod, Opts}); +transform_listener(Opt) -> + Opt. + +transform_listen_option(ejabberd_http, captcha, Opts) -> + [{captcha, true}|Opts]; +transform_listen_option(ejabberd_http, register, Opts) -> + [{register, true}|Opts]; +transform_listen_option(ejabberd_http, web_admin, Opts) -> + [{web_admin, true}|Opts]; +transform_listen_option(ejabberd_http, http_bind, Opts) -> + [{http_bind, true}|Opts]; +transform_listen_option(ejabberd_http, http_poll, Opts) -> + [{http_poll, true}|Opts]; +transform_listen_option(ejabberd_http, {request_handlers, Hs}, Opts) -> + Hs1 = lists:map( + fun({PList, Mod}) when is_list(PList) -> + Path = iolist_to_binary([[$/, P] || P <- PList]), + {Path, Mod}; + (Opt) -> + Opt + end, Hs), + [{request_handlers, Hs1} | Opts]; +transform_listen_option(ejabberd_service, {hosts, Hosts, O}, Opts) -> + case lists:keyfind(hosts, 1, Opts) of + {_, PrevHostOpts} -> + NewHostOpts = + lists:foldl( + fun(H, Acc) -> + dict:append_list(H, O, Acc) + end, dict:from_list(PrevHostOpts), Hosts), + [{hosts, dict:to_list(NewHostOpts)}| + lists:keydelete(hosts, 1, Opts)]; + _ -> + [{hosts, [{H, O} || H <- Hosts]}|Opts] + end; +transform_listen_option(ejabberd_service, {host, Host, Os}, Opts) -> + transform_listen_option(ejabberd_service, {hosts, [Host], Os}, Opts); +transform_listen_option(ejabberd_xmlrpc, {access_commands, ACOpts}, Opts) -> + NewACOpts = lists:map( + fun({AName, ACmds, AOpts}) -> + {AName, [{commands, ACmds}, {options, AOpts}]}; + (Opt) -> + Opt + end, ACOpts), + [{access_commands, NewACOpts}|Opts]; +transform_listen_option(_, Opt, Opts) -> + [Opt|Opts]. + +-spec all_zero_ip([proplists:property()]) -> inet:ip_address(). +all_zero_ip(Opts) -> + case proplists:get_bool(inet6, Opts) of + true -> {0,0,0,0,0,0,0,0}; + false -> {0,0,0,0} + end. + +%%%=================================================================== +%%% ejabberd_shaper +%%%=================================================================== +transform_shaper(Opts) -> + lists:foldl(fun transform_shaper/2, [], Opts). + +transform_shaper({shaper, Name, {maxrate, N}}, Opts) -> + [{shaper, [{Name, N}]} | Opts]; +transform_shaper({shaper, Name, none}, Opts) -> + [{shaper, [{Name, none}]} | Opts]; +transform_shaper({shaper, List}, Opts) when is_list(List) -> + R = lists:map( + fun({Name, Args}) when is_list(Args) -> + MaxRate = proplists:get_value(rate, Args, 1000), + BurstSize = proplists:get_value(burst_size, Args, MaxRate), + {Name, MaxRate, BurstSize}; + ({Name, Val}) -> + {Name, Val, Val} + end, List), + [{shaper, R} | Opts]; +transform_shaper(Opt, Opts) -> + [Opt | Opts]. + +%%%=================================================================== +%%% acl +%%%=================================================================== +transform_acl(Opts) -> + Opts1 = lists:foldl(fun transform_acl/2, [], Opts), + {ACLOpts, Opts2} = lists:mapfoldl( + fun({acl, Os}, Acc) -> + {Os, Acc}; + (O, Acc) -> + {[], [O|Acc]} + end, [], Opts1), + {AccessOpts, Opts3} = lists:mapfoldl( + fun({access, Os}, Acc) -> + {Os, Acc}; + (O, Acc) -> + {[], [O|Acc]} + end, [], Opts2), + {NewAccessOpts, Opts4} = lists:mapfoldl( + fun({access_rules, Os}, Acc) -> + {Os, Acc}; + (O, Acc) -> + {[], [O|Acc]} + end, [], Opts3), + {ShaperOpts, Opts5} = lists:mapfoldl( + fun({shaper_rules, Os}, Acc) -> + {Os, Acc}; + (O, Acc) -> + {[], [O|Acc]} + end, [], Opts4), + ACLOpts1 = collect_options(lists:flatten(ACLOpts)), + AccessOpts1 = case collect_options(lists:flatten(AccessOpts)) of + [] -> []; + L1 -> [{access, L1}] + end, + ACLOpts2 = case lists:map( + fun({ACLName, Os}) -> + {ACLName, collect_options(Os)} + end, ACLOpts1) of + [] -> []; + L2 -> [{acl, L2}] + end, + NewAccessOpts1 = case lists:map( + fun({NAName, Os}) -> + {NAName, transform_access_rules_config(Os)} + end, lists:flatten(NewAccessOpts)) of + [] -> []; + L3 -> [{access_rules, L3}] + end, + ShaperOpts1 = case lists:map( + fun({SName, Ss}) -> + {SName, transform_access_rules_config(Ss)} + end, lists:flatten(ShaperOpts)) of + [] -> []; + L4 -> [{shaper_rules, L4}] + end, + ACLOpts2 ++ AccessOpts1 ++ NewAccessOpts1 ++ ShaperOpts1 ++ Opts5. + +transform_acl({acl, Name, Type}, Opts) -> + T = case Type of + all -> all; + none -> none; + {user, U} -> {user, [b(U)]}; + {user, U, S} -> {user, [[{b(U), b(S)}]]}; + {shared_group, G} -> {shared_group, [b(G)]}; + {shared_group, G, H} -> {shared_group, [[{b(G), b(H)}]]}; + {user_regexp, UR} -> {user_regexp, [b(UR)]}; + {user_regexp, UR, S} -> {user_regexp, [[{b(UR), b(S)}]]}; + {node_regexp, UR, SR} -> {node_regexp, [[{b(UR), b(SR)}]]}; + {user_glob, UR} -> {user_glob, [b(UR)]}; + {user_glob, UR, S} -> {user_glob, [[{b(UR), b(S)}]]}; + {node_glob, UR, SR} -> {node_glob, [[{b(UR), b(SR)}]]}; + {server, S} -> {server, [b(S)]}; + {resource, R} -> {resource, [b(R)]}; + {server_regexp, SR} -> {server_regexp, [b(SR)]}; + {server_glob, S} -> {server_glob, [b(S)]}; + {ip, S} -> {ip, [b(S)]}; + {resource_glob, R} -> {resource_glob, [b(R)]}; + {resource_regexp, R} -> {resource_regexp, [b(R)]} + end, + [{acl, [{Name, [T]}]}|Opts]; +transform_acl({access, Name, Rules}, Opts) -> + NewRules = [{ACL, Action} || {Action, ACL} <- Rules], + [{access, [{Name, NewRules}]}|Opts]; +transform_acl(Opt, Opts) -> + [Opt|Opts]. + +transform_access_rules_config(Config) when is_list(Config) -> + lists:map(fun transform_access_rules_config2/1, lists:flatten(Config)); +transform_access_rules_config(Config) -> + transform_access_rules_config([Config]). + +transform_access_rules_config2(Type) when is_integer(Type); is_atom(Type) -> + {Type, [all]}; +transform_access_rules_config2({Type, ACL}) when is_atom(ACL) -> + {Type, [{acl, ACL}]}; +transform_access_rules_config2({Res, Rules}) when is_list(Rules) -> + T = lists:map(fun({Type, Args}) when is_list(Args) -> + {Type, hd(lists:flatten(Args))}; + (V) -> + V + end, lists:flatten(Rules)), + {Res, T}; +transform_access_rules_config2({Res, Rule}) -> + {Res, [Rule]}. + +%%%=================================================================== +%%% SQL +%%%=================================================================== +-define(PGSQL_PORT, 5432). +-define(MYSQL_PORT, 3306). + +transform_sql(Opts) -> + lists:foldl(fun transform_sql/2, [], Opts). + +transform_sql({odbc_server, {Type, Server, Port, DB, User, Pass}}, Opts) -> + [{sql_type, Type}, + {sql_server, Server}, + {sql_port, Port}, + {sql_database, DB}, + {sql_username, User}, + {sql_password, Pass}|Opts]; +transform_sql({odbc_server, {mysql, Server, DB, User, Pass}}, Opts) -> + transform_sql({odbc_server, {mysql, Server, ?MYSQL_PORT, DB, User, Pass}}, Opts); +transform_sql({odbc_server, {pgsql, Server, DB, User, Pass}}, Opts) -> + transform_sql({odbc_server, {pgsql, Server, ?PGSQL_PORT, DB, User, Pass}}, Opts); +transform_sql({odbc_server, {sqlite, DB}}, Opts) -> + [{sql_type, sqlite}, + {sql_database, DB}|Opts]; +transform_sql({odbc_pool_size, N}, Opts) -> + [{sql_pool_size, N}|Opts]; +transform_sql(Opt, Opts) -> + [Opt|Opts]. + +%%%=================================================================== +%%% modules +%%%=================================================================== +transform_modules(Opts) -> + lists:foldl(fun transform_modules/2, [], Opts). + +transform_modules({modules, ModOpts}, Opts) -> + [{modules, lists:map( + fun({Mod, Opts1}) -> + {Mod, transform_module(Mod, Opts1)}; + (Other) -> + Other + end, ModOpts)}|Opts]; +transform_modules(Opt, Opts) -> + [Opt|Opts]. + +transform_module(mod_disco, Opts) -> + lists:map( + fun({server_info, Infos}) -> + NewInfos = lists:map( + fun({Modules, Name, URLs}) -> + [[{modules, Modules}, + {name, Name}, + {urls, URLs}]]; + (Opt) -> + Opt + end, Infos), + {server_info, NewInfos}; + (Opt) -> + Opt + end, Opts); +transform_module(mod_muc_log, Opts) -> + lists:map( + fun({top_link, {S1, S2}}) -> + {top_link, [{S1, S2}]}; + (Opt) -> + Opt + end, Opts); +transform_module(mod_proxy65, Opts) -> + lists:map( + fun({ip, IP}) when is_tuple(IP) -> + {ip, misc:ip_to_list(IP)}; + ({hostname, IP}) when is_tuple(IP) -> + {hostname, misc:ip_to_list(IP)}; + (Opt) -> + Opt + end, Opts); +transform_module(mod_register, Opts) -> + lists:flatmap( + fun({welcome_message, {Subj, Body}}) -> + [{welcome_message, [{subject, Subj}, {body, Body}]}]; + (Opt) -> + [Opt] + end, Opts); +transform_module(_Mod, Opts) -> + Opts. + +%%%=================================================================== +%%% Host config +%%%=================================================================== +transform_host_config(Opts) -> + Opts1 = lists:foldl(fun transform_host_config/2, [], Opts), + {HOpts, Opts2} = lists:mapfoldl( + fun({host_config, O}, Os) -> + {[O], Os}; + (O, Os) -> + {[], [O|Os]} + end, [], Opts1), + {AHOpts, Opts3} = lists:mapfoldl( + fun({append_host_config, O}, Os) -> + {[O], Os}; + (O, Os) -> + {[], [O|Os]} + end, [], Opts2), + HOpts1 = case collect_options(lists:flatten(HOpts)) of + [] -> + []; + HOs -> + [{host_config, + [{H, transform(O)} || {H, O} <- HOs]}] + end, + AHOpts1 = case collect_options(lists:flatten(AHOpts)) of + [] -> + []; + AHOs -> + [{append_host_config, + [{H, transform(O)} || {H, O} <- AHOs]}] + end, + HOpts1 ++ AHOpts1 ++ Opts3. + +transform_host_config({host_config, Host, HOpts}, Opts) -> + {AddOpts, HOpts1} = + lists:mapfoldl( + fun({{add, Opt}, Val}, Os) -> + {[{Opt, Val}], Os}; + (O, Os) -> + {[], [O|Os]} + end, [], HOpts), + [{append_host_config, [{Host, lists:flatten(AddOpts)}]}, + {host_config, [{Host, HOpts1}]}|Opts]; +transform_host_config(Opt, Opts) -> + [Opt|Opts]. + +%%%=================================================================== +%%% Top-level options +%%%=================================================================== +transform_globals(Opts) -> + lists:foldl(fun transform_globals/2, [], Opts). + +transform_globals(Opt, Opts) when Opt == override_global; + Opt == override_local; + Opt == override_acls -> + ?WARNING_MSG("Option '~ts' has no effect anymore", [Opt]), + Opts; +transform_globals({node_start, _}, Opts) -> + ?WARNING_MSG("Option 'node_start' has no effect anymore", []), + Opts; +transform_globals({iqdisc, {queues, N}}, Opts) -> + [{iqdisc, N}|Opts]; +transform_globals({define_macro, Macro, Val}, Opts) -> + [{define_macro, [{Macro, Val}]}|Opts]; +transform_globals(Opt, Opts) -> + [Opt|Opts]. + +%%%=================================================================== +%%% Certfiles +%%%=================================================================== +transform_certfiles(Opts) -> + lists:foldl(fun transform_certfiles/2, [], Opts). + +transform_certfiles({domain_certfile, Domain, CertFile}, Opts) -> + [{host_config, [{Domain, [{domain_certfile, CertFile}]}]}|Opts]; +transform_certfiles(Opt, Opts) -> + [Opt|Opts]. + +%%%=================================================================== +%%% Consult file +%%%=================================================================== +consult(File) -> + case file:consult(File) of + {ok, Terms} -> + include_config_files(Terms); + Err -> + Err + end. + +include_config_files(Terms) -> + include_config_files(Terms, []). + +include_config_files([], Res) -> + {ok, Res}; +include_config_files([{include_config_file, Filename} | Terms], Res) -> + include_config_files([{include_config_file, Filename, []} | Terms], Res); +include_config_files([{include_config_file, Filename, Options} | Terms], Res) -> + case consult(Filename) of + {ok, Included_terms} -> + Disallow = proplists:get_value(disallow, Options, []), + Included_terms2 = delete_disallowed(Disallow, Included_terms), + Allow_only = proplists:get_value(allow_only, Options, all), + Included_terms3 = keep_only_allowed(Allow_only, Included_terms2), + include_config_files(Terms, Res ++ Included_terms3); + Err -> + Err + end; +include_config_files([Term | Terms], Res) -> + include_config_files(Terms, Res ++ [Term]). + +delete_disallowed(Disallowed, Terms) -> + lists:foldl( + fun(Dis, Ldis) -> + delete_disallowed2(Dis, Ldis) + end, + Terms, + Disallowed). + +delete_disallowed2(Disallowed, [H|T]) -> + case element(1, H) of + Disallowed -> + delete_disallowed2(Disallowed, T); + _ -> + [H|delete_disallowed2(Disallowed, T)] + end; +delete_disallowed2(_, []) -> + []. + +keep_only_allowed(all, Terms) -> + Terms; +keep_only_allowed(Allowed, Terms) -> + {As, _NAs} = lists:partition( + fun(Term) -> + lists:member(element(1, Term), Allowed) + end, Terms), + As. + +%%%=================================================================== +%%% Aux functions +%%%=================================================================== +strings_to_binary([]) -> + []; +strings_to_binary(L) when is_list(L) -> + case is_string(L) of + true -> + list_to_binary(L); + false -> + strings_to_binary1(L) + end; +strings_to_binary({A, B, C, D}) when + is_integer(A), is_integer(B), is_integer(C), is_integer(D) -> + {A, B, C, D}; +strings_to_binary(T) when is_tuple(T) -> + list_to_tuple(strings_to_binary1(tuple_to_list(T))); +strings_to_binary(X) -> + X. + +strings_to_binary1([El|L]) -> + [strings_to_binary(El)|strings_to_binary1(L)]; +strings_to_binary1([]) -> + []; +strings_to_binary1(T) -> + T. + +is_string([C|T]) when (C >= 0) and (C =< 255) -> + is_string(T); +is_string([]) -> + true; +is_string(_) -> + false. + +b(S) -> + iolist_to_binary(S). diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl new file mode 100644 index 000000000..775ea14c9 --- /dev/null +++ b/src/ejabberd_option.erl @@ -0,0 +1,1204 @@ +%% Generated automatically +%% DO NOT EDIT: run `make options` instead + +-module(ejabberd_option). + +-export([access_rules/0, access_rules/1]). +-export([acl/0, acl/1]). +-export([acme/0]). +-export([allow_contrib_modules/0]). +-export([allow_multiple_connections/0, allow_multiple_connections/1]). +-export([anonymous_protocol/0, anonymous_protocol/1]). +-export([api_permissions/0]). +-export([append_host_config/0]). +-export([auth_cache_life_time/0]). +-export([auth_cache_missed/0]). +-export([auth_cache_size/0]). +-export([auth_external_user_exists_check/0, auth_external_user_exists_check/1]). +-export([auth_method/0, auth_method/1]). +-export([auth_opts/0, auth_opts/1]). +-export([auth_password_format/0, auth_password_format/1]). +-export([auth_password_types_hidden_in_sasl1/0, auth_password_types_hidden_in_sasl1/1]). +-export([auth_scram_hash/0, auth_scram_hash/1]). +-export([auth_stored_password_types/0, auth_stored_password_types/1]). +-export([auth_use_cache/0, auth_use_cache/1]). +-export([c2s_cafile/0, c2s_cafile/1]). +-export([c2s_ciphers/0, c2s_ciphers/1]). +-export([c2s_dhfile/0, c2s_dhfile/1]). +-export([c2s_protocol_options/0, c2s_protocol_options/1]). +-export([c2s_tls_compression/0, c2s_tls_compression/1]). +-export([ca_file/0]). +-export([cache_life_time/0, cache_life_time/1]). +-export([cache_missed/0, cache_missed/1]). +-export([cache_size/0, cache_size/1]). +-export([captcha_cmd/0]). +-export([captcha_host/0]). +-export([captcha_limit/0]). +-export([captcha_url/0]). +-export([certfiles/0]). +-export([cluster_backend/0]). +-export([cluster_nodes/0]). +-export([default_db/0, default_db/1]). +-export([default_ram_db/0, default_ram_db/1]). +-export([define_keyword/0, define_keyword/1]). +-export([define_macro/0]). +-export([disable_sasl_mechanisms/0, disable_sasl_mechanisms/1]). +-export([disable_sasl_scram_downgrade_protection/0, disable_sasl_scram_downgrade_protection/1]). +-export([domain_balancing/0]). +-export([ext_api_headers/0, ext_api_headers/1]). +-export([ext_api_http_pool_size/0, ext_api_http_pool_size/1]). +-export([ext_api_path_oauth/0]). +-export([ext_api_url/0, ext_api_url/1]). +-export([extauth_pool_name/0, extauth_pool_name/1]). +-export([extauth_pool_size/0, extauth_pool_size/1]). +-export([extauth_program/0, extauth_program/1]). +-export([fqdn/0]). +-export([hide_sensitive_log_data/0, hide_sensitive_log_data/1]). +-export([host_config/0]). +-export([hosts/0]). +-export([hosts_alias/0]). +-export([include_config_file/0, include_config_file/1]). +-export([install_contrib_modules/0]). +-export([jwt_auth_only_rule/0, jwt_auth_only_rule/1]). +-export([jwt_jid_field/0, jwt_jid_field/1]). +-export([jwt_key/0, jwt_key/1]). +-export([language/0, language/1]). +-export([ldap_backups/0, ldap_backups/1]). +-export([ldap_base/0, ldap_base/1]). +-export([ldap_deref_aliases/0, ldap_deref_aliases/1]). +-export([ldap_dn_filter/0, ldap_dn_filter/1]). +-export([ldap_encrypt/0, ldap_encrypt/1]). +-export([ldap_filter/0, ldap_filter/1]). +-export([ldap_password/0, ldap_password/1]). +-export([ldap_port/0, ldap_port/1]). +-export([ldap_rootdn/0, ldap_rootdn/1]). +-export([ldap_servers/0, ldap_servers/1]). +-export([ldap_tls_cacertfile/0, ldap_tls_cacertfile/1]). +-export([ldap_tls_certfile/0, ldap_tls_certfile/1]). +-export([ldap_tls_depth/0, ldap_tls_depth/1]). +-export([ldap_tls_verify/0, ldap_tls_verify/1]). +-export([ldap_uids/0, ldap_uids/1]). +-export([listen/0]). +-export([log_burst_limit_count/0]). +-export([log_burst_limit_window_time/0]). +-export([log_modules_fully/0]). +-export([log_rotate_count/0]). +-export([log_rotate_size/0]). +-export([loglevel/0]). +-export([max_fsm_queue/0, max_fsm_queue/1]). +-export([modules/0, modules/1]). +-export([negotiation_timeout/0]). +-export([net_ticktime/0]). +-export([new_sql_schema/0]). +-export([oauth_access/0, oauth_access/1]). +-export([oauth_cache_life_time/0]). +-export([oauth_cache_missed/0]). +-export([oauth_cache_rest_failure_life_time/0]). +-export([oauth_cache_size/0]). +-export([oauth_client_id_check/0, oauth_client_id_check/1]). +-export([oauth_db_type/0]). +-export([oauth_expire/0]). +-export([oauth_use_cache/0]). +-export([oom_killer/0]). +-export([oom_queue/0]). +-export([oom_watermark/0]). +-export([outgoing_s2s_families/0, outgoing_s2s_families/1]). +-export([outgoing_s2s_ipv4_address/0, outgoing_s2s_ipv4_address/1]). +-export([outgoing_s2s_ipv6_address/0, outgoing_s2s_ipv6_address/1]). +-export([outgoing_s2s_port/0, outgoing_s2s_port/1]). +-export([outgoing_s2s_timeout/0, outgoing_s2s_timeout/1]). +-export([pam_service/0, pam_service/1]). +-export([pam_userinfotype/0, pam_userinfotype/1]). +-export([pgsql_users_number_estimate/0, pgsql_users_number_estimate/1]). +-export([queue_dir/0]). +-export([queue_type/0, queue_type/1]). +-export([redis_connect_timeout/0]). +-export([redis_db/0]). +-export([redis_password/0]). +-export([redis_pool_size/0]). +-export([redis_port/0]). +-export([redis_queue_type/0]). +-export([redis_server/0]). +-export([registration_timeout/0]). +-export([resource_conflict/0, resource_conflict/1]). +-export([rest_proxy/0, rest_proxy/1]). +-export([rest_proxy_password/0, rest_proxy_password/1]). +-export([rest_proxy_port/0, rest_proxy_port/1]). +-export([rest_proxy_username/0, rest_proxy_username/1]). +-export([router_cache_life_time/0]). +-export([router_cache_missed/0]). +-export([router_cache_size/0]). +-export([router_db_type/0]). +-export([router_use_cache/0]). +-export([rpc_timeout/0]). +-export([s2s_access/0, s2s_access/1]). +-export([s2s_cafile/0, s2s_cafile/1]). +-export([s2s_ciphers/0, s2s_ciphers/1]). +-export([s2s_dhfile/0, s2s_dhfile/1]). +-export([s2s_dns_retries/0, s2s_dns_retries/1]). +-export([s2s_dns_timeout/0, s2s_dns_timeout/1]). +-export([s2s_max_retry_delay/0]). +-export([s2s_protocol_options/0, s2s_protocol_options/1]). +-export([s2s_queue_type/0, s2s_queue_type/1]). +-export([s2s_timeout/0, s2s_timeout/1]). +-export([s2s_tls_compression/0, s2s_tls_compression/1]). +-export([s2s_use_starttls/0, s2s_use_starttls/1]). +-export([s2s_zlib/0, s2s_zlib/1]). +-export([shaper/0]). +-export([shaper_rules/0, shaper_rules/1]). +-export([sm_cache_life_time/0]). +-export([sm_cache_missed/0]). +-export([sm_cache_size/0]). +-export([sm_db_type/0, sm_db_type/1]). +-export([sm_use_cache/0, sm_use_cache/1]). +-export([sql_connect_timeout/0, sql_connect_timeout/1]). +-export([sql_database/0, sql_database/1]). +-export([sql_flags/0, sql_flags/1]). +-export([sql_keepalive_interval/0, sql_keepalive_interval/1]). +-export([sql_odbc_driver/0, sql_odbc_driver/1]). +-export([sql_password/0, sql_password/1]). +-export([sql_pool_size/0, sql_pool_size/1]). +-export([sql_port/0, sql_port/1]). +-export([sql_prepared_statements/0, sql_prepared_statements/1]). +-export([sql_query_timeout/0, sql_query_timeout/1]). +-export([sql_queue_type/0, sql_queue_type/1]). +-export([sql_server/0, sql_server/1]). +-export([sql_ssl/0, sql_ssl/1]). +-export([sql_ssl_cafile/0, sql_ssl_cafile/1]). +-export([sql_ssl_certfile/0, sql_ssl_certfile/1]). +-export([sql_ssl_verify/0, sql_ssl_verify/1]). +-export([sql_start_interval/0, sql_start_interval/1]). +-export([sql_type/0, sql_type/1]). +-export([sql_username/0, sql_username/1]). +-export([trusted_proxies/0]). +-export([update_sql_schema/0]). +-export([update_sql_schema_timeout/0, update_sql_schema_timeout/1]). +-export([use_cache/0, use_cache/1]). +-export([validate_stream/0]). +-export([version/0]). +-export([websocket_origin/0]). +-export([websocket_ping_interval/0]). +-export([websocket_timeout/0]). + +-spec access_rules() -> [{atom(),acl:access()}]. +access_rules() -> + access_rules(global). +-spec access_rules(global | binary()) -> [{atom(),acl:access()}]. +access_rules(Host) -> + ejabberd_config:get_option({access_rules, Host}). + +-spec acl() -> [{atom(),[acl:acl_rule()]}]. +acl() -> + acl(global). +-spec acl(global | binary()) -> [{atom(),[acl:acl_rule()]}]. +acl(Host) -> + ejabberd_config:get_option({acl, Host}). + +-spec acme() -> #{'auto'=>boolean(), 'ca_url'=>binary(), 'cert_type'=>'ec' | 'rsa', 'contact'=>[binary()]}. +acme() -> + ejabberd_config:get_option({acme, global}). + +-spec allow_contrib_modules() -> boolean(). +allow_contrib_modules() -> + ejabberd_config:get_option({allow_contrib_modules, global}). + +-spec allow_multiple_connections() -> boolean(). +allow_multiple_connections() -> + allow_multiple_connections(global). +-spec allow_multiple_connections(global | binary()) -> boolean(). +allow_multiple_connections(Host) -> + ejabberd_config:get_option({allow_multiple_connections, Host}). + +-spec anonymous_protocol() -> 'both' | 'login_anon' | 'sasl_anon'. +anonymous_protocol() -> + anonymous_protocol(global). +-spec anonymous_protocol(global | binary()) -> 'both' | 'login_anon' | 'sasl_anon'. +anonymous_protocol(Host) -> + ejabberd_config:get_option({anonymous_protocol, Host}). + +-spec api_permissions() -> [ejabberd_access_permissions:permission()]. +api_permissions() -> + ejabberd_config:get_option({api_permissions, global}). + +-spec append_host_config() -> [{binary(),any()}]. +append_host_config() -> + ejabberd_config:get_option({append_host_config, global}). + +-spec auth_cache_life_time() -> 'infinity' | pos_integer(). +auth_cache_life_time() -> + ejabberd_config:get_option({auth_cache_life_time, global}). + +-spec auth_cache_missed() -> boolean(). +auth_cache_missed() -> + ejabberd_config:get_option({auth_cache_missed, global}). + +-spec auth_cache_size() -> 'infinity' | pos_integer(). +auth_cache_size() -> + ejabberd_config:get_option({auth_cache_size, global}). + +-spec auth_external_user_exists_check() -> boolean(). +auth_external_user_exists_check() -> + auth_external_user_exists_check(global). +-spec auth_external_user_exists_check(global | binary()) -> boolean(). +auth_external_user_exists_check(Host) -> + ejabberd_config:get_option({auth_external_user_exists_check, Host}). + +-spec auth_method() -> [atom()]. +auth_method() -> + auth_method(global). +-spec auth_method(global | binary()) -> [atom()]. +auth_method(Host) -> + ejabberd_config:get_option({auth_method, Host}). + +-spec auth_opts() -> [{any(),any()}]. +auth_opts() -> + auth_opts(global). +-spec auth_opts(global | binary()) -> [{any(),any()}]. +auth_opts(Host) -> + ejabberd_config:get_option({auth_opts, Host}). + +-spec auth_password_format() -> 'plain' | 'scram'. +auth_password_format() -> + auth_password_format(global). +-spec auth_password_format(global | binary()) -> 'plain' | 'scram'. +auth_password_format(Host) -> + ejabberd_config:get_option({auth_password_format, Host}). + +-spec auth_password_types_hidden_in_sasl1() -> ['plain' | 'scram_sha1' | 'scram_sha256' | 'scram_sha512']. +auth_password_types_hidden_in_sasl1() -> + auth_password_types_hidden_in_sasl1(global). +-spec auth_password_types_hidden_in_sasl1(global | binary()) -> ['plain' | 'scram_sha1' | 'scram_sha256' | 'scram_sha512']. +auth_password_types_hidden_in_sasl1(Host) -> + ejabberd_config:get_option({auth_password_types_hidden_in_sasl1, Host}). + +-spec auth_scram_hash() -> 'sha' | 'sha256' | 'sha512'. +auth_scram_hash() -> + auth_scram_hash(global). +-spec auth_scram_hash(global | binary()) -> 'sha' | 'sha256' | 'sha512'. +auth_scram_hash(Host) -> + ejabberd_config:get_option({auth_scram_hash, Host}). + +-spec auth_stored_password_types() -> ['plain' | 'scram_sha1' | 'scram_sha256' | 'scram_sha512']. +auth_stored_password_types() -> + auth_stored_password_types(global). +-spec auth_stored_password_types(global | binary()) -> ['plain' | 'scram_sha1' | 'scram_sha256' | 'scram_sha512']. +auth_stored_password_types(Host) -> + ejabberd_config:get_option({auth_stored_password_types, Host}). + +-spec auth_use_cache() -> boolean(). +auth_use_cache() -> + auth_use_cache(global). +-spec auth_use_cache(global | binary()) -> boolean(). +auth_use_cache(Host) -> + ejabberd_config:get_option({auth_use_cache, Host}). + +-spec c2s_cafile() -> 'undefined' | binary(). +c2s_cafile() -> + c2s_cafile(global). +-spec c2s_cafile(global | binary()) -> 'undefined' | binary(). +c2s_cafile(Host) -> + ejabberd_config:get_option({c2s_cafile, Host}). + +-spec c2s_ciphers() -> 'undefined' | binary(). +c2s_ciphers() -> + c2s_ciphers(global). +-spec c2s_ciphers(global | binary()) -> 'undefined' | binary(). +c2s_ciphers(Host) -> + ejabberd_config:get_option({c2s_ciphers, Host}). + +-spec c2s_dhfile() -> 'undefined' | binary(). +c2s_dhfile() -> + c2s_dhfile(global). +-spec c2s_dhfile(global | binary()) -> 'undefined' | binary(). +c2s_dhfile(Host) -> + ejabberd_config:get_option({c2s_dhfile, Host}). + +-spec c2s_protocol_options() -> 'undefined' | binary(). +c2s_protocol_options() -> + c2s_protocol_options(global). +-spec c2s_protocol_options(global | binary()) -> 'undefined' | binary(). +c2s_protocol_options(Host) -> + ejabberd_config:get_option({c2s_protocol_options, Host}). + +-spec c2s_tls_compression() -> 'false' | 'true' | 'undefined'. +c2s_tls_compression() -> + c2s_tls_compression(global). +-spec c2s_tls_compression(global | binary()) -> 'false' | 'true' | 'undefined'. +c2s_tls_compression(Host) -> + ejabberd_config:get_option({c2s_tls_compression, Host}). + +-spec ca_file() -> binary(). +ca_file() -> + ejabberd_config:get_option({ca_file, global}). + +-spec cache_life_time() -> 'infinity' | pos_integer(). +cache_life_time() -> + cache_life_time(global). +-spec cache_life_time(global | binary()) -> 'infinity' | pos_integer(). +cache_life_time(Host) -> + ejabberd_config:get_option({cache_life_time, Host}). + +-spec cache_missed() -> boolean(). +cache_missed() -> + cache_missed(global). +-spec cache_missed(global | binary()) -> boolean(). +cache_missed(Host) -> + ejabberd_config:get_option({cache_missed, Host}). + +-spec cache_size() -> 'infinity' | pos_integer(). +cache_size() -> + cache_size(global). +-spec cache_size(global | binary()) -> 'infinity' | pos_integer(). +cache_size(Host) -> + ejabberd_config:get_option({cache_size, Host}). + +-spec captcha_cmd() -> 'undefined' | binary(). +captcha_cmd() -> + ejabberd_config:get_option({captcha_cmd, global}). + +-spec captcha_host() -> binary(). +captcha_host() -> + ejabberd_config:get_option({captcha_host, global}). + +-spec captcha_limit() -> 'infinity' | pos_integer(). +captcha_limit() -> + ejabberd_config:get_option({captcha_limit, global}). + +-spec captcha_url() -> 'auto' | 'undefined' | binary(). +captcha_url() -> + ejabberd_config:get_option({captcha_url, global}). + +-spec certfiles() -> 'undefined' | [binary()]. +certfiles() -> + ejabberd_config:get_option({certfiles, global}). + +-spec cluster_backend() -> atom(). +cluster_backend() -> + ejabberd_config:get_option({cluster_backend, global}). + +-spec cluster_nodes() -> [atom()]. +cluster_nodes() -> + ejabberd_config:get_option({cluster_nodes, global}). + +-spec default_db() -> 'mnesia' | 'sql'. +default_db() -> + default_db(global). +-spec default_db(global | binary()) -> 'mnesia' | 'sql'. +default_db(Host) -> + ejabberd_config:get_option({default_db, Host}). + +-spec default_ram_db() -> 'mnesia' | 'redis' | 'sql'. +default_ram_db() -> + default_ram_db(global). +-spec default_ram_db(global | binary()) -> 'mnesia' | 'redis' | 'sql'. +default_ram_db(Host) -> + ejabberd_config:get_option({default_ram_db, Host}). + +-spec define_keyword() -> any(). +define_keyword() -> + define_keyword(global). +-spec define_keyword(global | binary()) -> any(). +define_keyword(Host) -> + ejabberd_config:get_option({define_keyword, Host}). + +-spec define_macro() -> any(). +define_macro() -> + ejabberd_config:get_option({define_macro, global}). + +-spec disable_sasl_mechanisms() -> [binary()]. +disable_sasl_mechanisms() -> + disable_sasl_mechanisms(global). +-spec disable_sasl_mechanisms(global | binary()) -> [binary()]. +disable_sasl_mechanisms(Host) -> + ejabberd_config:get_option({disable_sasl_mechanisms, Host}). + +-spec disable_sasl_scram_downgrade_protection() -> boolean(). +disable_sasl_scram_downgrade_protection() -> + disable_sasl_scram_downgrade_protection(global). +-spec disable_sasl_scram_downgrade_protection(global | binary()) -> boolean(). +disable_sasl_scram_downgrade_protection(Host) -> + ejabberd_config:get_option({disable_sasl_scram_downgrade_protection, Host}). + +-spec domain_balancing() -> #{binary()=>#{'component_number'=>1..1114111, 'type'=>'bare_destination' | 'bare_source' | 'destination' | 'random' | 'source'}}. +domain_balancing() -> + ejabberd_config:get_option({domain_balancing, global}). + +-spec ext_api_headers() -> binary(). +ext_api_headers() -> + ext_api_headers(global). +-spec ext_api_headers(global | binary()) -> binary(). +ext_api_headers(Host) -> + ejabberd_config:get_option({ext_api_headers, Host}). + +-spec ext_api_http_pool_size() -> pos_integer(). +ext_api_http_pool_size() -> + ext_api_http_pool_size(global). +-spec ext_api_http_pool_size(global | binary()) -> pos_integer(). +ext_api_http_pool_size(Host) -> + ejabberd_config:get_option({ext_api_http_pool_size, Host}). + +-spec ext_api_path_oauth() -> binary(). +ext_api_path_oauth() -> + ejabberd_config:get_option({ext_api_path_oauth, global}). + +-spec ext_api_url() -> binary(). +ext_api_url() -> + ext_api_url(global). +-spec ext_api_url(global | binary()) -> binary(). +ext_api_url(Host) -> + ejabberd_config:get_option({ext_api_url, Host}). + +-spec extauth_pool_name() -> 'undefined' | binary(). +extauth_pool_name() -> + extauth_pool_name(global). +-spec extauth_pool_name(global | binary()) -> 'undefined' | binary(). +extauth_pool_name(Host) -> + ejabberd_config:get_option({extauth_pool_name, Host}). + +-spec extauth_pool_size() -> 'undefined' | pos_integer(). +extauth_pool_size() -> + extauth_pool_size(global). +-spec extauth_pool_size(global | binary()) -> 'undefined' | pos_integer(). +extauth_pool_size(Host) -> + ejabberd_config:get_option({extauth_pool_size, Host}). + +-spec extauth_program() -> 'undefined' | string(). +extauth_program() -> + extauth_program(global). +-spec extauth_program(global | binary()) -> 'undefined' | string(). +extauth_program(Host) -> + ejabberd_config:get_option({extauth_program, Host}). + +-spec fqdn() -> [binary()]. +fqdn() -> + ejabberd_config:get_option({fqdn, global}). + +-spec hide_sensitive_log_data() -> boolean(). +hide_sensitive_log_data() -> + hide_sensitive_log_data(global). +-spec hide_sensitive_log_data(global | binary()) -> boolean(). +hide_sensitive_log_data(Host) -> + ejabberd_config:get_option({hide_sensitive_log_data, Host}). + +-spec host_config() -> [{binary(),any()}]. +host_config() -> + ejabberd_config:get_option({host_config, global}). + +-spec hosts() -> [binary(),...]. +hosts() -> + ejabberd_config:get_option({hosts, global}). + +-spec hosts_alias() -> [{binary(),binary()}]. +hosts_alias() -> + ejabberd_config:get_option({hosts_alias, global}). + +-spec include_config_file() -> any(). +include_config_file() -> + include_config_file(global). +-spec include_config_file(global | binary()) -> any(). +include_config_file(Host) -> + ejabberd_config:get_option({include_config_file, Host}). + +-spec install_contrib_modules() -> [atom()]. +install_contrib_modules() -> + ejabberd_config:get_option({install_contrib_modules, global}). + +-spec jwt_auth_only_rule() -> atom(). +jwt_auth_only_rule() -> + jwt_auth_only_rule(global). +-spec jwt_auth_only_rule(global | binary()) -> atom(). +jwt_auth_only_rule(Host) -> + ejabberd_config:get_option({jwt_auth_only_rule, Host}). + +-spec jwt_jid_field() -> binary(). +jwt_jid_field() -> + jwt_jid_field(global). +-spec jwt_jid_field(global | binary()) -> binary(). +jwt_jid_field(Host) -> + ejabberd_config:get_option({jwt_jid_field, Host}). + +-spec jwt_key() -> jose_jwk:key() | 'undefined'. +jwt_key() -> + jwt_key(global). +-spec jwt_key(global | binary()) -> jose_jwk:key() | 'undefined'. +jwt_key(Host) -> + ejabberd_config:get_option({jwt_key, Host}). + +-spec language() -> binary(). +language() -> + language(global). +-spec language(global | binary()) -> binary(). +language(Host) -> + ejabberd_config:get_option({language, Host}). + +-spec ldap_backups() -> [binary()]. +ldap_backups() -> + ldap_backups(global). +-spec ldap_backups(global | binary()) -> [binary()]. +ldap_backups(Host) -> + ejabberd_config:get_option({ldap_backups, Host}). + +-spec ldap_base() -> binary(). +ldap_base() -> + ldap_base(global). +-spec ldap_base(global | binary()) -> binary(). +ldap_base(Host) -> + ejabberd_config:get_option({ldap_base, Host}). + +-spec ldap_deref_aliases() -> 'always' | 'finding' | 'never' | 'searching'. +ldap_deref_aliases() -> + ldap_deref_aliases(global). +-spec ldap_deref_aliases(global | binary()) -> 'always' | 'finding' | 'never' | 'searching'. +ldap_deref_aliases(Host) -> + ejabberd_config:get_option({ldap_deref_aliases, Host}). + +-spec ldap_dn_filter() -> {binary(),[binary()]}. +ldap_dn_filter() -> + ldap_dn_filter(global). +-spec ldap_dn_filter(global | binary()) -> {binary(),[binary()]}. +ldap_dn_filter(Host) -> + ejabberd_config:get_option({ldap_dn_filter, Host}). + +-spec ldap_encrypt() -> 'none' | 'starttls' | 'tls'. +ldap_encrypt() -> + ldap_encrypt(global). +-spec ldap_encrypt(global | binary()) -> 'none' | 'starttls' | 'tls'. +ldap_encrypt(Host) -> + ejabberd_config:get_option({ldap_encrypt, Host}). + +-spec ldap_filter() -> binary(). +ldap_filter() -> + ldap_filter(global). +-spec ldap_filter(global | binary()) -> binary(). +ldap_filter(Host) -> + ejabberd_config:get_option({ldap_filter, Host}). + +-spec ldap_password() -> binary(). +ldap_password() -> + ldap_password(global). +-spec ldap_password(global | binary()) -> binary(). +ldap_password(Host) -> + ejabberd_config:get_option({ldap_password, Host}). + +-spec ldap_port() -> 1..1114111. +ldap_port() -> + ldap_port(global). +-spec ldap_port(global | binary()) -> 1..1114111. +ldap_port(Host) -> + ejabberd_config:get_option({ldap_port, Host}). + +-spec ldap_rootdn() -> binary(). +ldap_rootdn() -> + ldap_rootdn(global). +-spec ldap_rootdn(global | binary()) -> binary(). +ldap_rootdn(Host) -> + ejabberd_config:get_option({ldap_rootdn, Host}). + +-spec ldap_servers() -> [binary()]. +ldap_servers() -> + ldap_servers(global). +-spec ldap_servers(global | binary()) -> [binary()]. +ldap_servers(Host) -> + ejabberd_config:get_option({ldap_servers, Host}). + +-spec ldap_tls_cacertfile() -> 'undefined' | binary(). +ldap_tls_cacertfile() -> + ldap_tls_cacertfile(global). +-spec ldap_tls_cacertfile(global | binary()) -> 'undefined' | binary(). +ldap_tls_cacertfile(Host) -> + ejabberd_config:get_option({ldap_tls_cacertfile, Host}). + +-spec ldap_tls_certfile() -> 'undefined' | binary(). +ldap_tls_certfile() -> + ldap_tls_certfile(global). +-spec ldap_tls_certfile(global | binary()) -> 'undefined' | binary(). +ldap_tls_certfile(Host) -> + ejabberd_config:get_option({ldap_tls_certfile, Host}). + +-spec ldap_tls_depth() -> 'undefined' | non_neg_integer(). +ldap_tls_depth() -> + ldap_tls_depth(global). +-spec ldap_tls_depth(global | binary()) -> 'undefined' | non_neg_integer(). +ldap_tls_depth(Host) -> + ejabberd_config:get_option({ldap_tls_depth, Host}). + +-spec ldap_tls_verify() -> 'false' | 'hard' | 'soft'. +ldap_tls_verify() -> + ldap_tls_verify(global). +-spec ldap_tls_verify(global | binary()) -> 'false' | 'hard' | 'soft'. +ldap_tls_verify(Host) -> + ejabberd_config:get_option({ldap_tls_verify, Host}). + +-spec ldap_uids() -> [{binary(),binary()}]. +ldap_uids() -> + ldap_uids(global). +-spec ldap_uids(global | binary()) -> [{binary(),binary()}]. +ldap_uids(Host) -> + ejabberd_config:get_option({ldap_uids, Host}). + +-spec listen() -> [ejabberd_listener:listener()]. +listen() -> + ejabberd_config:get_option({listen, global}). + +-spec log_burst_limit_count() -> pos_integer(). +log_burst_limit_count() -> + ejabberd_config:get_option({log_burst_limit_count, global}). + +-spec log_burst_limit_window_time() -> pos_integer(). +log_burst_limit_window_time() -> + ejabberd_config:get_option({log_burst_limit_window_time, global}). + +-spec log_modules_fully() -> [atom()]. +log_modules_fully() -> + ejabberd_config:get_option({log_modules_fully, global}). + +-spec log_rotate_count() -> non_neg_integer(). +log_rotate_count() -> + ejabberd_config:get_option({log_rotate_count, global}). + +-spec log_rotate_size() -> 'infinity' | pos_integer(). +log_rotate_size() -> + ejabberd_config:get_option({log_rotate_size, global}). + +-spec loglevel() -> ejabberd_logger:loglevel(). +loglevel() -> + ejabberd_config:get_option({loglevel, global}). + +-spec max_fsm_queue() -> 'undefined' | pos_integer(). +max_fsm_queue() -> + max_fsm_queue(global). +-spec max_fsm_queue(global | binary()) -> 'undefined' | pos_integer(). +max_fsm_queue(Host) -> + ejabberd_config:get_option({max_fsm_queue, Host}). + +-spec modules() -> [{module(),gen_mod:opts(),integer()}]. +modules() -> + modules(global). +-spec modules(global | binary()) -> [{module(),gen_mod:opts(),integer()}]. +modules(Host) -> + ejabberd_config:get_option({modules, Host}). + +-spec negotiation_timeout() -> pos_integer(). +negotiation_timeout() -> + ejabberd_config:get_option({negotiation_timeout, global}). + +-spec net_ticktime() -> pos_integer(). +net_ticktime() -> + ejabberd_config:get_option({net_ticktime, global}). + +-spec new_sql_schema() -> boolean(). +new_sql_schema() -> + ejabberd_config:get_option({new_sql_schema, global}). + +-spec oauth_access() -> 'none' | acl:acl(). +oauth_access() -> + oauth_access(global). +-spec oauth_access(global | binary()) -> 'none' | acl:acl(). +oauth_access(Host) -> + ejabberd_config:get_option({oauth_access, Host}). + +-spec oauth_cache_life_time() -> 'infinity' | pos_integer(). +oauth_cache_life_time() -> + ejabberd_config:get_option({oauth_cache_life_time, global}). + +-spec oauth_cache_missed() -> boolean(). +oauth_cache_missed() -> + ejabberd_config:get_option({oauth_cache_missed, global}). + +-spec oauth_cache_rest_failure_life_time() -> 'infinity' | pos_integer(). +oauth_cache_rest_failure_life_time() -> + ejabberd_config:get_option({oauth_cache_rest_failure_life_time, global}). + +-spec oauth_cache_size() -> 'infinity' | pos_integer(). +oauth_cache_size() -> + ejabberd_config:get_option({oauth_cache_size, global}). + +-spec oauth_client_id_check() -> 'allow' | 'db' | 'deny'. +oauth_client_id_check() -> + oauth_client_id_check(global). +-spec oauth_client_id_check(global | binary()) -> 'allow' | 'db' | 'deny'. +oauth_client_id_check(Host) -> + ejabberd_config:get_option({oauth_client_id_check, Host}). + +-spec oauth_db_type() -> atom(). +oauth_db_type() -> + ejabberd_config:get_option({oauth_db_type, global}). + +-spec oauth_expire() -> pos_integer(). +oauth_expire() -> + ejabberd_config:get_option({oauth_expire, global}). + +-spec oauth_use_cache() -> boolean(). +oauth_use_cache() -> + ejabberd_config:get_option({oauth_use_cache, global}). + +-spec oom_killer() -> boolean(). +oom_killer() -> + ejabberd_config:get_option({oom_killer, global}). + +-spec oom_queue() -> pos_integer(). +oom_queue() -> + ejabberd_config:get_option({oom_queue, global}). + +-spec oom_watermark() -> 1..255. +oom_watermark() -> + ejabberd_config:get_option({oom_watermark, global}). + +-spec outgoing_s2s_families() -> ['inet' | 'inet6',...]. +outgoing_s2s_families() -> + outgoing_s2s_families(global). +-spec outgoing_s2s_families(global | binary()) -> ['inet' | 'inet6',...]. +outgoing_s2s_families(Host) -> + ejabberd_config:get_option({outgoing_s2s_families, Host}). + +-spec outgoing_s2s_ipv4_address() -> 'undefined' | inet:ip4_address(). +outgoing_s2s_ipv4_address() -> + outgoing_s2s_ipv4_address(global). +-spec outgoing_s2s_ipv4_address(global | binary()) -> 'undefined' | inet:ip4_address(). +outgoing_s2s_ipv4_address(Host) -> + ejabberd_config:get_option({outgoing_s2s_ipv4_address, Host}). + +-spec outgoing_s2s_ipv6_address() -> 'undefined' | inet:ip6_address(). +outgoing_s2s_ipv6_address() -> + outgoing_s2s_ipv6_address(global). +-spec outgoing_s2s_ipv6_address(global | binary()) -> 'undefined' | inet:ip6_address(). +outgoing_s2s_ipv6_address(Host) -> + ejabberd_config:get_option({outgoing_s2s_ipv6_address, Host}). + +-spec outgoing_s2s_port() -> 1..1114111. +outgoing_s2s_port() -> + outgoing_s2s_port(global). +-spec outgoing_s2s_port(global | binary()) -> 1..1114111. +outgoing_s2s_port(Host) -> + ejabberd_config:get_option({outgoing_s2s_port, Host}). + +-spec outgoing_s2s_timeout() -> 'infinity' | pos_integer(). +outgoing_s2s_timeout() -> + outgoing_s2s_timeout(global). +-spec outgoing_s2s_timeout(global | binary()) -> 'infinity' | pos_integer(). +outgoing_s2s_timeout(Host) -> + ejabberd_config:get_option({outgoing_s2s_timeout, Host}). + +-spec pam_service() -> binary(). +pam_service() -> + pam_service(global). +-spec pam_service(global | binary()) -> binary(). +pam_service(Host) -> + ejabberd_config:get_option({pam_service, Host}). + +-spec pam_userinfotype() -> 'jid' | 'username'. +pam_userinfotype() -> + pam_userinfotype(global). +-spec pam_userinfotype(global | binary()) -> 'jid' | 'username'. +pam_userinfotype(Host) -> + ejabberd_config:get_option({pam_userinfotype, Host}). + +-spec pgsql_users_number_estimate() -> boolean(). +pgsql_users_number_estimate() -> + pgsql_users_number_estimate(global). +-spec pgsql_users_number_estimate(global | binary()) -> boolean(). +pgsql_users_number_estimate(Host) -> + ejabberd_config:get_option({pgsql_users_number_estimate, Host}). + +-spec queue_dir() -> 'undefined' | binary(). +queue_dir() -> + ejabberd_config:get_option({queue_dir, global}). + +-spec queue_type() -> 'file' | 'ram'. +queue_type() -> + queue_type(global). +-spec queue_type(global | binary()) -> 'file' | 'ram'. +queue_type(Host) -> + ejabberd_config:get_option({queue_type, Host}). + +-spec redis_connect_timeout() -> pos_integer(). +redis_connect_timeout() -> + ejabberd_config:get_option({redis_connect_timeout, global}). + +-spec redis_db() -> non_neg_integer(). +redis_db() -> + ejabberd_config:get_option({redis_db, global}). + +-spec redis_password() -> string(). +redis_password() -> + ejabberd_config:get_option({redis_password, global}). + +-spec redis_pool_size() -> pos_integer(). +redis_pool_size() -> + ejabberd_config:get_option({redis_pool_size, global}). + +-spec redis_port() -> 1..1114111. +redis_port() -> + ejabberd_config:get_option({redis_port, global}). + +-spec redis_queue_type() -> 'file' | 'ram'. +redis_queue_type() -> + ejabberd_config:get_option({redis_queue_type, global}). + +-spec redis_server() -> string(). +redis_server() -> + ejabberd_config:get_option({redis_server, global}). + +-spec registration_timeout() -> 'infinity' | pos_integer(). +registration_timeout() -> + ejabberd_config:get_option({registration_timeout, global}). + +-spec resource_conflict() -> 'acceptnew' | 'closenew' | 'closeold' | 'setresource'. +resource_conflict() -> + resource_conflict(global). +-spec resource_conflict(global | binary()) -> 'acceptnew' | 'closenew' | 'closeold' | 'setresource'. +resource_conflict(Host) -> + ejabberd_config:get_option({resource_conflict, Host}). + +-spec rest_proxy() -> binary(). +rest_proxy() -> + rest_proxy(global). +-spec rest_proxy(global | binary()) -> binary(). +rest_proxy(Host) -> + ejabberd_config:get_option({rest_proxy, Host}). + +-spec rest_proxy_password() -> string(). +rest_proxy_password() -> + rest_proxy_password(global). +-spec rest_proxy_password(global | binary()) -> string(). +rest_proxy_password(Host) -> + ejabberd_config:get_option({rest_proxy_password, Host}). + +-spec rest_proxy_port() -> char(). +rest_proxy_port() -> + rest_proxy_port(global). +-spec rest_proxy_port(global | binary()) -> char(). +rest_proxy_port(Host) -> + ejabberd_config:get_option({rest_proxy_port, Host}). + +-spec rest_proxy_username() -> string(). +rest_proxy_username() -> + rest_proxy_username(global). +-spec rest_proxy_username(global | binary()) -> string(). +rest_proxy_username(Host) -> + ejabberd_config:get_option({rest_proxy_username, Host}). + +-spec router_cache_life_time() -> 'infinity' | pos_integer(). +router_cache_life_time() -> + ejabberd_config:get_option({router_cache_life_time, global}). + +-spec router_cache_missed() -> boolean(). +router_cache_missed() -> + ejabberd_config:get_option({router_cache_missed, global}). + +-spec router_cache_size() -> 'infinity' | pos_integer(). +router_cache_size() -> + ejabberd_config:get_option({router_cache_size, global}). + +-spec router_db_type() -> atom(). +router_db_type() -> + ejabberd_config:get_option({router_db_type, global}). + +-spec router_use_cache() -> boolean(). +router_use_cache() -> + ejabberd_config:get_option({router_use_cache, global}). + +-spec rpc_timeout() -> pos_integer(). +rpc_timeout() -> + ejabberd_config:get_option({rpc_timeout, global}). + +-spec s2s_access() -> 'all' | acl:acl(). +s2s_access() -> + s2s_access(global). +-spec s2s_access(global | binary()) -> 'all' | acl:acl(). +s2s_access(Host) -> + ejabberd_config:get_option({s2s_access, Host}). + +-spec s2s_cafile() -> 'undefined' | binary(). +s2s_cafile() -> + s2s_cafile(global). +-spec s2s_cafile(global | binary()) -> 'undefined' | binary(). +s2s_cafile(Host) -> + ejabberd_config:get_option({s2s_cafile, Host}). + +-spec s2s_ciphers() -> 'undefined' | binary(). +s2s_ciphers() -> + s2s_ciphers(global). +-spec s2s_ciphers(global | binary()) -> 'undefined' | binary(). +s2s_ciphers(Host) -> + ejabberd_config:get_option({s2s_ciphers, Host}). + +-spec s2s_dhfile() -> 'undefined' | binary(). +s2s_dhfile() -> + s2s_dhfile(global). +-spec s2s_dhfile(global | binary()) -> 'undefined' | binary(). +s2s_dhfile(Host) -> + ejabberd_config:get_option({s2s_dhfile, Host}). + +-spec s2s_dns_retries() -> non_neg_integer(). +s2s_dns_retries() -> + s2s_dns_retries(global). +-spec s2s_dns_retries(global | binary()) -> non_neg_integer(). +s2s_dns_retries(Host) -> + ejabberd_config:get_option({s2s_dns_retries, Host}). + +-spec s2s_dns_timeout() -> 'infinity' | pos_integer(). +s2s_dns_timeout() -> + s2s_dns_timeout(global). +-spec s2s_dns_timeout(global | binary()) -> 'infinity' | pos_integer(). +s2s_dns_timeout(Host) -> + ejabberd_config:get_option({s2s_dns_timeout, Host}). + +-spec s2s_max_retry_delay() -> pos_integer(). +s2s_max_retry_delay() -> + ejabberd_config:get_option({s2s_max_retry_delay, global}). + +-spec s2s_protocol_options() -> 'undefined' | binary(). +s2s_protocol_options() -> + s2s_protocol_options(global). +-spec s2s_protocol_options(global | binary()) -> 'undefined' | binary(). +s2s_protocol_options(Host) -> + ejabberd_config:get_option({s2s_protocol_options, Host}). + +-spec s2s_queue_type() -> 'file' | 'ram'. +s2s_queue_type() -> + s2s_queue_type(global). +-spec s2s_queue_type(global | binary()) -> 'file' | 'ram'. +s2s_queue_type(Host) -> + ejabberd_config:get_option({s2s_queue_type, Host}). + +-spec s2s_timeout() -> 'infinity' | pos_integer(). +s2s_timeout() -> + s2s_timeout(global). +-spec s2s_timeout(global | binary()) -> 'infinity' | pos_integer(). +s2s_timeout(Host) -> + ejabberd_config:get_option({s2s_timeout, Host}). + +-spec s2s_tls_compression() -> 'false' | 'true' | 'undefined'. +s2s_tls_compression() -> + s2s_tls_compression(global). +-spec s2s_tls_compression(global | binary()) -> 'false' | 'true' | 'undefined'. +s2s_tls_compression(Host) -> + ejabberd_config:get_option({s2s_tls_compression, Host}). + +-spec s2s_use_starttls() -> 'false' | 'optional' | 'required' | 'true'. +s2s_use_starttls() -> + s2s_use_starttls(global). +-spec s2s_use_starttls(global | binary()) -> 'false' | 'optional' | 'required' | 'true'. +s2s_use_starttls(Host) -> + ejabberd_config:get_option({s2s_use_starttls, Host}). + +-spec s2s_zlib() -> boolean(). +s2s_zlib() -> + s2s_zlib(global). +-spec s2s_zlib(global | binary()) -> boolean(). +s2s_zlib(Host) -> + ejabberd_config:get_option({s2s_zlib, Host}). + +-spec shaper() -> #{atom()=>ejabberd_shaper:shaper_rate()}. +shaper() -> + ejabberd_config:get_option({shaper, global}). + +-spec shaper_rules() -> [{atom(),[ejabberd_shaper:shaper_rule()]}]. +shaper_rules() -> + shaper_rules(global). +-spec shaper_rules(global | binary()) -> [{atom(),[ejabberd_shaper:shaper_rule()]}]. +shaper_rules(Host) -> + ejabberd_config:get_option({shaper_rules, Host}). + +-spec sm_cache_life_time() -> 'infinity' | pos_integer(). +sm_cache_life_time() -> + ejabberd_config:get_option({sm_cache_life_time, global}). + +-spec sm_cache_missed() -> boolean(). +sm_cache_missed() -> + ejabberd_config:get_option({sm_cache_missed, global}). + +-spec sm_cache_size() -> 'infinity' | pos_integer(). +sm_cache_size() -> + ejabberd_config:get_option({sm_cache_size, global}). + +-spec sm_db_type() -> atom(). +sm_db_type() -> + sm_db_type(global). +-spec sm_db_type(global | binary()) -> atom(). +sm_db_type(Host) -> + ejabberd_config:get_option({sm_db_type, Host}). + +-spec sm_use_cache() -> boolean(). +sm_use_cache() -> + sm_use_cache(global). +-spec sm_use_cache(global | binary()) -> boolean(). +sm_use_cache(Host) -> + ejabberd_config:get_option({sm_use_cache, Host}). + +-spec sql_connect_timeout() -> pos_integer(). +sql_connect_timeout() -> + sql_connect_timeout(global). +-spec sql_connect_timeout(global | binary()) -> pos_integer(). +sql_connect_timeout(Host) -> + ejabberd_config:get_option({sql_connect_timeout, Host}). + +-spec sql_database() -> 'undefined' | binary(). +sql_database() -> + sql_database(global). +-spec sql_database(global | binary()) -> 'undefined' | binary(). +sql_database(Host) -> + ejabberd_config:get_option({sql_database, Host}). + +-spec sql_flags() -> ['mysql_alternative_upsert']. +sql_flags() -> + sql_flags(global). +-spec sql_flags(global | binary()) -> ['mysql_alternative_upsert']. +sql_flags(Host) -> + ejabberd_config:get_option({sql_flags, Host}). + +-spec sql_keepalive_interval() -> 'undefined' | pos_integer(). +sql_keepalive_interval() -> + sql_keepalive_interval(global). +-spec sql_keepalive_interval(global | binary()) -> 'undefined' | pos_integer(). +sql_keepalive_interval(Host) -> + ejabberd_config:get_option({sql_keepalive_interval, Host}). + +-spec sql_odbc_driver() -> binary(). +sql_odbc_driver() -> + sql_odbc_driver(global). +-spec sql_odbc_driver(global | binary()) -> binary(). +sql_odbc_driver(Host) -> + ejabberd_config:get_option({sql_odbc_driver, Host}). + +-spec sql_password() -> binary(). +sql_password() -> + sql_password(global). +-spec sql_password(global | binary()) -> binary(). +sql_password(Host) -> + ejabberd_config:get_option({sql_password, Host}). + +-spec sql_pool_size() -> pos_integer(). +sql_pool_size() -> + sql_pool_size(global). +-spec sql_pool_size(global | binary()) -> pos_integer(). +sql_pool_size(Host) -> + ejabberd_config:get_option({sql_pool_size, Host}). + +-spec sql_port() -> 1..1114111. +sql_port() -> + sql_port(global). +-spec sql_port(global | binary()) -> 1..1114111. +sql_port(Host) -> + ejabberd_config:get_option({sql_port, Host}). + +-spec sql_prepared_statements() -> boolean(). +sql_prepared_statements() -> + sql_prepared_statements(global). +-spec sql_prepared_statements(global | binary()) -> boolean(). +sql_prepared_statements(Host) -> + ejabberd_config:get_option({sql_prepared_statements, Host}). + +-spec sql_query_timeout() -> pos_integer(). +sql_query_timeout() -> + sql_query_timeout(global). +-spec sql_query_timeout(global | binary()) -> pos_integer(). +sql_query_timeout(Host) -> + ejabberd_config:get_option({sql_query_timeout, Host}). + +-spec sql_queue_type() -> 'file' | 'ram'. +sql_queue_type() -> + sql_queue_type(global). +-spec sql_queue_type(global | binary()) -> 'file' | 'ram'. +sql_queue_type(Host) -> + ejabberd_config:get_option({sql_queue_type, Host}). + +-spec sql_server() -> binary(). +sql_server() -> + sql_server(global). +-spec sql_server(global | binary()) -> binary(). +sql_server(Host) -> + ejabberd_config:get_option({sql_server, Host}). + +-spec sql_ssl() -> boolean(). +sql_ssl() -> + sql_ssl(global). +-spec sql_ssl(global | binary()) -> boolean(). +sql_ssl(Host) -> + ejabberd_config:get_option({sql_ssl, Host}). + +-spec sql_ssl_cafile() -> 'undefined' | binary(). +sql_ssl_cafile() -> + sql_ssl_cafile(global). +-spec sql_ssl_cafile(global | binary()) -> 'undefined' | binary(). +sql_ssl_cafile(Host) -> + ejabberd_config:get_option({sql_ssl_cafile, Host}). + +-spec sql_ssl_certfile() -> 'undefined' | binary(). +sql_ssl_certfile() -> + sql_ssl_certfile(global). +-spec sql_ssl_certfile(global | binary()) -> 'undefined' | binary(). +sql_ssl_certfile(Host) -> + ejabberd_config:get_option({sql_ssl_certfile, Host}). + +-spec sql_ssl_verify() -> boolean(). +sql_ssl_verify() -> + sql_ssl_verify(global). +-spec sql_ssl_verify(global | binary()) -> boolean(). +sql_ssl_verify(Host) -> + ejabberd_config:get_option({sql_ssl_verify, Host}). + +-spec sql_start_interval() -> pos_integer(). +sql_start_interval() -> + sql_start_interval(global). +-spec sql_start_interval(global | binary()) -> pos_integer(). +sql_start_interval(Host) -> + ejabberd_config:get_option({sql_start_interval, Host}). + +-spec sql_type() -> 'mssql' | 'mysql' | 'odbc' | 'pgsql' | 'sqlite'. +sql_type() -> + sql_type(global). +-spec sql_type(global | binary()) -> 'mssql' | 'mysql' | 'odbc' | 'pgsql' | 'sqlite'. +sql_type(Host) -> + ejabberd_config:get_option({sql_type, Host}). + +-spec sql_username() -> binary(). +sql_username() -> + sql_username(global). +-spec sql_username(global | binary()) -> binary(). +sql_username(Host) -> + ejabberd_config:get_option({sql_username, Host}). + +-spec trusted_proxies() -> 'all' | [{inet:ip4_address() | inet:ip6_address(),byte()}]. +trusted_proxies() -> + ejabberd_config:get_option({trusted_proxies, global}). + +-spec update_sql_schema() -> boolean(). +update_sql_schema() -> + ejabberd_config:get_option({update_sql_schema, global}). + +-spec update_sql_schema_timeout() -> 'infinity' | pos_integer(). +update_sql_schema_timeout() -> + update_sql_schema_timeout(global). +-spec update_sql_schema_timeout(global | binary()) -> 'infinity' | pos_integer(). +update_sql_schema_timeout(Host) -> + ejabberd_config:get_option({update_sql_schema_timeout, Host}). + +-spec use_cache() -> boolean(). +use_cache() -> + use_cache(global). +-spec use_cache(global | binary()) -> boolean(). +use_cache(Host) -> + ejabberd_config:get_option({use_cache, Host}). + +-spec validate_stream() -> boolean(). +validate_stream() -> + ejabberd_config:get_option({validate_stream, global}). + +-spec version() -> binary(). +version() -> + ejabberd_config:get_option({version, global}). + +-spec websocket_origin() -> [binary()]. +websocket_origin() -> + ejabberd_config:get_option({websocket_origin, global}). + +-spec websocket_ping_interval() -> pos_integer(). +websocket_ping_interval() -> + ejabberd_config:get_option({websocket_ping_interval, global}). + +-spec websocket_timeout() -> pos_integer(). +websocket_timeout() -> + ejabberd_config:get_option({websocket_timeout, global}). + diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl new file mode 100644 index 000000000..609d75b93 --- /dev/null +++ b/src/ejabberd_options.erl @@ -0,0 +1,865 @@ +%%%---------------------------------------------------------------------- +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- +-module(ejabberd_options). +-behaviour(ejabberd_config). + +-export([opt_type/1, options/0, globals/0, doc/0]). + +-ifdef(NEW_SQL_SCHEMA). +-define(USE_NEW_SQL_SCHEMA_DEFAULT, true). +-else. +-define(USE_NEW_SQL_SCHEMA_DEFAULT, false). +-endif. + +-include_lib("kernel/include/inet.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +-spec opt_type(atom()) -> econf:validator(). +opt_type(access_rules) -> + acl:validator(access_rules); +opt_type(acl) -> + acl:validator(acl); +opt_type(acme) -> + econf:options( + #{ca_url => econf:url(), + contact => econf:list_or_single(econf:binary("^[a-zA-Z]+:[^:]+$")), + auto => econf:bool(), + cert_type => econf:enum([ec, rsa])}, + [unique, {return, map}]); +opt_type(allow_contrib_modules) -> + econf:bool(); +opt_type(allow_multiple_connections) -> + econf:bool(); +opt_type(anonymous_protocol) -> + econf:enum([sasl_anon, login_anon, both]); +opt_type(api_permissions) -> + ejabberd_access_permissions:validator(); +opt_type(append_host_config) -> + opt_type(host_config); +opt_type(auth_cache_life_time) -> + econf:timeout(second, infinity); +opt_type(auth_cache_missed) -> + econf:bool(); +opt_type(auth_cache_size) -> + econf:pos_int(infinity); +opt_type(auth_method) -> + econf:list_or_single(econf:db_type(ejabberd_auth)); +opt_type(auth_opts) -> + fun(L) when is_list(L) -> + lists:map( + fun({host, V}) when is_binary(V) -> + {host, V}; + ({connection_pool_size, V}) when is_integer(V) -> + {connection_pool_size, V}; + ({connection_opts, V}) when is_list(V) -> + {connection_opts, V}; + ({basic_auth, V}) when is_binary(V) -> + {basic_auth, V}; + ({path_prefix, V}) when is_binary(V) -> + {path_prefix, V} + end, L) + end; +opt_type(auth_stored_password_types) -> + econf:list(econf:enum([plain, scram_sha1, scram_sha256, scram_sha512])); +opt_type(auth_password_types_hidden_in_sasl1) -> + econf:list(econf:enum([plain, scram_sha1, scram_sha256, scram_sha512])); +opt_type(auth_password_format) -> + econf:enum([plain, scram]); +opt_type(auth_scram_hash) -> + econf:enum([sha, sha256, sha512]); +opt_type(auth_external_user_exists_check) -> + econf:bool(); +opt_type(auth_use_cache) -> + econf:bool(); +opt_type(c2s_cafile) -> + econf:file(); +opt_type(c2s_ciphers) -> + fun(L) when is_list(L) -> + (econf:and_then( + econf:list(econf:binary(), [unique]), + concat_binary($:)))(L); + (B) -> + (econf:binary())(B) + end; +opt_type(c2s_dhfile) -> + econf:file(); +opt_type(c2s_protocol_options) -> + econf:and_then( + econf:list(econf:binary(), [unique]), + concat_binary($|)); +opt_type(c2s_tls_compression) -> + econf:bool(); +opt_type(ca_file) -> + econf:pem(); +opt_type(cache_life_time) -> + econf:timeout(second, infinity); +opt_type(cache_missed) -> + econf:bool(); +opt_type(cache_size) -> + econf:pos_int(infinity); +opt_type(captcha_cmd) -> + econf:binary(); +opt_type(captcha_host) -> + econf:binary(); +opt_type(captcha_limit) -> + econf:pos_int(infinity); +opt_type(captcha_url) -> + econf:either( + econf:url(), + econf:enum([auto, undefined])); +opt_type(certfiles) -> + econf:list(econf:binary()); +opt_type(cluster_backend) -> + econf:db_type(ejabberd_cluster); +opt_type(cluster_nodes) -> + econf:list(econf:atom(), [unique]); +opt_type(default_db) -> + econf:enum([mnesia, sql]); +opt_type(default_ram_db) -> + econf:enum([mnesia, sql, redis]); +opt_type(define_keyword) -> + econf:map(econf:binary(), econf:any(), [unique]); +opt_type(define_macro) -> + econf:map(econf:binary(), econf:any(), [unique]); +opt_type(disable_sasl_scram_downgrade_protection) -> + econf:bool(); +opt_type(disable_sasl_mechanisms) -> + econf:list_or_single( + econf:and_then( + econf:binary(), + fun str:to_upper/1)); +opt_type(domain_balancing) -> + econf:map( + econf:domain(), + econf:options( + #{component_number => econf:int(2, 1000), + type => econf:enum([random, source, destination, + bare_source, bare_destination])}, + [{return, map}, unique]), + [{return, map}]); +opt_type(ext_api_path_oauth) -> + econf:binary(); +opt_type(ext_api_http_pool_size) -> + econf:pos_int(); +opt_type(ext_api_url) -> + econf:url(); +opt_type(ext_api_headers) -> + econf:binary(); +opt_type(extauth_pool_name) -> + econf:binary(); +opt_type(extauth_pool_size) -> + econf:pos_int(); +opt_type(extauth_program) -> + econf:string(); +opt_type(fqdn) -> + econf:list_or_single(econf:domain()); +opt_type(hide_sensitive_log_data) -> + econf:bool(); +opt_type(host_config) -> + econf:and_then( + econf:and_then( + econf:map(econf:domain(), econf:list(econf:any())), + fun econf:group_dups/1), + econf:map( + econf:enum(ejabberd_config:get_option(hosts)), + validator(), + [unique])); +opt_type(hosts) -> + econf:non_empty(econf:list(econf:domain(), [unique])); +opt_type(hosts_alias) -> + econf:and_then( + econf:map(econf:domain(), econf:domain(), [unique]), + econf:map( + econf:domain(), + econf:enum(ejabberd_config:get_option(hosts)), + [unique])); +opt_type(include_config_file) -> + econf:any(); +opt_type(install_contrib_modules) -> + econf:list(econf:atom()); +opt_type(language) -> + econf:lang(); +opt_type(ldap_backups) -> + econf:list(econf:domain(), [unique]); +opt_type(ldap_base) -> + econf:binary(); +opt_type(ldap_deref_aliases) -> + econf:enum([never, searching, finding, always]); +opt_type(ldap_dn_filter) -> + econf:and_then( + econf:non_empty( + econf:map( + econf:ldap_filter(), + econf:list(econf:binary()))), + fun hd/1); +opt_type(ldap_encrypt) -> + econf:enum([tls, starttls, none]); +opt_type(ldap_filter) -> + econf:ldap_filter(); +opt_type(ldap_password) -> + econf:binary(); +opt_type(ldap_port) -> + econf:port(); +opt_type(ldap_rootdn) -> + econf:binary(); +opt_type(ldap_servers) -> + econf:list(econf:domain(), [unique]); +opt_type(ldap_tls_cacertfile) -> + econf:pem(); +opt_type(ldap_tls_certfile) -> + econf:pem(); +opt_type(ldap_tls_depth) -> + econf:non_neg_int(); +opt_type(ldap_tls_verify) -> + econf:enum([hard, soft, false]); +opt_type(ldap_uids) -> + econf:either( + econf:list( + econf:and_then( + econf:binary(), + fun(U) -> {U, <<"%u">>} end)), + econf:map(econf:binary(), econf:binary(), [unique])); +opt_type(listen) -> + ejabberd_listener:validator(); +opt_type(log_rotate_count) -> + econf:non_neg_int(); +opt_type(log_rotate_size) -> + econf:pos_int(infinity); +opt_type(log_burst_limit_window_time) -> + econf:timeout(second); +opt_type(log_burst_limit_count) -> + econf:pos_int(); +opt_type(log_modules_fully) -> + econf:list(econf:atom()); +opt_type(loglevel) -> + fun(N) when is_integer(N) -> + (econf:and_then( + econf:int(0, 5), + fun ejabberd_logger:convert_loglevel/1))(N); + (Level) -> + (econf:enum([none, emergency, alert, critical, + error, warning, notice, info, debug]))(Level) + end; +opt_type(max_fsm_queue) -> + econf:pos_int(); +opt_type(modules) -> + econf:map(econf:atom(), econf:any()); +opt_type(negotiation_timeout) -> + econf:timeout(second); +opt_type(net_ticktime) -> + econf:timeout(second); +opt_type(new_sql_schema) -> + econf:bool(); +opt_type(update_sql_schema) -> + econf:bool(); +opt_type(update_sql_schema_timeout) -> + econf:timeout(second, infinity); +opt_type(oauth_access) -> + econf:acl(); +opt_type(oauth_cache_life_time) -> + econf:timeout(second, infinity); +opt_type(oauth_cache_missed) -> + econf:bool(); +opt_type(oauth_cache_rest_failure_life_time) -> + econf:timeout(second, infinity); +opt_type(oauth_cache_size) -> + econf:pos_int(infinity); +opt_type(oauth_db_type) -> + econf:db_type(ejabberd_oauth); +opt_type(oauth_expire) -> + econf:timeout(second); +opt_type(oauth_use_cache) -> + econf:bool(); +opt_type(oauth_client_id_check) -> + econf:enum([allow, deny, db]); +opt_type(oom_killer) -> + econf:bool(); +opt_type(oom_queue) -> + econf:pos_int(); +opt_type(oom_watermark) -> + econf:int(1, 99); +opt_type(outgoing_s2s_families) -> + econf:and_then( + econf:non_empty( + econf:list(econf:enum([ipv4, ipv6]), [unique])), + fun(L) -> + lists:map( + fun(ipv4) -> inet; + (ipv6) -> inet6 + end, L) + end); +opt_type(outgoing_s2s_ipv4_address) -> + econf:ipv4(); +opt_type(outgoing_s2s_ipv6_address) -> + econf:ipv6(); +opt_type(outgoing_s2s_port) -> + econf:port(); +opt_type(outgoing_s2s_timeout) -> + econf:timeout(second, infinity); +opt_type(pam_service) -> + econf:binary(); +opt_type(pam_userinfotype) -> + econf:enum([username, jid]); +opt_type(pgsql_users_number_estimate) -> + econf:bool(); +opt_type(queue_dir) -> + econf:directory(write); +opt_type(queue_type) -> + econf:enum([ram, file]); +opt_type(redis_connect_timeout) -> + econf:timeout(second); +opt_type(redis_db) -> + econf:non_neg_int(); +opt_type(redis_password) -> + econf:string(); +opt_type(redis_pool_size) -> + econf:pos_int(); +opt_type(redis_port) -> + econf:port(); +opt_type(redis_queue_type) -> + econf:enum([ram, file]); +opt_type(redis_server) -> + econf:string(); +opt_type(registration_timeout) -> + econf:timeout(second, infinity); +opt_type(resource_conflict) -> + econf:enum([setresource, closeold, closenew, acceptnew]); +opt_type(rest_proxy) -> + econf:domain(); +opt_type(rest_proxy_port) -> + econf:port(); +opt_type(rest_proxy_username) -> + econf:string(); +opt_type(rest_proxy_password) -> + econf:string(); +opt_type(router_cache_life_time) -> + econf:timeout(second, infinity); +opt_type(router_cache_missed) -> + econf:bool(); +opt_type(router_cache_size) -> + econf:pos_int(infinity); +opt_type(router_db_type) -> + econf:db_type(ejabberd_router); +opt_type(router_use_cache) -> + econf:bool(); +opt_type(rpc_timeout) -> + econf:timeout(second); +opt_type(s2s_access) -> + econf:acl(); +opt_type(s2s_cafile) -> + econf:pem(); +opt_type(s2s_ciphers) -> + opt_type(c2s_ciphers); +opt_type(s2s_dhfile) -> + econf:file(); +opt_type(s2s_dns_retries) -> + econf:non_neg_int(); +opt_type(s2s_dns_timeout) -> + econf:timeout(second, infinity); +opt_type(s2s_max_retry_delay) -> + econf:timeout(second); +opt_type(s2s_protocol_options) -> + opt_type(c2s_protocol_options); +opt_type(s2s_queue_type) -> + econf:enum([ram, file]); +opt_type(s2s_timeout) -> + econf:timeout(second, infinity); +opt_type(s2s_tls_compression) -> + econf:bool(); +opt_type(s2s_use_starttls) -> + econf:either( + econf:bool(), + econf:enum([optional, required])); +opt_type(s2s_zlib) -> + econf:and_then( + econf:bool(), + fun(false) -> false; + (true) -> + ejabberd:start_app(ezlib), + true + end); +opt_type(shaper) -> + ejabberd_shaper:validator(shaper); +opt_type(shaper_rules) -> + ejabberd_shaper:validator(shaper_rules); +opt_type(sm_cache_life_time) -> + econf:timeout(second, infinity); +opt_type(sm_cache_missed) -> + econf:bool(); +opt_type(sm_cache_size) -> + econf:pos_int(infinity); +opt_type(sm_db_type) -> + econf:db_type(ejabberd_sm); +opt_type(sm_use_cache) -> + econf:bool(); +opt_type(sql_connect_timeout) -> + econf:timeout(second); +opt_type(sql_database) -> + econf:binary(); +opt_type(sql_keepalive_interval) -> + econf:timeout(second); +opt_type(sql_password) -> + econf:binary(); +opt_type(sql_odbc_driver) -> + econf:binary(); +opt_type(sql_pool_size) -> + econf:pos_int(); +opt_type(sql_port) -> + econf:port(); +opt_type(sql_query_timeout) -> + econf:timeout(second); +opt_type(sql_queue_type) -> + econf:enum([ram, file]); +opt_type(sql_server) -> + econf:binary(); +opt_type(sql_ssl) -> + econf:bool(); +opt_type(sql_ssl_cafile) -> + econf:pem(); +opt_type(sql_ssl_certfile) -> + econf:pem(); +opt_type(sql_ssl_verify) -> + econf:bool(); +opt_type(sql_start_interval) -> + econf:timeout(second); +opt_type(sql_type) -> + econf:enum([mysql, pgsql, sqlite, mssql, odbc]); +opt_type(sql_username) -> + econf:binary(); +opt_type(sql_prepared_statements) -> + econf:bool(); +opt_type(sql_flags) -> + econf:list_or_single(econf:enum([mysql_alternative_upsert]), [sorted, unique]); +opt_type(trusted_proxies) -> + econf:either(all, econf:list(econf:ip_mask())); +opt_type(use_cache) -> + econf:bool(); +opt_type(validate_stream) -> + econf:bool(); +opt_type(version) -> + econf:binary(); +opt_type(websocket_origin) -> + econf:list( + econf:and_then( + econf:and_then( + econf:binary_sep("\\s+"), + econf:list(econf:url(), [unique])), + fun(L) -> str:join(L, <<" ">>) end), + [unique]); +opt_type(websocket_ping_interval) -> + econf:timeout(second); +opt_type(websocket_timeout) -> + econf:timeout(second); +opt_type(jwt_key) -> + econf:and_then( + econf:path(), + fun(Path) -> + case file:read_file(Path) of + {ok, Data} -> + try jose_jwk:from_binary(Data) of + {error, _} -> econf:fail({bad_jwt_key, Path}); + JWK -> + case jose_jwk:to_map(JWK) of + {_, #{<<"keys">> := [Key]}} -> + jose_jwk:from_map(Key); + {_, #{<<"keys">> := [_|_]}} -> + econf:fail({bad_jwt_key_set, Path}); + {_, #{<<"keys">> := _}} -> + econf:fail({bad_jwt_key, Path}); + _ -> + JWK + end + catch _:_ -> + econf:fail({bad_jwt_key, Path}) + end; + {error, Reason} -> + econf:fail({read_file, Reason, Path}) + end + end); +opt_type(jwt_jid_field) -> + econf:binary(); +opt_type(jwt_auth_only_rule) -> + econf:atom(). + +%% We only define the types of options that cannot be derived +%% automatically by tools/opt_type.sh script +-spec options() -> [{s2s_protocol_options, undefined | binary()} | + {c2s_protocol_options, undefined | binary()} | + {s2s_ciphers, undefined | binary()} | + {c2s_ciphers, undefined | binary()} | + {websocket_origin, [binary()]} | + {disable_sasl_mechanisms, [binary()]} | + {s2s_zlib, boolean()} | + {loglevel, ejabberd_logger:loglevel()} | + {auth_opts, [{any(), any()}]} | + {listen, [ejabberd_listener:listener()]} | + {modules, [{module(), gen_mod:opts(), integer()}]} | + {ldap_uids, [{binary(), binary()}]} | + {ldap_dn_filter, {binary(), [binary()]}} | + {outgoing_s2s_families, [inet | inet6, ...]} | + {acl, [{atom(), [acl:acl_rule()]}]} | + {access_rules, [{atom(), acl:access()}]} | + {shaper, #{atom() => ejabberd_shaper:shaper_rate()}} | + {shaper_rules, [{atom(), [ejabberd_shaper:shaper_rule()]}]} | + {api_permissions, [ejabberd_access_permissions:permission()]} | + {jwt_key, jose_jwk:key() | undefined} | + {append_host_config, [{binary(), any()}]} | + {host_config, [{binary(), any()}]} | + {define_keyword, any()} | + {define_macro, any()} | + {include_config_file, any()} | + {atom(), any()}]. +options() -> + [%% Top-priority options + hosts, + {loglevel, info}, + {cache_life_time, timer:seconds(3600)}, + {cache_missed, true}, + {cache_size, 1000}, + {use_cache, true}, + {default_db, mnesia}, + {default_ram_db, mnesia}, + {queue_type, ram}, + {version, ejabberd_config:version()}, + %% Other options + {acl, []}, + {access_rules, []}, + {acme, #{}}, + {allow_contrib_modules, true}, + {install_contrib_modules, []}, + {allow_multiple_connections, false}, + {anonymous_protocol, sasl_anon}, + {api_permissions, + [{<<"admin access">>, + {[], + [{acl, admin}, + {oauth, {[<<"ejabberd:admin">>], [{acl, admin}]}}], + {all, [start, stop]}}}]}, + {append_host_config, []}, + {auth_cache_life_time, + fun(Host) -> ejabberd_config:get_option({cache_life_time, Host}) end}, + {auth_cache_missed, + fun(Host) -> ejabberd_config:get_option({cache_missed, Host}) end}, + {auth_cache_size, + fun(Host) -> ejabberd_config:get_option({cache_size, Host}) end}, + {auth_method, + fun(Host) -> [ejabberd_config:default_db(Host, ejabberd_auth)] end}, + {auth_opts, []}, + {auth_password_format, plain}, + {auth_scram_hash, sha}, + {auth_stored_password_types, []}, + {auth_password_types_hidden_in_sasl1, []}, + {auth_external_user_exists_check, true}, + {auth_use_cache, + fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end}, + {c2s_cafile, undefined}, + {c2s_ciphers, undefined}, + {c2s_dhfile, undefined}, + {c2s_protocol_options, undefined}, + {c2s_tls_compression, undefined}, + {ca_file, iolist_to_binary(pkix:get_cafile())}, + {captcha_cmd, undefined}, + {captcha_host, <<"">>}, + {captcha_limit, infinity}, + {captcha_url, auto}, + {certfiles, undefined}, + {cluster_backend, mnesia}, + {cluster_nodes, []}, + {define_keyword, []}, + {define_macro, []}, + {disable_sasl_scram_downgrade_protection, false}, + {disable_sasl_mechanisms, []}, + {domain_balancing, #{}}, + {ext_api_headers, <<>>}, + {ext_api_http_pool_size, 100}, + {ext_api_path_oauth, <<"/oauth">>}, + {ext_api_url, <<"http://localhost/api">>}, + {extauth_pool_name, undefined}, + {extauth_pool_size, undefined}, + {extauth_program, undefined}, + {fqdn, fun fqdn/1}, + {hide_sensitive_log_data, false}, + {hosts_alias, []}, + {host_config, []}, + {include_config_file, []}, + {language, <<"en">>}, + {ldap_backups, []}, + {ldap_base, <<"">>}, + {ldap_deref_aliases, never}, + {ldap_dn_filter, {undefined, []}}, + {ldap_encrypt, none}, + {ldap_filter, <<"">>}, + {ldap_password, <<"">>}, + {ldap_port, + fun(Host) -> + case ejabberd_config:get_option({ldap_encrypt, Host}) of + tls -> 636; + _ -> 389 + end + end}, + {ldap_rootdn, <<"">>}, + {ldap_servers, [<<"localhost">>]}, + {ldap_tls_cacertfile, undefined}, + {ldap_tls_certfile, undefined}, + {ldap_tls_depth, undefined}, + {ldap_tls_verify, false}, + {ldap_uids, [{<<"uid">>, <<"%u">>}]}, + {listen, []}, + {log_rotate_count, 1}, + {log_rotate_size, 10*1024*1024}, + {log_burst_limit_window_time, timer:seconds(1)}, + {log_burst_limit_count, 500}, + {log_modules_fully, []}, + {max_fsm_queue, undefined}, + {modules, []}, + {negotiation_timeout, timer:seconds(120)}, + {net_ticktime, timer:seconds(60)}, + {new_sql_schema, ?USE_NEW_SQL_SCHEMA_DEFAULT}, + {update_sql_schema, true}, + {update_sql_schema_timeout, timer:minutes(5)}, + {oauth_access, none}, + {oauth_cache_life_time, + fun(Host) -> ejabberd_config:get_option({cache_life_time, Host}) end}, + {oauth_cache_missed, + fun(Host) -> ejabberd_config:get_option({cache_missed, Host}) end}, + {oauth_cache_size, + fun(Host) -> ejabberd_config:get_option({cache_size, Host}) end}, + {oauth_cache_rest_failure_life_time, infinity}, + {oauth_db_type, + fun(Host) -> ejabberd_config:default_db(Host, ejabberd_oauth) end}, + {oauth_expire, 4294967}, + {oauth_use_cache, + fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end}, + {oauth_client_id_check, allow}, + {oom_killer, true}, + {oom_queue, 10000}, + {oom_watermark, 80}, + {outgoing_s2s_families, [inet6, inet]}, + {outgoing_s2s_ipv4_address, undefined}, + {outgoing_s2s_ipv6_address, undefined}, + {outgoing_s2s_port, 5269}, + {outgoing_s2s_timeout, timer:seconds(10)}, + {pam_service, <<"ejabberd">>}, + {pam_userinfotype, username}, + {pgsql_users_number_estimate, false}, + {queue_dir, undefined}, + {redis_connect_timeout, timer:seconds(1)}, + {redis_db, 0}, + {redis_password, ""}, + {redis_pool_size, 10}, + {redis_port, 6379}, + {redis_queue_type, + fun(Host) -> ejabberd_config:get_option({queue_type, Host}) end}, + {redis_server, "localhost"}, + {registration_timeout, timer:seconds(600)}, + {resource_conflict, acceptnew}, + {rest_proxy, <<>>}, + {rest_proxy_port, 0}, + {rest_proxy_username, ""}, + {rest_proxy_password, ""}, + {router_cache_life_time, + fun(Host) -> ejabberd_config:get_option({cache_life_time, Host}) end}, + {router_cache_missed, + fun(Host) -> ejabberd_config:get_option({cache_missed, Host}) end}, + {router_cache_size, + fun(Host) -> ejabberd_config:get_option({cache_size, Host}) end}, + {router_db_type, + fun(Host) -> ejabberd_config:default_ram_db(Host, ejabberd_router) end}, + {router_use_cache, + fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end}, + {rpc_timeout, timer:seconds(5)}, + {s2s_access, all}, + {s2s_cafile, undefined}, + {s2s_ciphers, undefined}, + {s2s_dhfile, undefined}, + {s2s_dns_retries, 2}, + {s2s_dns_timeout, timer:seconds(10)}, + {s2s_max_retry_delay, timer:seconds(300)}, + {s2s_protocol_options, undefined}, + {s2s_queue_type, + fun(Host) -> ejabberd_config:get_option({queue_type, Host}) end}, + {s2s_timeout, timer:hours(1)}, + {s2s_tls_compression, undefined}, + {s2s_use_starttls, false}, + {s2s_zlib, false}, + {shaper, #{}}, + {shaper_rules, []}, + {sm_cache_life_time, + fun(Host) -> ejabberd_config:get_option({cache_life_time, Host}) end}, + {sm_cache_missed, + fun(Host) -> ejabberd_config:get_option({cache_missed, Host}) end}, + {sm_cache_size, + fun(Host) -> ejabberd_config:get_option({cache_size, Host}) end}, + {sm_db_type, + fun(Host) -> ejabberd_config:default_ram_db(Host, ejabberd_sm) end}, + {sm_use_cache, + fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end}, + {sql_type, odbc}, + {sql_connect_timeout, timer:seconds(5)}, + {sql_database, undefined}, + {sql_keepalive_interval, undefined}, + {sql_password, <<"">>}, + {sql_odbc_driver, <<"libtdsodbc.so">>}, % default is FreeTDS driver + {sql_pool_size, + fun(Host) -> + case ejabberd_config:get_option({sql_type, Host}) of + sqlite -> 1; + _ -> 10 + end + end}, + {sql_port, + fun(Host) -> + case ejabberd_config:get_option({sql_type, Host}) of + mssql -> 1433; + mysql -> 3306; + pgsql -> 5432; + _ -> undefined + end + end}, + {sql_query_timeout, timer:seconds(60)}, + {sql_queue_type, + fun(Host) -> ejabberd_config:get_option({queue_type, Host}) end}, + {sql_server, <<"localhost">>}, + {sql_ssl, false}, + {sql_ssl_cafile, undefined}, + {sql_ssl_certfile, undefined}, + {sql_ssl_verify, false}, + {sql_start_interval, timer:seconds(30)}, + {sql_username, <<"ejabberd">>}, + {sql_prepared_statements, true}, + {sql_flags, []}, + {trusted_proxies, []}, + {validate_stream, false}, + {websocket_origin, []}, + {websocket_ping_interval, timer:seconds(60)}, + {websocket_timeout, timer:minutes(5)}, + {jwt_key, undefined}, + {jwt_jid_field, <<"jid">>}, + {jwt_auth_only_rule, none}]. + +-spec globals() -> [atom()]. +globals() -> + [acme, + allow_contrib_modules, + api_permissions, + append_host_config, + auth_cache_life_time, + auth_cache_missed, + auth_cache_size, + ca_file, + captcha_cmd, + captcha_host, + captcha_limit, + captcha_url, + certfiles, + cluster_backend, + cluster_nodes, + define_macro, + domain_balancing, + ext_api_path_oauth, + fqdn, + hosts, + hosts_alias, + host_config, + install_contrib_modules, + listen, + loglevel, + log_rotate_count, + log_rotate_size, + log_burst_limit_count, + log_burst_limit_window_time, + log_modules_fully, + negotiation_timeout, + net_ticktime, + new_sql_schema, + update_sql_schema, + node_start, + oauth_cache_life_time, + oauth_cache_missed, + oauth_cache_size, + oauth_cache_rest_failure_life_time, + oauth_db_type, + oauth_expire, + oauth_use_cache, + oom_killer, + oom_queue, + oom_watermark, + queue_dir, + redis_connect_timeout, + redis_db, + redis_password, + redis_pool_size, + redis_port, + redis_queue_type, + redis_server, + registration_timeout, + router_cache_life_time, + router_cache_missed, + router_cache_size, + router_db_type, + router_use_cache, + rpc_timeout, + s2s_max_retry_delay, + shaper, + sm_cache_life_time, + sm_cache_missed, + sm_cache_size, + trusted_proxies, + validate_stream, + version, + websocket_origin, + websocket_ping_interval, + websocket_timeout]. + +doc() -> + ejabberd_options_doc:doc(). + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +-spec validator() -> econf:validator(). +validator() -> + Disallowed = ejabberd_config:globals(), + {Validators, Required} = ejabberd_config:validators(Disallowed), + econf:and_then( + fun econf:group_dups/1, + econf:options( + Validators, + [{disallowed, Required ++ Disallowed}, unique])). + +-spec fqdn(global | binary()) -> [binary()]. +fqdn(global) -> + {ok, Hostname} = inet:gethostname(), + case inet:gethostbyname(Hostname) of + {ok, #hostent{h_name = FQDN}} -> + case jid:nameprep(iolist_to_binary(FQDN)) of + error -> []; + Domain -> [Domain] + end; + {error, _} -> + [] + end; +fqdn(_) -> + ejabberd_config:get_option(fqdn). + +-spec concat_binary(char()) -> fun(([binary()]) -> binary()). +concat_binary(C) -> + fun(Opts) -> str:join(Opts, <>) end. diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl new file mode 100644 index 000000000..56e2633c3 --- /dev/null +++ b/src/ejabberd_options_doc.erl @@ -0,0 +1,1618 @@ +%%%---------------------------------------------------------------------- +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- +-module(ejabberd_options_doc). + +%% API +-export([doc/0]). + +-include("translate.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +doc() -> + [{hosts, + #{value => ?T("[Domain1, Domain2, ...]"), + desc => + ?T("List of one or more " + "_`../configuration/basic.md#host-names|host names`_ " + "(or domains) that ejabberd will serve. This is a " + "**mandatory** option.")}}, + {listen, + #{value => "[Options, ...]", + desc => + ?T("The option for listeners configuration. See the " + "_`listen.md|Listen Modules`_ section " + "for details.")}}, + {modules, + #{value => "{Module: Options}", + desc => + ?T("Set all the " + "_`modules.md|modules`_ configuration options.")}}, + {loglevel, + #{value => + "none | emergency | alert | critical | " + "error | warning | notice | info | debug", + desc => + ?T("Verbosity of ejabberd " + "_`../configuration/basic.md#logging|logging`_. " + "The default value is 'info'. " + "NOTE: previous versions of ejabberd had log levels " + "defined in numeric format ('0..5'). The numeric values " + "are still accepted for backward compatibility, but " + "are not recommended.")}}, + {cache_life_time, + #{value => "timeout()", + desc => + ?T("The time of a cached item to keep in cache. " + "Once it's expired, the corresponding item is " + "erased from cache. The default value is '1 hour'. " + "Several modules have a similar option; and some core " + "ejabberd parts support similar options too, see " + "_`auth_cache_life_time`_, _`oauth_cache_life_time`_, " + "_`router_cache_life_time`_, and _`sm_cache_life_time`_.")}}, + {cache_missed, + #{value => "true | false", + desc => + ?T("Whether or not to cache missed lookups. When there is " + "an attempt to lookup for a value in a database and " + "this value is not found and the option is set to 'true', " + "this attempt will be cached and no attempts will be " + "performed until the cache expires (see _`cache_life_time`_). " + "Usually you don't want to change it. Default is 'true'. " + "Several modules have a similar option; and some core " + "ejabberd parts support similar options too, see " + "_`auth_cache_missed`_, _`oauth_cache_missed`_, " + "_`router_cache_missed`_, and _`sm_cache_missed`_.")}}, + {cache_size, + #{value => "pos_integer() | infinity", + desc => + ?T("A maximum number of items (not memory!) in cache. " + "The rule of thumb, for all tables except rosters, " + "you should set it to the number of maximum online " + "users you expect. For roster multiply this number " + "by 20 or so. If the cache size reaches this threshold, " + "it's fully cleared, i.e. all items are deleted, and " + "the corresponding warning is logged. You should avoid " + "frequent cache clearance, because this degrades " + "performance. The default value is '1000'. " + "Several modules have a similar option; and some core " + "ejabberd parts support similar options too, see " + "_`auth_cache_size`_, _`oauth_cache_size`_, " + "_`router_cache_size`_, and _`sm_cache_size`_.")}}, + {use_cache, + #{value => "true | false", + desc => + ?T("Enable or disable cache. The default is 'true'. " + "Several modules have a similar option; and some core " + "ejabberd parts support similar options too, see " + "_`auth_use_cache`_, _`oauth_use_cache`_, _`router_use_cache`_, " + "and _`sm_use_cache`_.")}}, + {default_db, + #{value => "mnesia | sql", + desc => + ?T("_`database.md#default-database|Default database`_ " + "to store persistent data in ejabberd. " + "Some components can be configured with specific toplevel options " + "like _`oauth_db_type`_. " + "Many modules can be configured with specific module options, " + "usually named `db_type`. " + "The default value is 'mnesia'.")}}, + {default_ram_db, + #{value => "mnesia | redis | sql", + desc => + ?T("Default volatile (in-memory) storage for ejabberd. " + "Some components can be configured with specific toplevel options " + "like _`router_db_type`_ and _`sm_db_type`_. " + "Some modules can be configured with specific module options, " + "usually named `ram_db_type`. " + "The default value is 'mnesia'.")}}, + {queue_type, + #{value => "ram | file", + desc => + ?T("Default type of queues in ejabberd. " + "Modules may have its own value of the option. " + "The value of 'ram' means that queues will be kept in memory. " + "If value 'file' is set, you may also specify directory " + "in _`queue_dir`_ option where file queues will be placed. " + "The default value is 'ram'.")}}, + {version, + #{value => "string()", + desc => + ?T("The option can be used to set custom ejabberd version, " + "that will be used by different parts of ejabberd, for " + "example by _`mod_version`_ module. The default value is " + "obtained at compile time from the underlying version " + "control system.")}}, + {acl, + #{value => "{ACLName: {ACLType: ACLValue}}", + desc => + ?T("This option defines " + "_`../configuration/basic.md#acl|access control lists`_: " + "named sets " + "of rules which are used to match against different targets " + "(such as a JID or an IP address). Every set of rules " + "has name 'ACLName': it can be any string except 'all' or 'none' " + "(those are predefined names for the rules that match all or nothing " + "respectively). The name 'ACLName' can be referenced from other " + "parts of the configuration file, for example in _`access_rules`_ " + "option. The rules of 'ACLName' are represented by mapping " + "'pass:[{ACLType: ACLValue}]'. These can be one of the following:")}, + [{user, + #{value => ?T("Username"), + desc => + ?T("If 'Username' is in the form of \"user@server\", " + "the rule matches a JID against this value. " + "Otherwise, if 'Username' is in the form of \"user\", " + "the rule matches any JID that has 'Username' in the node part " + "as long as the server part of this JID is any virtual " + "host served by ejabberd.")}}, + {server, + #{value => ?T("Server"), + desc => + ?T("The rule matches any JID from server 'Server'. " + "The value of 'Server' must be a valid " + "hostname or an IP address.")}}, + {resource, + #{value => ?T("Resource"), + desc => + ?T("The rule matches any JID with a resource 'Resource'.")}}, + {ip, + #{value => ?T("Network"), + desc => + ?T("The rule matches any IP address from the 'Network'.")}}, + {user_regexp, + #{value => ?T("Regexp"), + desc => + ?T("If 'Regexp' is in the form of \"regexp@server\", the rule " + "matches any JID with node part matching regular expression " + "\"regexp\" as long as the server part of this JID is equal " + "to \"server\". If 'Regexp' is in the form of \"regexp\", the rule " + "matches any JID with node part matching regular expression " + "\"regexp\" as long as the server part of this JID is any virtual " + "host served by ejabberd.")}}, + {server_regexp, + #{value => ?T("Regexp"), + desc => + ?T("The rule matches any JID from the server that " + "matches regular expression 'Regexp'.")}}, + {resource_regexp, + #{value => ?T("Regexp"), + desc => + ?T("The rule matches any JID with a resource that " + "matches regular expression 'Regexp'.")}}, + {node_regexp, + #{value => ?T("user_regexp@server_regexp"), + desc => + ?T("The rule matches any JID with node part matching regular " + "expression 'user_regexp' and server part matching regular " + "expression 'server_regexp'.")}}, + {user_glob, + #{value => ?T("Pattern"), + desc => + ?T("Same as 'user_regexp', but matching is performed on a " + "specified 'Pattern' according to the rules used by the " + "Unix shell.")}}, + {server_glob, + #{value => ?T("Pattern"), + desc => + ?T("Same as 'server_regexp', but matching is performed on a " + "specified 'Pattern' according to the rules used by the " + "Unix shell.")}}, + {resource_glob, + #{value => ?T("Pattern"), + desc => + ?T("Same as 'resource_regexp', but matching is performed on a " + "specified 'Pattern' according to the rules used by the " + "Unix shell.")}}, + {node_glob, + #{value => ?T("Pattern"), + desc => + ?T("Same as 'node_regexp', but matching is performed on a " + "specified 'Pattern' according to the rules used by the " + "Unix shell.")}}]}, + {access_rules, + #{value => "{AccessName: {allow|deny: ACLName|ACLDefinition}}", + desc => + ?T("This option defines " + "_`basic.md#access-rules|Access Rules`_. " + "Each access rule is " + "assigned a name that can be referenced from other parts " + "of the configuration file (mostly from 'access' options of " + "ejabberd modules). Each rule definition may contain " + "arbitrary number of 'allow' or 'deny' sections, and each " + "section may contain any number of ACL rules (see _`acl`_ option). " + "There are no access rules defined by default."), + example => + ["access_rules:", + " configure:", + " allow: admin", + " something:", + " deny: someone", + " allow: all", + " s2s_banned:", + " deny: problematic_hosts", + " deny: banned_forever", + " deny:", + " ip: 222.111.222.111/32", + " deny:", + " ip: 111.222.111.222/32", + " allow: all", + " xmlrpc_access:", + " allow:", + " user: peter@example.com", + " allow:", + " user: ivone@example.com", + " allow:", + " user: bot@example.com", + " ip: 10.0.0.0/24"]}}, + {acme, + #{value => ?T("Options"), + desc => + ?T("_`basic.md#acme|ACME`_ configuration, to automatically " + "obtain SSL certificates for the domains served by ejabberd, " + "which means that certificate requests and renewals are " + "performed to some CA server (aka \"ACME server\") in a fully " + "automated mode. The 'Options' are:"), + example => + ["acme:", + " ca_url: https://acme-v02.api.letsencrypt.org/directory", + " contact:", + " - mailto:admin@domain.tld", + " - mailto:bot@domain.tld", + " auto: true", + " cert_type: rsa"]}, + [{ca_url, + #{value => ?T("URL"), + desc => + ?T("The ACME directory URL used as an entry point " + "for the ACME server. The default value is " + " - " + "the directory URL of Let's Encrypt authority.")}}, + {contact, + #{value => ?T("[Contact, ...]"), + desc => + ?T("A list of contact addresses (typically emails) " + "where an ACME server will send notifications " + "when problems occur. The value of 'Contact' must " + "be in the form of \"scheme:address\" (e.g. " + "\"mailto:user@domain.tld\"). The default " + "is an empty list which means an ACME server " + "will send no notices.")}}, + {auto, + #{value => "true | false", + desc => + ?T("Whether to automatically request certificates for " + "all configured domains (that yet have no a certificate) " + "on server start or configuration reload. The default is 'true'.")}}, + {cert_type, + #{value => "rsa | ec", + desc => + ?T("A type of a certificate key. Available values are " + "'ec' and 'rsa' for EC and RSA certificates respectively. " + "It's better to have RSA certificates for the purpose " + "of backward compatibility with legacy clients and servers, " + "thus the default is 'rsa'.")}}]}, + {allow_contrib_modules, + #{value => "true | false", + desc => + ?T("Whether to allow installation of third-party modules or not. " + "See _`../../admin/guide/modules.md#ejabberd-contrib|ejabberd-contrib`_ " + "documentation section. " + "The default value is 'true'.")}}, + {allow_multiple_connections, + #{value => "true | false", + desc => + ?T("This option is only used when the anonymous mode is enabled. " + "Setting it to 'true' means that the same username can be " + "taken multiple times in anonymous login mode if different " + "resource are used to connect. This option is only useful " + "in very special occasions. The default value is 'false'.")}}, + {anonymous_protocol, + #{value => "login_anon | sasl_anon | both", + desc => + [?T("Define what " + "_`authentication.md#anonymous-login-and-sasl-anonymous|anonymous`_ " + "protocol will be used: "), "", + ?T("* 'login_anon' means that the anonymous login method will be used. "), "", + ?T("* 'sasl_anon' means that the SASL Anonymous method will be used. "), "", + ?T("* 'both' means that SASL Anonymous and login anonymous are both " + "enabled."), "", + ?T("The default value is 'sasl_anon'."), ""]}}, + {api_permissions, + #{value => "[Permission, ...]", + desc => + ?T("Define the permissions for API access. Please consult the " + "ejabberd Docs web -> For Developers -> ejabberd ReST API -> " + "_`../../developer/ejabberd-api/permissions.md|API Permissions`_.")}}, + {append_host_config, + #{value => "{Host: Options}", + desc => + ?T("Add a few specific options to a certain " + "_`../configuration/basic.md#virtual-hosting|virtual host`_.")}}, + {auth_cache_life_time, + #{value => "timeout()", + desc => + ?T("Same as _`cache_life_time`_, but applied to authentication cache " + "only. If not set, the value from _`cache_life_time`_ will be used.")}}, + {auth_cache_missed, + #{value => "true | false", + desc => + ?T("Same as _`cache_missed`_, but applied to authentication cache " + "only. If not set, the value from _`cache_missed`_ will be used.")}}, + {auth_cache_size, + #{value => "pos_integer() | infinity", + desc => + ?T("Same as _`cache_size`_, but applied to authentication cache " + "only. If not set, the value from _`cache_size`_ will be used.")}}, + {auth_method, + #{value => "[mnesia | sql | anonymous | external | jwt | ldap | pam, ...]", + desc => + ?T("A list of _`authentication.md|authentication`_ methods to use. " + "If several methods are defined, authentication is " + "considered successful as long as authentication of " + "at least one of the methods succeeds. " + "The default value is '[mnesia]'.")}}, + {auth_opts, + #{value => "[Option, ...]", + desc => + ?T("This is used by the contributed module " + "'ejabberd_auth_http' that can be installed from the " + "_`../../admin/guide/modules.md#ejabberd-contrib|ejabberd-contrib`_ " + "Git repository. Please refer to that " + "module's README file for details.")}}, + {auth_password_format, + #{value => "plain | scram", + note => "improved in 20.01", + desc => + [?T("The option defines in what format the users passwords " + "are stored, plain text or in _`authentication.md#scram|SCRAM`_ format:"), "", + ?T("* 'plain': The password is stored as plain text " + "in the database. This is risky because the passwords " + "can be read if your database gets compromised. " + "This is the default value. This format allows clients to " + "authenticate using: the old Jabber Non-SASL (XEP-0078), " + "SASL PLAIN, SASL DIGEST-MD5, and SASL SCRAM-SHA-1/256/512(-PLUS). "), "", + ?T("* 'scram': The password is not stored, only some information " + "required to verify the hash provided by the client. " + "It is impossible to obtain the original plain password " + "from the stored information; for this reason, when this " + "value is configured it cannot be changed to plain anymore. " + "This format allows clients to authenticate using: " + "SASL PLAIN and SASL SCRAM-SHA-1/256/512(-PLUS). The SCRAM variant " + "depends on the _`auth_scram_hash`_ option."), "", + ?T("The default value is 'plain'."), ""]}}, + + {auth_password_types_hidden_in_sasl1, + #{value => "[plain | scram_sha1 | scram_sha256 | scram_sha512]", + note => "added in 25.07", + desc => + ?T("List of password types that should not be offered in SASL1 authenticatication. " + "Because SASL1, unlike SASL2, can't have list of available mechanisms tailored to " + "individual user, it's possible that offered mechanisms will not be compatible " + "with stored password, especially if new password type was added recently. " + "This option allows disabling offering some mechanisms in SASL1, to a time until new " + "password type will be available for all users.")}}, + {auth_scram_hash, + #{value => "sha | sha256 | sha512", + desc => + ?T("Hash algorithm that should be used to store password in _`authentication.md#scram|SCRAM`_ format. " + "You shouldn't change this if you already have passwords generated with " + "a different algorithm - users that have such passwords will not be able " + "to authenticate. The default value is 'sha'.")}}, + {auth_stored_password_types, + #{value => "[plain | scram_sha1 | scram_sha256 | scram_sha512]", + note => "added in 25.03", + desc => + ?T("List of password types that should be stored simultaneously for each user in database. " + "When the user sets the account password, database will be updated to store the password in formats " + "compatible with each type listed here. This can be used to migrate user passwords " + "to a more secure format. If this option if set, it will override values set in _`auth_scram_hash`_ " + "and _`auth_password_format`_ options. The default value is `[]`.")}}, + {auth_external_user_exists_check, + #{value => "true | false", + note => "added in 23.10", + desc => + ?T("Supplement check for user existence based on _`mod_last`_ data, for authentication " + "methods that don't have a way to reliably tell if a user exists (like is the case for " + "'jwt' and certificate based authentication). This helps with processing offline message " + "for those users. The default value is 'true'.")}}, + {auth_use_cache, + #{value => "true | false", + desc => + ?T("Same as _`use_cache`_, but applied to authentication cache " + "only. If not set, the value from _`use_cache`_ will be used.")}}, + {c2s_cafile, + #{value => ?T("Path"), + desc => + [?T("Full path to a file containing one or more CA certificates " + "in PEM format. All client certificates should be signed by " + "one of these root CA certificates and should contain the " + "corresponding JID(s) in 'subjectAltName' field. " + "There is no default value."), "", + ?T("You can use _`host_config`_ to specify this option per-vhost."), "", + ?T("To set a specific file per listener, use the listener's _`listen-options.md#cafile|cafile`_ option. Please notice that 'c2s_cafile' overrides the listener's 'cafile' option."), "" + ]}}, + {c2s_ciphers, + #{value => "[Cipher, ...]", + desc => + ?T("A list of OpenSSL ciphers to use for c2s connections. " + "The default value is shown in the example below:"), + example => + ["c2s_ciphers:", + " - HIGH", + " - \"!aNULL\"", + " - \"!eNULL\"", + " - \"!3DES\"", + " - \"@STRENGTH\""]}}, + {c2s_dhfile, + #{value => ?T("Path"), + desc => + ?T("Full path to a file containing custom DH parameters " + "to use for c2s connections. " + "Such a file could be created with the command '\"openssl " + "dhparam -out dh.pem 2048\"'. If this option is not specified, " + "2048-bit MODP Group with 256-bit Prime Order Subgroup will be " + "used as defined in RFC5114 Section 2.3.")}}, + {c2s_protocol_options, + #{value => "[Option, ...]", + desc => + ?T("List of general SSL options to use for c2s connections. " + "These map to OpenSSL's 'set_options()'. The default value is " + "shown in the example below:"), + example => + ["c2s_protocol_options:", + " - no_sslv3", + " - cipher_server_preference", + " - no_compression"]}}, + {c2s_tls_compression, + #{value => "true | false", + desc => + ?T("Whether to enable or disable TLS compression for c2s connections. " + "The default value is 'false'.")}}, + {ca_file, + #{value => ?T("Path"), + desc => + [?T("Path to a file of CA root certificates. " + "The default is to use system defined file if possible."), "", + ?T("For server connections, this 'ca_file' option is overridden by the _`s2s_cafile`_ option."), "" + ]}}, + {captcha_cmd, + #{value => ?T("Path | ModuleName"), + note => "improved in 23.01", + desc => + ?T("Full path to a script that generates _`basic.md#captcha|CAPTCHA`_ images. " + "The keyword '@VERSION@' is replaced with ejabberd version number in 'XX.YY' format. " + "The keyword '@SEMVER@' is replaced with ejabberd version number in semver format " + "when compiled with Elixir's mix, or XX.YY format otherwise. " + "Alternatively, it can be the name of a module that implements ejabberd CAPTCHA support. " + "There is no default value: when this option is not " + "set, CAPTCHA functionality is completely disabled."), + example => + [{?T("When using the ejabberd installers or container image, the example captcha scripts can be used like this:"), + ["captcha_cmd: /opt/ejabberd-@VERSION@/lib/ejabberd-@SEMVER@/priv/bin/captcha.sh"]}]}}, + {captcha_limit, + #{value => "pos_integer() | infinity", + desc => + ?T("Maximum number of _`basic.md#captcha|CAPTCHA`_ generated images per minute for " + "any given JID. The option is intended to protect the server " + "from CAPTCHA DoS. The default value is 'infinity'.")}}, + {captcha_host, + #{value => "String", + desc => ?T("Deprecated. Use _`captcha_url`_ instead.")}}, + {captcha_url, + #{value => ?T("URL | auto | undefined"), + note => "improved in 23.04", + desc => + ?T("An URL where _`basic.md#captcha|CAPTCHA`_ requests should be sent. NOTE: you need " + "to configure 'request_handlers' for 'ejabberd_http' listener " + "as well. " + "If set to 'auto', it builds the URL using a 'request_handler' " + "already enabled, with encryption if available. " + "If set to 'undefined', it builds the URL using " + "the deprecated _`captcha_host`_ '+ /captcha'. " + "The default value is 'auto'.")}}, + {certfiles, + #{value => "[Path, ...]", + desc => + ?T("The option accepts a list of file paths (optionally with " + "wildcards) containing either PEM certificates or PEM private " + "keys. At startup or configuration reload, ejabberd reads all " + "certificates from these files, sorts them, removes duplicates, " + "finds matching private keys and then rebuilds full certificate " + "chains for the use in TLS connections. " + "Use this option when TLS is enabled in either of " + "ejabberd listeners: 'ejabberd_c2s', 'ejabberd_http' and so on. " + "NOTE: if you modify the certificate files or change the value " + "of the option, run 'ejabberdctl reload-config' in order to " + "rebuild and reload the certificate chains."), + example => + [{?T("If you use https://letsencrypt.org[Let's Encrypt] certificates " + "for your domain \"domain.tld\", the configuration will look " + "like this:"), + ["certfiles:", + " - /etc/letsencrypt/live/domain.tld/fullchain.pem", + " - /etc/letsencrypt/live/domain.tld/privkey.pem"]}]}}, + {cluster_backend, + #{value => ?T("Backend"), + desc => + ?T("A database backend to use for storing information about " + "cluster. The only available value so far is 'mnesia'.")}}, + {cluster_nodes, + #{value => "[Node, ...]", + desc => + ?T("A list of Erlang nodes to connect on ejabberd startup. " + "This option is mostly intended for ejabberd customization " + "and sophisticated setups. The default value is an empty list.")}}, + {define_keyword, + #{value => "{NAME: Value}", + note => "added in 25.03", + desc => + ?T("Allows to define configuration " + "_`../configuration/file-format.md#macros-and-keywords|keywords`_. "), + example => + ["define_keyword:", + " SQL_USERNAME: \"eja.global\"", + "", + "host_config:", + " localhost:", + " define_keyword:", + " SQL_USERNAME: \"eja.localhost\"", + "", + "sql_username: \"prefix.@SQL_USERNAME@\""]}}, + {define_macro, + #{value => "{NAME: Value}", + note => "improved in 25.03", + desc => + ?T("Allows to define configuration " + "_`../configuration/file-format.md#macros-and-keywords|macros`_. "), + example => + ["define_macro:", + " DEBUG: debug", + " LOG_LEVEL: DEBUG", + " USERBOB:", + " user: bob@localhost", + "", + "loglevel: LOG_LEVEL", + "", + "acl:", + " admin: USERBOB"]}}, + {disable_sasl_scram_downgrade_protection, + #{value => "true | false", + desc => + ?T("Allows to disable sending data required by " + "'XEP-0474: SASL SCRAM Downgrade Protection'. " + "There are known buggy clients (like those that use strophejs 1.6.2) " + "which will not be able to authenticatate when servers sends data from " + "that specification. This options allows server to disable it to allow " + "even buggy clients connects, but in exchange decrease MITM protection. " + "The default value of this option is 'false' which enables this extension.")}}, + {disable_sasl_mechanisms, + #{value => "[Mechanism, ...]", + desc => + ?T("Specify a list of SASL mechanisms (such as 'DIGEST-MD5' or " + "'SCRAM-SHA1') that should not be offered to the client. " + "For convenience, the value of 'Mechanism' is case-insensitive. " + "The default value is an empty list, i.e. no mechanisms " + "are disabled by default.")}}, + {domain_balancing, + #{value => "{Domain: Options}", + desc => + ?T("An algorithm to " + "_`../guide/clustering.md#service-load-balancing|load-balance`_ " + "the components that are plugged " + "on an ejabberd cluster. It means that you can plug one or several " + "instances of the same component on each ejabberd node and that " + "the traffic will be automatically distributed. The algorithm " + "to deliver messages to the component(s) can be specified by " + "this option. For any component connected as 'Domain', available " + "'Options' are:"), + example => + ["domain_balancing:", + " component.domain.tld:", + " type: destination", + " component_number: 5", + " transport.example.org:", + " type: bare_source"]}, + [{type, + #{value => ?T("Value"), + desc => + ?T("How to deliver stanzas to connected components. " + "The default value is 'random'. Possible values: ")}, + [{'- random', + #{desc => + ?T("an instance is chosen at random")}}, + {'- source', + #{desc => + ?T("by the full JID of the packet's 'from' attribute")}}, + {'- bare_destination', + #{desc => + ?T("by the bare JID (without resource) of the packet's 'to' attribute")}}, + {'- bare_source', + #{desc => + ?T("by the bare JID (without resource) of the packet's 'from' attribute is used")}}, + {'- destination', + #{desc => + ?T("an instance is chosen by the full JID of the packet's 'to' attribute")}}]}, + {component_number, + #{value => "2..1000", + desc => + ?T("The number of components to balance.")}}]}, + {extauth_pool_name, + #{value => ?T("Name"), + desc => + ?T("Define the pool name appendix in " + "_`authentication.md#external-script|external auth`_, " + "so the full pool name will be " + "'extauth_pool_Name'. The default value is the hostname.")}}, + {extauth_pool_size, + #{value => ?T("Size"), + desc => + ?T("The option defines the number of instances of the same " + "_`authentication.md#external-script|external auth`_ " + "program to start for better load balancing. " + "The default is the number of available CPU cores.")}}, + {extauth_program, + #{value => ?T("Path"), + desc => + ?T("Indicate in this option the full path to the " + "_`authentication.md#external-script|external authentication script`_. " + "The script must be executable by ejabberd.")}}, + {ext_api_headers, + #{value => "Headers", + desc => + ?T("String of headers (separated with commas ',') that will be " + "provided by ejabberd when sending ReST requests. " + "The default value is an empty string of headers: '\"\"'.")}}, + {ext_api_http_pool_size, + #{value => "pos_integer()", + desc => + ?T("Define the size of the HTTP pool, that is, the maximum number " + "of sessions that the ejabberd ReST service will handle " + "simultaneously. The default value is: '100'.")}}, + {ext_api_path_oauth, + #{value => "Path", + desc => + ?T("Define the base URI path when performing OAUTH ReST requests. " + "The default value is: '\"/oauth\"'.")}}, + {ext_api_url, + #{value => "URL", + desc => + ?T("Define the base URI when performing ReST requests. " + "The default value is: '\"http://localhost/api\"'.")}}, + {fqdn, + #{value => ?T("Domain"), + desc => + ?T("A fully qualified domain name that will be used in " + "SASL DIGEST-MD5 authentication. The default is detected " + "automatically.")}}, + {hide_sensitive_log_data, + #{value => "true | false", + desc => + ?T("A privacy option to not log sensitive data " + "(mostly IP addresses). The default value " + "is 'false' for backward compatibility.")}}, + {host_config, + #{value => "{Host: Options}", + desc => + ?T("The option is used to redefine 'Options' for " + "_`../configuration/basic.md#virtual-hosting|virtual host`_ " + "'Host'. " + "In the example below LDAP authentication method " + "will be used on virtual host 'domain.tld' and SQL method " + "will be used on virtual host 'example.org'."), + example => + ["hosts:", + " - domain.tld", + " - example.org", + "", + "auth_method:", + " - sql", + "", + "host_config:", + " domain.tld:", + " auth_method:", + " - ldap"]}}, + {hosts_alias, + #{value => "{Alias: Host}", + desc => + ?T("Define aliases for existing vhosts managed by ejabberd. " + "An alias may be a regexp expression. " + "This option is only consulted by the 'ejabberd_http' listener."), + note => "added in 25.07", + example => + ["hosts:", + " - domain.tld", + " - example.org", + "", + "hosts_alias:", + " xmpp.domain.tld: domain.tld", + " jabber.domain.tld: domain.tld", + " mytest.net: example.org", + " \"exa*\": example.org"]}}, + {include_config_file, + #{value => "[Filename, ...\\] | {Filename: Options}", + desc => + ?T("Read and " + "_`../configuration/file-format.md#include-additional-files|include additional file`_ " + "from 'Filename'. If the " + "value is provided in 'pass:[{Filename: Options}]' format, the " + "'Options' must be one of the following:")}, + [{disallow, + #{value => "[OptionName, ...]", + desc => + ?T("Disallows the usage of those options in the included " + "file 'Filename'. The options that match this criteria " + "are not accepted. The default value is an empty list.")}}, + {allow_only, + #{value => "[OptionName, ...]", + desc => + ?T("Allows only the usage of those options in the included " + "file 'Filename'. The options that do not match this " + "criteria are not accepted. The default value is to include " + "all options.")}}]}, + {install_contrib_modules, + #{value => "[Module, ...]", + note => "added in 23.10", + desc => + ?T("Modules to install from " + "_`../../admin/guide/modules.md#ejabberd-contrib|ejabberd-contrib`_ " + "at start time. " + "The default value is an empty list of modules: '[]'.")}}, + {jwt_auth_only_rule, + #{value => ?T("AccessName"), + desc => + ?T("This ACL rule defines accounts that can use only the " + "_`authentication.md#jwt-authentication|JWT`_ auth " + "method, even if others are also defined in the ejabberd " + "configuration file. In other words: if there are several auth " + "methods enabled for this host (JWT, SQL, ...), users that " + "match this rule can only use JWT. " + "The default value is 'none'.")}}, + {jwt_jid_field, + #{value => ?T("FieldName"), + desc => + ?T("By default, the JID is defined in the '\"jid\"' JWT field. " + "In this option you can specify other " + "_`authentication.md#jwt-authentication|JWT`_ " + "field name " + "where the JID is defined.")}}, + {jwt_key, + #{value => ?T("FilePath"), + desc => + ?T("Path to the file that contains the " + "_`authentication.md#jwt-authentication|JWT`_ key. " + "The default value is 'undefined'.")}}, + {language, + #{value => ?T("Language"), + desc => + ?T("Define the " + "_`../configuration/basic.md#default-language|default language`_ " + "of server strings " + "that can be seen by XMPP clients. If an XMPP client does not " + "possess 'xml:lang' attribute, the specified language is used. " + "The default value is '\"en\"'. ")}}, + {ldap_servers, + #{value => "[Host, ...]", + desc => + ?T("A list of IP addresses or DNS names of your LDAP servers (see " + "_`../configuration/ldap.md#ldap-connection|LDAP connection`_). " + "ejabberd connects immediately to all of them, " + "and reconnects infinitely if connection is lost. " + "The default value is '[localhost]'.")}}, + {ldap_backups, + #{value => "[Host, ...]", + desc => + ?T("A list of IP addresses or DNS names of LDAP backup servers (see " + "_`../configuration/ldap.md#ldap-connection|LDAP connection`_). " + "When no servers listed in _`ldap_servers`_ option are reachable, " + "ejabberd connects to these backup servers. " + "The default is an empty list, i.e. no backup servers specified. " + "Please notice that ejabberd only connects to the next server " + "when the existing connection is lost; it doesn't detect when a " + "previously-attempted server becomes available again.")}}, + {ldap_encrypt, + #{value => "tls | none", + desc => + ?T("Whether to encrypt LDAP connection using TLS or not. " + "The default value is 'none'. NOTE: STARTTLS encryption " + "is not supported.")}}, + {ldap_tls_certfile, + #{value => ?T("Path"), + desc => + ?T("A path to a file containing PEM encoded certificate " + "along with PEM encoded private key. This certificate " + "will be provided by ejabberd when TLS enabled for " + "LDAP connections. There is no default value, which means " + "no client certificate will be sent.")}}, + {ldap_tls_verify, + #{value => "false | soft | hard", + desc => + ?T("This option specifies whether to verify LDAP server " + "certificate or not when TLS is enabled. When 'hard' is set, " + "ejabberd doesn't proceed if the certificate is invalid. " + "When 'soft' is set, ejabberd proceeds even if the check has failed. " + "The default is 'false', which means no checks are performed.")}}, + {ldap_tls_cacertfile, + #{value => ?T("Path"), + desc => + ?T("A path to a file containing PEM encoded CA certificates. " + "This option is required when TLS verification is enabled.")}}, + {ldap_tls_depth, + #{value => ?T("Number"), + desc => + ?T("Specifies the maximum verification depth when TLS verification " + "is enabled, i.e. how far in a chain of certificates the " + "verification process can proceed before the verification " + "is considered to be failed. Peer certificate = 0, " + "CA certificate = 1, higher level CA certificate = 2, etc. " + "The value '2' thus means that a chain can at most contain " + "peer cert, CA cert, next CA cert, and an additional CA cert. " + "The default value is '1'.")}}, + {ldap_port, + #{value => "1..65535", + desc => + ?T("Port to connect to your LDAP server. The default port is " + "'389' if encryption is disabled and '636' if encryption is " + "enabled.")}}, + {ldap_rootdn, + #{value => "RootDN", + desc => + ?T("Bind Distinguished Name. The default value is an empty " + "string, which means \"anonymous connection\".")}}, + {ldap_password, + #{value => ?T("Password"), + desc => + ?T("Bind password. The default value is an empty string.")}}, + {ldap_deref_aliases, + #{value => "never | always | finding | searching", + desc => + ?T("Whether to dereference aliases or not. " + "The default value is 'never'.")}}, + {ldap_base, + #{value => "Base", + desc => + ?T("LDAP base directory which stores users accounts. " + "There is no default value: you must set the option " + "in order for LDAP connections to work properly.")}}, + {ldap_uids, + #{value => "[Attr\\] | {Attr: AttrFormat}", + desc => + ?T("LDAP attributes which hold a list of attributes to use " + "as alternatives for getting the JID, where 'Attr' is " + "an LDAP attribute which holds the user's part of the JID and " + "'AttrFormat' must contain one and only one pattern variable " + "'\"%u\"' which will be replaced by the user's part of the JID. " + "For example, '\"%u@example.org\"'. If the value is in the form " + "of '[Attr]' then 'AttrFormat' is assumed to be '\"%u\"'.")}}, + {ldap_filter, + #{value => ?T("Filter"), + desc => + ?T("An LDAP filter as defined in " + "https://tools.ietf.org/html/rfc4515[RFC4515]. " + "There is no default value. Example: " + "'\"(&(objectClass=shadowAccount)(memberOf=XMPP Users))\"'. " + "NOTE: don't forget to close brackets and don't use superfluous " + "whitespaces. Also you must not use '\"uid\"' attribute in the " + "filter because this attribute will be appended to the filter " + "automatically.")}}, + {ldap_dn_filter, + #{value => "{Filter: FilterAttrs}", + desc => + ?T("This filter is applied on the results returned by the main " + "filter. The filter performs an additional LDAP lookup to make " + "the complete result. This is useful when you are unable to " + "define all filter rules in 'ldap_filter'. You can define " + "'\"%u\"', '\"%d\"', '\"%s\"' and '\"%D\"' pattern variables in 'Filter: " + "\"%u\"' is replaced by a user's part of the JID, '\"%d\"' is " + "replaced by the corresponding domain (virtual host), all '\"%s\"' " + "variables are consecutively replaced by values from the attributes " + "in 'FilterAttrs' and '\"%D\"' is replaced by Distinguished Name from " + "the result set. There is no default value, which means the " + "result is not filtered. WARNING: Since this filter makes " + "additional LDAP lookups, use it only as the last resort: " + "try to define all filter rules in _`ldap_filter`_ option if possible."), + example => + ["ldap_dn_filter:", + " \"(&(name=%s)(owner=%D)(user=%u@%d))\": [sn]"]}}, + {log_rotate_count, + #{value => ?T("Number"), + desc => + ?T("The number of rotated log files to keep. " + "The default value is '1', which means that only keeps " + "`ejabberd.log.0`, `error.log.0` and `crash.log.0`.")}}, + {log_rotate_size, + #{value => "pos_integer() | infinity", + desc => + ?T("The size (in bytes) of a log file to trigger rotation. " + "If set to 'infinity', log rotation is disabled. " + "The default value is 10 Mb expressed in bytes: '10485760'.")}}, + {log_burst_limit_count, + #{value => ?T("Number"), + note => "added in 22.10", + desc => + ?T("The number of messages to accept in " + "`log_burst_limit_window_time` period before starting to " + "drop them. Default `500`")}}, + {log_burst_limit_window_time, + #{value => ?T("Number"), + note => "added in 22.10", + desc => + ?T("The time period to rate-limit log messages " + "by. Defaults to `1` second.")}}, + {log_modules_fully, + #{value => "[Module, ...]", + note => "added in 23.01", + desc => + ?T("List of modules that will log everything " + "independently from the general loglevel option.")}}, + {max_fsm_queue, + #{value => ?T("Size"), + desc => + ?T("This option specifies the maximum number of elements " + "in the queue of the FSM (Finite State Machine). Roughly " + "speaking, each message in such queues represents one " + "XML stanza queued to be sent into its relevant outgoing " + "stream. If queue size reaches the limit (because, for " + "example, the receiver of stanzas is too slow), the FSM " + "and the corresponding connection (if any) will be terminated " + "and error message will be logged. The reasonable value for " + "this option depends on your hardware configuration. " + "The allowed values are positive integers. " + "The default value is '10000'.")}}, + {negotiation_timeout, + #{value => "timeout()", + desc => + ?T("Time to wait for an XMPP stream negotiation to complete. " + "When timeout occurs, the corresponding XMPP stream is closed. " + "The default value is '120' seconds.")}}, + {net_ticktime, + #{value => "timeout()", + desc => + ?T("This option can be used to tune tick time parameter of " + "'net_kernel'. It tells Erlang VM how often nodes should check " + "if intra-node communication was not interrupted. This option " + "must have identical value on all nodes, or it will lead to subtle " + "bugs. Usually leaving default value of this is option is best, " + "tweak it only if you know what you are doing. " + "The default value is '1 minute'.")}}, + {new_sql_schema, + #{value => "true | false", + desc => + {?T("Whether to use the " + "_`database.md#default-and-new-schemas|new SQL schema`_. " + "All schemas are located " + "at . " + "There are two schemas available. The default legacy schema " + "stores one XMPP domain into one ejabberd database. " + "The 'new' schema can handle several XMPP domains in a " + "single ejabberd database. Using this 'new' schema is best when " + "serving several XMPP domains and/or changing domains from " + "time to time. This avoid need to manage several databases and " + "handle complex configuration changes. The default depends on " + "configuration flag '--enable-new-sql-schema' which is set " + "at compile time."), + [binary:part(ejabberd_config:version(), {0,5})]}}}, + {update_sql_schema, + #{value => "true | false", + note => "updated in 24.06", + desc => + ?T("Allow ejabberd to update SQL schema in " + "MySQL, PostgreSQL and SQLite databases. " + "This option was added in ejabberd 23.10, " + "and enabled by default since 24.06. " + "The default value is 'true'.")}}, + {update_sql_schema_timeout, + #{value => "timeout()", + note => "added in 24.07", + desc => + ?T("Time allocated to SQL schema update queries. " + "The default value is set to 5 minutes.")}}, + {oauth_access, + #{value => ?T("AccessName"), + desc => ?T("By default creating OAuth tokens is not allowed. " + "To define which users can create OAuth tokens, " + "you can refer to an ejabberd access rule in the " + "'oauth_access' option. Use 'all' to allow everyone " + "to create tokens.")}}, + {oauth_cache_life_time, + #{value => "timeout()", + desc => + ?T("Same as _`cache_life_time`_, but applied to OAuth cache " + "only. If not set, the value from _`cache_life_time`_ will be used.")}}, + {oauth_cache_missed, + #{value => "true | false", + desc => + ?T("Same as _`cache_missed`_, but applied to OAuth cache " + "only. If not set, the value from _`cache_missed`_ will be used.")}}, + {oauth_cache_rest_failure_life_time, + #{value => "timeout()", + note => "added in 21.01", + desc => + ?T("The time that a failure in OAuth ReST is cached. " + "The default value is 'infinity'.")}}, + {oauth_cache_size, + #{value => "pos_integer() | infinity", + desc => + ?T("Same as _`cache_size`_, but applied to OAuth cache " + "only. If not set, the value from _`cache_size`_ will be used.")}}, + {oauth_client_id_check, + #{value => "allow | db | deny", + desc => + ?T("Define whether the client authentication is always allowed, " + "denied, or it will depend if the client ID is present in the " + "database. The default value is 'allow'.")}}, + {oauth_use_cache, + #{value => "true | false", + desc => + ?T("Same as _`use_cache`_, but applied to OAuth cache " + "only. If not set, the value from _`use_cache`_ will be used.")}}, + {oauth_db_type, + #{value => "mnesia | sql", + desc => + ?T("Database backend to use for OAuth authentication. " + "The default value is picked from _`default_db`_ option, or " + "if it's not set, 'mnesia' will be used.")}}, + {oauth_expire, + #{value => "timeout()", + desc => + ?T("Time during which the OAuth token is valid, in seconds. " + "After that amount of time, the token expires and the delegated " + "credential cannot be used and is removed from the database. " + "The default is '4294967' seconds.")}}, + {oom_killer, + #{value => "true | false", + desc => + ?T("Enable or disable OOM (out-of-memory) killer. " + "When system memory raises above the limit defined in " + "_`oom_watermark`_ option, ejabberd triggers OOM killer " + "to terminate most memory consuming Erlang processes. " + "Note that in order to maintain functionality, ejabberd only " + "attempts to kill transient processes, such as those managing " + "client sessions, s2s or database connections. " + "The default value is 'true'.")}}, + {oom_queue, + #{value => ?T("Size"), + desc => + ?T("Trigger OOM killer when some of the running Erlang processes " + "have messages queue above this 'Size'. Note that " + "such processes won't be killed if _`oom_killer`_ option is set " + "to 'false' or if _`oom_watermark`_ is not reached yet.")}}, + {oom_watermark, + #{value => ?T("Percent"), + desc => + ?T("A percent of total system memory consumed at which " + "OOM killer should be activated with some of the processes " + "possibly be killed (see _`oom_killer`_ option). Later, when " + "memory drops below this 'Percent', OOM killer is deactivated. " + "The default value is '80' percents.")}}, + {outgoing_s2s_families, + #{value => "[ipv6 | ipv4, ...]", + note => "changed in 23.01", + desc => + ?T("Specify which address families to try, in what order. " + "The default is '[ipv6, ipv4]' which means it first tries " + "connecting with IPv6, if that fails it tries using IPv4. " + "This option is obsolete and irrelevant when using ejabberd 23.01 " + "and Erlang/OTP 22, or newer versions of them.")}}, + {outgoing_s2s_ipv4_address, + #{value => "Address", + note => "added in 20.12", + desc => + ?T("Specify the IPv4 address that will be used when establishing " + "an outgoing S2S IPv4 connection, for example '\"127.0.0.1\"'. " + "The default value is 'undefined'.")}}, + {outgoing_s2s_ipv6_address, + #{value => "Address", + note => "added in 20.12", + desc => + ?T("Specify the IPv6 address that will be used when establishing " + "an outgoing S2S IPv6 connection, for example " + "'\"::FFFF:127.0.0.1\"'. The default value is 'undefined'.")}}, + {outgoing_s2s_port, + #{value => "1..65535", + desc => + ?T("A port number to use for outgoing s2s connections when the target " + "server doesn't have an SRV record. The default value is '5269'.")}}, + {outgoing_s2s_timeout, + #{value => "timeout()", + desc => + ?T("The timeout in seconds for outgoing S2S connection attempts. " + "The default value is '10' seconds.")}}, + {pam_service, + #{value => ?T("Name"), + desc => + ?T("This option defines the " + "_`authentication.md#pam-authentication|PAM`_ " + "service name. Refer to the PAM " + "documentation of your operation system for more information. " + "The default value is 'ejabberd'.")}}, + {pam_userinfotype, + #{value => "username | jid", + desc => + ?T("This option defines what type of information about the " + "user ejabberd provides to the " + "_`authentication.md#pam-authentication|PAM`_ " + "service: only the username, " + "or the user's JID. Default is 'username'.")}}, + {pgsql_users_number_estimate, + #{value => "true | false", + desc => + ?T("Whether to use PostgreSQL estimation when counting registered " + "users. The default value is 'false'.")}}, + {queue_dir, + #{value => ?T("Directory"), + desc => + ?T("If _`queue_type`_ option is set to 'file', use this 'Directory' " + "to store file queues. The default is to keep queues inside " + "Mnesia directory.")}}, + {redis_connect_timeout, + #{value => "timeout()", + desc => + ?T("A timeout to wait for the connection to be re-established " + "to the _`database.md#redis|Redis`_ " + "server. The default is '1 second'.")}}, + {redis_db, + #{value => ?T("Number"), + desc => ?T("_`database.md#redis|Redis`_ " + "database number. The default is '0'.")}}, + {redis_password, + #{value => ?T("Password"), + desc => + ?T("The password to the _`database.md#redis|Redis`_ server. " + "The default is an empty string, i.e. no password.")}}, + {redis_pool_size, + #{value => ?T("Number"), + desc => + ?T("The number of simultaneous connections to the " + "_`database.md#redis|Redis`_ server. " + "The default value is '10'.")}}, + {redis_port, + #{value => "1..65535", + desc => + ?T("The port where the _`database.md#redis|Redis`_ " + " server is accepting connections. " + "The default is '6379'.")}}, + {redis_queue_type, + #{value => "ram | file", + desc => + ?T("The type of request queue for the " + "_`database.md#redis|Redis`_ server. " + "See description of _`queue_type`_ option for the explanation. " + "The default value is the value defined in _`queue_type`_ " + "or 'ram' if the latter is not set.")}}, + {redis_server, + #{value => "Host | IP Address | Unix Socket Path", + note => "improved in 24.12", + desc => + ?T("A hostname, IP address or unix domain socket file of the " + "_`database.md#redis|Redis`_ server. " + "Setup the path to unix domain socket like: '\"unix:/path/to/socket\"'. " + "The default value is 'localhost'.")}}, + {registration_timeout, + #{value => "timeout()", + desc => + ?T("This is a global option for module _`mod_register`_. " + "It limits the frequency of registrations from a given " + "IP or username. So, a user that tries to register a " + "new account from the same IP address or JID during " + "this time after their previous registration " + "will receive an error with the corresponding explanation. " + "To disable this limitation, set the value to 'infinity'. " + "The default value is '600 seconds'.")}}, + {resource_conflict, + #{value => "setresource | closeold | closenew", + desc => + ?T("NOTE: this option is deprecated and may be removed " + "anytime in the future versions. The possible values " + "match exactly the three possibilities described in " + "https://tools.ietf.org/html/rfc6120#section-7.7.2.2" + "[XMPP Core: section 7.7.2.2]. " + "The default value is 'closeold'. If the client " + "uses old Jabber Non-SASL authentication (XEP-0078), " + "then this option is not respected, and the action performed " + "is 'closeold'.")}}, + {rest_proxy, + #{value => "Host", + note => "added in 25.07", + desc => ?T("Address of a HTTP Connect proxy used by modules issuing rest calls " + "(like ejabberd_oauth_rest)")}}, + {rest_proxy_port, + #{value => "1..65535", + note => "added in 25.07", + desc => ?T("Port of a HTTP Connect proxy used by modules issuing rest calls " + "(like ejabberd_oauth_rest)")}}, + {rest_proxy_username, + #{value => "string()", + note => "added in 25.07", + desc => ?T("Username used to authenticate to HTTP Connect proxy used by modules issuing rest calls " + "(like ejabberd_oauth_rest)")}}, + {rest_proxy_password, + #{value => "string()", + note => "added in 25.07", + desc => ?T("Password used to authenticate to HTTP Connect proxy used by modules issuing rest calls " + "(like ejabberd_oauth_rest)")}}, + {router_cache_life_time, + #{value => "timeout()", + desc => + ?T("Same as _`cache_life_time`_, but applied to routing table cache " + "only. If not set, the value from _`cache_life_time`_ will be used.")}}, + {router_cache_missed, + #{value => "true | false", + desc => + ?T("Same as _`cache_missed`_, but applied to routing table cache " + "only. If not set, the value from _`cache_missed`_ will be used.")}}, + {router_cache_size, + #{value => "pos_integer() | infinity", + desc => + ?T("Same as _`cache_size`_, but applied to routing table cache " + "only. If not set, the value from _`cache_size`_ will be used.")}}, + {router_db_type, + #{value => "mnesia | redis | sql", + desc => + ?T("Database backend to use for routing information. " + "The default value is picked from _`default_ram_db`_ option, or " + "if it's not set, 'mnesia' will be used.")}}, + {router_use_cache, + #{value => "true | false", + desc => + ?T("Same as _`use_cache`_, but applied to routing table cache " + "only. If not set, the value from _`use_cache`_ will be used.")}}, + {rpc_timeout, + #{value => "timeout()", + desc => + ?T("A timeout for remote function calls between nodes " + "in an ejabberd cluster. You should probably never change " + "this value since those calls are used for internal needs " + "only. The default value is '5' seconds.")}}, + {s2s_access, + #{value => ?T("Access"), + desc => + ?T("This _`basic.md#access-rules|Access Rule`_ defines to " + "what remote servers can s2s connections be established. " + "The default value is 'all'; no restrictions are applied, it is" + " allowed to connect s2s to/from all remote servers.")}}, + {s2s_cafile, + #{value => ?T("Path"), + desc => + [?T("A path to a file with CA root certificates that will " + "be used to authenticate s2s connections. If not set, " + "the value of _`ca_file`_ will be used."), "", + ?T("You can use _`host_config`_ to specify this option per-vhost."), "" + ]}}, + {s2s_ciphers, + #{value => "[Cipher, ...]", + desc => + ?T("A list of OpenSSL ciphers to use for s2s connections. " + "The default value is shown in the example below:"), + example => + ["s2s_ciphers:", + " - HIGH", + " - \"!aNULL\"", + " - \"!eNULL\"", + " - \"!3DES\"", + " - \"@STRENGTH\""]}}, + {s2s_dhfile, + #{value => ?T("Path"), + desc => + ?T("Full path to a file containing custom DH parameters " + "to use for s2s connections. " + "Such a file could be created with the command '\"openssl " + "dhparam -out dh.pem 2048\"'. If this option is not specified, " + "2048-bit MODP Group with 256-bit Prime Order Subgroup will be " + "used as defined in RFC5114 Section 2.3.")}}, + {s2s_protocol_options, + #{value => "[Option, ...]", + desc => + ?T("List of general SSL options to use for s2s connections. " + "These map to OpenSSL's 'set_options()'. The default value is " + "shown in the example below:"), + example => + ["s2s_protocol_options:", + " - no_sslv3", + " - cipher_server_preference", + " - no_compression"]}}, + {s2s_tls_compression, + #{value => "true | false", + desc => + ?T("Whether to enable or disable TLS compression for s2s connections. " + "The default value is 'false'.")}}, + {s2s_dns_retries, + #{value => ?T("Number"), + desc => + ?T("DNS resolving retries. The default value is '2'.")}}, + {s2s_dns_timeout, + #{value => "timeout()", + desc => + ?T("The timeout for DNS resolving. The default value is '10' seconds.")}}, + {s2s_max_retry_delay, + #{value => "timeout()", + desc => + ?T("The maximum allowed delay for s2s connection retry to connect after a " + "failed connection attempt. The default value is '300' seconds " + "(5 minutes).")}}, + {s2s_queue_type, + #{value => "ram | file", + desc => + ?T("The type of a queue for s2s packets. " + "See description of _`queue_type`_ option for the explanation. " + "The default value is the value defined in _`queue_type`_ " + "or 'ram' if the latter is not set.")}}, + {s2s_timeout, + #{value => "timeout()", + desc => + ?T("A time to wait before closing an idle s2s connection. " + "The default value is '1' hour.")}}, + {s2s_use_starttls, + #{value => "true | false | optional | required", + desc => + ?T("Whether to use STARTTLS for s2s connections. " + "The value of 'false' means STARTTLS is prohibited. " + "The value of 'true' or 'optional' means STARTTLS is enabled " + "but plain connections are still allowed. And the value of " + "'required' means that only STARTTLS connections are allowed. " + "The default value is 'false' (for historical reasons).")}}, + {s2s_zlib, + #{value => "true | false", + desc => + ?T("Whether to use 'zlib' compression (as defined in " + "https://xmpp.org/extensions/xep-0138.html[XEP-0138]) or not. " + "The default value is 'false'. WARNING: this type " + "of compression is nowadays considered insecure.")}}, + {shaper, + #{value => "{ShaperName: Rate}", + desc => + ?T("The option defines a set of " + "_`../configuration/basic.md#shapers|shapers`_. " + "Every shaper is assigned " + "a name 'ShaperName' that can be used in other parts of the " + "configuration file, such as _`shaper_rules`_ option. The shaper " + "itself is defined by its 'Rate', where 'Rate' stands for the " + "maximum allowed incoming rate in **bytes** per second. " + "When a connection exceeds this limit, ejabberd stops reading " + "from the socket until the average rate is again below the " + "allowed maximum. In the example below shaper 'normal' limits " + "the traffic speed to 1,000 bytes/sec and shaper 'fast' limits " + "the traffic speed to 50,000 bytes/sec:"), + example => + ["shaper:", + " normal: 1000", + " fast: 50000"]}}, + {shaper_rules, + #{value => "{ShaperRuleName: {Number|ShaperName: ACLName|ACLDefinition}}", + desc => + ?T("This option defines " + "_`../configuration/basic.md#shaper-rules|shaper rules`_ " + "to use for matching user/hosts. " + "Semantics is similar to _`access_rules`_ option, the only difference is " + "that instead using 'allow' or 'deny', a name of a shaper (defined in " + "_`shaper`_ option) or a positive number should be used."), + example => + ["shaper_rules:", + " connections_limit:", + " 10:", + " user: peter@example.com", + " 100: admin", + " 5: all", + " download_speed:", + " fast: admin", + " slow: anonymous_users", + " normal: all", + " log_days: 30"]}}, + {sm_cache_life_time, + #{value => "timeout()", + desc => + ?T("Same as _`cache_life_time`_, but applied to client sessions table cache " + "only. If not set, the value from _`cache_life_time`_ will be used.")}}, + {sm_cache_missed, + #{value => "true | false", + desc => + ?T("Same as _`cache_missed`_, but applied to client sessions table cache " + "only. If not set, the value from _`cache_missed`_ will be used.")}}, + {sm_cache_size, + #{value => "pos_integer() | infinity", + desc => + ?T("Same as _`cache_size`_, but applied to client sessions table cache " + "only. If not set, the value from _`cache_size`_ will be used.")}}, + {sm_db_type, + #{value => "mnesia | redis | sql", + desc => + ?T("Database backend to use for client sessions information. " + "The default value is picked from _`default_ram_db`_ option, or " + "if it's not set, 'mnesia' will be used.")}}, + {sm_use_cache, + #{value => "true | false", + desc => + ?T("Same as _`use_cache`_, but applied to client sessions table cache " + "only. If not set, the value from _`use_cache`_ will be used.")}}, + {sql_type, + #{value => "mssql | mysql | odbc | pgsql | sqlite", + desc => + ?T("The type of an SQL connection. The default is 'odbc'.")}}, + {sql_connect_timeout, + #{value => "timeout()", + desc => + ?T("A time to wait for connection to an SQL server to be " + "established. The default value is '5' seconds.")}}, + {sql_database, + #{value => ?T("Database"), + desc => + ?T("An SQL database name. For SQLite this must be a full " + "path to a database file. The default value is 'ejabberd'.")}}, + {sql_keepalive_interval, + #{value => "timeout()", + desc => + ?T("An interval to make a dummy SQL request to keep alive the " + "connections to the database. There is no default value, so no " + "keepalive requests are made.")}}, + {sql_odbc_driver, + #{value => "Path", + note => "added in 20.12", + desc => + ?T("Path to the ODBC driver to use to connect to a Microsoft SQL " + "Server database. This option only applies if the _`sql_type`_ " + "option is set to 'mssql' and _`sql_server`_ is not an ODBC " + "connection string. The default value is: 'libtdsodbc.so'")}}, + {sql_password, + #{value => ?T("Password"), + desc => + ?T("The password for SQL authentication. The default is empty string.")}}, + {sql_pool_size, + #{value => ?T("Size"), + desc => + ?T("Number of connections to the SQL server that ejabberd will " + "open for each virtual host. The default value is '10'. WARNING: " + "for SQLite this value is '1' by default and it's not recommended " + "to change it due to potential race conditions.")}}, + {sql_port, + #{value => "1..65535", + desc => + ?T("The port where the SQL server is accepting connections. " + "The default is '3306' for MySQL, '5432' for PostgreSQL and " + "'1433' for MS SQL. The option has no effect for SQLite.")}}, + {sql_prepared_statements, + #{value => "true | false", + note => "added in 20.01", + desc => + ?T("This option is 'true' by default, and is useful to disable " + "prepared statements. The option is valid for PostgreSQL and MySQL.")}}, + {sql_flags, + #{value => "[mysql_alternative_upsert]", + note => "added in 24.02", + desc => + ?T("This option accepts a list of SQL flags, and is empty by default. " + "'mysql_alternative_upsert' forces the alternative upsert implementation in MySQL.")}}, + {sql_query_timeout, + #{value => "timeout()", + desc => + ?T("A time to wait for an SQL query response. " + "The default value is '60' seconds.")}}, + {sql_queue_type, + #{value => "ram | file", + desc => + ?T("The type of a request queue for the SQL server. " + "See description of _`queue_type`_ option for the explanation. " + "The default value is the value defined in _`queue_type`_ " + "or 'ram' if the latter is not set.")}}, + {sql_server, + #{value => "Host | IP Address | ODBC Connection String | Unix Socket Path", + note => "improved in 24.06", + desc => + ?T("The hostname or IP address of the SQL server. For _`sql_type`_ " + "'mssql' or 'odbc' this can also be an ODBC connection string. " + "When _`sql_type`_ is 'mysql' or 'pgsql', this can be the path to " + "a unix domain socket expressed like: '\"unix:/path/to/socket\"'." + "The default value is 'localhost'.")}}, + {sql_ssl, + #{value => "true | false", + note => "improved in 20.03", + desc => + ?T("Whether to use SSL encrypted connections to the " + "SQL server. The option is only available for MySQL, MS SQL and " + "PostgreSQL. The default value is 'false'.")}}, + {sql_ssl_cafile, + #{value => ?T("Path"), + desc => + ?T("A path to a file with CA root certificates that will " + "be used to verify SQL connections. Implies _`sql_ssl`_ " + "and _`sql_ssl_verify`_ options are set to 'true'. " + "There is no default which means " + "certificate verification is disabled. " + "This option has no effect for MS SQL.")}}, + {sql_ssl_certfile, + #{value => ?T("Path"), + desc => + ?T("A path to a certificate file that will be used " + "for SSL connections to the SQL server. Implies _`sql_ssl`_ " + "option is set to 'true'. There is no default which means " + "ejabberd won't provide a client certificate to the SQL " + "server. " + "This option has no effect for MS SQL.")}}, + {sql_ssl_verify, + #{value => "true | false", + desc => + ?T("Whether to verify SSL connection to the SQL server against " + "CA root certificates defined in _`sql_ssl_cafile`_ option. " + "Implies _`sql_ssl`_ option is set to 'true'. " + "This option has no effect for MS SQL. " + "The default value is 'false'.")}}, + {sql_start_interval, + #{value => "timeout()", + desc => + ?T("A time to wait before retrying to restore failed SQL connection. " + "The default value is '30' seconds.")}}, + {sql_username, + #{value => ?T("Username"), + desc => + ?T("A user name for SQL authentication. " + "The default value is 'ejabberd'.")}}, + {trusted_proxies, + #{value => "all | [Network1, Network2, ...]", + desc => + ?T("Specify what proxies are trusted when an HTTP request " + "contains the header 'X-Forwarded-For'. You can specify " + "'all' to allow all proxies, or specify a list of IPs, " + "possibly with masks. The default value is an empty list. " + "Using this option you can know the real IP " + "of the request, for admin purpose, or security configuration " + "(for example using _`mod_fail2ban`_). IMPORTANT: The proxy MUST " + "be configured to set the 'X-Forwarded-For' header if you " + "enable this option as, otherwise, the client can set it " + "itself and as a result the IP value cannot be trusted for " + "security rules in ejabberd.")}}, + {validate_stream, + #{value => "true | false", + desc => + ?T("Whether to validate any incoming XML packet according " + "to the schemas of " + "https://github.com/processone/xmpp#supported-xmpp-elements" + "[supported XMPP extensions]. WARNING: the validation is only " + "intended for the use by client developers - don't enable " + "it in production environment. The default value is 'false'.")}}, + {websocket_origin, + #{value => "ignore | URL", + desc => + ?T("This option enables validation for 'Origin' header to " + "protect against connections from other domains than given " + "in the configuration file. In this way, the lower layer load " + "balancer can be chosen for a specific ejabberd implementation " + "while still providing a secure WebSocket connection. " + "The default value is 'ignore'. An example value of the 'URL' is " + "'\"https://test.example.org:8081\"'.")}}, + {websocket_ping_interval, + #{value => "timeout()", + desc => + ?T("Defines time between pings sent by the server to a client " + "(WebSocket level protocol pings are used for this) to keep " + "a connection active. If the client doesn't respond to two " + "consecutive pings, the connection will be assumed as closed. " + "The value of '0' can be used to disable the feature. This option " + "makes the server sending pings only for connections using the RFC " + "compliant protocol. For older style connections the server " + "expects that whitespace pings would be used for this purpose. " + "The default value is '60' seconds.")}}, + {websocket_timeout, + #{value => "timeout()", + desc => + ?T("Amount of time without any communication after which the " + "connection would be closed. The default value is '300' seconds.")}}]. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== diff --git a/src/ejabberd_piefxis.erl b/src/ejabberd_piefxis.erl index b6f90ccf8..789be7359 100644 --- a/src/ejabberd_piefxis.erl +++ b/src/ejabberd_piefxis.erl @@ -1,14 +1,11 @@ %%%---------------------------------------------------------------------- %%% File : ejabberd_piefxis.erl -%%% Author : Pablo Polvorin, Vidal Santiago Martinez +%%% Author : Pablo Polvorin, Vidal Santiago Martinez, Evgeniy Khramtsov %%% Purpose : XEP-0227: Portable Import/Export Format for XMPP-IM Servers %%% Created : 17 Jul 2008 by Pablo Polvorin -%%%------------------------------------------------------------------- -%%% @author Evgeniy Khramtsov -%%% @doc %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -27,28 +24,23 @@ %%%---------------------------------------------------------------------- %%% Not implemented: +%%% - PEP nodes export/import +%%% - message archives export/import %%% - write mod_piefxis with ejabberdctl commands -%%% - Export from mod_offline_sql.erl -%%% - Export from mod_private_sql.erl -%%% - XEP-227: 6. Security Considerations %%% - Other schemas of XInclude are not tested, and may not be imported correctly. %%% - If a host has many users, split that host in XML files with 50 users each. -%%%% Headers -module(ejabberd_piefxis). --behaviour(ejabberd_config). +-protocol({xep, 227, '1.1', '2.1.0', "partial", ""}). --protocol({xep, 227, '1.0'}). - --export([import_file/1, export_server/1, export_host/2, - opt_type/1]). +-export([import_file/1, export_server/1, export_host/2]). -define(CHUNK_SIZE, 1024*20). %20k --include("ejabberd.hrl"). +-include_lib("xmpp/include/scram.hrl"). -include("logger.hrl"). --include("xmpp.hrl"). +-include_lib("xmpp/include/xmpp.hrl"). -include("mod_privacy.hrl"). -include("mod_roster.hrl"). @@ -64,10 +56,10 @@ -define(NS_PIEFXIS, <<"http://www.xmpp.org/extensions/xep-0227.html#ns">>). -define(NS_XI, <<"http://www.w3.org/2001/XInclude">>). --record(state, {xml_stream_state :: fxml_stream:xml_stream_state(), +-record(state, {xml_stream_state :: fxml_stream:xml_stream_state() | undefined, user = <<"">> :: binary(), server = <<"">> :: binary(), - fd :: file:io_device(), + fd = self() :: file:io_device(), dir = <<"">> :: binary()}). -type state() :: #state{}. @@ -92,13 +84,13 @@ import_file(FileName, State) -> Res; {error, Reason} -> ErrTxt = file:format_error(Reason), - ?ERROR_MSG("Failed to open file '~s': ~s", [FileName, ErrTxt]), + ?ERROR_MSG("Failed to open file '~ts': ~ts", [FileName, ErrTxt]), {error, Reason} end. -spec export_server(binary()) -> any(). export_server(Dir) -> - export_hosts(?MYHOSTS, Dir). + export_hosts(ejabberd_option:hosts(), Dir). -spec export_host(binary(), binary()) -> any(). export_host(Dir, Host) -> @@ -131,7 +123,7 @@ export_hosts(Hosts, Dir) -> end, ok, FilesAndHosts); {error, Reason} -> ErrTxt = file:format_error(Reason), - ?ERROR_MSG("Failed to open file '~s': ~s", [DFn, ErrTxt]), + ?ERROR_MSG("Failed to open file '~ts': ~ts", [DFn, ErrTxt]), {error, Reason} end. @@ -141,7 +133,7 @@ export_host(Dir, FnH, Host) -> {ok, Fd} -> print(Fd, make_piefxis_xml_head()), print(Fd, make_piefxis_host_head(Host)), - Users = ejabberd_auth:get_vh_registered_users(Host), + Users = ejabberd_auth:get_users(Host), case export_users(Users, Host, Fd) of ok -> print(Fd, make_piefxis_host_tail()), @@ -155,7 +147,7 @@ export_host(Dir, FnH, Host) -> end; {error, Reason} -> ErrTxt = file:format_error(Reason), - ?ERROR_MSG("Failed to open file '~s': ~s", [DFn, ErrTxt]), + ?ERROR_MSG("Failed to open file '~ts': ~ts", [DFn, ErrTxt]), {error, Reason} end. @@ -172,40 +164,81 @@ export_users([], _Server, _Fd) -> export_user(User, Server, Fd) -> Password = ejabberd_auth:get_password_s(User, Server), LServer = jid:nameprep(Server), - PasswordFormat = ejabberd_config:get_option({auth_password_format, LServer}, fun(X) -> X end, plain), - Pass = case Password of - {_,_,_,_} -> - case PasswordFormat of - scram -> format_scram_password(Password); - _ -> <<"">> - end; - _ -> Password - end, - Els = get_offline(User, Server) ++ + {PassPlain, PassScram} = case ejabberd_auth:password_format(LServer) of + scram -> {[], [format_scram_password(Password)]}; + _ when Password == <<"">> -> {[], []}; + _ -> {[{<<"password">>, Password}], []} + end, + Els = + PassScram ++ + get_offline(User, Server) ++ get_vcard(User, Server) ++ get_privacy(User, Server) ++ get_roster(User, Server) ++ get_private(User, Server), print(Fd, fxml:element_to_binary( #xmlel{name = <<"user">>, - attrs = [{<<"name">>, User}, - {<<"password">>, Pass}], + attrs = [{<<"name">>, User} | PassPlain], children = Els})). -format_scram_password({StoredKey, ServerKey, Salt, IterationCount}) -> - StoredKeyB64 = base64:encode(StoredKey), - ServerKeyB64 = base64:encode(ServerKey), - SaltB64 = base64:encode(Salt), - IterationCountBin = (integer_to_binary(IterationCount)), - <<"scram:", StoredKeyB64/binary, ",", ServerKeyB64/binary, ",", SaltB64/binary, ",", IterationCountBin/binary>>. +format_scram_password(#scram{hash = Hash, storedkey = StoredKey, serverkey = ServerKey, + salt = Salt, iterationcount = IterationCount}) -> + StoredKeyB64 = base64:encode(StoredKey), + ServerKeyB64 = base64:encode(ServerKey), + SaltB64 = base64:encode(Salt), + IterationCountBin = (integer_to_binary(IterationCount)), + MechanismB = case Hash of + sha -> <<"SCRAM-SHA-1">>; + sha256 -> <<"SCRAM-SHA-256">>; + sha512 -> <<"SCRAM-SHA-512">> + end, + Children = + [ + #xmlel{name = <<"iter-count">>, + children = [{xmlcdata, IterationCountBin}]}, + #xmlel{name = <<"salt">>, + children = [{xmlcdata, SaltB64}]}, + #xmlel{name = <<"server-key">>, + children = [{xmlcdata, ServerKeyB64}]}, + #xmlel{name = <<"stored-key">>, + children = [{xmlcdata, StoredKeyB64}]} + ], + #xmlel{name = <<"scram-credentials">>, + attrs = [{<<"xmlns">>, <>}, + {<<"mechanism">>, MechanismB}], + children = Children}. + +parse_scram_password(#xmlel{attrs = Attrs} = El) -> + Hash = case fxml:get_attr_s(<<"mechanism">>, Attrs) of + <<"SCRAM-SHA-1">> -> sha; + <<"SCRAM-SHA-256">> -> sha256; + <<"SCRAM-SHA-512">> -> sha512 + end, + StoredKeyB64 = fxml:get_path_s(El, [{elem, <<"stored-key">>}, cdata]), + ServerKeyB64 = fxml:get_path_s(El, [{elem, <<"server-key">>}, cdata]), + IterationCountBin = fxml:get_path_s(El, [{elem, <<"iter-count">>}, cdata]), + SaltB64 = fxml:get_path_s(El, [{elem, <<"salt">>}, cdata]), + #scram{ + storedkey = base64:decode(StoredKeyB64), + serverkey = base64:decode(ServerKeyB64), + salt = base64:decode(SaltB64), + hash = Hash, + iterationcount = (binary_to_integer(IterationCountBin)) + }; parse_scram_password(PassData) -> Split = binary:split(PassData, <<",">>, [global]), - [StoredKeyB64, ServerKeyB64, SaltB64, IterationCountBin] = Split, + [Hash, StoredKeyB64, ServerKeyB64, SaltB64, IterationCountBin] = + case Split of + [K1, K2, K3, K4] -> [sha, K1, K2, K3, K4]; + [<<"sha256">>, K1, K2, K3, K4] -> [sha256, K1, K2, K3, K4]; + [<<"sha512">>, K1, K2, K3, K4] -> [sha512, K1, K2, K3, K4] + end, #scram{ - storedkey = StoredKeyB64, - serverkey = ServerKeyB64, - salt = SaltB64, + storedkey = base64:decode(StoredKeyB64), + serverkey = base64:decode(ServerKeyB64), + salt = base64:decode(SaltB64), + hash = Hash, iterationcount = (binary_to_integer(IterationCountBin)) }. @@ -213,26 +246,30 @@ parse_scram_password(PassData) -> get_vcard(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), - case mod_vcard:get_vcard(LUser, LServer) of + try mod_vcard:get_vcard(LUser, LServer) of error -> []; Els -> Els + catch + error:{module_not_loaded, _, _} -> [] end. -spec get_offline(binary(), binary()) -> [xmlel()]. get_offline(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), - case mod_offline:get_offline_els(LUser, LServer) of + try mod_offline:get_offline_els(LUser, LServer) of [] -> []; Els -> NewEls = lists:map(fun xmpp:encode/1, Els), [#xmlel{name = <<"offline-messages">>, children = NewEls}] + catch + error:{module_not_loaded, _, _} -> [] end. -spec get_privacy(binary(), binary()) -> [xmlel()]. get_privacy(User, Server) -> - case mod_privacy:get_user_lists(User, Server) of + try mod_privacy:get_user_lists(User, Server) of {ok, #privacy{default = Default, lists = [_|_] = Lists}} -> XLists = lists:map( @@ -245,12 +282,14 @@ get_privacy(User, Server) -> [xmpp:encode(#privacy_query{default = Default, lists = XLists})]; _ -> [] + catch + error:{module_not_loaded, _, _} -> [] end. -spec get_roster(binary(), binary()) -> [xmlel()]. get_roster(User, Server) -> - JID = jid:make(User, Server, <<>>), - case mod_roster:get_roster(User, Server) of + JID = jid:make(User, Server), + try mod_roster:get_roster(User, Server) of [_|_] = Items -> Subs = lists:flatmap( @@ -277,15 +316,19 @@ get_roster(User, Server) -> [xmpp:encode(#roster_query{items = Rs}) | Subs]; _ -> [] + catch + error:{module_not_loaded, _, _} -> [] end. -spec get_private(binary(), binary()) -> [xmlel()]. get_private(User, Server) -> - case mod_private:get_data(User, Server) of + try mod_private:get_data(User, Server) of [_|_] = Els -> - [xmpp:encode(#private{xml_els = Els})]; + [xmpp:encode(#private{sub_els = Els})]; _ -> [] + catch + error:{module_not_loaded, _, _} -> [] end. process(#state{xml_stream_state = XMLStreamState, fd = Fd} = State) -> @@ -306,17 +349,26 @@ process(#state{xml_stream_state = XMLStreamState, fd = Fd} = State) -> end. process_els(State) -> + Els = gather_els(State, []), + process_els(State, lists:reverse(Els)). + +gather_els(State, List) -> receive {'$gen_event', El} -> - case process_el(El, State) of - {ok, NewState} -> - process_els(NewState); - Err -> - Err - end + gather_els(State, [El | List]) after 0 -> - {ok, State} - end. + List +end. + +process_els(State, [El | Tail]) -> + case process_el(El, State) of + {ok, NewState} -> + process_els(NewState, Tail); + Err -> + Err + end; +process_els(State, []) -> + {ok, State}. process_el({xmlstreamstart, <<"server-data">>, Attrs}, State) -> case fxml:get_attr_s(<<"xmlns">>, Attrs) of @@ -325,7 +377,7 @@ process_el({xmlstreamstart, <<"server-data">>, Attrs}, State) -> ?NS_PIE -> {ok, State}; NS -> - stop("Unknown 'server-data' namespace = ~s", [NS]) + stop("Unknown 'server-data' namespace = ~ts", [NS]) end; process_el({xmlstreamend, _}, State) -> {ok, State}; @@ -348,16 +400,16 @@ process_el({xmlstreamelement, #xmlel{name = <<"host">>, attrs = Attrs, children = Els}}, State) -> JIDS = fxml:get_attr_s(<<"jid">>, Attrs), - case jid:from_string(JIDS) of + try jid:decode(JIDS) of #jid{lserver = S} -> - case lists:member(S, ?MYHOSTS) of + case ejabberd_router:is_my_host(S) of true -> process_users(Els, State#state{server = S}); false -> - stop("Unknown host: ~s", [S]) - end; - error -> - stop("Invalid 'jid': ~s", [JIDS]) + stop("Unknown host: ~ts", [S]) + end + catch _:{bad_jid, _} -> + stop("Invalid 'jid': ~ts", [JIDS]) end; process_el({xmlstreamstart, <<"user">>, Attrs}, State = #state{server = S}) when S /= <<"">> -> @@ -388,33 +440,40 @@ process_users([_|Els], State) -> process_users([], State) -> {ok, State}. -process_user(#xmlel{name = <<"user">>, attrs = Attrs, children = Els}, +process_user(#xmlel{name = <<"user">>, attrs = Attrs, children = Els} = El, #state{server = LServer} = State) -> Name = fxml:get_attr_s(<<"name">>, Attrs), - Password = fxml:get_attr_s(<<"password">>, Attrs), - PasswordFormat = ejabberd_config:get_option({auth_password_format, LServer}, fun(X) -> X end, plain), - Pass = case PasswordFormat of - scram -> - case Password of - <<"scram:", PassData/binary>> -> - parse_scram_password(PassData); - P -> P - end; - _ -> Password - end, - + Pass = process_password(El, LServer), case jid:nodeprep(Name) of error -> - stop("Invalid 'user': ~s", [Name]); + stop("Invalid 'user': ~ts", [Name]); LUser -> case ejabberd_auth:try_register(LUser, LServer, Pass) of - {atomic, _} -> + ok -> process_user_els(Els, State#state{user = LUser}); - Err -> - stop("Failed to create user '~s': ~p", [Name, Err]) + {error, invalid_password} when (Pass == <<>>) -> + process_user_els(Els, State#state{user = LUser}); + {error, Err} -> + stop("Failed to create user '~ts': ~p", [Name, Err]) end end. +process_password(#xmlel{name = <<"user">>, attrs = Attrs} = El, LServer) -> + {PassPlain, PassOldScram} = case fxml:get_attr_s(<<"password">>, Attrs) of + <<"scram:", PassData/binary>> -> {<<"">>, PassData}; + P -> {P, false} + end, + ScramCred = fxml:get_subtag(El, <<"scram-credentials">>), + PasswordFormat = ejabberd_auth:password_format(LServer), + case {PassPlain, PassOldScram, ScramCred, PasswordFormat} of + {PassPlain, false, false, plain} -> PassPlain; + {<<"">>, false, ScramCred, plain} -> parse_scram_password(ScramCred); + {<<"">>, PassOldScram, false, plain} -> parse_scram_password(PassOldScram); + {PassPlain, false, false, scram} -> PassPlain; + {<<"">>, false, ScramCred, scram} -> parse_scram_password(ScramCred); + {<<"">>, PassOldScram, false, scram} -> parse_scram_password(PassOldScram) + end. + process_user_els([#xmlel{} = El|Els], State) -> case process_user_el(El, State) of {ok, NewState} -> @@ -439,7 +498,7 @@ process_user_el(#xmlel{name = Name, attrs = Attrs, children = Els} = El, {<<"query">>, ?NS_PRIVATE} -> process_private(xmpp:decode(El), State); {<<"vCard">>, ?NS_VCARD} -> - process_vcard(El, State); + process_vcard(xmpp:decode(El), State); {<<"offline-messages">>, NS} -> Msgs = [xmpp:decode(E, NS, [ignore_els]) || E <- Els], process_offline_msgs(Msgs, State); @@ -450,7 +509,7 @@ process_user_el(#xmlel{name = Name, attrs = Attrs, children = Els} = El, end catch _:{xmpp_codec, Why} -> ErrTxt = xmpp:format_error(Why), - stop("failed to decode XML '~s': ~s", + stop("failed to decode XML '~ts': ~ts", [fxml:element_to_binary(El), ErrTxt]) end. @@ -479,31 +538,47 @@ process_roster(RosterQuery, State = #state{user = U, server = S}) -> -spec process_privacy(privacy_query(), state()) -> {ok, state()} | {error, _}. process_privacy(#privacy_query{lists = Lists, default = Default, - active = Active} = PrivacyQuery, + active = Active}, State = #state{user = U, server = S}) -> JID = jid:make(U, S), - IQ = #iq{type = set, id = randoms:get_string(), - from = JID, to = JID, sub_els = [PrivacyQuery]}, - Txt = <<"No module is handling this query">>, - Error = {error, xmpp:err_feature_not_implemented(Txt, ?MYLANG)}, - case mod_privacy:process_iq_set(Error, IQ, #userlist{}) of - {error, #stanza_error{reason = Reason}} = Err -> - if Reason == 'item-not-found', Lists == [], - Active == undefined, Default /= undefined -> + if Lists /= undefined -> + process_privacy2(JID, #privacy_query{lists = Lists}); + true -> + ok + end, + if Active /= undefined -> + process_privacy2(JID, #privacy_query{active = Active}); + true -> + ok + end, + if Default /= undefined -> + process_privacy2(JID, #privacy_query{default = Default}); + true -> + ok + end, + {ok, State}. + +process_privacy2(JID, PQ) -> + case mod_privacy:process_iq(#iq{type = set, id = p1_rand:get_string(), + from = JID, to = JID, + sub_els = [PQ]}) of + #iq{type = error} = ResIQ -> + #stanza_error{reason = Reason} = xmpp:get_error(ResIQ), + if Reason /= 'item-not-found' -> %% Failed to set default list because there is no %% list with such name. We shouldn't stop here. - {ok, State}; - true -> - stop("Failed to write privacy: ~p", [Err]) - end; - _ -> - {ok, State} - end. + stop("Failed to write default privacy: ~p", [Reason]); + true -> + ok + end; + _ -> + ok + end. -spec process_private(private(), state()) -> {ok, state()} | {error, _}. process_private(Private, State = #state{user = U, server = S}) -> JID = jid:make(U, S), - IQ = #iq{type = set, id = randoms:get_string(), + IQ = #iq{type = set, id = p1_rand:get_string(), from = JID, to = JID, sub_els = [Private]}, case mod_private:process_sm_iq(IQ) of #iq{type = result} -> @@ -512,10 +587,10 @@ process_private(Private, State = #state{user = U, server = S}) -> stop("Failed to write private: ~p", [Err]) end. --spec process_vcard(xmlel(), state()) -> {ok, state()} | {error, _}. +-spec process_vcard(xmpp_element(), state()) -> {ok, state()} | {error, _}. process_vcard(El, State = #state{user = U, server = S}) -> JID = jid:make(U, S), - IQ = #iq{type = set, id = randoms:get_string(), + IQ = #iq{type = set, id = p1_rand:get_string(), from = JID, to = JID, sub_els = [El]}, case mod_vcard:process_sm_iq(IQ) of #iq{type = result} -> @@ -528,24 +603,18 @@ process_vcard(El, State = #state{user = U, server = S}) -> process_offline_msg(#message{from = undefined}, _State) -> stop("No 'from' attribute found", []); process_offline_msg(Msg, State = #state{user = U, server = S}) -> - From = xmpp:get_from(Msg), - To = jid:make(U, S, <<>>), - NewMsg = xmpp:set_from_to(Msg, From, To), - case catch mod_offline:store_packet(From, To, NewMsg) of - {'EXIT', _} = Err -> - stop("Failed to store offline message: ~p", [Err]); - _ -> - {ok, State} - end. + To = jid:make(U, S), + ejabberd_hooks:run_fold( + offline_message_hook, To#jid.lserver, {pass, xmpp:set_to(Msg, To)}, []), + {ok, State}. -spec process_presence(presence(), state()) -> {ok, state()} | {error, _}. process_presence(#presence{from = undefined}, _State) -> stop("No 'from' attribute found", []); process_presence(Pres, #state{user = U, server = S} = State) -> - From = xmpp:get_from(Pres), - To = jid:make(U, S, <<>>), - NewPres = xmpp:set_from_to(Pres, From, To), - ejabberd_router:route(From, To, NewPres), + To = jid:make(U, S), + NewPres = xmpp:set_to(Pres, To), + ejabberd_router:route(NewPres), {ok, State}. stop(Fmt, Args) -> @@ -562,8 +631,8 @@ make_main_basefilename(Dir, FnT) -> filename:join([Dir, Filename2]). %% @doc Make the filename for the host. -%% Example: ``(<<"20080804-231550">>, <<"jabber.example.org">>) -> -%% <<"20080804-231550_jabber_example_org.xml">>'' +%% Example: ``(<<"20080804-231550">>, <<"xmpp.domain.tld">>) -> +%% <<"20080804-231550_xmpp_domain_tld.xml">>'' make_host_filename(FnT, Host) -> Host2 = str:join(str:tokens(Host, <<".">>), <<"_">>), <>. @@ -573,40 +642,29 @@ make_host_filename(FnT, Host) -> make_host_basefilename(Dir, FnT) -> filename:join([Dir, FnT]). -%% @spec () -> string() make_piefxis_xml_head() -> "". -%% @spec () -> string() make_piefxis_xml_tail() -> "". -%% @spec () -> string() make_piefxis_server_head() -> - io_lib:format("", + io_lib:format("", [?NS_PIE, ?NS_XI]). -%% @spec () -> string() make_piefxis_server_tail() -> "". -%% @spec (Host::string()) -> string() make_piefxis_host_head(Host) -> - io_lib:format("", + io_lib:format("", [?NS_PIE, ?NS_XI, Host]). -%% @spec () -> string() make_piefxis_host_tail() -> "". -%% @spec (Fn::string()) -> string() make_xinclude(Fn) -> Base = filename:basename(Fn), - io_lib:format("", [Base]). + io_lib:format("", [Base]). print(Fd, String) -> file:write(Fd, String). - -opt_type(auth_password_format) -> fun (X) -> X end; -opt_type(_) -> [auth_password_format]. - diff --git a/src/ejabberd_pkix.erl b/src/ejabberd_pkix.erl new file mode 100644 index 000000000..b699454dd --- /dev/null +++ b/src/ejabberd_pkix.erl @@ -0,0 +1,433 @@ +%%%------------------------------------------------------------------- +%%% Author : Evgeny Khramtsov +%%% Created : 4 Mar 2017 by Evgeny Khramtsov +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%------------------------------------------------------------------- +-module(ejabberd_pkix). +-behaviour(gen_server). + +%% API +-export([start_link/0]). +-export([certs_dir/0]). +-export([add_certfile/1, del_certfile/1, commit/0]). +-export([notify_expired/1]). +-export([try_certfile/1, get_certfile/0, get_certfile/1]). +-export([get_certfile_no_default/1]). +%% Hooks +-export([ejabberd_started/0, config_reloaded/0, cert_expired/2]). +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +-include("logger.hrl"). +-define(CALL_TIMEOUT, timer:minutes(1)). + +-record(state, {files = sets:new() :: sets:set(filename())}). + +-type state() :: #state{}. +-type filename() :: binary(). + +%%%=================================================================== +%%% API +%%%=================================================================== +-spec start_link() -> {ok, pid()} | {error, {already_started, pid()} | term()}. +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +-spec add_certfile(file:filename_all()) -> {ok, filename()} | {error, pkix:error_reason()}. +add_certfile(Path0) -> + Path = prep_path(Path0), + try gen_server:call(?MODULE, {add_certfile, Path}, ?CALL_TIMEOUT) + catch exit:{noproc, _} -> + case add_file(Path) of + ok -> {ok, Path}; + Err -> Err + end + end. + +-spec del_certfile(file:filename_all()) -> ok. +del_certfile(Path0) -> + Path = prep_path(Path0), + try gen_server:call(?MODULE, {del_certfile, Path}, ?CALL_TIMEOUT) + catch exit:{noproc, _} -> + pkix:del_file(Path) + end. + +-spec try_certfile(file:filename_all()) -> filename(). +try_certfile(Path0) -> + Path = prep_path(Path0), + case pkix:is_pem_file(Path) of + true -> Path; + {false, Reason} -> + ?ERROR_MSG("Failed to read PEM file ~ts: ~ts", + [Path, pkix:format_error(Reason)]), + erlang:error(badarg) + end. + +-spec get_certfile(binary()) -> {ok, filename()} | error. +get_certfile(Domain) -> + case get_certfile_no_default(Domain) of + {ok, Path} -> + {ok, Path}; + error -> + get_certfile() + end. + +-spec get_certfile_no_default(binary()) -> {ok, filename()} | error. +get_certfile_no_default(Domain) -> + try list_to_binary(idna:utf8_to_ascii(Domain)) of + ASCIIDomain -> + case pkix:get_certfile(ASCIIDomain) of + error -> error; + Ret -> {ok, select_certfile(Ret)} + end + catch _:_ -> + error + end. + +-spec get_certfile() -> {ok, filename()} | error. +get_certfile() -> + case pkix:get_certfile() of + error -> error; + Ret -> {ok, select_certfile(Ret)} + end. + +-spec certs_dir() -> file:filename_all(). +certs_dir() -> + MnesiaDir = mnesia:system_info(directory), + filename:join(MnesiaDir, "certs"). + +-spec commit() -> ok. +commit() -> + gen_server:call(?MODULE, commit, ?CALL_TIMEOUT). + +-spec ejabberd_started() -> ok. +ejabberd_started() -> + gen_server:call(?MODULE, ejabberd_started, ?CALL_TIMEOUT). + +-spec config_reloaded() -> ok. +config_reloaded() -> + gen_server:call(?MODULE, config_reloaded, ?CALL_TIMEOUT). + +-spec notify_expired(pkix:notify_event()) -> ok. +notify_expired(Event) -> + gen_server:cast(?MODULE, Event). + +-spec cert_expired(_, pkix:cert_info()) -> ok. +cert_expired(_Cert, #{domains := Domains, + expiry := Expiry, + files := [{Path, Line}|_]}) -> + ?WARNING_MSG("Certificate in ~ts (at line: ~B)~ts ~ts", + [Path, Line, + case Domains of + [] -> ""; + _ -> " for " ++ misc:format_hosts_list(Domains) + end, + format_expiration_date(Expiry)]). + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== +-spec init([]) -> {ok, state()}. +init([]) -> + process_flag(trap_exit, true), + ejabberd_hooks:add(cert_expired, ?MODULE, cert_expired, 50), + ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 100), + ejabberd_hooks:add(ejabberd_started, ?MODULE, ejabberd_started, 30), + case add_files() of + {_Files, []} -> + {ok, #state{}}; + {Files, [_|_]} -> + case ejabberd:is_loaded() of + true -> + {ok, #state{}}; + false -> + del_files(Files), + stop_ejabberd() + end + end. + +-spec handle_call(term(), {pid(), term()}, state()) -> + {reply, ok, state()} | {noreply, state()}. +handle_call({add_certfile, Path}, _From, State) -> + case add_file(Path) of + ok -> + {reply, {ok, Path}, State}; + {error, _} = Err -> + {reply, Err, State} + end; +handle_call({del_certfile, Path}, _From, State) -> + pkix:del_file(Path), + {reply, ok, State}; +handle_call(ejabberd_started, _From, State) -> + case do_commit() of + {ok, []} -> + check_domain_certfiles(), + {reply, ok, State}; + _ -> + stop_ejabberd() + end; +handle_call(config_reloaded, _From, State) -> + Files = get_certfiles_from_config_options(), + _ = add_files(Files), + case do_commit() of + {ok, _} -> + check_domain_certfiles(), + {reply, ok, State}; + error -> + {reply, ok, State} + end; +handle_call(commit, From, State) -> + handle_call(config_reloaded, From, State); +handle_call(Request, _From, State) -> + ?WARNING_MSG("Unexpected call: ~p", [Request]), + {noreply, State}. + +-spec handle_cast(term(), state()) -> {noreply, state()}. +handle_cast({cert_expired, Cert, CertInfo}, State) -> + ejabberd_hooks:run(cert_expired, [Cert, CertInfo]), + {noreply, State}; +handle_cast(Request, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Request]), + {noreply, State}. + +-spec handle_info(term(), state()) -> {noreply, state()}. +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), + {noreply, State}. + +-spec terminate(normal | shutdown | {shutdown, term()} | term(), + state()) -> any(). +terminate(_Reason, State) -> + ejabberd_hooks:delete(cert_expired, ?MODULE, cert_expired, 50), + ejabberd_hooks:delete(ejabberd_started, ?MODULE, ejabberd_started, 30), + ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 100), + del_files(State#state.files). + +-spec code_change(term() | {down, term()}, state(), term()) -> {ok, state()}. +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +-spec add_files() -> {sets:set(filename()), [{filename(), pkix:error_reason()}]}. +add_files() -> + Files = get_certfiles_from_config_options(), + add_files(sets:to_list(Files), sets:new(), []). + +-spec add_files(sets:set(filename())) -> + {sets:set(filename()), [{filename(), pkix:error_reason()}]}. +add_files(Files) -> + add_files(sets:to_list(Files), sets:new(), []). + +-spec add_files([filename()], sets:set(filename()), + [{filename(), pkix:error_reason()}]) -> + {sets:set(filename()), [{filename(), pkix:error_reason()}]}. +add_files([File|Files], Set, Errs) -> + case add_file(File) of + ok -> + Set1 = sets:add_element(File, Set), + add_files(Files, Set1, Errs); + {error, Reason} -> + Errs1 = [{File, Reason}|Errs], + add_files(Files, Set, Errs1) + end; +add_files([], Set, Errs) -> + {Set, Errs}. + +-spec add_file(filename()) -> ok | {error, pkix:error_reason()}. +add_file(File) -> + case pkix:add_file(File) of + ok -> ok; + {error, Reason} = Err -> + ?ERROR_MSG("Failed to read PEM file ~ts: ~ts", + [File, pkix:format_error(Reason)]), + Err + end. + +-spec del_files(sets:set(filename())) -> ok. +del_files(Files) -> + lists:foreach(fun pkix:del_file/1, sets:to_list(Files)). + +-spec do_commit() -> {ok, [{filename(), pkix:error_reason()}]} | error. +do_commit() -> + CAFile = ejabberd_option:ca_file(), + ?DEBUG("Using CA root certificates from: ~ts", [CAFile]), + Opts = [{cafile, CAFile}, + {notify_before, [7*24*60*60, % 1 week + 24*60*60, % 1 day + 60*60, % 1 hour + 0]}, + {notify_fun, fun ?MODULE:notify_expired/1}], + case pkix:commit(certs_dir(), Opts) of + {ok, Errors, Warnings, CAError} -> + log_errors(Errors), + log_cafile_error(CAError), + log_warnings(Warnings), + fast_tls_add_certfiles(), + {ok, Errors}; + {error, File, Reason} -> + ?CRITICAL_MSG("Failed to write to ~ts: ~ts", + [File, file:format_error(Reason)]), + error + end. + +-spec check_domain_certfiles() -> ok. +check_domain_certfiles() -> + Hosts = ejabberd_option:hosts(), + Routes = ejabberd_router:get_all_routes(), + check_domain_certfiles(Hosts ++ Routes). + +-spec check_domain_certfiles([binary()]) -> ok. +check_domain_certfiles(Hosts) -> + case ejabberd_listener:tls_listeners() of + [] -> ok; + _ -> + lists:foreach( + fun(Host) -> + case get_certfile_no_default(Host) of + error -> + ?WARNING_MSG( + "No certificate found matching ~ts", + [Host]); + _ -> + ok + end + end, Hosts) + end. + +-spec get_certfiles_from_config_options() -> sets:set(filename()). +get_certfiles_from_config_options() -> + case ejabberd_option:certfiles() of + undefined -> + sets:new(); + Paths -> + lists:foldl( + fun(Path, Acc) -> + Files = wildcard(Path), + lists:foldl(fun sets:add_element/2, Acc, Files) + end, sets:new(), Paths) + end. + +-spec prep_path(file:filename_all()) -> filename(). +prep_path(Path0) -> + case filename:pathtype(Path0) of + relative -> + case file:get_cwd() of + {ok, CWD} -> + unicode:characters_to_binary(filename:join(CWD, Path0)); + {error, Reason} -> + ?WARNING_MSG("Failed to get current directory name: ~ts", + [file:format_error(Reason)]), + unicode:characters_to_binary(Path0) + end; + _ -> + unicode:characters_to_binary(Path0) + end. + +-spec stop_ejabberd() -> no_return(). +stop_ejabberd() -> + ?CRITICAL_MSG("ejabberd initialization was aborted due to " + "invalid certificates configuration", []), + ejabberd:halt(). + +-spec wildcard(file:filename_all()) -> [filename()]. +wildcard(Path) when is_binary(Path) -> + wildcard(binary_to_list(Path)); +wildcard(Path) -> + case filelib:wildcard(Path) of + [] -> + ?WARNING_MSG("Path ~ts is empty, please make sure ejabberd has " + "sufficient rights to read it", [Path]), + []; + Files -> + [prep_path(File) || File <- Files] + end. + +-spec select_certfile({filename() | undefined, + filename() | undefined, + filename() | undefined}) -> filename(). +select_certfile({EC, _, _}) when EC /= undefined -> EC; +select_certfile({_, RSA, _}) when RSA /= undefined -> RSA; +select_certfile({_, _, DSA}) when DSA /= undefined -> DSA. + +-spec fast_tls_add_certfiles() -> ok. +fast_tls_add_certfiles() -> + lists:foreach( + fun({Domain, Files}) -> + fast_tls:add_certfile(Domain, select_certfile(Files)) + end, pkix:get_certfiles()), + fast_tls:clear_cache(). + +reason_to_fmt({invalid_cert, _, _}) -> + "Invalid certificate in ~ts: ~ts"; +reason_to_fmt(_) -> + "Failed to read PEM file ~ts: ~ts". + +-spec log_warnings([{filename(), pkix:error_reason()}]) -> ok. +log_warnings(Warnings) -> + lists:foreach( + fun({File, Reason}) -> + ?WARNING_MSG(reason_to_fmt(Reason), + [File, pkix:format_error(Reason)]) + end, Warnings). + +-spec log_errors([{filename(), pkix:error_reason()}]) -> ok. +log_errors(Errors) -> + lists:foreach( + fun({File, Reason}) -> + ?ERROR_MSG(reason_to_fmt(Reason), + [File, pkix:format_error(Reason)]) + end, Errors). + +-spec log_cafile_error({filename(), pkix:error_reason()} | undefined) -> ok. +log_cafile_error({File, Reason}) -> + ?CRITICAL_MSG("Failed to read CA certitificates from ~ts: ~ts. " + "Try to change/set option 'ca_file'", + [File, pkix:format_error(Reason)]); +log_cafile_error(_) -> + ok. + +-spec time_before_expiration(calendar:datetime()) -> {non_neg_integer(), string()}. +time_before_expiration(Expiry) -> + T1 = calendar:datetime_to_gregorian_seconds(Expiry), + T2 = calendar:datetime_to_gregorian_seconds( + calendar:now_to_datetime(erlang:timestamp())), + Secs = max(0, T1 - T2), + if Secs == {0, ""}; + Secs >= 220752000 -> {round(Secs/220752000), "year"}; + Secs >= 2592000 -> {round(Secs/2592000), "month"}; + Secs >= 604800 -> {round(Secs/604800), "week"}; + Secs >= 86400 -> {round(Secs/86400), "day"}; + Secs >= 3600 -> {round(Secs/3600), "hour"}; + Secs >= 60 -> {round(Secs/60), "minute"}; + true -> {Secs, "second"} + end. + +-spec format_expiration_date(calendar:datetime()) -> string(). +format_expiration_date(DateTime) -> + case time_before_expiration(DateTime) of + {0, _} -> "is expired"; + {1, Unit} -> "will expire in a " ++ Unit; + {Int, Unit} -> + "will expire in " ++ integer_to_list(Int) + ++ " " ++ Unit ++ "s" + end. diff --git a/src/ejabberd_rdbms.erl b/src/ejabberd_rdbms.erl deleted file mode 100644 index 5224b035b..000000000 --- a/src/ejabberd_rdbms.erl +++ /dev/null @@ -1,101 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : ejabberd_rdbms.erl -%%% Author : Mickael Remond -%%% Purpose : Manage the start of the database modules when needed -%%% Created : 31 Jan 2003 by Alexey Shchepin -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne -%%% -%%% This program is free software; you can redistribute it and/or -%%% modify it under the terms of the GNU General Public License as -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License along -%%% with this program; if not, write to the Free Software Foundation, Inc., -%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -%%% -%%%---------------------------------------------------------------------- - --module(ejabberd_rdbms). - --behaviour(ejabberd_config). - --author('alexey@process-one.net'). - --export([start/0, opt_type/1]). - --include("ejabberd.hrl"). --include("logger.hrl"). - -start() -> - file:delete(ejabberd_sql:freetds_config()), - file:delete(ejabberd_sql:odbc_config()), - file:delete(ejabberd_sql:odbcinst_config()), - case lists:any(fun(H) -> needs_sql(H) /= false end, - ?MYHOSTS) of - true -> - start_hosts(); - false -> - ok - end. - -%% Start relationnal DB module on the nodes where it is needed -start_hosts() -> - lists:foreach(fun (Host) -> - case needs_sql(Host) of - {true, App} -> start_sql(Host, App); - false -> ok - end - end, - ?MYHOSTS). - -%% Start the SQL module on the given host -start_sql(Host, App) -> - ejabberd:start_app(App), - Supervisor_name = gen_mod:get_module_proc(Host, - ejabberd_sql_sup), - ChildSpec = {Supervisor_name, - {ejabberd_sql_sup, start_link, [Host]}, transient, - infinity, supervisor, [ejabberd_sql_sup]}, - case supervisor:start_child(ejabberd_sup, ChildSpec) of - {ok, _PID} -> ok; - _Error -> - ?ERROR_MSG("Start of supervisor ~p failed:~n~p~nRetrying." - "..~n", - [Supervisor_name, _Error]), - start_sql(Host, App) - end. - -%% Returns {true, App} if we have configured sql for the given host -needs_sql(Host) -> - LHost = jid:nameprep(Host), - case ejabberd_config:get_option({sql_type, LHost}, - fun(mysql) -> mysql; - (pgsql) -> pgsql; - (sqlite) -> sqlite; - (mssql) -> mssql; - (odbc) -> odbc - end, undefined) of - mysql -> {true, p1_mysql}; - pgsql -> {true, p1_pgsql}; - sqlite -> {true, sqlite3}; - mssql -> {true, odbc}; - odbc -> {true, odbc}; - undefined -> false - end. - -opt_type(sql_type) -> - fun (mysql) -> mysql; - (pgsql) -> pgsql; - (sqlite) -> sqlite; - (mssql) -> mssql; - (odbc) -> odbc - end; -opt_type(_) -> [sql_type]. diff --git a/src/ejabberd_receiver.erl b/src/ejabberd_receiver.erl deleted file mode 100644 index 0a33e30ec..000000000 --- a/src/ejabberd_receiver.erl +++ /dev/null @@ -1,341 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : ejabberd_receiver.erl -%%% Author : Alexey Shchepin -%%% Purpose : Socket receiver for C2S and S2S connections -%%% Created : 10 Nov 2003 by Alexey Shchepin -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne -%%% -%%% This program is free software; you can redistribute it and/or -%%% modify it under the terms of the GNU General Public License as -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License along -%%% with this program; if not, write to the Free Software Foundation, Inc., -%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -%%% -%%%---------------------------------------------------------------------- - --module(ejabberd_receiver). - --author('alexey@process-one.net'). - --behaviour(gen_server). - -%% API --export([start_link/4, - start/3, - start/4, - change_shaper/2, - reset_stream/1, - starttls/2, - compress/2, - become_controller/2, - close/1]). - -%% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, - handle_info/2, terminate/2, code_change/3]). - --include("ejabberd.hrl"). --include("logger.hrl"). - --record(state, - {socket :: inet:socket() | fast_tls:tls_socket() | ezlib:zlib_socket(), - sock_mod = gen_tcp :: gen_tcp | fast_tls | ezlib, - shaper_state = none :: shaper:shaper(), - c2s_pid :: pid(), - max_stanza_size = infinity :: non_neg_integer() | infinity, - xml_stream_state :: fxml_stream:xml_stream_state(), - timeout = infinity:: timeout()}). - --define(HIBERNATE_TIMEOUT, ejabberd_config:get_option(receiver_hibernate, fun(X) when is_integer(X); X == hibernate-> X end, 90000)). - - --spec start_link(inet:socket(), atom(), shaper:shaper(), - non_neg_integer() | infinity) -> ignore | - {error, any()} | - {ok, pid()}. - -start_link(Socket, SockMod, Shaper, MaxStanzaSize) -> - gen_server:start_link(?MODULE, - [Socket, SockMod, Shaper, MaxStanzaSize], []). - --spec start(inet:socket(), atom(), shaper:shaper()) -> undefined | pid(). - -start(Socket, SockMod, Shaper) -> - start(Socket, SockMod, Shaper, infinity). - --spec start(inet:socket(), atom(), shaper:shaper(), - non_neg_integer() | infinity) -> undefined | pid(). - -start(Socket, SockMod, Shaper, MaxStanzaSize) -> - {ok, Pid} = gen_server:start(ejabberd_receiver, - [Socket, SockMod, Shaper, MaxStanzaSize], []), - Pid. - --spec change_shaper(pid(), shaper:shaper()) -> ok. - -change_shaper(Pid, Shaper) -> - gen_server:cast(Pid, {change_shaper, Shaper}). - --spec reset_stream(pid()) -> ok | {error, any()}. - -reset_stream(Pid) -> do_call(Pid, reset_stream). - --spec starttls(pid(), fast_tls:tls_socket()) -> ok. - -starttls(Pid, TLSSocket) -> - do_call(Pid, {starttls, TLSSocket}). - --spec compress(pid(), iodata() | undefined) -> {error, any()} | - {ok, ezlib:zlib_socket()}. - -compress(Pid, Data) -> - do_call(Pid, {compress, Data}). - --spec become_controller(pid(), pid()) -> ok | {error, any()}. - -become_controller(Pid, C2SPid) -> - do_call(Pid, {become_controller, C2SPid}). - --spec close(pid()) -> ok. - -close(Pid) -> - gen_server:cast(Pid, close). - - -%%==================================================================== -%% gen_server callbacks -%%==================================================================== - -init([Socket, SockMod, Shaper, MaxStanzaSize]) -> - ShaperState = shaper:new(Shaper), - Timeout = case SockMod of - ssl -> 20; - _ -> infinity - end, - {ok, - #state{socket = Socket, sock_mod = SockMod, - shaper_state = ShaperState, - max_stanza_size = MaxStanzaSize, timeout = Timeout}}. - -handle_call({starttls, TLSSocket}, _From, State) -> - State1 = reset_parser(State), - NewState = State1#state{socket = TLSSocket, - sock_mod = fast_tls}, - case fast_tls:recv_data(TLSSocket, <<"">>) of - {ok, TLSData} -> - {reply, ok, - process_data(TLSData, NewState), ?HIBERNATE_TIMEOUT}; - {error, _Reason} -> - {stop, normal, ok, NewState} - end; -handle_call({compress, Data}, _From, - #state{socket = Socket, sock_mod = SockMod} = - State) -> - ejabberd:start_app(ezlib), - {ok, ZlibSocket} = ezlib:enable_zlib(SockMod, - Socket), - if Data /= undefined -> do_send(State, Data); - true -> ok - end, - State1 = reset_parser(State), - NewState = State1#state{socket = ZlibSocket, - sock_mod = ezlib}, - case ezlib:recv_data(ZlibSocket, <<"">>) of - {ok, ZlibData} -> - {reply, {ok, ZlibSocket}, - process_data(ZlibData, NewState), ?HIBERNATE_TIMEOUT}; - {error, _Reason} -> - {stop, normal, ok, NewState} - end; -handle_call(reset_stream, _From, State) -> - NewState = reset_parser(State), - Reply = ok, - {reply, Reply, NewState, ?HIBERNATE_TIMEOUT}; -handle_call({become_controller, C2SPid}, _From, State) -> - XMLStreamState = fxml_stream:new(C2SPid, State#state.max_stanza_size), - NewState = State#state{c2s_pid = C2SPid, - xml_stream_state = XMLStreamState}, - activate_socket(NewState), - Reply = ok, - {reply, Reply, NewState, ?HIBERNATE_TIMEOUT}; -handle_call(_Request, _From, State) -> - Reply = ok, {reply, Reply, State, ?HIBERNATE_TIMEOUT}. - -handle_cast({change_shaper, Shaper}, State) -> - NewShaperState = shaper:new(Shaper), - {noreply, State#state{shaper_state = NewShaperState}, - ?HIBERNATE_TIMEOUT}; -handle_cast(close, State) -> {stop, normal, State}; -handle_cast(_Msg, State) -> - {noreply, State, ?HIBERNATE_TIMEOUT}. - -handle_info({Tag, _TCPSocket, Data}, - #state{socket = Socket, sock_mod = SockMod} = State) - when (Tag == tcp) or (Tag == ssl) or - (Tag == ejabberd_xml) -> - case SockMod of - fast_tls -> - case fast_tls:recv_data(Socket, Data) of - {ok, TLSData} -> - {noreply, process_data(TLSData, State), - ?HIBERNATE_TIMEOUT}; - {error, Reason} -> - if is_binary(Reason) -> - ?DEBUG("TLS error = ~s", [Reason]); - true -> - ok - end, - {stop, normal, State} - end; - ezlib -> - case ezlib:recv_data(Socket, Data) of - {ok, ZlibData} -> - {noreply, process_data(ZlibData, State), - ?HIBERNATE_TIMEOUT}; - {error, _Reason} -> {stop, normal, State} - end; - _ -> - {noreply, process_data(Data, State), ?HIBERNATE_TIMEOUT} - end; -handle_info({Tag, _TCPSocket}, State) - when (Tag == tcp_closed) or (Tag == ssl_closed) -> - {stop, normal, State}; -handle_info({Tag, _TCPSocket, Reason}, State) - when (Tag == tcp_error) or (Tag == ssl_error) -> - case Reason of - timeout -> {noreply, State, ?HIBERNATE_TIMEOUT}; - _ -> {stop, normal, State} - end; -handle_info({timeout, _Ref, activate}, State) -> - activate_socket(State), - {noreply, State, ?HIBERNATE_TIMEOUT}; -handle_info(timeout, State) -> - proc_lib:hibernate(gen_server, enter_loop, - [?MODULE, [], State]), - {noreply, State, ?HIBERNATE_TIMEOUT}; -handle_info(_Info, State) -> - {noreply, State, ?HIBERNATE_TIMEOUT}. - -terminate(_Reason, - #state{xml_stream_state = XMLStreamState, - c2s_pid = C2SPid} = - State) -> - close_stream(XMLStreamState), - if C2SPid /= undefined -> - gen_fsm:send_event(C2SPid, closed); - true -> ok - end, - catch (State#state.sock_mod):close(State#state.socket), - ok. - -code_change(_OldVsn, State, _Extra) -> {ok, State}. - -%%-------------------------------------------------------------------- -%%% Internal functions -%%-------------------------------------------------------------------- - -activate_socket(#state{socket = Socket, - sock_mod = SockMod}) -> - PeerName = case SockMod of - gen_tcp -> - inet:setopts(Socket, [{active, once}]), - inet:peername(Socket); - _ -> - SockMod:setopts(Socket, [{active, once}]), - SockMod:peername(Socket) - end, - case PeerName of - {error, _Reason} -> self() ! {tcp_closed, Socket}; - {ok, _} -> ok - end. - -%% Data processing for connectors directly generating xmlelement in -%% Erlang data structure. -%% WARNING: Shaper does not work with Erlang data structure. -process_data([], State) -> - activate_socket(State), State; -process_data([Element | Els], - #state{c2s_pid = C2SPid} = State) - when element(1, Element) == xmlel; - element(1, Element) == xmlstreamstart; - element(1, Element) == xmlstreamelement; - element(1, Element) == xmlstreamend -> - if C2SPid == undefined -> State; - true -> - catch gen_fsm:send_event(C2SPid, - element_wrapper(Element)), - process_data(Els, State) - end; -%% Data processing for connectors receivind data as string. -process_data(Data, - #state{xml_stream_state = XMLStreamState, - shaper_state = ShaperState, c2s_pid = C2SPid} = - State) -> - ?DEBUG("Received XML on stream = ~p", [(Data)]), - XMLStreamState1 = case XMLStreamState of - undefined -> - XMLStreamState; - _ -> - fxml_stream:parse(XMLStreamState, Data) - end, - {NewShaperState, Pause} = shaper:update(ShaperState, byte_size(Data)), - if - C2SPid == undefined -> - ok; - Pause > 0 -> - erlang:start_timer(Pause, self(), activate); - true -> - activate_socket(State) - end, - State#state{xml_stream_state = XMLStreamState1, - shaper_state = NewShaperState}. - -%% Element coming from XML parser are wrapped inside xmlstreamelement -%% When we receive directly xmlelement tuple (from a socket module -%% speaking directly Erlang XML), we wrap it inside the same -%% xmlstreamelement coming from the XML parser. -element_wrapper(XMLElement) - when element(1, XMLElement) == xmlel -> - {xmlstreamelement, XMLElement}; -element_wrapper(Element) -> Element. - -close_stream(undefined) -> ok; -close_stream(XMLStreamState) -> - fxml_stream:close(XMLStreamState). - -reset_parser(#state{xml_stream_state = undefined} = State) -> - State; -reset_parser(#state{c2s_pid = C2SPid, - max_stanza_size = MaxStanzaSize, - xml_stream_state = XMLStreamState} - = State) -> - NewStreamState = try fxml_stream:reset(XMLStreamState) - catch error:_ -> - close_stream(XMLStreamState), - case C2SPid of - undefined -> - undefined; - _ -> - fxml_stream:new(C2SPid, MaxStanzaSize) - end - end, - State#state{xml_stream_state = NewStreamState}. - -do_send(State, Data) -> - (State#state.sock_mod):send(State#state.socket, Data). - -do_call(Pid, Msg) -> - case catch gen_server:call(Pid, Msg) of - {'EXIT', Why} -> {error, Why}; - Res -> Res - end. diff --git a/src/ejabberd_redis.erl b/src/ejabberd_redis.erl index c6e3b4dd0..c0e61c0e6 100644 --- a/src/ejabberd_redis.erl +++ b/src/ejabberd_redis.erl @@ -1,86 +1,458 @@ %%%------------------------------------------------------------------- -%%% @author Evgeny Khramtsov -%%% @copyright (C) 2016, Evgeny Khramtsov -%%% @doc -%%% -%%% @end +%%% File : ejabberd_redis.erl +%%% Author : Evgeny Khramtsov %%% Created : 8 May 2016 by Evgeny Khramtsov -%%%------------------------------------------------------------------- --module(ejabberd_redis). +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2025 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. +%%% +%%%---------------------------------------------------------------------- --behaviour(gen_server). --behaviour(ejabberd_config). +-module(ejabberd_redis). +-ifndef(GEN_SERVER). +-define(GEN_SERVER, gen_server). +-endif. +-behaviour(?GEN_SERVER). + +-compile({no_auto_import, [get/1, put/2]}). %% API --export([start/0, start_link/0, q/1, qp/1, opt_type/1]). +-export([start_link/1, get_proc/1, get_connection/1, q/1, qp/1, format_error/1]). +%% Commands +-export([multi/1, get/1, set/2, del/1, info/1, + sadd/2, srem/2, smembers/1, sismember/2, scard/1, + hget/2, hset/3, hdel/2, hlen/1, hgetall/1, hkeys/1, + subscribe/1, publish/2, script_load/1, evalsha/3]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --define(SERVER, ?MODULE). --define(PROCNAME, 'ejabberd_redis_client'). +-define(TR_STACK, redis_transaction_stack). +-define(DEFAULT_MAX_QUEUE, 10000). +-define(MAX_RETRIES, 1). +-define(CALL_TIMEOUT, 60*1000). %% 60 seconds -include("logger.hrl"). --include("ejabberd.hrl"). --record(state, {}). + +-record(state, {connection :: pid() | undefined, + num :: pos_integer(), + subscriptions = #{} :: subscriptions(), + pending_q :: queue()}). + +-type queue() :: p1_queue:queue({{pid(), term()}, integer()}). +-type subscriptions() :: #{binary() => [pid()]}. +-type error_reason() :: binary() | timeout | disconnected | overloaded. +-type redis_error() :: {error, error_reason()}. +-type redis_reply() :: undefined | binary() | [binary()]. +-type redis_command() :: [iodata() | integer()]. +-type redis_pipeline() :: [redis_command()]. +-type redis_info() :: server | clients | memory | persistence | + stats | replication | cpu | commandstats | + cluster | keyspace | default | all. +-type state() :: #state{}. + +-export_type([error_reason/0]). + +-ifdef(USE_OLD_HTTP_URI). % Erlang/OTP lower than 21 +-dialyzer([{no_return, do_connect/6}, + {no_unused, flush_queue/1}, + {no_match, flush_queue/1}, + {no_unused, re_subscribe/2}, + {no_match, handle_info/2}]). +-endif. %%%=================================================================== %%% API %%%=================================================================== -start_link() -> - gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). +start_link(I) -> + ?GEN_SERVER:start_link({local, get_proc(I)}, ?MODULE, [I], []). -start() -> - case lists:any( - fun(Host) -> - is_redis_configured(Host) - end, ?MYHOSTS) of - true -> - Spec = {?MODULE, {?MODULE, start_link, []}, - permanent, 2000, worker, [?MODULE]}, - supervisor:start_child(ejabberd_sup, Spec); - false -> - ok - end. +get_proc(I) -> + misc:binary_to_atom( + iolist_to_binary( + [atom_to_list(?MODULE), $_, integer_to_list(I)])). +get_connection(I) -> + misc:binary_to_atom( + iolist_to_binary( + [atom_to_list(?MODULE), "_connection_", integer_to_list(I)])). + +-spec q(redis_command()) -> {ok, redis_reply()} | redis_error(). q(Command) -> - try eredis:q(?PROCNAME, Command) - catch _:Reason -> {error, Reason} + call(get_rnd_id(), {q, Command}, ?MAX_RETRIES). + +-spec qp(redis_pipeline()) -> [{ok, redis_reply()} | redis_error()] | redis_error(). +qp(Pipeline) -> + call(get_rnd_id(), {qp, Pipeline}, ?MAX_RETRIES). + +-spec multi(fun(() -> any())) -> {ok, redis_reply()} | redis_error(). +multi(F) -> + case erlang:get(?TR_STACK) of + undefined -> + erlang:put(?TR_STACK, []), + try F() of + _ -> + Stack = erlang:erase(?TR_STACK), + Command = [["MULTI"]|lists:reverse([["EXEC"]|Stack])], + case qp(Command) of + {error, _} = Err -> Err; + Result -> get_result(Result) + end + catch + E:R:St -> + erlang:erase(?TR_STACK), + erlang:raise(E, R, St) + end; + _ -> + erlang:error(nested_transaction) end. -qp(Pipeline) -> - try eredis:qp(?PROCNAME, Pipeline) - catch _:Reason -> {error, Reason} +-spec format_error(atom() | binary()) -> binary(). +format_error(Reason) when is_atom(Reason) -> + format_error(misc:atom_to_binary(Reason)); +format_error(Reason) -> + Reason. + +%%%=================================================================== +%%% Redis commands API +%%%=================================================================== +-spec get(iodata()) -> {ok, undefined | binary()} | redis_error(). +get(Key) -> + case erlang:get(?TR_STACK) of + undefined -> + q([<<"GET">>, Key]); + _ -> + erlang:error(transaction_unsupported) + end. + +-spec set(iodata(), iodata()) -> ok | redis_error() | queued. +set(Key, Val) -> + Cmd = [<<"SET">>, Key, Val], + case erlang:get(?TR_STACK) of + undefined -> + case q(Cmd) of + {ok, <<"OK">>} -> ok; + {error, _} = Err -> Err + end; + Stack -> + tr_enq(Cmd, Stack) + end. + +-spec del(list()) -> {ok, non_neg_integer()} | redis_error() | queued. +del([]) -> + reply(0); +del(Keys) -> + Cmd = [<<"DEL">>|Keys], + case erlang:get(?TR_STACK) of + undefined -> + case q(Cmd) of + {ok, N} -> {ok, binary_to_integer(N)}; + {error, _} = Err -> Err + end; + Stack -> + tr_enq(Cmd, Stack) + end. + +-spec sadd(iodata(), list()) -> {ok, non_neg_integer()} | redis_error() | queued. +sadd(_Set, []) -> + reply(0); +sadd(Set, Members) -> + Cmd = [<<"SADD">>, Set|Members], + case erlang:get(?TR_STACK) of + undefined -> + case q(Cmd) of + {ok, N} -> {ok, binary_to_integer(N)}; + {error, _} = Err -> Err + end; + Stack -> + tr_enq(Cmd, Stack) + end. + +-spec srem(iodata(), list()) -> {ok, non_neg_integer()} | redis_error() | queued. +srem(_Set, []) -> + reply(0); +srem(Set, Members) -> + Cmd = [<<"SREM">>, Set|Members], + case erlang:get(?TR_STACK) of + undefined -> + case q(Cmd) of + {ok, N} -> {ok, binary_to_integer(N)}; + {error, _} = Err -> Err + end; + Stack -> + tr_enq(Cmd, Stack) + end. + +-spec smembers(iodata()) -> {ok, [binary()]} | redis_error(). +smembers(Set) -> + case erlang:get(?TR_STACK) of + undefined -> + q([<<"SMEMBERS">>, Set]); + _ -> + erlang:error(transaction_unsupported) + end. + +-spec sismember(iodata(), iodata()) -> boolean() | redis_error(). +sismember(Set, Member) -> + case erlang:get(?TR_STACK) of + undefined -> + case q([<<"SISMEMBER">>, Set, Member]) of + {ok, Flag} -> {ok, dec_bool(Flag)}; + {error, _} = Err -> Err + end; + _ -> + erlang:error(transaction_unsupported) + end. + +-spec scard(iodata()) -> {ok, non_neg_integer()} | redis_error(). +scard(Set) -> + case erlang:get(?TR_STACK) of + undefined -> + case q([<<"SCARD">>, Set]) of + {ok, N} -> + {ok, binary_to_integer(N)}; + {error, _} = Err -> + Err + end; + _ -> + erlang:error(transaction_unsupported) + end. + +-spec hget(iodata(), iodata()) -> {ok, undefined | binary()} | redis_error(). +hget(Key, Field) -> + case erlang:get(?TR_STACK) of + undefined -> + q([<<"HGET">>, Key, Field]); + _ -> + erlang:error(transaction_unsupported) + end. + +-spec hset(iodata(), iodata(), iodata()) -> {ok, boolean()} | redis_error() | queued. +hset(Key, Field, Val) -> + Cmd = [<<"HSET">>, Key, Field, Val], + case erlang:get(?TR_STACK) of + undefined -> + case q(Cmd) of + {ok, Flag} -> {ok, dec_bool(Flag)}; + {error, _} = Err -> Err + end; + Stack -> + tr_enq(Cmd, Stack) + end. + +-spec hdel(iodata(), list()) -> {ok, non_neg_integer()} | redis_error() | queued. +hdel(_Key, []) -> + reply(0); +hdel(Key, Fields) -> + Cmd = [<<"HDEL">>, Key|Fields], + case erlang:get(?TR_STACK) of + undefined -> + case q(Cmd) of + {ok, N} -> {ok, binary_to_integer(N)}; + {error, _} = Err -> Err + end; + Stack -> + tr_enq(Cmd, Stack) + end. + +-spec hgetall(iodata()) -> {ok, [{binary(), binary()}]} | redis_error(). +hgetall(Key) -> + case erlang:get(?TR_STACK) of + undefined -> + case q([<<"HGETALL">>, Key]) of + {ok, Pairs} -> {ok, decode_pairs(Pairs)}; + {error, _} = Err -> Err + end; + _ -> + erlang:error(transaction_unsupported) + end. + +-spec hlen(iodata()) -> {ok, non_neg_integer()} | redis_error(). +hlen(Key) -> + case erlang:get(?TR_STACK) of + undefined -> + case q([<<"HLEN">>, Key]) of + {ok, N} -> {ok, binary_to_integer(N)}; + {error, _} = Err -> Err + end; + _ -> + erlang:error(transaction_unsupported) + end. + +-spec hkeys(iodata()) -> {ok, [binary()]} | redis_error(). +hkeys(Key) -> + case erlang:get(?TR_STACK) of + undefined -> + q([<<"HKEYS">>, Key]); + _ -> + erlang:error(transaction_unsupported) + end. + +-spec subscribe([binary()]) -> ok | redis_error(). +subscribe(Channels) -> + try gen_server_call(get_proc(1), {subscribe, self(), Channels}) + catch exit:{Why, {?GEN_SERVER, call, _}} -> + Reason = case Why of + timeout -> timeout; + _ -> disconnected + end, + {error, Reason} + end. + +-spec publish(iodata(), iodata()) -> {ok, non_neg_integer()} | redis_error() | queued. +publish(Channel, Data) -> + Cmd = [<<"PUBLISH">>, Channel, Data], + case erlang:get(?TR_STACK) of + undefined -> + case q(Cmd) of + {ok, N} -> {ok, binary_to_integer(N)}; + {error, _} = Err -> Err + end; + Stack -> + tr_enq(Cmd, Stack) + end. + +-spec script_load(iodata()) -> {ok, binary()} | redis_error(). +script_load(Data) -> + case erlang:get(?TR_STACK) of + undefined -> + q([<<"SCRIPT">>, <<"LOAD">>, Data]); + _ -> + erlang:error(transaction_unsupported) + end. + +-spec evalsha(binary(), [iodata()], [iodata() | integer()]) -> {ok, binary()} | redis_error(). +evalsha(SHA, Keys, Args) -> + case erlang:get(?TR_STACK) of + undefined -> + q([<<"EVALSHA">>, SHA, length(Keys)|Keys ++ Args]); + _ -> + erlang:error(transaction_unsupported) + end. + +-spec info(redis_info()) -> {ok, [{atom(), binary()}]} | redis_error(). +info(Type) -> + case erlang:get(?TR_STACK) of + undefined -> + case q([<<"INFO">>, misc:atom_to_binary(Type)]) of + {ok, Info} -> + Lines = binary:split(Info, <<"\r\n">>, [global]), + KVs = [binary:split(Line, <<":">>) || Line <- Lines], + {ok, [{misc:binary_to_atom(K), V} || [K, V] <- KVs]}; + {error, _} = Err -> + Err + end; + _ -> + erlang:error(transaction_unsupported) end. %%%=================================================================== %%% gen_server callbacks %%%=================================================================== -init([]) -> +init([I]) -> process_flag(trap_exit, true), - connect(), - {ok, #state{}}. + QueueType = get_queue_type(), + Limit = max_fsm_queue(), + self() ! connect, + {ok, #state{num = I, pending_q = p1_queue:new(QueueType, Limit)}}. -handle_call(_Request, _From, State) -> - Reply = ok, - {reply, Reply, State}. +handle_call(connect, From, #state{connection = undefined, + pending_q = Q} = State) -> + CurrTime = erlang:monotonic_time(millisecond), + Q2 = try p1_queue:in({From, CurrTime}, Q) + catch error:full -> + Q1 = clean_queue(Q, CurrTime), + p1_queue:in({From, CurrTime}, Q1) + end, + {noreply, State#state{pending_q = Q2}}; +handle_call(connect, From, #state{connection = Pid} = State) -> + case is_process_alive(Pid) of + true -> + {reply, ok, State}; + false -> + self() ! connect, + handle_call(connect, From, State#state{connection = undefined}) + end; +handle_call({subscribe, Caller, Channels}, _From, + #state{connection = Pid, subscriptions = Subs} = State) -> + Subs1 = lists:foldl( + fun(Channel, Acc) -> + Callers = maps:get(Channel, Acc, []) -- [Caller], + maps:put(Channel, [Caller|Callers], Acc) + end, Subs, Channels), + eredis_subscribe(Pid, Channels), + {reply, ok, State#state{subscriptions = Subs1}}; +handle_call(Request, _From, State) -> + ?WARNING_MSG("Unexpected call: ~p", [Request]), + {noreply, State}. handle_cast(_Msg, State) -> {noreply, State}. +handle_info(connect, #state{connection = undefined} = State) -> + NewState = case connect(State) of + {ok, Connection} -> + Q1 = flush_queue(State#state.pending_q), + re_subscribe(Connection, State#state.subscriptions), + State#state{connection = Connection, pending_q = Q1}; + {error, _} -> + State + end, + {noreply, NewState}; handle_info(connect, State) -> - connect(), + %% Already connected {noreply, State}; -handle_info({'DOWN', _MRef, _Type, _Pid, Reason}, State) -> - ?INFO_MSG("Redis connection has failed: ~p", [Reason]), - connect(), +handle_info({'EXIT', Pid, _}, State) -> + case State#state.connection of + Pid -> + self() ! connect, + {noreply, State#state{connection = undefined}}; + _ -> + {noreply, State} + end; +handle_info({subscribed, Channel, Pid}, State) -> + case State#state.connection of + Pid -> + case maps:is_key(Channel, State#state.subscriptions) of + true -> eredis_sub:ack_message(Pid); + false -> + ?WARNING_MSG("Got subscription ack for unknown channel ~ts", + [Channel]) + end; + _ -> + ok + end, {noreply, State}; -handle_info({'EXIT', _, _}, State) -> +handle_info({message, Channel, Data, Pid}, State) -> + case State#state.connection of + Pid -> + lists:foreach( + fun(Subscriber) -> + erlang:send(Subscriber, {redis_message, Channel, Data}) + end, maps:get(Channel, State#state.subscriptions, [])), + eredis_sub:ack_message(Pid); + _ -> + ok + end, {noreply, State}; handle_info(Info, State) -> - ?INFO_MSG("unexpected info = ~p", [Info]), + ?WARNING_MSG("Unexpected info = ~p", [Info]), {noreply, State}. terminate(_Reason, _State) -> @@ -92,88 +464,198 @@ code_change(_OldVsn, State, _Extra) -> %%%=================================================================== %%% Internal functions %%%=================================================================== -is_redis_configured(Host) -> - ServerConfigured = ejabberd_config:has_option({redis_server, Host}), - PortConfigured = ejabberd_config:has_option({redis_port, Host}), - DBConfigured = ejabberd_config:has_option({redis_db, Host}), - PassConfigured = ejabberd_config:has_option({redis_password, Host}), - ReconnTimeoutConfigured = ejabberd_config:has_option( - {redis_reconnect_timeout, Host}), - ConnTimeoutConfigured = ejabberd_config:has_option( - {redis_connect_timeout, Host}), - Modules = ejabberd_config:get_option( - {modules, Host}, - fun(L) when is_list(L) -> L end, []), - SMConfigured = ejabberd_config:get_option( - {sm_db_type, Host}, - fun(V) -> V end) == redis, - ModuleWithRedisDBConfigured = - lists:any( - fun({Module, Opts}) -> - gen_mod:db_type(Host, Opts, Module) == redis - end, Modules), - ServerConfigured or PortConfigured or DBConfigured or PassConfigured or - ReconnTimeoutConfigured or ConnTimeoutConfigured or - SMConfigured or ModuleWithRedisDBConfigured. - -iolist_to_list(IOList) -> - binary_to_list(iolist_to_binary(IOList)). - -connect() -> - Server = ejabberd_config:get_option(redis_server, - fun iolist_to_list/1, - "localhost"), - Port = ejabberd_config:get_option(redis_port, - fun(P) when is_integer(P), - P>0, P<65536 -> - P - end, 6379), - DB = ejabberd_config:get_option(redis_db, - fun(I) when is_integer(I), I >= 0 -> - I - end, 0), - Pass = ejabberd_config:get_option(redis_password, - fun iolist_to_list/1, - ""), - ReconnTimeout = timer:seconds( - ejabberd_config:get_option( - redis_reconnect_timeout, - fun(I) when is_integer(I), I>0 -> I end, - 1)), - ConnTimeout = timer:seconds( - ejabberd_config:get_option( - redis_connect_timeout, - fun(I) when is_integer(I), I>0 -> I end, - 1)), - try case eredis:start_link(Server, Port, DB, Pass, - ReconnTimeout, ConnTimeout) of +-spec connect(state()) -> {ok, pid()} | {error, any()}. +connect(#state{num = Num}) -> + Server1 = ejabberd_option:redis_server(), + Port = ejabberd_option:redis_port(), + DB = ejabberd_option:redis_db(), + Pass = ejabberd_option:redis_password(), + ConnTimeout = ejabberd_option:redis_connect_timeout(), + Server = parse_server(Server1), + try case do_connect(Num, Server, Port, Pass, DB, ConnTimeout) of {ok, Client} -> - ?INFO_MSG("Connected to Redis at ~s:~p", [Server, Port]), - unlink(Client), - erlang:monitor(process, Client), - register(?PROCNAME, Client), + ?DEBUG("Connection #~p established to Redis at ~ts:~p", + [Num, Server, Port]), + register(get_connection(Num), Client), {ok, Client}; {error, Why} -> erlang:error(Why) end catch _:Reason -> - Timeout = 10, - ?ERROR_MSG("Redis connection at ~s:~p has failed: ~p; " + Timeout = p1_rand:uniform( + min(10, ejabberd_redis_sup:get_pool_size())), + ?ERROR_MSG("Redis connection #~p at ~ts:~p has failed: ~p; " "reconnecting in ~p seconds", - [Server, Port, Reason, Timeout]), - erlang:send_after(timer:seconds(Timeout), self(), connect) + [Num, Server, Port, Reason, Timeout]), + erlang:send_after(timer:seconds(Timeout), self(), connect), + {error, Reason} end. -opt_type(redis_connect_timeout) -> - fun (I) when is_integer(I), I > 0 -> I end; -opt_type(redis_db) -> - fun (I) when is_integer(I), I >= 0 -> I end; -opt_type(redis_password) -> fun iolist_to_list/1; -opt_type(redis_port) -> - fun (P) when is_integer(P), P > 0, P < 65536 -> P end; -opt_type(redis_reconnect_timeout) -> - fun (I) when is_integer(I), I > 0 -> I end; -opt_type(redis_server) -> fun iolist_to_list/1; -opt_type(_) -> - [redis_connect_timeout, redis_db, redis_password, - redis_port, redis_reconnect_timeout, redis_server]. +parse_server([$u,$n,$i,$x,$: | Path]) -> + {local, Path}; +parse_server(Server) -> + Server. + +do_connect(1, Server, Port, Pass, _DB, _ConnTimeout) -> + %% First connection in the pool is always a subscriber + Options = [{host, Server}, + {port, Port}, + {password, Pass}, + {reconnect_sleep, no_reconnect}, + {max_queue_size, infinity}, + {queue_behaviour, drop}], + Res = eredis_sub:start_link(Options), + case Res of + {ok, Pid} -> eredis_sub:controlling_process(Pid); + _ -> ok + end, + Res; +do_connect(_, Server, Port, Pass, DB, ConnTimeout) -> + Options = [{host, Server}, + {port, Port}, + {database, DB}, + {password, Pass}, + {reconnect_sleep, no_reconnect}, + {connect_timeout, ConnTimeout}], + eredis:start_link(Options). + +-spec call(pos_integer(), {q, redis_command()}, integer()) -> + {ok, redis_reply()} | redis_error(); + (pos_integer(), {qp, redis_pipeline()}, integer()) -> + [{ok, redis_reply()} | redis_error()] | redis_error(). +call(I, {F, Cmd}, Retries) -> + ?DEBUG("Redis query: ~p", [Cmd]), + Conn = get_connection(I), + Res = try eredis:F(Conn, Cmd, ?CALL_TIMEOUT) of + {error, Reason} when is_atom(Reason) -> + try exit(whereis(Conn), kill) catch _:_ -> ok end, + {error, disconnected}; + Other -> + Other + catch exit:{timeout, _} -> {error, timeout}; + exit:{_, {gen_server, call, _}} -> {error, disconnected} + end, + case Res of + {error, disconnected} when Retries > 0 -> + try gen_server_call(get_proc(I), connect) of + ok -> call(I, {F, Cmd}, Retries-1); + {error, _} = Err -> Err + catch exit:{Why, {?GEN_SERVER, call, _}} -> + Reason1 = case Why of + timeout -> timeout; + _ -> disconnected + end, + log_error(Cmd, Reason1), + {error, Reason1} + end; + {error, Reason1} -> + log_error(Cmd, Reason1), + Res; + _ -> + Res + end. + +gen_server_call(Proc, Msg) -> + case ejabberd_redis_sup:start() of + ok -> + ?GEN_SERVER:call(Proc, Msg, ?CALL_TIMEOUT); + {error, _} -> + {error, disconnected} + end. + +-spec log_error(redis_command() | redis_pipeline(), atom() | binary()) -> ok. +log_error(Cmd, Reason) -> + ?ERROR_MSG("Redis request has failed:~n" + "** request = ~p~n" + "** response = ~ts", + [Cmd, format_error(Reason)]). + +-spec get_rnd_id() -> pos_integer(). +get_rnd_id() -> + p1_rand:round_robin(ejabberd_redis_sup:get_pool_size() - 1) + 2. + +-spec get_result([{ok, redis_reply()} | redis_error()]) -> + {ok, redis_reply()} | redis_error(). +get_result([{error, _} = Err|_]) -> + Err; +get_result([{ok, _} = OK]) -> + OK; +get_result([_|T]) -> + get_result(T). + +-spec tr_enq([iodata()], list()) -> queued. +tr_enq(Cmd, Stack) -> + erlang:put(?TR_STACK, [Cmd|Stack]), + queued. + +-spec decode_pairs([binary()]) -> [{binary(), binary()}]. +decode_pairs(Pairs) -> + decode_pairs(Pairs, []). + +-spec decode_pairs([binary()], [{binary(), binary()}]) -> [{binary(), binary()}]. +decode_pairs([Field, Val|Pairs], Acc) -> + decode_pairs(Pairs, [{Field, Val}|Acc]); +decode_pairs([], Acc) -> + lists:reverse(Acc). + +dec_bool(<<$1>>) -> true; +dec_bool(<<$0>>) -> false. + +-spec reply(T) -> {ok, T} | queued. +reply(Val) -> + case erlang:get(?TR_STACK) of + undefined -> {ok, Val}; + _ -> queued + end. + +-spec max_fsm_queue() -> pos_integer(). +max_fsm_queue() -> + proplists:get_value(max_queue, fsm_limit_opts(), ?DEFAULT_MAX_QUEUE). + +fsm_limit_opts() -> + ejabberd_config:fsm_limit_opts([]). + +get_queue_type() -> + ejabberd_option:redis_queue_type(). + +-spec flush_queue(queue()) -> queue(). +flush_queue(Q) -> + CurrTime = erlang:monotonic_time(millisecond), + p1_queue:dropwhile( + fun({From, Time}) -> + if (CurrTime - Time) >= ?CALL_TIMEOUT -> + ok; + true -> + ?GEN_SERVER:reply(From, ok) + end, + true + end, Q). + +-spec clean_queue(queue(), integer()) -> queue(). +clean_queue(Q, CurrTime) -> + Q1 = p1_queue:dropwhile( + fun({_From, Time}) -> + (CurrTime - Time) >= ?CALL_TIMEOUT + end, Q), + Len = p1_queue:len(Q1), + Limit = p1_queue:get_limit(Q1), + if Len >= Limit -> + ?ERROR_MSG("Redis request queue is overloaded", []), + p1_queue:dropwhile( + fun({From, _Time}) -> + ?GEN_SERVER:reply(From, {error, overloaded}), + true + end, Q1); + true -> + Q1 + end. + +re_subscribe(Pid, Subs) -> + case maps:keys(Subs) of + [] -> ok; + Channels -> eredis_subscribe(Pid, Channels) + end. + +eredis_subscribe(Pid, Channels) -> + ?DEBUG("Redis query: ~p", [[<<"SUBSCRIBE">>|Channels]]), + eredis_sub:subscribe(Pid, Channels). diff --git a/src/ejabberd_redis_sup.erl b/src/ejabberd_redis_sup.erl new file mode 100644 index 000000000..8d49f4632 --- /dev/null +++ b/src/ejabberd_redis_sup.erl @@ -0,0 +1,109 @@ +%%%------------------------------------------------------------------- +%%% Author : Evgeny Khramtsov +%%% Created : 6 Apr 2017 by Evgeny Khramtsov +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%------------------------------------------------------------------- +-module(ejabberd_redis_sup). + +-behaviour(supervisor). + +%% API +-export([start/0, stop/0, start_link/0]). +-export([get_pool_size/0, config_reloaded/0]). + +%% Supervisor callbacks +-export([init/1]). + +-include("logger.hrl"). + +%%%=================================================================== +%%% API functions +%%%=================================================================== +start() -> + case is_started() of + true -> ok; + false -> + ejabberd:start_app(eredis), + Spec = {?MODULE, {?MODULE, start_link, []}, + permanent, infinity, supervisor, [?MODULE]}, + case supervisor:start_child(ejabberd_db_sup, Spec) of + {ok, _} -> ok; + {error, {already_started, Pid}} -> + %% Wait for the supervisor to fully start + _ = supervisor:count_children(Pid), + ok; + {error, Why} = Err -> + ?ERROR_MSG("Failed to start ~ts: ~p", [?MODULE, Why]), + Err + end + end. + +stop() -> + ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 20), + _ = supervisor:terminate_child(ejabberd_db_sup, ?MODULE), + _ = supervisor:delete_child(ejabberd_db_sup, ?MODULE), + ok. + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +config_reloaded() -> + case is_started() of + true -> + lists:foreach( + fun(Spec) -> + supervisor:start_child(?MODULE, Spec) + end, get_specs()), + PoolSize = get_pool_size(), + lists:foreach( + fun({Id, _, _, _}) when Id > PoolSize -> + case supervisor:terminate_child(?MODULE, Id) of + ok -> supervisor:delete_child(?MODULE, Id); + _ -> ok + end; + (_) -> + ok + end, supervisor:which_children(?MODULE)); + false -> + ok + end. + +%%%=================================================================== +%%% Supervisor callbacks +%%%=================================================================== +init([]) -> + ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 20), + {ok, {{one_for_one, 500, 1}, get_specs()}}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +get_specs() -> + lists:map( + fun(I) -> + {I, {ejabberd_redis, start_link, [I]}, + transient, 2000, worker, [?MODULE]} + end, lists:seq(1, get_pool_size())). + +get_pool_size() -> + ejabberd_option:redis_pool_size() + 1. + +is_started() -> + whereis(?MODULE) /= undefined. diff --git a/src/ejabberd_regexp.erl b/src/ejabberd_regexp.erl index b79774e31..0d18deac6 100644 --- a/src/ejabberd_regexp.erl +++ b/src/ejabberd_regexp.erl @@ -1,11 +1,11 @@ %%%---------------------------------------------------------------------- %%% File : ejabberd_regexp.erl -%%% Author : Badlop -%%% Purpose : Frontend to Re and Regexp OTP modules -%%% Created : 8 Dec 2011 by Badlop +%%% Author : Badlop +%%% Purpose : Frontend to Re OTP module +%%% Created : 8 Dec 2011 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -25,71 +25,84 @@ -module(ejabberd_regexp). --compile([export_all]). - -exec({ReM, ReF, ReA}, {RgM, RgF, RgA}) -> - try apply(ReM, ReF, ReA) catch - error:undef -> apply(RgM, RgF, RgA); - A:B -> {error, {A, B}} - end. +-export([run/2, split/2, replace/3, greplace/3, sh_to_awk/1]). -spec run(binary(), binary()) -> match | nomatch | {error, any()}. run(String, Regexp) -> - case exec({re, run, [String, Regexp, [{capture, none}]]}, - {regexp, first_match, [binary_to_list(String), - binary_to_list(Regexp)]}) - of - {match, _, _} -> match; - {match, _} -> match; - match -> match; - nomatch -> nomatch; - {error, Error} -> {error, Error} - end. + re:run(String, Regexp, [{capture, none}, unicode]). -spec split(binary(), binary()) -> [binary()]. split(String, Regexp) -> - case exec({re, split, [String, Regexp, [{return, binary}]]}, - {regexp, split, [binary_to_list(String), - binary_to_list(Regexp)]}) - of - {ok, FieldList} -> [iolist_to_binary(F) || F <- FieldList]; - {error, Error} -> throw(Error); - A -> A - end. + re:split(String, Regexp, [{return, binary}]). -spec replace(binary(), binary(), binary()) -> binary(). replace(String, Regexp, New) -> - case exec({re, replace, [String, Regexp, New, [{return, binary}]]}, - {regexp, sub, [binary_to_list(String), - binary_to_list(Regexp), - binary_to_list(New)]}) - of - {ok, NewString, _RepCount} -> iolist_to_binary(NewString); - {error, Error} -> throw(Error); - A -> A - end. + re:replace(String, Regexp, New, [{return, binary}]). -spec greplace(binary(), binary(), binary()) -> binary(). greplace(String, Regexp, New) -> - case exec({re, replace, [String, Regexp, New, [global, {return, binary}]]}, - {regexp, sub, [binary_to_list(String), - binary_to_list(Regexp), - binary_to_list(New)]}) - of - {ok, NewString, _RepCount} -> iolist_to_binary(NewString); - {error, Error} -> throw(Error); - A -> A - end. + re:replace(String, Regexp, New, [global, {return, binary}]). + +%% This code was copied and adapted from xmerl_regexp.erl -spec sh_to_awk(binary()) -> binary(). +sh_to_awk(Sh) -> + iolist_to_binary([<<"^(">>, sh_to_awk_1(Sh)]). %Fix the beginning -sh_to_awk(ShRegExp) -> - case exec({xmerl_regexp, sh_to_awk, [binary_to_list(ShRegExp)]}, - {regexp, sh_to_awk, [binary_to_list(ShRegExp)]}) - of - A -> iolist_to_binary(A) - end. +sh_to_awk_1(<<"*", Sh/binary>>) -> %This matches any string + [<<".*">>, sh_to_awk_1(Sh)]; +sh_to_awk_1(<<"?", Sh/binary>>) -> %This matches any character + [$., sh_to_awk_1(Sh)]; +sh_to_awk_1(<<"[^]", Sh/binary>>) -> %This takes careful handling + [<<"\\^">>, sh_to_awk_1(Sh)]; +%% Must move '^' to end. +sh_to_awk_1(<<"[^", Sh/binary>>) -> + [$[, sh_to_awk_2(Sh, true)]; +sh_to_awk_1(<<"[!", Sh/binary>>) -> + [<<"[^">>, sh_to_awk_2(Sh, false)]; +sh_to_awk_1(<<"[", Sh/binary>>) -> + [$[, sh_to_awk_2(Sh, false)]; +sh_to_awk_1(<>) -> %% Unspecialise everything else which is not an escape character. + case sh_special_char(C) of + true -> [$\\,C|sh_to_awk_1(Sh)]; + false -> [C|sh_to_awk_1(Sh)] + end; +sh_to_awk_1(<<>>) -> + <<")$">>. %Fix the end + +sh_to_awk_2(<<"]", Sh/binary>>, UpArrow) -> + [$]|sh_to_awk_3(Sh, UpArrow)]; +sh_to_awk_2(Sh, UpArrow) -> + sh_to_awk_3(Sh, UpArrow). + +sh_to_awk_3(<<"]", Sh/binary>>, true) -> + [<<"^]">>, sh_to_awk_1(Sh)]; +sh_to_awk_3(<<"]", Sh/binary>>, false) -> + [$]|sh_to_awk_1(Sh)]; +sh_to_awk_3(<>, UpArrow) -> + [C|sh_to_awk_3(Sh, UpArrow)]; +sh_to_awk_3(<<>>, true) -> + [$^|sh_to_awk_1(<<>>)]; +sh_to_awk_3(<<>>, false) -> + sh_to_awk_1(<<>>). + +%% Test if a character is a special character. +-spec sh_special_char(char()) -> boolean(). +sh_special_char($|) -> true; +sh_special_char($*) -> true; +sh_special_char($+) -> true; +sh_special_char($?) -> true; +sh_special_char($() -> true; +sh_special_char($)) -> true; +sh_special_char($\\) -> true; +sh_special_char($^) -> true; +sh_special_char($$) -> true; +sh_special_char($.) -> true; +sh_special_char($[) -> true; +sh_special_char($]) -> true; +sh_special_char($") -> true; +sh_special_char(_C) -> false. diff --git a/src/ejabberd_riak.erl b/src/ejabberd_riak.erl deleted file mode 100644 index 44628d1c2..000000000 --- a/src/ejabberd_riak.erl +++ /dev/null @@ -1,564 +0,0 @@ -%%%------------------------------------------------------------------- -%%% @author Alexey Shchepin -%%% @doc -%%% Interface for Riak database -%%% @end -%%% Created : 29 Dec 2011 by Alexey Shchepin -%%% @copyright (C) 2002-2016 ProcessOne -%%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne -%%% -%%% This program is free software; you can redistribute it and/or -%%% modify it under the terms of the GNU General Public License as -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License along -%%% with this program; if not, write to the Free Software Foundation, Inc., -%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -%%% -%%%------------------------------------------------------------------- --module(ejabberd_riak). - --behaviour(gen_server). - -%% API --export([start_link/5, get_proc/1, make_bucket/1, put/2, put/3, - get/2, get/3, get_by_index/4, delete/1, delete/2, - count_by_index/3, get_by_index_range/5, - get_keys/1, get_keys_by_index/3, is_connected/0, - count/1, delete_by_index/3]). -%% For debugging --export([get_tables/0]). -%% map/reduce exports --export([map_key/3]). - -%% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). - --include("ejabberd.hrl"). --include("logger.hrl"). - --record(state, {pid = self() :: pid()}). - --type index() :: {binary(), any()}. - --type index_info() :: [{i, any()} | {'2i', [index()]}]. - -%% The `record_schema()' is just a tuple: -%% {record_info(fields, some_record), #some_record{}} - --type record_schema() :: {[atom()], tuple()}. - -%% The `index_info()' is used in put/delete functions: -%% `i' defines a primary index, `` '2i' '' defines secondary indexes. -%% There must be only one primary index. If `i' is not specified, -%% the first element of the record is assumed as a primary index, -%% i.e. `i' = element(2, Record). - --export_types([index_info/0]). - -%%%=================================================================== -%%% API -%%%=================================================================== -%% @private -start_link(Num, Server, Port, _StartInterval, Options) -> - gen_server:start_link({local, get_proc(Num)}, ?MODULE, [Server, Port, Options], []). - -%% @private -is_connected() -> - lists:all( - fun({_Id, Pid, _Type, _Modules}) when is_pid(Pid) -> - case catch riakc_pb_socket:is_connected(get_riak_pid(Pid)) of - true -> true; - _ -> false - end; - (_) -> - false - end, supervisor:which_children(ejabberd_riak_sup)). - -%% @private -get_proc(I) -> - jlib:binary_to_atom( - iolist_to_binary( - [atom_to_list(?MODULE), $_, integer_to_list(I)])). - --spec make_bucket(atom()) -> binary(). -%% @doc Makes a bucket from a table name -%% @private -make_bucket(Table) -> - erlang:atom_to_binary(Table, utf8). - --spec put(tuple(), record_schema()) -> ok | {error, any()}. -%% @equiv put(Record, []) -put(Record, RecFields) -> - ?MODULE:put(Record, RecFields, []). - --spec put(tuple(), record_schema(), index_info()) -> ok | {error, any()}. -%% @doc Stores a record `Rec' with indexes described in ``IndexInfo'' -put(Rec, RecSchema, IndexInfo) -> - Key = encode_key(proplists:get_value(i, IndexInfo, element(2, Rec))), - SecIdxs = [encode_index_key(K, V) || - {K, V} <- proplists:get_value('2i', IndexInfo, [])], - Table = element(1, Rec), - Value = encode_record(Rec, RecSchema), - case put_raw(Table, Key, Value, SecIdxs) of - ok -> - ok; - {error, _} = Error -> - log_error(Error, put, [{record, Rec}, - {index_info, IndexInfo}]), - Error - end. - -put_raw(Table, Key, Value, Indexes) -> - Bucket = make_bucket(Table), - Obj = riakc_obj:new(Bucket, Key, Value, "application/x-erlang-term"), - Obj1 = if Indexes /= [] -> - MetaData = dict:store(<<"index">>, Indexes, dict:new()), - riakc_obj:update_metadata(Obj, MetaData); - true -> - Obj - end, - catch riakc_pb_socket:put(get_random_pid(), Obj1). - -get_object_raw(Table, Key) -> - Bucket = make_bucket(Table), - catch riakc_pb_socket:get(get_random_pid(), Bucket, Key). - --spec get(atom(), record_schema()) -> {ok, [any()]} | {error, any()}. -%% @doc Returns all objects from table `Table' -get(Table, RecSchema) -> - Bucket = make_bucket(Table), - case catch riakc_pb_socket:mapred( - get_random_pid(), - Bucket, - [{map, {modfun, riak_kv_mapreduce, map_object_value}, - none, true}]) of - {ok, [{_, Objs}]} -> - {ok, lists:flatmap( - fun(Obj) -> - case catch decode_record(Obj, RecSchema) of - {'EXIT', _} -> - Error = {error, make_invalid_object(Obj)}, - log_error(Error, get, - [{table, Table}]), - []; - Term -> - [Term] - end - end, Objs)}; - {ok, []} -> - {ok, []}; - {error, notfound} -> - {ok, []}; - {error, _} = Error -> - Error - end. - --spec get(atom(), record_schema(), any()) -> {ok, any()} | {error, any()}. -%% @doc Reads record by `Key' from table `Table' -get(Table, RecSchema, Key) -> - case get_raw(Table, encode_key(Key)) of - {ok, Val} -> - case catch decode_record(Val, RecSchema) of - {'EXIT', _} -> - Error = {error, make_invalid_object(Val)}, - log_error(Error, get, [{table, Table}, {key, Key}]), - {error, notfound}; - Term -> - {ok, Term} - end; - {error, _} = Error -> - log_error(Error, get, [{table, Table}, - {key, Key}]), - Error - end. - --spec get_by_index(atom(), record_schema(), binary(), any()) -> - {ok, [any()]} | {error, any()}. -%% @doc Reads records by `Index' and value `Key' from `Table' -get_by_index(Table, RecSchema, Index, Key) -> - {NewIndex, NewKey} = encode_index_key(Index, Key), - case get_by_index_raw(Table, NewIndex, NewKey) of - {ok, Vals} -> - {ok, lists:flatmap( - fun(Val) -> - case catch decode_record(Val, RecSchema) of - {'EXIT', _} -> - Error = {error, make_invalid_object(Val)}, - log_error(Error, get_by_index, - [{table, Table}, - {index, Index}, - {key, Key}]), - []; - Term -> - [Term] - end - end, Vals)}; - {error, notfound} -> - {ok, []}; - {error, _} = Error -> - log_error(Error, get_by_index, - [{table, Table}, - {index, Index}, - {key, Key}]), - Error - end. - --spec get_by_index_range(atom(), record_schema(), binary(), any(), any()) -> - {ok, [any()]} | {error, any()}. -%% @doc Reads records by `Index' in the range `FromKey'..`ToKey' from `Table' -get_by_index_range(Table, RecSchema, Index, FromKey, ToKey) -> - {NewIndex, NewFromKey} = encode_index_key(Index, FromKey), - {NewIndex, NewToKey} = encode_index_key(Index, ToKey), - case get_by_index_range_raw(Table, NewIndex, NewFromKey, NewToKey) of - {ok, Vals} -> - {ok, lists:flatmap( - fun(Val) -> - case catch decode_record(Val, RecSchema) of - {'EXIT', _} -> - Error = {error, make_invalid_object(Val)}, - log_error(Error, get_by_index_range, - [{table, Table}, - {index, Index}, - {start_key, FromKey}, - {end_key, ToKey}]), - []; - Term -> - [Term] - end - end, Vals)}; - {error, notfound} -> - {ok, []}; - {error, _} = Error -> - log_error(Error, get_by_index_range, - [{table, Table}, {index, Index}, - {start_key, FromKey}, {end_key, ToKey}]), - Error - end. - -get_raw(Table, Key) -> - case get_object_raw(Table, Key) of - {ok, Obj} -> - {ok, riakc_obj:get_value(Obj)}; - {error, _} = Error -> - Error - end. - --spec get_keys(atom()) -> {ok, [any()]} | {error, any()}. -%% @doc Returns a list of index values -get_keys(Table) -> - Bucket = make_bucket(Table), - case catch riakc_pb_socket:mapred( - get_random_pid(), - Bucket, - [{map, {modfun, ?MODULE, map_key}, none, true}]) of - {ok, [{_, Keys}]} -> - {ok, Keys}; - {ok, []} -> - {ok, []}; - {error, _} = Error -> - log_error(Error, get_keys, [{table, Table}]), - Error - end. - --spec get_keys_by_index(atom(), binary(), - any()) -> {ok, [any()]} | {error, any()}. -%% @doc Returns a list of primary keys of objects indexed by `Key'. -get_keys_by_index(Table, Index, Key) -> - {NewIndex, NewKey} = encode_index_key(Index, Key), - Bucket = make_bucket(Table), - case catch riakc_pb_socket:mapred( - get_random_pid(), - {index, Bucket, NewIndex, NewKey}, - [{map, {modfun, ?MODULE, map_key}, none, true}]) of - {ok, [{_, Keys}]} -> - {ok, Keys}; - {ok, []} -> - {ok, []}; - {error, _} = Error -> - log_error(Error, get_keys_by_index, [{table, Table}, - {index, Index}, - {key, Key}]), - Error - end. - -%% @hidden -get_tables() -> - catch riakc_pb_socket:list_buckets(get_random_pid()). - -get_by_index_raw(Table, Index, Key) -> - Bucket = make_bucket(Table), - case riakc_pb_socket:mapred( - get_random_pid(), - {index, Bucket, Index, Key}, - [{map, {modfun, riak_kv_mapreduce, map_object_value}, - none, true}]) of - {ok, [{_, Objs}]} -> - {ok, Objs}; - {ok, []} -> - {ok, []}; - {error, _} = Error -> - Error - end. - -get_by_index_range_raw(Table, Index, FromKey, ToKey) -> - Bucket = make_bucket(Table), - case catch riakc_pb_socket:mapred( - get_random_pid(), - {index, Bucket, Index, FromKey, ToKey}, - [{map, {modfun, riak_kv_mapreduce, map_object_value}, - none, true}]) of - {ok, [{_, Objs}]} -> - {ok, Objs}; - {ok, []} -> - {ok, []}; - {error, _} = Error -> - Error - end. - --spec count(atom()) -> {ok, non_neg_integer()} | {error, any()}. -%% @doc Returns the number of objects in the `Table' -count(Table) -> - Bucket = make_bucket(Table), - case catch riakc_pb_socket:mapred( - get_random_pid(), - Bucket, - [{reduce, {modfun, riak_kv_mapreduce, reduce_count_inputs}, - none, true}]) of - {ok, [{_, [Cnt]}]} -> - {ok, Cnt}; - {error, _} = Error -> - log_error(Error, count, [{table, Table}]), - Error - end. - --spec count_by_index(atom(), binary(), any()) -> - {ok, non_neg_integer()} | {error, any()}. -%% @doc Returns the number of objects in the `Table' by index -count_by_index(Tab, Index, Key) -> - {NewIndex, NewKey} = encode_index_key(Index, Key), - case count_by_index_raw(Tab, NewIndex, NewKey) of - {ok, Cnt} -> - {ok, Cnt}; - {error, notfound} -> - {ok, 0}; - {error, _} = Error -> - log_error(Error, count_by_index, - [{table, Tab}, - {index, Index}, - {key, Key}]), - Error - end. - -count_by_index_raw(Table, Index, Key) -> - Bucket = make_bucket(Table), - case catch riakc_pb_socket:mapred( - get_random_pid(), - {index, Bucket, Index, Key}, - [{reduce, {modfun, riak_kv_mapreduce, reduce_count_inputs}, - none, true}]) of - {ok, [{_, [Cnt]}]} -> - {ok, Cnt}; - {error, _} = Error -> - Error - end. - --spec delete(tuple() | atom()) -> ok | {error, any()}. -%% @doc Same as delete(T, []) when T is record. -%% Or deletes all elements from table if T is atom. -delete(Rec) when is_tuple(Rec) -> - delete(Rec, []); -delete(Table) when is_atom(Table) -> - try - {ok, Keys} = ?MODULE:get_keys(Table), - lists:foreach( - fun(K) -> - ok = delete(Table, K) - end, Keys) - catch _:{badmatch, Err} -> - Err - end. - --spec delete(tuple() | atom(), index_info() | any()) -> ok | {error, any()}. -%% @doc Delete an object -delete(Rec, Opts) when is_tuple(Rec) -> - Table = element(1, Rec), - Key = proplists:get_value(i, Opts, element(2, Rec)), - delete(Table, Key); -delete(Table, Key) when is_atom(Table) -> - case delete_raw(Table, encode_key(Key)) of - ok -> - ok; - Err -> - log_error(Err, delete, [{table, Table}, {key, Key}]), - Err - end. - -delete_raw(Table, Key) -> - Bucket = make_bucket(Table), - catch riakc_pb_socket:delete(get_random_pid(), Bucket, Key). - --spec delete_by_index(atom(), binary(), any()) -> ok | {error, any()}. -%% @doc Deletes objects by index -delete_by_index(Table, Index, Key) -> - try - {ok, Keys} = get_keys_by_index(Table, Index, Key), - lists:foreach( - fun(K) -> - ok = delete(Table, K) - end, Keys) - catch _:{badmatch, Err} -> - Err - end. - -%%%=================================================================== -%%% map/reduce functions -%%%=================================================================== -%% @private -map_key(Obj, _, _) -> - [case riak_object:key(Obj) of - <<"b_", B/binary>> -> - B; - <<"i_", B/binary>> -> - (binary_to_integer(B)); - B -> - erlang:binary_to_term(B) - end]. - -%%%=================================================================== -%%% gen_server API -%%%=================================================================== -%% @private -init([Server, Port, Options]) -> - case riakc_pb_socket:start(Server, Port, Options) of - {ok, Pid} -> - erlang:monitor(process, Pid), - {ok, #state{pid = Pid}}; - Err -> - {stop, Err} - end. - -%% @private -handle_call(get_pid, _From, #state{pid = Pid} = State) -> - {reply, {ok, Pid}, State}; -handle_call(_Request, _From, State) -> - Reply = ok, - {reply, Reply, State}. - -%% @private -handle_cast(_Msg, State) -> - {noreply, State}. - -%% @private -handle_info({'DOWN', _MonitorRef, _Type, _Object, _Info}, State) -> - {stop, normal, State}; -handle_info(_Info, State) -> - ?ERROR_MSG("unexpected info: ~p", [_Info]), - {noreply, State}. - -%% @private -terminate(_Reason, _State) -> - ok. - -%% @private -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -%%%=================================================================== -%%% Internal functions -%%%=================================================================== -encode_index_key(Idx, Key) when is_integer(Key) -> - {<>, Key}; -encode_index_key(Idx, Key) -> - {<>, encode_key(Key)}. - -encode_key(Bin) when is_binary(Bin) -> - <<"b_", Bin/binary>>; -encode_key(Int) when is_integer(Int) -> - <<"i_", ((integer_to_binary(Int)))/binary>>; -encode_key(Term) -> - erlang:term_to_binary(Term). - -log_error({error, notfound}, _, _) -> - ok; -log_error({error, Why} = Err, Function, Opts) -> - Txt = lists:map( - fun({table, Table}) -> - io_lib:fwrite("** Table: ~p~n", [Table]); - ({key, Key}) -> - io_lib:fwrite("** Key: ~p~n", [Key]); - ({index, Index}) -> - io_lib:fwrite("** Index = ~p~n", [Index]); - ({start_key, Key}) -> - io_lib:fwrite("** Start Key: ~p~n", [Key]); - ({end_key, Key}) -> - io_lib:fwrite("** End Key: ~p~n", [Key]); - ({record, Rec}) -> - io_lib:fwrite("** Record = ~p~n", [Rec]); - ({index_info, IdxInfo}) -> - io_lib:fwrite("** Index info = ~p~n", [IdxInfo]); - (_) -> - "" - end, Opts), - ErrTxt = if is_binary(Why) -> - io_lib:fwrite("** Error: ~s", [Why]); - true -> - io_lib:fwrite("** Error: ~p", [Err]) - end, - ?ERROR_MSG("database error:~n** Function: ~p~n~s~s", - [Function, Txt, ErrTxt]); -log_error(_, _, _) -> - ok. - -make_invalid_object(Val) -> - (str:format("Invalid object: ~p", [Val])). - -get_random_pid() -> - PoolPid = ejabberd_riak_sup:get_random_pid(), - get_riak_pid(PoolPid). - -get_riak_pid(PoolPid) -> - case catch gen_server:call(PoolPid, get_pid) of - {ok, Pid} -> - Pid; - {'EXIT', {timeout, _}} -> - throw({error, timeout}); - {'EXIT', Err} -> - throw({error, Err}) - end. - -encode_record(Rec, {Fields, DefRec}) -> - term_to_binary(encode_record(Rec, Fields, DefRec, 2)). - -encode_record(Rec, [FieldName|Fields], DefRec, Pos) -> - Value = element(Pos, Rec), - DefValue = element(Pos, DefRec), - if Value == DefValue -> - encode_record(Rec, Fields, DefRec, Pos+1); - true -> - [{FieldName, Value}|encode_record(Rec, Fields, DefRec, Pos+1)] - end; -encode_record(_, [], _, _) -> - []. - -decode_record(Bin, {Fields, DefRec}) -> - decode_record(binary_to_term(Bin), Fields, DefRec, 2). - -decode_record(KeyVals, [FieldName|Fields], Rec, Pos) -> - case lists:keyfind(FieldName, 1, KeyVals) of - {_, Value} -> - NewRec = setelement(Pos, Rec, Value), - decode_record(KeyVals, Fields, NewRec, Pos+1); - false -> - decode_record(KeyVals, Fields, Rec, Pos+1) - end; -decode_record(_, [], Rec, _) -> - Rec. diff --git a/src/ejabberd_riak_sup.erl b/src/ejabberd_riak_sup.erl deleted file mode 100644 index ad65ecf80..000000000 --- a/src/ejabberd_riak_sup.erl +++ /dev/null @@ -1,206 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : ejabberd_riak_sup.erl -%%% Author : Alexey Shchepin -%%% Purpose : Riak connections supervisor -%%% Created : 29 Dec 2011 by Alexey Shchepin -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne -%%% -%%% This program is free software; you can redistribute it and/or -%%% modify it under the terms of the GNU General Public License as -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License along -%%% with this program; if not, write to the Free Software Foundation, Inc., -%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -%%% -%%%---------------------------------------------------------------------- - --module(ejabberd_riak_sup). - --behaviour(ejabberd_config). --author('alexey@process-one.net'). - --export([start/0, start_link/0, init/1, get_pids/0, - transform_options/1, get_random_pid/0, get_random_pid/1, - opt_type/1]). - --include("ejabberd.hrl"). --include("logger.hrl"). - --define(DEFAULT_POOL_SIZE, 10). --define(DEFAULT_RIAK_START_INTERVAL, 30). % 30 seconds --define(DEFAULT_RIAK_HOST, "127.0.0.1"). --define(DEFAULT_RIAK_PORT, 8087). - -% time to wait for the supervisor to start its child before returning -% a timeout error to the request --define(CONNECT_TIMEOUT, 500). % milliseconds - -start() -> - case lists:any( - fun(Host) -> - is_riak_configured(Host) - end, ?MYHOSTS) of - true -> - ejabberd:start_app(riakc), - do_start(); - false -> - ok - end. - -is_riak_configured(Host) -> - ServerConfigured = ejabberd_config:get_option( - {riak_server, Host}, - fun(_) -> true end, false), - PortConfigured = ejabberd_config:get_option( - {riak_port, Host}, - fun(_) -> true end, false), - AuthConfigured = lists:member( - ejabberd_auth_riak, - ejabberd_auth:auth_modules(Host)), - Modules = ejabberd_config:get_option( - {modules, Host}, - fun(L) when is_list(L) -> L end, []), - ModuleWithRiakDBConfigured = lists:any( - fun({Module, Opts}) -> - gen_mod:db_type(Host, Opts, Module) == riak - end, Modules), - ServerConfigured or PortConfigured - or AuthConfigured or ModuleWithRiakDBConfigured. - -do_start() -> - SupervisorName = ?MODULE, - ChildSpec = - {SupervisorName, - {?MODULE, start_link, []}, - transient, - infinity, - supervisor, - [?MODULE]}, - case supervisor:start_child(ejabberd_sup, ChildSpec) of - {ok, _PID} -> - ok; - _Error -> - ?ERROR_MSG("Start of supervisor ~p failed:~n~p~nRetrying...~n", - [SupervisorName, _Error]), - timer:sleep(5000), - start() - end. - -start_link() -> - supervisor:start_link({local, ?MODULE}, ?MODULE, []). - -init([]) -> - PoolSize = get_pool_size(), - StartInterval = get_start_interval(), - Server = get_riak_server(), - Port = get_riak_port(), - CACertFile = get_riak_cacertfile(), - Username = get_riak_username(), - Password = get_riak_password(), - Options = lists:filter( - fun(X) -> X /= nil end, - [auto_reconnect, - {keepalive, true}, - if CACertFile /= nil -> {cacertfile ,CACertFile}; - true -> nil - end, - if (Username /= nil) and (Password /= nil) -> - {credentials, Username, Password}; - true -> nil - end - ]), - {ok, {{one_for_one, PoolSize*10, 1}, - lists:map( - fun(I) -> - {ejabberd_riak:get_proc(I), - {ejabberd_riak, start_link, - [I, Server, Port, StartInterval*1000, Options]}, - transient, 2000, worker, [?MODULE]} - end, lists:seq(1, PoolSize))}}. - -get_start_interval() -> - ejabberd_config:get_option( - riak_start_interval, - fun(N) when is_integer(N), N >= 1 -> N end, - ?DEFAULT_RIAK_START_INTERVAL). - -get_pool_size() -> - ejabberd_config:get_option( - riak_pool_size, - fun(N) when is_integer(N), N >= 1 -> N end, - ?DEFAULT_POOL_SIZE). - -get_riak_server() -> - ejabberd_config:get_option( - riak_server, - fun(S) -> - binary_to_list(iolist_to_binary(S)) - end, ?DEFAULT_RIAK_HOST). - -get_riak_cacertfile() -> - ejabberd_config:get_option( - riak_cacertfile, - fun(S) -> - binary_to_list(iolist_to_binary(S)) - end, nil). - -get_riak_username() -> - ejabberd_config:get_option( - riak_username, - fun(S) -> - binary_to_list(iolist_to_binary(S)) - end, nil). - -get_riak_password() -> - ejabberd_config:get_option( - riak_password, - fun(S) -> - binary_to_list(iolist_to_binary(S)) - end, nil). - -get_riak_port() -> - ejabberd_config:get_option( - riak_port, - fun(P) when is_integer(P), P > 0, P < 65536 -> P end, - ?DEFAULT_RIAK_PORT). - -get_pids() -> - [ejabberd_riak:get_proc(I) || I <- lists:seq(1, get_pool_size())]. - -get_random_pid() -> - get_random_pid(p1_time_compat:monotonic_time()). - -get_random_pid(Term) -> - I = erlang:phash2(Term, get_pool_size()) + 1, - ejabberd_riak:get_proc(I). - -transform_options(Opts) -> - lists:foldl(fun transform_options/2, [], Opts). - -transform_options({riak_server, {S, P}}, Opts) -> - [{riak_server, S}, {riak_port, P}|Opts]; -transform_options(Opt, Opts) -> - [Opt|Opts]. - -opt_type(modules) -> fun (L) when is_list(L) -> L end; -opt_type(riak_pool_size) -> - fun (N) when is_integer(N), N >= 1 -> N end; -opt_type(riak_port) -> fun (_) -> true end; -opt_type(riak_server) -> fun (_) -> true end; -opt_type(riak_start_interval) -> - fun (N) when is_integer(N), N >= 1 -> N end; -opt_type(riak_cacertfile) -> fun iolist_to_binary/1; -opt_type(riak_username) -> fun iolist_to_binary/1; -opt_type(riak_password) -> fun iolist_to_binary/1; -opt_type(_) -> - [modules, riak_pool_size, riak_port, riak_server, - riak_start_interval, riak_cacertfile, riak_username, riak_password]. diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl index c6d919097..236b5081a 100644 --- a/src/ejabberd_router.erl +++ b/src/ejabberd_router.erl @@ -5,7 +5,7 @@ %%% Created : 27 Nov 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -25,69 +25,105 @@ -module(ejabberd_router). --behaviour(ejabberd_config). - -author('alexey@process-one.net'). --behaviour(gen_server). +-ifndef(GEN_SERVER). +-define(GEN_SERVER, gen_server). +-endif. +-behaviour(?GEN_SERVER). %% API --export([route/3, - route_error/4, - register_route/1, +-export([route/1, + route_error/2, + route_iq/2, + route_iq/3, + route_iq/4, register_route/2, register_route/3, + register_route/4, register_routes/1, host_of_route/1, - process_iq/3, + process_iq/1, unregister_route/1, + unregister_route/2, unregister_routes/1, - dirty_get_all_routes/0, - dirty_get_all_domains/0 - ]). + get_all_routes/0, + is_my_route/1, + is_my_host/1, + clean_cache/1, + config_reloaded/0, + get_backend/0]). -export([start_link/0]). -export([init/1, handle_call/3, handle_cast/2, - handle_info/2, terminate/2, code_change/3, opt_type/1]). + handle_info/2, terminate/2, code_change/3]). + +%% Deprecated functions +-export([route/3, route_error/4]). +-deprecated([{route, 3}, {route_error, 4}]). + +%% This value is used in SIP and Megaco for a transaction lifetime. +-define(IQ_TIMEOUT, 32000). +-define(CALL_TIMEOUT, timer:minutes(10)). --include("ejabberd.hrl"). -include("logger.hrl"). +-include("ejabberd_router.hrl"). +-include_lib("xmpp/include/xmpp.hrl"). --include("xmpp.hrl"). --type local_hint() :: undefined | integer() | {apply, atom(), atom()}. --record(route, {domain, server_host, pid, local_hint}). +-callback init() -> any(). +-callback register_route(binary(), binary(), local_hint(), + undefined | pos_integer(), pid()) -> ok | {error, term()}. +-callback unregister_route(binary(), undefined | pos_integer(), pid()) -> ok | {error, term()}. +-callback find_routes(binary()) -> {ok, [#route{}]} | {error, any()}. +-callback get_all_routes() -> {ok, [binary()]} | {error, any()}. --record(state, {}). +-record(state, {route_monitors = #{} :: #{{binary(), pid()} => reference()}}). %%==================================================================== %% API %%==================================================================== -%%-------------------------------------------------------------------- -%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} -%% Description: Starts the server -%%-------------------------------------------------------------------- start_link() -> - gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + ?GEN_SERVER:start_link({local, ?MODULE}, ?MODULE, [], []). + +-spec route(stanza()) -> ok. +route(Packet) -> + try do_route(Packet) + catch + Class:Reason:StackTrace -> + ?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts", + [xmpp:pp(Packet), + misc:format_exception(2, Class, Reason, StackTrace)]) + end. -spec route(jid(), jid(), xmlel() | stanza()) -> ok. +route(#jid{} = From, #jid{} = To, #xmlel{} = El) -> + try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of + Pkt -> route(From, To, Pkt) + catch _:{xmpp_codec, Why} -> + ?ERROR_MSG("Failed to decode xml element ~p when " + "routing from ~ts to ~ts: ~ts", + [El, jid:encode(From), jid:encode(To), + xmpp:format_error(Why)]) + end; +route(#jid{} = From, #jid{} = To, Packet) -> + route(xmpp:set_from_to(Packet, From, To)). -route(From, To, Packet) -> - case catch do_route(From, To, Packet) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p~nwhen processing: ~p", - [Reason, {From, To, Packet}]); - _ -> - ok +-spec route_error(stanza(), stanza_error()) -> ok. +route_error(Packet, Err) -> + Type = xmpp:get_type(Packet), + if Type == error; Type == result -> + ok; + true -> + route(xmpp:make_error(Packet, Err)) end. %% Route the error packet only if the originating packet is not an error itself. %% RFC3920 9.3.1 -spec route_error(jid(), jid(), xmlel(), xmlel()) -> ok; (jid(), jid(), stanza(), stanza_error()) -> ok. - route_error(From, To, #xmlel{} = ErrPacket, #xmlel{} = OrigPacket) -> #xmlel{attrs = Attrs} = OrigPacket, case <<"error">> == fxml:get_attr_s(<<"type">>, Attrs) of @@ -99,390 +135,421 @@ route_error(From, To, Packet, #stanza_error{} = Err) -> if Type == error; Type == result -> ok; true -> - ejabberd_router:route(From, To, xmpp:make_error(Packet, Err)) + route(From, To, xmpp:make_error(Packet, Err)) end. --spec register_route(binary()) -> term(). +-spec route_iq(iq(), fun((iq() | timeout) -> any())) -> ok. +route_iq(IQ, Fun) -> + route_iq(IQ, Fun, undefined, ?IQ_TIMEOUT). -register_route(Domain) -> - ?WARNING_MSG("~s:register_route/1 is deprected, " - "use ~s:register_route/2 instead", - [?MODULE, ?MODULE]), - register_route(Domain, ?MYNAME). +-spec route_iq(iq(), term(), pid() | atom()) -> ok. +route_iq(IQ, State, Proc) -> + route_iq(IQ, State, Proc, ?IQ_TIMEOUT). --spec register_route(binary(), binary()) -> term(). +-spec route_iq(iq(), term(), pid() | atom(), undefined | non_neg_integer()) -> ok. +route_iq(IQ, State, Proc, undefined) -> + route_iq(IQ, State, Proc, ?IQ_TIMEOUT); +route_iq(IQ, State, Proc, Timeout) -> + ejabberd_iq:route(IQ, Proc, State, Timeout). +-spec register_route(binary(), binary()) -> ok. register_route(Domain, ServerHost) -> register_route(Domain, ServerHost, undefined). --spec register_route(binary(), binary(), local_hint()) -> term(). - +-spec register_route(binary(), binary(), local_hint() | undefined) -> ok. register_route(Domain, ServerHost, LocalHint) -> + register_route(Domain, ServerHost, LocalHint, self()). + +-spec register_route(binary(), binary(), local_hint() | undefined, pid()) -> ok. +register_route(Domain, ServerHost, LocalHint, Pid) -> case {jid:nameprep(Domain), jid:nameprep(ServerHost)} of - {error, _} -> erlang:error({invalid_domain, Domain}); - {_, error} -> erlang:error({invalid_domain, ServerHost}); - {LDomain, LServerHost} -> - Pid = self(), - case get_component_number(LDomain) of - undefined -> - F = fun () -> - mnesia:write(#route{domain = LDomain, pid = Pid, - server_host = LServerHost, - local_hint = LocalHint}) - end, - mnesia:transaction(F); - N -> - F = fun () -> - case mnesia:wread({route, LDomain}) of - [] -> - mnesia:write(#route{domain = LDomain, - server_host = LServerHost, - pid = Pid, - local_hint = 1}), - lists:foreach( - fun (I) -> - mnesia:write( - #route{domain = LDomain, - pid = undefined, - server_host = LServerHost, - local_hint = I}) - end, - lists:seq(2, N)); - Rs -> - lists:any( - fun (#route{pid = undefined, - local_hint = I} = R) -> - mnesia:write( - #route{domain = LDomain, - pid = Pid, - server_host = LServerHost, - local_hint = I}), - mnesia:delete_object(R), - true; - (_) -> false - end, - Rs) - end - end, - mnesia:transaction(F) - end + {error, _} -> + erlang:error({invalid_domain, Domain}); + {_, error} -> + erlang:error({invalid_domain, ServerHost}); + {LDomain, LServerHost} -> + Mod = get_backend(), + case Mod:register_route(LDomain, LServerHost, LocalHint, + get_component_number(LDomain), Pid) of + ok -> + ?DEBUG("Route registered: ~ts", [LDomain]), + monitor_route(LDomain, Pid), + ejabberd_hooks:run(route_registered, [LDomain]), + delete_cache(Mod, LDomain); + {error, Err} -> + ?ERROR_MSG("Failed to register route ~ts: ~p", + [LDomain, Err]) + end end. -spec register_routes([{binary(), binary()}]) -> ok. - register_routes(Domains) -> lists:foreach(fun ({Domain, ServerHost}) -> register_route(Domain, ServerHost) end, Domains). --spec unregister_route(binary()) -> term(). - +-spec unregister_route(binary()) -> ok. unregister_route(Domain) -> + unregister_route(Domain, self()). + +-spec unregister_route(binary(), pid()) -> ok. +unregister_route(Domain, Pid) -> case jid:nameprep(Domain) of - error -> erlang:error({invalid_domain, Domain}); - LDomain -> - Pid = self(), - case get_component_number(LDomain) of - undefined -> - F = fun () -> - case mnesia:match_object(#route{domain = LDomain, - pid = Pid, _ = '_'}) - of - [R] -> mnesia:delete_object(R); - _ -> ok - end - end, - mnesia:transaction(F); - _ -> - F = fun () -> - case mnesia:match_object(#route{domain = LDomain, - pid = Pid, _ = '_'}) - of - [R] -> - I = R#route.local_hint, - ServerHost = R#route.server_host, - mnesia:write(#route{domain = LDomain, - server_host = ServerHost, - pid = undefined, - local_hint = I}), - mnesia:delete_object(R); - _ -> ok - end - end, - mnesia:transaction(F) - end + error -> + erlang:error({invalid_domain, Domain}); + LDomain -> + Mod = get_backend(), + case Mod:unregister_route( + LDomain, get_component_number(LDomain), Pid) of + ok -> + ?DEBUG("Route unregistered: ~ts", [LDomain]), + demonitor_route(LDomain, Pid), + ejabberd_hooks:run(route_unregistered, [LDomain]), + delete_cache(Mod, LDomain); + {error, Err} -> + ?ERROR_MSG("Failed to unregister route ~ts: ~p", + [LDomain, Err]) + end end. -spec unregister_routes([binary()]) -> ok. - unregister_routes(Domains) -> lists:foreach(fun (Domain) -> unregister_route(Domain) end, Domains). --spec dirty_get_all_routes() -> [binary()]. +-spec find_routes(binary()) -> [#route{}]. +find_routes(Domain) -> + Mod = get_backend(), + case use_cache(Mod) of + true -> + case ets_cache:lookup( + ?ROUTES_CACHE, {route, Domain}, + fun() -> + case Mod:find_routes(Domain) of + {ok, Rs} when Rs /= [] -> + {ok, Rs}; + _ -> + error + end + end) of + {ok, Rs} -> Rs; + error -> [] + end; + false -> + case Mod:find_routes(Domain) of + {ok, Rs} -> Rs; + _ -> [] + end + end. -dirty_get_all_routes() -> - lists:usort(mnesia:dirty_all_keys(route)) -- (?MYHOSTS). - --spec dirty_get_all_domains() -> [binary()]. - -dirty_get_all_domains() -> - lists:usort(mnesia:dirty_all_keys(route)). +-spec get_all_routes() -> [binary()]. +get_all_routes() -> + Mod = get_backend(), + case use_cache(Mod) of + true -> + case ets_cache:lookup( + ?ROUTES_CACHE, routes, + fun() -> + case Mod:get_all_routes() of + {ok, Rs} when Rs /= [] -> + {ok, Rs}; + _ -> + error + end + end) of + {ok, Rs} -> Rs; + error -> [] + end; + false -> + case Mod:get_all_routes() of + {ok, Rs} -> Rs; + _ -> [] + end + end. -spec host_of_route(binary()) -> binary(). - host_of_route(Domain) -> case jid:nameprep(Domain) of error -> erlang:error({invalid_domain, Domain}); LDomain -> - case mnesia:dirty_read(route, LDomain) of + case find_routes(LDomain) of [#route{server_host = ServerHost}|_] -> ServerHost; - [] -> + _ -> erlang:error({unregistered_route, Domain}) end end. --spec process_iq(jid(), jid(), iq() | xmlel()) -> any(). -process_iq(From, To, #iq{} = IQ) -> - if To#jid.luser == <<"">> -> - ejabberd_local:process_iq(From, To, IQ); - true -> - ejabberd_sm:process_iq(From, To, IQ) - end; -process_iq(From, To, El) -> - try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of - IQ -> process_iq(From, To, IQ) - catch _:{xmpp_codec, Why} -> - Type = xmpp:get_type(El), - if Type == <<"get">>; Type == <<"set">> -> - Txt = xmpp:format_error(Why), - Lang = xmpp:get_lang(El), - Err = xmpp:make_error(El, xmpp:err_bad_request(Txt, Lang)), - ejabberd_router:route(To, From, Err); - true -> - ok +-spec is_my_route(binary()) -> boolean(). +is_my_route(Domain) -> + case jid:nameprep(Domain) of + error -> + erlang:error({invalid_domain, Domain}); + LDomain -> + find_routes(LDomain) /= [] + end. + +-spec is_my_host(binary()) -> boolean(). +is_my_host(Domain) -> + case jid:nameprep(Domain) of + error -> + erlang:error({invalid_domain, Domain}); + LDomain -> + case find_routes(LDomain) of + [#route{server_host = LDomain}|_] -> true; + _ -> false end end. +-spec process_iq(iq()) -> any(). +process_iq(IQ) -> + gen_iq_handler:handle(IQ). + +-spec config_reloaded() -> ok. +config_reloaded() -> + Mod = get_backend(), + init_cache(Mod). + %%==================================================================== %% gen_server callbacks %%==================================================================== - -%%-------------------------------------------------------------------- -%% Function: init(Args) -> {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} -%% Description: Initiates the server -%%-------------------------------------------------------------------- init([]) -> - update_tables(), - mnesia:create_table(route, - [{ram_copies, [node()]}, - {type, bag}, - {attributes, record_info(fields, route)}]), - mnesia:add_table_copy(route, node(), ram_copies), - mnesia:subscribe({table, route, simple}), - lists:foreach(fun (Pid) -> erlang:monitor(process, Pid) - end, - mnesia:dirty_select(route, - [{{route, '_', '$1', '_'}, [], ['$1']}])), + ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50), + Mod = get_backend(), + init_cache(Mod), + Mod:init(), + clean_cache(), {ok, #state{}}. -%%-------------------------------------------------------------------- -%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | -%% {stop, Reason, State} -%% Description: Handling call messages -%%-------------------------------------------------------------------- -handle_call(_Request, _From, State) -> - Reply = ok, {reply, Reply, State}. - -%%-------------------------------------------------------------------- -%% Function: handle_cast(Msg, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling cast messages -%%-------------------------------------------------------------------- -handle_cast(_Msg, State) -> {noreply, State}. - -%%-------------------------------------------------------------------- -%% Function: handle_info(Info, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling all non call/cast messages -%%-------------------------------------------------------------------- -handle_info({route, From, To, Packet}, State) -> - case catch do_route(From, To, Packet) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p~nwhen processing: ~p", - [Reason, {From, To, Packet}]); - _ -> ok - end, - {noreply, State}; -handle_info({mnesia_table_event, - {write, #route{pid = Pid}, _ActivityId}}, - State) -> - erlang:monitor(process, Pid), {noreply, State}; -handle_info({'DOWN', _Ref, _Type, Pid, _Info}, State) -> - F = fun () -> - Es = mnesia:select(route, - [{#route{pid = Pid, _ = '_'}, [], ['$_']}]), - lists:foreach(fun (E) -> - if is_integer(E#route.local_hint) -> - LDomain = E#route.domain, - I = E#route.local_hint, - ServerHost = E#route.server_host, - mnesia:write(#route{domain = - LDomain, - server_host = - ServerHost, - pid = - undefined, - local_hint = - I}), - mnesia:delete_object(E); - true -> mnesia:delete_object(E) - end - end, - Es) - end, - mnesia:transaction(F), - {noreply, State}; -handle_info(_Info, State) -> +handle_call({monitor, Domain, Pid}, _From, State) -> + MRefs = State#state.route_monitors, + MRefs1 = case maps:is_key({Domain, Pid}, MRefs) of + true -> MRefs; + false -> + MRef = erlang:monitor(process, Pid), + MRefs#{{Domain, Pid} => MRef} + end, + {reply, ok, State#state{route_monitors = MRefs1}}; +handle_call({demonitor, Domain, Pid}, _From, State) -> + MRefs = State#state.route_monitors, + MRefs1 = case maps:find({Domain, Pid}, MRefs) of + {ok, MRef} -> + erlang:demonitor(MRef, [flush]), + maps:remove({Domain, Pid}, MRefs); + error -> + MRefs + end, + {reply, ok, State#state{route_monitors = MRefs1}}; +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), {noreply, State}. -%%-------------------------------------------------------------------- -%% Function: terminate(Reason, State) -> void() -%% Description: This function is called by a gen_server when it is about to -%% terminate. It should be the opposite of Module:init/1 and do any necessary -%% cleaning up. When it returns, the gen_server terminates with Reason. -%% The return value is ignored. -%%-------------------------------------------------------------------- -terminate(_Reason, _State) -> - ok. +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), + {noreply, State}. + +handle_info({route, Packet}, State) -> + route(Packet), + {noreply, State}; +handle_info({'DOWN', MRef, _, Pid, Info}, State) -> + MRefs = maps:filter( + fun({Domain, P}, M) when P == Pid, M == MRef -> + ?DEBUG("Process ~p with route registered to ~ts " + "has terminated unexpectedly with reason: ~p", + [P, Domain, Info]), + try unregister_route(Domain, Pid) + catch _:_ -> ok + end, + false; + (_, _) -> + true + end, State#state.route_monitors), + {noreply, State#state{route_monitors = MRefs}}; +handle_info(Info, State) -> + ?ERROR_MSG("Unexpected info: ~p", [Info]), + {noreply, State}. + +terminate(_Reason, _State) -> + ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 50). -%%-------------------------------------------------------------------- -%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} -%% Description: Convert process state when code is changed -%%-------------------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> {ok, State}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- --spec do_route(jid(), jid(), xmlel() | xmpp_element()) -> any(). -do_route(OrigFrom, OrigTo, OrigPacket) -> - ?DEBUG("route~n\tfrom ~p~n\tto ~p~n\tpacket " - "~p~n", - [OrigFrom, OrigTo, OrigPacket]), - case ejabberd_hooks:run_fold(filter_packet, - {OrigFrom, OrigTo, OrigPacket}, []) - of - {From, To, Packet} -> - LDstDomain = To#jid.lserver, - case mnesia:dirty_read(route, LDstDomain) of - [] -> - try xmpp:decode(Packet, ?NS_CLIENT, [ignore_els]) of - Pkt -> - ejabberd_s2s:route(From, To, Pkt) - catch _:{xmpp_codec, Why} -> - log_decoding_error(From, To, Packet, Why) - end; - [R] -> - do_route(From, To, Packet, R); - Rs -> - Value = get_domain_balancing(From, To, LDstDomain), - case get_component_number(LDstDomain) of - undefined -> - case [R || R <- Rs, node(R#route.pid) == node()] of +-spec do_route(stanza()) -> ok. +do_route(OrigPacket1) -> + ?DEBUG("Route:~n~ts", [xmpp:pp(OrigPacket1)]), + OrigPacket = process_privilege_iq(OrigPacket1), + case ejabberd_hooks:run_fold(filter_packet, OrigPacket, []) of + drop -> + ok; + Packet -> + case ejabberd_iq:dispatch(Packet) of + true -> + ok; + false -> + To = xmpp:get_to(Packet), + LDstDomain = To#jid.lserver, + case find_routes(LDstDomain) of [] -> - R = lists:nth(erlang:phash(Value, length(Rs)), Rs), - do_route(From, To, Packet, R); - LRs -> - R = lists:nth(erlang:phash(Value, length(LRs)), LRs), - do_route(From, To, Packet, R) - end; - _ -> - SRs = lists:ukeysort(#route.local_hint, Rs), - R = lists:nth(erlang:phash(Value, length(SRs)), SRs), - do_route(From, To, Packet, R) - end - end; - drop -> ok + ejabberd_s2s:route(Packet); + [Route] -> + do_route(Packet, Route); + Routes -> + From = xmpp:get_from(Packet), + balancing_route(From, To, Packet, Routes) + end, + ok + end end. --spec do_route(jid(), jid(), xmlel() | xmpp_element(), #route{}) -> any(). -do_route(From, To, Packet, #route{local_hint = LocalHint, - pid = Pid}) when is_pid(Pid) -> - try xmpp:decode(Packet, ?NS_CLIENT, [ignore_els]) of - Pkt -> - case LocalHint of - {apply, Module, Function} when node(Pid) == node() -> - Module:Function(From, To, Pkt); - _ -> - Pid ! {route, From, To, Pkt} - end - catch error:{xmpp_codec, Why} -> - log_decoding_error(From, To, Packet, Why) - end; -do_route(_From, _To, _Packet, _Route) -> - drop. +%% @format-begin +process_privilege_iq(Packet) -> + Type = xmpp:get_type(Packet), + case xmpp:get_meta(Packet, privilege_iq, none) of + {OriginalId, OriginalHost, ReplacedJid} when (Type == result) or (Type == error) -> + Privilege = #privilege{forwarded = #forwarded{sub_els = [Packet]}}, + #iq{type = xmpp:get_type(Packet), + id = OriginalId, + to = jid:make(OriginalHost), + from = ReplacedJid, + sub_els = [Privilege]}; + _ -> + Packet + end. +%% @format-end --spec log_decoding_error(jid(), jid(), xmlel() | xmpp_element(), term()) -> ok. -log_decoding_error(From, To, Packet, Reason) -> - ?ERROR_MSG("failed to decode xml element ~p when " - "routing from ~s to ~s: ~s", - [Packet, jid:to_string(From), jid:to_string(To), - xmpp:format_error(Reason)]). +-spec do_route(stanza(), #route{}) -> any(). +do_route(Pkt, #route{local_hint = LocalHint, + pid = Pid}) when is_pid(Pid) -> + case LocalHint of + {apply, Module, Function} when node(Pid) == node() -> + Module:Function(Pkt); + _ -> + ejabberd_cluster:send(Pid, {route, Pkt}) + end; +do_route(_Pkt, _Route) -> + ok. + +-spec balancing_route(jid(), jid(), stanza(), [#route{}]) -> any(). +balancing_route(From, To, Packet, Rs) -> + case get_domain_balancing(From, To, To#jid.lserver) of + undefined -> + Value = erlang:system_time(), + case [R || R <- Rs, node(R#route.pid) == node()] of + [] -> + R = lists:nth(erlang:phash2(Value, length(Rs))+1, Rs), + do_route(Packet, R); + LRs -> + R = lists:nth(erlang:phash2(Value, length(LRs))+1, LRs), + do_route(Packet, R) + end; + Value -> + SRs = lists:ukeysort(#route.local_hint, Rs), + R = lists:nth(erlang:phash2(Value, length(SRs))+1, SRs), + do_route(Packet, R) + end. -spec get_component_number(binary()) -> pos_integer() | undefined. get_component_number(LDomain) -> - ejabberd_config:get_option( - {domain_balancing_component_number, LDomain}, - fun(N) when is_integer(N), N > 1 -> N end, - undefined). + M = ejabberd_option:domain_balancing(), + case maps:get(LDomain, M, undefined) of + undefined -> undefined; + Opts -> maps:get(component_number, Opts) + end. --spec get_domain_balancing(jid(), jid(), binary()) -> any(). +-spec get_domain_balancing(jid(), jid(), binary()) -> integer() | ljid() | undefined. get_domain_balancing(From, To, LDomain) -> - case ejabberd_config:get_option( - {domain_balancing, LDomain}, fun(D) when is_atom(D) -> D end) of - undefined -> p1_time_compat:monotonic_time(); - random -> p1_time_compat:monotonic_time(); - source -> jid:tolower(From); - destination -> jid:tolower(To); - bare_source -> jid:remove_resource(jid:tolower(From)); - bare_destination -> jid:remove_resource(jid:tolower(To)) + M = ejabberd_option:domain_balancing(), + case maps:get(LDomain, M, undefined) of + undefined -> undefined; + Opts -> + case maps:get(type, Opts, random) of + random -> erlang:system_time(); + source -> jid:tolower(From); + destination -> jid:tolower(To); + bare_source -> jid:remove_resource(jid:tolower(From)); + bare_destination -> jid:remove_resource(jid:tolower(To)) + end end. --spec update_tables() -> ok. -update_tables() -> - try - mnesia:transform_table(route, ignore, record_info(fields, route)) - catch exit:{aborted, {no_exists, _}} -> +-spec monitor_route(binary(), pid()) -> ok. +monitor_route(Domain, Pid) -> + ?GEN_SERVER:call(?MODULE, {monitor, Domain, Pid}, ?CALL_TIMEOUT). + +-spec demonitor_route(binary(), pid()) -> ok. +demonitor_route(Domain, Pid) -> + case whereis(?MODULE) == self() of + true -> + ok; + false -> + ?GEN_SERVER:call(?MODULE, {demonitor, Domain, Pid}, ?CALL_TIMEOUT) + end. + +-spec get_backend() -> module(). +get_backend() -> + DBType = ejabberd_option:router_db_type(), + list_to_existing_atom("ejabberd_router_" ++ atom_to_list(DBType)). + +-spec cache_nodes(module()) -> [node()]. +cache_nodes(Mod) -> + case erlang:function_exported(Mod, cache_nodes, 0) of + true -> Mod:cache_nodes(); + false -> ejabberd_cluster:get_nodes() + end. + +-spec use_cache(module()) -> boolean(). +use_cache(Mod) -> + case erlang:function_exported(Mod, use_cache, 0) of + true -> Mod:use_cache(); + false -> ejabberd_option:router_use_cache() + end. + +-spec delete_cache(module(), binary()) -> ok. +delete_cache(Mod, Domain) -> + case use_cache(Mod) of + true -> + ets_cache:delete(?ROUTES_CACHE, {route, Domain}, cache_nodes(Mod)), + ets_cache:delete(?ROUTES_CACHE, routes, cache_nodes(Mod)); + false -> ok - end, - case lists:member(local_route, - mnesia:system_info(tables)) - of - true -> mnesia:delete_table(local_route); - false -> ok end. -opt_type(domain_balancing) -> - fun (random) -> random; - (source) -> source; - (destination) -> destination; - (bare_source) -> bare_source; - (bare_destination) -> bare_destination - end; -opt_type(domain_balancing_component_number) -> - fun (N) when is_integer(N), N > 1 -> N end; -opt_type(_) -> [domain_balancing, domain_balancing_component_number]. +-spec init_cache(module()) -> ok. +init_cache(Mod) -> + case use_cache(Mod) of + true -> + ets_cache:new(?ROUTES_CACHE, cache_opts()); + false -> + ets_cache:delete(?ROUTES_CACHE) + end. + +-spec cache_opts() -> [proplists:property()]. +cache_opts() -> + MaxSize = ejabberd_option:router_cache_size(), + CacheMissed = ejabberd_option:router_cache_missed(), + LifeTime = ejabberd_option:router_cache_life_time(), + [{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}]. + +-spec clean_cache(node()) -> non_neg_integer(). +clean_cache(Node) -> + ets_cache:filter( + ?ROUTES_CACHE, + fun(_, error) -> + false; + (routes, _) -> + false; + ({route, _}, {ok, Rs}) -> + not lists:any( + fun(#route{pid = Pid}) -> + node(Pid) == Node + end, Rs) + end). + +-spec clean_cache() -> ok. +clean_cache() -> + ejabberd_cluster:eval_everywhere(?MODULE, clean_cache, [node()]). diff --git a/src/ejabberd_router_mnesia.erl b/src/ejabberd_router_mnesia.erl new file mode 100644 index 000000000..66ae02208 --- /dev/null +++ b/src/ejabberd_router_mnesia.erl @@ -0,0 +1,236 @@ +%%%------------------------------------------------------------------- +%%% Created : 11 Jan 2017 by Evgeny Khramtsov +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%------------------------------------------------------------------- +-module(ejabberd_router_mnesia). +-behaviour(ejabberd_router). +-behaviour(gen_server). + +%% API +-export([init/0, register_route/5, unregister_route/3, find_routes/1, + get_all_routes/0, use_cache/0]). +%% gen_server callbacks +-export([init/1, handle_cast/2, handle_call/3, handle_info/2, + terminate/2, code_change/3, start_link/0]). + +-include("ejabberd_router.hrl"). +-include("logger.hrl"). +-include_lib("stdlib/include/ms_transform.hrl"). + +-record(state, {}). + +%%%=================================================================== +%%% API +%%%=================================================================== +-spec init() -> ok | {error, any()}. +init() -> + Spec = {?MODULE, {?MODULE, start_link, []}, + transient, 5000, worker, [?MODULE]}, + case supervisor:start_child(ejabberd_backend_sup, Spec) of + {ok, _Pid} -> ok; + Err -> Err + end. + +-spec start_link() -> {ok, pid()} | {error, any()}. +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +use_cache() -> + false. + +register_route(Domain, ServerHost, LocalHint, undefined, Pid) -> + F = fun () -> + mnesia:write(#route{domain = Domain, + pid = Pid, + server_host = ServerHost, + local_hint = LocalHint}) + end, + transaction(F); +register_route(Domain, ServerHost, _LocalHint, N, Pid) -> + F = fun () -> + case mnesia:wread({route, Domain}) of + [] -> + mnesia:write(#route{domain = Domain, + server_host = ServerHost, + pid = Pid, + local_hint = 1}), + lists:foreach( + fun (I) -> + mnesia:write( + #route{domain = Domain, + pid = undefined, + server_host = ServerHost, + local_hint = I}) + end, + lists:seq(2, N)); + Rs -> + lists:any( + fun (#route{pid = undefined, + local_hint = I} = R) -> + mnesia:write( + #route{domain = Domain, + pid = Pid, + server_host = ServerHost, + local_hint = I}), + mnesia:delete_object(R), + true; + (_) -> false + end, + Rs) + end + end, + transaction(F). + +unregister_route(Domain, undefined, Pid) -> + F = fun () -> + case mnesia:select( + route, + ets:fun2ms( + fun(#route{domain = D, pid = P} = R) + when D == Domain, P == Pid -> R + end)) of + [R] -> mnesia:delete_object(R); + _ -> ok + end + end, + transaction(F); +unregister_route(Domain, _, Pid) -> + F = fun () -> + case mnesia:select( + route, + ets:fun2ms( + fun(#route{domain = D, pid = P} = R) + when D == Domain, P == Pid -> R + end)) of + [R] -> + I = R#route.local_hint, + ServerHost = R#route.server_host, + mnesia:write(#route{domain = Domain, + server_host = ServerHost, + pid = undefined, + local_hint = I}), + mnesia:delete_object(R); + _ -> ok + end + end, + transaction(F). + +find_routes(Domain) -> + {ok, mnesia:dirty_read(route, Domain)}. + +get_all_routes() -> + {ok, mnesia:dirty_select( + route, + ets:fun2ms( + fun(#route{domain = Domain, server_host = ServerHost}) + when Domain /= ServerHost -> Domain + end))}. + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== +init([]) -> + update_tables(), + ejabberd_mnesia:create(?MODULE, route, + [{ram_copies, [node()]}, + {type, bag}, + {attributes, record_info(fields, route)}]), + mnesia:subscribe({table, route, simple}), + lists:foreach( + fun (Pid) -> erlang:monitor(process, Pid) end, + mnesia:dirty_select( + route, + ets:fun2ms( + fun(#route{pid = Pid}) -> Pid end))), + {ok, #state{}}. + +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), + {noreply, State}. + +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), + {noreply, State}. + +handle_info({mnesia_table_event, + {write, #route{pid = Pid}, _ActivityId}}, State) -> + erlang:monitor(process, Pid), + {noreply, State}; +handle_info({mnesia_table_event, _}, State) -> + {noreply, State}; +handle_info({'DOWN', _Ref, _Type, Pid, _Info}, State) -> + F = fun () -> + Es = mnesia:select( + route, + ets:fun2ms( + fun(#route{pid = P} = E) + when P == Pid -> E + end)), + lists:foreach( + fun(E) -> + if is_integer(E#route.local_hint) -> + LDomain = E#route.domain, + I = E#route.local_hint, + ServerHost = E#route.server_host, + mnesia:write(#route{domain = LDomain, + server_host = ServerHost, + pid = undefined, + local_hint = I}), + mnesia:delete_object(E); + true -> + mnesia:delete_object(E) + end + end, Es) + end, + transaction(F), + {noreply, State}; +handle_info(Info, State) -> + ?ERROR_MSG("Unexpected info: ~p", [Info]), + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +transaction(F) -> + case mnesia:transaction(F) of + {atomic, _} -> + ok; + {aborted, Reason} -> + ?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]), + {error, db_failure} + end. + +-spec update_tables() -> ok. +update_tables() -> + try + mnesia:transform_table(route, ignore, record_info(fields, route)) + catch exit:{aborted, {no_exists, _}} -> + ok + end, + case lists:member(local_route, mnesia:system_info(tables)) of + true -> mnesia:delete_table(local_route); + false -> ok + end. diff --git a/src/ejabberd_router_multicast.erl b/src/ejabberd_router_multicast.erl index 283bcac25..df8473c2b 100644 --- a/src/ejabberd_router_multicast.erl +++ b/src/ejabberd_router_multicast.erl @@ -5,7 +5,7 @@ %%% Created : 11 Aug 2007 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -30,7 +30,7 @@ -behaviour(gen_server). %% API --export([route_multicast/4, +-export([route_multicast/5, register_route/1, unregister_route/1 ]). @@ -39,11 +39,10 @@ %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). + terminate/2, code_change/3, update_to_in_wrapped/2]). --include("ejabberd.hrl"). -include("logger.hrl"). --include("xmpp.hrl"). +-include_lib("xmpp/include/xmpp.hrl"). -record(route_multicast, {domain = <<"">> :: binary() | '_', pid = self() :: pid()}). @@ -59,9 +58,11 @@ start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). --spec route_multicast(jid(), binary(), [jid()], stanza()) -> ok. -route_multicast(From, Domain, Destinations, Packet) -> - case catch do_route(From, Domain, Destinations, Packet) of +-spec route_multicast(jid(), binary(), [jid()], stanza(), boolean()) -> ok. +route_multicast(From0, Domain0, Destinations0, Packet0, Wrapped0) -> + {From, Domain, Destinations, Packet, Wrapped} = + ejabberd_hooks:run_fold(multicast_route, Domain0, {From0, Domain0, Destinations0, Packet0, Wrapped0}, []), + case catch do_route(Domain, Destinations, xmpp:set_from(Packet, From), Wrapped) of {'EXIT', Reason} -> ?ERROR_MSG("~p~nwhen processing: ~p", [Reason, {From, Domain, Destinations, Packet}]); @@ -115,12 +116,11 @@ unregister_route(Domain) -> %% Description: Initiates the server %%-------------------------------------------------------------------- init([]) -> - mnesia:create_table(route_multicast, + ejabberd_mnesia:create(?MODULE, route_multicast, [{ram_copies, [node()]}, {type, bag}, {attributes, record_info(fields, route_multicast)}]), - mnesia:add_table_copy(route_multicast, node(), ram_copies), mnesia:subscribe({table, route_multicast, simple}), lists:foreach( fun(Pid) -> @@ -138,9 +138,9 @@ init([]) -> %% {stop, Reason, State} %% Description: Handling call messages %%-------------------------------------------------------------------- -handle_call(_Request, _From, State) -> - Reply = ok, - {reply, Reply, State}. +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), + {noreply, State}. %%-------------------------------------------------------------------- %% Function: handle_cast(Msg, State) -> {noreply, State} | @@ -148,7 +148,8 @@ handle_call(_Request, _From, State) -> %% {stop, Reason, State} %% Description: Handling cast messages %%-------------------------------------------------------------------- -handle_cast(_Msg, State) -> +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), {noreply, State}. %%-------------------------------------------------------------------- @@ -157,11 +158,11 @@ handle_cast(_Msg, State) -> %% {stop, Reason, State} %% Description: Handling all non call/cast messages %%-------------------------------------------------------------------- -handle_info({route_multicast, From, Domain, Destinations, Packet}, State) -> - case catch do_route(From, Domain, Destinations, Packet) of +handle_info({route_multicast, Domain, Destinations, Packet}, State) -> + case catch do_route(Domain, Destinations, Packet, false) of {'EXIT', Reason} -> ?ERROR_MSG("~p~nwhen processing: ~p", - [Reason, {From, Domain, Destinations, Packet}]); + [Reason, {Domain, Destinations, Packet}]); _ -> ok end, @@ -184,7 +185,8 @@ handle_info({'DOWN', _Ref, _Type, Pid, _Info}, State) -> end, mnesia:transaction(F), {noreply, State}; -handle_info(_Info, State) -> +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), {noreply, State}. %%-------------------------------------------------------------------- @@ -204,30 +206,54 @@ terminate(_Reason, _State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. +-spec update_to_in_wrapped(stanza(), jid()) -> stanza(). +update_to_in_wrapped(Packet, To) -> + case Packet of + #message{sub_els = [#ps_event{ + items = #ps_items{ + items = [#ps_item{ + sub_els = [Internal] + } = PSItem] + } = PSItems + } = PSEvent]} -> + Internal2 = xmpp:set_to(Internal, To), + PSItem2 = PSItem#ps_item{sub_els = [Internal2]}, + PSItems2 = PSItems#ps_items{items = [PSItem2]}, + PSEvent2 = PSEvent#ps_event{items = PSItems2}, + xmpp:set_to(Packet#message{sub_els = [PSEvent2]}, To); + _ -> + xmpp:set_to(Packet, To) + end. + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- %% From = #jid %% Destinations = [#jid] --spec do_route(jid(), binary(), [jid()], stanza()) -> any(). -do_route(From, Domain, Destinations, Packet) -> - - ?DEBUG("route_multicast~n\tfrom ~s~n\tdomain ~s~n\tdestinations ~p~n\tpacket ~p~n", - [jid:to_string(From), - Domain, - [jid:to_string(To) || To <- Destinations], - Packet]), - +-spec do_route(binary(), [jid()], stanza(), boolean()) -> any(). +do_route(Domain, Destinations, Packet, true) -> + ?DEBUG("Route multicast:~n~ts~nDomain: ~ts~nDestinations: ~ts~n", + [xmpp:pp(Packet), Domain, + str:join([jid:encode(To) || To <- Destinations], <<", ">>)]), + lists:foreach( + fun(To) -> + Packet2 = update_to_in_wrapped(Packet, To), + ejabberd_router:route(Packet2) + end, Destinations); +do_route(Domain, Destinations, Packet, false) -> + ?DEBUG("Route multicast:~n~ts~nDomain: ~ts~nDestinations: ~ts~n", + [xmpp:pp(Packet), Domain, + str:join([jid:encode(To) || To <- Destinations], <<", ">>)]), %% Try to find an appropriate multicast service case mnesia:dirty_read(route_multicast, Domain) of %% If no multicast service is available in this server, send manually - [] -> do_route_normal(From, Destinations, Packet); + [] -> do_route_normal(Destinations, Packet); %% If some is available, send the packet using multicast service Rs when is_list(Rs) -> Pid = pick_multicast_pid(Rs), - Pid ! {route_trusted, From, Destinations, Packet} + Pid ! {route_trusted, Destinations, Packet} end. -spec pick_multicast_pid([#route_multicast{}]) -> pid(). @@ -238,6 +264,9 @@ pick_multicast_pid(Rs) -> end, (hd(List))#route_multicast.pid. --spec do_route_normal(jid(), [jid()], stanza()) -> any(). -do_route_normal(From, Destinations, Packet) -> - [ejabberd_router:route(From, To, Packet) || To <- Destinations]. +-spec do_route_normal([jid()], stanza()) -> any(). +do_route_normal(Destinations, Packet) -> + lists:foreach( + fun(To) -> + ejabberd_router:route(xmpp:set_to(Packet, To)) + end, Destinations). diff --git a/src/ejabberd_router_redis.erl b/src/ejabberd_router_redis.erl new file mode 100644 index 000000000..0ddd63aa7 --- /dev/null +++ b/src/ejabberd_router_redis.erl @@ -0,0 +1,189 @@ +%%%------------------------------------------------------------------- +%%% Author : Evgeny Khramtsov +%%% Created : 28 Mar 2017 by Evgeny Khramtsov +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%------------------------------------------------------------------- +-module(ejabberd_router_redis). +-behaviour(ejabberd_router). +-behaviour(gen_server). + +%% API +-export([init/0, register_route/5, unregister_route/3, find_routes/1, + get_all_routes/0]). +%% gen_server callbacks +-export([init/1, handle_cast/2, handle_call/3, handle_info/2, + terminate/2, code_change/3, start_link/0]). + +-include("logger.hrl"). +-include("ejabberd_router.hrl"). + +-record(state, {}). + +-define(ROUTES_KEY, <<"ejabberd:routes">>). +-define(DOMAINS_KEY, <<"ejabberd:domains">>). + +%%%=================================================================== +%%% API +%%%=================================================================== +init() -> + Spec = {?MODULE, {?MODULE, start_link, []}, + transient, 5000, worker, [?MODULE]}, + case supervisor:start_child(ejabberd_backend_sup, Spec) of + {ok, _Pid} -> ok; + Err -> Err + end. + +-spec start_link() -> {ok, pid()} | {error, any()}. +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +register_route(Domain, ServerHost, LocalHint, _, Pid) -> + DomKey = domain_key(Domain), + PidKey = term_to_binary(Pid), + T = term_to_binary({ServerHost, LocalHint}), + case ejabberd_redis:multi( + fun() -> + ejabberd_redis:hset(DomKey, PidKey, T), + ejabberd_redis:sadd(?DOMAINS_KEY, [Domain]), + if Domain /= ServerHost -> + ejabberd_redis:sadd(?ROUTES_KEY, [Domain]); + true -> + ok + end + end) of + {ok, _} -> + ok; + {error, _} -> + {error, db_failure} + end. + +unregister_route(Domain, _, Pid) -> + DomKey = domain_key(Domain), + PidKey = term_to_binary(Pid), + try + {ok, Num} = ejabberd_redis:hdel(DomKey, [PidKey]), + if Num > 0 -> + {ok, Len} = ejabberd_redis:hlen(DomKey), + if Len == 0 -> + {ok, _} = ejabberd_redis:multi( + fun() -> + ejabberd_redis:del([DomKey]), + ejabberd_redis:srem(?ROUTES_KEY, [Domain]), + ejabberd_redis:srem(?DOMAINS_KEY, [Domain]) + end), + ok; + true -> + ok + end; + true -> + ok + end + catch _:{badmatch, {error, _}} -> + {error, db_failure} + end. + +find_routes(Domain) -> + DomKey = domain_key(Domain), + case ejabberd_redis:hgetall(DomKey) of + {ok, Vals} -> + {ok, decode_routes(Domain, Vals)}; + _ -> + {error, db_failure} + end. + +get_all_routes() -> + case ejabberd_redis:smembers(?ROUTES_KEY) of + {ok, Routes} -> + {ok, Routes}; + _ -> + {error, db_failure} + end. + +get_all_domains() -> + case ejabberd_redis:smembers(?DOMAINS_KEY) of + {ok, Domains} -> + {ok, Domains}; + _ -> + {error, db_failure} + end. + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== +init([]) -> + clean_table(), + {ok, #state{}}. + +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), + {noreply, State}. + +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), + {noreply, State}. + +handle_info(Info, State) -> + ?ERROR_MSG("Unexpected info: ~p", [Info]), + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +clean_table() -> + ?DEBUG("Cleaning Redis route entries...", []), + lists:foreach( + fun(#route{domain = Domain, pid = Pid}) when node(Pid) == node() -> + unregister_route(Domain, undefined, Pid); + (_) -> + ok + end, find_routes()). + +find_routes() -> + case get_all_domains() of + {ok, Domains} -> + lists:flatmap( + fun(Domain) -> + case find_routes(Domain) of + {ok, Routes} -> Routes; + {error, _} -> [] + end + end, Domains); + {error, _} -> + [] + end. + +domain_key(Domain) -> + <<"ejabberd:route:", Domain/binary>>. + +decode_routes(Domain, Vals) -> + lists:map( + fun({Pid, Data}) -> + {ServerHost, LocalHint} = binary_to_term(Data), + #route{domain = Domain, + pid = binary_to_term(Pid), + server_host = ServerHost, + local_hint = LocalHint} + end, Vals). diff --git a/src/ejabberd_router_sql.erl b/src/ejabberd_router_sql.erl new file mode 100644 index 000000000..2d7631476 --- /dev/null +++ b/src/ejabberd_router_sql.erl @@ -0,0 +1,154 @@ +%%%------------------------------------------------------------------- +%%% Author : Evgeny Khramtsov +%%% Created : 28 Mar 2017 by Evgeny Khramtsov +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%------------------------------------------------------------------- +-module(ejabberd_router_sql). +-behaviour(ejabberd_router). + + +%% API +-export([init/0, register_route/5, unregister_route/3, find_routes/1, + get_all_routes/0]). +-export([sql_schemas/0]). + +-include("logger.hrl"). +-include("ejabberd_sql_pt.hrl"). +-include("ejabberd_router.hrl"). + + + +%%%=================================================================== +%%% API +%%%=================================================================== +init() -> + ejabberd_sql_schema:update_schema( + ejabberd_config:get_myname(), ?MODULE, sql_schemas()), + Node = erlang:atom_to_binary(node(), latin1), + ?DEBUG("Cleaning SQL 'route' table...", []), + case ejabberd_sql:sql_query( + ejabberd_config:get_myname(), ?SQL("delete from route where node=%(Node)s")) of + {updated, _} -> + ok; + Err -> + ?ERROR_MSG("Failed to clean 'route' table: ~p", [Err]), + Err + end. + +sql_schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"route">>, + columns = + [#sql_column{name = <<"domain">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"node">>, type = text}, + #sql_column{name = <<"pid">>, type = text}, + #sql_column{name = <<"local_hint">>, type = text}], + indices = [#sql_index{ + columns = [<<"domain">>, <<"server_host">>, + <<"node">>, <<"pid">>], + unique = true}]}]}]. + +register_route(Domain, ServerHost, LocalHint, _, Pid) -> + PidS = misc:encode_pid(Pid), + LocalHintS = enc_local_hint(LocalHint), + Node = erlang:atom_to_binary(node(Pid), latin1), + case ?SQL_UPSERT(ejabberd_config:get_myname(), "route", + ["!domain=%(Domain)s", + "!server_host=%(ServerHost)s", + "!node=%(Node)s", + "!pid=%(PidS)s", + "local_hint=%(LocalHintS)s"]) of + ok -> + ok; + _ -> + {error, db_failure} + end. + +unregister_route(Domain, _, Pid) -> + PidS = misc:encode_pid(Pid), + Node = erlang:atom_to_binary(node(Pid), latin1), + case ejabberd_sql:sql_query( + ejabberd_config:get_myname(), + ?SQL("delete from route where domain=%(Domain)s " + "and pid=%(PidS)s and node=%(Node)s")) of + {updated, _} -> + ok; + _ -> + {error, db_failure} + end. + +find_routes(Domain) -> + case ejabberd_sql:sql_query( + ejabberd_config:get_myname(), + ?SQL("select @(server_host)s, @(node)s, @(pid)s, @(local_hint)s " + "from route where domain=%(Domain)s")) of + {selected, Rows} -> + {ok, lists:flatmap( + fun(Row) -> + row_to_route(Domain, Row) + end, Rows)}; + _ -> + {error, db_failure} + end. + +get_all_routes() -> + case ejabberd_sql:sql_query( + ejabberd_config:get_myname(), + ?SQL("select @(domain)s from route where domain <> server_host")) of + {selected, Domains} -> + {ok, [Domain || {Domain} <- Domains]}; + _ -> + {error, db_failure} + end. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +enc_local_hint(undefined) -> + <<"">>; +enc_local_hint(LocalHint) -> + misc:term_to_expr(LocalHint). + +dec_local_hint(<<"">>) -> + undefined; +dec_local_hint(S) -> + ejabberd_sql:decode_term(S). + +row_to_route(Domain, {ServerHost, NodeS, PidS, LocalHintS} = Row) -> + try [#route{domain = Domain, + server_host = ServerHost, + pid = misc:decode_pid(PidS, NodeS), + local_hint = dec_local_hint(LocalHintS)}] + catch _:{bad_node, _} -> + []; + Class:Reason:StackTrace -> + ?ERROR_MSG("Failed to decode row from 'route' table:~n" + "** Row = ~p~n" + "** Domain = ~ts~n" + "** ~ts", + [Row, + Domain, + misc:format_exception(2, Class, Reason, StackTrace)]), + [] + end. diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index 97aef3cab..0876ca584 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -5,7 +5,7 @@ %%% Created : 7 Dec 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -25,142 +25,101 @@ -module(ejabberd_s2s). --protocol({xep, 220, '1.1'}). - --behaviour(ejabberd_config). - -author('alexey@process-one.net'). -behaviour(gen_server). %% API --export([start_link/0, route/3, have_connection/1, - make_key/2, get_connections_pids/1, try_register/1, - remove_connection/2, find_connection/2, +-export([start_link/0, stop/0, route/1, have_connection/1, + get_connections_pids/1, + start_connection/2, start_connection/3, dirty_get_connections/0, allow_host/2, incoming_s2s_number/0, outgoing_s2s_number/0, - stop_all_connections/0, + stop_s2s_connections/0, clean_temporarily_blocked_table/0, list_temporarily_blocked_hosts/0, external_host_overloaded/1, is_temporarly_blocked/1, - check_peer_certificate/3, - get_commands_spec/0]). + get_commands_spec/0, zlib_enabled/1, get_idle_timeout/1, + tls_required/1, tls_enabled/1, tls_options/3, + host_up/1, host_down/1, queue_type/1, register_connection/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --export([get_info_s2s_connections/1, - transform_options/1, opt_type/1]). +-export([get_info_s2s_connections/1]). --include("ejabberd.hrl"). -include("logger.hrl"). - --include("xmpp.hrl"). - +-include_lib("xmpp/include/xmpp.hrl"). -include("ejabberd_commands.hrl"). +-include_lib("stdlib/include/ms_transform.hrl"). --include_lib("public_key/include/public_key.hrl"). - --define(PKIXEXPLICIT, 'OTP-PUB-KEY'). - --define(PKIXIMPLICIT, 'OTP-PUB-KEY'). - --include("XmppAddr.hrl"). +-include("translate.hrl"). -define(DEFAULT_MAX_S2S_CONNECTIONS_NUMBER, 1). - -define(DEFAULT_MAX_S2S_CONNECTIONS_NUMBER_PER_NODE, 1). - -define(S2S_OVERLOAD_BLOCK_PERIOD, 60). -%% once a server is temporarly blocked, it stay blocked for 60 seconds +%% once a server is temporary blocked, it stay blocked for 60 seconds --record(s2s, {fromto = {<<"">>, <<"">>} :: {binary(), binary()} | '_', - pid = self() :: pid() | '_' | '$1'}). +-record(s2s, {fromto :: {binary(), binary()} | '_', + pid :: pid()}). -record(state, {}). --record(temporarily_blocked, {host = <<"">> :: binary(), - timestamp :: integer()}). +-record(temporarily_blocked, {host :: binary(), + timestamp :: integer()}). -type temporarily_blocked() :: #temporarily_blocked{}. - start_link() -> - gen_server:start_link({local, ?MODULE}, ?MODULE, [], - []). + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). --spec route(jid(), jid(), xmpp_element()) -> ok. - -route(From, To, Packet) -> - case catch do_route(From, To, Packet) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p~nwhen processing: ~p", - [Reason, {From, To, Packet}]); - _ -> ok - end. +-spec stop() -> ok. +stop() -> + _ = supervisor:terminate_child(ejabberd_sup, ?MODULE), + _ = supervisor:delete_child(ejabberd_sup, ?MODULE), + ok. clean_temporarily_blocked_table() -> mnesia:clear_table(temporarily_blocked). -spec list_temporarily_blocked_hosts() -> [temporarily_blocked()]. - list_temporarily_blocked_hosts() -> ets:tab2list(temporarily_blocked). -spec external_host_overloaded(binary()) -> {aborted, any()} | {atomic, ok}. - external_host_overloaded(Host) -> - ?INFO_MSG("Disabling connections from ~s for ~p " - "seconds", + ?INFO_MSG("Disabling s2s connections to ~ts for ~p seconds", [Host, ?S2S_OVERLOAD_BLOCK_PERIOD]), mnesia:transaction(fun () -> - Time = p1_time_compat:monotonic_time(), + Time = erlang:monotonic_time(), mnesia:write(#temporarily_blocked{host = Host, timestamp = Time}) end). -spec is_temporarly_blocked(binary()) -> boolean(). - is_temporarly_blocked(Host) -> case mnesia:dirty_read(temporarily_blocked, Host) of - [] -> false; - [#temporarily_blocked{timestamp = T} = Entry] -> - Diff = p1_time_compat:monotonic_time() - T, - case p1_time_compat:convert_time_unit(Diff, native, micro_seconds) of - N when N > (?S2S_OVERLOAD_BLOCK_PERIOD) * 1000 * 1000 -> - mnesia:dirty_delete_object(Entry), false; - _ -> true - end - end. - --spec remove_connection({binary(), binary()}, - pid()) -> {atomic, ok} | ok | {aborted, any()}. - -remove_connection(FromTo, Pid) -> - case catch mnesia:dirty_match_object(s2s, - #s2s{fromto = FromTo, pid = Pid}) - of - [#s2s{pid = Pid}] -> - F = fun () -> - mnesia:delete_object(#s2s{fromto = FromTo, pid = Pid}) - end, - mnesia:transaction(F); - _ -> ok + [] -> false; + [#temporarily_blocked{timestamp = T} = Entry] -> + Diff = erlang:monotonic_time() - T, + case erlang:convert_time_unit(Diff, native, microsecond) of + N when N > (?S2S_OVERLOAD_BLOCK_PERIOD) * 1000 * 1000 -> + mnesia:dirty_delete_object(Entry), false; + _ -> true + end end. -spec have_connection({binary(), binary()}) -> boolean(). - have_connection(FromTo) -> case catch mnesia:dirty_read(s2s, FromTo) of - [_] -> + [_] -> true; _ -> false end. -spec get_connections_pids({binary(), binary()}) -> [pid()]. - get_connections_pids(FromTo) -> case catch mnesia:dirty_read(s2s, FromTo) of L when is_list(L) -> @@ -169,107 +128,159 @@ get_connections_pids(FromTo) -> [] end. --spec try_register({binary(), binary()}) -> boolean(). - -try_register(FromTo) -> - MaxS2SConnectionsNumber = max_s2s_connections_number(FromTo), - MaxS2SConnectionsNumberPerNode = - max_s2s_connections_number_per_node(FromTo), - F = fun () -> - L = mnesia:read({s2s, FromTo}), - NeededConnections = needed_connections_number(L, - MaxS2SConnectionsNumber, - MaxS2SConnectionsNumberPerNode), - if NeededConnections > 0 -> - mnesia:write(#s2s{fromto = FromTo, pid = self()}), - true; - true -> false - end - end, - case mnesia:transaction(F) of - {atomic, Res} -> Res; - _ -> false - end. +-spec register_connection(FromTo :: {binary(), binary()}) -> ok. +register_connection(FromTo) -> + gen_server:call(ejabberd_s2s, {register_connection, FromTo, self()}). -spec dirty_get_connections() -> [{binary(), binary()}]. - dirty_get_connections() -> mnesia:dirty_all_keys(s2s). -check_peer_certificate(SockMod, Sock, Peer) -> - case SockMod:get_peer_certificate(Sock) of - {ok, Cert} -> - case SockMod:get_verify_result(Sock) of - 0 -> - case ejabberd_idna:domain_utf8_to_ascii(Peer) of - false -> - {error, <<"Cannot decode remote server name">>}; - AsciiPeer -> - case - lists:any(fun(D) -> match_domain(AsciiPeer, D) end, - get_cert_domains(Cert)) of - true -> - {ok, <<"Verification successful">>}; - false -> - {error, <<"Certificate host name mismatch">>} - end - end; - VerifyRes -> - {error, fast_tls:get_cert_verify_string(VerifyRes, Cert)} - end; - {error, _Reason} -> - {error, <<"Cannot get peer certificate">>}; - error -> - {error, <<"Cannot get peer certificate">>} +-spec tls_options(binary(), binary(), [proplists:property()]) -> [proplists:property()]. +tls_options(LServer, ServerHost, DefaultOpts) -> + TLSOpts1 = case ejabberd_pkix:get_certfile(LServer) of + error -> DefaultOpts; + {ok, CertFile} -> + lists:keystore(certfile, 1, DefaultOpts, + {certfile, CertFile}) + end, + TLSOpts2 = case ejabberd_option:s2s_ciphers(ServerHost) of + undefined -> TLSOpts1; + Ciphers -> lists:keystore(ciphers, 1, TLSOpts1, + {ciphers, Ciphers}) + end, + TLSOpts3 = case ejabberd_option:s2s_protocol_options(ServerHost) of + undefined -> TLSOpts2; + ProtoOpts -> lists:keystore(protocol_options, 1, TLSOpts2, + {protocol_options, ProtoOpts}) + end, + TLSOpts4 = case ejabberd_option:s2s_dhfile(ServerHost) of + undefined -> TLSOpts3; + DHFile -> lists:keystore(dhfile, 1, TLSOpts3, + {dhfile, DHFile}) + end, + TLSOpts5 = case lists:keymember(cafile, 1, TLSOpts4) of + true -> TLSOpts4; + false -> [{cafile, get_cafile(ServerHost)}|TLSOpts4] + end, + case ejabberd_option:s2s_tls_compression(ServerHost) of + undefined -> TLSOpts5; + false -> [compression_none | TLSOpts5]; + true -> lists:delete(compression_none, TLSOpts5) end. --spec make_key({binary(), binary()}, binary()) -> binary(). -make_key({From, To}, StreamID) -> - Secret = ejabberd_config:get_option(shared_key, fun(V) -> V end), - p1_sha:to_hexlist( - crypto:hmac(sha256, p1_sha:to_hexlist(crypto:hash(sha256, Secret)), - [To, " ", From, " ", StreamID])). +-spec tls_required(binary()) -> boolean(). +tls_required(LServer) -> + TLS = use_starttls(LServer), + TLS == required. + +-spec tls_enabled(binary()) -> boolean(). +tls_enabled(LServer) -> + TLS = use_starttls(LServer), + TLS /= false. + +-spec zlib_enabled(binary()) -> boolean(). +zlib_enabled(LServer) -> + ejabberd_option:s2s_zlib(LServer). + +-spec use_starttls(binary()) -> boolean() | optional | required. +use_starttls(LServer) -> + ejabberd_option:s2s_use_starttls(LServer). + +-spec get_idle_timeout(binary()) -> non_neg_integer() | infinity. +get_idle_timeout(LServer) -> + ejabberd_option:s2s_timeout(LServer). + +-spec queue_type(binary()) -> ram | file. +queue_type(LServer) -> + ejabberd_option:s2s_queue_type(LServer). + +-spec get_cafile(binary()) -> file:filename_all() | undefined. +get_cafile(LServer) -> + case ejabberd_option:s2s_cafile(LServer) of + undefined -> + ejabberd_option:ca_file(); + File -> + File + end. %%==================================================================== %% gen_server callbacks %%==================================================================== - init([]) -> update_tables(), - mnesia:create_table(s2s, - [{ram_copies, [node()]}, - {type, bag}, - {attributes, record_info(fields, s2s)}]), - mnesia:add_table_copy(s2s, node(), ram_copies), - mnesia:subscribe(system), - ejabberd_commands:register_commands(get_commands_spec()), - mnesia:create_table(temporarily_blocked, - [{ram_copies, [node()]}, - {attributes, record_info(fields, temporarily_blocked)}]), - {ok, #state{}}. + ejabberd_mnesia:create(?MODULE, s2s, + [{ram_copies, [node()]}, + {type, bag}, + {attributes, record_info(fields, s2s)}]), + case mnesia:subscribe(system) of + {ok, _} -> + ejabberd_commands:register_commands(get_commands_spec()), + ejabberd_mnesia:create( + ?MODULE, temporarily_blocked, + [{ram_copies, [node()]}, + {attributes, record_info(fields, temporarily_blocked)}]), + ejabberd_hooks:add(host_up, ?MODULE, host_up, 50), + ejabberd_hooks:add(host_down, ?MODULE, host_down, 60), + lists:foreach(fun host_up/1, ejabberd_option:hosts()), + {ok, #state{}}; + {error, Reason} -> + {stop, Reason} + end. -handle_call(_Request, _From, State) -> - {reply, ok, State}. +handle_call({new_connection, Args}, _From, State) -> + {reply, erlang:apply(fun new_connection_int/7, Args), State}; +handle_call({register_connection, FromTo, Pid}, _From, State) -> + {reply, register_connection_int(FromTo, Pid), State}; +handle_call(Request, From, State) -> + ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), + {noreply, State}. -handle_cast(_Msg, State) -> +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), {noreply, State}. handle_info({mnesia_system_event, {mnesia_down, Node}}, State) -> + ?INFO_MSG("Node ~p has left our Mnesia S2S tables", [Node]), clean_table_from_bad_node(Node), {noreply, State}; -handle_info({route, From, To, Packet}, State) -> - case catch do_route(From, To, Packet) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p~nwhen processing: ~p", - [Reason, {From, To, Packet}]); - _ -> ok +handle_info({mnesia_system_event, {mnesia_up, Node}}, State) -> + ?INFO_MSG("Node ~p joined our Mnesia S2S tables", [Node]), + {noreply, State}; +handle_info({route, Packet}, State) -> + try route(Packet) + catch + Class:Reason:StackTrace -> + ?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts", + [xmpp:pp(Packet), + misc:format_exception(2, Class, Reason, StackTrace)]) end, {noreply, State}; -handle_info(_Info, State) -> {noreply, State}. +handle_info({'DOWN', _Ref, process, Pid, _Reason}, State) -> + case mnesia:dirty_match_object(s2s, #s2s{fromto = '_', pid = Pid}) of + [#s2s{pid = Pid, fromto = {From, To}} = Obj] -> + F = fun() -> mnesia:delete_object(Obj) end, + case mnesia:transaction(F) of + {atomic, _} -> ok; + {aborted, Reason} -> + ?ERROR_MSG("Failed to unregister s2s connection for pid ~p (~ts -> ~ts):" + "Mnesia failure: ~p", + [Pid, From, To, Reason]) + end, + {noreply, State}; + _ -> + {noreply, State} + end; +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), + {noreply, State}. terminate(_Reason, _State) -> ejabberd_commands:unregister_commands(get_commands_spec()), - ok. + stop_s2s_connections(stream_error()), + lists:foreach(fun host_down/1, ejabberd_option:hosts()), + ejabberd_hooks:delete(host_up, ?MODULE, host_up, 50), + ejabberd_hooks:delete(host_down, ?MODULE, host_down, 60). code_change(_OldVsn, State, _Extra) -> {ok, State}. @@ -277,44 +288,75 @@ code_change(_OldVsn, State, _Extra) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- +-spec host_up(binary()) -> ok. +host_up(Host) -> + ejabberd_s2s_in:host_up(Host), + ejabberd_s2s_out:host_up(Host). + +-spec host_down(binary()) -> ok. +host_down(Host) -> + Err = stream_error(), + lists:foreach( + fun(#s2s{fromto = {From, _}, pid = Pid}) when node(Pid) == node() -> + case ejabberd_router:host_of_route(From) of + Host -> + ejabberd_s2s_out:send(Pid, Err), + ejabberd_s2s_out:stop_async(Pid); + _ -> + ok + end; + (_) -> + ok + end, ets:tab2list(s2s)), + ejabberd_s2s_in:host_down(Host), + ejabberd_s2s_out:host_down(Host). + -spec clean_table_from_bad_node(node()) -> any(). clean_table_from_bad_node(Node) -> F = fun() -> Es = mnesia:select( s2s, - [{#s2s{pid = '$1', _ = '_'}, - [{'==', {node, '$1'}, Node}], - ['$_']}]), - lists:foreach(fun(E) -> - mnesia:delete_object(E) - end, Es) + ets:fun2ms( + fun(#s2s{pid = Pid} = E) when node(Pid) == Node -> + E + end)), + lists:foreach(fun mnesia:delete_object/1, Es) end, mnesia:async_dirty(F). --spec do_route(jid(), jid(), stanza()) -> ok | false. -do_route(From, To, Packet) -> - ?DEBUG("s2s manager~n\tfrom ~p~n\tto ~p~n\tpacket " - "~P~n", - [From, To, Packet, 8]), - case find_connection(From, To) of - {atomic, Pid} when is_pid(Pid) -> - ?DEBUG("sending to process ~p~n", [Pid]), - #jid{lserver = MyServer} = From, - ejabberd_hooks:run(s2s_send_packet, MyServer, - [From, To, Packet]), - send_element(Pid, xmpp:set_from_to(Packet, From, To)), - ok; - {aborted, _Reason} -> - Lang = xmpp:get_lang(Packet), - Txt = <<"No s2s connection found">>, - Err = xmpp:err_service_unavailable(Txt, Lang), - ejabberd_router:route_error(To, From, Packet, Err), - false +-spec route(stanza()) -> ok. +route(Packet) -> + ?DEBUG("Local route:~n~ts", [xmpp:pp(Packet)]), + From = xmpp:get_from(Packet), + To = xmpp:get_to(Packet), + case start_connection(From, To) of + {ok, Pid} when is_pid(Pid) -> + ?DEBUG("Sending to process ~p~n", [Pid]), + #jid{lserver = MyServer} = From, + case ejabberd_hooks:run_fold(s2s_send_packet, MyServer, Packet, + []) of + drop -> ok; + Packet1 -> ejabberd_s2s_out:route(Pid, Packet1) + end; + {error, Reason} -> + Lang = xmpp:get_lang(Packet), + Err = case Reason of + forbidden -> + xmpp:err_forbidden(?T("Access denied by service policy"), Lang); + internal_server_error -> + xmpp:err_internal_server_error() + end, + ejabberd_router:route_error(Packet, Err) end. --spec find_connection(jid(), jid()) -> {aborted, any()} | {atomic, pid()}. +-spec start_connection(jid(), jid()) + -> {ok, pid()} | {error, forbidden | internal_server_error}. +start_connection(From, To) -> + start_connection(From, To, []). -find_connection(From, To) -> +-spec start_connection(jid(), jid(), [proplists:property()]) + -> {ok, pid()} | {error, forbidden | internal_server_error}. +start_connection(From, To, Opts) -> #jid{lserver = MyServer} = From, #jid{lserver = Server} = To, FromTo = {MyServer, Server}, @@ -323,39 +365,39 @@ find_connection(From, To) -> MaxS2SConnectionsNumberPerNode = max_s2s_connections_number_per_node(FromTo), ?DEBUG("Finding connection for ~p~n", [FromTo]), - case catch mnesia:dirty_read(s2s, FromTo) of - {'EXIT', Reason} -> {aborted, Reason}; - [] -> - %% We try to establish all the connections if the host is not a - %% service and if the s2s host is not blacklisted or - %% is in whitelist: - case not is_service(From, To) andalso - allow_host(MyServer, Server) - of - true -> - NeededConnections = needed_connections_number([], - MaxS2SConnectionsNumber, - MaxS2SConnectionsNumberPerNode), - open_several_connections(NeededConnections, MyServer, - Server, From, FromTo, - MaxS2SConnectionsNumber, - MaxS2SConnectionsNumberPerNode); - false -> {aborted, error} - end; - L when is_list(L) -> - NeededConnections = needed_connections_number(L, - MaxS2SConnectionsNumber, - MaxS2SConnectionsNumberPerNode), - if NeededConnections > 0 -> - %% We establish the missing connections for this pair. - open_several_connections(NeededConnections, MyServer, - Server, From, FromTo, + case mnesia:dirty_read(s2s, FromTo) of + [] -> + %% We try to establish all the connections if the host is not a + %% service and if the s2s host is not blacklisted or + %% is in whitelist: + LServer = ejabberd_router:host_of_route(MyServer), + case allow_host(LServer, Server) of + true -> + NeededConnections = needed_connections_number( + [], MaxS2SConnectionsNumber, - MaxS2SConnectionsNumberPerNode); - true -> - %% We choose a connexion from the pool of opened ones. - {atomic, choose_connection(From, L)} - end + MaxS2SConnectionsNumberPerNode), + open_several_connections(NeededConnections, MyServer, + Server, From, FromTo, + MaxS2SConnectionsNumber, + MaxS2SConnectionsNumberPerNode, Opts); + false -> + {error, forbidden} + end; + L when is_list(L) -> + NeededConnections = needed_connections_number(L, + MaxS2SConnectionsNumber, + MaxS2SConnectionsNumberPerNode), + if NeededConnections > 0 -> + %% We establish the missing connections for this pair. + open_several_connections(NeededConnections, MyServer, + Server, From, FromTo, + MaxS2SConnectionsNumber, + MaxS2SConnectionsNumberPerNode, Opts); + true -> + %% We choose a connection from the pool of opened ones. + {ok, choose_connection(From, L)} + end end. -spec choose_connection(jid(), [#s2s{}]) -> pid(). @@ -365,63 +407,107 @@ choose_connection(From, Connections) -> -spec choose_pid(jid(), [pid()]) -> pid(). choose_pid(From, Pids) -> Pids1 = case [P || P <- Pids, node(P) == node()] of - [] -> Pids; - Ps -> Ps + [] -> Pids; + Ps -> Ps end, Pid = - lists:nth(erlang:phash(jid:remove_resource(From), - length(Pids1)), + lists:nth(erlang:phash2(jid:remove_resource(From), + length(Pids1))+1, Pids1), ?DEBUG("Using ejabberd_s2s_out ~p~n", [Pid]), Pid. +-spec open_several_connections(pos_integer(), binary(), binary(), + jid(), {binary(), binary()}, + integer(), integer(), [proplists:property()]) -> + {ok, pid()} | {error, internal_server_error}. open_several_connections(N, MyServer, Server, From, FromTo, MaxS2SConnectionsNumber, - MaxS2SConnectionsNumberPerNode) -> - ConnectionsResult = [new_connection(MyServer, Server, - From, FromTo, MaxS2SConnectionsNumber, - MaxS2SConnectionsNumberPerNode) - || _N <- lists:seq(1, N)], - case [PID || {atomic, PID} <- ConnectionsResult] of - [] -> hd(ConnectionsResult); - PIDs -> {atomic, choose_pid(From, PIDs)} + MaxS2SConnectionsNumberPerNode, Opts) -> + case lists:flatmap( + fun(_) -> + new_connection(MyServer, Server, + From, FromTo, MaxS2SConnectionsNumber, + MaxS2SConnectionsNumberPerNode, Opts) + end, lists:seq(1, N)) of + [] -> + {error, internal_server_error}; + PIDs -> + {ok, choose_pid(From, PIDs)} end. +-spec new_connection(binary(), binary(), jid(), {binary(), binary()}, + integer(), integer(), [proplists:property()]) -> [pid()]. new_connection(MyServer, Server, From, FromTo, - MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode) -> - {ok, Pid} = ejabberd_s2s_out:start( - MyServer, Server, new), + MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode, Opts) -> + case whereis(ejabberd_s2s) == self() of + true -> + new_connection_int(MyServer, Server, From, FromTo, + MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode, Opts); + false -> + gen_server:call(ejabberd_s2s, {new_connection, [MyServer, Server, From, FromTo, + MaxS2SConnectionsNumber, + MaxS2SConnectionsNumberPerNode, Opts]}) + end. + +new_connection_int(MyServer, Server, From, FromTo, + MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode, Opts) -> + {ok, Pid} = ejabberd_s2s_out:start(MyServer, Server, Opts), F = fun() -> L = mnesia:read({s2s, FromTo}), NeededConnections = needed_connections_number(L, MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode), if NeededConnections > 0 -> - mnesia:write(#s2s{fromto = FromTo, pid = Pid}), - ?INFO_MSG("New s2s connection started ~p", [Pid]), - Pid; + mnesia:write(#s2s{fromto = FromTo, pid = Pid}), + Pid; true -> choose_connection(From, L) end end, TRes = mnesia:transaction(F), case TRes of - {atomic, Pid} -> ejabberd_s2s_out:start_connection(Pid); - _ -> ejabberd_s2s_out:stop_connection(Pid) - end, - TRes. - --spec max_s2s_connections_number({binary(), binary()}) -> integer(). -max_s2s_connections_number({From, To}) -> - case acl:match_rule(From, max_s2s_connections, jid:make(To)) of - Max when is_integer(Max) -> Max; - _ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER + {atomic, Pid1} -> + if Pid1 == Pid -> + erlang:monitor(process, Pid), + ejabberd_s2s_out:connect(Pid); + true -> + ejabberd_s2s_out:stop_async(Pid) + end, + [Pid1]; + {aborted, Reason} -> + ?ERROR_MSG("Failed to register s2s connection ~ts -> ~ts: " + "Mnesia failure: ~p", + [MyServer, Server, Reason]), + ejabberd_s2s_out:stop_async(Pid), + [] end. --spec max_s2s_connections_number_per_node({binary(), binary()}) -> integer(). +-spec register_connection_int(FromTo :: {binary(), binary()}, Pid :: pid()) -> ok. +register_connection_int(FromTo, Pid) -> + F = fun() -> + mnesia:write(#s2s{fromto = FromTo, pid = Pid}) + end, + TRes = mnesia:transaction(F), + case TRes of + {atomic, _} -> + erlang:monitor(process, Pid), + ok; + _ -> + ok + end. + +-spec max_s2s_connections_number({binary(), binary()}) -> pos_integer(). +max_s2s_connections_number({From, To}) -> + case ejabberd_shaper:match(From, max_s2s_connections, jid:make(To)) of + Max when is_integer(Max) -> Max; + _ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER + end. + +-spec max_s2s_connections_number_per_node({binary(), binary()}) -> pos_integer(). max_s2s_connections_number_per_node({From, To}) -> - case acl:match_rule(From, max_s2s_connections_per_node, jid:make(To)) of - Max when is_integer(Max) -> Max; - _ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER_PER_NODE + case ejabberd_shaper:match(From, max_s2s_connections_per_node, jid:make(To)) of + Max when is_integer(Max) -> Max; + _ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER_PER_NODE end. -spec needed_connections_number([#s2s{}], integer(), integer()) -> integer(). @@ -431,61 +517,28 @@ needed_connections_number(Ls, MaxS2SConnectionsNumber, lists:min([MaxS2SConnectionsNumber - length(Ls), MaxS2SConnectionsNumberPerNode - length(LocalLs)]). -%%-------------------------------------------------------------------- -%% Function: is_service(From, To) -> true | false -%% Description: Return true if the destination must be considered as a -%% service. -%% -------------------------------------------------------------------- --spec is_service(jid(), jid()) -> boolean(). -is_service(From, To) -> - LFromDomain = From#jid.lserver, - case ejabberd_config:get_option( - {route_subdomains, LFromDomain}, - fun(s2s) -> s2s; (local) -> local end, local) of - s2s -> % bypass RFC 3920 10.3 - false; - local -> - Hosts = (?MYHOSTS), - P = fun (ParentDomain) -> - lists:member(ParentDomain, Hosts) - end, - lists:any(P, parent_domains(To#jid.lserver)) - end. - -parent_domains(Domain) -> - lists:foldl(fun (Label, []) -> [Label]; - (Label, [Head | Tail]) -> - [<