Compare commits

..

110 Commits
4.7 ... 4.10

Author SHA1 Message Date
Alex Vandiver
4bb22d2535 Release Zulip Server 4.10. 2022-02-25 21:19:38 +00:00
Mateusz Mandera
c93cef91e8 create_preregistration_user: Add additional hardening assertion.
TestMaybeSendToRegistration needs tweaking here, because it wasn't
setting the subdomain for the dummy request, so
maybe_send_to_registration was actually running with realm=None, which
is not right for these tests.

Also, test_sso_only_when_preregistration_user_exists was creating
PreregistrationUser without setting the realm, which was also incorrect.
2022-02-22 23:18:34 +00:00
Mateusz Mandera
0c227217b2 registration: Change create_preregistration_user to take realm as arg.
create_preregistration_user is a footgun, because it takes the realm
from the request. The calling code is supposed to validate that
registration for the realm is allowed
first, but can sometimes do that on "realm" taken from something else
than the request - and later on calls create_preregistration_user, thus
leading to prereg user creation on unvalidated request.realm.

It's safer, and makes more sense, for this function to take the intended
realm as argument, instead of taking the entire request. It follows that
the same should be done for prepare_activation_url.
2022-02-22 23:18:34 +00:00
Mateusz Mandera
b5c7a79bdf tests: Fix some instances of logged in session polluting test state.
In these tests, the code ends up with a logged in session when it's
undesired - later on these tests make requests to a different subdomain
- where a logged in session is not supposed to exist. This leads to an
unintended, strange situation where request.user is a user from the old
subdomain but the request itself is to a *different* subdomain. This
throws off get_realm_from_request, which will return the realm from
request.user.realm - which is not what these tests want and can lead to
these tests failing when some of the production code being tested
switches to using get_realm_from_request instead of
get_realm(get_subdomain).
2022-02-22 23:18:34 +00:00
Mateusz Mandera
7e991c8c7e CVE-2022-21706: Prevent use of multiuse invites to join other orgs.
The codepaths for joining an organization via a multi-use invitation
(accounts_home_from_multiuse_invite and maybe_send_to_registration)
weren't validating whether
the organization the invite was generated for matches the organization
the user attempts to join - potentially allowing an attacker with access
to organization A to generate a multi-use invite and use it to join
organization B within the same deployment, that they shouldn't have
access to.
2022-02-22 23:18:31 +00:00
Mateusz Mandera
974c98a45a CVE-2021-3967: Only regenerate the API key by authing with the old key. 2022-02-22 14:13:56 -08:00
Alex Vandiver
5784bdd0ed puppet: Use goarch for wal-g.
wal-g does not currently provide pre-built binaries for
arm64/aarch64 (see #21070) but if they begin to, it will likely be
with the goarch names.

(cherry picked from commit d7e8733705)
2022-02-15 15:57:00 -08:00
Alex Vandiver
e10ea15aa9 puppet: Use goarch for go-camo.
(cherry picked from commit abdbe4ca83)
2022-02-15 15:57:00 -08:00
Alex Vandiver
d860242220 puppet: Use goarch for golang.
Fixes: #21051.
(cherry picked from commit be2f2a5bde)
2022-02-15 15:57:00 -08:00
Alex Vandiver
b2c3f5e510 puppet: Include go version in go-camo release information. 2022-02-15 15:57:00 -08:00
Alex Vandiver
232fe495be puppet: Factor out $::architecture case statement for golang.
(cherry picked from commit 788daa953b)
2022-02-15 15:57:00 -08:00
Alex Vandiver
c20afad828 puppet: Add aarch64 build hashes to external dependencies.
wal-g does not ship aarch64 binaries, currently; the compilation
process([1]) is somewhat complicated, so we defer the decision about
how to support wal-g for aarch64 until a later date.

[1]: https://github.com/wal-g/wal-g/blob/master/docs/PostgreSQL.md#installing

(cherry picked from commit c094867a74)
2022-02-15 15:57:00 -08:00
Alex Vandiver
3fad49a9c1 puppet: Centralize versions and sha256 hashes of external dependencies.
This will make it easier to update versions of these dependencies.

(cherry picked from commit f166f9f7d6)
2022-02-15 15:57:00 -08:00
Alex Vandiver
cc95aac176 puppet: Move wal-g to external_dep, in /srv/zulip-wal-g-*. 2022-02-15 15:57:00 -08:00
Alex Vandiver
1b27ec9fae puppet: Stop making resources for external binaries and directories.
In the event that extracting doesn't produce the binary we expected it
to, all this will do is create an _empty_ file where we expect the
binary to be.  This will likely muddle debugging.

Since the only reason the resource was made in the first place was to
make dependencies clear, switch to depending on the External_Dep
itself, when such a dependency is needed.

(cherry picked from commit 1e4e6a09af)
2022-02-15 15:57:00 -08:00
Alex Vandiver
ebd74239a2 puppet: Move slash out of $dir by convention.
(cherry picked from commit 3c163a7d5e)
2022-02-15 15:57:00 -08:00
Alex Vandiver
8dcb1e489d puppet: Adjust wal-g release version and SHA256.
wal-g apparently removed the 1.1.1 release; replace it with the
equivalent rc.

(cherry picked from commit d2a78bac7e)
2022-02-15 15:57:00 -08:00
Anders Kaseorg
4e7419e168 provision: Use apt-get --allow-downgrades.
Needed for commit 9c8d2b7be3 (#21115).

Signed-off-by: Anders Kaseorg <anders@zulip.com>
(cherry picked from commit 7213116dd3)
2022-02-14 16:45:29 -08:00
Anders Kaseorg
8567e19fff apt-repos: Downgrade PostgreSQL to dodge PGroonga regression.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
(cherry picked from commit 9c8d2b7be3)
2022-02-14 15:05:06 -08:00
Anders Kaseorg
9aa7c19891 apt-repos: Remove groovy.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
(cherry picked from commit 43c4672deb)
2022-02-14 15:05:06 -08:00
Anders Kaseorg
4fdbf274ac setup-apt-repo: Support installing an APT preferences file.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
(cherry picked from commit fdc1294993)
2022-02-14 15:05:06 -08:00
Anders Kaseorg
218eca14b8 setup-apt-repo: Move supported release check earlier.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
(cherry picked from commit 7077a289ae)
2022-02-14 15:05:06 -08:00
Anders Kaseorg
08efebbaff setup-apt-repo: Use /etc/os-release instead of lsb_release.
But still install lsb-release for now since Puppet acts funny without
it.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
(cherry picked from commit c8bb98554e)
2022-02-14 15:05:06 -08:00
Alex Vandiver
1c819208d0 setup: Merge multiple setup-apt-repo scripts into one.
This moves the `.asc` files into subdirectories, and writes out the
according `.list` files into them.  It moves from templates to
written-out `.list` files for clarity and ease of
implementation (Debian and Ubuntu need different templates for
`zulip`), and as a way of making explicit which releases are supported
for each list.  For the special-case of the PGroonga signing key, we
source an additional file within the directory.

This simplifies the process for adding another class of `.list` file.

(cherry picked from commit f3eea72c2a)
2022-02-14 15:05:06 -08:00
Anders Kaseorg
8424649c70 reindex-textual-data: Find psycopg2 in the virtualenv.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
(cherry picked from commit aec6cd4cdb)
2022-01-26 17:15:47 -08:00
Alex Vandiver
33d8c190d8 version: Update version after 4.9 release. 2022-01-24 18:44:09 -08:00
Alex Vandiver
0213b811ec Release Zulip Server 4.9 2022-01-25 01:40:31 +00:00
Alex Vandiver
c27324927e CVE-2021-43799: Set a secure Erlang cookie.
The RabbitMQ docs state ([1]):

    RabbitMQ nodes and CLI tools (e.g. rabbitmqctl) use a cookie to
    determine whether they are allowed to communicate with each
    other. [...] The cookie is just a string of alphanumeric
    characters up to 255 characters in size. It is usually stored in a
    local file.

...and goes on to state (emphasis ours):

    If the file does not exist, Erlang VM will try to create one with
    a randomly generated value when the RabbitMQ server starts
    up. Using such generated cookie files are **appropriate in
    development environments only.**

The auto-generated cookie does not use cryptographic sources of
randomness, and generates 20 characters of `[A-Z]`.  Because of a
semi-predictable seed, the entropy of this password is thus less than
the idealized 26^20 = 94 bits of entropy; in actuality, it is 36 bits
of entropy, or potentially as low as 20 if the performance of the
server is known.

These sizes are well within the scope of remote brute-force attacks.

On provision, install, and upgrade, replace the default insecure
20-character Erlang cookie with a cryptographically secure
255-character string (the max length allowed).

[1] https://www.rabbitmq.com/clustering.html#erlang-cookie
2022-01-25 01:35:31 +00:00
Alex Vandiver
c087ed4c26 configure-rabbitmq: Set -u, and not -x. 2022-01-25 01:34:20 +00:00
Alex Vandiver
ffc1f81cde configure-rabbitmq: Factor out sudo, instead of rabbitmqctl. 2022-01-25 01:34:20 +00:00
Alex Vandiver
90b6fe2c6e upgrade: Show output from (re)starting zulip.
5c450afd2d, in ancient history, switched from `check_call` to
`check_output` and throwing away its result.

Use check_call, so that we show the steps to (re)starting the server.
2022-01-25 01:34:20 +00:00
Alex Vandiver
36cebad4c0 CVE-2021-43799: During upgrades, restart rabbitmq if necessary.
Check if it is listening on a public interface on port 25672, and if
so shut it down so it can pick up the new configuration.
2022-01-25 01:34:20 +00:00
Alex Vandiver
f33fbb527c upgrade: Make calling shutdown_server twice, only try once. 2022-01-25 01:34:20 +00:00
Alex Vandiver
134a8d4301 CVE-2021-43799: Write rabbitmq configuration before starting.
Zulip writes a `rabbitmq.config` configuration file which locks down
RabbitMQ to listen only on localhost:5672, as well as the RabbitMQ
distribution port, on localhost:25672.

The "distribution port" is part of Erlang's clustering configuration;
while it is documented that the protocol is fundamentally
insecure ([1], [2]) and can result in remote arbitrary execution of
code, by default the RabbitMQ configuration on Debian and Ubuntu
leaves it publicly accessible, with weak credentials.

The configuration file that Zulip writes, while effective, is only
written _after_ the package has been installed and the service
started, which leaves the port exposed until RabbitMQ or system
restart.

Ensure that rabbitmq's `/etc/rabbitmq/rabbitmq.config` is written
before rabbitmq is installed or starts, and that changes to that file
trigger a restart of the service, such that the ports are only ever
bound to localhost.  This does not mitigate existing installs, since
it does not force a rabbitmq restart.

[1] https://www.erlang.org/doc/apps/erts/erl_dist_protocol.html
[2] https://www.erlang.org/doc/reference_manual/distributed.html#distributed-erlang-system
2022-01-25 01:34:17 +00:00
Alex Vandiver
a07f64a463 puppet: Always set the RabbitMQ nodename to zulip@localhost.
This is required in order to lock down the RabbitMQ port to only
listen on localhost.  If the nodename is `rabbit@hostname`, in most
circumstances the hostname will resolve to an external IP, which the
rabbitmq port will not be bound to.

Installs which used `rabbit@hostname`, due to RabbitMQ having been
installed before Zulip, would not have functioned if the host or
RabbitMQ service was restarted, as the localhost restrictions in the
RabbitMQ configuration would have made rabbitmqctl (and Zulip cron
jobs that call it) unable to find the rabbitmq server.

The previous commit ensures that configure-rabbitmq is re-run after
the nodename has changed.  However, rabbitmq needs to be stopped
before `rabbitmq-env.conf` is changed; we use an `onlyif` on an `exec`
to print the warning about the node change, and let the subsequent
config change and notify of the service and configure-rabbitmq to
complete the re-configuration.
2022-01-25 01:33:27 +00:00
Alex Vandiver
e9af26df6e puppet: Run configure-rabbitmq on nodename change.
`/etc/rabbitmq/rabbitmq-env.conf` sets the nodename; anytime the
nodename changes, the backing database changes, and this requires
re-creating the rabbitmq users and permissions.

Trigger this in puppet by running configure-rabbitmq after the file
changes.
2022-01-24 23:09:02 +00:00
Alex Vandiver
7f6b423532 setup: Remove unused RABBITMQ_NODE.
This reverts commit 889547ff5e.  It is
unused in the Docker container, as the configurtaion of the `zulip`
user in the rabbitmq node is done via environment variables.  The
Zulip host in that context does not have `rabbitmqctl` installed, and
would have needed to know the Erlang cookie to be able to run these
commands.
2022-01-24 23:09:02 +00:00
Alex Vandiver
d95fb34ba7 puppet: Admit we leave epmd port 4369 open on all interfaces.
The Erlang `epmd` daemon listens on port 4369, and provides
information (without authentication) about which Erlang processes are
listening on what ports.  This information is not itself a
vulnerability, but may provide information for remote attackers about
what local Erlang services (such as `rabbitmq-server`) are running,
and where.

`epmd` supports an `ERL_EPMD_ADDRESS` environment variable to limit
which interfaces it binds on.  While this environment variable is set
in `/etc/default/rabbitmq-server`, Zulip unfortunately attempts to
start `epmd` using an explicit `exec` block, which ignores those
settings.

Regardless, this lack of `ERL_EPMD_ADDRESS` variable only controls
`epmd`'s startup upon first installation.  Upon reboot, there are two
ways in which `epmd` might be started, neither of which respect
`ERL_EPMD_ADDRESS`:

 - On Focal, an `epmd` service exists and is activated, which uses
   systemd's configuration to choose which interfaces to bind on, and
   thus `ERL_EPMD_ADDRESS` is irrelevant.

 - On Bionic (and Focal, due to a broken dependency from
   `rabbitmq-server` to `epmd@` instead of `epmd`, which may lead to
   the explicit `epmd` service losing a race), `epmd` is started by
   `rabbitmq-server` when it does not detect a running instance.
   Unfortunately, only `/etc/init.d/rabbitmq-server` would respects
   `/etc/default/rabbitmq-server` -- and it defers the actual startup
   to using systemd, which does not pass the environment variable
   down.  Thus, `ERL_EPMD_ADDRESS` is also irrelevant here.

We unfortunately cannot limit `epmd` to only listening on localhost,
due to a number of overlapping bugs and limitations:

 - Manually starting `epmd` with `-address 127.0.0.1` silently fails
   to start on hosts with IPv6 disabled, due to an Erlang bug ([1],
   [2]).

 - The dependencies of the systemd `rabbitmq-server` service can be
   fixed to include the `epmd` service, and systemd can be made to
   bind to `127.0.0.1:4369` and pass that socket to `epmd`, bypassing
   the above bug.  However, the startup of this service is not
   guaranteed, because it races with other sources of `epmd` (see
   below).

 - Any process that runs `rabbitmqctl` results in `epmd` being started
   if one is not currently running; these instances do not respect any
   environment variables as to which addresses to bind on.  This is
   also triggered by `service rabbitmq-server status`, as well as
   various Zulip cron jobs which inspect the rabbitmq queues.  As
   such, it is difficult-to-impossible to ensure that some other
   `epmd` process will not win the race and open the port on all
   interfaces.

Since the only known exposure from leaving port 4369 open is
information that rabbitmq is running on the host, and the complexity
of adjusting this to only bind on localhost is high, we remove the
setting which does not address the problem, and document that the port
is left open, and should be protected via system-level or
network-level firewalls.

[1]: https://bugs.launchpad.net/ubuntu/+source/erlang/+bug/1374109
[2]: https://github.com/erlang/otp/issues/4820
2022-01-24 23:09:02 +00:00
Alex Vandiver
5ff759d35c puppet: Remove rabbitmq_mochiweb configuration.
mochiweb was renamed to web_dispatch in RabbitMQ 3.8.0, and the plugin
is not enabled.  Nor does this control the management interface, which
would listen on port 15672.
2022-01-24 23:09:02 +00:00
Alex Vandiver
a0d1074212 ci: Cache with the OS name, not the job name.
The job name is just the constant `production_build`.  Renaming it to
have the OS in the key ensures that it is not shared across OS'es (for
instance between `4.x` and `main`, which are now bionic and buster,
respectively), and also allows it to share caches with the install
step, which uses the OS name in that place.
2022-01-24 15:07:50 -08:00
Alex Vandiver
2e1e2b08f1 puppet: Fix standalone certbot configurations.
This addresses the problems mentioned in the previous commit, but for
existing installations which have `authenticator = standalone` in
their configurations.

This reconfigures all hostnames in certbot to use the webroot
authenticator, and attempts to force-renew their certificates.
Force-renewal is necessary because certbot contains no way to merely
update the configuration.  Let's Encrypt allows for multiple extra
renewals per week, so this is a reasonable cost.

Because the certbot configuration is `configobj`, and not
`configparser`, we have no way to easily parse to determine if webroot
is in use; additionally, `certbot certificates` does not provide this
information.  We use `grep`, on the assumption that this will catch
nearly all cases.

It is possible that this will find `authenticator = standalone`
certificates which are managed by Certbot, but not Zulip certificates.
These certificates would also fail to renew while Zulip is running, so
switching them to use the Zulip webroot would still be an improvement.

Fixes #20593.

(cherry picked from commit a3adaf4aa3)
2022-01-24 20:14:23 +00:00
Alex Vandiver
b44a1b68f6 setup: Install a temporary certificate, before certbot runs.
Installing certbot with --method=standalone means that the
configuration file will be written to assume that the standalone
method will be used going forward.  Since nginx will be running,
attempts to renew the certificate will fail.

Install a temporary self-signed certificate, just to allow nginx to
start, and then follow up (after applying puppet to start nginx) with
the call to setup-certbot, which will use the webroot authenticator.

The `setup-certbot --method=standalone` option is left intact, for use
in development environments.

Fixes part of #20593; it does not address installs which were
previously improperly configured with `authenticator = standalone`.

(cherry picked from commit 76ce8631c0)
2022-01-24 20:14:23 +00:00
Alex Vandiver
c3adbcea13 docs: Mention Camo does not use a local Smokescreen in the proxies docs.
This documents the new behaviour in d328d3dd4d.

(cherry picked from commit be1c4c2bd8)
2022-01-21 16:21:15 -08:00
Alex Vandiver
e088b343b3 puppet: Document that upgrades from Git require 3GB.
The step of rebuilding static assets using webpack requires more than
2G of RAM.

(cherry picked from commit 5f237cb34e)
2022-01-19 12:37:55 -08:00
Alex Vandiver
1d559bbffa puppet: Allow routing camo requests through an outgoing proxy.
Because Camo includes logic to deny access to private subnets, routing
its requests through Smokescreen is generally not necessary.  However,
it may be necessary if Zulip has configured a non-Smokescreen exit
proxy.

Default Camo to using the proxy only if it is not Smokescreen, with a
new `proxy.enable_for_camo` setting to override this behaviour if need
be.  Note that that setting is in `zulip.conf` on the host with Camo
installed -- not the Zulip frontend host, if they are different.

Fixes: #20550.
(cherry picked from commit d328d3dd4d)
2022-01-11 15:13:09 -08:00
Alex Vandiver
cb24f93bba puppet: Make zulipconf() understand coercion to bool.
If the default is a bool, coerce the value into a bool as well.  For
backwards compatibility, this does not adjust any existing callsites.

`queue_workers_multiprocess` is the only setting which is passed a
bool default, but it was already documented to take `true` or `false`;
simplify it to no longer add the now-unnecessary Boolean conversion.

(cherry picked from part of commit 2c5fc1827c)
2022-01-11 15:13:09 -08:00
Tim Abbott
868180a25d upgrade-zulip-from-git: Improve webpack failure error handling.
We've had a number of unhappy reports of upgrades failing due to
webpack requiring too much memory.  While the previous commit will
likely fix this issue for everyone, it's worth improving the error
message for failures here.

We avoid doing the stop+retry ourselves, because that could cause an
outage in a production system if webpack fails for another reason.

Fixes #20105.
2022-01-07 11:47:05 -08:00
Tim Abbott
20fc1f651a upgrade-zulip-from-git: Require more memory to run webpack.
Since the upgrade to Webpack 5, we've been seeing occasional reports
that servers with roughly 4GiB of RAM were getting OOM kills while
running webpack.

Since we can't readily optimize the memory requirements for webpack
itself, we should raise the RAM requirements for doing the
lower-downtime upgrade strategy.

Fixes #20231.
2022-01-07 11:47:05 -08:00
Emilio López
0d79d6735a docs: Clarify use of loadbalancer.ips when using a reverse proxy.
When Zulip is run behind one or more reverse proxies, you must
configure `loadbalancer.ips` so that Zulip respects the client IP
addresses found in the `X-Forwarded-For` header. This is not
immediately clear from the documentation, so this commit makes it more
clear and augments the existing examples to showcase this need.

Fixes: #19073
(cherry picked from commit baea14ee57)
2022-01-07 11:44:41 -08:00
Anders Kaseorg
45568a08c0 reindex-textual-data: Reindex textual functional indexes too.
This catches nine functional indexes that the previous query didn’t:

upper_preregistration_email_idx
upper_stream_name_idx
upper_subject_idx
upper_userprofile_email_idx
zerver_message_recipient_upper_subject
zerver_mutedtopic_stream_topic
zerver_stream_realm_id_name_uniq
zerver_userprofile_realm_id_delivery_email_uniq
zerver_userprofile_realm_id_email_uniq

Signed-off-by: Anders Kaseorg <anders@zulip.com>
(cherry picked from commit 1cc1de82cd)
2022-01-07 10:37:38 -08:00
Alex Vandiver
22152a0662 Revert "puppet: Do not assume amd64 architecture."
This reverts commit 859d88f76c.  It does
not work, since the sha256 hashes are different for different
architectures.

arm64 support exists in `main`.
2022-01-04 15:00:39 -08:00
Alya Abbott
9bbb336441 developer docs: Tweak ToS for push notifications wording. 2021-12-14 14:47:20 -08:00
Sahil Batra
3d966f1af9 message: Check wildcard mention restrictions while editing message.
This commit adds code to check whether a user is allowed to use
wildcard mention in a large stream or not while editing a message
based on the realm settings.

Previously this was only checked while sending message, thus user
was easily able to use wildcard mention by first sending a normal
message and then using a wildcard mention by editing it.

(cherry picked from commit b68ebf5a22)
2021-12-14 11:55:18 -08:00
Alex Vandiver
ab98f3801f setup-certbot: Reinstate nginx reload after installation.
If nginx was already installed, and we're using the webroot method of
initializing certbot, nginx needs to be reloaded.  Hooks in
`/etc/letsencrypt/renewal-hooks/deploy/` do not run during initial
`certbot certonly`, so an explicit reload is required.

(cherry picked from commit f6520a97cd)
2021-12-13 10:30:00 -08:00
Alex Vandiver
ddca8a7f9a puppet: Use certbot package timer, not our own cron job.
The certbot package installs its own systemd timer (and cron job,
which disabled itself if systemd is enabled) which updates
certificates.  This process races with the cron job which Zulip
installs -- the only difference being that Zulip respects the
`certbot.auto_renew` setting, and that it passes the deploy hook.
This means that occasionally nginx would not be reloaded, when the
systemd timer caught the expiration first.

Remove the custom cron job and `certbot-maybe-renew` script, and
reconfigure certbot to always reload nginx after deploying, using
certbot directory hooks.

Since `certbot.auto_renew` can't have an effect, remove the setting.
In turn, this removes the need for `--no-zulip-conf` to
`setup-certbot`.  `--deploy-hook` is similarly removed, as running
deploy hooks to restart nginx is now the default; pass
`--no-directory-hooks` in standalone mode to not attempt to reload
nginx.  The other property of `--deploy-hook`, of skipping symlinking
into place, is given its own flog.

(cherry picked from commit 01e8f752a8)
2021-12-09 13:48:20 -08:00
Tim Abbott
c1c3dfced5 scripts: Fix running compare-settings-to-template from any CWD.
This matches the number of dirname() calls for other files in its
directory.

Fixes #20489.
2021-12-07 14:47:27 -08:00
Alex Vandiver
2d3f505505 puppet: Install camo on Docker.
Now that go-camo runs within supervisor, it can be run in Docker
simply.

Fixes #20101.
Fixes zulip/docker-zulip#179.

(cherry picked from commit f31bf3f06c)
2021-12-06 19:33:31 +00:00
Alex Vandiver
d3573af95c puppet: Read camo secret at startup time, not at puppet-apply time.
Writing the secret to the supervisor configuration file makes changes
to the secret requires a zulip-puppet-apply to take hold.  The Docker
image is constructed to avoid having to run zulip-puppet-apply on
startup, and indeed cannot run zulip-puppet-apply after having
configured secrets, as it has replaced the zulip.conf file with a
symlink, for example.  This means that camo gets the static secret
that was built into the image, and not the one regenerated on first
startup.

Read the camo secret at process startup time.  Because this pattern is
likely common with "12-factor" applications which can read from
environment variables, write a generic tool to map secrets to
environment variables before exec'ing a binary, and use that for Camo.

(cherry picked from commit 358a7fb0c6)
2021-12-06 19:33:31 +00:00
Alex Vandiver
859d88f76c puppet: Do not assume amd64 architecture.
(cherry picked from commit 7db146d0a9)
2021-12-06 11:10:37 -08:00
Alex Vandiver
9a0fb497a4 changelog: Fix lint issues. 2021-12-01 23:39:28 +00:00
Alex Vandiver
7ea4ad75af version: Update version after 4.8 release. 2021-12-01 23:37:49 +00:00
Alex Vandiver
ae000bfdba Release Zulip Server 4.8 2021-12-01 23:17:46 +00:00
Mateusz Mandera
551b387164 CVE-2021-43791: Validate confirmation keys in /accounts/register/ codepath.
A confirmation link takes a user to the check_prereg_key_and_redirect
endpoint, before getting redirected to POST to /accounts/register/. The
problem was that validation was happening in the check_prereg_key_and_redirect
part and not in /accounts/register/ - meaning that one could submit an
expired confirmation key and be able to register.

We fix this by moving validation into /accouts/register/.
2021-12-01 23:13:11 +00:00
Mateusz Mandera
720d16e809 confirmation: Use error status codes for confirmation link error pages. 2021-12-01 20:28:51 +00:00
Alex Vandiver
f338ff64c3 puppet: Use sysv status command, not supervisorctl status.
Since Supervisor 4, which is installed on Ubuntu 20.04 and Debian 11,
`supervisorctl status` returns exit code 3 if any of the
supervisor-controlled processes are not running.

Using `supervisorctl status` as the Puppet `status` command for
Supervisor leads to unnecessarily trying to "start" a Supervisor
process which is already started, but happens to have one or more of
its managed processes stopped.  This is an unnecessary no-op in
production environments, but in docker-init enviroments, such as in
CI, attempting to start the process a second time is an error.

Switch to checking if supervisor is running by way of sysv init.  This
fixes the potential error in CI, as well as eliminates unnecessary
"starts" of supervisor when it was already running -- a situation
which made zulip-puppet-apply not idempotent:

```
root@alexmv-prod:~# supervisorctl status
process-fts-updates                                             STOPPED   Nov 10 12:33 AM
smokescreen                                                     RUNNING   pid 1287280, uptime 0:35:32
zulip-django                                                    STOPPED   Nov 10 12:33 AM
zulip-tornado                                                   STOPPED   Nov 10 12:33 AM
[...]

root@alexmv-prod:~# ~zulip/deployments/current/scripts/zulip-puppet-apply --force
Notice: Compiled catalog for alexmv-prod.zulipdev.org in environment production in 2.32 seconds
Notice: /Stage[main]/Zulip::Supervisor/Service[supervisor]/ensure: ensure changed 'stopped' to 'running'
Notice: Applied catalog in 0.91 seconds

root@alexmv-prod:~# ~zulip/deployments/current/scripts/zulip-puppet-apply --force
Notice: Compiled catalog for alexmv-prod.zulipdev.org in environment production in 2.35 seconds
Notice: /Stage[main]/Zulip::Supervisor/Service[supervisor]/ensure: ensure changed 'stopped' to 'running'
Notice: Applied catalog in 0.92 seconds
```

(cherry picked from commit 7af2fa2e92)
2021-12-01 12:19:30 -08:00
Tim Abbott
98610c984c i18n: Add Sinhala translation. 2021-11-30 15:09:31 -08:00
Tim Abbott
ab965e5892 i18n: Update translation dat from Transifex. 2021-11-30 15:08:05 -08:00
PIG208
7a03827047 integrations: Add V3 support for PagerDuty. 2021-11-30 14:43:03 -08:00
PIG208
5954e622bc doc: Change supported extension type to reflect the change. 2021-11-30 14:42:57 -08:00
PIG208
687db48ea8 integrations: Change format of templates for PagerDuty V3.
Because the payload of V3 will no longer include the description,
We replace the ":" by "." in the message and create the new string
template for trigger messages.
2021-11-30 14:42:31 -08:00
Alex Vandiver
399391c3aa puppet: Default go-camo to listening on localhost for standalone deploys.
The default in the previous commit, inherited from camo, was to bind
to 0.0.0.0:9292.  In standalone deployments, camo is deployed on the
same host as the nginx reverse proxy, and as such there is no need to
open it up to other IPs.

Make `zulip::camo` take an optional parameter, which allows overriding
it in puppet, but skips a `zulip.conf` setting for it, since it is
unlikely to be adjust by most users.

(cherry picked from commit c514feaa22)
2021-11-19 17:51:08 -08:00
Alex Vandiver
cd5eec5eea camo: Replace with go-camo implementation.
The upstream of the `camo` repository[1] has been unmaintained for
several years, and is now archived by the owner.  Additionally, it has
a number of limitations:
 - It is installed as a sysinit service, which does not run under
   Docker
 - It does not prevent access to internal IPs, like 127.0.0.1
 - It does not respect standard `HTTP_proxy` environment variables,
   making it unable to use Smokescreen to prevent the prior flaw
 - It occasionally just crashes, and thus must have a cron job to
   restart it.

Swap camo out for the drop-in replacement go-camo[2], which has the
same external API, requiring not changes to Django code, but is more
maintained.  Additionally, it resolves all of the above complaints.

go-camo is not configured to use Smokescreen as a proxy, because its
own private-IP filtering prevents using a proxy which lies within that
IP space.  It is also unclear if the addition of Smokescreen would
provide any additional protection over the existing IP address
restrictions in go-camo.

go-camo has a subset of the security headers that our nginx reverse
proxy sets, and which camo set; provide the missing headers with `-H`
to ensure that go-camo, if exposed from behind some other non-nginx
load-balancer, still provides the necessary security headers.

Fixes #18351 by moving to supervisor.
Fixes zulip/docker-zulip#298 also by moving to supervisor.

[1] https://github.com/atmos/camo
[2] https://github.com/cactus/go-camo

(cherry picked from commit b982222e03)
2021-11-19 17:50:47 -08:00
Alex Vandiver
e7d48c0c10 puppet: Default to installing smokescreen on application frontends.
This is an additional security hardening step, to make Zulip default
to preventing SSRF attacks.  The overhead of running Smokescreen is
minimal, and there is no reason to force deployments to take
additional steps in order to secure themselves against SSRF attacks.

Deployments which already have a different external proxy configured
will not gain a local Smokescreen installation, and running without
Smokescreen is supported by explicitly unsetting the `host` or `port`
values in `/etc/zulip/zulip.conf`.

(cherry picked from commit c33562f0a8)
2021-11-19 17:49:37 -08:00
Alex Vandiver
023dfc01ba puppet: Split smokescreen into a non-profile version.
In a subsequent commit, we intend to include it from
`zulip::app_frontend_base`, which is a layering violation if it only
exists in the form of a profile.

(cherry picked from commit 44f1ea6bae)
2021-11-19 17:49:22 -08:00
Alex Vandiver
5d9285fff3 puppet: Remove unused smokescreen symlink.
(cherry picked from commit c2ed3c22b5)
2021-11-19 17:48:38 -08:00
Alex Vandiver
53f353ec26 puppet: Tidy old smokescreen binaries.
(cherry picked from commit 47e16a5d41)
2021-11-19 17:48:38 -08:00
Alex Vandiver
245c87c567 puppet: Embed golang version into binary path, to rebuild on new golang.
This will cause the output binary path to be sensitive to golang
version, causing it to be rebuilt on new golang, and an updated
supervisor config file written out, and thus supervisor also
restarted.

(cherry picked from commit 239ac8413e)
2021-11-19 17:48:38 -08:00
Alex Vandiver
26aa4d57e3 puppet: Factor out smokescreen binary path.
(cherry picked from commit 216eeba2dd)
2021-11-19 17:48:37 -08:00
Alex Vandiver
bee225782a puppet: Switch smokescreen to using zulip::external_dep, so it tidies.
(cherry picked from commit 3a7cef6582)
2021-11-19 17:48:37 -08:00
Alex Vandiver
4a6e69357a puppet: Move /srv/smokescreen-src to /srv/zulip-smokescreen-src.
As with the previous commit for `/srv/golang`, we have the custom of
namespacing things under `/srv` with `zulip-` to help ensure that we
play nice with anything else that happens to be on the host.

(cherry picked from commit ea08111d60)
2021-11-19 17:48:37 -08:00
Anders Kaseorg
3e6d3810d4 puppet: Upgrade Smokescreen v0.0.2-59-gbfca45c to v0.0.2-63-gdc40301.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
(cherry picked from commit c64e1adb19)
2021-11-19 17:48:37 -08:00
Alex Vandiver
bc21dde235 puppet: Extract an external-tarball-dependency manifest.
(cherry picked from commit bb9d2df1ae)
2021-11-19 17:48:37 -08:00
Alex Vandiver
182ce488e2 puppet: Tidy old golang directories.
This relies on behavior which is only in Puppet 5.5.1 and above, which
means it must be skipped on Ubuntu 18.04.

(cherry picked from commit 3c8d7e2598)
2021-11-19 17:48:37 -08:00
Alex Vandiver
bd557a9a13 puppet: Move /srv/golang to /srv/zulip-golang.
We have the custom of namespacing things under `/srv` with `zulip-`
to help ensure that we play nice with anything else that happens
to be on the host.

(cherry picked from commit 2fc4acdf81)
2021-11-19 17:48:36 -08:00
Alex Vandiver
7e8ead7325 puppet: Switch dependency to the golang binary we need.
(cherry picked from commit 00a4abb642)
2021-11-19 17:48:36 -08:00
Alex Vandiver
8fa783f13d puppet: Stop making a /srv/golang symlink.
Nothing needs this extra directory.

(cherry picked from commit 2d5f813094)
2021-11-19 17:48:36 -08:00
Alex Vandiver
11924f4b66 puppet: Factor out golang variables.
(cherry picked from commit 93af6c7f06)
2021-11-19 17:48:36 -08:00
Alex Vandiver
f01cbba0ce puppet: Shorten golang version variable name.
(cherry picked from commit 21be36f15f)
2021-11-19 17:48:36 -08:00
Alex Vandiver
31050be173 puppet: Upgrade golang from 1.16.4 to 1.17.3.
(cherry picked from commit 6b9e74adee)
2021-11-19 17:48:35 -08:00
Alex Vandiver
56d857ca89 puppet: Split out golang toolchain into its own manifest.
(cherry picked from commit 514801c509)
2021-11-19 17:48:35 -08:00
Alex Vandiver
d587252ddb tornado: Move SIGTERM shutdown handler into a callback.
A SIGTERM can show up at any point in the ioloop, even in places which
are not prepared to handle it.  This results in the process ignoring
the `sys.exit` which the SIGTERM handler calls, with an uncaught
SystemExit exception:

```
2021-11-09 15:37:49.368 ERR  [tornado.application:9803] Uncaught exception
Traceback (most recent call last):
  File "/home/zulip/deployments/2021-11-08-05-10-23/zulip-py3-venv/lib/python3.6/site-packages/tornado/http1connection.py", line 238, in _read_message
    delegate.finish()
  File "/home/zulip/deployments/2021-11-08-05-10-23/zulip-py3-venv/lib/python3.6/site-packages/tornado/httpserver.py", line 314, in finish
    self.delegate.finish()
  File "/home/zulip/deployments/2021-11-08-05-10-23/zulip-py3-venv/lib/python3.6/site-packages/tornado/routing.py", line 251, in finish
    self.delegate.finish()
  File "/home/zulip/deployments/2021-11-08-05-10-23/zulip-py3-venv/lib/python3.6/site-packages/tornado/web.py", line 2097, in finish
    self.execute()
  File "/home/zulip/deployments/2021-11-08-05-10-23/zulip-py3-venv/lib/python3.6/site-packages/tornado/web.py", line 2130, in execute
    **self.path_kwargs)
  File "/home/zulip/deployments/2021-11-08-05-10-23/zulip-py3-venv/lib/python3.6/site-packages/tornado/gen.py", line 307, in wrapper
    yielded = next(result)
  File "/home/zulip/deployments/2021-11-08-05-10-23/zulip-py3-venv/lib/python3.6/site-packages/tornado/web.py", line 1510, in _execute
    result = method(*self.path_args, **self.path_kwargs)
  File "/home/zulip/deployments/2021-11-08-05-10-23/zerver/tornado/handlers.py", line 150, in get
    request = self.convert_tornado_request_to_django_request()
  File "/home/zulip/deployments/2021-11-08-05-10-23/zerver/tornado/handlers.py", line 113, in convert_tornado_request_to_django_request
    request = WSGIRequest(environ)
  File "/home/zulip/deployments/2021-11-08-05-10-23/zulip-py3-venv/lib/python3.6/site-packages/django/core/handlers/wsgi.py", line 66, in __init__
    script_name = get_script_name(environ)
  File "/home/zulip/deployments/2021-11-08-05-10-23/zerver/tornado/event_queue.py", line 611, in <lambda>
    signal.signal(signal.SIGTERM, lambda signum, stack: sys.exit(1))
SystemExit: 1
```

Supervisor then terminates the process with a SIGKILL, which results
in dropping data held in the tornado process, as it does not dump its
queue.

The only command which is safe to run in the signal handler is
`ioloop.add_callback_from_signal`, which schedules the callback to run
during the course of the normal ioloop.  This callbacks does an
orderly shutdown of the server and the ioloop before exiting.

(cherry picked from commit bc5539d871)
2021-11-12 09:59:58 -08:00
Alex Vandiver
eadefdf2f5 soft_deactivate: Handle multiple SUBSCRIPTION_DEACTIVATEDs.
Race conditions in stream unsubscription may lead to multiple
back-to-back SUBSCRIPTION_DEACTIVATED RealmAuditLog entries for the
same stream.  The current logic constructs duplicate UserMessage
entries for such, which then later fail to insert.

Keep a set of message-ids that have been prep'd to be inserted, so
that we don't duplicate them if there is a duplicated
SUBSCRIPTION_DEACTIVATED row.  This also renames the `message` local
variable, which otherwise overrode the `message` argument of a
different type.

(cherry picked from commit 6b6dcf6ce1)
2021-11-10 12:30:24 -08:00
Anders Kaseorg
c05bbd0fd4 requirements: Upgrade Python requirements.
Sync versions from commit 069d6ced69 on
main, excluding django-auth-ldap, Jinja2, mypy, premailer, PyJWT,
semgrep, Sphinx, SQLAlchemy, zulip, and zulip-bots.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-11-03 20:47:32 -07:00
Tim Abbott
deedda2c18 push_notifications: Truncate overly large remove events.
Fixes #19224.
2021-11-03 11:41:57 -07:00
Tim Abbott
9bec6bb5eb docs: Extend Certbot troubleshooting documentation.
This should help folks who have problems with Certbot renewal; we had
a couple reported this week which I think were both caused by firewall
issues.
2021-11-02 21:35:50 -07:00
Alex Vandiver
634b6ea97b markdown: CSS-escape preview links.
This adds `soupsieve` as an explicit dependency, but intentionally
does not adjust the provision version, as it was already an indirect
dependency.

(cherry picked from commit 6a40c17ccf)
2021-10-27 05:23:34 +00:00
Alex Vandiver
10583bdb32 markdown: Run URL preview links through camo.
Not proxying these requests through camo is a security concern.
Furthermore, on the desktop client, any embed image which is hosted on
a server with an expired or otherwise invalid certificate will trigger
a blocking modal window with no clear source and a confusing error
message; see zulip/zulip-desktop#1119.

Rewrite all `message_embed_image` URLs through camo, if it is enabled.

(cherry picked from commit 52f74bbd9b)
2021-10-27 04:36:47 +00:00
Mateusz Mandera
ebb6a92f71 saml: Don't raise AssertionError if no name is provided in SAMLResponse.
This is an acceptable edge case for SAML and shouldn't raise any errors.
2021-10-26 16:48:23 -07:00
Alex Vandiver
80b7df1b0d scheduled_email: Consistently lock users table.
Only clear_scheduled_emails previously took a lock on the users before
removing them; make deliver_scheduled_emails do so as well, by using
prefetch_related to ensure that the table appears in the SELECT.  This
is not necessary for correctness, since all accesses of
ScheduledEmailUser first access the ScheduledEmail and lock it; it is
merely for consistency.

Since SELECT ... FOR UPDATE takes an UPDATE lock on all tables
mentioned in the SELECT, merely doing the prefetch is sufficient to
lock both tables; no `on=(...)` is needed to `select_for_update`.

This also does not address the pre-existing potential deadlock from
these two use cases, where both try to lock the same ScheduledEmail
rows in opposite orders.

(cherry picked from commit 4c518c2bba)
2021-10-18 17:06:11 -07:00
Alex Vandiver
7b6cee1164 send_email: Change clear_scheduled_emails to only take one user.
No codepath except tests passes in more than one user_profile -- and
doing so is what makes the deduplication necessary.

Simplify the API by making it only take one user_profile id.

(cherry picked from commit ebaafb32f3)
2021-10-18 17:06:11 -07:00
Alex Vandiver
99cc5598ac send_email: Fix sleep logic.
This was broken in the refactor in 1e67e0f218.

(cherry picked from commit 4ffda1be87)
2021-10-18 17:06:11 -07:00
Alex Vandiver
d23778869f deliver_scheduled_*: SELECT FOR UPDATE the relevant rows.
`deliver_scheduled_emails` and `deliver_scheduled_messages` use their
respective tables like a queue, but do not have guarantees that there
was only one consumer (besides the EMAIL_DELIVERER_DISABLED setting),
and could send duplicate messages if multiple consumers raced in
reading rows.

Use database locking to ensure that the database only feeds a given
ScheduledMessage or ScheduledEmail row to a single consumer.  A second
consumer, if it exists, will block until the first consumer commits
the transaction.

(cherry picked from commit 1e67e0f218)
2021-10-18 17:06:11 -07:00
Adam Benesh
6ba333c2ff puppet: Add WSGIApplicationGroup config to Apache SSO example.
Zulip apparently is now affected by a bad interaction between Apache's
WSGI using Python subinterpreters and C extension modules like `re2`
that are not designed for it.

The solution is apparently to set WSGIApplicationGroup to %{GLOBAL},
which disables Apache's use of Python subinterpreters.

See https://serverfault.com/questions/514242/non-responsive-apache-mod-wsgi-after-installing-scipy/514251#514251 for background.

Fixes #19924.
2021-10-08 15:08:14 -07:00
rht
3cf07d1671 Slack import: Use Python ZipFile to unzip.
This should handle the case when non-ASCII Unicode folder names are
created on Windows.

Fixes #19899.
2021-10-07 09:47:20 -07:00
rht
1b4832a703 slack_import: Remove obsolete SlackImportAttachment placeholder.
This was introduced in f4ad464d82, and
incompletely removed in e037c2f93e649c28a71c02559b5ae7a3333f42a8; here
we finish removing it.
2021-10-07 09:47:20 -07:00
Alex Vandiver
af5958e407 data_import: Protect better against bad Slack tokens.
An invalid token would be treated the same as a token with no scopes;
differentiate these better.
2021-10-07 09:47:20 -07:00
Alex Vandiver
a659944fe3 data_import: Support importing from Slack conversions in a directory.
Sometimes the Slack import zip file we get isn't quite the canonical
form that Slack produces -- often because the user has unzip'd it,
looked at it, and re-zip'd it, resulting in extra nested directories
and the like.

For such cases, support passing in a path to an unpacked Slack export
tree.
2021-10-07 09:47:20 -07:00
Alex Vandiver
19db2fa773 import_data: Do some quick verification of Slack import formats. 2021-10-07 09:47:20 -07:00
Priyansh Garg
b303477e86 data_import: Make slack bot emails unique.
Slack bot emails generated by us can be duplicate for two bots.
If such a case occur, append a counter to the email to make it
unique.

For maintaining the counter of duplicate emails and the final
email assigned to each bot, a class based approach is used with
static variables and static (class) methods. This keeps all the
data related to slack bot emails at the same place and easily
accessible from anywhere inside the module (without defining any
class object and passing it around).

Fixes: #16793
2021-10-07 09:47:20 -07:00
Alex Vandiver
5c01e23776 version: Update version after 4.7 release. 2021-10-04 14:24:43 -07:00
137 changed files with 11192 additions and 3080 deletions

View File

@@ -68,22 +68,22 @@ jobs:
uses: actions/cache@v2
with:
path: /srv/zulip-npm-cache
key: v1-yarn-deps-${{ github.job }}-${{ hashFiles('package.json') }}-${{ hashFiles('yarn.lock') }}
restore-keys: v1-yarn-deps-${{ github.job }}
key: v1-yarn-deps-bionic-${{ hashFiles('package.json') }}-${{ hashFiles('yarn.lock') }}
restore-keys: v1-yarn-deps-bionic
- name: Restore python cache
uses: actions/cache@v2
with:
path: /srv/zulip-venv-cache
key: v1-venv-${{ github.job }}-${{ hashFiles('requirements/dev.txt') }}
restore-keys: v1-venv-${{ github.job }}
key: v1-venv-bionic-${{ hashFiles('requirements/dev.txt') }}
restore-keys: v1-venv-bionic
- name: Restore emoji cache
uses: actions/cache@v2
with:
path: /srv/zulip-emoji-cache
key: v1-emoji-${{ github.job }}-${{ hashFiles('tools/setup/emoji/emoji_map.json') }}-${{ hashFiles('tools/setup/emoji/build_emoji') }}-${{ hashFiles('tools/setup/emoji/emoji_setup_utils.py') }}-${{ hashFiles('tools/setup/emoji/emoji_names.py') }}-${{ hashFiles('package.json') }}
restore-keys: v1-emoji-${{ github.job }}
key: v1-emoji-bionic-${{ hashFiles('tools/setup/emoji/emoji_map.json') }}-${{ hashFiles('tools/setup/emoji/build_emoji') }}-${{ hashFiles('tools/setup/emoji/emoji_setup_utils.py') }}-${{ hashFiles('tools/setup/emoji/emoji_names.py') }}-${{ hashFiles('package.json') }}
restore-keys: v1-emoji-bionic
- name: Do Bionic hack
run: |

View File

@@ -34,10 +34,10 @@ def render_confirmation_key_error(
request: HttpRequest, exception: ConfirmationKeyException
) -> HttpResponse:
if exception.error_type == ConfirmationKeyException.WRONG_LENGTH:
return render(request, "confirmation/link_malformed.html")
return render(request, "confirmation/link_malformed.html", status=404)
if exception.error_type == ConfirmationKeyException.EXPIRED:
return render(request, "confirmation/link_expired.html")
return render(request, "confirmation/link_does_not_exist.html")
return render(request, "confirmation/link_expired.html", status=404)
return render(request, "confirmation/link_does_not_exist.html", status=404)
def generate_key() -> str:
@@ -143,9 +143,9 @@ class ConfirmationType:
_properties = {
Confirmation.USER_REGISTRATION: ConfirmationType("check_prereg_key_and_redirect"),
Confirmation.USER_REGISTRATION: ConfirmationType("get_prereg_key_and_redirect"),
Confirmation.INVITATION: ConfirmationType(
"check_prereg_key_and_redirect", validity_in_days=settings.INVITATION_LINK_VALIDITY_DAYS
"get_prereg_key_and_redirect", validity_in_days=settings.INVITATION_LINK_VALIDITY_DAYS
),
Confirmation.EMAIL_CHANGE: ConfirmationType("confirm_email_change"),
Confirmation.UNSUBSCRIBE: ConfirmationType(
@@ -155,7 +155,7 @@ _properties = {
Confirmation.MULTIUSE_INVITE: ConfirmationType(
"join", validity_in_days=settings.INVITATION_LINK_VALIDITY_DAYS
),
Confirmation.REALM_CREATION: ConfirmationType("check_prereg_key_and_redirect"),
Confirmation.REALM_CREATION: ConfirmationType("get_prereg_key_and_redirect"),
Confirmation.REALM_REACTIVATION: ConfirmationType("realm_reactivation"),
}

View File

@@ -300,7 +300,7 @@ different.
mkdir -p /var/lib/zulip/certbot-webroot/
# if nginx running this will fail and you need to run `service nginx stop`
/home/zulipdev/zulip/scripts/setup/setup-certbot \
hostname.example.com --no-zulip-conf \
hostname.example.com \
--email=username@example.com --method=standalone
```

View File

@@ -7,6 +7,109 @@ up-to-date list of raw changes.
## Zulip 4.x series
## Zulip 4.10 -- 2022-02-25
- CVE-2022-21706: Reusable invitation links could be improperly used
for other organizations.
- CVE-2021-3967: Enforce that regenerating an API key must be done
with an API key, not a cookie. Thanks to nhiephon
(twitter.com/_nhiephon) for their responsible disclosure of this
vulnerability.
- Fixed a bug with the `reindex-textual-data` tool, where it would
sometimes fail to find the libraries it needed.
- Pin PostgreSQL to 10.19, 11.14, 12.9, 13.5 or 14.1 to avoid a
regression which caused deploys with PGroonga enabled to
unpredictably fail database queries with the error `variable not
found in subplan target list`.
- Fix ARM64 support; however, the wal-g binary is not yet supported on
ARM64 (zulip/zulip#21070).
## Zulip 4.9 -- 2022-01-24
- CVE-2021-43799: Remote execution of code involving RabbitMQ.
- Closed access to RabbitMQ port 25672; initial installs tried to
close this port, but failed to restart RabbitMQ for the
configuration.
- Removed the `rabbitmq.nodename` configuration in `zulip.conf`; all
RabbitMQ instances will be reconfigured to have a nodename of
`zulip@localhost`. You can remove this setting from your
`zulip.conf` configuration file, if it exists.
- Added missing support for the Camo image proxy in the Docker
image. This resolves a longstanding issue with image previews, if
enabled, appearing as broken images for Docker-based installs.
- Fixed a bug which allowed a user to edit a message to add a wildcard
mention when they did not have permissions to send such messages
originally.
- Fixed a bug in the tool that corrects database corruption caused by
updating the operating system hosting PostgreSQL, which previously
omitted some indexes from its verification. If you updated the
operating system of your Zulip instance from Ubuntu 18.04 to 20.04,
or from Debian Stretch to Debian Buster, you should run the tool,
even if you did so previously; full details and instructions are
available in the previous blog post.
- Began routing requests from the Camo image proxy through a
non-Smokescreen proxy, if one is configured; because Camo includes
logic to deny access to private subnets, routing its requests
through Smokescreen is generally not necessary.
- Fixed a bug where changing the Camo secret required running
`zulip-puppet-apply`.
- Fixed `scripts/setup/compare-settings-to-template` to be able to run
from any directory.
- Switched Let's Encrypt renewal to use its own timer, rather than our
custom cron job. This fixes a bug where occasionally `nginx` would
not reload after getting an updated certificate.
- Updated documentation and tooling to note that installs using
`upgrade-zulip-from-git` require 3 GB of RAM, or 2 GB and at least 1
GB of swap.
## Zulip 4.8 -- 2021-12-01
- CVE-2021-43791: Zulip could fail to enforce expiration dates
on confirmation keys, allowing users to potentially use expired
invitations, self-registrations, or realm creation links.
- Began installing Smokescreen to harden Zulip against SSRF attacks by
default. Zulip has offered Smokescreen as an option since Zulip
4.0. Existing installs which configured an outgoing proxy which is
not on `localhost:4750` will continue to use that; all other
installations will begin having a Smokescreen installation listening
on 127.0.0.1, which Zulip will proxy traffic through. The version of
Smokescreen was also upgraded.
- Replaced the camo image proxy with go-camo, a maintained
reimplementation that also protects against SSRF attacks. This
server now listens only on 127.0.0.1 when it is deployed as part of
a standalone deployment.
- Began using camo for images displayed in URL previews. This improves
privacy and also resolves an issue where an image link to a third
party server with an expired or otherwise invalid SSL certificate
would trigger a confusing pop-up window for Zulip Desktop users.
- Fixed a bug which could cause Tornado to shut down improperly
(causing an immediate full-page reload for their clients) when
restarting a heavily loaded Zulip server.
- Updated Python dependencies.
- Truncated large “remove” mobile notification events so that marking
hundreds of private messages or other notifiable messages as read at
once wont exceed Apples 4 KB notification size limit.
- Slack importer improvements:
- Ensured that generated fake email addresses for Slack bots are
unique.
- Added support for importing Slack exports from a directory, not
just a .zip file.
- Provided better error messages with invalid Slack tokens.
- Added support for non-ASCII Unicode folder names on Windows.
- Add support for V3 Pagerduty webhook.
- Updated documentation for Apache SSO, which now requires additional
configuration now that Zulip uses a C extension (the `re2` module).
- Fixed a bug where an empty name in a SAML response would raise an
error.
- Ensured that `deliver_scheduled_emails` and
`deliver_scheduled_messages` did not double-deliver if run on
multiple servers at once.
- Extended Certbot troubleshooting documentation.
- Fixed a bug in soft deactivation catch-up code, in cases where a
race condition had created multiple subscription deactivation
entries for a single user and single stream in the audit log.
- Updated translations, including adding a Sinhala translation.
### 4.7 -- 2021-10-04
- CVE-2021-41115: Prevent organization administrators from affecting
@@ -197,7 +300,7 @@ up-to-date list of raw changes.
major release.
[docker-zulip-manual]: https://github.com/zulip/docker-zulip#manual-configuration
[smokescreen]: ../production/deployment.html#using-an-outgoing-http-proxy
[smokescreen]: ../production/deployment.html#customizing-the-outgoing-http-proxy
[update-settings-docs]: ../production/upgrade-or-modify.html#updating-settings-py-inline-documentation
#### Full feature changelog

View File

@@ -206,28 +206,19 @@ behind reverse proxies.
[using-http]: ../production/deployment.html#configuring-zulip-to-allow-http
## Using an outgoing HTTP proxy
## Customizing the outgoing HTTP proxy
Zulip supports routing all of its outgoing HTTP and HTTPS traffic
through an HTTP `CONNECT` proxy, such as [Smokescreen][smokescreen];
this includes outgoing webhooks, image and website previews, and
mobile push notifications. You may wish to enable this feature to
provide a consistent egress point, or enforce access control on URLs
to prevent [SSRF][ssrf] against internal resources.
To protect against [SSRF][ssrf], Zulip 4.8 and above default to
routing all outgoing HTTP and HTTPS traffic through
[Smokescreen][smokescreen], an HTTP `CONNECT` proxy; this includes
outgoing webhooks, website previews, and mobile push notifications.
By default, the Camo image proxy will be automatically configured to
use a custom outgoing proxy, but does not use Smokescreen by default
because Camo includes similar logic to deny access to private
subnets. You can [override][proxy.enable_for_camo] this default
configuration if desired.
To use Smokescreen:
1. Add `, zulip::profile::smokescreen` to the list of `puppet_classes`
in `/etc/zulip/zulip.conf`. A typical value after this change is:
```ini
puppet_classes = zulip::profile::standalone, zulip::profile::smokescreen
```
1. Optionally, configure the [smokescreen ACLs][smokescreen-acls]. By
default, Smokescreen denies access to all [non-public IP
addresses](https://en.wikipedia.org/wiki/Private_network), including
127.0.0.1.
To use a custom outgoing proxy:
1. Add the following block to `/etc/zulip/zulip.conf`, substituting in
your proxy's hostname/IP and port:
@@ -238,20 +229,30 @@ To use Smokescreen:
port = 4750
```
1. If you intend to also make the Smokescreen install available to
other hosts, set `listen_address` in the same block. Note that you
must control access to the Smokescreen port if you do this, as
failing to do so opens a public HTTP proxy!
1. As root, run
`/home/zulip/deployments/current/scripts/zulip-puppet-apply`. This
will compile and install Smokescreen, reconfigure services to use
it, and restart Zulip.
will reconfigure and restart Zulip.
If you would like to use an already-installed HTTP proxy, omit the
first step, and adjust the IP address and port in the second step
accordingly.
If you have a deployment with multiple frontend servers, or wish to
install Smokescreen on a separate host, you can apply the
`zulip::profile::smokescreen` Puppet class on that host, and follow
the above steps, setting the `[http_proxy]` block to point to that
host.
If you wish to disable the outgoing proxy entirely, follow the above
steps, configuring an empty `host` value.
Optionally, you can also configure the [Smokescreen ACL
list][smokescreen-acls]. By default, Smokescreen denies access to all
[non-public IP
addresses](https://en.wikipedia.org/wiki/Private_network), including
127.0.0.1, but allows traffic to all public Internet hosts.
In Zulip 4.7 and older, to enable SSRF protection via Smokescreen, you
will need to explicitly add the `zulip::profile::smokescreen` Puppet
class, and configure the `[http_proxy]` block as above.
[proxy.enable_for_camo]: #enable-for-camo
[smokescreen]: https://github.com/stripe/smokescreen
[smokescreen-acls]: https://github.com/stripe/smokescreen#acls
[ssrf]: https://owasp.org/www-community/attacks/Server_Side_Request_Forgery
@@ -294,6 +295,33 @@ HTTP as follows:
1. Finally, restart the Zulip server, using
`/home/zulip/deployments/current/scripts/restart-server`.
#### Configuring Zulip to trust proxies
Before placing Zulip behind a reverse proxy, it needs to be configured to trust
the client IP addresses that the proxy reports. This is important to have
accurate IP addresses in server logs, as well as in notification emails which
are sent to end users.
1. Determine the IP addresses of all reverse proxies you are setting up, as seen
from the Zulip host. Depending on your network setup, these may not be the
same as the public IP addresses of the reverse proxies.
1. Add the following block to `/etc/zulip/zulip.conf`.
```ini
[loadbalancer]
# Use the IP addresses you determined above, separated by commas.
ips = 192.168.0.100
```
1. Reconfigure Zulip with these settings. As root, run
`/home/zulip/deployments/current/scripts/zulip-puppet-apply`. This will
adjust Zulip's `nginx` configuration file to accept the `X-Forwarded-For`
header when it is sent from one of the reverse proxy IPs.
1. Finally, restart the Zulip server, using
`/home/zulip/deployments/current/scripts/restart-server`.
### nginx configuration
For `nginx` configuration, there's two things you need to set up:
@@ -330,6 +358,10 @@ Don't forget to update `server_name`, `ssl_certificate`,
`ssl_certificate_key` and `proxy_pass` with the appropriate values for
your installation.
On the Zulip side, you will need to add the `nginx` server IP as a trusted
reverse proxy. Follow the instructions to [configure Zulip to trust
proxies](#configuring-zulip-to-trust-proxies).
[nginx-proxy-longpolling-config]: https://github.com/zulip/zulip/blob/main/puppet/zulip/files/nginx/zulip-include-common/proxy_longpolling
[standalone.pp]: https://github.com/zulip/zulip/blob/main/puppet/zulip/manifests/profile/standalone.pp
[zulipchat-puppet]: https://github.com/zulip/zulip/tree/main/puppet/zulip_ops/manifests
@@ -352,7 +384,11 @@ make the following changes in two configuration files.
3. Restart your Zulip server with `/home/zulip/deployments/current/scripts/restart-server`.
4. Create an Apache2 virtual host configuration file, similar to the
4. Follow the instructions to [configure Zulip to trust
proxies](#configuring-zulip-to-trust-proxies). For this example, the reverse
proxy IP would be `127.0.0.1`.
5. Create an Apache2 virtual host configuration file, similar to the
following. Place it the appropriate path for your Apache2
installation and enable it (E.g. if you use Debian or Ubuntu, then
place it in `/etc/apache2/sites-available/zulip.example.com.conf`
@@ -410,7 +446,9 @@ backend zulip
Since this configuration uses the `http` mode, you will also need to
[configure Zulip to allow HTTP](#configuring-zulip-to-allow-http) as
described above.
described above. Additionally, you will need to [add the the HAProxy server IP
address as a trusted load balancer](#configuring-zulip-to-trust-proxies)
to have Zulip respect the addresses in `X-Forwarded-For` headers.
### Other proxies
@@ -419,7 +457,9 @@ things you need to be careful about when configuring it:
1. Configure your reverse proxy (or proxies) to correctly maintain the
`X-Forwarded-For` HTTP header, which is supposed to contain the series
of IP addresses the request was forwarded through. You can verify
of IP addresses the request was forwarded through. Additionally,
[configure Zulip to respect the addresses sent by your reverse
proxies](#configuring-zulip-to-trust-proxies). You can verify
your work by looking at `/var/log/zulip/server.log` and checking it
has the actual IP addresses of clients, not the IP address of the
proxy server.
@@ -551,14 +591,6 @@ Override the default uwsgi backlog of 128 connections.
Override the default `uwsgi` (Django) process count of 6 on hosts with
more than 3.5GiB of RAM, 4 on hosts with less.
### `[certbot]`
#### `auto_renew`
If set to the string `yes`, [Certbot will attempt to automatically
renew its certificate](../production/ssl-certificates.html#certbot-recommended). Do
no set by hand; use `scripts/setup/setup-certbot` to configure this.
### `[postfix]`
#### `mailname`
@@ -609,12 +641,6 @@ connections.
The version of PostgreSQL that is in use. Do not set by hand; use the
[PostgreSQL upgrade tool](../production/upgrade-or-modify.html#upgrading-postgresql).
### `[rabbitmq]`
#### `nodename`
The name used to identify the local RabbitMQ server; do not modify.
### `[memcached]`
#### `memory`
@@ -634,13 +660,22 @@ load balancers whose `X-Forwarded-For` should be respected.
#### `host`
The hostname or IP address of an [outgoing HTTP `CONNECT`
proxy](#using-an-outgoing-http-proxy).
proxy](#customizing-the-outgoing-http-proxy). Defaults to `localhost`
if unspecified.
#### `port`
The TCP port of the HTTP `CONNECT` proxy on the host specified above.
Defaults to `4750` if unspecified.
#### `listen_address`
The IP address that Smokescreen should bind to and listen on.
Defaults to `127.0.0.1`.
#### `enable_for_camo`
Because Camo includes logic to deny access to private subnets, routing
its requests through Smokescreen is generally not necessary. Set to
'true' or 'false' to override the default, which uses the proxy only if
it is not the default of Smokescreen on a local host.

View File

@@ -63,9 +63,11 @@ If the script gives an error, consult [Troubleshooting](#troubleshooting) below.
suitable for production use, but may be convenient for testing.
- `--certbot`: With this option, the Zulip installer automatically
obtains an SSL certificate for the server [using Certbot][doc-certbot].
If you'd prefer to acquire an SSL certificate yourself in any other
way, it's easy to [provide it to Zulip][doc-ssl-manual].
obtains an SSL certificate for the server [using
Certbot][doc-certbot], and configures a cron job to renew the
certificate automatically. If you'd prefer to acquire an SSL
certificate yourself in any other way, it's easy to [provide it to
Zulip][doc-ssl-manual].
You can see the more advanced installer options in our [deployment options][doc-deployment-options]
documentation.

View File

@@ -18,7 +18,7 @@ support forwarding push notifications to a central push notification
forwarding service. Accessing this service requires outgoing HTTPS
access to the public Internet; if that is restricted by a proxy, you
will need to [configure Zulip to use your outgoing HTTP
proxy](../production/deployment.html#using-an-outgoing-http-proxy)
proxy](../production/deployment.html#customizing-the-outgoing-http-proxy)
first.
You can enable this for your Zulip server as follows:
@@ -106,9 +106,11 @@ forwarding service).
## Security and privacy
Use of the push notification bouncer is subject to the
[Zulipchat Terms of Service](https://zulip.com/terms/). By using
push notifications, you agree to those terms.
Use of the push notification bouncer is subject to the Zulip Cloud [Terms of
Service](https://zulip.com/policies/terms), [Privacy
Policy](https://zulip.com/policies/privacy) and [Rules of
Use](https://zulip.com/policies/rules). By using push notifications, you agree
to these terms.
We've designed this push notification bouncer service with security
and privacy in mind:
@@ -170,10 +172,11 @@ Zulip open source project understand how many people are using Zulip,
and help us allocate resources towards supporting self-hosted
installations.
Our use of these statistics is governed by the same Terms of Service
and Privacy Policy that covers the Mobile Push Notifications Service
itself. If your organization does not want to submit these statistics,
you can disable this feature at any time by setting
Our use of these statistics is governed by the same [Terms of
Service](https://zulip.com/policies/terms) and [Privacy
Policy](https://zulip.com/policies/privacy) that covers the Mobile Push
Notifications Service itself. If your organization does not want to submit these
statistics, you can disable this feature at any time by setting
`SUBMIT_USAGE_STATISTICS=False` in `/etc/zulip/settings.py`.
## Sending push notifications directly from your server

View File

@@ -10,11 +10,15 @@ To run a Zulip server, you will need:
- Debian 10 Buster
- At least 2GB RAM, and 10GB disk space
- If you expect 100+ users: 4GB RAM, and 2 CPUs
- If you intend to [upgrade from Git][upgrade-from-git]: 3GB RAM, or
2G and at least 1G of swap configured.
- A hostname in DNS
- Credentials for sending email
For details on each of these requirements, see below.
[upgrade-from-git]: ../production/upgrade-or-modify.html#upgrading-from-a-git-repository
## Server
#### General
@@ -77,6 +81,11 @@ on hardware requirements for larger organizations.
HTTPS, and will redirect HTTP requests to HTTPS.
- Incoming port 25 if you plan to enable Zulip's [incoming email
integration](../production/email-gateway.md).
- Incoming port 4369 should be protected by a firewall to prevent
exposing `epmd`, an Erlang service which does not support binding
only to localhost. Leaving this exposed will allow unauthenticated
remote users to determine that the server is running RabbitMQ, and
on which port, though no further information is leaked.
- Outgoing HTTP(S) access (ports 80 and 443) to the public Internet so
that Zulip can properly manage image and website previews and mobile
push notifications. Outgoing Internet access is not required if you
@@ -92,13 +101,14 @@ on hardware requirements for larger organizations.
address as its external hostname (though we don't recommend that
configuration).
- Zulip supports [running behind a reverse proxy][reverse-proxy].
- Zulip servers running inside a private network should configure the
[Smokescreen integration][smokescreen-proxy] to protect against
[SSRF attacks][ssrf], where users could make the Zulip server make
requests to private resources.
- Zulip configures [Smokescreen, and outgoing HTTP
proxy][smokescreen-proxy], to protect against [SSRF attacks][ssrf],
which prevents user from making the Zulip server make requests to
private resources. If your network has its own outgoing HTTP proxy,
Zulip supports using that instead.
[ssrf]: https://owasp.org/www-community/attacks/Server_Side_Request_Forgery
[smokescreen-proxy]: ../production/deployment.html#using-an-outgoing-http-proxy
[smokescreen-proxy]: ../production/deployment.html#customizing-the-outgoing-http-proxy
[reverse-proxy]: ../production/deployment.html#putting-the-zulip-application-behind-a-reverse-proxy
[email-mirror-code]: https://github.com/zulip/zulip/blob/main/zerver/management/commands/email_mirror.py

View File

@@ -236,7 +236,7 @@ strength allowed is controlled by two settings in
browser is logged into a Zulip account that has received the
uploaded file in question).
- Zulip supports using the Camo image proxy to proxy content like
- Zulip supports using the [go-camo][go-camo] image proxy to proxy content like
inline image previews, that can be inserted into the Zulip message feed by
other users. This ensures that clients do not make requests to external
servers to fetch images, improving privacy.
@@ -256,15 +256,21 @@ strength allowed is controlled by two settings in
- Mobile push notifications (must be configured to be enabled)
- Notably, these first 3 features give end users (limited) control to cause
the Zulip server to make HTTP requests on their behalf. As a result,
Zulip supports routing all outgoing outgoing HTTP requests [through
the Zulip server to make HTTP requests on their behalf. Because of this,
Zulip routes all outgoing outgoing HTTP requests [through
Smokescreen][smokescreen-setup] to ensure that Zulip cannot be
used to execute [SSRF attacks][ssrf] against other systems on an
internal corporate network. The default Smokescreen configuration
denies access to all non-public IP addresses, including 127.0.0.1.
The Camo image server does not, by default, route its traffic
through Smokescreen, since Camo includes logic to deny access to
private subnets; this can be [overridden][proxy.enable_for_camo].
[go-camo]: https://github.com/cactus/go-camo
[ssrf]: https://owasp.org/www-community/attacks/Server_Side_Request_Forgery
[smokescreen-setup]: ../production/deployment.html#using-an-outgoing-http-proxy
[smokescreen-setup]: ../production/deployment.html#customizing-the-outgoing-http-proxy
[proxy.enable_for_camo]: ../production/deployment.html#enable-for-camo
## Final notes and security response

View File

@@ -107,11 +107,29 @@ the server controls the website at that hostname; and is then given a
certificate. (For details, refer to
[Let's Encrypt](https://letsencrypt.org/how-it-works/).)
Then it records a flag in `/etc/zulip/zulip.conf` saying Certbot is in
use and should be auto-renewed. A cron job checks that flag, then
checks if any certificates are due for renewal, and if they are (so
approximately once every 60 days), repeats the process of request,
prove, get a fresh certificate.
### Renewal
Let's Encrypt certificates expire after 90 days. Short expiration
periods are good for security, but they also mean that it's important
to automatically renew them to avoid regular maintenance work.
Zulip configures automatic renewal for you. As a result, a Zulip
server configured with Certbot does not require any ongoing work to
maintain a current valid SSL certificate.
The `certbot` package configures a systemd timer (similar to a cron
job) that will renew any Certbot certificates that are due for
renewal. The renewal process repeats the Certbot proof-of-control
process, receives the new certificate from Certbot, installs the new
certificate, and then reloads `nginx`.
#### Troubleshooting
If your Certbot certificate expires, it is usually because of firewall
rules preventing the Certbot renewal process (which is essentially
identical to the initial certificate request process) from
working. You can debug interactively by running the command from the
cron job, `/usr/bin/certbot renew`, as `root`.
## Self-signed certificate

View File

@@ -638,7 +638,7 @@
"Send digest emails when I'm away": "E-Mail mit Zusammenfassung senden, wenn ich abwesend bin",
"Send email notifications for new logins to my account": "E-Mail-Benachrichtigungen für neue Logins in meinen Account senden",
"Send emails introducing Zulip to new users": "E-Mails mit Einführung zu Zulip an neue Nutzer versenden ",
"Send me occasional marketing emails about Zulip (a few times a year)": "Senden Sie mir gelegentlich Marketing-E-Mails über Zulip (ein paar mal pro Jahr)",
"Send me occasional marketing emails about Zulip (a few times a year)": "Schicke mir gelegentlich Marketing-E-Mails über Zulip (ein paar mal pro Jahr)",
"Send mobile notifications even if I'm online (useful for testing)": "Mobile Benachrichtigungen senden, auch wenn ich online bin (nützlich zum Testen)",
"Send notification to new topic": "Benachrichtigung an neues Thema senden",
"Send notification to old topic": "Benachrichtigung an altes Thema senden",
@@ -750,8 +750,8 @@
"Unknown stream": "Unbekannter Stream",
"Unmute": "Stummschaltung aufheben",
"Unmute stream": "Stummschaltung des Streams aufheben",
"Unmute the topic <b>{topic}</b>": "Stummschalten des Themas<b>{topic}</b>deaktivieren",
"Unmute this user": "stummschalten für Nutzer aufheben",
"Unmute the topic <b>{topic}</b>": "Stummschaltung des Themas<b>{topic}</b>aufheben",
"Unmute this user": "Stummschaltung für Nutzer aufheben",
"Unmute topic": "Stummschaltung des Themas aufheben",
"Unpin stream from top": "Oben angepinnten Stream loslösen",
"Unread": "Ungelesen",
@@ -870,7 +870,7 @@
"You're not subscribed to this stream. You will not be notified if other users reply to your message.": "Du hast diesen Stream nicht abonniert. Du wirst nicht benachrichtigt, wenn andere Nutzer auf Deine Nachricht antworten.",
"Your API key:": "Dein API-Schlüssel:",
"Your reminder note is empty!": "Deine Erinnerungsnotiz ist leer!",
"Zulip's translations are contributed by our amazing community of volunteer translators. If you'd like to help, see the <z-link>Zulip translation guidelines</z-link>.": "Zulips Übersetzungen werden von unserer erstaunlichen Gemeinschaft freiwilliger Übersetzer beigesteuert. Wenn Sie helfen möchten, lesen Sie die <z-link>Zulip translation guidelines</z-link>.",
"Zulip's translations are contributed by our amazing community of volunteer translators. If you'd like to help, see the <z-link>Zulip translation guidelines</z-link>.": "Die Übersetzungen von Zulip werden von unserer wunderbaren Gemeinschaft freiwilliger Übersetzer beigesteuert. Wenn du helfen möchtest, schau dir bitte die <z-link>Richtlinien für die Übersetzung von Zulip</z-link> an.",
"[Condense message]": "[Nachricht minimieren]",
"[Configure]": "[Konfigurieren]",
"[Disable]": "[Deaktivieren]",

View File

@@ -5,17 +5,17 @@
"(no topic)": "(sin tema)",
"(unavailable)": "(no disponible)",
"(you)": "(tu)",
"({message_retention_days} days)": "",
"({message_retention_days} days)": "({message_retention_days} días)",
"/dark (Toggle night mode)": "",
"/day (Toggle day mode)": "",
"/fixed-width (Toggle fixed width mode)": "",
"/fluid-width (Toggle fluid width mode)": "",
"/fixed-width (Toggle fixed width mode)": "/fixed-width (Cambiar a modo de ancho fijo)",
"/fluid-width (Toggle fluid width mode)": "/fluid-width (Cambiar a modo de ancho fluido)",
"/light (Toggle day mode)": "",
"/me is excited (Display action text)": "/me se emociona (Mostrar texto de acción)",
"/night (Toggle night mode)": "",
"/poll Where should we go to lunch today? (Create a poll)": "/poll ¿Donde deberíamos comer hoy? (Crear encuesta)",
"/settings (Load settings menu)": "",
"/todo (Create a todo list)": "",
"/todo (Create a todo list)": "/todo (Crear una lista de tareas)",
"1 day": "1 día",
"1 hour": "1 hora",
"1 week": "1 semana",
@@ -24,15 +24,15 @@
"2 minutes": "2 minutos",
"24-hour clock (17:00)": "Formato 24 horas (17:00)",
"3 days": "3 días",
"<b>Total messages</b>: {total_messages}": "",
"<p>Stream will be announced in <b>#{notifications_stream}</b>.</p>": "",
"<p>The stream <b>{stream_name}</b> does not exist.</p><p>Manage your subscriptions <z-link>on your Streams page</z-link>.</p>": "",
"<strong>{name}</strong> is not subscribed to this stream. They will not be notified if you mention them.": "",
"<strong>{name}</strong> is not subscribed to this stream. They will not be notified unless you subscribe them.": "",
"<z-link>Click here</z-link> to learn about exporting private streams and messages.": "",
"<z-link>Upgrade</z-link> for more space.": "",
"<b>Total messages</b>: {total_messages}": "<b>Mensajes totales</b>: {total_messages}",
"<p>Stream will be announced in <b>#{notifications_stream}</b>.</p>": "<p>El canal será anunciado en <b>#{notifications_stream}</b>.</p>",
"<p>The stream <b>{stream_name}</b> does not exist.</p><p>Manage your subscriptions <z-link>on your Streams page</z-link>.</p>": "<p>El canal <b>{stream_name}</b> no existe.</p><p>Gestiona tus subscripciones <z-link>en tu página de canales</z-link>.</p>",
"<strong>{name}</strong> is not subscribed to this stream. They will not be notified if you mention them.": "<strong>{name}</strong> no está subscrito a este canal. No será notificado si le mencionas.",
"<strong>{name}</strong> is not subscribed to this stream. They will not be notified unless you subscribe them.": "<strong>{name}</strong> no está subscrito a este canal. No será notificado si no le subscribes.",
"<z-link>Click here</z-link> to learn about exporting private streams and messages.": "<z-link>Haz clic aquí</z-link> para aprender sobre cómo exportar canales privados y mensajes.",
"<z-link>Upgrade</z-link> for more space.": "<z-link>Mejora</z-link> para más espacio.",
"A Topic Move already in progress.": "Un cambio de tema ya en marcha. ",
"A language is marked as 100% translated only if every string in the web, desktop, and mobile apps is translated, including administrative UI and error messages.": "",
"A language is marked as 100% translated only if every string in the web, desktop, and mobile apps is translated, including administrative UI and error messages.": "Un idioma está marcado como traducido al 100% únicamente si todas las cadenas en las aplicaciones web, escritorio y móvil son traducidas. Incluyendo la interfaz administrativa y mensajes de error.",
"A stream needs to have a name": "Un canal necesita tener un nombre",
"A stream with this name already exists": "Ya existe un canal con este nombre",
"A unicode emoji with name <b>{emoji_name}</b> already exists. Uploading a custom emoji with this name will override the unicode emoji with this name.": "",
@@ -95,7 +95,7 @@
"All streams": "Todos los canales",
"All time": "Todos los tiempos",
"All unreads": "Todos sin leer",
"Allow message content in message notification emails": "",
"Allow message content in message notification emails": "Permitir el contenido del mensaje en los correos electrónicos de notificación de mensajes",
"Allow message deleting": "Permitir borrado de mensajes",
"Allow message editing": "Permitir la edición de mensajes",
"Allow subdomains": "Permitir subdominios",
@@ -122,14 +122,14 @@
"Are you sure you want to deactivate your account?": "",
"Are you sure you want to delete <b>{group_name}</b>?": "",
"Are you sure you want to delete your profile picture?": "",
"Are you sure you want to mention all <strong>{count}</strong> people in this stream? <br /> This will send email and mobile push notifications to most of those <strong>{count}</strong> users. <br /> If you don't want to do that, please edit your message to remove the <strong>@{mention}</strong> mention.": "",
"Are you sure you want to mute <b>{user_name}</b>? Messages sent by muted users will never trigger notifications, will be marked as read, and will be hidden.": "",
"Are you sure you want to mention all <strong>{count}</strong> people in this stream? <br /> This will send email and mobile push notifications to most of those <strong>{count}</strong> users. <br /> If you don't want to do that, please edit your message to remove the <strong>@{mention}</strong> mention.": "¿Estás seguro de querer mencionar a toda la gente, <strong>{count}</strong>, en este canal? <br /> Esto enviara notificaciones de correo electrónico y móvil a la mayoria de los <strong>{count}</strong> usuarios. <br /> si no quieres hacer eso, por favor, edita tu mensaje para quitar la mención <strong>@{mention}</strong>.",
"Are you sure you want to mute <b>{user_name}</b>? Messages sent by muted users will never trigger notifications, will be marked as read, and will be hidden.": "¿Estás seguro de querer silenciar a <b>{user_name}</b>? Los mensajes enviados por usuarios silenciados no emitirán notificaciones, serán marcados como leídos, y serán ocultados.",
"Are you sure you want to permanently delete <b>{topic_name}</b>?": "",
"Are you sure you want to resend the invitation to <z-email></z-email>?": "",
"Are you sure you want to revoke the invitation to <strong>{email}</strong>?": "",
"Are you sure you want to revoke this invitation link created by <strong>{referred_by}</strong>?": "",
"Are you sure you want to unstar all messages in <stream-topic></stream-topic>? This action cannot be undone.": "",
"Are you sure you want to unstar all starred messages? This action cannot be undone.": "",
"Are you sure you want to unstar all messages in <stream-topic></stream-topic>? This action cannot be undone.": "¿Seguro que quieres quitar la estrella a todos los mensajes en <stream-topic></stream-topic>? Esta acción no se puede deshacer.",
"Are you sure you want to unstar all starred messages? This action cannot be undone.": "¿Estas seguro de querer quitar la estrella a todos los mensajes con estrella? Esta acción no se puede deshacer.",
"Attachment deleted": "Adjunto eliminado",
"Audible desktop notifications": "Notificaciones de escritorio audibles",
"Audio": "Audio",
@@ -171,7 +171,7 @@
"Collapse": "Contraer",
"Color scheme": "Esquema de color",
"Complete": "Completado",
"Compose message": "",
"Compose message": "Escribir mensaje",
"Compose your message here": "Escribe tu mensaje aquí",
"Compose your message here...": "Escribe tu mensaje aquí...",
"Condense message (-)": "Acortar mensaje (-)",
@@ -188,7 +188,7 @@
"Copy code": "Copiar código",
"Copy from stream": "Copiar desde canal",
"Copy link": "Copiar enlace",
"Copy link to message": "",
"Copy link to message": "Copiar enlace al mensaje",
"Copy mention syntax": "Copiar la sintaxis de la mención",
"Copy zuliprc": "Copiar zuliprc",
"Create": "Crear",
@@ -244,7 +244,7 @@
"Depending on the size of your organization, an export can take anywhere from seconds to an hour.": "Dependiendo del tamaño de tu organización, las exportaciones pueden tardar de segundos a horas.",
"Description": "Descripción",
"Desktop": "Escritorio",
"Detailed message formatting documentation": "",
"Detailed message formatting documentation": "Documentación detallada sobre el formato de los mensajes",
"Disabled": "Desactivado",
"Discard": "Descartar",
"Display my availability to other users when online": "Mostrar disponibilidad a otros usuarios cuando estás conectado",
@@ -363,9 +363,9 @@
"Image": "Imagen",
"Inactive bots": "Bots inactivos",
"Include content of private messages in desktop notifications": "Incluir el contenido de los mensajes privados en las notificaciones de escritorio",
"Include message content in message notification emails": "",
"Include message content in message notification emails": "Incluir el contenido de los mensajes en las notificaciones por correo electrónico",
"Include muted": "Incluye silenciado",
"Include organization name in subject of message notification emails": "",
"Include organization name in subject of message notification emails": "Incluir el nombre de la organización en el asunto de los correos electrónicos de notificación",
"Incoming webhooks can only send messages.": "Los webhooks de entrada solo pueden enviar mensajes.",
"Interface": "Interfaz",
"Invalid URL": "URL inválida",
@@ -415,8 +415,8 @@
"Mention a timezone-aware time": "Mencionar una hora del huso horario",
"Mentioned in": "Mencionado en",
"Mentions": "Menciones",
"Message #{stream_name}": "",
"Message #{stream_name} > {topic_name}": "",
"Message #{stream_name}": "Enviar mensaje #{stream_name}",
"Message #{stream_name} > {topic_name}": "Enviar mensaje a #{stream_name} > {topic_name}",
"Message actions": "Acciones del mensaje",
"Message editing": "Edición de mensajes",
"Message formatting": "Formato de mensajes",
@@ -424,10 +424,10 @@
"Message retention for stream": "Retención de mensajes para el canal",
"Message retention period": "Período de retención de mensajes",
"Message sent when you were not subscribed": "Mensaje enviado cuando no estabas suscrito",
"Message {recipient_label}": "",
"Message {recipient_names}": "",
"Message {recipient_name} ({recipient_status})": "",
"Messages in this stream will be automatically deleted after {retention_days} days.": "",
"Message {recipient_label}": "Enviar mensaje a {recipient_label}",
"Message {recipient_names}": "Enviar mensaje a {recipient_names}",
"Message {recipient_name} ({recipient_status})": "Enviar mensaje a {recipient_name} ({recipient_status})",
"Messages in this stream will be automatically deleted after {retention_days} days.": "Los mensajes en este canal serán borrados automáticamente después de {retention_days} días.",
"Messages in this stream will be retained forever.": "Mensajes en este canal serán guardados este canal para siempre",
"Method": "Método",
"Mobile": "Móvil",
@@ -453,8 +453,8 @@
"Name changes are disabled in this organization. Contact an administrator to change your name.": "El cambio de nombre esta desactivado para esta organización. Contacta con un administrador para cambiar tu nombre.",
"Narrow to stream &quot;{display_recipient}&quot;": "",
"Narrow to stream &quot;{display_recipient}&quot;, topic &quot;{topic}&quot;": "",
"Narrow to your private messages with {display_reply_to}": "",
"Narrow to {message_recipient}": "",
"Narrow to your private messages with {display_reply_to}": "Limitar a tus mensajes privados con {display_reply_to}",
"Narrow to {message_recipient}": "Limitar a {message_recipient}",
"Never": "Nunca",
"Never had one? Forgotten it?": "¿Nunca has tenido una? ¿La has olvidado?",
"New": "Nuevo",
@@ -716,11 +716,11 @@
"This is a <z-icon></z-icon> <b>web public stream</b>. Any member of the organization can join without an invitation and anyone on the internet can read the content published.": "",
"This is a private stream": "Este es un canal privado",
"This is what a Zulip notification looks like.": "Así es como se verá una notificación de Zulip",
"This message was hidden because you have muted the sender.": "",
"This organization is configured to restrict editing of message content to {minutes_to_edit} minutes after it is sent.": "",
"This message was hidden because you have muted the sender.": "Este mensaje ha sido ocultado porque has silenciado al remitente.",
"This organization is configured to restrict editing of message content to {minutes_to_edit} minutes after it is sent.": "Esta organización está configurada para restringir la edición del contenido de los mensajes hasta {minutes_to_edit} minutos después de enviados.",
"This stream does not exist or is private.": "Este canal no existe o es privado.",
"This stream has been deactivated": "Este canal ha sido desactivado",
"This stream is reserved for <strong>announcements</strong>. <br /> Are you sure you want to message all <strong>{count}</strong> people in this stream?": "",
"This stream is reserved for <strong>announcements</strong>. <br /> Are you sure you want to message all <strong>{count}</strong> people in this stream?": "Este canal está reservado para <strong>anuncios</strong>. <br /> ¿Seguro que quieres enviar un mensaje a toda la gente, <strong>{count}</strong>, en este canal?",
"Thursday": "Jueves",
"Time": "Fecha y hora",
"Time format": "Formato de fecha y hora",
@@ -758,8 +758,8 @@
"Unread count summary (appears in desktop sidebar and browser tab)": "Resumen de mensajes sin leer (aparece en el menú lateral en escritorio y en una pestaña en navegadores)",
"Unstar": "Desmarcar",
"Unstar all messages": "Desmarcar todos los mensajes destacados",
"Unstar all messages in topic": "",
"Unstar messages in topic": "",
"Unstar all messages in topic": "Quitar estrella a todos los mensajes en el tema",
"Unstar messages in topic": "Quitar estrella a mensajes en el tema",
"Unsubscribe": "Desuscribirse",
"Unsubscribe from {stream_name}": "",
"Unsubscribed successfully!": "¡Desuscrito exitosamente!",
@@ -822,7 +822,7 @@
"Who can add users to streams": "Quiénes pueden añadir usuarios a canales",
"Who can create and manage user groups": "¿Quién puede crear y gestionar grupos de usuarios?",
"Who can create streams": "Quiénes pueden crear canales",
"Who can move messages between streams": "",
"Who can move messages between streams": "Quién puede mover mensajes entre canales",
"Who can post to the stream?": "Quiénes pueden publicar al canal?",
"Who can use @all/@everyone mentions in large streams": "Quienes pueden usar @all/@everyone menciones en canales largos",
"Who can use private messages": "Quienes pueden usar mensajes privados",
@@ -881,7 +881,7 @@
"beta": "beta",
"clear": "limpiar",
"cookie": "cookie",
"group private messages with {recipient}": "",
"group private messages with {recipient}": "agrupar mensajes privados con {recipient}",
"in 1 hour": "en 1 hora",
"in 20 minutes": "en 20 minutos",
"in 3 hours": "en 3 horas",
@@ -889,7 +889,7 @@
"marketing": "marketing",
"more topics": "más temas",
"private messages with yourself": "mensajes privados contigo mismo",
"private messages with {recipient}": "",
"private messages with {recipient}": "mensajes privados con {recipient}",
"{comma_separated_usernames} and {last_username} reacted with {emoji_name}": "",
"{count} users are subscribed to #{title}": "",
"{days_old} days ago": "",
@@ -897,11 +897,11 @@
"{hours} hours ago": "",
"{last_active_date}": "",
"{minutes} min to edit": "",
"{minutes} minutes ago": "",
"{minutes} minutes ago": "Hace {minutes} minutos",
"{seconds} sec to edit": "",
"{starred_status} this message": "",
"{starred_status} this message (Ctrl + s)": "",
"{username} [said]({link_to_message}):": "",
"{starred_status} this message": "{starred_status} este mensaje",
"{starred_status} this message (Ctrl + s)": "{starred_status} este mensaje (Ctrl + s)",
"{username} [said]({link_to_message}):": "{username} [ha dicho]({link_to_message}):",
"{username} reacted with {emoji_name}": "",
"{wildcard_mention_token} (Notify stream)": ""
}

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-23 19:14+0000\n"
"POT-Creation-Date: 2021-11-30 22:59+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -1404,7 +1404,7 @@ msgstr ""
msgid "Organization permissions"
msgstr ""
#: templates/zerver/app/settings_overlay.html:76 zerver/models.py:2390
#: templates/zerver/app/settings_overlay.html:76 zerver/models.py:2391
msgid "Custom emoji"
msgstr ""
@@ -3803,7 +3803,7 @@ msgstr ""
msgid "Message must have recipients"
msgstr ""
#: zerver/lib/addressee.py:154 zerver/lib/outgoing_webhook.py:193
#: zerver/lib/addressee.py:154 zerver/lib/outgoing_webhook.py:215
msgid "Invalid message type"
msgstr ""
@@ -3917,7 +3917,7 @@ msgstr ""
msgid "Must be an organization administrator or emoji author"
msgstr ""
#: zerver/lib/emoji.py:117 zerver/models.py:840
#: zerver/lib/emoji.py:117 zerver/models.py:841
msgid "Invalid characters in emoji name"
msgstr ""
@@ -4237,11 +4237,11 @@ msgid ""
"a previous message."
msgstr ""
#: zerver/lib/outgoing_webhook.py:293
#: zerver/lib/outgoing_webhook.py:315
msgid "Invalid JSON in response"
msgstr ""
#: zerver/lib/outgoing_webhook.py:302
#: zerver/lib/outgoing_webhook.py:324
msgid "Invalid response format"
msgstr ""
@@ -4436,7 +4436,7 @@ msgstr ""
msgid "Invalid interface type"
msgstr ""
#: zerver/lib/users.py:223 zerver/lib/users.py:228 zerver/models.py:3469
#: zerver/lib/users.py:223 zerver/lib/users.py:228 zerver/models.py:3470
msgid "Invalid user ID: {}"
msgstr ""
@@ -4612,87 +4612,91 @@ msgstr ""
msgid "CSRF error: {reason}"
msgstr ""
#: zerver/models.py:372
#: zerver/models.py:373
msgid "stream events"
msgstr ""
#: zerver/models.py:406
#: zerver/models.py:407
msgid "Available on Zulip Standard. Upgrade to access."
msgstr ""
#: zerver/models.py:910
msgid "Invalid linkifier pattern. Valid characters are {}."
#: zerver/models.py:919 zerver/models.py:921
msgid "Bad regular expression: {}"
msgstr ""
#: zerver/models.py:928
#: zerver/models.py:922
msgid "Unknown regular expression error"
msgstr ""
#: zerver/models.py:929
msgid "Invalid URL format string."
msgstr ""
#: zerver/models.py:1035 zerver/views/realm_playgrounds.py:24
#: zerver/models.py:1036 zerver/views/realm_playgrounds.py:24
msgid "Invalid characters in pygments language"
msgstr ""
#: zerver/models.py:1415
#: zerver/models.py:1416
msgid "Organization owner"
msgstr ""
#: zerver/models.py:1416
#: zerver/models.py:1417
msgid "Organization administrator"
msgstr ""
#: zerver/models.py:1417
#: zerver/models.py:1418
msgid "Moderator"
msgstr ""
#: zerver/models.py:1418
#: zerver/models.py:1419
msgid "Member"
msgstr ""
#: zerver/models.py:1419
#: zerver/models.py:1420
msgid "Guest"
msgstr ""
#: zerver/models.py:2389
#: zerver/models.py:2390
msgid "Unicode emoji"
msgstr ""
#: zerver/models.py:2391
#: zerver/models.py:2392
msgid "Zulip extra emoji"
msgstr ""
#: zerver/models.py:3473
#: zerver/models.py:3474
msgid "User with ID {} is deactivated"
msgstr ""
#: zerver/models.py:3476
#: zerver/models.py:3477
msgid "User with ID {} is a bot"
msgstr ""
#: zerver/models.py:3509
#: zerver/models.py:3510
msgid "List of options"
msgstr ""
#: zerver/models.py:3512
#: zerver/models.py:3513
msgid "Person picker"
msgstr ""
#: zerver/models.py:3524
#: zerver/models.py:3525
msgid "Short text"
msgstr ""
#: zerver/models.py:3525
#: zerver/models.py:3526
msgid "Long text"
msgstr ""
#: zerver/models.py:3526
#: zerver/models.py:3527
msgid "Date picker"
msgstr ""
#: zerver/models.py:3527
#: zerver/models.py:3528
msgid "Link"
msgstr ""
#: zerver/models.py:3530
#: zerver/models.py:3531
msgid "External account"
msgstr ""
@@ -4708,24 +4712,24 @@ msgstr ""
msgid "An unknown browser"
msgstr ""
#: zerver/tornado/event_queue.py:632
#: zerver/tornado/event_queue.py:644
msgid "Missing 'queue_id' argument"
msgstr ""
#: zerver/tornado/event_queue.py:635
#: zerver/tornado/event_queue.py:647
msgid "Missing 'last_event_id' argument"
msgstr ""
#: zerver/tornado/event_queue.py:638
#: zerver/tornado/event_queue.py:650
msgid "You are not authorized to get events from this queue"
msgstr ""
#: zerver/tornado/event_queue.py:644
#: zerver/tornado/event_queue.py:656
#, python-brace-format
msgid "An event newer than {event_id} has already been pruned!"
msgstr ""
#: zerver/tornado/event_queue.py:654
#: zerver/tornado/event_queue.py:666
#, python-brace-format
msgid "Event {event_id} was not in this queue"
msgstr ""
@@ -5423,14 +5427,14 @@ msgstr ""
msgid "Invalid data."
msgstr ""
#: zproject/backends.py:1922
#: zproject/backends.py:1927
msgid "Missing id_token parameter"
msgstr ""
#: zproject/backends.py:2277
#: zproject/backends.py:2282
msgid "Invalid OTP"
msgstr ""
#: zproject/backends.py:2280
#: zproject/backends.py:2285
msgid "Can't use both mobile_flow_otp and desktop_flow_otp together."
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -41,13 +41,13 @@
"Action": "Tevékenység",
"Actions": "Tevékenységek",
"Active": "Aktív",
"Active bots": "Aktív botok",
"Active bots": "Aktív robotok",
"Active now": "Most aktív",
"Active users": "Aktív felhasználók",
"Add": "Hozzáadás",
"Add GIF": "GIF hozzáadása",
"Add a new alert word": "Új figyelt szó hozzáadása",
"Add a new bot": "Új bot hozzáadása",
"Add a new bot": "Új robot hozzáadása",
"Add a new code playground": "Kód játszótér hozzáadása",
"Add a new emoji": "Új hangulatjel hozzáadása",
"Add a new linkifier": "Új linkesítő létrehozása",
@@ -76,7 +76,7 @@
"Admins": "Adminok",
"Admins and full members": "Adminok és teljes tagok",
"Admins and members": "Adminok és tagok",
"Admins and members, but only admins can add generic bots": "Adminok és tagok, de csak az adminok adhatnak hozzá általános botokat",
"Admins and members, but only admins can add generic bots": "Adminok és tagok, de csak az adminok adhatnak hozzá általános robotokat",
"Admins and moderators": "Adminok és moderátorok",
"Admins only": "Csak adminisztrátorok",
"Admins, members and guests": "Adminok, tagok és vendégek",
@@ -104,7 +104,7 @@
"Already subscribed to {stream}": "Már feliratkozott a/az {stream} csatornára",
"Already subscribed users:": "Már feliratkozott felhasználók:",
"Always": "Mindig",
"An API key can be used to programmatically access a Zulip account. Anyone with access to your API key has the ability to read your messages, send messages on your behalf, and otherwise impersonate you on Zulip, so you should guard your API key as carefully as you guard your password. <br /> We recommend creating bots and using the bots' accounts and API keys to access the Zulip API, unless the task requires access to your account.": "Egy API kulcs a Zulip-fiók programozott elérésére használható. Az API kulcsod birtokában bárki képes olvasni az üzeneteidet, üzeneteket küldhet a nevedben, és más módokon is megszemélyesíthet téged a Zulip szolgáltatásban, ezért a kulcsot ugyanolyan gondosan kell őrizned, mint a jelszavad.<br /> Hacsak a feladathoz nincs szükség közvetlen hozzáférést adni a saját fiókodhoz, a Zulip API eléréséhez elsősorban botok létrehozását javasoljuk, amelyek a botok saját fiókjának API kulcsát használják.",
"An API key can be used to programmatically access a Zulip account. Anyone with access to your API key has the ability to read your messages, send messages on your behalf, and otherwise impersonate you on Zulip, so you should guard your API key as carefully as you guard your password. <br /> We recommend creating bots and using the bots' accounts and API keys to access the Zulip API, unless the task requires access to your account.": "Egy API kulcs a Zulip-fiók programozott elérésére használható. Az API kulcsod birtokában bárki képes olvasni az üzeneteidet, üzeneteket küldhet a nevedben, és más módokon is megszemélyesíthet téged a Zulip szolgáltatásban, ezért a kulcsot ugyanolyan gondosan kell őrizned, mint a jelszavad.<br /> Hacsak a feladathoz nincs szükség közvetlen hozzáférést adni a saját fiókodhoz, a Zulip API eléréséhez elsősorban robotok létrehozását javasoljuk, amelyek a robotok saját fiókjának API kulcsát használják.",
"An hour ago": "Egy órája",
"An unknown error occurred.": "Ismeretlen hiba történt!",
"Announce stream": "Kihirdető üzenetfolyam",
@@ -112,7 +112,7 @@
"Any organization administrator can conduct an export.": "A szervezet bármelyik adminisztrátora végezhet exportálást.",
"Any time": "Bármikor",
"Anyone can join; anyone can view complete message history without joining": "Bárki beléphet; bárki megtekintheti a múltbeli üzeneteket belépés nélkül is",
"Anyone in this organization can add bots": "Ebből a szervezetből bárki hozzáadhat botokat",
"Anyone in this organization can add bots": "Ebből a szervezetből bárki hozzáadhat robotokat",
"April": "április",
"Archive stream": "Archív üzenetfolyam",
"Are invitations required for joining the organization?": "Szükség van meghívóra a szervezethez való csatlakozáshoz?",
@@ -138,17 +138,17 @@
"Author": "Szerző",
"Automatic": "Automatikus",
"Avatar from Gravatar": "Avatar a Gravatar-ról",
"Bot": "Bot",
"Bot email": "Bot e-mail",
"Bot email (a-z, 0-9, and dashes only)": "Bot e-mail (csak a-z, 0-9 és kötőjelek)",
"Bot type": "Bot típusa",
"Bots": "Botok",
"Bot": "Robot",
"Bot email": "Robot e-mail",
"Bot email (a-z, 0-9, and dashes only)": "Robot e-mail (csak a-z, 0-9 és kötőjelek)",
"Bot type": "Robot típusa",
"Bots": "Robotok",
"By deactivating <z-user></z-user>, they will be logged out immediately.": "A/az <z-user></z-user> deaktiválásával azonnal ki is lesznek jelentkeztetve.",
"By deactivating your account, you will be logged out immediately.": "A fiókod deaktiválásával azonnal ki is fogunk jelentkeztetni.",
"Cancel": "Mégse",
"Cancel compose": "Írás megszakítása",
"Change": "Változtatás",
"Change bot info and owner": "Bot info és tulajdonos módosítása",
"Change bot info and owner": "Robot info és tulajdonos módosítása",
"Change color": "Szín módosítása",
"Change email": "E-mail változtatása",
"Change full name": "Teljes név változtatása",
@@ -182,7 +182,7 @@
"Configure the default streams new users are subscribed to when joining your organization.": "Az alapértelmezett üzenetfolyamok beállítása, amelyekre az új felhasználók fel legyenek iratkozva, amikor csatlakoznak a szervezetedhez.",
"Confirm": "Megerősít",
"Convert emoticons before sending (<code>:)</code> becomes \ud83d\ude03)": "Hangulatjelek konvertálása elküldés előtt ( <code>:)</code> -ből 😃 lesz)",
"Cookie Bot": "Süti bot",
"Cookie Bot": "Süti robot",
"Copied!": "Lemásolva!",
"Copy and close": "Másolás és bezárás",
"Copy code": "Kód másolása",
@@ -192,10 +192,10 @@
"Copy mention syntax": "Említés formájának másolása",
"Copy zuliprc": "zuliprc másolása",
"Create": "Létrehozás",
"Create bot": "Bot létrehozása",
"Create bot": "Robot létrehozása",
"Create new stream": "Új üzenetfolyam létrehozása",
"Create stream": "Folyam létrehozása",
"Creating bot": "Bot létrehozása...",
"Creating bot": "Robot létrehozása...",
"Creating stream...": "Üzenetfolyam létrehozása...",
"Current password": "Jelenlegi jelszó",
"Custom": "Egyedi",
@@ -227,7 +227,7 @@
"Default view": "Alapértelmezett nézet",
"Delete": "Törlés",
"Delete alert word": "Figyelt szó törlése",
"Delete bot": "Bot törlése",
"Delete bot": "Robot törlése",
"Delete draft": "Piszkozat törlése",
"Delete file": "Fájl törlése",
"Delete icon": "Ikon törlése",
@@ -254,7 +254,7 @@
"Don\u2019t allow disposable email addresses": "Nem engedje meg az eldobható email címek használatát",
"Download .zuliprc": ".zuliprc letöltése",
"Download botserverrc": "A botserverrc letöltése",
"Download config of all active outgoing webhook bots in Zulip Botserver format.": "Minden kimenő aktív webhook bot konfigurációjának letöltése Zulip Botserver formátumban",
"Download config of all active outgoing webhook bots in Zulip Botserver format.": "Minden kimenő aktív webhook robot konfigurációjának letöltése Zulip Botserver formátumban",
"Download file": "Fájl letöltése",
"Download zuliprc": "zuliprc letöltése",
"Drafts": "Piszkozatok",
@@ -262,7 +262,7 @@
"Drafts older than <strong>{draft_lifetime}</strong> days are automatically removed.": "A/az <strong>{draft_lifetime}</strong> napnál régebbi piszkozatokat automatikusan eltávolítjuk.",
"EDITED": "SZERKESZTVE",
"Edit": "Szerkesztés",
"Edit bot": "Bot szerkesztése",
"Edit bot": "Robot szerkesztése",
"Edit linkifiers": "Linkesítők szerkesztése",
"Edit status message": "Állapotüzeneted szerkesztése",
"Edit topic": "Téma szerkesztése",
@@ -316,7 +316,7 @@
"File size must be at most {max_file_size} MiB.": "Az állomány mérete maximum {max_file_size} MiB lehet.",
"File type is not supported.": "A fájltípus nem támogatott.",
"Filter": "Szűrő",
"Filter bots": "Szűrőbotok",
"Filter bots": "Robotok szűrése",
"Filter by category": "Szűrés kategória szerint",
"Filter code playgrounds": "Kód játszóterek szűrése",
"Filter deactivated users": "Letiltott felhasználók szűrése",
@@ -361,7 +361,7 @@
"Humans": "Emberek",
"Idle": "Tétlen",
"Image": "Kép",
"Inactive bots": "Inaktív botok",
"Inactive bots": "Inaktív robotok",
"Include content of private messages in desktop notifications": "A privát üzenetek szövege megjelenhet az asztali értesítésekben",
"Include message content in message notification emails": "Az üzenet szövege megjelenhet az üzenetről értesítő mailekben",
"Include muted": "Némítottakkal együtt",
@@ -479,7 +479,7 @@
"Night": "Éjszaka",
"Night logo": "Éjszakai logó",
"Night mode": "Éjszakai mód",
"No bots match your current filter.": "Nincs a szűrésnek megfelelő bot.",
"No bots match your current filter.": "Nincs a szűrésnek megfelelő robot.",
"No custom emoji.": "Nincs egyedi hangulatjel.",
"No default streams match you current filter.": "Nincs a szűrésnek megfelelő alapértelmezett üzenetfolyam.",
"No description.": "Nincs leírás.",
@@ -501,7 +501,7 @@
"No. Only moderators and admins can send invitations.": "Nem. Csak moderátorok és adminok küldhetnek meghívókat.",
"Nobody": "Senki",
"None": "Nincs",
"Note that any bots that you maintain will be disabled.": "Vedd figyelembe, hogy minden botod letiltásra fog kerülni.",
"Note that any bots that you maintain will be disabled.": "Vedd figyelembe, hogy minden robotod letiltásra fog kerülni.",
"Note that organizations are limited to five exports per week.": "Megjegyzés: a szervezetek hetente öt alkalommal végezhetnek exportálást.",
"Nothing to preview": "Nincs mit megmutatni előnézetként",
"Notification sound": "Értesítési hang",
@@ -516,9 +516,9 @@
"Only group members and organization administrators can modify a group.": "Csak csoporttagok és szervezeti adminisztrátorok módosíthatják a csoportokat.",
"Only organization administrators and moderators can post": "Csak szervezeti adminisztrátorok és moderátorok üzenhetnek",
"Only organization administrators and moderators can post.": "Csak szervezeti adminisztrátorok és moderátorok üzenhetnek.",
"Only organization administrators can add bots to this organization": "Csak szervezeti adminisztrátorok adhatnak hozzá botokat ehhez a szervezethez",
"Only organization administrators can add bots to this organization": "Csak szervezeti adminisztrátorok adhatnak hozzá robotokat ehhez a szervezethez",
"Only organization administrators can add custom emoji in this organization.": "Csak szervezeti adminisztrátorok adhatnak egyedi hangulatjelet ehhez a szervezethez.",
"Only organization administrators can add generic bots": "Csak szervezeti adminisztrátorok hozhatnak létre általános botokat",
"Only organization administrators can add generic bots": "Csak szervezeti adminisztrátorok hozhatnak létre általános robotokat",
"Only organization administrators can edit these settings.": "Ezeket a beállításokat csak a szervezet adminisztrátorai módosíthatják.",
"Only organization administrators can modify user groups in this organization.": "Csak szervezeti adminisztrátorok módosíthatnak felhasználói csoportokat ebben a szervezetben.",
"Only organization administrators can post": "Csak a szervezeti adminisztrátorok üzenhetnek",
@@ -591,7 +591,7 @@
"Public stream messages in organization": "Nyilvános üzenetfolyam üzenetek a szervezetben",
"Quote and reply or forward": "Válasz vagy továbbítás idézéssel",
"Reactivate": "Újraaktiválás",
"Reactivate bot": "Bot újraaktíválása",
"Reactivate bot": "Robot újraaktíválása",
"Receives new stream notifications": "Új üzenetfolyam értesítéseket kap",
"Recent topics": "Legfrissebb témák",
"Remind me about this": "Emlékeztess erről",
@@ -699,14 +699,14 @@
"Subscribers": "Feliratkozók",
"Successfully subscribed users:": "Sikeresen feliratkozott felhasználók:",
"Sunday": "vasárnap",
"System bot": "Rendszet bot",
"System bot": "Rendszer robot",
"Task already exists": "A feladat már létezik",
"The recipient {recipient} is not valid": "A/az {recipient} címzett érvénytelen",
"The recipients {recipients} are not valid": "A/az {recipients} címzettek érvénytelenek",
"The stream description cannot contain newline characters.": "Az üzenetfolyam leírása nem tartalmazhat soremelést.",
"The stream description has been updated!": "Az üzenetfolyam leírását frissítettük!",
"The stream has been renamed!": "Az üzenetfolyamot átnevezésre került!",
"Their password will be cleared from our systems, and any bots they maintain will be disabled.": "Jelszavuk törlődik a rendszereinkből, és minden általuk fenntartott botot letiltunk.",
"Their password will be cleared from our systems, and any bots they maintain will be disabled.": "Jelszavuk törlődik a rendszereinkből, és minden általuk fenntartott robotot letiltunk.",
"There are no current alert words.": "Jelenleg nincsenek figyelt szavak.",
"These settings are explained in detail in the <z-link>help center</z-link>.": "Ezeket a beállításokat a<z-link> Súgó Központban</z-link> részletesen is bemutatjuk.",
"This action is permanent and cannot be undone. All users will permanently lose access to their Zulip accounts.": "Ez a művelet végleges és nem lehet visszavonni. Minden felhasználó végleg el fogja veszíteni hozzáférését a Zulip fiókjaikhoz.",
@@ -817,7 +817,7 @@
"Whether wildcard mentions like @all are treated as mentions for the purpose of notifications.": "A csoportos említések - mint pl. az @all - értesítési szempontból is említésként legyenek-e kezelve vagy sem.",
"Who can access the stream?": "Ki érheti el az üzenetfolyamot?",
"Who can access user email addresses": "Ki érheti el a felhasználó e-mail címeit",
"Who can add bots": "Ki hozhat létre botokat",
"Who can add bots": "Ki hozhat létre robotokat",
"Who can add custom emoji": "Ki adhat hozzá egyedi hangulatjeleket",
"Who can add users to streams": "Ki adhat hozzá felhasználókat az üzenetfolyamhoz",
"Who can create and manage user groups": "Ki hozhat létre és kezelhet felhasználói csoportokat",
@@ -855,8 +855,8 @@
"You do not have permission to use wildcard mentions in this stream.": "Nincs engedélyed csoportos említések használatára ebben az üzenetfolyamban.",
"You get": "Azt kapod",
"You have muted <z-stream-topic></z-stream-topic>.": "<z-stream-topic></z-stream-topic> elnémítva.",
"You have no active bots.": "Nincsenek aktív botjaid.",
"You have no inactive bots.": "Nincsenek inaktív botjaid.",
"You have no active bots.": "Nincsenek aktív robotjaid.",
"You have no inactive bots.": "Nincsenek inaktív robotjaid.",
"You have not muted any topics yet.": "Nem némítottál el egyetlen témát sem.",
"You have not muted any users yet.": "Nem némítottál el egyetlen felhasználót sem.",
"You have not uploaded any files.": "Még nincsenek feltöltött állományaid.",

View File

@@ -11,6 +11,7 @@
# Alessio Schreiber <alessio.schreiber@gmail.com>, 2021
# Paolo Midali <pmidali@latek.it>, 2021
# Andrea, 2021
# Alya Abbott <alya@zulip.com>, 2021
#
#, fuzzy
msgid ""
@@ -19,7 +20,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-05-26 18:04+0000\n"
"PO-Revision-Date: 2021-04-02 21:39+0000\n"
"Last-Translator: Andrea, 2021\n"
"Last-Translator: Alya Abbott <alya@zulip.com>, 2021\n"
"Language-Team: Italian (https://www.transifex.com/zulip/teams/53893/it/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -3821,7 +3822,7 @@ msgstr "Non siamo riusciti a trovare questa l'organizzazione Zulip."
#: zerver/lib/actions.py:357 zerver/lib/actions.py:369
#, python-brace-format
msgid "{user} just signed up for Zulip. (total: {user_count})"
msgstr "{user}appena registrato su Zulip. (total: {user_count})"
msgstr "{user} appena registrato su Zulip. (total: {user_count})"
#: zerver/lib/actions.py:360
msgid "signups"
@@ -4869,7 +4870,7 @@ msgstr "{var_name} è troppo lungo (limit: {max_length} characters)"
#, python-brace-format
msgid "{var_name} has incorrect length {length}; should be {target_length}"
msgstr ""
"{var_name} ha lunghezza errata{length}; dovrebbe essere {target_length}"
"{var_name} ha lunghezza errata {length}; dovrebbe essere {target_length}"
#: zerver/lib/validator.py:122 zerver/lib/validator.py:124
#, python-brace-format
@@ -5111,7 +5112,7 @@ msgstr "Un evento più recente di {event_id} è già stato eliminato!"
#: zerver/tornado/event_queue.py:654
#, python-brace-format
msgid "Event {event_id} was not in this queue"
msgstr "Evento{event_id} non è in questa coda"
msgstr "Evento {event_id} non è in questa coda"
#: zerver/tornado/exceptions.py:15
#, python-brace-format

View File

@@ -17,6 +17,8 @@
# 최진호 <jinho.choi.999@gmail.com>, 2021
# Yeonjeong Noh <nyj2800@hanmail.net>, 2021
# sung yong kim <hikimsy@gmail.com>, 2021
# F4D3C0D3 0x, 2021
# KIM JINWOO, 2021
#
#, fuzzy
msgid ""
@@ -25,7 +27,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-05-26 18:04+0000\n"
"PO-Revision-Date: 2021-04-02 21:39+0000\n"
"Last-Translator: sung yong kim <hikimsy@gmail.com>, 2021\n"
"Last-Translator: KIM JINWOO, 2021\n"
"Language-Team: Korean (https://www.transifex.com/zulip/teams/53893/ko/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -226,6 +228,7 @@ msgid ""
"A full update of all the graphs happens once a day. The “messages sent over "
"time” graph is updated once an hour."
msgstr ""
"모든 그래프의 전체 업데이트는 하루에 한 번 합니다. \"메세지가 보내진지 시간 경과된\" 그래프는 한 시간에 한 번 업데이트 됩니다."
#: templates/analytics/stats.html:129
msgid "Analytics documentation"
@@ -243,10 +246,13 @@ msgid ""
" from %(old_email_html_tag)s to %(new_email_html_tag)s\n"
" "
msgstr ""
"\n"
"이것은 너의 Zulip 계정에 있는 이메일 주소가 변경되었는지 확인한다.\n"
"from 전 이메일%(old_email_html_tag)s to 현재 새로운 이메일%(new_email_html_tag)s "
#: templates/confirmation/link_does_not_exist.html:9
msgid "Whoops. We couldn't find your confirmation link in the system."
msgstr ""
msgstr "오류. 우리는 이 시스템에서 너를 확인할 만한 것을 찾지 못했습니다."
#: templates/confirmation/link_does_not_exist.html:11
#: templates/confirmation/link_malformed.html:12
@@ -256,24 +262,27 @@ msgid ""
" Anyway, shoot us a line at %(support_email_html_tag)s and we'll get this resolved shortly.\n"
" "
msgstr ""
"\n"
"어떠한 방식으로든, 너는 우리에게 어떠한 얘기라도 보내라 %(support_email_html_tag)s으로 그러면 우리는 이것을 즉시 해결 할 것이다."
#: templates/confirmation/link_expired.html:10
msgid "Whoops. The confirmation link has expired or been deactivated."
msgstr ""
msgstr "오류. 확인 링크가 만료되었거나 비활성화 되었습니다."
#: templates/confirmation/link_expired.html:11
msgid "Please contact your organization administrator for a new link."
msgstr ""
msgstr "조직 관리자에게 새로운 링크로 연락해라"
#: templates/confirmation/link_malformed.html:9
msgid "Whoops. The confirmation link is malformed."
msgstr ""
msgstr "오류. 이 확인 링크는 형식화되지 않았습니다."
#: templates/confirmation/link_malformed.html:10
msgid ""
"Make sure you copied the link correctly in to your browser. If you're still "
"encountering this page, it's probably our fault. We're sorry."
msgstr ""
"너의 브라우저에 올바른 링크를 복사하는 것을 확실히 해주십시오. 이 페이지가 계속 생성될 경우, 아마도 오류가 생겼습니다. 죄송합니다."
#: templates/corporate/billing.html:21 templates/zerver/app/navbar.html:140
msgid "Billing"
@@ -298,11 +307,11 @@ msgstr "해마다 지불"
#: templates/corporate/upgrade.html:85
msgid "License management"
msgstr ""
msgstr "라이센스 관리"
#: templates/corporate/upgrade.html:89
msgid "Automatic"
msgstr ""
msgstr "자동"
#: templates/corporate/upgrade.html:90
msgid "Most convenient"
@@ -447,11 +456,11 @@ msgstr "메시지 삭제"
#: templates/zerver/app/delete_message.html:13
msgid "Are you sure you want to permanently delete this message?"
msgstr ""
msgstr "당신은 이 메세지를 영구적으로 삭제하시겠습니까?"
#: templates/zerver/app/delete_message.html:14
msgid "Deleting a message removes it for everyone."
msgstr ""
msgstr "모두를 위해 삭제된 메세지를 지움"
#: templates/zerver/app/delete_message.html:19
#: templates/zerver/app/index.html:167
@@ -461,7 +470,7 @@ msgstr "취소"
#: templates/zerver/app/delete_message.html:20
msgid "Confirm"
msgstr ""
msgstr "확인"
#: templates/zerver/app/deprecation_notice.html:6
msgid "Deprecation notice"
@@ -644,19 +653,19 @@ msgstr ""
#: templates/zerver/app/index.html:160
msgid "In a meeting"
msgstr ""
msgstr "회의 안 에서"
#: templates/zerver/app/index.html:161
msgid "Commuting"
msgstr ""
msgstr "출근중"
#: templates/zerver/app/index.html:162
msgid "Out sick"
msgstr ""
msgstr "병가"
#: templates/zerver/app/index.html:163
msgid "Vacationing"
msgstr ""
msgstr "휴가 중"
#: templates/zerver/app/index.html:164
msgid "Working remotely"
@@ -702,7 +711,7 @@ msgstr "회원"
#: templates/zerver/app/invite_user.html:43
#: templates/zerver/development/dev_login.html:40
msgid "Moderators"
msgstr ""
msgstr "중재자들"
#: templates/zerver/app/invite_user.html:44
msgid "Organization administrators"
@@ -789,7 +798,7 @@ msgstr "키보드 단축키 보기"
#: templates/zerver/app/keyboard_shortcuts.html:56
msgid "Go to default view"
msgstr ""
msgstr "기본 보기로 돌아가기"
#: templates/zerver/app/keyboard_shortcuts.html:65
msgid "Navigation"
@@ -853,11 +862,11 @@ msgstr "선택해서 보기"
#: templates/zerver/app/keyboard_shortcuts.html:159
msgid "Narrow to stream"
msgstr ""
msgstr "스트림을 묶기"
#: templates/zerver/app/keyboard_shortcuts.html:163
msgid "Narrow to topic or PM conversation"
msgstr ""
msgstr "주제 또는 PM 대화와 묶기"
#: templates/zerver/app/keyboard_shortcuts.html:167
msgid "Narrow to all private messages"
@@ -931,7 +940,7 @@ msgstr "최근 주제들 보기"
#: templates/zerver/app/keyboard_shortcuts.html:251
msgid "Filter topics"
msgstr ""
msgstr "주제를 필터링"
#: templates/zerver/app/keyboard_shortcuts.html:260
msgid "Drafts"
@@ -1012,11 +1021,11 @@ msgstr "구독, 추가 혹은 스트림 구성"
#: templates/zerver/app/left_sidebar.html:73
msgid "Back to streams"
msgstr ""
msgstr "스트림으로 돌아가기"
#: templates/zerver/app/left_sidebar.html:80
msgid "Subscribe to more streams"
msgstr ""
msgstr "더 많은 스트림들을 구독"
#: templates/zerver/app/lightbox_overlay.html:10
msgid "Pan &amp; Zoom"
@@ -1079,7 +1088,7 @@ msgstr "조직 관리"
#: templates/zerver/app/navbar.html:83
msgid "Usage statistics"
msgstr ""
msgstr "사용 통계"
#: templates/zerver/app/navbar.html:90 templates/zerver/footer.html:16
msgid "Help center"
@@ -1100,7 +1109,7 @@ msgstr "Zulip에 대해"
#: templates/zerver/app/navbar.html:117
msgid "Contact support"
msgstr ""
msgstr "고객지원 연결"
#: templates/zerver/app/navbar.html:124
msgid "Desktop & mobile apps"
@@ -1116,7 +1125,7 @@ msgstr "API 문서"
#: templates/zerver/app/navbar.html:147
msgid "Support Zulip"
msgstr ""
msgstr "Zulip에 도움"
#: templates/zerver/app/navbar.html:154
msgid "Plans and pricing"
@@ -1193,19 +1202,19 @@ msgstr ""
#: templates/zerver/app/navbar_alerts.html:43
msgid "Download the latest version."
msgstr ""
msgstr "가장 최근 버전 다운로드"
#: templates/zerver/app/navbar_alerts.html:50
msgid "This Zulip server is running an old version and should be upgraded."
msgstr ""
msgstr "Zulip 서버가 오래된 버전으로 사용 중 입니다. 업그레이드를 진행 하십시오"
#: templates/zerver/app/navbar_alerts.html:53
msgid "Learn more"
msgstr ""
msgstr "좀 더 알아보기"
#: templates/zerver/app/navbar_alerts.html:56
msgid "Dismiss for a week"
msgstr ""
msgstr "한 주 동안 무시"
#: templates/zerver/app/navbar_alerts.html:64
#, python-format
@@ -1285,7 +1294,7 @@ msgstr "해당 메시지 ID만 보기"
#: templates/zerver/app/search_operators.html:45
msgid "Search all public streams in the organization."
msgstr ""
msgstr "이 조직에 있는 모든 공개 스트림들을 찾기 "
#: templates/zerver/app/search_operators.html:49
msgid "Narrow to messages with alert words."
@@ -1395,7 +1404,7 @@ msgstr "알림끈 주제"
#: templates/zerver/app/settings_overlay.html:48
msgid "Muted users"
msgstr ""
msgstr "음소거된 유저들"
#: templates/zerver/app/settings_overlay.html:55
msgid "Organization profile"
@@ -1440,7 +1449,7 @@ msgstr "인증 방법"
#: templates/zerver/app/settings_overlay.html:95
msgid "Only organization owners can edit these settings."
msgstr ""
msgstr "오직 조직 주인만 이 설정을 변경할 수 있습니다."
#: templates/zerver/app/settings_overlay.html:110
msgid "Deactivated users"
@@ -1456,7 +1465,7 @@ msgstr "링크 변환기"
#: templates/zerver/app/settings_overlay.html:143
msgid "Code playgrounds"
msgstr ""
msgstr "코드방"
#: templates/zerver/app/settings_overlay.html:151
msgid "Custom profile fields"
@@ -1468,11 +1477,11 @@ msgstr "초대장"
#: templates/zerver/app/settings_overlay.html:163
msgid "Data exports"
msgstr ""
msgstr "데이터 내보내기"
#: templates/zerver/app/settings_overlay.html:169
msgid "Show more"
msgstr ""
msgstr "더 보여주기"
#: templates/zerver/config_error.html:14
msgid ""
@@ -3571,7 +3580,7 @@ msgstr ""
#: zerver/lib/actions.py:2991
msgid "Private messages are disabled in this organization."
msgstr ""
msgstr "이 조직에서 개인 메시지는 비활성화 되었습니다."
#: zerver/lib/actions.py:3116
msgid "Widgets: API programmer sent invalid JSON content"
@@ -4562,7 +4571,7 @@ msgstr ""
#: zerver/models.py:1417
msgid "Moderator"
msgstr ""
msgstr "중재자"
#: zerver/models.py:1418
msgid "Member"
@@ -5122,7 +5131,7 @@ msgstr ""
#: zerver/views/user_settings.py:49
msgid "Avatar changes are disabled in this organization."
msgstr ""
msgstr "아바타 변경은 이 조직에서 불가능합니다."
#: zerver/views/user_settings.py:63 zerver/views/user_settings.py:149
msgid "Email address changes are disabled in this organization."

File diff suppressed because it is too large Load Diff

View File

@@ -1,138 +1,138 @@
{
"ar": {
"not_translated": 218,
"total": 256
"not_translated": 239,
"total": 277
},
"bg": {
"not_translated": 126,
"total": 256
"not_translated": 147,
"total": 277
},
"ca": {
"not_translated": 31,
"total": 256
"not_translated": 52,
"total": 277
},
"cs": {
"not_translated": 21,
"total": 256
"not_translated": 41,
"total": 277
},
"de": {
"not_translated": 0,
"total": 256
"total": 277
},
"en_GB": {
"not_translated": 0,
"total": 256
"total": 277
},
"es": {
"not_translated": 31,
"total": 256
"not_translated": 51,
"total": 277
},
"fa": {
"not_translated": 77,
"total": 256
"not_translated": 97,
"total": 277
},
"fi": {
"not_translated": 27,
"total": 256
"not_translated": 47,
"total": 277
},
"fr": {
"not_translated": 0,
"total": 256
"total": 277
},
"gl": {
"not_translated": 123,
"total": 256
"not_translated": 144,
"total": 277
},
"hi": {
"not_translated": 93,
"total": 256
"not_translated": 114,
"total": 277
},
"hu": {
"not_translated": 29,
"total": 256
"not_translated": 49,
"total": 277
},
"id": {
"not_translated": 131,
"total": 256
"not_translated": 152,
"total": 277
},
"it": {
"not_translated": 7,
"total": 256
"not_translated": 20,
"total": 277
},
"ja": {
"not_translated": 88,
"total": 256
"not_translated": 108,
"total": 277
},
"ko": {
"not_translated": 31,
"total": 256
"not_translated": 51,
"total": 277
},
"lt": {
"not_translated": 225,
"total": 256
"not_translated": 246,
"total": 277
},
"ml": {
"not_translated": 222,
"total": 256
"not_translated": 243,
"total": 277
},
"nl": {
"not_translated": 22,
"total": 256
"not_translated": 42,
"total": 277
},
"pl": {
"not_translated": 39,
"total": 256
"not_translated": 59,
"total": 277
},
"pt": {
"not_translated": 31,
"total": 256
"not_translated": 51,
"total": 277
},
"pt_PT": {
"not_translated": 75,
"total": 256
"not_translated": 95,
"total": 277
},
"ro": {
"not_translated": 30,
"total": 256
"not_translated": 50,
"total": 277
},
"ru": {
"not_translated": 0,
"total": 256
"total": 277
},
"sr": {
"not_translated": 235,
"total": 256
"not_translated": 256,
"total": 277
},
"sv": {
"not_translated": 197,
"total": 256
"not_translated": 217,
"total": 277
},
"ta": {
"not_translated": 192,
"total": 256
"not_translated": 213,
"total": 277
},
"tr": {
"not_translated": 0,
"total": 256
"not_translated": 20,
"total": 277
},
"uk": {
"not_translated": 21,
"total": 256
"not_translated": 41,
"total": 277
},
"vi": {
"not_translated": 232,
"total": 256
"not_translated": 253,
"total": 277
},
"zh_Hans": {
"not_translated": 96,
"total": 256
"not_translated": 116,
"total": 277
},
"zh_Hant": {
"not_translated": 107,
"total": 256
"not_translated": 128,
"total": 277
},
"zh_TW": {
"not_translated": 29,
"total": 256
"not_translated": 49,
"total": 277
}
}

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-23 19:14+0000\n"
"POT-Creation-Date: 2021-11-30 22:59+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -1403,7 +1403,7 @@ msgstr ""
msgid "Organization permissions"
msgstr ""
#: templates/zerver/app/settings_overlay.html:76 zerver/models.py:2390
#: templates/zerver/app/settings_overlay.html:76 zerver/models.py:2391
msgid "Custom emoji"
msgstr ""
@@ -3802,7 +3802,7 @@ msgstr ""
msgid "Message must have recipients"
msgstr ""
#: zerver/lib/addressee.py:154 zerver/lib/outgoing_webhook.py:193
#: zerver/lib/addressee.py:154 zerver/lib/outgoing_webhook.py:215
msgid "Invalid message type"
msgstr ""
@@ -3916,7 +3916,7 @@ msgstr ""
msgid "Must be an organization administrator or emoji author"
msgstr ""
#: zerver/lib/emoji.py:117 zerver/models.py:840
#: zerver/lib/emoji.py:117 zerver/models.py:841
msgid "Invalid characters in emoji name"
msgstr ""
@@ -4236,11 +4236,11 @@ msgid ""
"a previous message."
msgstr ""
#: zerver/lib/outgoing_webhook.py:293
#: zerver/lib/outgoing_webhook.py:315
msgid "Invalid JSON in response"
msgstr ""
#: zerver/lib/outgoing_webhook.py:302
#: zerver/lib/outgoing_webhook.py:324
msgid "Invalid response format"
msgstr ""
@@ -4435,7 +4435,7 @@ msgstr ""
msgid "Invalid interface type"
msgstr ""
#: zerver/lib/users.py:223 zerver/lib/users.py:228 zerver/models.py:3469
#: zerver/lib/users.py:223 zerver/lib/users.py:228 zerver/models.py:3470
msgid "Invalid user ID: {}"
msgstr ""
@@ -4611,87 +4611,91 @@ msgstr ""
msgid "CSRF error: {reason}"
msgstr ""
#: zerver/models.py:372
#: zerver/models.py:373
msgid "stream events"
msgstr ""
#: zerver/models.py:406
#: zerver/models.py:407
msgid "Available on Zulip Standard. Upgrade to access."
msgstr ""
#: zerver/models.py:910
msgid "Invalid linkifier pattern. Valid characters are {}."
#: zerver/models.py:919 zerver/models.py:921
msgid "Bad regular expression: {}"
msgstr ""
#: zerver/models.py:928
#: zerver/models.py:922
msgid "Unknown regular expression error"
msgstr ""
#: zerver/models.py:929
msgid "Invalid URL format string."
msgstr ""
#: zerver/models.py:1035 zerver/views/realm_playgrounds.py:24
#: zerver/models.py:1036 zerver/views/realm_playgrounds.py:24
msgid "Invalid characters in pygments language"
msgstr ""
#: zerver/models.py:1415
#: zerver/models.py:1416
msgid "Organization owner"
msgstr ""
#: zerver/models.py:1416
#: zerver/models.py:1417
msgid "Organization administrator"
msgstr ""
#: zerver/models.py:1417
#: zerver/models.py:1418
msgid "Moderator"
msgstr ""
#: zerver/models.py:1418
#: zerver/models.py:1419
msgid "Member"
msgstr ""
#: zerver/models.py:1419
#: zerver/models.py:1420
msgid "Guest"
msgstr ""
#: zerver/models.py:2389
#: zerver/models.py:2390
msgid "Unicode emoji"
msgstr ""
#: zerver/models.py:2391
#: zerver/models.py:2392
msgid "Zulip extra emoji"
msgstr ""
#: zerver/models.py:3473
#: zerver/models.py:3474
msgid "User with ID {} is deactivated"
msgstr ""
#: zerver/models.py:3476
#: zerver/models.py:3477
msgid "User with ID {} is a bot"
msgstr ""
#: zerver/models.py:3509
#: zerver/models.py:3510
msgid "List of options"
msgstr ""
#: zerver/models.py:3512
#: zerver/models.py:3513
msgid "Person picker"
msgstr ""
#: zerver/models.py:3524
#: zerver/models.py:3525
msgid "Short text"
msgstr ""
#: zerver/models.py:3525
#: zerver/models.py:3526
msgid "Long text"
msgstr ""
#: zerver/models.py:3526
#: zerver/models.py:3527
msgid "Date picker"
msgstr ""
#: zerver/models.py:3527
#: zerver/models.py:3528
msgid "Link"
msgstr ""
#: zerver/models.py:3530
#: zerver/models.py:3531
msgid "External account"
msgstr ""
@@ -4707,24 +4711,24 @@ msgstr ""
msgid "An unknown browser"
msgstr ""
#: zerver/tornado/event_queue.py:632
#: zerver/tornado/event_queue.py:644
msgid "Missing 'queue_id' argument"
msgstr ""
#: zerver/tornado/event_queue.py:635
#: zerver/tornado/event_queue.py:647
msgid "Missing 'last_event_id' argument"
msgstr ""
#: zerver/tornado/event_queue.py:638
#: zerver/tornado/event_queue.py:650
msgid "You are not authorized to get events from this queue"
msgstr ""
#: zerver/tornado/event_queue.py:644
#: zerver/tornado/event_queue.py:656
#, python-brace-format
msgid "An event newer than {event_id} has already been pruned!"
msgstr ""
#: zerver/tornado/event_queue.py:654
#: zerver/tornado/event_queue.py:666
#, python-brace-format
msgid "Event {event_id} was not in this queue"
msgstr ""
@@ -5422,14 +5426,14 @@ msgstr ""
msgid "Invalid data."
msgstr ""
#: zproject/backends.py:1922
#: zproject/backends.py:1927
msgid "Missing id_token parameter"
msgstr ""
#: zproject/backends.py:2277
#: zproject/backends.py:2282
msgid "Invalid OTP"
msgstr ""
#: zproject/backends.py:2280
#: zproject/backends.py:2285
msgid "Can't use both mobile_flow_otp and desktop_flow_otp together."
msgstr ""

View File

@@ -6,7 +6,7 @@
"(unavailable)": "(недоступно)",
"(you)": "(вы)",
"({message_retention_days} days)": "({message_retention_days} дней)",
"/dark (Toggle night mode)": "/dark (Включить ночной режим)",
"/dark (Toggle night mode)": "/dark (Включить темный режим)",
"/day (Toggle day mode)": "/day (Включить дневной режим)",
"/fixed-width (Toggle fixed width mode)": "/fixed-width (Включить фиксированный формат отображения рабочей области)",
"/fluid-width (Toggle fluid width mode)": "/fluid-width (Включить широкий формат отображения рабочей области)",
@@ -32,10 +32,10 @@
"<z-link>Click here</z-link> to learn about exporting private streams and messages.": "<z-link>Нажмите здесь</z-link>, чтобы узнать больше об экспорте закрытых каналов и сообщений.",
"<z-link>Upgrade</z-link> for more space.": "<z-link>Обновите</z-link>, чтобы получить больше места.",
"A Topic Move already in progress.": "Перемещение темы уже в процессе.",
"A language is marked as 100% translated only if every string in the web, desktop, and mobile apps is translated, including administrative UI and error messages.": "Язык отмечается переведенным на 100% только если все строки в web, настольном и мобильном приложениях переведены, включая интерфейс администратора и сообщения об ошибках.",
"A language is marked as 100% translated only if every string in the web, desktop, and mobile apps is translated, including administrative UI and error messages.": "Язык отмечается переведенным на 100% только, если переведены все строки веб-интерфейса, настольного и мобильного приложений, включая интерфейс администратора и сообщения об ошибках.",
"A stream needs to have a name": "Канал должен иметь название",
"A stream with this name already exists": "Канал с таким именем уже существует",
"A unicode emoji with name <b>{emoji_name}</b> already exists. Uploading a custom emoji with this name will override the unicode emoji with this name.": "Эмодзи с именем <b>{emoji_name}</b> уже существует. Загрузка дополнительного эмодзи с этим именем перезапишет существующий эмодзи.",
"A stream with this name already exists": "Канал с таким названием уже существует",
"A unicode emoji with name <b>{emoji_name}</b> already exists. Uploading a custom emoji with this name will override the unicode emoji with this name.": "Эмодзи с названием <b>{emoji_name}</b> уже существует. Загрузка дополнительного эмодзи с этим названием перезапишет существующий эмодзи.",
"A wide image for the upper left corner of the app.": "Широкое изображение для верхнего левого угла приложения.",
"API key": "API-ключ",
"Action": "Действие",
@@ -86,7 +86,7 @@
"Alert word already exists!": "Сигнальное слово уже существует!",
"Alert word can't be empty!": "Сигнальное слово не может быть пустым!",
"Alert word removed successfully!": "Сигнальное слово успешно удалено!",
"Alert words allow you to be notified as if you were @-mentioned when certain words or phrases are used in Zulip. Alert words are not case sensitive.": "Слова предупреждения позволяют получать уведомления, как если бы вы упоминали @ при использовании определенных слов или фраз в Zulip. Оповещения не чувствительны к регистру.",
"Alert words allow you to be notified as if you were @-mentioned when certain words or phrases are used in Zulip. Alert words are not case sensitive.": "Сигнальные слова позволяют получать уведомления, как если бы вас упомянули с помощью @, в случае, если в Zulip используются определенные слова или фразы. Сигнальные слова не чувствительны к регистру.",
"All": "Все",
"All messages": "Все сообщения",
"All messages including muted streams": "Все сообщения, включая заглушенные каналы",
@@ -123,7 +123,7 @@
"Are you sure you want to delete <b>{group_name}</b>?": "Вы уверены, что хотите удалить <b>{group_name}</b>?",
"Are you sure you want to delete your profile picture?": "Вы действительно хотите удалить изображение профиля?",
"Are you sure you want to mention all <strong>{count}</strong> people in this stream? <br /> This will send email and mobile push notifications to most of those <strong>{count}</strong> users. <br /> If you don't want to do that, please edit your message to remove the <strong>@{mention}</strong> mention.": "Вы уверены, что хотите упомянуть всех <strong>{count}</strong> человек в этом канале? <br /> Это приведет к отправке почтовых и мобильных уведомлений для большинства из этих <strong>{count}</strong> пользователей. <br /> Если вы не хотите этого, пожалуйста, отредактируйте сообщение и удалите упоминание <strong>@{mention}</strong>.",
"Are you sure you want to mute <b>{user_name}</b>? Messages sent by muted users will never trigger notifications, will be marked as read, and will be hidden.": "Вы уверенны, что хотите заглушить<b>{user_name}</b>? Сообщения отправленные заглушенным пользователем никогда не будут вызывать оповещения, будут помечены как прочитанные и будут скрыты.",
"Are you sure you want to mute <b>{user_name}</b>? Messages sent by muted users will never trigger notifications, will be marked as read, and will be hidden.": "Вы уверенны, что хотите заглушить<b>{user_name}</b>? Сообщения, отправленные заглушенным пользователем, никогда не будут вызывать оповещения, будут помечены как прочитанные и будут скрыты.",
"Are you sure you want to permanently delete <b>{topic_name}</b>?": "Вы уверены, что хотите безвозвратно удалить тему <b>{topic_name}</b>?",
"Are you sure you want to resend the invitation to <z-email></z-email>?": "Вы уверены, что хотите повторно отправить приглашение для <z-email></z-email>?",
"Are you sure you want to revoke the invitation to <strong>{email}</strong>?": "Вы уверены, что хотите отозвать приглашение для <strong>{email}</strong>?",
@@ -164,7 +164,7 @@
"Clear emoji image": "Очистить изображение эмодзи",
"Clear profile picture": "Очистить картинку профиля",
"Click anywhere on a message to reply.": "Кликните в любое место сообщения чтобы ответить.",
"Click here to reveal.": "Нажмите здесь чтобы открыть.",
"Click here to reveal.": "Нажмите здесь, чтобы открыть.",
"Click outside the input box to save. We'll automatically notify anyone that was added or removed.": "Для сохранения нажмите вне поля ввода. Мы автоматически уведомим каждого, кто был добавлен или удален.",
"Click to join video call": "Нажмите здесь, чтобы присоединиться к видеозвонку",
"Close": "Закрыть",
@@ -186,11 +186,11 @@
"Copied!": "Скопировано!",
"Copy and close": "Скопировать и закрыть",
"Copy code": "Скопировать код",
"Copy from stream": "Копировать из канала",
"Copy from stream": "Скопировать из канала",
"Copy link": "Скопировать ссылку",
"Copy link to message": "Скопировать ссылку в сообщение",
"Copy mention syntax": "Скопировать синтаксис упоминания",
"Copy zuliprc": "Копировать zuliprc",
"Copy zuliprc": "Скопировать zuliprc",
"Create": "Создать",
"Create bot": "Создать бот",
"Create new stream": "Создать новый канал",
@@ -331,7 +331,7 @@
"Filter topics (t)": "Фильтровать темы (t)",
"Filter uploads": "Фильтр загрузок",
"Filter users": "Фильтр пользователей",
"First time? Read our <z-link>guidelines</z-link> for creating and naming streams.": "В первый раз? Прочтите наши <z-link>рекомендации</z-link>по созданию и именованию каналов.",
"First time? Read our <z-link>guidelines</z-link> for creating and naming streams.": "В первый раз? Прочтите наши <z-link>рекомендации</z-link> по созданию и именованию каналов.",
"Fixed width": "Фиксированная ширина",
"Fixed width mode": "Модус с фиксированной шириной",
"Fluid width": "Изменяющаяся ширина",
@@ -500,7 +500,7 @@
"No. Only full members and admins can send invitations.": "Нет. Только участники и администраторы могут высылать приглашения.",
"No. Only moderators and admins can send invitations.": "Нет. Только модераторы и администраторы могут отправлять приглашения.",
"Nobody": "Никто",
"None": "Пусто",
"None": "Нет",
"Note that any bots that you maintain will be disabled.": "Обратите внимание, что все ваши боты будут отключены.",
"Note that organizations are limited to five exports per week.": "Пожалуйста, учтите, что организации ограничены пятью выгрузками данных за неделю.",
"Nothing to preview": "Ничего нет для предпросмотра",
@@ -809,7 +809,7 @@
"Visual desktop notifications": "Визуальные оповещения на рабочем столе",
"Waiting period (days)": "Период ожидания (дни)",
"Waiting period before new members turn into full members": "Период ожидания, пока из новых членов становятся полноценные члены",
"Warning: <strong>{stream_name}</strong> is a private stream.": "Предупреждение: канал<strong>{stream_name}</strong>является закрытым.",
"Warning: <strong>{stream_name}</strong> is a private stream.": "Предупреждение: канал <strong>{stream_name}</strong> является закрытым.",
"We are about to have a poll. Please wait for the question.": "У нас будет голосование. Пожалуйста, подождите, пока появится вопрос.",
"We've replaced the \"{originalHotkey}\" hotkey with \"{replacementHotkey}\" to make this common shortcut easier to trigger.": "Мы заменили горячие клавиши \"{originalHotkey}\" на \"{replacementHotkey}\" чтобы упростить использование этого сокращения.",
"Wednesday": "Среда",

File diff suppressed because it is too large Load Diff

907
locale/si/translations.json Normal file
View File

@@ -0,0 +1,907 @@
{
"(This user has been deactivated)": "",
"(forever)": "(සදහටම)",
"(no description)": "(සවිස්තරයක් නැත)",
"(no topic)": "(මාතෘකාවක් නැත)",
"(unavailable)": "",
"(you)": "(ඔබ)",
"({message_retention_days} days)": "(දවස් {message_retention_days})",
"/dark (Toggle night mode)": "",
"/day (Toggle day mode)": "",
"/fixed-width (Toggle fixed width mode)": "",
"/fluid-width (Toggle fluid width mode)": "",
"/light (Toggle day mode)": "",
"/me is excited (Display action text)": "",
"/night (Toggle night mode)": "",
"/poll Where should we go to lunch today? (Create a poll)": "",
"/settings (Load settings menu)": "",
"/todo (Create a todo list)": "",
"1 day": "දවස් 1",
"1 hour": "පැය 1",
"1 week": "සති 1",
"10 minutes": "විනාඩි 10",
"12-hour clock (5:00 PM)": "පැය 12 ඔරලෝසුව (ප.ව. 5:00)",
"2 minutes": "විනාඩි 2",
"24-hour clock (17:00)": "පැය 24 ඔරලෝසුව (17:00)",
"3 days": "දවස් 3",
"<b>Total messages</b>: {total_messages}": "<b>මුළු පණිවිඩ</b>: {total_messages}",
"<p>Stream will be announced in <b>#{notifications_stream}</b>.</p>": "",
"<p>The stream <b>{stream_name}</b> does not exist.</p><p>Manage your subscriptions <z-link>on your Streams page</z-link>.</p>": "",
"<strong>{name}</strong> is not subscribed to this stream. They will not be notified if you mention them.": "",
"<strong>{name}</strong> is not subscribed to this stream. They will not be notified unless you subscribe them.": "",
"<z-link>Click here</z-link> to learn about exporting private streams and messages.": "",
"<z-link>Upgrade</z-link> for more space.": "",
"A Topic Move already in progress.": "මාතෘකාවක් ගෙන යාම දැනටමත් කෙරෙමින් පවතී.",
"A language is marked as 100% translated only if every string in the web, desktop, and mobile apps is translated, including administrative UI and error messages.": "",
"A stream needs to have a name": "",
"A stream with this name already exists": "",
"A unicode emoji with name <b>{emoji_name}</b> already exists. Uploading a custom emoji with this name will override the unicode emoji with this name.": "",
"A wide image for the upper left corner of the app.": "",
"API key": "යෙ.ක්‍ර.මු. යතුර",
"Action": "ක්‍රියාමාර්ගය",
"Actions": "ක්‍රියාමාර්ග",
"Active": "ක්‍රියාත්මකයි",
"Active bots": "ක්‍රියාත්මක ස්වයංක්‍රමලේඛ",
"Active now": "දැන් ක්‍රියාත්මකයි",
"Active users": "",
"Add": "එකතු",
"Add GIF": "චලනරූ එකතු කරන්න",
"Add a new alert word": "නව අනතුරු ඇඟවීමේ වචනයක් එක්කරන්න",
"Add a new bot": "නව ස්වයංක්‍රමලේඛයක් එක්කරන්න",
"Add a new code playground": "නව කේත ක්‍රීඩාපිටියක් එක් කරන්න",
"Add a new emoji": "නව ඉමොජි එකතු කරන්න",
"Add a new linkifier": "",
"Add a new profile field": "නව පැතිකඩ ක්ෂේත්‍රයක් එක්කරන්න",
"Add a new user group": "නව පරිශීලක සමූහයක් එක්කරන්න",
"Add alert word": "අනතුරු ඇඟවීමේ වචනය එක්කරන්න",
"Add another user...": "වෙනත් පරිශීලකයෙකු එක්කරන්න...",
"Add choice": "",
"Add code playground": "කේත ක්‍රීඩා පිටිය එක්කරන්න",
"Add emoji": "ඉමොජි එකතු කරන්න",
"Add emoji reaction": "",
"Add extra emoji for members of the {realm_name} organization.": "{realm_name} සංවිධානයේ සාමාජිකයින් සඳහා අතිරේක ඉමොජි එක්කරන්න.",
"Add linkifier": "",
"Add member\u2026": "සාමාජිකයා එක්කරන්න...",
"Add new default stream": "",
"Add one or more users": "පරිශීලකයෙකු හෝ වැඩි ගණනක් එක්කරන්න",
"Add profile field": "පැතිකඩ ක්ෂේත්‍රය එක්කරන්න",
"Add question": "ප්‍රශ්නය එකතු කරන්න",
"Add stream": "",
"Add subscribers. Use #streamname to add all subscribers from that stream.": "",
"Add task": "කාර්යය එකතු කරන්න",
"Add video call": "දෘශ්‍ය ඇමතුම එක්කරන්න",
"Added successfully!": "සාර්ථකව එකතු කරන ලදි!",
"Administrator": "පරිපාලකයින්",
"Administrators can always delete any message.": "",
"Admins": "පරිපාලකයින්",
"Admins and full members": "පරිපාලකයින් සහ පූර්ණ සාමාජිකයින්",
"Admins and members": "පරිපාලකයින් සහ සාමාජිකයින්",
"Admins and members, but only admins can add generic bots": "",
"Admins and moderators": "",
"Admins only": "පරිපාලකයින් පමණි",
"Admins, members and guests": "පරිපාලකයින්, සාමාජිකයින් සහ අමුත්තන්",
"Admins, members, and guests": "පරිපාලකයින්, සාමාජිකයින් සහ අමුත්තන්",
"Alert word": "අනතුරු ඇඟවීමේ වචනය",
"Alert word \"{word}\" added successfully!": "අනතුරු ඇඟවීමේ වචනය \"{word}\" සාර්ථකව එකතු කෙරිණි!",
"Alert word already exists!": "අනතුරු ඇඟවීමේ වචනය දැනටමත් පවතී!",
"Alert word can't be empty!": "අනතුරු ඇඟවීමේ වචනය හිස් විය නොහැකිය!",
"Alert word removed successfully!": "අනතුරු ඇඟවීමේ වචනය සාර්ථකව ඉවත් කෙරිණි!",
"Alert words allow you to be notified as if you were @-mentioned when certain words or phrases are used in Zulip. Alert words are not case sensitive.": "",
"All": "සියළුම",
"All messages": "සියළුම පණිවිඩ",
"All messages including muted streams": "",
"All stream members can post": "",
"All stream members can post.": "",
"All streams": "",
"All time": "සෑම වෙලාවෙම",
"All unreads": "නොකියවූ සියල්ල",
"Allow message content in message notification emails": "පණිවිඩ දැනුම්දීම් වි-තැපැල් හි පණිවිඩ අන්තර්ගතයට ඉඩදෙන්න",
"Allow message deleting": "",
"Allow message editing": "පණිවිඩ සංස්කරණයට ඉඩ දෙන්න",
"Allow subdomains": "උප වසම් වලට ඉඩ දෙන්න",
"Allowed domains": "ඉඩ දුන් වසම්",
"Allowed domains: {domains}": "ඉඩ දුන් වසම්: {domains}",
"Already subscribed to {stream}": "",
"Already subscribed users:": "",
"Always": "සැමවිටම",
"An API key can be used to programmatically access a Zulip account. Anyone with access to your API key has the ability to read your messages, send messages on your behalf, and otherwise impersonate you on Zulip, so you should guard your API key as carefully as you guard your password. <br /> We recommend creating bots and using the bots' accounts and API keys to access the Zulip API, unless the task requires access to your account.": "සුලිප් ගිණුමකට ක්‍රමලේඛිතව ප්‍රවේශ වීම සඳහා යෙ.ක්‍ර.මු. යතුර භාවිතා කළ හැකිය. ඔබගේ යෙ.ක්‍ර.මු. යතුරට ප්‍රවේශ වන ඕනෑම අයෙකුට ඔබගේ පණිවිඩ කියවීමට, ඔබ වෙනුවට පණිවිඩ යැවීමට සහ වෙනත් අයුරකින් ඔබ ලෙස සුලිප් හි පෙනී සිටීමට හැකියාව ඇත, එබැවින් ඔබ යෙ.ක්‍ර.මු. යතුර මුරපදය ආරක්‍ෂා කරන තරමටම ප්‍රවේශම් කර ගත යුතුය. <br /> කාර්යයට ඔබගේ ගිණුමට ප්‍රවේශය අවශ්‍ය නොවන්නේ නම්, ස්වයංක්‍රමලේඛ සෑදීම සහ ස්වයංක්‍රමලේඛ ගිණුම් භාවිතය සහ සුලිප් යෙ.ක්‍ර.මු. වෙත ප්‍රවේශයට යෙ.ක්‍ර.මු. යතුරු ද අපි නිර්දේශ කරමු.",
"An hour ago": "පැයකට කලින්",
"An unknown error occurred.": "නොදන්නා දෝෂයක් සිදු විය.",
"Announce stream": "",
"Any member of this organization can add custom emoji.": "මෙම සංවිධානයේ ඕනෑම සාමාජිකයෙකුට අභිරුචි ඉමොජි එකතු කළ හැකිය.",
"Any organization administrator can conduct an export.": "ඕනෑම සංවිධානයේ පරිපාලකයෙකුට නිර්යාතයක් කළ හැකිය.",
"Any time": "ඕනෑම වෙලාවක",
"Anyone can join; anyone can view complete message history without joining": "",
"Anyone in this organization can add bots": "මෙම සංවිධානයේ ඕනෑම අයෙකුට ස්වයංක්‍රමලේඛ එකතු කළ හැකිය",
"April": "බක්",
"Archive stream": "",
"Are invitations required for joining the organization?": "සංවිධානයට එක්වීමට ඇරයුම් කළ යුතුද?",
"Are you sure you want to archive this stream?": "",
"Are you sure you want to create stream ''''{stream_name}'''' and subscribe {count} users to it?": "",
"Are you sure you want to deactivate this organization?": "",
"Are you sure you want to deactivate your account?": "",
"Are you sure you want to delete <b>{group_name}</b>?": "",
"Are you sure you want to delete your profile picture?": "",
"Are you sure you want to mention all <strong>{count}</strong> people in this stream? <br /> This will send email and mobile push notifications to most of those <strong>{count}</strong> users. <br /> If you don't want to do that, please edit your message to remove the <strong>@{mention}</strong> mention.": "",
"Are you sure you want to mute <b>{user_name}</b>? Messages sent by muted users will never trigger notifications, will be marked as read, and will be hidden.": "ඔබට <b>{user_name}</b> නිහඬ කිරීමට අවශ්‍ය බව විශ්වාසයි ද? නිහඬ කළ පරිශීලකයින් විසින් යවන ලද පණිවිඩ වලට දැනුම්දීම් සිදු නොවේ, කියවූ ලෙස සලකුණු වී සැඟවෙනු ඇත.",
"Are you sure you want to permanently delete <b>{topic_name}</b>?": "",
"Are you sure you want to resend the invitation to <z-email></z-email>?": "ඇරයුම <z-email></z-email> වෙත නැවත යැවීමට අවශ්‍ය බව ඔබට විශ්වාසද?",
"Are you sure you want to revoke the invitation to <strong>{email}</strong>?": "",
"Are you sure you want to revoke this invitation link created by <strong>{referred_by}</strong>?": "",
"Are you sure you want to unstar all messages in <stream-topic></stream-topic>? This action cannot be undone.": "",
"Are you sure you want to unstar all starred messages? This action cannot be undone.": "",
"Attachment deleted": "",
"Audible desktop notifications": "",
"Audio": "ශ්‍රව්‍ය",
"August": "නිකිණි",
"Authentication methods": "",
"Author": "කර්තෘ",
"Automatic": "ස්වයංක්‍රීය",
"Avatar from Gravatar": "",
"Bot": "ස්වයංක්‍රමලේඛ",
"Bot email": "ස්වයංක්‍රමලේඛයේ වි-තැපෑල",
"Bot email (a-z, 0-9, and dashes only)": "ස්වයංක්‍රමලේඛ වි-තැපෑල (අ-ෆ, 0-9, සහ ඉරි පමණි)",
"Bot type": "ස්වයංක්‍රමලේඛ වර්ගය",
"Bots": "ස්වයංක්‍රමලේඛ",
"By deactivating <z-user></z-user>, they will be logged out immediately.": "",
"By deactivating your account, you will be logged out immediately.": "",
"Cancel": "අවලංගු",
"Cancel compose": "",
"Change": "වෙනස් කරන්න",
"Change bot info and owner": "ස්වයංක්‍රමලේඛයේ තොරතුරු හා හිමිකරු වෙනස්කරන්න",
"Change color": "වර්ණය වෙනස් කරන්න",
"Change email": "වි-තැපෑල වෙනස් කරන්න",
"Change full name": "සම්පූර්ණ නම වෙනස් කරන්න",
"Change later messages to this topic": "මෙම මාතෘකාවට පසු පණිවිඩ වෙනස් කරන්න",
"Change only this message topic": "මෙම පණිවිඩයේ මාතෘකාව පමණක් වෙනස් කරන්න",
"Change password": "මුරපදය වෙනස් කරන්න",
"Change previous and following messages to this topic": "මෙම මාතෘකාවට පෙර සහ පහත සඳහන් පණිවිඩ වෙනස් කරන්න",
"Change stream permissions for #": "",
"Change user info and roles": "පරිශීලක තොරතුරු සහ භූමිකාවන් වෙනස් කරන්න",
"Check all": "සියල්ල පරීක්ෂා කරන්න",
"Check your email ({email}) to confirm the new address.": "",
"Choose avatar": "",
"Clear emoji image": "",
"Clear profile picture": "",
"Click anywhere on a message to reply.": "පිළිතුරු දීමට පණිවිඩයක් මත ඕනෑම තැනක ඔබන්න.",
"Click here to reveal.": "හෙළි කිරීමට මෙතැන ඔබන්න.",
"Click outside the input box to save. We'll automatically notify anyone that was added or removed.": "",
"Click to join video call": "දෘශ්‍ය ඇමතුමට එක්වීමට ඔබන්න",
"Close": "වසන්න",
"Collapse": "",
"Color scheme": "",
"Complete": "සම්පූර්ණයි",
"Compose message": "",
"Compose your message here": "",
"Compose your message here...": "",
"Condense message (-)": "",
"Configure external code playgrounds for your Zulip organization. Code playgrounds are interactive in-browser development environments, such as <z-link-repl>replit</z-link-repl>, that are designed to make it convenient to edit and debug code. Zulip code blocks that are <z-link-markdown-help>tagged with a programming language</z-link-markdown-help> will have a button visible on hover that allows you to open the code block in the code playground site.": "",
"Configure how Zulip notifies you about new messages.": "නව පණිවිඩ ගැන සුලිප් ඔබට දැනුම් දෙන අයුරු සකසන්න.",
"Configure regular expression patterns that will be automatically linkified when used in Zulip message bodies or topics. For example to automatically linkify commit IDs and issue numbers (e.g. #123) to the corresponding items in a GitHub project, you could use the following:": "",
"Configure the authentication methods for your organization.": "",
"Configure the default streams new users are subscribed to when joining your organization.": "",
"Confirm": "",
"Convert emoticons before sending (<code>:)</code> becomes \ud83d\ude03)": "",
"Cookie Bot": "",
"Copied!": "පිටපත් විය!",
"Copy and close": "පිටපත් කර වසන්න",
"Copy code": "කේතය පිටපත් කරන්න",
"Copy from stream": "",
"Copy link": "සබැඳිය පිටපත් කරන්න",
"Copy link to message": "පණිවිඩයට සබැඳිය පිටපත් කරන්න",
"Copy mention syntax": "",
"Copy zuliprc": "zuliprc පිටපත් කරන්න",
"Create": "සාදන්න",
"Create bot": "ස්වයංක්‍රමලේඛය සාදන්න",
"Create new stream": "",
"Create stream": "",
"Creating bot": "ස්වයංක්‍රමලේඛ සෑදීම",
"Creating stream...": "",
"Current password": "වත්මන් මුරපදය",
"Custom": "අභිරුචි",
"Custom emoji added!": "අභිරුචි ඉමොජි එකතු කෙරිණි!",
"Custom language: {query}": "අභිරුචි භාෂාව: {query}",
"Custom linkifier added!": "",
"Custom playground added!": "අභිරුචි ක්‍රීඩා පිටිය එකතු කෙරිණි!",
"Custom profile fields": "අභිරුචි පැතිකඩ ක්ෂේත්‍ර",
"Customize profile picture": "",
"Data exports": "දත්ත නිර්යාත",
"Date muted": "නිහඬ කළ දිනය ",
"Date uploaded": "උඩුගත කළ දිනය",
"Day": "දවස",
"Day logo": "දවසේ ලාංඡනය",
"Day mode": "",
"Day of the week to send digests": "",
"Deactivate": "",
"Deactivate account": "",
"Deactivate organization": "",
"Deactivate your account": "",
"Deactivated": "",
"Deactivated users": "",
"December": "උඳුවප්",
"Default is {language}. Use 'text' to disable highlighting.": "",
"Default language": "පෙරනිමි භාෂාව",
"Default language for code blocks:": "",
"Default settings for new users joining this organization.": "මෙම සංවිධානයට එක්වන නව පරිශීලකයින් සඳහා පෙරනිමි සැකසුම්.",
"Default user settings": "පෙරනිමි පරිශීලක සැකසුම්",
"Default view": "",
"Delete": "",
"Delete alert word": "",
"Delete bot": "",
"Delete draft": "",
"Delete file": "",
"Delete icon": "",
"Delete logo": "",
"Delete message": "",
"Delete profile picture": "",
"Delete topic": "",
"Delete user group": "",
"Deleted": "",
"Deleted successfully!": "",
"Deleting a topic will immediately remove it and its messages for everyone. Other users may find this confusing, especially if they had received an email or push notification related to the deleted messages.": "",
"Demote inactive streams": "",
"Dense mode": "",
"Depending on the size of your organization, an export can take anywhere from seconds to an hour.": "ඔබේ සංවිධානයේ ප්‍රමාණය අනුව නිර්යාතයට තත්පරයක සිට පැයක් දක්වා කාලයක් ගත විය හැකිය.",
"Description": "සවිස්තරය",
"Desktop": "",
"Detailed message formatting documentation": "",
"Disabled": "අබල කර ඇත",
"Discard": "",
"Display my availability to other users when online": "",
"Display settings": "",
"Do you want to proceed?": "ඔබට ඉදිරියට යාමට අවශ්‍යද?",
"Domain": "වසම",
"Don\u2019t allow disposable email addresses": "",
"Download .zuliprc": ".zuliprc බාගන්න",
"Download botserverrc": "බොට්සර්වර්ක් බාගන්න",
"Download config of all active outgoing webhook bots in Zulip Botserver format.": "",
"Download file": "ගොනුව බාගන්න",
"Download zuliprc": "zuliprc බාගන්න",
"Drafts": "",
"Drafts ({draft_count})": "",
"Drafts older than <strong>{draft_lifetime}</strong> days are automatically removed.": "",
"EDITED": "සංස්කරණය කර ඇත",
"Edit": "සංස්කරණය",
"Edit bot": "ස්වයංක්‍රමලේඛය සංස්කරණය",
"Edit linkifiers": "",
"Edit status message": "තත්ව පණිවිඩය සංස්කරණය",
"Edit topic": "මාතෘකාව සංස්කරණය",
"Edit user": "පරිශීලක සංස්කරණය",
"Edit your profile": "ඔබගේ පැතිකඩ සංස්කරණය කරන්න",
"Edited ({last_edit_timestr})": "({last_edit_timestr}) සංස්කරණය කෙරිණි",
"Email": "වි-තැපෑල",
"Email address": "වි-තැපැල් ලිපිනය",
"Email address changes are disabled in this organization.": "මෙම සංවිධානය තුළ වි-තැපැල් ලිපිනය වෙනස් කිරීම් අබල කර ඇත.",
"Email copied": "වි-තැපෑල පිටපත් විය",
"Email notifications": "වි-තැපැල් දැනුම්දීම්",
"Emoji name": "ඉමොජි නම",
"Emoji settings": "ඉමොජි සැකසුම්",
"Emojiset changed successfully!": "ඉමොජි කට්ටලය සාර්ථකව වෙනස් කෙරිණි!",
"Enable message edit history": "පණිවිඩ සංස්කරණ ඉතිහාසය සබල කරන්න",
"Enabled": "සබල කර ඇත",
"Endpoint URL": "",
"Error": "දෝෂයකි",
"Error adding alert word!": "අනතුරු ඇඟවීමේ වචනය එකතු කිරීමේ දෝෂයකි!",
"Error adding subscription": "",
"Error checking subscription": "",
"Error creating stream": "",
"Error deleting message": "",
"Error fetching message edit history": "පණිවිඩ සංස්කරණ ඉතිහාසය ගැනීමේ දෝෂයකි",
"Error listing invites": "",
"Error moving the topic": "මාතෘකාව ගෙන යාමේ දෝෂයකි",
"Error removing alert word!": "අනතුරු ඇඟවීමේ වචනය ඉවත් කිරීමේ දෝෂයකි!",
"Error removing subscription": "",
"Error removing user from this stream.": "",
"Error saving edit": "සංස්කරණය සුරැකීමේ දෝෂයකි",
"Error: Cannot deactivate the only organization owner.": "",
"Error: Cannot deactivate the only user. You can deactivate the whole organization though in your <z-link>organization profile settings</z-link>.": "",
"Estimated messages per week": "සතියකට තක්සේරුගත පණිවිඩ",
"Everyone": "සෑම දෙනාම",
"Expand message (-)": "පණිවිඩය පුළුල් කරන්න (-)",
"Export failed": "නිර්යාතයට අසමත් විය",
"Export started. Check back in a few minutes.": "නිර්යාතය ඇරඹිණි. විනාඩි කිහිපයකින් ආපසු පරීක්‍ෂා කරන්න.",
"Exports all users, settings, and all data visible in public streams.": "",
"External account type": "බාහිර ගිණුම් වර්ගය",
"External link": "බාහිර සබැඳිය",
"Failed": "අසමත් විය",
"Failed to create video call.": "දෘශ්‍ය ඇමතුමක් සෑදීමට අසමත් විය.",
"Failed to generate preview": "පෙරදසුන ජනනය කිරීමට අසමත් විය",
"Failed to upload %'{file}'": "%'{file}' උඩුගත කිරීමට අසමත් විය",
"Failed!": "අසමත්!",
"Failed: Emoji name is required.": "අසමත් විය: ඉමොජි නම අවශ්‍යයි.",
"February": "නවම්",
"Field choices": "",
"File": "ගොනුව",
"File and image uploads have been disabled for this organization.": "මෙම සංවිධානය සඳහා ගොනු සහ රූප උඩුගත කිරීම් අබල කර ඇත.",
"File size must be at most {max_file_size} MiB.": "ගොනුවේ ප්‍රමාණය බොහෝ විට මෙ.බ. {max_file_size} විය යුතුය.",
"File type is not supported.": "ගොනු වර්ගයට සහාය නොදක්වයි.",
"Filter": "පෙරහන",
"Filter bots": "ස්වයංක්‍රමලේඛ පෙරන්න",
"Filter by category": "ප්‍රවර්ගය අනුව පෙරන්න",
"Filter code playgrounds": "කේත ක්‍රීඩා පිටි පෙරන්න",
"Filter deactivated users": "",
"Filter emojis": "ඉමොජි පෙරන්න",
"Filter exports": "නිර්යාත පෙරන්න",
"Filter invites": "ඇරයුම් පෙරන්න",
"Filter languages": "භාෂා පෙරන්න",
"Filter linkifiers": "",
"Filter muted topics": "නිහඬ කළ මාතෘකා පෙරන්න",
"Filter muted users": "නිහඬ කළ පරිශීලකයින් පෙරන්න",
"Filter streams": "",
"Filter topics (t)": "",
"Filter uploads": "උඩුගත කිරීම් පෙරන්න",
"Filter users": "පරිශීලකයින් පෙරන්න",
"First time? Read our <z-link>guidelines</z-link> for creating and naming streams.": "",
"Fixed width": "",
"Fixed width mode": "",
"Fluid width": "",
"Fluid width mode": "",
"For example, to configure code playgrounds for languages like Python or JavaScript, you could specify the <i>Language</i> and <i>URL prefix</i> fields as:": "උදාහරණයක් ලෙස, පයිතන් හෝ ජාවාස්ක්‍රිප්ට් වැනි භාෂා සඳහා කේත ක්‍රීඩාපිටි වින්‍යාසගත කිරීමට <i>භාෂාව</i> සහ <i>ඒ.ස.නි. උපසර්ගය</i> යන ක්ෂේත්‍රයන් මෙසේ දැක්විය හැකිය:",
"Forgotten it?": "එය අමතක වෙලාද?",
"Friday": "සිකුරාදා",
"Full name": "සම්පූර්ණ නම",
"GIPHY attribution": "",
"GIPHY integration": "",
"Generate invite link": "ඇරයුම් සබැඳියක් ජනනය කරන්න",
"Generate new API key": "නව යෙ.ක්‍ර.මු. යතුරක් ජනනය කරන්න",
"Generating link...": "සබැඳිය ජනනය වෙමින්...",
"Generic": "",
"Get API key": "යෙ.ක්‍ර.මු. යතුර ගන්න",
"Go back": "ආපසු යන්න",
"Got it!": "",
"Guest": "අමුත්තා",
"Guests are not allowed to post to this stream.": "",
"Guests cannot edit custom emoji.": "අමුත්තන්ට අභිරුචි ඉමොජි සංස්කරණය කළ නොහැකිය.",
"Help": "උදව්",
"Hide password": "මුරපදය සඟවන්න",
"Hide starred message count": "",
"High contrast mode": "",
"Hint": "ඉඟිය",
"Hint (up to 80 characters)": "ඉඟිය (අකුරු 80 දක්වා)",
"Humans": "මනුෂ්‍යයින්",
"Idle": "",
"Image": "",
"Inactive bots": "",
"Include content of private messages in desktop notifications": "",
"Include message content in message notification emails": "පණිවිඩ දැනුම්දීම් වි-තැපැල් හි පණිවිඩ අන්තර්ගතය ඇතුළත් කරන්න",
"Include muted": "නිහඬ කිරීම් ඇතුළත්ව",
"Include organization name in subject of message notification emails": "පණිවිඩ දැනුම් දීමේ වි-තැපැල් වලට සංවිධානයේ නම ඇතුළත් කරන්න",
"Incoming webhooks can only send messages.": "",
"Interface": "අතුරුමුහුණත",
"Invalid URL": "වලංගු නොවන ඒ.ස.නි.",
"Invalid slash command. Check if you are missing a space after the command.": "",
"Invalid stream id": "",
"Invalid time format: {timestamp}": "",
"Invite": "ආරාධනා කරන්න",
"Invite link": "ඇරයුම් සබැඳිය",
"Invite more users": "තවත් පරිශීලකයින්ට ආරාධනා කරන්න",
"Invited as": "ලෙස ආරාධනා කර ඇත",
"Invited at": "ට ආරාධනා කරන ලදි",
"Invited by": "විසින් ආරාධනා කරන ලදි",
"Invitee": "ආරාධිතයා",
"Invites": "ඇරයුම්",
"Inviting...": "ආරාධනා කරමින්...",
"January": "දුරුතු",
"Joined": "එක් විණි",
"Joining the organization": "සංවිධානයට එක් වෙමින්",
"July": "ඇසළ",
"June": "පොසොන්",
"Just now": "මේ දැන්",
"Keyboard shortcuts": "යතුරුපුවරුවේ කෙටිමං",
"Label": "",
"Language": "භාෂාව",
"Language settings": "භාෂා සැකසුම්",
"Large number of subscribers": "",
"Last 10 days": "පසුගිය දවස් 10",
"Last 2 months": "පසුගිය මාස 2",
"Last 30 days": "පසුගිය දවස් 30",
"Last 6 months": "පසුගිය මාස 6",
"Last active": "",
"Last active: {last_seen}": "",
"Last modified": "",
"Link:": "සබැඳිය:",
"Local time": "දේශීය වේලාව",
"Looking for our <z-integrations>integrations</z-integrations> or <z-api>API</z-api> documentation?": "<z-integrations>අනුකලන</z-integrations> හෝ<z-api>යෙ.ක්‍ර.මු.</z-api> ප්‍රලේඛන බලන්නේද?",
"Manage organization": "සංවිධානය කළමනාකරණය",
"Manage user groups": "පරිශීලක සමූහ කළමනාකරණය",
"March": "මැදින්",
"Mark all messages as read": "සියළු පණිවිඩ කියවූ ලෙස සලකුණු කරන්න",
"Mark as read": "කියවූ ලෙස සලකුණු කරන්න",
"Marketing team": "අලෙවිකරණ කණ්ඩායම",
"May": "වෙසක්",
"Me": "මා",
"Member": "සාමාජිකයා",
"Members can only view or manage invitations that you yourself sent.": "",
"Mention a timezone-aware time": "",
"Mentioned in": "හි සඳහන් කර ඇත",
"Mentions": "සැඳහුම්",
"Message #{stream_name}": "",
"Message #{stream_name} > {topic_name}": "",
"Message actions": "පණිවිඩ ක්‍රියාමාර්ග",
"Message editing": "පණිවිඩය සංස්කරණය",
"Message formatting": "",
"Message retention": "පණිවිඩ රඳවා ගැනීම",
"Message retention for stream": "",
"Message retention period": "පණිවිඩ රඳවා ගැනීමේ කාලය",
"Message sent when you were not subscribed": "",
"Message {recipient_label}": "{recipient_label} පණිවිඩය ",
"Message {recipient_names}": "{recipient_names} පණිවිඩය",
"Message {recipient_name} ({recipient_status})": "පණිවිඩය {recipient_name} ({recipient_status})",
"Messages in this stream will be automatically deleted after {retention_days} days.": "",
"Messages in this stream will be retained forever.": "",
"Method": "ක්‍රමය",
"Mobile": "ජංගම",
"Mobile notifications": "ජංගම දැනුම්දීම්",
"Mobile push notifications are not configured on this server.": "",
"Moderator": "",
"Monday": "සඳුදා",
"More details are available <z-link>in the Help Center article</z-link>.": "වැඩි විස්තර <z-link>උපකාරක මධ්‍යස්ථානයේ ලිපියේ</z-link> තිබේ",
"More than 2 weeks ago": "සති 2 කටත් පෙර",
"Move all messages in <strong>{topic_name}</strong>": "සියළුම පණිවිඩ <strong>{topic_name}</strong> තුළට ගෙන යන්න",
"Move topic": "මාතෘකාව ගෙන යන්න",
"Must be invited by a member; new members can only see messages sent after they join; hidden from non-administrator users": "",
"Must be invited by a member; new members can view complete message history; hidden from non-administrator users": "",
"Mute stream": "",
"Mute the topic <b>{topic}</b>": "<b>{topic}</b> මාතෘකාව නිහඬකරන්න",
"Mute this user": "මෙම පරිශීලකයා නිහඬකරන්න",
"Mute topic": "මාතෘකාව නිහඬකරන්න",
"Mute user": "පරිශීලකයා නිහඬකරන්න",
"Muted streams don't show up in \"All messages\" or generate notifications unless you are mentioned.": "",
"Muted user": "නිහඬ කළ පරිශීලකයෙකි",
"N": "",
"Name": "නම",
"Name changes are disabled in this organization. Contact an administrator to change your name.": "",
"Narrow to stream &quot;{display_recipient}&quot;": "",
"Narrow to stream &quot;{display_recipient}&quot;, topic &quot;{topic}&quot;": "",
"Narrow to your private messages with {display_reply_to}": "",
"Narrow to {message_recipient}": "",
"Never": "",
"Never had one? Forgotten it?": "",
"New": "නව",
"New alert word": "නව අනතුරු ඇඟවීමේ වචනය",
"New choice": "",
"New email": "නව වි-තැපෑල",
"New full name": "නව සම්පූර්ණ නම",
"New members are not allowed to post to this stream.<br />Permission will be granted in {days} days.": "",
"New members can only see messages sent after they join.": "නව සාමාජිකයින්ට ඔවුන් සම්බන්ධ වූ පසු පමණක් යැවූ පණිවිඩ දැකිය හැකිය.",
"New members can view complete message history.": "නව සාමාජිකයින්ට සම්පූර්ණ පණිවිඩ ඉතිහාසය බැලිය හැකිය.",
"New message": "නව පණිවිඩය",
"New option": "නව විකල්පය",
"New password": "නව මුරපදය",
"New password is too weak": "නව මුරපදය ඉතා දුර්වල ය",
"New private message": "නව පෞද්ගලික පණිවිඩය",
"New stream message": "",
"New stream notifications:": "",
"New task": "නව කාර්යය",
"New topic": "නව මාතෘකාව",
"New user notifications:": "නව පරිශීලක දැනුම්දීම්:",
"Next week": "ලබන සතිය",
"Night": "රාත්‍රිය",
"Night logo": "රාත්‍රී ලාංඡනය",
"Night mode": "",
"No bots match your current filter.": "ඔබගේ වත්මන් පෙරහනට ස්වයංක්‍රමලේඛ නොගැලපේ.",
"No custom emoji.": "අභිරුචි ඉමොජි නැත.",
"No default streams match you current filter.": "",
"No description.": "සවිස්තරයක් නැත.",
"No drafts.": "",
"No exports.": "නිර්යාත නැත.",
"No invites match your current filter.": "ඔබගේ වත්මන් පෙරහනට කිසිදු ඇරයුමක් නොගැලපේ.",
"No language set": "භාෂා කට්ටලයක් නැත",
"No linkifiers set.": "",
"No owner": "හිමිකරු නැත",
"No playgrounds configured.": "ක්‍රීඩාපිටි වින්‍යාසගත කර නැත.",
"No restrictions": "",
"No streams": "",
"No topics match your current filter.": "ඔබගේ වත්මන් පෙරහනට මාතෘකා කිසිවක් නොගැලපේ.",
"No user to subscribe.": "",
"No users match your current filter.": "ඔබගේ වර්තමාන පෙරහනට කිසිදු පරිශීලයෙකු නොගැලපේ.",
"No. Members and admins can send invitations.": "නැහැ. සාමාජිකයින්ට හා පරිපාලකයින්ට ආරාධනා යැවිය හැකිය.",
"No. Only admins can send invitations.": "නැහැ. ආරාධනා යැවිය හැකි වන්නේ පරිපාලකයින්ට පමණි.",
"No. Only full members and admins can send invitations.": "නැහැ. ආරාධනා යැවිය හැකි වන්නේ පූර්ණ සාමාජිකයින්ට සහ පරිපාලකයින්ට පමණි.",
"No. Only moderators and admins can send invitations.": "",
"Nobody": "කිසිවෙක් නැත",
"None": "",
"Note that any bots that you maintain will be disabled.": "ඔබ නඩත්තු කරන ඕනෑම ස්වයංක්‍රමලේඛයක් අබල වන බව සලකන්න.",
"Note that organizations are limited to five exports per week.": "සංවිධාන සතියකට නිර්යාත පහකට සීමා වී ඇති බව සලකන්න.",
"Nothing to preview": "පෙරදසුන් සඳහා කිසි දෙයක් නැත",
"Notification sound": "දැනුම් දීමේ ශබ්දය",
"Notification triggers": "",
"Notifications": "දැනුම්දීම්",
"Notifications for @all/@everyone mentions": "@සියළු/@සෑමදෙනා සැඳහුම් සඳහා දැනුම්දීම්",
"November": "ඉල්",
"October": "වප්",
"Offline": "",
"Old password": "පරණ මුරපදය",
"Once you leave a private stream, you will not be able to rejoin.": "",
"Only group members and organization administrators can modify a group.": "",
"Only organization administrators and moderators can post": "",
"Only organization administrators and moderators can post.": "",
"Only organization administrators can add bots to this organization": "මෙම සංවිධානයට ස්වයංක්‍රමලේඛ එකතු කළ හැකි වන්නේ පරිපාලකයින්ට පමණි",
"Only organization administrators can add custom emoji in this organization.": "මෙම සංවිධානය තුළ අභිරුචි ඉමොජි එකතු කළ හැකි වන්නේ පරිපාලකයින්ට පමණි.",
"Only organization administrators can add generic bots": "",
"Only organization administrators can edit these settings.": "මෙම සැකසුම් සංස්කරණය කළ හැකි වන්නේ පරිපාලකයින්ට පමණි",
"Only organization administrators can modify user groups in this organization.": "",
"Only organization administrators can post": "පළ කළ හැකි වන්නේ පරිපාලකයින්ට පමණි",
"Only organization administrators can post.": "පළ කළ හැකි වන්නේ පරිපාලකයින්ට පමණි.",
"Only organization admins and moderators are allowed to post to this stream.": "",
"Only organization admins are allowed to post to this stream.": "",
"Only organization full members can post": "පළ කළ හැකි වන්නේ පූර්ණ සාමාජිකයින්ට පමණි",
"Only organization full members can post.": "පළ කළ හැකි වන්නේ පූර්ණ සාමාජිකයින්ට පමණි.",
"Only organization owners can edit these settings.": "මෙම සැකසුම් සංස්කරණය කළ හැකි වන්නේ හිමිකරුවන්ට පමණි.",
"Only owners can change message retention policy.": "පණිවිඩ රඳවා ගැනීමේ ප්‍රතිපත්තිය වෙනස් කළ හැකි වන්නේ හිමිකරුවන්ට පමණි.",
"Only owners can change stream message retention policy.": "",
"Only owners can deactivate the organization.": "",
"Only stream members can add users to a private stream": "",
"Optional": "",
"Or, to automatically linkify GitHub's <code>org/repo#1234</code> syntax:": "",
"Organization": "සංවිධානය",
"Organization administrators can change this in the organization settings.": "සංවිධානයේ සැකසුම් තුළ සංවිධාන පරිපාලකයින්ට මෙය වෙනස් කළ හැකිය.",
"Organization administrators can reactivate deactivated users.": "",
"Organization description": "සංවිධානයේ සවිස්තරය",
"Organization logo": "සංවිධානයේ ලාංඡනය",
"Organization name": "සංවිධානයේ නම",
"Organization profile": "සංවිධානයේ පැතිකඩ",
"Organization profile picture": "",
"Organization settings": "සංවිධානයේ සැකසුම්",
"Organization using {percent_used}% of {upload_quota}.": "සංවිධානය {upload_quota} න් {percent_used}% ක් භාවිතා කරයි.",
"Other notification settings": "වෙනත් දැනුම්දීම් සැකසුම්",
"Other permissions": "වෙනත් අවසර",
"Other settings": "වෙනත් සැකසුම්",
"Outgoing webhook message format": "",
"Override unicode emoji?": "",
"Owner": "හිමිකරු",
"Owner: {name}": "හිමිකරු: {name}",
"PMs, mentions, and alerts": "පෞ.ප., සැඳහුම් හා අනතුරු ඇඟවීම්",
"Participants": "සහභාගිවන්නන්",
"Participated": "සහභාගී වූහ",
"Password": "මුරපදය",
"Password is too weak": "මුරපදය ඉතා දුර්වල ය",
"Password should be at least {length} characters long": "මුරපදය අවම වශයෙන් අකුරු {length} ක් දිග විය යුතුය",
"Pattern": "රටාව",
"People to add": "එකතු කිරීමට මිනිසුන්",
"Personal settings": "පෞද්ගලික සැකසුම්",
"Pin stream to top": "",
"Pin stream to top of left sidebar": "",
"Play sound": "ශබ්දය වාදනය",
"Please just upload one file.": "කරුණාකර එක් ගොනුවක් උඩුගත කරන්න.",
"Please re-enter your password to confirm your identity.": "",
"Please specify a date or time": "",
"Please specify a stream": "",
"Please specify a topic": "",
"Please specify at least one valid recipient": "",
"Presence": "",
"Press > for list of topics": "",
"Press Enter to send": "යැවීමට ඇතුල්කරන්න ඔබන්න",
"Prevent users from changing their avatar": "",
"Prevent users from changing their email address": "පරිශීලකයින් ඔවුන්ගේ වි-තැපැල් ලිපිනය වෙනස් කිරීම වලක්වන්න",
"Prevent users from changing their name": "පරිශීලකයින් තම නම වෙනස් කිරීම වලක්වන්න",
"Preview": "පෙරදසුන",
"Preview organization profile": "සංවිධානයේ පැතිකඩ පෙරදසුන",
"Preview profile": "පැතිකඩ පෙරදසුන",
"Private messages": "පෞද්ගලික පණිවිඩ",
"Private messages and mentions": "පෞද්ගලික පණිවිඩ සහ සැඳහුම්",
"Private messages are disabled in this organization.": "මෙම සංවිධානය තුළ පෞද්ගලික පණිවිඩ අබල කර ඇත.",
"Private messages disabled": "පෞද්ගලික පණිවිඩ අබල කර ඇත",
"Private, protected history": "පෞද්ගලික, ආරක්‍ෂිත ඉතිහාසය",
"Private, shared history": "පෞද්ගලික, හවුල් ඉතිහාසය",
"Pro tip: You can use 'd' to open your drafts.": "",
"Profile": "පැතිකඩ",
"Profile picture": "පැතිකඩ පින්තූරය",
"Public": "ප්‍රසිද්ධ",
"Public stream messages in organization": "",
"Quote and reply or forward": "",
"Reactivate": "",
"Reactivate bot": "",
"Receives new stream notifications": "",
"Recent topics": "මෑත මාතෘකා",
"Remind me about this": "මේ ගැන මට මතක් කරන්න",
"Reminder not set!": "සිහිකැඳවීම සකසා නැත!",
"Reminder set!": "සිහිකැඳවීම සකස් කෙරිණි!",
"Remove": "ඉවත් කරන්න",
"Remove from default": "පෙරනිමියෙන් ඉවත් කරන්න",
"Reply": "පිළිතුරු",
"Reply mentioning user": "පරිශීලකයා සඳහන් කරමින් පිළිතුරු දෙන්න",
"Requesting user": "පරිශීලකයාගෙන් ඉල්ලීම",
"Require topics in stream messages": "",
"Resend": "නැවත යවන්න",
"Resend invitation to <z-email></z-email>": "<z-email></z-email> වෙත යළි ආරාධනාවක් යවන්න",
"Resending encountered an error. Please reload and try again.": "",
"Restore draft": "",
"Restrict email domains of new users?": "",
"Restrict to a list of domains": "",
"Retain for N days after posting": "",
"Retain forever": "සදහටම රඳවා ගන්න",
"Retry": "නැවත උත්සාහ කරන්න",
"Revoke": "",
"Revoke invitation link": "",
"Revoke invitation to {email}": "",
"Role": "",
"SAVING": "සුරැකෙමින්",
"Saturday": "සෙනසුරාදා",
"Save": "සුරකින්න",
"Save changes": "වෙනස්කම් සුරකින්න",
"Save failed": "සුරැකීමට අසමත් විය",
"Saved": "සුරැකිණි",
"Saved as draft": "",
"Saved. Please <z-link>reload</z-link> for the change to take effect.": "",
"Saving": "සුරැකෙමින්",
"Scheduling...": "",
"Search": "සොයන්න",
"Search GIFs": "චලනරූ සොයන්න",
"Search operators": "ක්‍රියාකරුවන් සොයන්න",
"Search results": "සෙවුම් ප්‍රතිඵල",
"Search subscribers": "",
"Select a stream below or change topic name.": "",
"Select date and time": "",
"Select default language": "",
"Send": "යවන්න",
"Send digest emails when I'm away": "",
"Send email notifications for new logins to my account": "මාගේ ගිණුමට නව ඇතුල් වීම් ගැන වි-තැපැල් දැනුම්දීම් යවන්න",
"Send emails introducing Zulip to new users": "නව පරිශීලකයින්ට සුලිප් හඳුන්වා දෙන වි-තැපැල් යවන්න",
"Send me occasional marketing emails about Zulip (a few times a year)": "සුලිප් ගැන මට ඉඳහිට අලෙවිකරණ වි-තැපැල් එවන්න. (වසරකට කිහිප වතාවක්)",
"Send mobile notifications even if I'm online (useful for testing)": "",
"Send notification to new topic": "නව මාතෘකාවට දැනුම් දීමක් යවන්න",
"Send notification to old topic": "පරණ මාතෘකාවට දැනුම් දීමක් යවන්න",
"Send private message": "පෞද්ගලික පණිවිඩය යවන්න",
"Send test notification": "අත්හදාබැලීමේ නිවේදනය යවන්න",
"Send weekly digest emails to inactive users": "",
"Sending...": "යැවෙමින්...",
"Sent!": "යැවිණි!",
"Sent! Scroll down to view your message.": "",
"Sent! Your message is outside your current narrow.": "",
"Sent! Your message was sent to a stream you have muted.": "",
"Sent! Your message was sent to a topic you have muted.": "යැවිණි! ඔබ නිහඬ කළ මාතෘකාවකට ඔබගේ පණිවිඩය යවන ලදි.",
"Sent! Your recent message is outside the current search.": "යැවිණි! ඔබගේ මෑත පණිවිඩය වත්මන් සෙවුමෙන් පිටත ය.",
"September": "බිනර",
"Set a status message": "තත්ව පණිවිඩයක් සකසන්න",
"Set up two factor authentication": "",
"Set yourself as active": "",
"Set yourself as unavailable": "",
"Settings": "සැකසුම්",
"Setup": "පිහිටුවීම",
"Several people are typing\u2026": "",
"Show API key": "යෙ.ක්‍ර.මු. යතුර පෙන්වන්න",
"Show counts for starred messages": "",
"Show fewer": "වඩා අඩුවෙන් පෙන්වන්න",
"Show more": "තව පෙන්වන්න",
"Show password": "මුරපදය පෙන්වන්න",
"Show previews of linked websites": "",
"Show previews of uploaded and linked images": "",
"Show starred message count": "තරු ලකුණු කළ පණිවිඩ ගණන පෙන්වන්න",
"Show user list on left sidebar in narrow windows": "",
"Show/change your API key": "ඔබගේ යෙ.ක්‍ර.මු. යතුර පෙන්වන්න/වෙනස් කරන්න",
"Size": "ප්‍රමාණය",
"Slack compatible": "",
"Slack's outgoing webhooks": "",
"Some common words were excluded from your search.": "ඔබගේ සෙවුමෙන් සමහර සුලබ වචන බැහැර කර ඇත.",
"Sort by estimated weekly traffic": "",
"Sort by name": "",
"Sort by number of subscribers": "",
"Spoiler": "",
"Star": "තරුව",
"Starred messages": "තරු ලකුණු කළ පණිවිඩ",
"Start public export": "ප්‍රසිද්ධ නිර්යාතය අරඹන්න",
"Status": "තත්ත්වය",
"Stream": "",
"Stream color": "",
"Stream created recently": "",
"Stream creation": "",
"Stream description": "",
"Stream membership": "",
"Stream name": "",
"Stream permissions": "",
"Stream settings": "",
"Stream successfully created!": "",
"Streams": "",
"Subscribe": "",
"Subscribed": "",
"Subscriber count": "",
"Subscribers": "",
"Successfully subscribed users:": "",
"Sunday": "ඉරිදා",
"System bot": "පද්ධතියේ ස්වයංක්‍රමලේඛය",
"Task already exists": "කාර්යය දැනටමත් පවතී",
"The recipient {recipient} is not valid": "{recipient} ලබන්නා වලංගු නොවේ",
"The recipients {recipients} are not valid": "{recipients} ලබන්නන් වලංගු නොවේ",
"The stream description cannot contain newline characters.": "",
"The stream description has been updated!": "",
"The stream has been renamed!": "",
"Their password will be cleared from our systems, and any bots they maintain will be disabled.": "ඔවුන්ගේ මුරපදය අපගේ පද්ධතියෙන් හිස් කෙරෙන අතර, ඔවුන් නඩත්තු කරන ඕනෑම ස්වයංක්‍රමලේඛයක් අබල වෙනු ඇත.",
"There are no current alert words.": "දැනට අනතුරු ඇඟවීමේ වචන නැත.",
"These settings are explained in detail in the <z-link>help center</z-link>.": "<z-link>උපකාරක මධ්යස්ථානය</z-link> හි මෙම සැකසුම් විස්තරාත්මකව දක්වා ඇත.",
"This action is permanent and cannot be undone. All users will permanently lose access to their Zulip accounts.": "මෙම ක්‍රියාමාර්ගය නිත්‍ය වන අතර එය ආපසු හැරවිය නොහැකිය. සියළුම පරිශීලකයින්ට ඔවුන්ගේ සුලිප් ගිණුම් වෙත ප්‍රවේශය සදහටම අහිමි වේ.",
"This file exceeds maximum allowed size of": "මෙම ගොනුව ඉඩ දී ඇති උපරිම ප්‍රමාණය ඉක්මවයි",
"This is a <z-icon></z-icon> <b>private stream</b>. Only people who have been invited can access its content, but any member of the stream can invite others.": "",
"This is a <z-icon></z-icon> <b>public stream</b>. Any member of the organization can join without an invitation.": "",
"This is a <z-icon></z-icon> <b>web public stream</b>. Any member of the organization can join without an invitation and anyone on the internet can read the content published.": "",
"This is a private stream": "",
"This is what a Zulip notification looks like.": "සුලිප් නිවේදනයක පෙනුම මෙවැනි ය.",
"This message was hidden because you have muted the sender.": "ඔබ එවන්නා නිහඬ කර ඇති නිසා මෙම පණිවිඩය සඟවා ඇත.",
"This organization is configured to restrict editing of message content to {minutes_to_edit} minutes after it is sent.": "මෙම සංවිධානය වින්‍යාසගත කර ඇත්තේ පණිවිඩය යැවීමෙන් පසු අන්තර්ගතය සංස්කරණය විනාඩි {minutes_to_edit} කට සීමා කිරීමට ය.",
"This stream does not exist or is private.": "",
"This stream has been deactivated": "",
"This stream is reserved for <strong>announcements</strong>. <br /> Are you sure you want to message all <strong>{count}</strong> people in this stream?": "",
"Thursday": "බ්‍රහස්පතින්දා",
"Time": "කාලය",
"Time format": "",
"Time settings": "කාල සැකසුම්",
"Time zone": "වේලා කලාපය",
"Time's up!": "කාලය ඉවරයි!",
"Tip: You can also send \"/poll Some question\"": "",
"To": "වෙත",
"To add syntax highlighting to a multi-line code block, add the language's <b>first</b> <z-link>Pygments short name</z-link> after the first set of back-ticks. You can also make a code block by indenting each line with 4 spaces.": "",
"Today": "අද",
"Toggle subscription": "",
"Tomorrow": "හෙට",
"Topic": "මාතෘකාව",
"Topic editing only": "මාතෘකා සංස්කරණය කිරීම පමණි",
"Topic muted": "මාතෘකාව නිහඬ කෙරිණි",
"Try again": "නැවත උත්සහා කරන්න",
"Tuesday": "අඟහරුවාදා",
"Two factor authentication": "",
"Type": "වර්ගය",
"URL format string": "",
"URL pattern": "ඒ.ස.නි. රටාව",
"URL prefix": "ඒ.ස.නි. උපසර්ගය",
"Un-collapse": "",
"Unavailable": "",
"Uncheck all": "",
"Unknown": "නොදන්නා",
"Unknown stream": "",
"Unmute": "",
"Unmute stream": "",
"Unmute the topic <b>{topic}</b>": "",
"Unmute this user": "",
"Unmute topic": "",
"Unpin stream from top": "",
"Unread": "නොකියවූ",
"Unread count summary (appears in desktop sidebar and browser tab)": "",
"Unstar": "",
"Unstar all messages": "",
"Unstar all messages in topic": "",
"Unstar messages in topic": "",
"Unsubscribe": "",
"Unsubscribe from {stream_name}": "",
"Unsubscribed successfully!": "",
"Up to N minutes after posting": "",
"Up to {time_limit} after posting": "පළ කිරීමෙන් පසු {time_limit} දක්වා",
"Update successful: Subdomains allowed for {domain}": "යාවත්කාල කිරීම සාර්ථකයි: {domain} සඳහා උප වසම් වලට ඉඩ දුණි",
"Update successful: Subdomains no longer allowed for {domain}": "යාවත්කාල කිරීම සාර්ථකයි: තවදුරටත් {domain} සඳහා උපවසම් වලට ඉඩ නොදේ",
"Upload files": "ගොනු උඩුගත කරන්න",
"Upload icon": "උඩුගත නිරූපකය",
"Upload image or GIF": "",
"Upload logo": "ලාංඡනය උඩුගත කරන්න",
"Upload new profile picture": "",
"Upload profile picture": "පැතිකඩ පින්තූරය උඩුගත කරන්න",
"Upload stalled for %'{seconds}' seconds, aborting.": "",
"Uploading {filename}\u2026": "{filename} උඩුගත වෙමින්…",
"Uploading\u2026": "උඩුගත වෙමින්…",
"Use full width on wide screens": "පුළුල් තිර මත පූර්ණ පළල භාවිතා කරන්න",
"Use organization level settings": "සංවිධාන මට්ටමේ සැකසුම් භාවිතා කරන්න",
"User": "පරිශීලක",
"User group added!": "පරිශීලක සමූහය එකතු කෙරිණි!",
"User groups allow you to <z-link>mention</z-link> multiple users at once. When you mention a user group, everyone in the group is notified as if they were individually mentioned.": "පරිශීලක සමූහය ඔබට එකවර පරිශීලකයින් කිහිපයක් <z-link>සැඳහුම</z-link> ට ඉඩ දෙයි. ඔබ පරිශීලක සමූහයක් සඳහන් කළ විට සමූහයේ සෑම දෙනාම තනි තනිව සඳහන් කළා ක් මෙන් දැනුම් දෙනු ඇත.",
"User identity": "පරිශීලක අනන්යතාව",
"User is already not subscribed.": "",
"User is deactivated": "",
"User role": "පරිශීලක භූමිකාව",
"User settings": "පරිශීලක සැකසුම්",
"User will not be notified": "පරිශීලකයාට දැනුම් නොදෙනු ඇත",
"User(s) invited successfully.": "",
"Users": "පරිශීලකයින්",
"Users can edit the topic of any message": "පරිශීලකයින්ට ඕනෑම පණිවිඩයක මාතෘකාව සංස්කරණය කළ හැකිය",
"Video call provider": "",
"View edit history": "",
"View file": "",
"View full profile": "",
"View in playground": "",
"View in {name}": "",
"View in {playground_name}": "",
"View messages sent": "",
"View private messages": "",
"View private messages to myself": "",
"View source": "",
"View source / Edit topic": "",
"View stream": "",
"View user profile": "",
"View your profile": "",
"Visual": "",
"Visual desktop notifications": "",
"Waiting period (days)": "පොරොත්තු කාලය (දවස්)",
"Waiting period before new members turn into full members": "නව සාමාජිකයින් පූර්ණ සාමාජිකයින් බවට පත්වීමට පෙර පොරොත්තු කාලය",
"Warning: <strong>{stream_name}</strong> is a private stream.": "",
"We are about to have a poll. Please wait for the question.": "අපි මත විමසුමක් පැවැත්වීමට ආසන්නයි. කරුණාකර ප්‍රශ්නය සඳහා රැඳී සිටින්න.",
"We've replaced the \"{originalHotkey}\" hotkey with \"{replacementHotkey}\" to make this common shortcut easier to trigger.": "",
"Wednesday": "බදාදා",
"Week of {date}": "",
"Whether wildcard mentions like @all are treated as mentions for the purpose of notifications.": "",
"Who can access the stream?": "",
"Who can access user email addresses": "පරිශීලක වි-තැපැල් ලිපින වලට ප්‍රවේශ විය හැකි වන්නේ කාටද?",
"Who can add bots": "කාටද ස්වයංක්‍රමලේඛ එකතු කළ හැකි වන්නේ",
"Who can add custom emoji": "අභිරුචි ඉමොජි එකතු කළ හැකි වන්නේ කාටද",
"Who can add users to streams": "",
"Who can create and manage user groups": "පරිශීලක සමූහ සෑදීමට සහ කළමනාකරණය කිරීමට හැකි වන්නේ කාටද",
"Who can create streams": "",
"Who can move messages between streams": "",
"Who can post to the stream?": "",
"Who can use @all/@everyone mentions in large streams": "",
"Who can use private messages": "පෞද්ගලික පණිවිඩ භාවිතා කළ හැකි වන්නේ කාටද",
"Widgets cannot be edited.": "",
"Word": "වචනය",
"Working\u2026": "වැඩ කරමින්…",
"Write": "ලියන්න",
"Yes": "ඔව්",
"Yes, send": "ඔව්, යවන්න",
"Yes, subscribe {count} users!": "",
"Yes. Members and admins can send invitations.": "ඔව්. සාමාජිකයින්ට සහ පරිපාලකයින්ට ආරාධනා යැවිය හැකිය.",
"Yes. Only admins can send invitations.": "ඔව්. ආරාධනා යැවිය හැකි වන්නේ පරිපාලකයින්ට පමණි.",
"Yes. Only full members and admins can send invitations.": "ඔව්. ආරාධනා යැවිය හැකි වන්නේ පූර්ණ සාමාජිකයින්ට සහ පරිපාලකයින්ට පමණි.",
"Yes. Only moderators and admins can send invitations.": "",
"Yesterday": "ඊයේ",
"You (click to remove) and {other_username} reacted with {emoji_name}": "",
"You (click to remove) reacted with {emoji_name}": "",
"You (click to remove), {comma_separated_usernames} and {last_username} reacted with {emoji_name}": "",
"You and": "ඔබ සහ",
"You and {display_reply_to}": "ඔබ සහ {display_reply_to}",
"You and {recipients}": "ඔබ සහ {recipients}",
"You are not currently subscribed to this stream.": "",
"You are not subscribed to stream {stream}": "",
"You are searching for messages that are sent by more than one person, which is not possible.": "ඔබ සොයන්නේ නොහැකි දෙයක් වන එක් පුද්ගලයෙකුට වඩා වැඩි දෙනෙක් යැවූ පණිවිඩයි",
"You are searching for messages that belong to more than one stream, which is not possible.": "",
"You are searching for messages that belong to more than one topic, which is not possible.": "ඔබ සොයන්නේ නොහැකි දෙයක් වන මාතෘකා එකකට වඩා වැඩි ගණනකට අයත් පණිවිඩ ය.",
"You can also make <z-link>tables</z-link> with this <z-link>Markdown-ish table syntax</z-link>.": "",
"You can reactivate deactivated users from <z-link>organization settings</z-link>.": "",
"You cannot create a stream with no subscribers!": "",
"You do not have permission to use wildcard mentions in this stream.": "",
"You get": "ඔබට ලැබේ",
"You have muted <z-stream-topic></z-stream-topic>.": "ඔබ <z-stream-topic></z-stream-topic> නිහඬ කර ඇත.",
"You have no active bots.": "",
"You have no inactive bots.": "",
"You have not muted any topics yet.": "ඔබ තවමත් මාතෘකා කිසිවක් නිහඬ කර නැත.",
"You have not muted any users yet.": "ඔබ තවමත් කිසිදු පරිශීලකයෙකු නිහඬ කර නැත.",
"You have not uploaded any files.": "ඔබ කිසිදු ගොනුවක් උඩුගත කර නැත.",
"You have nothing to send!": "ඔබට යැවීමට කිසිවක් නැත!",
"You must be an organization administrator to create a stream without subscribing.": "",
"You need to be running Zephyr mirroring in order to send messages!": "",
"You searched for:": "ඔබ සෙව්වේ:",
"You subscribed to stream {stream}": "",
"You type": "",
"You unsubscribed from stream {stream}": "",
"You're not subscribed to this stream. You will not be notified if other users reply to your message.": "",
"Your API key:": "ඔබගේ යෙ.ක්‍ර.මු. යතුර:",
"Your reminder note is empty!": "ඔබගේ සිහිකැඳවීමේ සටහන හිස් ය!",
"Zulip's translations are contributed by our amazing community of volunteer translators. If you'd like to help, see the <z-link>Zulip translation guidelines</z-link>.": "සුලිප් පරිවර්තන සඳහා දායක වී ඇත්තේ අපගේ විශ්මය ජනක ස්වේච්ඡා පරිවර්තක ප්‍රජාව විසිනි. ඔබට උදව් කිරීමට අවශ්‍ය නම්, <z-link>සුලිප් පරිවර්තන මාර්ගෝපදේශය</z-link> බලන්න.",
"[Condense message]": "[සංෂිප්ත පණිවිඩය]",
"[Configure]": "[වින්‍යාසගත කරන්න]",
"[Disable]": "[අබල කරන්න]",
"[More\u2026]": "[තව…]",
"[Remove owner]": "[හිමිකරු ඉවත් කරන්න]",
"[Unset]": "[සකසා නැත]",
"and": "හා",
"beta": "බීටා",
"clear": "",
"cookie": "",
"group private messages with {recipient}": "{recipient} සමඟ පෞද්ගලික පණිවිඩ සමූහගත කරන්න",
"in 1 hour": "පැය 1 කින්",
"in 20 minutes": "විනාඩි 20 කින්",
"in 3 hours": "පැය 3 කින්",
"leafy green vegetable": "කොළ පැහැති එලවළු",
"marketing": "අලෙවිකරණය",
"more topics": "තවත් මාතෘකා",
"private messages with yourself": "ඔබ සමඟ පෞද්ගලික පණිවිඩ",
"private messages with {recipient}": "{recipient} සමඟ පෞද්ගලික පණිවිඩ",
"{comma_separated_usernames} and {last_username} reacted with {emoji_name}": "",
"{count} users are subscribed to #{title}": "",
"{days_old} days ago": "දවස් {days_old} කට පෙර",
"{full_name} is typing\u2026": "",
"{hours} hours ago": "පැය {hours} කට පෙර",
"{last_active_date}": "{last_active_date}",
"{minutes} min to edit": "සංස්කරණයට විනාඩි {minutes}",
"{minutes} minutes ago": "විනාඩි {minutes} කට පෙර",
"{seconds} sec to edit": "සංස්කරණයට තත්පර {seconds}",
"{starred_status} this message": "මෙම පණිවිඩය {starred_status}",
"{starred_status} this message (Ctrl + s)": "",
"{username} [said]({link_to_message}):": "",
"{username} reacted with {emoji_name}": "",
"{wildcard_mention_token} (Notify stream)": ""
}

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Zulip\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-23 19:14+0000\n"
"POT-Creation-Date: 2021-11-30 22:59+0000\n"
"PO-Revision-Date: 2018-04-11 21:06+0000\n"
"Last-Translator: Tim Abbott <tabbott@kandralabs.com>\n"
"Language-Team: Tamil (http://www.transifex.com/zulip/zulip/language/ta/)\n"
@@ -1428,7 +1428,7 @@ msgstr ""
msgid "Organization permissions"
msgstr ""
#: templates/zerver/app/settings_overlay.html:76 zerver/models.py:2390
#: templates/zerver/app/settings_overlay.html:76 zerver/models.py:2391
msgid "Custom emoji"
msgstr ""
@@ -3863,7 +3863,7 @@ msgstr ""
msgid "Message must have recipients"
msgstr ""
#: zerver/lib/addressee.py:154 zerver/lib/outgoing_webhook.py:193
#: zerver/lib/addressee.py:154 zerver/lib/outgoing_webhook.py:215
msgid "Invalid message type"
msgstr ""
@@ -3979,7 +3979,7 @@ msgstr ""
msgid "Must be an organization administrator or emoji author"
msgstr ""
#: zerver/lib/emoji.py:117 zerver/models.py:840
#: zerver/lib/emoji.py:117 zerver/models.py:841
msgid "Invalid characters in emoji name"
msgstr ""
@@ -4307,11 +4307,11 @@ msgid ""
"a previous message."
msgstr ""
#: zerver/lib/outgoing_webhook.py:293
#: zerver/lib/outgoing_webhook.py:315
msgid "Invalid JSON in response"
msgstr ""
#: zerver/lib/outgoing_webhook.py:302
#: zerver/lib/outgoing_webhook.py:324
#, fuzzy
#| msgid "Enable notifications"
msgid "Invalid response format"
@@ -4518,7 +4518,7 @@ msgstr ""
msgid "Invalid interface type"
msgstr ""
#: zerver/lib/users.py:223 zerver/lib/users.py:228 zerver/models.py:3469
#: zerver/lib/users.py:223 zerver/lib/users.py:228 zerver/models.py:3470
#, fuzzy
#| msgid "Enable notifications"
msgid "Invalid user ID: {}"
@@ -4697,93 +4697,97 @@ msgstr ""
msgid "CSRF error: {reason}"
msgstr ""
#: zerver/models.py:372
#: zerver/models.py:373
#, fuzzy
#| msgid "Username"
msgid "stream events"
msgstr "பயனர்பெயர்"
#: zerver/models.py:406
#: zerver/models.py:407
msgid "Available on Zulip Standard. Upgrade to access."
msgstr ""
#: zerver/models.py:910
msgid "Invalid linkifier pattern. Valid characters are {}."
#: zerver/models.py:919 zerver/models.py:921
msgid "Bad regular expression: {}"
msgstr ""
#: zerver/models.py:928
#: zerver/models.py:922
msgid "Unknown regular expression error"
msgstr ""
#: zerver/models.py:929
msgid "Invalid URL format string."
msgstr ""
#: zerver/models.py:1035 zerver/views/realm_playgrounds.py:24
#: zerver/models.py:1036 zerver/views/realm_playgrounds.py:24
msgid "Invalid characters in pygments language"
msgstr ""
#: zerver/models.py:1415
#: zerver/models.py:1416
#, fuzzy
#| msgid "Administrators"
msgid "Organization owner"
msgstr "நிர்வாகிகள்"
#: zerver/models.py:1416
#: zerver/models.py:1417
#, fuzzy
#| msgid "Administrators"
msgid "Organization administrator"
msgstr "நிர்வாகிகள்"
#: zerver/models.py:1417
#: zerver/models.py:1418
msgid "Moderator"
msgstr ""
#: zerver/models.py:1418
#: zerver/models.py:1419
msgid "Member"
msgstr ""
#: zerver/models.py:1419
#: zerver/models.py:1420
msgid "Guest"
msgstr ""
#: zerver/models.py:2389
#: zerver/models.py:2390
msgid "Unicode emoji"
msgstr ""
#: zerver/models.py:2391
#: zerver/models.py:2392
msgid "Zulip extra emoji"
msgstr ""
#: zerver/models.py:3473
#: zerver/models.py:3474
msgid "User with ID {} is deactivated"
msgstr ""
#: zerver/models.py:3476
#: zerver/models.py:3477
msgid "User with ID {} is a bot"
msgstr ""
#: zerver/models.py:3509
#: zerver/models.py:3510
msgid "List of options"
msgstr ""
#: zerver/models.py:3512
#: zerver/models.py:3513
msgid "Person picker"
msgstr ""
#: zerver/models.py:3524
#: zerver/models.py:3525
msgid "Short text"
msgstr ""
#: zerver/models.py:3525
#: zerver/models.py:3526
msgid "Long text"
msgstr ""
#: zerver/models.py:3526
#: zerver/models.py:3527
msgid "Date picker"
msgstr ""
#: zerver/models.py:3527
#: zerver/models.py:3528
msgid "Link"
msgstr ""
#: zerver/models.py:3530
#: zerver/models.py:3531
msgid "External account"
msgstr ""
@@ -4799,24 +4803,24 @@ msgstr ""
msgid "An unknown browser"
msgstr ""
#: zerver/tornado/event_queue.py:632
#: zerver/tornado/event_queue.py:644
msgid "Missing 'queue_id' argument"
msgstr ""
#: zerver/tornado/event_queue.py:635
#: zerver/tornado/event_queue.py:647
msgid "Missing 'last_event_id' argument"
msgstr ""
#: zerver/tornado/event_queue.py:638
#: zerver/tornado/event_queue.py:650
msgid "You are not authorized to get events from this queue"
msgstr ""
#: zerver/tornado/event_queue.py:644
#: zerver/tornado/event_queue.py:656
#, python-brace-format
msgid "An event newer than {event_id} has already been pruned!"
msgstr ""
#: zerver/tornado/event_queue.py:654
#: zerver/tornado/event_queue.py:666
#, python-brace-format
msgid "Event {event_id} was not in this queue"
msgstr ""
@@ -5530,15 +5534,15 @@ msgstr ""
msgid "Invalid data."
msgstr ""
#: zproject/backends.py:1922
#: zproject/backends.py:1927
msgid "Missing id_token parameter"
msgstr ""
#: zproject/backends.py:2277
#: zproject/backends.py:2282
msgid "Invalid OTP"
msgstr ""
#: zproject/backends.py:2280
#: zproject/backends.py:2285
msgid "Can't use both mobile_flow_otp and desktop_flow_otp together."
msgstr ""

View File

@@ -9,8 +9,8 @@
# Cihan Alkan <cihanalk@gmail.com>, 2021
# Gokdeniz.Kucukali, 2021
# Hüseyin Fahri Uzun <mail@fahriuzun.com>, 2021
# Oktay Altunergil, 2021
# Tim Abbott <tabbott@kandralabs.com>, 2021
# Oktay Altunergil, 2021
#
#, fuzzy
msgid ""
@@ -19,7 +19,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-05-26 18:04+0000\n"
"PO-Revision-Date: 2021-04-02 21:39+0000\n"
"Last-Translator: Tim Abbott <tabbott@kandralabs.com>, 2021\n"
"Last-Translator: Oktay Altunergil, 2021\n"
"Language-Team: Turkish (https://www.transifex.com/zulip/teams/53893/tr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -664,7 +664,7 @@ msgstr ""
#: templates/zerver/app/index.html:68
msgid "Retrying soon..."
msgstr "Yakıda yeniden deniyor..."
msgstr "Az sonra yeniden denenecek..."
#: templates/zerver/app/index.html:68
msgid "Try now."
@@ -836,7 +836,7 @@ msgstr "Navigasyon"
#: templates/zerver/app/left_sidebar.html:64
#: templates/zerver/app/left_sidebar.html:66
msgid "Filter streams"
msgstr "Kanalları filtrele"
msgstr "Kanalları süz"
#: templates/zerver/app/keyboard_shortcuts.html:77
#: templates/zerver/app/right_sidebar.html:9
@@ -967,7 +967,7 @@ msgstr "Son konuları görüntüle"
#: templates/zerver/app/keyboard_shortcuts.html:251
msgid "Filter topics"
msgstr "Konuları filtrele"
msgstr "Konuları süz"
#: templates/zerver/app/keyboard_shortcuts.html:260
msgid "Drafts"
@@ -1287,7 +1287,7 @@ msgstr ""
#: templates/zerver/app/right_sidebar.html:5
#: templates/zerver/app/right_sidebar.html:6
msgid "Filter users"
msgstr "Kullanıcıları filtrele"
msgstr "Kullanıcıları süz"
#: templates/zerver/app/right_sidebar.html:5
msgid "USERS"
@@ -3175,7 +3175,7 @@ msgstr "Arama entegrasyonları"
#: templates/zerver/integrations/index.html:48
msgid "Filter by category"
msgstr "Kategorilere göre filtrele"
msgstr "Kategorilere göre süz"
#: templates/zerver/integrations/index.html:62
#: templates/zerver/integrations/index.html:89

View File

@@ -315,22 +315,22 @@
"File and image uploads have been disabled for this organization.": "Dosya ve resim yüklemeleri bu organizasyon için iptal edilmiştir.",
"File size must be at most {max_file_size} MiB.": "Dosya boyutu en fazla {max_file_size} MiB olmalıdır.",
"File type is not supported.": "Dosya türü desteklenmiyor.",
"Filter": "Filtre",
"Filter bots": "Filtreleme botları",
"Filter by category": "Kategorilere göre filtrele",
"Filter code playgrounds": "Kod oyun alanlarını filtrele",
"Filter": "Süz",
"Filter bots": "Süzme botları",
"Filter by category": "Kategorilere göre süz",
"Filter code playgrounds": "Kod oyun alanlarını süz",
"Filter deactivated users": "Devre dışı bırakılmış kullanıcıları ayır",
"Filter emojis": "Emojlleri filtrele",
"Filter exports": "Dışa aktarımları filtrele",
"Filter invites": "Davetiyeleri filtrele",
"Filter languages": "Dilleri filtrele",
"Filter linkifiers": "Linkifierları filtrele",
"Filter muted topics": "Sessiz konuları filtrele",
"Filter muted users": "Sesi kapatılmış kullanıcıları filtrele",
"Filter streams": "Kanalları filtrele",
"Filter topics (t)": "Konuları filtrele (t)",
"Filter uploads": "Yüklemeleri filtrele",
"Filter users": "Kullanıcıları filtrele",
"Filter emojis": "Emojlleri süz",
"Filter exports": "Dışa aktarımları süz",
"Filter invites": "Davetiyeleri süz",
"Filter languages": "Dilleri süz",
"Filter linkifiers": "Linkifierları süz",
"Filter muted topics": "Sessiz konuları süz",
"Filter muted users": "Sesi kapatılmış kullanıcıları süz",
"Filter streams": "Kanalları süz",
"Filter topics (t)": "Konuları süz (t)",
"Filter uploads": "Yüklemeleri süz",
"Filter users": "Kullanıcıları süz",
"First time? Read our <z-link>guidelines</z-link> for creating and naming streams.": "İlk kullanışınız mı? Kanal oluşturmak ve adlandırmak için<z-link> yönergeleri</z-link>.",
"Fixed width": "Sabitlenmiş genişlik",
"Fixed width mode": "Sabit genişlik modu",
@@ -479,22 +479,22 @@
"Night": "Gece",
"Night logo": "Gece logosu",
"Night mode": "Gece modu",
"No bots match your current filter.": "Şu anki filtrenizle eşleşen bot bulunmuyor.",
"No bots match your current filter.": "Şu anki süzmeyle eşleşen bot bulunmuyor.",
"No custom emoji.": "Özel emoji yok",
"No default streams match you current filter.": "Şu anki filtrenizle eşleşen varsayılan kanal bulunmuyor.",
"No default streams match you current filter.": "Şu anki süzmeyle eşleşen varsayılan kanal bulunmuyor.",
"No description.": "Tanım yok.",
"No drafts.": "Taslak yok.",
"No exports.": "Dışa aktarım yok",
"No invites match your current filter.": "Şu anki filtrenizle eşleşen davetiye bulunmuyor.",
"No invites match your current filter.": "Şu anki süzmeyle eşleşen davetiye bulunmuyor.",
"No language set": "Dil ayarlanmadı",
"No linkifiers set.": "Linkifier oluşturulmadı.",
"No owner": "Sahipsiz",
"No playgrounds configured.": "Yapılandırılmış oyun alanı yok.",
"No restrictions": "Kısıtlama yok",
"No streams": "Kanal yok",
"No topics match your current filter.": "Şuanki filtre ile eşleşen konu yok.",
"No topics match your current filter.": "Şuanki süzme ile eşleşen konu yok.",
"No user to subscribe.": "Abone olacak kullanıcı yok",
"No users match your current filter.": "Şu anki filtrenizle eşleşen kullanıcı bulunmuyor.",
"No users match your current filter.": "Şu anki süzmeyle eşleşen kullanıcı bulunmuyor.",
"No. Members and admins can send invitations.": "Hayır. Üyeler ve yöneticiler davetiye gönderebilir.",
"No. Only admins can send invitations.": "Hayır. Yalnızca yöneticiler davetiye gönderebilir.",
"No. Only full members and admins can send invitations.": "Hayır. Yalnızca tam üyeler ve yöneticiler davetiye gönderebilir.",

View File

@@ -38,6 +38,7 @@ Listen 127.0.0.1:8888
WSGIScriptAlias / /home/zulip/deployments/current/zproject/wsgi.py
WSGIDaemonProcess zulip threads=5 user=zulip python-path=/home/zulip/deployments/current/
WSGIProcessGroup zulip
WSGIApplicationGroup %{GLOBAL}
ErrorLog ${APACHE_LOG_DIR}/zulip_auth_error.log

View File

@@ -1,6 +0,0 @@
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
USER=root
# Cron job to renew certbot twice a day.
52 0,12 * * * root /home/zulip/deployments/current/scripts/lib/certbot-maybe-renew

View File

@@ -0,0 +1,3 @@
#!/usr/bin/env bash
service nginx reload

View File

@@ -3,12 +3,8 @@
# combination. See the clustering on a single machine guide for details:
# http://www.rabbitmq.com/clustering.html#single-machine
#
# By default, we set nodename to rabbit@localhost so it will always resolve
<% if @rabbitmq_nodename != '' -%>
NODENAME=<%= @rabbitmq_nodename %>
<% else -%>
NODENAME=rabbit
<% end -%>
# By default, we set nodename to zulip@localhost so it will always resolve
NODENAME=zulip@localhost
# By default RabbitMQ will bind to all interfaces, on IPv4 and IPv6 if
# available. Set this if you only want to bind to one network interface or#

View File

@@ -1,11 +0,0 @@
# This file is sourced by /etc/init.d/rabbitmq-server. Its primary
# reason for existing is to allow adjustment of system limits for the
# rabbitmq-server process.
#
# Maximum number of open file handles. This will need to be increased
# to handle many simultaneous connections. Refer to the system
# documentation for ulimit (in man bash) for more information.
#
#ulimit -n 1024
export ERL_EPMD_ADDRESS=127.0.0.1

View File

@@ -1,4 +1,2 @@
[{kernel, [{inet_dist_use_interface, {127,0,0,1}}]},
{rabbit, [{tcp_listeners, [{"127.0.0.1", 5672}]}]},
{rabbitmq_mochiweb, [{listeners, [{mgmt, [{ip, "127.0.0.1"},
{port, 55672}]}]}]}].
{rabbit, [{tcp_listeners, [{"127.0.0.1", 5672}]}]}].

View File

@@ -0,0 +1,25 @@
#!/usr/bin/env bash
set -eu
for arg in "$@"; do
if [ "$arg" == "--" ]; then
shift
exec "$@"
elif [[ "$arg" == *"="* ]]; then
shift
varname="${arg%%=*}"
secretname="${arg#*=}"
secret=$(crudini --get /etc/zulip/zulip-secrets.conf secrets "$secretname")
export "$varname"="$secret"
else
exec "$@"
fi
done
{
echo "Usage:"
echo " secret-env-wrapper ENVNAME=secretname binary [argument [argument [...]]]"
} >&2
exit 1

View File

@@ -5,7 +5,12 @@ module Puppet::Parser::Functions
zulip_conf_path = lookupvar("zulip_conf_path")
output = `/usr/bin/crudini --get #{zulip_conf_path} #{joined} 2>&1`; result = $?.success?
if result
output.strip()
if [true, false].include? default
# If the default is a bool, coerce into a bool
['1','y','t','true','yes','enable','enabled'].include? output.strip.downcase
else
output.strip
end
else
default
end

View File

@@ -73,7 +73,7 @@ class zulip::app_frontend_base {
# multiprocess. Multiprocess scales much better, but requires more
# RAM; we just auto-detect based on available system RAM.
$queues_multiprocess_default = $zulip::common::total_memory_mb > 3500
$queues_multiprocess = Boolean(zulipconf('application_server', 'queue_workers_multiprocess', $queues_multiprocess_default))
$queues_multiprocess = zulipconf('application_server', 'queue_workers_multiprocess', $queues_multiprocess_default)
$queues = [
'deferred_work',
'digest_emails',
@@ -96,8 +96,14 @@ class zulip::app_frontend_base {
$uwsgi_default_processes = 4
}
$tornado_ports = $zulip::tornado_sharding::tornado_ports
$proxy_host = zulipconf('http_proxy', 'host', '')
$proxy_port = zulipconf('http_proxy', 'port', '')
$proxy_host = zulipconf('http_proxy', 'host', 'localhost')
$proxy_port = zulipconf('http_proxy', 'port', '4750')
if ($proxy_host in ['localhost', '127.0.0.1', '::1']) and ($proxy_port == '4750') {
include zulip::smokescreen
}
if $proxy_host != '' and $proxy_port != '' {
$proxy = "http://${proxy_host}:${proxy_port}"
} else {

View File

@@ -1,22 +1,57 @@
class zulip::camo {
$camo_packages = [# Needed for camo
'nodejs',
'camo',
]
package { $camo_packages: ensure => 'installed' }
class zulip::camo (String $listen_address = '0.0.0.0') {
# TODO/compatibility: Removed 2021-11 in version 5.0; these lines
# can be removed once one must have upgraded through Zulip 5.0 or
# higher to get to the next release.
package { 'camo':
ensure => 'purged',
}
$camo_key = zulipsecret('secrets', 'camo_key', '')
$version = $zulip::common::versions['go-camo']['version']
$goversion = $zulip::common::versions['go-camo']['goversion']
$dir = "/srv/zulip-go-camo-${version}"
$bin = "${dir}/bin/go-camo"
file { '/etc/default/camo':
zulip::external_dep { 'go-camo':
version => $version,
url => "https://github.com/cactus/go-camo/releases/download/v${version}/go-camo-${version}.go${goversion}.linux-${zulip::common::goarch}.tar.gz",
tarball_prefix => "go-camo-${version}",
}
# We would like to not waste resources by going through Smokescreen,
# as go-camo already prohibits private-IP access; but a
# non-Smokescreen exit proxy may be required to access the public
# Internet. The `enable_for_camo` flag, if it exists, can override
# our guess, in either direction.
$proxy_host = zulipconf('http_proxy', 'host', 'localhost')
$proxy_port = zulipconf('http_proxy', 'port', '4750')
$proxy_is_smokescreen = ($proxy_host in ['localhost', '127.0.0.1', '::1']) and ($proxy_port == '4750')
$camo_use_proxy = zulipconf('http_proxy', 'enable_for_camo', !$proxy_is_smokescreen)
if $camo_use_proxy {
if $proxy_is_smokescreen {
include zulip::smokescreen
}
if $proxy_host != '' and $proxy_port != '' {
$proxy = "http://${proxy_host}:${proxy_port}"
} else {
$proxy = ''
}
} else {
$proxy = ''
}
file { "${zulip::common::supervisor_conf_dir}/go-camo.conf":
ensure => file,
require => Package[camo],
require => [
Package['camo'],
Package[supervisor],
Zulip::External_Dep['go-camo'],
File['/usr/local/bin/secret-env-wrapper'],
],
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/camo_defaults.template.erb'),
notify => Service[camo],
}
service { 'camo':
ensure => running,
content => template('zulip/supervisor/go-camo.conf.erb'),
notify => Service[supervisor],
}
}

View File

@@ -12,6 +12,7 @@ class zulip::common {
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=877086
# "restart" is actually "stop" under sysvinit
$supervisor_reload = '/etc/init.d/supervisor restart && (/etc/init.d/supervisor start || /bin/true) && /etc/init.d/supervisor status'
$supervisor_status = '/etc/init.d/supervisor status'
}
'redhat': {
$nagios_plugins = 'nagios-plugins'
@@ -22,6 +23,7 @@ class zulip::common {
$supervisor_service = 'supervisord'
$supervisor_start = 'systemctl start supervisord'
$supervisor_reload = 'systemctl reload supervisord'
$supervisor_status = 'systemctl status supervisord'
}
default: {
fail('osfamily not supported')
@@ -30,4 +32,46 @@ class zulip::common {
$supervisor_conf_dir = "${supervisor_system_conf_dir}/zulip"
$total_memory_mb = Integer($::memorysize_mb)
$goarch = $::architecture ? {
'amd64' => 'amd64',
'aarch64' => 'arm64',
}
$versions = {
# https://github.com/cactus/go-camo/releases
'go-camo' => {
'version' => '2.3.0',
'goversion' => '1171',
'sha256' => {
'amd64' => '965506e6edb9d974c810519d71e847afb7ca69d1d01ae7d8be6d7a91de669c0c',
'aarch64' => '40463f6790eb0d2da69ad6a902fcc4c6b0c0ac24106a6c28fbfce9dfa4cb15cd',
},
},
# https://go.dev/dl/
'golang' => {
'version' => '1.17.3',
'sha256' => {
'amd64' => '550f9845451c0c94be679faf116291e7807a8d78b43149f9506c1b15eb89008c',
'aarch64' => '06f505c8d27203f78706ad04e47050b49092f1b06dc9ac4fbee4f0e4d015c8d4',
},
},
# https://github.com/stripe/smokescreen/tags
'smokescreen-src' => {
'version' => 'dc403015f563eadc556a61870c6ad327688abe88',
# Source code, so arch-invariant sha256
'sha256' => 'ad4b181d14adcd9425045152b903a343dbbcfcad3c1e7625d2c65d1d50e1959d',
},
# https://github.com/wal-g/wal-g/releases
'wal-g' => {
'version' => '1.1.1-rc',
'sha256' => {
'amd64' => 'eed4de63c2657add6e0fe70f8c0fbe62a4a54405b9bfc801b1912b6c4f2c7107',
# No aarch64 builds
},
},
}
}

View File

@@ -0,0 +1,45 @@
define zulip::external_dep(
String $version,
String $url,
String $tarball_prefix,
String $sha256 = '',
) {
if $sha256 == '' {
if $zulip::common::versions[$title]['sha256'] =~ Hash {
$sha256_filled = $zulip::common::versions[$title]['sha256'][$::architecture]
if $sha256_filled == undef {
err("No sha256 found for ${title} for architecture ${::architecture}")
fail()
}
} else {
# For things like source code which are arch-invariant
$sha256_filled = $zulip::common::versions[$title]['sha256']
}
} else {
$sha256_filled = $sha256
}
$dir = "/srv/zulip-${title}-${version}"
zulip::sha256_tarball_to { $title:
url => $url,
sha256 => $sha256_filled,
install => {
$tarball_prefix => $dir,
},
}
unless $::operatingsystem == 'Ubuntu' and $::operatingsystemrelease == '18.04' {
# Puppet 5.5.0 and below make this always-noisy, as they spout out
# a notify line about tidying the managed directory above. Skip
# on Bionic, which has that old version; they'll get tidied upon
# upgrade to 20.04.
tidy { "/srv/zulip-${title}-*":
path => '/srv/',
recurse => 1,
rmdirs => true,
matches => "zulip-${title}-*",
require => Zulip::Sha256_Tarball_To[$title],
}
}
}

View File

@@ -0,0 +1,13 @@
# @summary go compiler and tools
#
class zulip::golang {
$version = $zulip::common::versions['golang']['version']
$dir = "/srv/zulip-golang-${version}"
$bin = "${dir}/bin/go"
zulip::external_dep { 'golang':
version => $version,
url => "https://golang.org/dl/go${version}.linux-${zulip::common::goarch}.tar.gz",
tarball_prefix => 'go',
}
}

View File

@@ -1,5 +1,7 @@
class zulip::localhost_camo {
include zulip::camo
class { 'zulip::camo':
listen_address => '127.0.0.1',
}
# Install nginx configuration to run camo locally
file { '/etc/nginx/zulip-include/app.d/camo.conf':

View File

@@ -124,14 +124,9 @@ class zulip::nginx {
mode => '0644',
source => 'puppet:///modules/zulip/logrotate/nginx',
}
$certbot_auto_renew = zulipconf('certbot', 'auto_renew', '')
if $certbot_auto_renew == 'yes' {
package { 'certbot':
ensure => 'installed',
}
package { 'certbot':
ensure => 'installed',
}
file { ['/var/lib/zulip', '/var/lib/zulip/certbot-webroot']:
ensure => 'directory',
owner => 'zulip',

View File

@@ -3,17 +3,28 @@
class zulip::postgresql_backups {
include zulip::postgresql_common
$wal_g_version = '0.2.15'
zulip::sha256_tarball_to { 'wal-g':
url => "https://github.com/wal-g/wal-g/releases/download/v${wal_g_version}/wal-g.linux-amd64.tar.gz",
sha256 => 'ea33c2341d7bfb203c6948590c29834c013ab06a28c7a2b236a73d906f785c84',
install => {
'wal-g' => "/usr/local/bin/wal-g-${wal_g_version}",
},
$wal_g_version = $zulip::common::versions['wal-g']['version']
$bin = "/srv/zulip-wal-g-${wal_g_version}"
$package = "wal-g-pg-ubuntu-20.04-${zulip::common::goarch}"
# This tarball contains only a single file
zulip::external_dep { 'wal-g':
version => $wal_g_version,
url => "https://github.com/wal-g/wal-g/releases/download/v${wal_g_version}/${package}.tar.gz",
tarball_prefix => $package,
}
file { '/usr/local/bin/wal-g':
ensure => 'link',
target => "/usr/local/bin/wal-g-${wal_g_version}",
ensure => 'link',
target => $bin,
require => Zulip::External_Dep['wal-g'],
}
# We used to install versions into /usr/local/bin/wal-g-VERSION,
# until we moved to using Zulip::External_Dep which places them in
# /srv/zulip-wal-g-VERSION. Tidy old versions.
tidy { '/usr/local/bin/wal-g-*':
recurse => 1,
path => '/usr/local/bin/',
matches => 'wal-g-*',
}
file { '/usr/local/bin/env-wal-g':
ensure => file,

View File

@@ -38,13 +38,32 @@ class zulip::profile::app_frontend {
notify => Service['nginx'],
}
# Trigger 2x a day certbot renew
# We used to install a cron job, but certbot now has a systemd cron
# that does better. This can be removed once upgrading from 5.0 is
# no longer possible.
file { '/etc/cron.d/certbot-renew':
ensure => file,
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/cron.d/certbot-renew',
ensure => absent,
}
# Reload nginx after deploying a new cert.
file { ['/etc/letsencrypt/renewal-hooks', '/etc/letsencrypt/renewal-hooks/deploy']:
ensure => directory,
owner => 'root',
group => 'root',
mode => '0755',
require => Package[certbot],
}
file { '/etc/letsencrypt/renewal-hooks/deploy/001-nginx.sh':
ensure => file,
owner => 'root',
group => 'root',
mode => '0755',
source => 'puppet:///modules/zulip/letsencrypt/nginx-deploy-hook.sh',
require => Package[certbot],
}
exec { 'fix-standalone-certbot':
onlyif => 'test -d /etc/letsencrypt/renewal && grep -qx "authenticator = standalone" /etc/letsencrypt/renewal/*.conf',
command => "${::zulip_scripts_path}/lib/fix-standalone-certbot",
}
# Restart the server regularly to avoid potential memory leak problems.

View File

@@ -5,6 +5,7 @@
class zulip::profile::docker {
include zulip::profile::base
include zulip::profile::app_frontend
include zulip::localhost_camo
include zulip::supervisor
include zulip::process_fts_updates

View File

@@ -8,50 +8,56 @@ class zulip::profile::rabbitmq {
$erlang,
'rabbitmq-server',
]
package { $rabbitmq_packages: ensure => 'installed' }
# Removed 2020-09 in version 4.0; these lines can be removed in
# Zulip version 5.0 and later.
file { ['/etc/cron.d/rabbitmq-queuesize', '/etc/cron.d/rabbitmq-numconsumers']:
ensure => absent,
}
file { '/etc/default/rabbitmq-server':
ensure => file,
require => Package[rabbitmq-server],
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/rabbitmq/rabbitmq-server',
file { '/etc/rabbitmq':
ensure => 'directory',
owner => 'root',
group => 'root',
mode => '0755',
before => Package['rabbitmq-server'],
}
file { '/etc/rabbitmq/rabbitmq.config':
ensure => file,
require => Package[rabbitmq-server],
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/rabbitmq/rabbitmq.config',
ensure => file,
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/rabbitmq/rabbitmq.config',
# This config file must be installed before the package, so that
# port 25672 is not even briefly open to the Internet world, which
# would be a security risk, due to insecure defaults in the
# RabbitMQ package.
before => Package['rabbitmq-server'],
notify => Service['rabbitmq-server'],
}
$rabbitmq_nodename = zulipconf('rabbitmq', 'nodename', '')
if $rabbitmq_nodename != '' {
file { '/etc/rabbitmq':
ensure => 'directory',
owner => 'root',
group => 'root',
mode => '0755',
}
file { '/etc/rabbitmq/rabbitmq-env.conf':
ensure => file,
require => File['/etc/rabbitmq'],
before => [Package[rabbitmq-server], Service[rabbitmq-server]],
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/rabbitmq-env.conf.template.erb'),
}
exec { 'warn-rabbitmq-nodename-change':
command => "${::zulip_scripts_path}/lib/warn-rabbitmq-nodename-change",
onlyif => '[ -f /etc/rabbitmq/rabbitmq-env.conf ] && ! grep -xq NODENAME=zulip@localhost /etc/rabbitmq/rabbitmq-env.conf',
before => [
File['/etc/rabbitmq/rabbitmq-env.conf'],
Service['rabbitmq-server'],
],
logoutput => true,
loglevel => 'warning',
}
file { '/etc/rabbitmq/rabbitmq-env.conf':
ensure => file,
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/rabbitmq/rabbitmq-env.conf',
before => Package['rabbitmq-server'],
notify => [
Service['rabbitmq-server'],
Exec['configure-rabbitmq'],
],
}
package { $rabbitmq_packages:
ensure => 'installed',
}
# epmd doesn't have an init script, so we just check if it is
# running, and if it isn't, start it. Even in case of a race, this
@@ -66,10 +72,15 @@ class zulip::profile::rabbitmq {
service { 'rabbitmq-server':
ensure => running,
require => [Exec['epmd'],
File['/etc/rabbitmq/rabbitmq.config'],
File['/etc/default/rabbitmq-server']],
require => [
Exec['epmd'],
Package['rabbitmq-server'],
],
}
# TODO: Should also call exactly once "configure-rabbitmq"
exec { 'configure-rabbitmq':
command => "${::zulip_scripts_path}/setup/configure-rabbitmq",
refreshonly => true,
require => Service['rabbitmq-server'],
}
}

View File

@@ -2,62 +2,5 @@
#
class zulip::profile::smokescreen {
include zulip::profile::base
include zulip::supervisor
$golang_version = '1.16.4'
zulip::sha256_tarball_to { 'golang':
url => "https://golang.org/dl/go${golang_version}.linux-amd64.tar.gz",
sha256 => '7154e88f5a8047aad4b80ebace58a059e36e7e2e4eb3b383127a28c711b4ff59',
install => {
'go/' => "/srv/golang-${golang_version}/",
},
}
file { '/srv/golang':
ensure => 'link',
target => "/srv/golang-${golang_version}/",
require => Zulip::Sha256_tarball_to['golang'],
}
$version = 'bfca45c5e61f3587eaaf1dcc89a0c4116501cba3'
zulip::sha256_tarball_to { 'smokescreen':
url => "https://github.com/stripe/smokescreen/archive/${version}.tar.gz",
sha256 => '7aa2719abd282930b01394e5e748885a8e8cb8121fe97a15446f93623ec13f59',
install => {
"smokescreen-${version}/" => "/srv/smokescreen-src-${version}/",
},
}
exec { 'compile smokescreen':
command => "/srv/golang/bin/go build -o /usr/local/bin/smokescreen-${version}",
cwd => "/srv/smokescreen-src-${version}/",
# GOCACHE is required; nothing is written to GOPATH, but it is required to be set
environment => ['GOCACHE=/tmp/gocache', 'GOPATH=/root/go'],
creates => "/usr/local/bin/smokescreen-${version}",
require => [Zulip::Sha256_tarball_to['golang'], Zulip::Sha256_tarball_to['smokescreen']],
}
file { '/usr/local/bin/smokescreen':
ensure => 'link',
target => "/usr/local/bin/smokescreen-${version}",
require => Exec['compile smokescreen'],
notify => Service[supervisor],
}
$listen_address = zulipconf('http_proxy', 'listen_address', '127.0.0.1')
file { '/etc/supervisor/conf.d/zulip/smokescreen.conf':
ensure => file,
require => [
Package[supervisor],
File['/usr/local/bin/smokescreen'],
],
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/supervisor/smokescreen.conf.erb'),
notify => Service[supervisor],
}
# Removed 2021-03 in version 4.0; these lines can be removed in
# Zulip version 5.0 and later.
file { '/etc/supervisor/conf.d/smokescreen.conf':
ensure => absent,
}
include zulip::smokescreen
}

View File

@@ -13,10 +13,6 @@ class zulip::profile::standalone {
include zulip::profile::redis
include zulip::profile::memcached
include zulip::profile::rabbitmq
if $::osfamily == debian {
# camo is only required on Debian-based systems as part of
# our migration towards not including camo at all.
include zulip::localhost_camo
}
include zulip::localhost_camo
include zulip::static_asset_compiler
}

View File

@@ -0,0 +1,57 @@
class zulip::smokescreen {
include zulip::supervisor
include zulip::golang
$version = $zulip::common::versions['smokescreen-src']['version']
$dir = "/srv/zulip-smokescreen-src-${version}"
$bin = "/usr/local/bin/smokescreen-${version}-go-${zulip::golang::version}"
zulip::external_dep { 'smokescreen-src':
version => $version,
url => "https://github.com/stripe/smokescreen/archive/${version}.tar.gz",
tarball_prefix => "smokescreen-${version}",
}
exec { 'compile smokescreen':
command => "${zulip::golang::bin} build -o ${bin}",
cwd => $dir,
# GOCACHE is required; nothing is written to GOPATH, but it is required to be set
environment => ['GOCACHE=/tmp/gocache', 'GOPATH=/root/go'],
creates => $bin,
require => [
Zulip::External_Dep['golang'],
Zulip::External_Dep['smokescreen-src'],
],
}
unless $::operatingsystem == 'Ubuntu' and $::operatingsystemrelease == '18.04' {
# Puppet 5.5.0 and below make this always-noisy, as they spout out
# a notify line about tidying the managed file above. Skip
# on Bionic, which has that old version; they'll get tidied upon
# upgrade to 20.04.
tidy { '/usr/local/bin/smokescreen-*':
path => '/usr/local/bin',
recurse => 1,
matches => 'smokescreen-*',
require => Exec['compile smokescreen'],
}
}
$listen_address = zulipconf('http_proxy', 'listen_address', '127.0.0.1')
file { '/etc/supervisor/conf.d/zulip/smokescreen.conf':
ensure => file,
require => [
Package[supervisor],
Exec['compile smokescreen'],
],
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/supervisor/smokescreen.conf.erb'),
notify => Service[supervisor],
}
# Removed 2021-03 in version 4.0; these lines can be removed in
# Zulip version 5.0 and later.
file { '/etc/supervisor/conf.d/smokescreen.conf':
ensure => absent,
}
}

View File

@@ -66,7 +66,7 @@ class zulip::supervisor {
Package['supervisor'],
],
hasstatus => true,
status => 'supervisorctl status',
status => $zulip::common::supervisor_status,
# Restarting the whole supervisorctl on every update to its
# configuration files has the unfortunate side-effect of
# restarting all of the services it controls; this results in an
@@ -107,4 +107,12 @@ class zulip::supervisor {
content => template('zulip/supervisor/supervisord.conf.erb'),
notify => Exec['supervisor-restart'],
}
file { '/usr/local/bin/secret-env-wrapper':
ensure => file,
owner => 'root',
group => 'root',
mode => '0755',
source => 'puppet:///modules/zulip/secret-env-wrapper',
}
}

View File

@@ -1,3 +0,0 @@
ENABLED=yes
PORT=9292
CAMO_KEY=<%= scope["zulip::camo::camo_key"] %>

View File

@@ -0,0 +1,9 @@
[program:go-camo]
command=/usr/local/bin/secret-env-wrapper GOCAMO_HMAC=camo_key <%= @bin %> --listen=<%= @listen_address %>:9292 -H "Strict-Transport-Security: max-age=15768000" -H "X-Frame-Options: DENY" --verbose
environment=HTTP_PROXY="<%= @proxy %>",HTTPS_PROXY="<%= @proxy %>"
priority=15
autostart=true
autorestart=true
user=zulip
redirect_stderr=true
stdout_logfile=/var/log/zulip/camo.log

View File

@@ -1,5 +1,5 @@
[program:smokescreen]
command=/usr/local/bin/smokescreen-<%= @version %> --listen-ip <%= @listen_address %>
command=<%= @bin %> --listen-ip <%= @listen_address %>
priority=15
autostart=true
autorestart=true

View File

@@ -1 +0,0 @@
* * * * * root /bin/bash -c '(pgrep -u nobody -F /var/run/camo.pid || /etc/init.d/camo restart) 2>&1 >>/var/log/camo/restart-log'

View File

@@ -1,6 +1,7 @@
class zulip_ops::apt_repository_debathena {
$setup_file = "${::zulip_scripts_path}/lib/setup-apt-repo-debathena"
$setup_apt_repo_file = "${::zulip_scripts_path}/lib/setup-apt-repo"
exec { 'setup_apt_repo_debathena':
command => "bash -c '${setup_file}'",
command => "${setup_apt_repo_file} --list zulip_debathena",
unless => "${setup_apt_repo_file} --list zulip_debathena --verify",
}
}

View File

@@ -1,11 +1,5 @@
class zulip_ops::camo {
include zulip::camo
file { '/etc/cron.d/camo':
ensure => file,
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip_ops/cron.d/camo',
class { 'zulip::camo':
listen_address => '0.0.0.0',
}
}

View File

@@ -14,10 +14,10 @@ class zulip_ops::ksplice_uptrack {
mode => '0640',
content => template('zulip_ops/uptrack/uptrack.conf.erb'),
}
$setup_apt_repo_file = "${::zulip_scripts_path}/lib/setup-apt-repo-ksplice"
$setup_apt_repo_file = "${::zulip_scripts_path}/lib/setup-apt-repo"
exec{ 'setup-apt-repo-ksplice':
command => $setup_apt_repo_file,
unless => "${setup_apt_repo_file} --verify",
command => "${setup_apt_repo_file} --list ksplice",
unless => "${setup_apt_repo_file} --list ksplice --verify",
}
Package { 'uptrack':
require => [

View File

@@ -12,7 +12,7 @@ typing-extensions
dataclasses;python_version<"3.7"
# Needed for rendering backend templates
Jinja2
Jinja2<3.0.0
# Needed for Markdown processing
Markdown
@@ -61,7 +61,7 @@ html2text
https://github.com/zulip/talon/archive/1711705c952806d4a704c7dbf58f21db8e11756a.zip#egg=talon-core==1.4.8.zulip1&subdirectory=talon-core
# Needed for inlining the CSS in emails
premailer
premailer<3.9.0
# Needed for JWT-based auth
PyJWT
@@ -132,7 +132,7 @@ https://github.com/zulip/python-zulip-api/archive/4d482e0ef30297f716885fd8246f46
py3dns
# Install Python Social Auth
social-auth-app-django
social-auth-app-django<5.0.0
social-auth-core[azuread,saml]<4.0.3 # 4.0.3 needs PyJWT 2: https://github.com/Pr0Ger/PyAPNs2/pull/122, https://github.com/twilio/twilio-python/issues/556
# For encrypting a login token to the desktop app
@@ -143,6 +143,7 @@ lxml
# Needed for 2-factor authentication
django-two-factor-auth[call,phonenumberslite,sms]
twilio<7.0.0
# Needed for processing payments (in corporate)
stripe
@@ -171,7 +172,7 @@ requests[security]
requests-oauthlib
# For OpenAPI schema validation.
openapi-core<0.14.0 # https://github.com/p1c2u/openapi-core/issues/322
openapi-core
# For reporting errors to sentry.io
sentry-sdk
@@ -187,3 +188,6 @@ backoff
# Non-backtracking regular expressions
google-re2
# CSS manipulation
soupsieve

View File

@@ -27,7 +27,7 @@ responses
isort
# For doing highly usable Python profiling
line-profiler<3.2 # https://github.com/pyutils/line_profiler/issues/77
line-profiler
# for pep8 linter
pycodestyle
@@ -54,10 +54,10 @@ snakeviz
python-digitalocean
# Needed for updating the locked pip dependencies
pip-tools
pip-tools<6.3.0 # https://github.com/jazzband/pip-tools/pull/1455 breaks our hack for installing specific commits from Git
# zulip's linting framework - zulint
https://github.com/zulip/zulint/archive/6cc46d23906757895e917cc75e231f81f824a31d.zip#egg=zulint==0.0.1
https://github.com/zulip/zulint/archive/9908540b7734b51f86ccabab706befc2ff33212a.zip#egg=zulint==0.0.1
-r mypy.in
@@ -70,10 +70,10 @@ importlib-metadata;python_version<"3.8" # for jsonpickle, jsonschema
cairosvg
# Needed for tools/check-thirdparty
python-debian
python-debian<0.1.42 # https://bugs.debian.org/997857
# Pattern-based lint tool
semgrep
semgrep<0.53.0
# Contains Pysa, a security-focused static analyzer
pyre-check

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@
# See requirements/README.md for more detail.
# Needed to build RTD docs
sphinx
sphinx<4.0.0
sphinx-rtd-theme
# Needed to build Markdown docs

View File

@@ -19,13 +19,13 @@ babel==2.9.1 \
--hash=sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9 \
--hash=sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0
# via sphinx
certifi==2020.12.5 \
--hash=sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c \
--hash=sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830
certifi==2021.10.8 \
--hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 \
--hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569
# via requests
chardet==4.0.0 \
--hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa \
--hash=sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5
charset-normalizer==2.0.7 \
--hash=sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0 \
--hash=sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b
# via requests
docutils==0.16 \
--hash=sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af \
@@ -34,9 +34,9 @@ docutils==0.16 \
# myst-parser
# sphinx
# sphinx-rtd-theme
idna==2.10 \
--hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 \
--hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0
idna==3.3 \
--hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \
--hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d
# via requests
imagesize==1.2.0 \
--hash=sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1 \
@@ -54,127 +54,144 @@ markdown-it-py==1.1.0 \
# via
# mdit-py-plugins
# myst-parser
markupsafe==1.1.1 \
--hash=sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473 \
--hash=sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161 \
--hash=sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235 \
--hash=sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5 \
--hash=sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42 \
--hash=sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f \
--hash=sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39 \
--hash=sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff \
--hash=sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b \
--hash=sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014 \
--hash=sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f \
--hash=sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1 \
--hash=sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e \
--hash=sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183 \
--hash=sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66 \
--hash=sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b \
--hash=sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1 \
--hash=sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15 \
--hash=sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1 \
--hash=sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85 \
--hash=sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1 \
--hash=sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e \
--hash=sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b \
--hash=sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905 \
--hash=sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850 \
--hash=sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0 \
--hash=sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735 \
--hash=sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d \
--hash=sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb \
--hash=sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e \
--hash=sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d \
--hash=sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c \
--hash=sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1 \
--hash=sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2 \
--hash=sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21 \
--hash=sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2 \
--hash=sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5 \
--hash=sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7 \
--hash=sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b \
--hash=sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8 \
--hash=sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6 \
--hash=sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193 \
--hash=sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f \
--hash=sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b \
--hash=sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f \
--hash=sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2 \
--hash=sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5 \
--hash=sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c \
--hash=sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032 \
--hash=sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7 \
--hash=sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be \
--hash=sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621
markupsafe==2.0.1 \
--hash=sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298 \
--hash=sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64 \
--hash=sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b \
--hash=sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194 \
--hash=sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567 \
--hash=sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff \
--hash=sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724 \
--hash=sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74 \
--hash=sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646 \
--hash=sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35 \
--hash=sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6 \
--hash=sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a \
--hash=sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6 \
--hash=sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad \
--hash=sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26 \
--hash=sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38 \
--hash=sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac \
--hash=sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7 \
--hash=sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6 \
--hash=sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047 \
--hash=sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75 \
--hash=sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f \
--hash=sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b \
--hash=sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135 \
--hash=sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8 \
--hash=sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a \
--hash=sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a \
--hash=sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1 \
--hash=sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9 \
--hash=sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864 \
--hash=sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914 \
--hash=sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee \
--hash=sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f \
--hash=sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18 \
--hash=sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8 \
--hash=sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2 \
--hash=sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d \
--hash=sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b \
--hash=sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b \
--hash=sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86 \
--hash=sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6 \
--hash=sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f \
--hash=sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb \
--hash=sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833 \
--hash=sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28 \
--hash=sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e \
--hash=sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415 \
--hash=sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902 \
--hash=sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f \
--hash=sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d \
--hash=sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9 \
--hash=sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d \
--hash=sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145 \
--hash=sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066 \
--hash=sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c \
--hash=sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1 \
--hash=sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a \
--hash=sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207 \
--hash=sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f \
--hash=sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53 \
--hash=sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd \
--hash=sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134 \
--hash=sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85 \
--hash=sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9 \
--hash=sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5 \
--hash=sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94 \
--hash=sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509 \
--hash=sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51 \
--hash=sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872
# via jinja2
mdit-py-plugins==0.2.8 \
--hash=sha256:1833bf738e038e35d89cb3a07eb0d227ed647ce7dd357579b65343740c6d249c \
--hash=sha256:5991cef645502e80a5388ec4fc20885d2313d4871e8b8e320ca2de14ac0c015f
# via myst-parser
myst-parser==0.15.1 \
--hash=sha256:7c3c78a36c4bc30ce6a67933ebd800a880c8d81f1688fad5c2ebd82cddbc1603 \
--hash=sha256:e8baa9959dac0bcf0f3ea5fc32a1a28792959471d8a8094e3ed5ee0de9733ade
myst-parser==0.15.2 \
--hash=sha256:40124b6f27a4c42ac7f06b385e23a9dcd03d84801e9c7130b59b3729a554b1f9 \
--hash=sha256:f7f3b2d62db7655cde658eb5d62b2ec2a4631308137bd8d10f296a40d57bbbeb
# via -r requirements/docs.in
packaging==20.9 \
--hash=sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5 \
--hash=sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a
packaging==21.2 \
--hash=sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966 \
--hash=sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0
# via sphinx
pygments==2.9.0 \
--hash=sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f \
--hash=sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e
pygments==2.10.0 \
--hash=sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380 \
--hash=sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6
# via sphinx
pyparsing==2.4.7 \
--hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 \
--hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b
# via packaging
pytz==2021.1 \
--hash=sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da \
--hash=sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798
pytz==2021.3 \
--hash=sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c \
--hash=sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326
# via babel
pyyaml==5.4.1 \
--hash=sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf \
--hash=sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696 \
--hash=sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393 \
--hash=sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77 \
--hash=sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922 \
--hash=sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5 \
--hash=sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8 \
--hash=sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10 \
--hash=sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc \
--hash=sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018 \
--hash=sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e \
--hash=sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253 \
--hash=sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347 \
--hash=sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183 \
--hash=sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541 \
--hash=sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb \
--hash=sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185 \
--hash=sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc \
--hash=sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db \
--hash=sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa \
--hash=sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46 \
--hash=sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122 \
--hash=sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b \
--hash=sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63 \
--hash=sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df \
--hash=sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc \
--hash=sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247 \
--hash=sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6 \
--hash=sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0
pyyaml==6.0 \
--hash=sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293 \
--hash=sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b \
--hash=sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57 \
--hash=sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b \
--hash=sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4 \
--hash=sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07 \
--hash=sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba \
--hash=sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9 \
--hash=sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287 \
--hash=sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513 \
--hash=sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0 \
--hash=sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0 \
--hash=sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92 \
--hash=sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f \
--hash=sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2 \
--hash=sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc \
--hash=sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c \
--hash=sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86 \
--hash=sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4 \
--hash=sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c \
--hash=sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34 \
--hash=sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b \
--hash=sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c \
--hash=sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb \
--hash=sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737 \
--hash=sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3 \
--hash=sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d \
--hash=sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53 \
--hash=sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78 \
--hash=sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803 \
--hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a \
--hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174 \
--hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5
# via myst-parser
requests==2.25.1 \
--hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 \
--hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e
requests==2.26.0 \
--hash=sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24 \
--hash=sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7
# via sphinx
snowballstemmer==2.1.0 \
--hash=sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2 \
--hash=sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914
# via sphinx
sphinx-rtd-theme==0.5.2 \
--hash=sha256:32bd3b5d13dc8186d7a42fc816a23d32e83a4827d7d9882948e7b837c232da5a \
--hash=sha256:4a05bdbe8b1446d77a01e20a23ebc6777c74f43237035e76be89699308987d6f
# via -r requirements/docs.in
sphinx==3.5.4 \
--hash=sha256:19010b7b9fa0dc7756a6e105b2aacd3a80f798af3c25c273be64d7beeb482cb1 \
--hash=sha256:2320d4e994a191f4b4be27da514e46b3d6b420f2ff895d064f52415d342461e8
@@ -182,6 +199,10 @@ sphinx==3.5.4 \
# -r requirements/docs.in
# myst-parser
# sphinx-rtd-theme
sphinx-rtd-theme==1.0.0 \
--hash=sha256:4d35a56f4508cfee4c4fb604373ede6feae2a306731d533f409ef5c3496fdbd8 \
--hash=sha256:eec6d497e4c2195fa0e8b2016b337532b8a699a68bcb22a512870e16925c6a5c
# via -r requirements/docs.in
sphinxcontrib-applehelp==1.0.2 \
--hash=sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a \
--hash=sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58
@@ -190,9 +211,9 @@ sphinxcontrib-devhelp==1.0.2 \
--hash=sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e \
--hash=sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4
# via sphinx
sphinxcontrib-htmlhelp==1.0.3 \
--hash=sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f \
--hash=sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b
sphinxcontrib-htmlhelp==2.0.0 \
--hash=sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07 \
--hash=sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2
# via sphinx
sphinxcontrib-jsmath==1.0.1 \
--hash=sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178 \
@@ -202,22 +223,22 @@ sphinxcontrib-qthelp==1.0.3 \
--hash=sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72 \
--hash=sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6
# via sphinx
sphinxcontrib-serializinghtml==1.1.4 \
--hash=sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc \
--hash=sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a
sphinxcontrib-serializinghtml==1.1.5 \
--hash=sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd \
--hash=sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952
# via sphinx
typing-extensions==3.10.0.0 \
--hash=sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497 \
--hash=sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342 \
--hash=sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84
typing-extensions==3.10.0.2 \
--hash=sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e \
--hash=sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7 \
--hash=sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34
# via markdown-it-py
urllib3==1.26.4 \
--hash=sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df \
--hash=sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937
urllib3==1.26.7 \
--hash=sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece \
--hash=sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844
# via requests
# The following packages are considered to be unsafe in a requirements file:
setuptools==56.1.0 \
--hash=sha256:7d9b5609ebda3db0f2e7c6d2fba807e9bd653e3a3a93ce3426b2b68260193a77 \
--hash=sha256:9ceb15de17f8e99dd4903f88e05e04285fa60f364f67bdcc4334aa71604b7a39
setuptools==58.4.0 \
--hash=sha256:af632270cb4b5ca0ebd272ac1939a3e8f76aa975d2722e999cfdcea2b9e824cb \
--hash=sha256:e8b1d3127a0441fb99a130bcc3c2bf256c2d3ead3aba8fd400e5cbbaf788e036
# via sphinx

View File

@@ -2,5 +2,5 @@
# /tools/update-locked-requirements to update requirements/dev.txt
# and requirements/mypy.txt.
# See requirements/README.md for more detail.
mypy
mypy==0.812
sqlalchemy-stubs

View File

@@ -7,10 +7,6 @@
#
# For details, see requirements/README.md .
#
mypy-extensions==0.4.3 \
--hash=sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d \
--hash=sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8
# via mypy
mypy==0.812 \
--hash=sha256:0d0a87c0e7e3a9becdfbe936c981d32e5ee0ccda3e0f07e1ef2c3d1a817cf73e \
--hash=sha256:25adde9b862f8f9aac9d2d11971f226bd4c8fbaa89fb76bdadb267ef22d10064 \
@@ -37,6 +33,10 @@ mypy==0.812 \
# via
# -r requirements/mypy.in
# sqlalchemy-stubs
mypy-extensions==0.4.3 \
--hash=sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d \
--hash=sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8
# via mypy
sqlalchemy-stubs==0.4 \
--hash=sha256:5eec7aa110adf9b957b631799a72fef396b23ff99fe296df726645d01e312aa5 \
--hash=sha256:c665d6dd4482ef642f01027fa06c3d5e91befabb219dc71fc2a09e7d7695f7ae
@@ -73,10 +73,10 @@ typed-ast==1.4.3 \
--hash=sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f \
--hash=sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65
# via mypy
typing-extensions==3.10.0.0 \
--hash=sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497 \
--hash=sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342 \
--hash=sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84
typing-extensions==3.10.0.2 \
--hash=sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e \
--hash=sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7 \
--hash=sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34
# via
# mypy
# sqlalchemy-stubs

View File

@@ -7,9 +7,9 @@
#
# For details, see requirements/README.md .
#
wheel==0.36.2 \
--hash=sha256:78b5b185f0e5763c26ca1e324373aadd49182ca90e825f7853f4b2509215dc0e \
--hash=sha256:e11eefd162658ea59a60a0f6c7d493a7190ea4b9a85e335b33489d9f17e0245e
wheel==0.37.0 \
--hash=sha256:21014b2bd93c6d0034b6ba5d35e4eb284340e09d63c59aef6fc14b0f346146fd \
--hash=sha256:e2ef7239991699e3355d54f8e968a21bb940a1dbf34a4d226741e64462516fad
# via -r requirements/pip.in
# The following packages are considered to be unsafe in a requirements file:
@@ -17,7 +17,7 @@ pip==20.3.4 \
--hash=sha256:217ae5161a0e08c0fb873858806e3478c9775caffce5168b50ec885e358c199d \
--hash=sha256:6773934e5f5fc3eaa8c5a44949b5b924fc122daa0a8aa9f80c835b4ca2a543fc
# via -r requirements/pip.in
setuptools==56.1.0 \
--hash=sha256:7d9b5609ebda3db0f2e7c6d2fba807e9bd653e3a3a93ce3426b2b68260193a77 \
--hash=sha256:9ceb15de17f8e99dd4903f88e05e04285fa60f364f67bdcc4334aa71604b7a39
setuptools==58.4.0 \
--hash=sha256:af632270cb4b5ca0ebd272ac1939a3e8f76aa975d2722e999cfdcea2b9e824cb \
--hash=sha256:e8b1d3127a0441fb99a130bcc3c2bf256c2d3ead3aba8fd400e5cbbaf788e036
# via -r requirements/pip.in

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +0,0 @@
#!/usr/bin/env bash
zulip_conf_get_boolean() {
# Get a boolean flag from zulip.conf, using the Python
# `configparser` library's conventions for what counts as true.
# Treat absent and invalid values as false.
value=$(crudini --get /etc/zulip/zulip.conf "$1" "$2" 2>/dev/null)
case "$(echo "$value" | tr '[:upper:]' '[:lower:]')" in
1 | yes | true | on) return 0 ;;
*) return 1 ;;
esac
}
if ! zulip_conf_get_boolean certbot auto_renew; then
exit 0
fi
deploy_hook="${ZULIP_CERTBOT_DEPLOY_HOOK:-service nginx reload}"
certbot renew --quiet \
--webroot --webroot-path=/var/lib/zulip/certbot-webroot/ \
--deploy-hook "$deploy_hook"

View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -eu
set -o pipefail
hostnames=$(grep -l 'authenticator = standalone' /etc/letsencrypt/renewal/*.conf | sed 's/.*\///; s/\.conf$//')
for hostname in $hostnames; do
# Force a cert renewal to force the config file to update
/usr/bin/certbot certonly --webroot --webroot-path=/var/lib/zulip/certbot-webroot/ --force-renewal -d "$hostname"
done
# Pick up any updated certs
service nginx reload

View File

@@ -318,7 +318,7 @@ fi
# installation process more seamless.
if [ -z "$NO_DIST_UPGRADE" ]; then
if [ "$package_system" = apt ]; then
apt-get -y dist-upgrade "${APT_OPTIONS[@]}"
apt-get -y --allow-downgrades dist-upgrade "${APT_OPTIONS[@]}"
elif [ "$package_system" = yum ]; then
# On CentOS, there is no need to do `yum -y upgrade` because `yum -y
# update` already does the same thing.
@@ -353,11 +353,10 @@ elif [ "$package_system" = yum ]; then
fi
fi
if [ -n "$USE_CERTBOT" ]; then
"$ZULIP_PATH"/scripts/setup/setup-certbot \
--no-zulip-conf --method=standalone \
"$EXTERNAL_HOST" --email "$ZULIP_ADMINISTRATOR"
elif [ -n "$SELF_SIGNED_CERT" ]; then
# We generate a self-signed cert even with certbot, so we can use the
# webroot authenticator, which requires nginx be set up with a
# certificate.
if [ -n "$SELF_SIGNED_CERT" ] || [ -n "$USE_CERTBOT" ]; then
"$ZULIP_PATH"/scripts/setup/generate-self-signed-cert \
--exists-ok "${EXTERNAL_HOST:-$(hostname)}"
fi
@@ -397,10 +396,6 @@ deploy_type = production
version = $POSTGRESQL_VERSION
EOF
if [ -n "$USE_CERTBOT" ]; then
crudini --set /etc/zulip/zulip.conf certbot auto_renew yes
fi
if [ -n "$POSTGRESQL_MISSING_DICTIONARIES" ]; then
crudini --set /etc/zulip/zulip.conf postgresql missing_dictionaries true
fi
@@ -415,29 +410,6 @@ EOF
if ! has_class "zulip::postgresql_common" || [ "$package_system" != apt ]; then
crudini --del /etc/zulip/zulip.conf postgresql
fi
# Note: there are four dpkg-query outputs to consider:
#
# root@host# dpkg-query --showformat '${Status}\n' -W rabbitmq-server 2>/dev/null
# root@host# apt install rabbitmq-server
# root@host# dpkg-query --showformat '${Status}\n' -W rabbitmq-server 2>/dev/null
# install ok installed
# root@host# apt remove rabbitmq-server
# root@host# dpkg-query --showformat '${Status}\n' -W rabbitmq-server 2>/dev/null
# deinstall ok config-files
# root@host# apt purge rabbitmq-server
# root@host# dpkg-query --showformat '${Status}\n' -W rabbitmq-server 2>/dev/null
# unknown ok not-installed
#
# (There are more possibilities in the case of dpkg errors.) Here
# we are checking for either empty or not-installed.
if ! dpkg-query --showformat '${Status}\n' -W rabbitmq-server 2>/dev/null | grep -vq ' not-installed$'; then
cat <<EOF >>/etc/zulip/zulip.conf
[rabbitmq]
nodename = zulip@localhost
EOF
fi
fi
if has_class "zulip::app_frontend_base"; then
@@ -457,12 +429,17 @@ fi
"$ZULIP_PATH"/scripts/zulip-puppet-apply -f
if [ "$package_system" = apt ]; then
apt-get -y upgrade
apt-get -y --allow-downgrades upgrade
elif [ "$package_system" = yum ]; then
# No action is required because `yum update` already does upgrade.
:
fi
if [ -n "$USE_CERTBOT" ]; then
"$ZULIP_PATH"/scripts/setup/setup-certbot \
"$EXTERNAL_HOST" --email "$ZULIP_ADMINISTRATOR"
fi
if has_class "zulip::nginx" && ! has_class "zulip::profile::docker"; then
# Check nginx was configured properly now that we've installed it.
# Most common failure mode is certs not having been installed.
@@ -497,7 +474,6 @@ For more information, see:
EOF
exit 1
fi
"$ZULIP_PATH"/scripts/setup/configure-rabbitmq
fi
if has_class "zulip::postgresql_common" && [ -z "$NO_INIT_DB" ]; then

View File

@@ -1,18 +1,35 @@
#!/usr/bin/env bash
#
# This script handles adding custom apt repositories into
# /etc/apt/sources.list.d/ files. It bundles the GPG keys which are
# used to verify the repositories (via `apt-key`), to explicitly pin
# the trusted signing keys, as opposed to blindly trusting HTTPS.
#
# Each /etc/apt/sources.list.d/foo.list file is created via `--list
# foo`, where `foo` defaults to `zulip`. The default `zulip.list` is
# installed in `scripts/lib/install` / `tools/lib/provision.py`, and
# other `.list` files may be installed by Puppet.
set -x
set -e
set -u
set -o pipefail
shopt -s extglob
verify=false
args="$(getopt -o '' --long verify -- "$@")"
args="$(getopt -o '' --long verify,list: -- "$@")"
eval "set -- $args"
LIST=zulip
while true; do
case "$1" in
--verify)
verify=true
shift
;;
--list)
LIST="$2"
shift
shift
;;
--)
shift
break
@@ -23,12 +40,34 @@ done
# Ensure the directory for LAST_DEPENDENCIES_HASH exists
mkdir -p /var/lib/zulip
SOURCES_FILE=/etc/apt/sources.list.d/zulip.list
STAMP_FILE=/etc/apt/sources.list.d/zulip.list.apt-update-in-progress
SOURCES_FILE=/etc/apt/sources.list.d/$LIST.list
PREF_FILE=/etc/apt/preferences.d/$LIST.pref
STAMP_FILE=/etc/apt/sources.list.d/$LIST.list.apt-update-in-progress
ZULIP_SCRIPTS="$(dirname "$(dirname "$0")")"
DEPENDENCIES_HASH=$(sha1sum "$ZULIP_SCRIPTS/setup/"*.asc "$0")
DEPENDENCIES_HASH_FILE="/var/lib/zulip/setup-repositories-state"
ZULIP_SCRIPTS="$(cd "$(dirname "$(dirname "$0")")" && pwd)"
LIST_PATH="$ZULIP_SCRIPTS/setup/apt-repos/$LIST"
if ! [ -d "$LIST_PATH" ]; then
echo "Not a valid value for --list: '$LIST'"
echo ""
echo "Valid values are:"
ls -1 "$ZULIP_SCRIPTS/setup/apt-repos/"
exit 1
fi
release="$(. /etc/os-release && printf '%s' "$VERSION_CODENAME")"
if [ ! -f "$LIST_PATH/$release.list" ]; then
cat <<EOF
Unsupported release $release for sources.list file $LIST. To add a
new release, make a $LIST_PATH/$release.list file based on existing
.list files in that directory.
EOF
exit 1
fi
DEPENDENCIES_HASH="$(sha256sum "$LIST_PATH"/?(*.asc|"$release.list"|"$LIST.pref"|custom.sh) "$ZULIP_SCRIPTS/lib/setup-apt-repo")"
DEPENDENCIES_HASH_FILE="/var/lib/zulip/setup-repositories-state-$LIST"
# Ensure that DEPENDENCIES_HASH_FILE exists before hashing it.
touch "$DEPENDENCIES_HASH_FILE"
LAST_DEPENDENCIES_HASH="$(cat "$DEPENDENCIES_HASH_FILE")"
@@ -41,11 +80,8 @@ elif [ "$verify" == true ]; then
exit 1
fi
# Ensure that the sources file exists
touch "$SOURCES_FILE"
# Hash it to check if the sources file is changed by the script later.
zulip_source_hash=$(sha1sum "$SOURCES_FILE")
# Hash to check if the configuration is changed by the script later.
hashes=$(sha256sum "$SOURCES_FILE" "$PREF_FILE" 2>/dev/null || true)
pre_setup_deps=(lsb-release apt-transport-https ca-certificates gnupg wget)
if ! apt-get -dy install "${pre_setup_deps[@]}"; then
@@ -53,70 +89,23 @@ if ! apt-get -dy install "${pre_setup_deps[@]}"; then
fi
apt-get -y install "${pre_setup_deps[@]}"
SCRIPTS_PATH="$(cd "$(dirname "$(dirname "$0")")" && pwd)"
apt-key add "$LIST_PATH/"*.asc
cp "$LIST_PATH/$release.list" "$SOURCES_FILE"
release=$(lsb_release -sc)
if [[ "$release" =~ ^(bionic|cosmic|disco|eoan|focal|groovy)$ ]]; then
distribution=ubuntu
apt-key add "$SCRIPTS_PATH"/setup/pgdg.asc
apt-key add "$SCRIPTS_PATH"/setup/pgroonga-ppa.asc
cat >$SOURCES_FILE <<EOF
deb http://apt.postgresql.org/pub/repos/apt/ $release-pgdg main
deb-src http://apt.postgresql.org/pub/repos/apt/ $release-pgdg main
deb http://ppa.launchpad.net/groonga/ppa/ubuntu $release main
deb-src http://ppa.launchpad.net/groonga/ppa/ubuntu $release main
EOF
elif [[ "$release" =~ ^(buster|bullseye)$ ]]; then
distribution=debian
apt-key add "$SCRIPTS_PATH"/setup/pgdg.asc
cat >$SOURCES_FILE <<EOF
deb http://apt.postgresql.org/pub/repos/apt/ $release-pgdg main
deb-src http://apt.postgresql.org/pub/repos/apt/ $release-pgdg main
EOF
if [ -e "$LIST_PATH/$LIST.pref" ]; then
cp "$LIST_PATH/$LIST.pref" "$PREF_FILE"
else
echo "Unsupported release $release."
exit 1
rm -f "$PREF_FILE"
fi
if [[ ! -e /usr/share/doc/groonga-apt-source/copyright ]]; then
remove_pgroonga_apt_tmp_dir() {
rm -rf "$pgroonga_apt_tmp_dir"
}
pgroonga_apt_tmp_dir=$(mktemp --directory)
trap remove_pgroonga_apt_tmp_dir EXIT
pushd "$pgroonga_apt_tmp_dir"
tmp_gpg_home=.gnupg
pgroonga_apt_sign_key="$SCRIPTS_PATH"/setup/pgroonga-packages.groonga.org.asc
gpg --homedir="$tmp_gpg_home" --import "$pgroonga_apt_sign_key"
# Find fingerprint of the first key.
pgroonga_apt_sign_key_fingerprint=$(
gpg --homedir="$tmp_gpg_home" --with-colons --list-keys \
| grep '^fpr:' \
| cut --delimiter=: --fields=10 \
| head --lines=1
)
groonga_apt_source_deb="groonga-apt-source-latest-$release.deb"
groonga_apt_source_deb_sign="$groonga_apt_source_deb.asc.$pgroonga_apt_sign_key_fingerprint"
wget "https://packages.groonga.org/$distribution/$groonga_apt_source_deb"
wget "https://packages.groonga.org/$distribution/$groonga_apt_source_deb_sign"
gpg \
--homedir="$tmp_gpg_home" \
--verify \
"$groonga_apt_source_deb_sign" \
"$groonga_apt_source_deb"
# To suppress the following warning by "apt-get install":
# N: Download is performed unsandboxed as root as file
# '.../groonga-apt-source-latest-$release.deb' couldn't be
# accessed by user '_apt'. - pkgAcquire::Run (13: Permission denied)
chown _apt .
apt-get -y install "./$groonga_apt_source_deb"
popd
touch "$STAMP_FILE"
if [ -e "$LIST_PATH/custom.sh" ]; then
export LIST_PATH
export STAMP_FILE
bash "$LIST_PATH/custom.sh"
fi
if [ "$zulip_source_hash" = "$(sha1sum "$SOURCES_FILE")" ] && ! [ -e "$STAMP_FILE" ]; then
echo "zulip.list file did not change; skipping apt-get update"
if [ "$hashes" = "$(sha256sum "$SOURCES_FILE" "$PREF_FILE" 2>/dev/null || true)" ] && ! [ -e "$STAMP_FILE" ]; then
echo "APT configuration did not change; skipping apt-get update"
else
# We create this stamp file to ensure `apt-get update` will be run
# the next time this script is invoked, and each time after, until

View File

@@ -1,57 +0,0 @@
#!/usr/bin/env bash
set -x
set -e
# Ensure the directory for LAST_DEPENDENCIES_HASH exists
mkdir -p /var/lib/zulip
SOURCES_FILE=/etc/apt/sources.list.d/zulip_debathena.list
STAMP_FILE=/etc/apt/sources.list.d/zulip_debathena.list.apt-update-in-progress
ZULIP_SCRIPTS="$(dirname "$(dirname "$0")")"
DEPENDENCIES_HASH=$(sha1sum "$ZULIP_SCRIPTS/setup/"*.asc "$0")
DEPENDENCIES_HASH_FILE="/var/lib/zulip/setup-repositories-state-debathena"
# Ensure that DEPENDENCIES_HASH_FILE exists before hashing it.
touch "$DEPENDENCIES_HASH_FILE"
LAST_DEPENDENCIES_HASH="$(cat "$DEPENDENCIES_HASH_FILE")"
# First, we only do anything in setup-apt-repo if any of its inputs
# (apt keys, code, etc.) changed.
if [ "$DEPENDENCIES_HASH" = "$LAST_DEPENDENCIES_HASH" ]; then
exit 0
fi
# Ensure that the sources file exists
touch "$SOURCES_FILE"
# Hash it to check if the sources file is changed by the script later.
zulip_source_hash=$(sha1sum "$SOURCES_FILE")
apt-get install -y lsb-release apt-transport-https gnupg
SCRIPTS_PATH="$(dirname "$(dirname "$0")")"
release=$(lsb_release -sc)
if [ "$release" = "bionic" ]; then
apt-key add "$SCRIPTS_PATH"/setup/debathena-archive.asc
cat >$SOURCES_FILE <<EOF
deb http://debathena.mit.edu/apt $release debathena debathena-config
deb-src http://debathena.mit.edu/apt $release debathena debathena-config
EOF
else
echo "Unsupported release $release."
exit 1
fi
# Copied blindly from scripts/lib/setup-apt-repo
if [ "$zulip_source_hash" = "$(sha1sum "$SOURCES_FILE")" ] && ! [ -e "$STAMP_FILE" ]; then
echo "zulip.list file did not change; skipping apt-get update"
else
# We create this stamp file to ensure `apt-get update` will be run
# the next time this script is invoked, and each time after, until
# `apt-get update` finishes successfully.
touch "$STAMP_FILE"
apt-get update && rm -f "$STAMP_FILE"
fi
echo "$DEPENDENCIES_HASH" >"$DEPENDENCIES_HASH_FILE"

View File

@@ -1,80 +0,0 @@
#!/usr/bin/env bash
set -x
set -e
set -u
set -o pipefail
verify=false
args="$(getopt -o '' --long verify -- "$@")"
eval "set -- $args"
while true; do
case "$1" in
--verify)
verify=true
shift
;;
--)
shift
break
;;
esac
done
# Ensure the directory for LAST_DEPENDENCIES_HASH exists
mkdir -p /var/lib/zulip
SOURCES_FILE=/etc/apt/sources.list.d/ksplice.list
STAMP_FILE=/etc/apt/sources.list.d/ksplice.list.apt-update-in-progress
ZULIP_SCRIPTS="$(dirname "$(dirname "$0")")"
DEPENDENCIES_HASH=$(sha1sum "$ZULIP_SCRIPTS/setup/"*.asc "$0")
DEPENDENCIES_HASH_FILE="/var/lib/zulip/setup-repositories-state-ksplice"
# Ensure that DEPENDENCIES_HASH_FILE exists before hashing it.
touch "$DEPENDENCIES_HASH_FILE"
LAST_DEPENDENCIES_HASH="$(cat "$DEPENDENCIES_HASH_FILE")"
# First, we only do anything in setup-apt-repo if any of its inputs
# (apt keys, code, etc.) changed.
if [ "$DEPENDENCIES_HASH" = "$LAST_DEPENDENCIES_HASH" ]; then
exit 0
elif [ "$verify" == true ]; then
exit 1
fi
# Ensure that the sources file exists
touch "$SOURCES_FILE"
# Hash it to check if the sources file is changed by the script later.
zulip_source_hash=$(sha1sum "$SOURCES_FILE")
pre_setup_deps=(lsb-release apt-transport-https ca-certificates gnupg wget)
if ! apt-get -dy install "${pre_setup_deps[@]}"; then
apt-get update
fi
apt-get -y install "${pre_setup_deps[@]}"
SCRIPTS_PATH="$(cd "$(dirname "$(dirname "$0")")" && pwd)"
release=$(lsb_release -sc)
if [[ "$release" =~ ^(buster|bullseye|bionic|cosmic|disco|eoan|focal|groovy)$ ]]; then
apt-key add "$SCRIPTS_PATH"/setup/ksplice.asc
cat >$SOURCES_FILE <<EOF
deb http://www.ksplice.com/apt $release ksplice
deb-src http://www.ksplice.com/apt $release ksplice
EOF
else
echo "Unsupported release $release."
exit 1
fi
if [ "$zulip_source_hash" = "$(sha1sum "$SOURCES_FILE")" ] && ! [ -e "$STAMP_FILE" ]; then
echo "zulip.list file did not change; skipping apt-get update"
else
# We create this stamp file to ensure `apt-get update` will be run
# the next time this script is invoked, and each time after, until
# `apt-get update` finishes successfully.
touch "$STAMP_FILE"
apt-get update && rm -f "$STAMP_FILE"
fi
echo "$DEPENDENCIES_HASH" >"$DEPENDENCIES_HASH_FILE"

View File

@@ -13,7 +13,7 @@ import re
import subprocess
import sys
import time
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Optional
if TYPE_CHECKING:
from typing import NoReturn
@@ -31,6 +31,7 @@ from scripts.lib.zulip_tools import (
get_config,
get_config_file,
get_tornado_ports,
listening_publicly,
parse_os_release,
su_to_zulip,
)
@@ -101,12 +102,43 @@ tornado_processes = len(get_tornado_ports(config_file))
IS_SERVER_UP = True
# Check if rabbitmq port 25672 is listening on anything except 127.0.0.1
rabbitmq_dist_listen = listening_publicly(25672)
# Check the erlang magic cookie size
cookie_size: Optional[int] = None
if os.path.exists("/var/lib/rabbitmq/.erlang.cookie"):
with open("/var/lib/rabbitmq/.erlang.cookie", "r") as cookie_fh:
cookie_size = len(cookie_fh.readline())
else:
logging.info("No RabbitMQ erlang cookie found, not auditing RabbitMQ security.")
if args.skip_puppet and rabbitmq_dist_listen:
logging.error(
"RabbitMQ is publicly-accessible on %s; this is a security vulnerability!",
", ".join(rabbitmq_dist_listen),
)
issue = "issue"
if cookie_size is not None and cookie_size == 20:
# See the below comment -- this is used as a lightweight
# signal for a cookie made with Erlang's bad randomizer.
logging.error(
"RabbitMQ erlang cookie is insecure; this is a critical security vulnerability!"
)
issue = "issues"
logging.error(
f"To fix the above security {issue}, re-run the upgrade without --skip-puppet "
"(which may be set in /etc/zulip/zulip.conf), in order to restart the "
"necessary services. Running zulip-puppet-apply by itself is not sufficient."
)
sys.exit(1)
def shutdown_server() -> None:
global IS_SERVER_UP
logging.info("Stopping Zulip...")
subprocess.check_call(["./scripts/stop-server"], preexec_fn=su_to_zulip)
IS_SERVER_UP = False
if IS_SERVER_UP:
logging.info("Stopping Zulip...")
subprocess.check_call(["./scripts/stop-server"], preexec_fn=su_to_zulip)
IS_SERVER_UP = False
# postgresql.version is required for database servers, but wasn't
@@ -150,7 +182,7 @@ if glob.glob("/usr/share/postgresql/*/extension/tsearch_extras.control"):
if not args.skip_puppet:
logging.info("Upgrading system packages...")
subprocess.check_call(["apt-get", "update"])
subprocess.check_call(["apt-get", "-y", "upgrade"])
subprocess.check_call(["apt-get", "-y", "--allow-downgrades", "upgrade"])
# To bootstrap zulip-puppet-apply, we need to install the system yaml
# package; new installs get this, but old installs may not have it.
@@ -210,8 +242,8 @@ elif args.from_git:
# Ideally, we'd have 2 thresholds here, depending on whether the
# system is running queue workers multithreaded or multiprocess.
# See puppet/zulip/manifests/app_frontend_base.pp for background.
if mem_gib < 3.8:
logging.info("Shutting down server to build static assets on a low-RAM system.")
if mem_gib < 4.2:
logging.info("Shutting down server to ensure sufficient free RAM for webpack.")
shutdown_server()
# Note: The fact that this is before we apply Puppet changes means
@@ -219,7 +251,15 @@ elif args.from_git:
# update-prod-static with the Git upgrade process. But it'll fail
# safely; this seems like a worthwhile tradeoff to minimize downtime.
logging.info("Building static assets...")
subprocess.check_call(["./tools/update-prod-static"], preexec_fn=su_to_zulip)
try:
subprocess.check_call(["./tools/update-prod-static"], preexec_fn=su_to_zulip)
except subprocess.CalledProcessError:
logging.error("Failed to build static assets.")
if IS_SERVER_UP:
logging.error("Usually the cause is insufficient free RAM to run webpack.")
logging.error("Try stopping the Zulip server (scripts/stop-server) and trying again.")
sys.exit(1)
logging.info("Caching Zulip Git version...")
subprocess.check_call(["./tools/cache-zulip-git-version"], preexec_fn=su_to_zulip)
else:
@@ -266,6 +306,21 @@ if (not args.skip_puppet or migrations_needed) and IS_SERVER_UP:
# state.
shutdown_server()
if rabbitmq_dist_listen:
shutdown_server()
logging.info("Shutting down rabbitmq to adjust its ports...")
subprocess.check_call(["/usr/sbin/service", "rabbitmq-server", "stop"])
if cookie_size is not None and cookie_size == 20:
# Checking for a 20-character cookie is used as a signal that it
# was generated by Erlang's insecure randomizer, which only
# provides between 20 and 36 bits of entropy; were it 20
# characters long by a good randomizer, it would be 96 bits and
# more than sufficient. We generate, using good randomness, a
# 255-character cookie, the max allowed length.
logging.info("Generating a secure erlang cookie...")
subprocess.check_call(["./scripts/setup/generate-rabbitmq-cookie"])
# Adjust Puppet class names for the manifest renames in the 4.0 release
class_renames = {
"zulip::app_frontend": "zulip::profile::app_frontend",
@@ -295,7 +350,7 @@ if classes != new_classes:
if not args.skip_puppet:
logging.info("Applying Puppet changes...")
subprocess.check_call(["./scripts/zulip-puppet-apply", "--force"])
subprocess.check_call(["apt-get", "-y", "upgrade"])
subprocess.check_call(["apt-get", "-y", "--allow-downgrades", "upgrade"])
if migrations_needed:
logging.info("Applying database migrations...")
@@ -308,9 +363,9 @@ if IS_SERVER_UP or not args.skip_puppet:
# Even if the server wasn't up previously, puppet might have
# started it if there were supervisord configuration changes, so
# we need to use restart-server if puppet ran.
subprocess.check_output(["./scripts/restart-server", "--fill-cache"], preexec_fn=su_to_zulip)
subprocess.check_call(["./scripts/restart-server", "--fill-cache"], preexec_fn=su_to_zulip)
else:
subprocess.check_output(["./scripts/start-server", "--fill-cache"], preexec_fn=su_to_zulip)
subprocess.check_call(["./scripts/start-server", "--fill-cache"], preexec_fn=su_to_zulip)
logging.info("Upgrade complete!")

View File

@@ -0,0 +1,29 @@
#!/usr/bin/env bash
# For security reasons, we need to configure RabbitMQ to listen only
# on localhost, which we cannot do if the nodename contains the
# hostname (e.g. rabbit@zulip-host). Containing the hostname also
# makes it brittle to hostname changes, which are (for example) common
# in containerized environments.
set -eu
# Try to find the current nodename
CURRENT=$(sh -c 'cd /usr/lib/rabbitmq/bin/ && . ./rabbitmq-env && echo $NODENAME')
if [ "$CURRENT" != "zulip@localhost" ]; then
cat <<EOF
***** WARNING *****
We are renaming the rabbitmq server's nodename from '$CURRENT' to
'zulip@localhost', as rabbitmq is being reconfigured to listen only on
localhost. This will also make the server more resilient to hostname
changes. This will only affect you if you were using the rabbitmq
server for other, non-Zulip uses.
*******************
EOF
service rabbitmq-server stop || true
fi

View File

@@ -625,6 +625,24 @@ def deport(netloc: str) -> str:
return "[" + r.hostname + "]" if ":" in r.hostname else r.hostname
def listening_publicly(port: int) -> List[str]:
filter = f"sport = :{port} and not src 127.0.0.1:{port} and not src [::1]:{port}"
# Parse lines that look like this:
# tcp LISTEN 0 128 0.0.0.0:25672 0.0.0.0:*
lines = (
subprocess.check_output(
["/bin/ss", "-Hnl", filter],
universal_newlines=True,
# Hosts with IPv6 disabled will get "RTNETLINK answers: Invalid
# argument"; eat stderr to hide that
stderr=subprocess.DEVNULL,
)
.strip()
.splitlines()
)
return [line.split()[4] for line in lines]
if __name__ == "__main__":
cmd = sys.argv[1]
if cmd == "make_deploy_path":

View File

@@ -0,0 +1,2 @@
deb http://www.ksplice.com/apt bionic ksplice
deb-src http://www.ksplice.com/apt bionic ksplice

View File

@@ -0,0 +1,2 @@
deb http://www.ksplice.com/apt focal ksplice
deb-src http://www.ksplice.com/apt focal ksplice

View File

@@ -0,0 +1,5 @@
deb http://apt.postgresql.org/pub/repos/apt/ bionic-pgdg main
deb-src http://apt.postgresql.org/pub/repos/apt/ bionic-pgdg main
deb http://ppa.launchpad.net/groonga/ppa/ubuntu bionic main
deb-src http://ppa.launchpad.net/groonga/ppa/ubuntu bionic main

View File

@@ -0,0 +1,5 @@
deb http://apt.postgresql.org/pub/repos/apt/ bullseye-pgdg main
deb-src http://apt.postgresql.org/pub/repos/apt/ bullseye-pgdg main
deb http://apt-archive.postgresql.org/pub/repos/apt/ bullseye-pgdg-archive main
deb-src http://apt-archive.postgresql.org/pub/repos/apt/ bullseye-pgdg-archive main

View File

@@ -0,0 +1,5 @@
deb http://apt.postgresql.org/pub/repos/apt/ buster-pgdg main
deb-src http://apt.postgresql.org/pub/repos/apt/ buster-pgdg main
deb http://apt-archive.postgresql.org/pub/repos/apt/ buster-pgdg-archive main
deb-src http://apt-archive.postgresql.org/pub/repos/apt/ buster-pgdg-archive main

View File

@@ -0,0 +1,5 @@
deb http://apt.postgresql.org/pub/repos/apt/ cosmic-pgdg main
deb-src http://apt.postgresql.org/pub/repos/apt/ cosmic-pgdg main
deb http://ppa.launchpad.net/groonga/ppa/ubuntu cosmic main
deb-src http://ppa.launchpad.net/groonga/ppa/ubuntu cosmic main

View File

@@ -0,0 +1,44 @@
#!/usr/bin/env bash
if [[ ! -e /usr/share/doc/groonga-apt-source/copyright ]]; then
remove_pgroonga_apt_tmp_dir() {
rm -rf "$pgroonga_apt_tmp_dir"
}
pgroonga_apt_tmp_dir=$(mktemp --directory)
trap remove_pgroonga_apt_tmp_dir EXIT
{
cd "$pgroonga_apt_tmp_dir" || exit 1
tmp_gpg_home=.gnupg
pgroonga_apt_sign_key="$LIST_PATH/pgroonga-packages.groonga.org.asc"
gpg --homedir="$tmp_gpg_home" --import "$pgroonga_apt_sign_key"
# Find fingerprint of the first key.
pgroonga_apt_sign_key_fingerprint=$(
gpg --homedir="$tmp_gpg_home" --with-colons --list-keys \
| grep '^fpr:' \
| cut --delimiter=: --fields=10 \
| head --lines=1
)
os_info="$(. /etc/os-release && printf '%s\n' "$ID" "$VERSION_CODENAME")"
{
read -r distribution
read -r release
} <<<"$os_info"
groonga_apt_source_deb="groonga-apt-source-latest-$release.deb"
groonga_apt_source_deb_sign="$groonga_apt_source_deb.asc.$pgroonga_apt_sign_key_fingerprint"
wget "https://packages.groonga.org/$distribution/$groonga_apt_source_deb"
wget "https://packages.groonga.org/$distribution/$groonga_apt_source_deb_sign"
gpg \
--homedir="$tmp_gpg_home" \
--verify \
"$groonga_apt_source_deb_sign" \
"$groonga_apt_source_deb"
# To suppress the following warning by "apt-get install":
# N: Download is performed unsandboxed as root as file
# '.../groonga-apt-source-latest-$release.deb' couldn't be
# accessed by user '_apt'. - pkgAcquire::Run (13: Permission denied)
chown _apt .
apt-get -y install "./$groonga_apt_source_deb"
}
touch "$STAMP_FILE"
fi

View File

@@ -0,0 +1,5 @@
deb http://apt.postgresql.org/pub/repos/apt/ disco-pgdg main
deb-src http://apt.postgresql.org/pub/repos/apt/ disco-pgdg main
deb http://ppa.launchpad.net/groonga/ppa/ubuntu disco main
deb-src http://ppa.launchpad.net/groonga/ppa/ubuntu disco main

View File

@@ -0,0 +1,5 @@
deb http://apt.postgresql.org/pub/repos/apt/ eoan-pgdg main
deb-src http://apt.postgresql.org/pub/repos/apt/ eoan-pgdg main
deb http://ppa.launchpad.net/groonga/ppa/ubuntu eoan main
deb-src http://ppa.launchpad.net/groonga/ppa/ubuntu eoan main

View File

@@ -0,0 +1,8 @@
deb http://apt.postgresql.org/pub/repos/apt/ focal-pgdg main
deb-src http://apt.postgresql.org/pub/repos/apt/ focal-pgdg main
deb http://apt-archive.postgresql.org/pub/repos/apt/ focal-pgdg-archive main
deb-src http://apt-archive.postgresql.org/pub/repos/apt/ focal-pgdg-archive main
deb http://ppa.launchpad.net/groonga/ppa/ubuntu focal main
deb-src http://ppa.launchpad.net/groonga/ppa/ubuntu focal main

View File

@@ -0,0 +1,19 @@
Package: postgresql-10 postgresql-client-10 postgresql-server-dev-10
Pin: version 10.19-*
Pin-Priority: 1000
Package: postgresql-11 postgresql-client-11 postgresql-server-dev-11
Pin: version 11.14-*
Pin-Priority: 1000
Package: postgresql-12 postgresql-client-12 postgresql-server-dev-12
Pin: version 12.9-*
Pin-Priority: 1000
Package: postgresql-13 postgresql-client-13 postgresql-server-dev-13
Pin: version 13.5-*
Pin-Priority: 1000
Package: postgresql-14 postgresql-client-14 postgresql-server-dev-14
Pin: version 14.1-*
Pin-Priority: 1000

View File

@@ -0,0 +1,2 @@
deb http://debathena.mit.edu/apt bionic debathena debathena-config
deb-src http://debathena.mit.edu/apt bionic debathena debathena-config

View File

@@ -5,7 +5,7 @@ import os
import re
import sys
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.path.append(BASE_DIR)
from scripts.lib.setup_path import setup_path

View File

@@ -2,25 +2,25 @@
#
# Delete the "guest" default user and replace it with a Zulip user
# with a real password
set -e
set -x
set -eu
if [ "$EUID" -eq 0 ]; then
rabbitmqctl=(rabbitmqctl)
sudo=()
else
rabbitmqctl=(sudo rabbitmqctl)
sudo=(sudo)
fi
if [ -n "$RABBITMQ_NODE" ]; then
rabbitmqctl+=(-n "$RABBITMQ_NODE")
fi
# If the RabbitMQ distribution cookie is insecure, reset it and
# restart RabbitMQ.
"${sudo[@]}" "$(dirname "$0")/generate-rabbitmq-cookie"
RABBITMQ_USERNAME=$("$(dirname "$0")/../get-django-setting" RABBITMQ_USERNAME)
RABBITMQ_PASSWORD=$("$(dirname "$0")/../get-django-setting" RABBITMQ_PASSWORD)
# Wait for RabbitMQ to start up
try_ping() {
# `rabbitmqctl ping` requires 3.7.6 or newer
out="$("${rabbitmqctl[@]}" eval 'net_adm:ping(node()).')" && [ "$out" = 'pong' ]
out="$("${sudo[@]}" rabbitmqctl eval 'net_adm:ping(node()).')" && [ "$out" = 'pong' ]
}
retries=9
while ! try_ping 2>/dev/null; do
@@ -31,9 +31,9 @@ while ! try_ping 2>/dev/null; do
fi
done
"${rabbitmqctl[@]}" delete_user "$RABBITMQ_USERNAME" || true
"${rabbitmqctl[@]}" delete_user zulip || true
"${rabbitmqctl[@]}" delete_user guest || true
"${rabbitmqctl[@]}" add_user "$RABBITMQ_USERNAME" "$RABBITMQ_PASSWORD"
"${rabbitmqctl[@]}" set_user_tags "$RABBITMQ_USERNAME" administrator
"${rabbitmqctl[@]}" set_permissions -p / "$RABBITMQ_USERNAME" '.*' '.*' '.*'
"${sudo[@]}" rabbitmqctl delete_user "$RABBITMQ_USERNAME" || true
"${sudo[@]}" rabbitmqctl delete_user zulip || true
"${sudo[@]}" rabbitmqctl delete_user guest || true
"${sudo[@]}" rabbitmqctl add_user "$RABBITMQ_USERNAME" "$RABBITMQ_PASSWORD"
"${sudo[@]}" rabbitmqctl set_user_tags "$RABBITMQ_USERNAME" administrator
"${sudo[@]}" rabbitmqctl set_permissions -p / "$RABBITMQ_USERNAME" '.*' '.*' '.*'

View File

@@ -0,0 +1,27 @@
#!/usr/bin/env bash
#
# rabbitmq sets an insecure "magic cookie" which is used for auth;
# reset it to be longer.
set -eu
cookiefile=/var/lib/rabbitmq/.erlang.cookie
# If the RabbitMQ distribution cookie is insecure, reset it
if [ ! -f "$cookiefile" ] || ! size="$(wc -c "$cookiefile")" || [ "${size%% *}" -le 20 ]; then
running=0
if service rabbitmq-server status >/dev/null; then
running=1
service rabbitmq-server stop
fi
echo "Setting a more secure RabbitMQ distribution magic cookie"
cookie="$(LC_ALL=C tr -dc '[:alnum:]' </dev/urandom | head -c255)"
[ "${#cookie}" -eq 255 ] # make sure tr wasnt OOM-killed
tmpfile="$(mktemp "$cookiefile.XXXXXXXXXX")"
chown rabbitmq: "$tmpfile"
printf '%s' "$cookie" >"$tmpfile"
mv "$tmpfile" "$cookiefile"
if [ "$running" == "1" ]; then
service rabbitmq-server start
fi
fi

View File

@@ -6,15 +6,15 @@ import os
import sys
import time
import psycopg2
import psycopg2.extensions
from psycopg2.sql import SQL, Identifier
sys.path.append(os.path.join(os.path.dirname(__file__), "../.."))
from scripts.lib.setup_path import setup_path
setup_path()
import psycopg2
import psycopg2.extensions
from psycopg2.sql import SQL, Identifier
from scripts.lib.zulip_tools import su_to_zulip
su_to_zulip()
@@ -65,9 +65,7 @@ FROM
JOIN pg_class AS trel ON trel.oid = i.indrelid
JOIN pg_namespace AS tnsp ON trel.relnamespace = tnsp.oid
JOIN pg_class AS irel ON irel.oid = i.indexrelid
CROSS JOIN unnest(i.indkey) colnum
JOIN pg_attribute AS a ON trel.oid = a.attrelid
AND a.attnum = colnum
JOIN pg_attribute AS a ON a.attrelid = i.indexrelid
WHERE tnsp.nspname = 'zulip'
AND a.attcollation != 0
GROUP BY 1, 2, i.indrelid

View File

@@ -5,7 +5,7 @@ set -e
usage() {
cat <<EOF >&2
Usage: $0 --email=admin@example.com [--method={webroot|standalone}] \
[--no-zulip-conf] hostname.example.com [another.example.com]
hostname.example.com [another.example.com]
EOF
exit 1
}
@@ -16,7 +16,7 @@ if [ "$EUID" -ne 0 ]; then
fi
method=webroot
args="$(getopt -o '' --long help,email:,method:,deploy-hook:,no-zulip-conf,agree-tos -n "$0" -- "$@")"
args="$(getopt -o '' --long help,email:,method:,skip-symlink,agree-tos -n "$0" -- "$@")"
eval "set -- $args"
while true; do
case "$1" in
@@ -30,19 +30,14 @@ while true; do
shift
shift
;;
--deploy-hook)
deploy_hook=(--deploy-hook "$2")
shift
--skip-symlink)
skip_symlink=1
shift
;;
--agree-tos)
agree_tos=--agree-tos
shift
;;
--no-zulip-conf)
no_zulip_conf=1
shift
;;
--help)
show_help=1
shift
@@ -71,7 +66,7 @@ fi
case "$method" in
standalone)
method_args=(--standalone)
method_args=(--standalone --no-directory-hooks)
;;
webroot)
method_args=(--webroot '--webroot-path=/var/lib/zulip/certbot-webroot/')
@@ -112,7 +107,6 @@ esac
certbot certonly "${method_args[@]}" \
"${HOSTNAMES[@]}" -m "$EMAIL" \
$agree_tos \
"${deploy_hook[@]}" \
--force-interactive --no-eff-email
symlink_with_backup() {
@@ -125,22 +119,18 @@ symlink_with_backup() {
ln -nsf "$1" "$2"
}
if [ ${#deploy_hook} -eq 0 ]; then
# If no deploy hook was specified, assume we're deploying to the default
# location Zulip wants.
if [ -z "$skip_symlink" ]; then
CERT_DIR=/etc/letsencrypt/live/"$DOMAIN"
symlink_with_backup "$CERT_DIR"/privkey.pem /etc/ssl/private/zulip.key
symlink_with_backup "$CERT_DIR"/fullchain.pem /etc/ssl/certs/zulip.combined-chain.crt
fi
# "certbot certonly" does not run deploy hooks, so reload nginx if
# need be to pick up the new certificate.
case "$method" in
webroot)
service nginx reload
;;
esac
if [ -z "$no_zulip_conf" ]; then
crudini --set /etc/zulip/zulip.conf certbot auto_renew yes
fi
echo "Certbot SSL certificate configuration succeeded."

View File

@@ -345,8 +345,15 @@ export function set_up() {
}
$("#show_api_key").on("click", "button.regenerate_api_key", (e) => {
const email = page_params.delivery_email;
const api_key = $("#api_key_value").text();
const authorization_header = "Basic " + btoa(`${email}:${api_key}`);
channel.post({
url: "/json/users/me/api_key/regenerate",
// This endpoint is only accessible with the previous API key,
// via our usual HTTP Basic auth mechanism.
url: "/api/v1/users/me/api_key/regenerate",
headers: {Authorization: authorization_header},
success(data) {
$("#api_key_value").text(data.api_key);
},

View File

@@ -278,6 +278,7 @@ def install_apt_deps(deps_to_install: List[str]) -> None:
"apt-get",
"-y",
"install",
"--allow-downgrades",
"--no-install-recommends",
*deps_to_install,
]

View File

@@ -1,6 +1,6 @@
import os
ZULIP_VERSION = "4.7"
ZULIP_VERSION = "4.10"
# Add information on number of commits and commit hash to version, if available
zulip_git_version_file = os.path.join(
@@ -14,7 +14,7 @@ ZULIP_VERSION = lines.pop(0).strip()
ZULIP_MERGE_BASE = lines.pop(0).strip()
LATEST_MAJOR_VERSION = "4.0"
LATEST_RELEASE_VERSION = "4.7"
LATEST_RELEASE_VERSION = "4.10"
LATEST_RELEASE_ANNOUNCEMENT = "https://blog.zulip.com/2021/05/13/zulip-4-0-released/"
# Versions of the desktop app below DESKTOP_MINIMUM_VERSION will be
@@ -47,4 +47,4 @@ API_FEATURE_LEVEL = 65
# historical commits sharing the same major version, in which case a
# minor version bump suffices.
PROVISION_VERSION = "146.1"
PROVISION_VERSION = "146.2"

View File

@@ -4,8 +4,9 @@ import random
import secrets
import shutil
import subprocess
import zipfile
from collections import defaultdict
from typing import Any, Dict, Iterator, List, Optional, Set, Tuple
from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Type, TypeVar
import orjson
import requests
@@ -55,6 +56,41 @@ AddedChannelsT = Dict[str, Tuple[str, int]]
AddedMPIMsT = Dict[str, Tuple[str, int]]
DMMembersT = Dict[str, Tuple[str, str]]
SlackToZulipRecipientT = Dict[str, int]
# Generic type for SlackBotEmail class
SlackBotEmailT = TypeVar("SlackBotEmailT", bound="SlackBotEmail")
class SlackBotEmail:
duplicate_email_count: Dict[str, int] = {}
# Mapping of `bot_id` to final email assigned to the bot.
assigned_email: Dict[str, str] = {}
@classmethod
def get_email(cls: Type[SlackBotEmailT], user_profile: ZerverFieldsT, domain_name: str) -> str:
slack_bot_id = user_profile["bot_id"]
if slack_bot_id in cls.assigned_email:
return cls.assigned_email[slack_bot_id]
if "real_name_normalized" in user_profile:
slack_bot_name = user_profile["real_name_normalized"]
elif "first_name" in user_profile:
slack_bot_name = user_profile["first_name"]
else:
raise AssertionError("Could not identify bot type")
email = slack_bot_name.replace("Bot", "").replace(" ", "") + f"-bot@{domain_name}"
if email in cls.duplicate_email_count:
email_prefix, email_suffix = email.split("@")
email_prefix += cls.duplicate_email_count[email]
email = "@".join([email_prefix, email_suffix])
# Increment the duplicate email count
cls.duplicate_email_count[email] += 1
else:
cls.duplicate_email_count[email] = 1
cls.assigned_email[slack_bot_id] = email
return email
def rm_tree(path: str) -> None:
@@ -367,13 +403,7 @@ def get_user_email(user: ZerverFieldsT, domain_name: str) -> str:
if user["is_mirror_dummy"]:
return "{}@{}.slack.com".format(user["name"], user["team_domain"])
if "bot_id" in user["profile"]:
if "real_name_normalized" in user["profile"]:
slack_bot_name = user["profile"]["real_name_normalized"]
elif "first_name" in user["profile"]:
slack_bot_name = user["profile"]["first_name"]
else:
raise AssertionError("Could not identify bot type")
return slack_bot_name.replace("Bot", "").replace(" ", "") + f"-bot@{domain_name}"
return SlackBotEmail.get_email(user["profile"], domain_name)
if get_user_full_name(user).lower() == "slackbot":
return f"imported-slackbot-bot@{domain_name}"
raise AssertionError(f"Could not find email address for Slack user {user}")
@@ -1065,8 +1095,6 @@ def get_attachment_path_and_content(fileinfo: ZerverFieldsT, realm_id: int) -> T
s3_path = "/".join(
[
str(realm_id),
"SlackImportAttachment", # This is a special placeholder which should be kept
# in sync with 'exports.py' function 'import_message_data'
format(random.randint(0, 255), "x"),
secrets.token_urlsafe(18),
sanitize_name(fileinfo["name"]),
@@ -1246,7 +1274,7 @@ def fetch_team_icons(
return records
def do_convert_data(slack_zip_file: str, output_dir: str, token: str, threads: int = 6) -> None:
def do_convert_data(original_path: str, output_dir: str, token: str, threads: int = 6) -> None:
# Subdomain is set by the user while running the import command
realm_subdomain = ""
realm_id = 0
@@ -1254,15 +1282,24 @@ def do_convert_data(slack_zip_file: str, output_dir: str, token: str, threads: i
check_token_access(token)
slack_data_dir = slack_zip_file.replace(".zip", "")
if not os.path.exists(slack_data_dir):
os.makedirs(slack_data_dir)
os.makedirs(output_dir, exist_ok=True)
if os.listdir(output_dir):
raise Exception("Output directory should be empty!")
subprocess.check_call(["unzip", "-q", slack_zip_file, "-d", slack_data_dir])
if os.path.isfile(original_path) and original_path.endswith(".zip"):
slack_data_dir = original_path.replace(".zip", "")
if not os.path.exists(slack_data_dir):
os.makedirs(slack_data_dir)
with zipfile.ZipFile(original_path) as zipObj:
zipObj.extractall(slack_data_dir)
elif os.path.isdir(original_path):
slack_data_dir = original_path
else:
raise ValueError(f"Don't know how to import Slack data from {original_path}")
if not os.path.isfile(os.path.join(slack_data_dir, "channels.json")):
raise ValueError(f"{original_path} does not have the layout we expect from a Slack export!")
# We get the user data from the legacy token method of Slack API, which is depreciated
# but we use it as the user email data is provided only in this method
@@ -1332,7 +1369,9 @@ def do_convert_data(slack_zip_file: str, output_dir: str, token: str, threads: i
create_converted_data_files(attachment, output_dir, "/attachment.json")
create_converted_data_files(realm_icon_records, output_dir, "/realm_icons/records.json")
rm_tree(slack_data_dir)
# Clean up the directory if we unpacked it ourselves.
if original_path != slack_data_dir:
rm_tree(slack_data_dir)
subprocess.check_call(["tar", "-czf", output_dir + ".tar.gz", output_dir, "-P"])
logging.info("######### DATA CONVERSION FINISHED #########\n")
@@ -1352,6 +1391,8 @@ def check_token_access(token: str) -> None:
data = requests.get(
"https://slack.com/api/team.info", headers={"Authorization": "Bearer {}".format(token)}
)
if data.status_code != 200 or not data.json()["ok"]:
raise ValueError("Invalid Slack token: {}".format(token))
has_scopes = set(data.headers.get("x-oauth-scopes", "").split(","))
required_scopes = set(["emoji:read", "users:read", "users:read.email", "team:read"])
missing_scopes = required_scopes - has_scopes

Some files were not shown because too many files have changed in this diff Show More