v1.2.9
This commit is contained in:
		
						commit
						656991b263
					
				
							
								
								
									
										2
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| certs/ | ||||
| data/ | ||||
							
								
								
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| data/* | ||||
| certs/ | ||||
| matterbrige/*.t* | ||||
| tests/certs/ | ||||
| tests/venv/ | ||||
| tests/__pycache__/ | ||||
| *.swp | ||||
							
								
								
									
										9
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| [submodule "tests/bats/bats-support"] | ||||
| 	path = tests/bats/bats-support | ||||
| 	url = https://github.com/bats-core/bats-support.git | ||||
| [submodule "tests/bats/bats-core"] | ||||
| 	path = tests/bats/bats-core | ||||
| 	url = https://github.com/bats-core/bats-core.git | ||||
| [submodule "tests/bats/bats-assert"] | ||||
| 	path = tests/bats/bats-assert | ||||
| 	url = https://github.com/bats-core/bats-assert.git | ||||
							
								
								
									
										119
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,119 @@ | ||||
| # Changelog | ||||
| 
 | ||||
| ## v1.2.9 | ||||
| 
 | ||||
| * Updated to Prosody version 0.12.3. | ||||
| * Added _prosody-migrator_ tool. | ||||
| * Added _Matterbridge_. | ||||
| * GPLv3 licensed. | ||||
| 
 | ||||
| ## v1.2.8 | ||||
| 
 | ||||
| * Updated to Prosody version [0.12.1](https://blog.prosody.im/prosody-0.12.1-released/). | ||||
| 
 | ||||
| ## v1.2.7 | ||||
| 
 | ||||
| * Updated to Prosody version [0.12.0](https://blog.prosody.im/prosody-0.12.0-released/). | ||||
| * Updated luarocks to version 3.9.0. | ||||
| 
 | ||||
| ## v1.2.6 | ||||
| 
 | ||||
| * Updated to Prosody version [0.11.13](https://blog.prosody.im/prosody-0.11.13-released/). | ||||
| 
 | ||||
| ## v1.2.5 | ||||
| 
 | ||||
| * Updated to Prosody version [0.11.12](https://blog.prosody.im/prosody-0.11.12-released/). | ||||
| 
 | ||||
| ## v1.2.4 | ||||
| 
 | ||||
| * Updated to Prosody version [0.11.11](https://blog.prosody.im/prosody-0.11.11-released/). | ||||
| * Updated luarocks to version 3.8.0. | ||||
| 
 | ||||
| ## v1.2.3 | ||||
| 
 | ||||
| * Updated to Prosody version [0.11.10](https://blog.prosody.im/prosody-0.11.10-released/). | ||||
| 
 | ||||
| ## v1.2.2 | ||||
| 
 | ||||
| - Update debian from buster-slim to bullseye-slim (#27) | ||||
| 
 | ||||
| ## v1.2.1 | ||||
| 
 | ||||
| * Updated to Prosody version [0.11.9](https://blog.prosody.im/prosody-0.11.9-released/). | ||||
| 
 | ||||
| ## v1.2.0 | ||||
| 
 | ||||
| ### New features | ||||
| 
 | ||||
| * New environment variables for database settings. It is now possible to use MariaDB or Postgres instead of SQLite. SQLite is the default. See [README](https://github.com/SaraSmiseth/prosody#environment-variables). | ||||
| 
 | ||||
| ### Updates | ||||
| 
 | ||||
| * Updated luarocks to version 3.7.0. | ||||
| 
 | ||||
| ## v1.1.4 | ||||
| 
 | ||||
| ### Updates | ||||
| 
 | ||||
| * Updated to Prosody version [0.11.8](https://blog.prosody.im/prosody-0.11.8-released/). | ||||
| * Updated luarocks to version 3.5.0. | ||||
| 
 | ||||
| ## v1.1.3 | ||||
| 
 | ||||
| ### New features | ||||
| 
 | ||||
| * Set pidfile in prosody.cfg.lua. | ||||
| * Created a tests folder which contains pytest and bats tests. | ||||
| 
 | ||||
| ### Bug fixes | ||||
| 
 | ||||
| * Fixed using list ENV variables with multiple values. | ||||
| 
 | ||||
| ## v1.1.2 | ||||
| 
 | ||||
| ### Updates | ||||
| 
 | ||||
| * Updated to Prosody version [0.11.7](https://blog.prosody.im/prosody-0.11.7-released/). | ||||
| * Updated luarocks to version 3.4.0. | ||||
| 
 | ||||
| ### New features | ||||
| 
 | ||||
| * Made 04-server_contact_info.cfg.lua configurable with ENV variables. Fixes [#4](https://github.com/SaraSmiseth/prosody/issues/4). | ||||
| * Made 03-e2e-policy.cfg.lua configurable with ENV variables. Fixes [#9](https://github.com/SaraSmiseth/prosody/issues/9). | ||||
| * Added E2E_POLICY_WHITELIST ENV variable to configure e2e_policy_whitelist. Fixes [#10](https://github.com/SaraSmiseth/prosody/issues/10). | ||||
| 
 | ||||
| ### Bug fixes | ||||
| 
 | ||||
| * Cherry picked [commit](https://github.com/zipizap/prosody/commit/fa13a990a1b87745ae5f5fe8297cb0669f9e8779) from [zipizap/prosody](https://github.com/zipizap/prosody) which fixes a bug with env-vars not beeing initialized. | ||||
| 
 | ||||
| ### Other changes | ||||
| 
 | ||||
| * Changed hashing of downloaded packages in Dockerfile to sha256. | ||||
| 
 | ||||
| ## v1.1.1 | ||||
| 
 | ||||
| * Updated to Prosody version [0.11.6](https://blog.prosody.im/prosody-0.11.6-released/). | ||||
| * Replace "master" with "dev". | ||||
| 
 | ||||
| ## v1.1.0 | ||||
| 
 | ||||
| ### New features | ||||
| 
 | ||||
| * Enable "announce" and "lastactivity" modules. | ||||
| * Add PROSODY_ADMINS to specify who is an administrator. Fixes #7 | ||||
| 
 | ||||
| ### Breaking changes | ||||
| 
 | ||||
| * Move global ssl section to https_ssl and legacy_ssl_ssl section. It is only needed there. #3 | ||||
|   * <https://prosody.im/doc/ports#ssl_configuration> | ||||
| 
 | ||||
| As explained in the [README](https://github.com/SaraSmiseth/prosody#ssl-certificates) this setup uses automatic location to find your certs. This did not work correctly before this change. It just always used the main certificate defined with the global `ssl` config setting. This setting was removed and for the [services](https://prosody.im/doc/certificates#service_certificates) that do not use automatic location new global settings were introduced. These are `legacy_ssl_ssl` and `https_ssl`. | ||||
| 
 | ||||
| ### Other changes | ||||
| 
 | ||||
| * Add badges to README. Fixes #5. | ||||
| * Add link to official documentation on certificate permissions to README. Related to #3 | ||||
| 
 | ||||
| ## v1.0.0 | ||||
| 
 | ||||
| * First version | ||||
							
								
								
									
										120
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,120 @@ | ||||
| FROM debian:bullseye-slim | ||||
| 
 | ||||
| ARG BUILD_DATE | ||||
| ARG VCS_REF | ||||
| ARG VERSION | ||||
| 
 | ||||
| ARG LUAROCKS_VERSION=3.9.2 | ||||
| ARG PROSODY_VERSION=0.12.3 | ||||
| 
 | ||||
| ARG LUAROCKS_SHA256=bca6e4ecc02c203e070acdb5f586045d45c078896f6236eb46aa33ccd9b94edb | ||||
| ARG PROSODY_DOWNLOAD_SHA256=35da0d031ff46040a2d638e004d4255e249b6323fe6212db9ddd76b401db2101 | ||||
| 
 | ||||
| LABEL luarocks.version="${LUAROCKS_VERSION}" | ||||
| LABEL org.opencontainers.image.authors="Wproject Garapenak" | ||||
| LABEL org.opencontainers.image.created="${BUILD_DATE}" | ||||
| LABEL org.opencontainers.image.description="This docker image provides you with a configured Prosody XMPP server." | ||||
| LABEL org.opencontainers.image.documentation="https://git.lainoa.eus/aitzol/prosody-docker/src/branch/main/readme.md" | ||||
| LABEL org.opencontainers.image.revision="${VCS_REF}" | ||||
| LABEL org.opencontainers.image.source="https://git.lainoa.eus/aitzol/prosody-docker" | ||||
| LABEL org.opencontainers.image.title="prosody" | ||||
| LABEL org.opencontainers.image.url="https://git.lainoa.eus/aitzol/prosody-docker" | ||||
| LABEL org.opencontainers.image.vendor="Wproject Garapenak" | ||||
| LABEL org.opencontainers.image.version="${VERSION}" | ||||
| LABEL prosody.version="${PROSODY_VERSION}" | ||||
| 
 | ||||
| RUN apt-get update \ | ||||
|  && DEBIAN_FRONTEND=noninteractive apt-get install -y \ | ||||
|       libevent-dev `# this is no build dependency, but needed for luaevent` \ | ||||
|       libicu67 \ | ||||
|       libidn11 \ | ||||
|       libpq-dev \ | ||||
|       libsqlite3-0 \ | ||||
|       lua5.2 \ | ||||
|       lua-bitop \ | ||||
|       lua-dbi-mysql \ | ||||
|       lua-expat \ | ||||
|       lua-filesystem \ | ||||
|       lua-ldap \ | ||||
|       lua-socket \ | ||||
|       lua-sec \ | ||||
|       lua-unbound \ | ||||
|       wget \ | ||||
|  && apt-get clean \ | ||||
|  && rm -rf /var/lib/apt/lists/* | ||||
| 
 | ||||
| RUN buildDeps='gcc git libc6-dev libidn11-dev liblua5.2-dev libsqlite3-dev libssl-dev libicu-dev make unzip' \ | ||||
|  && set -x \ | ||||
|  && apt-get update && apt-get install -y $buildDeps --no-install-recommends \ | ||||
|  && rm -rf /var/lib/apt/lists/* \ | ||||
|  \ | ||||
|  # prosody \ | ||||
|  && wget -O prosody.tar.gz "https://prosody.im/downloads/source/prosody-${PROSODY_VERSION}.tar.gz" \ | ||||
|  && echo "${PROSODY_DOWNLOAD_SHA256} *prosody.tar.gz" | sha256sum -c - \ | ||||
|  && mkdir -p /usr/src/prosody \ | ||||
|  && tar -xzf prosody.tar.gz -C /usr/src/prosody --strip-components=1 \ | ||||
|  && rm prosody.tar.gz \ | ||||
|  && cd /usr/src/prosody && ./configure \ | ||||
|  && make \ | ||||
|  && make install \ | ||||
|  # prosody-migrator \ | ||||
|  && cd /usr/src/prosody/tools/migration \ | ||||
|  && make install \ | ||||
|  && cd / && rm -r /usr/src/prosody \ | ||||
|  \ | ||||
|  && mkdir /usr/src/luarocks \ | ||||
|  && cd /usr/src/luarocks \ | ||||
|  && wget https://luarocks.org/releases/luarocks-${LUAROCKS_VERSION}.tar.gz \ | ||||
|  && echo "${LUAROCKS_SHA256} luarocks-${LUAROCKS_VERSION}.tar.gz" | sha256sum -c - \ | ||||
|  && tar zxpf luarocks-${LUAROCKS_VERSION}.tar.gz \ | ||||
|  && cd luarocks-${LUAROCKS_VERSION} \ | ||||
|  && ./configure \ | ||||
|  && make bootstrap \ | ||||
|  && cd / && rm -r /usr/src/luarocks \ | ||||
|  \ | ||||
|  && luarocks install luaevent \ | ||||
|  && luarocks install luadbi \ | ||||
|  `#&& luarocks install luadbi-mysql MYSQL_INCDIR=/usr/include/mariadb/` \ | ||||
|  && luarocks install luadbi-postgresql POSTGRES_INCDIR=/usr/include/postgresql/ \ | ||||
|  && luarocks install luadbi-sqlite3 \ | ||||
|  && luarocks install stringy \ | ||||
|  \ | ||||
|  && apt-get purge -y --auto-remove $buildDeps | ||||
| 
 | ||||
| EXPOSE 5000 5222 5223 5269 5347 5280 5281 | ||||
| 
 | ||||
| RUN groupadd -r prosody \ | ||||
|  && useradd -r -g prosody prosody \ | ||||
|  && chown prosody:prosody /usr/local/var/lib/prosody | ||||
| 
 | ||||
| RUN mkdir -p /var/run/prosody/ \ | ||||
|  && chown prosody:prosody /var/run/prosody/ | ||||
| 
 | ||||
| ENV __FLUSH_LOG yes | ||||
| 
 | ||||
| VOLUME ["/usr/local/var/lib/prosody"] | ||||
| 
 | ||||
| COPY prosody.cfg.lua /usr/local/etc/prosody/prosody.cfg.lua | ||||
| COPY migrator.cfg.lua /usr/local/etc/prosody/migrator.cfg.lua | ||||
| COPY docker-entrypoint.bash /entrypoint.bash | ||||
| COPY conf.d/*.cfg.lua /usr/local/etc/prosody/conf.d/ | ||||
| COPY *.bash /usr/local/bin/ | ||||
| 
 | ||||
| RUN download-prosody-modules.bash \ | ||||
|  && docker-prosody-module-install.bash \ | ||||
|         bookmarks `# XEP-0411: Bookmarks Conversion` \ | ||||
|         carbons `# message carbons (XEP-0280)` \ | ||||
|         cloud_notify `# XEP-0357: Push Notifications` \ | ||||
|         csi `# client state indication (XEP-0352)` \ | ||||
|         e2e_policy `# require end-2-end encryption` \ | ||||
|         filter_chatstates `# disable "X is typing" type messages` \ | ||||
|         smacks `# stream management (XEP-0198)` \ | ||||
|         throttle_presence `# presence throttling in CSI` \ | ||||
|         http_upload `# file sharing (XEP-0363)` \ | ||||
|         vcard_muc `# XEP-0153: vCard-Based Avatar (MUC)` \ | ||||
|  && rm -rf "/usr/src/prosody-modules" | ||||
| RUN echo "TLS_REQCERT allow" >> /etc/ldap/ldap.conf | ||||
| USER prosody | ||||
| 
 | ||||
| ENTRYPOINT ["/entrypoint.bash"] | ||||
| CMD ["prosody", "-F"] | ||||
							
								
								
									
										674
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										674
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,674 @@ | ||||
|                     GNU GENERAL PUBLIC LICENSE | ||||
|                        Version 3, 29 June 2007 | ||||
| 
 | ||||
|  Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> | ||||
|  Everyone is permitted to copy and distribute verbatim copies | ||||
|  of this license document, but changing it is not allowed. | ||||
| 
 | ||||
|                             Preamble | ||||
| 
 | ||||
|   The GNU General Public License is a free, copyleft license for | ||||
| software and other kinds of works. | ||||
| 
 | ||||
|   The licenses for most software and other practical works are designed | ||||
| to take away your freedom to share and change the works.  By contrast, | ||||
| the GNU General Public License is intended to guarantee your freedom to | ||||
| share and change all versions of a program--to make sure it remains free | ||||
| software for all its users.  We, the Free Software Foundation, use the | ||||
| GNU General Public License for most of our software; it applies also to | ||||
| any other work released this way by its authors.  You can apply it to | ||||
| your programs, too. | ||||
| 
 | ||||
|   When we speak of free software, we are referring to freedom, not | ||||
| price.  Our General Public Licenses are designed to make sure that you | ||||
| have the freedom to distribute copies of free software (and charge for | ||||
| them if you wish), that you receive source code or can get it if you | ||||
| want it, that you can change the software or use pieces of it in new | ||||
| free programs, and that you know you can do these things. | ||||
| 
 | ||||
|   To protect your rights, we need to prevent others from denying you | ||||
| these rights or asking you to surrender the rights.  Therefore, you have | ||||
| certain responsibilities if you distribute copies of the software, or if | ||||
| you modify it: responsibilities to respect the freedom of others. | ||||
| 
 | ||||
|   For example, if you distribute copies of such a program, whether | ||||
| gratis or for a fee, you must pass on to the recipients the same | ||||
| freedoms that you received.  You must make sure that they, too, receive | ||||
| or can get the source code.  And you must show them these terms so they | ||||
| know their rights. | ||||
| 
 | ||||
|   Developers that use the GNU GPL protect your rights with two steps: | ||||
| (1) assert copyright on the software, and (2) offer you this License | ||||
| giving you legal permission to copy, distribute and/or modify it. | ||||
| 
 | ||||
|   For the developers' and authors' protection, the GPL clearly explains | ||||
| that there is no warranty for this free software.  For both users' and | ||||
| authors' sake, the GPL requires that modified versions be marked as | ||||
| changed, so that their problems will not be attributed erroneously to | ||||
| authors of previous versions. | ||||
| 
 | ||||
|   Some devices are designed to deny users access to install or run | ||||
| modified versions of the software inside them, although the manufacturer | ||||
| can do so.  This is fundamentally incompatible with the aim of | ||||
| protecting users' freedom to change the software.  The systematic | ||||
| pattern of such abuse occurs in the area of products for individuals to | ||||
| use, which is precisely where it is most unacceptable.  Therefore, we | ||||
| have designed this version of the GPL to prohibit the practice for those | ||||
| products.  If such problems arise substantially in other domains, we | ||||
| stand ready to extend this provision to those domains in future versions | ||||
| of the GPL, as needed to protect the freedom of users. | ||||
| 
 | ||||
|   Finally, every program is threatened constantly by software patents. | ||||
| States should not allow patents to restrict development and use of | ||||
| software on general-purpose computers, but in those that do, we wish to | ||||
| avoid the special danger that patents applied to a free program could | ||||
| make it effectively proprietary.  To prevent this, the GPL assures that | ||||
| patents cannot be used to render the program non-free. | ||||
| 
 | ||||
|   The precise terms and conditions for copying, distribution and | ||||
| modification follow. | ||||
| 
 | ||||
|                        TERMS AND CONDITIONS | ||||
| 
 | ||||
|   0. Definitions. | ||||
| 
 | ||||
|   "This License" refers to version 3 of the GNU General Public License. | ||||
| 
 | ||||
|   "Copyright" also means copyright-like laws that apply to other kinds of | ||||
| works, such as semiconductor masks. | ||||
| 
 | ||||
|   "The Program" refers to any copyrightable work licensed under this | ||||
| License.  Each licensee is addressed as "you".  "Licensees" and | ||||
| "recipients" may be individuals or organizations. | ||||
| 
 | ||||
|   To "modify" a work means to copy from or adapt all or part of the work | ||||
| in a fashion requiring copyright permission, other than the making of an | ||||
| exact copy.  The resulting work is called a "modified version" of the | ||||
| earlier work or a work "based on" the earlier work. | ||||
| 
 | ||||
|   A "covered work" means either the unmodified Program or a work based | ||||
| on the Program. | ||||
| 
 | ||||
|   To "propagate" a work means to do anything with it that, without | ||||
| permission, would make you directly or secondarily liable for | ||||
| infringement under applicable copyright law, except executing it on a | ||||
| computer or modifying a private copy.  Propagation includes copying, | ||||
| distribution (with or without modification), making available to the | ||||
| public, and in some countries other activities as well. | ||||
| 
 | ||||
|   To "convey" a work means any kind of propagation that enables other | ||||
| parties to make or receive copies.  Mere interaction with a user through | ||||
| a computer network, with no transfer of a copy, is not conveying. | ||||
| 
 | ||||
|   An interactive user interface displays "Appropriate Legal Notices" | ||||
| to the extent that it includes a convenient and prominently visible | ||||
| feature that (1) displays an appropriate copyright notice, and (2) | ||||
| tells the user that there is no warranty for the work (except to the | ||||
| extent that warranties are provided), that licensees may convey the | ||||
| work under this License, and how to view a copy of this License.  If | ||||
| the interface presents a list of user commands or options, such as a | ||||
| menu, a prominent item in the list meets this criterion. | ||||
| 
 | ||||
|   1. Source Code. | ||||
| 
 | ||||
|   The "source code" for a work means the preferred form of the work | ||||
| for making modifications to it.  "Object code" means any non-source | ||||
| form of a work. | ||||
| 
 | ||||
|   A "Standard Interface" means an interface that either is an official | ||||
| standard defined by a recognized standards body, or, in the case of | ||||
| interfaces specified for a particular programming language, one that | ||||
| is widely used among developers working in that language. | ||||
| 
 | ||||
|   The "System Libraries" of an executable work include anything, other | ||||
| than the work as a whole, that (a) is included in the normal form of | ||||
| packaging a Major Component, but which is not part of that Major | ||||
| Component, and (b) serves only to enable use of the work with that | ||||
| Major Component, or to implement a Standard Interface for which an | ||||
| implementation is available to the public in source code form.  A | ||||
| "Major Component", in this context, means a major essential component | ||||
| (kernel, window system, and so on) of the specific operating system | ||||
| (if any) on which the executable work runs, or a compiler used to | ||||
| produce the work, or an object code interpreter used to run it. | ||||
| 
 | ||||
|   The "Corresponding Source" for a work in object code form means all | ||||
| the source code needed to generate, install, and (for an executable | ||||
| work) run the object code and to modify the work, including scripts to | ||||
| control those activities.  However, it does not include the work's | ||||
| System Libraries, or general-purpose tools or generally available free | ||||
| programs which are used unmodified in performing those activities but | ||||
| which are not part of the work.  For example, Corresponding Source | ||||
| includes interface definition files associated with source files for | ||||
| the work, and the source code for shared libraries and dynamically | ||||
| linked subprograms that the work is specifically designed to require, | ||||
| such as by intimate data communication or control flow between those | ||||
| subprograms and other parts of the work. | ||||
| 
 | ||||
|   The Corresponding Source need not include anything that users | ||||
| can regenerate automatically from other parts of the Corresponding | ||||
| Source. | ||||
| 
 | ||||
|   The Corresponding Source for a work in source code form is that | ||||
| same work. | ||||
| 
 | ||||
|   2. Basic Permissions. | ||||
| 
 | ||||
|   All rights granted under this License are granted for the term of | ||||
| copyright on the Program, and are irrevocable provided the stated | ||||
| conditions are met.  This License explicitly affirms your unlimited | ||||
| permission to run the unmodified Program.  The output from running a | ||||
| covered work is covered by this License only if the output, given its | ||||
| content, constitutes a covered work.  This License acknowledges your | ||||
| rights of fair use or other equivalent, as provided by copyright law. | ||||
| 
 | ||||
|   You may make, run and propagate covered works that you do not | ||||
| convey, without conditions so long as your license otherwise remains | ||||
| in force.  You may convey covered works to others for the sole purpose | ||||
| of having them make modifications exclusively for you, or provide you | ||||
| with facilities for running those works, provided that you comply with | ||||
| the terms of this License in conveying all material for which you do | ||||
| not control copyright.  Those thus making or running the covered works | ||||
| for you must do so exclusively on your behalf, under your direction | ||||
| and control, on terms that prohibit them from making any copies of | ||||
| your copyrighted material outside their relationship with you. | ||||
| 
 | ||||
|   Conveying under any other circumstances is permitted solely under | ||||
| the conditions stated below.  Sublicensing is not allowed; section 10 | ||||
| makes it unnecessary. | ||||
| 
 | ||||
|   3. Protecting Users' Legal Rights From Anti-Circumvention Law. | ||||
| 
 | ||||
|   No covered work shall be deemed part of an effective technological | ||||
| measure under any applicable law fulfilling obligations under article | ||||
| 11 of the WIPO copyright treaty adopted on 20 December 1996, or | ||||
| similar laws prohibiting or restricting circumvention of such | ||||
| measures. | ||||
| 
 | ||||
|   When you convey a covered work, you waive any legal power to forbid | ||||
| circumvention of technological measures to the extent such circumvention | ||||
| is effected by exercising rights under this License with respect to | ||||
| the covered work, and you disclaim any intention to limit operation or | ||||
| modification of the work as a means of enforcing, against the work's | ||||
| users, your or third parties' legal rights to forbid circumvention of | ||||
| technological measures. | ||||
| 
 | ||||
|   4. Conveying Verbatim Copies. | ||||
| 
 | ||||
|   You may convey verbatim copies of the Program's source code as you | ||||
| receive it, in any medium, provided that you conspicuously and | ||||
| appropriately publish on each copy an appropriate copyright notice; | ||||
| keep intact all notices stating that this License and any | ||||
| non-permissive terms added in accord with section 7 apply to the code; | ||||
| keep intact all notices of the absence of any warranty; and give all | ||||
| recipients a copy of this License along with the Program. | ||||
| 
 | ||||
|   You may charge any price or no price for each copy that you convey, | ||||
| and you may offer support or warranty protection for a fee. | ||||
| 
 | ||||
|   5. Conveying Modified Source Versions. | ||||
| 
 | ||||
|   You may convey a work based on the Program, or the modifications to | ||||
| produce it from the Program, in the form of source code under the | ||||
| terms of section 4, provided that you also meet all of these conditions: | ||||
| 
 | ||||
|     a) The work must carry prominent notices stating that you modified | ||||
|     it, and giving a relevant date. | ||||
| 
 | ||||
|     b) The work must carry prominent notices stating that it is | ||||
|     released under this License and any conditions added under section | ||||
|     7.  This requirement modifies the requirement in section 4 to | ||||
|     "keep intact all notices". | ||||
| 
 | ||||
|     c) You must license the entire work, as a whole, under this | ||||
|     License to anyone who comes into possession of a copy.  This | ||||
|     License will therefore apply, along with any applicable section 7 | ||||
|     additional terms, to the whole of the work, and all its parts, | ||||
|     regardless of how they are packaged.  This License gives no | ||||
|     permission to license the work in any other way, but it does not | ||||
|     invalidate such permission if you have separately received it. | ||||
| 
 | ||||
|     d) If the work has interactive user interfaces, each must display | ||||
|     Appropriate Legal Notices; however, if the Program has interactive | ||||
|     interfaces that do not display Appropriate Legal Notices, your | ||||
|     work need not make them do so. | ||||
| 
 | ||||
|   A compilation of a covered work with other separate and independent | ||||
| works, which are not by their nature extensions of the covered work, | ||||
| and which are not combined with it such as to form a larger program, | ||||
| in or on a volume of a storage or distribution medium, is called an | ||||
| "aggregate" if the compilation and its resulting copyright are not | ||||
| used to limit the access or legal rights of the compilation's users | ||||
| beyond what the individual works permit.  Inclusion of a covered work | ||||
| in an aggregate does not cause this License to apply to the other | ||||
| parts of the aggregate. | ||||
| 
 | ||||
|   6. Conveying Non-Source Forms. | ||||
| 
 | ||||
|   You may convey a covered work in object code form under the terms | ||||
| of sections 4 and 5, provided that you also convey the | ||||
| machine-readable Corresponding Source under the terms of this License, | ||||
| in one of these ways: | ||||
| 
 | ||||
|     a) Convey the object code in, or embodied in, a physical product | ||||
|     (including a physical distribution medium), accompanied by the | ||||
|     Corresponding Source fixed on a durable physical medium | ||||
|     customarily used for software interchange. | ||||
| 
 | ||||
|     b) Convey the object code in, or embodied in, a physical product | ||||
|     (including a physical distribution medium), accompanied by a | ||||
|     written offer, valid for at least three years and valid for as | ||||
|     long as you offer spare parts or customer support for that product | ||||
|     model, to give anyone who possesses the object code either (1) a | ||||
|     copy of the Corresponding Source for all the software in the | ||||
|     product that is covered by this License, on a durable physical | ||||
|     medium customarily used for software interchange, for a price no | ||||
|     more than your reasonable cost of physically performing this | ||||
|     conveying of source, or (2) access to copy the | ||||
|     Corresponding Source from a network server at no charge. | ||||
| 
 | ||||
|     c) Convey individual copies of the object code with a copy of the | ||||
|     written offer to provide the Corresponding Source.  This | ||||
|     alternative is allowed only occasionally and noncommercially, and | ||||
|     only if you received the object code with such an offer, in accord | ||||
|     with subsection 6b. | ||||
| 
 | ||||
|     d) Convey the object code by offering access from a designated | ||||
|     place (gratis or for a charge), and offer equivalent access to the | ||||
|     Corresponding Source in the same way through the same place at no | ||||
|     further charge.  You need not require recipients to copy the | ||||
|     Corresponding Source along with the object code.  If the place to | ||||
|     copy the object code is a network server, the Corresponding Source | ||||
|     may be on a different server (operated by you or a third party) | ||||
|     that supports equivalent copying facilities, provided you maintain | ||||
|     clear directions next to the object code saying where to find the | ||||
|     Corresponding Source.  Regardless of what server hosts the | ||||
|     Corresponding Source, you remain obligated to ensure that it is | ||||
|     available for as long as needed to satisfy these requirements. | ||||
| 
 | ||||
|     e) Convey the object code using peer-to-peer transmission, provided | ||||
|     you inform other peers where the object code and Corresponding | ||||
|     Source of the work are being offered to the general public at no | ||||
|     charge under subsection 6d. | ||||
| 
 | ||||
|   A separable portion of the object code, whose source code is excluded | ||||
| from the Corresponding Source as a System Library, need not be | ||||
| included in conveying the object code work. | ||||
| 
 | ||||
|   A "User Product" is either (1) a "consumer product", which means any | ||||
| tangible personal property which is normally used for personal, family, | ||||
| or household purposes, or (2) anything designed or sold for incorporation | ||||
| into a dwelling.  In determining whether a product is a consumer product, | ||||
| doubtful cases shall be resolved in favor of coverage.  For a particular | ||||
| product received by a particular user, "normally used" refers to a | ||||
| typical or common use of that class of product, regardless of the status | ||||
| of the particular user or of the way in which the particular user | ||||
| actually uses, or expects or is expected to use, the product.  A product | ||||
| is a consumer product regardless of whether the product has substantial | ||||
| commercial, industrial or non-consumer uses, unless such uses represent | ||||
| the only significant mode of use of the product. | ||||
| 
 | ||||
|   "Installation Information" for a User Product means any methods, | ||||
| procedures, authorization keys, or other information required to install | ||||
| and execute modified versions of a covered work in that User Product from | ||||
| a modified version of its Corresponding Source.  The information must | ||||
| suffice to ensure that the continued functioning of the modified object | ||||
| code is in no case prevented or interfered with solely because | ||||
| modification has been made. | ||||
| 
 | ||||
|   If you convey an object code work under this section in, or with, or | ||||
| specifically for use in, a User Product, and the conveying occurs as | ||||
| part of a transaction in which the right of possession and use of the | ||||
| User Product is transferred to the recipient in perpetuity or for a | ||||
| fixed term (regardless of how the transaction is characterized), the | ||||
| Corresponding Source conveyed under this section must be accompanied | ||||
| by the Installation Information.  But this requirement does not apply | ||||
| if neither you nor any third party retains the ability to install | ||||
| modified object code on the User Product (for example, the work has | ||||
| been installed in ROM). | ||||
| 
 | ||||
|   The requirement to provide Installation Information does not include a | ||||
| requirement to continue to provide support service, warranty, or updates | ||||
| for a work that has been modified or installed by the recipient, or for | ||||
| the User Product in which it has been modified or installed.  Access to a | ||||
| network may be denied when the modification itself materially and | ||||
| adversely affects the operation of the network or violates the rules and | ||||
| protocols for communication across the network. | ||||
| 
 | ||||
|   Corresponding Source conveyed, and Installation Information provided, | ||||
| in accord with this section must be in a format that is publicly | ||||
| documented (and with an implementation available to the public in | ||||
| source code form), and must require no special password or key for | ||||
| unpacking, reading or copying. | ||||
| 
 | ||||
|   7. Additional Terms. | ||||
| 
 | ||||
|   "Additional permissions" are terms that supplement the terms of this | ||||
| License by making exceptions from one or more of its conditions. | ||||
| Additional permissions that are applicable to the entire Program shall | ||||
| be treated as though they were included in this License, to the extent | ||||
| that they are valid under applicable law.  If additional permissions | ||||
| apply only to part of the Program, that part may be used separately | ||||
| under those permissions, but the entire Program remains governed by | ||||
| this License without regard to the additional permissions. | ||||
| 
 | ||||
|   When you convey a copy of a covered work, you may at your option | ||||
| remove any additional permissions from that copy, or from any part of | ||||
| it.  (Additional permissions may be written to require their own | ||||
| removal in certain cases when you modify the work.)  You may place | ||||
| additional permissions on material, added by you to a covered work, | ||||
| for which you have or can give appropriate copyright permission. | ||||
| 
 | ||||
|   Notwithstanding any other provision of this License, for material you | ||||
| add to a covered work, you may (if authorized by the copyright holders of | ||||
| that material) supplement the terms of this License with terms: | ||||
| 
 | ||||
|     a) Disclaiming warranty or limiting liability differently from the | ||||
|     terms of sections 15 and 16 of this License; or | ||||
| 
 | ||||
|     b) Requiring preservation of specified reasonable legal notices or | ||||
|     author attributions in that material or in the Appropriate Legal | ||||
|     Notices displayed by works containing it; or | ||||
| 
 | ||||
|     c) Prohibiting misrepresentation of the origin of that material, or | ||||
|     requiring that modified versions of such material be marked in | ||||
|     reasonable ways as different from the original version; or | ||||
| 
 | ||||
|     d) Limiting the use for publicity purposes of names of licensors or | ||||
|     authors of the material; or | ||||
| 
 | ||||
|     e) Declining to grant rights under trademark law for use of some | ||||
|     trade names, trademarks, or service marks; or | ||||
| 
 | ||||
|     f) Requiring indemnification of licensors and authors of that | ||||
|     material by anyone who conveys the material (or modified versions of | ||||
|     it) with contractual assumptions of liability to the recipient, for | ||||
|     any liability that these contractual assumptions directly impose on | ||||
|     those licensors and authors. | ||||
| 
 | ||||
|   All other non-permissive additional terms are considered "further | ||||
| restrictions" within the meaning of section 10.  If the Program as you | ||||
| received it, or any part of it, contains a notice stating that it is | ||||
| governed by this License along with a term that is a further | ||||
| restriction, you may remove that term.  If a license document contains | ||||
| a further restriction but permits relicensing or conveying under this | ||||
| License, you may add to a covered work material governed by the terms | ||||
| of that license document, provided that the further restriction does | ||||
| not survive such relicensing or conveying. | ||||
| 
 | ||||
|   If you add terms to a covered work in accord with this section, you | ||||
| must place, in the relevant source files, a statement of the | ||||
| additional terms that apply to those files, or a notice indicating | ||||
| where to find the applicable terms. | ||||
| 
 | ||||
|   Additional terms, permissive or non-permissive, may be stated in the | ||||
| form of a separately written license, or stated as exceptions; | ||||
| the above requirements apply either way. | ||||
| 
 | ||||
|   8. Termination. | ||||
| 
 | ||||
|   You may not propagate or modify a covered work except as expressly | ||||
| provided under this License.  Any attempt otherwise to propagate or | ||||
| modify it is void, and will automatically terminate your rights under | ||||
| this License (including any patent licenses granted under the third | ||||
| paragraph of section 11). | ||||
| 
 | ||||
|   However, if you cease all violation of this License, then your | ||||
| license from a particular copyright holder is reinstated (a) | ||||
| provisionally, unless and until the copyright holder explicitly and | ||||
| finally terminates your license, and (b) permanently, if the copyright | ||||
| holder fails to notify you of the violation by some reasonable means | ||||
| prior to 60 days after the cessation. | ||||
| 
 | ||||
|   Moreover, your license from a particular copyright holder is | ||||
| reinstated permanently if the copyright holder notifies you of the | ||||
| violation by some reasonable means, this is the first time you have | ||||
| received notice of violation of this License (for any work) from that | ||||
| copyright holder, and you cure the violation prior to 30 days after | ||||
| your receipt of the notice. | ||||
| 
 | ||||
|   Termination of your rights under this section does not terminate the | ||||
| licenses of parties who have received copies or rights from you under | ||||
| this License.  If your rights have been terminated and not permanently | ||||
| reinstated, you do not qualify to receive new licenses for the same | ||||
| material under section 10. | ||||
| 
 | ||||
|   9. Acceptance Not Required for Having Copies. | ||||
| 
 | ||||
|   You are not required to accept this License in order to receive or | ||||
| run a copy of the Program.  Ancillary propagation of a covered work | ||||
| occurring solely as a consequence of using peer-to-peer transmission | ||||
| to receive a copy likewise does not require acceptance.  However, | ||||
| nothing other than this License grants you permission to propagate or | ||||
| modify any covered work.  These actions infringe copyright if you do | ||||
| not accept this License.  Therefore, by modifying or propagating a | ||||
| covered work, you indicate your acceptance of this License to do so. | ||||
| 
 | ||||
|   10. Automatic Licensing of Downstream Recipients. | ||||
| 
 | ||||
|   Each time you convey a covered work, the recipient automatically | ||||
| receives a license from the original licensors, to run, modify and | ||||
| propagate that work, subject to this License.  You are not responsible | ||||
| for enforcing compliance by third parties with this License. | ||||
| 
 | ||||
|   An "entity transaction" is a transaction transferring control of an | ||||
| organization, or substantially all assets of one, or subdividing an | ||||
| organization, or merging organizations.  If propagation of a covered | ||||
| work results from an entity transaction, each party to that | ||||
| transaction who receives a copy of the work also receives whatever | ||||
| licenses to the work the party's predecessor in interest had or could | ||||
| give under the previous paragraph, plus a right to possession of the | ||||
| Corresponding Source of the work from the predecessor in interest, if | ||||
| the predecessor has it or can get it with reasonable efforts. | ||||
| 
 | ||||
|   You may not impose any further restrictions on the exercise of the | ||||
| rights granted or affirmed under this License.  For example, you may | ||||
| not impose a license fee, royalty, or other charge for exercise of | ||||
| rights granted under this License, and you may not initiate litigation | ||||
| (including a cross-claim or counterclaim in a lawsuit) alleging that | ||||
| any patent claim is infringed by making, using, selling, offering for | ||||
| sale, or importing the Program or any portion of it. | ||||
| 
 | ||||
|   11. Patents. | ||||
| 
 | ||||
|   A "contributor" is a copyright holder who authorizes use under this | ||||
| License of the Program or a work on which the Program is based.  The | ||||
| work thus licensed is called the contributor's "contributor version". | ||||
| 
 | ||||
|   A contributor's "essential patent claims" are all patent claims | ||||
| owned or controlled by the contributor, whether already acquired or | ||||
| hereafter acquired, that would be infringed by some manner, permitted | ||||
| by this License, of making, using, or selling its contributor version, | ||||
| but do not include claims that would be infringed only as a | ||||
| consequence of further modification of the contributor version.  For | ||||
| purposes of this definition, "control" includes the right to grant | ||||
| patent sublicenses in a manner consistent with the requirements of | ||||
| this License. | ||||
| 
 | ||||
|   Each contributor grants you a non-exclusive, worldwide, royalty-free | ||||
| patent license under the contributor's essential patent claims, to | ||||
| make, use, sell, offer for sale, import and otherwise run, modify and | ||||
| propagate the contents of its contributor version. | ||||
| 
 | ||||
|   In the following three paragraphs, a "patent license" is any express | ||||
| agreement or commitment, however denominated, not to enforce a patent | ||||
| (such as an express permission to practice a patent or covenant not to | ||||
| sue for patent infringement).  To "grant" such a patent license to a | ||||
| party means to make such an agreement or commitment not to enforce a | ||||
| patent against the party. | ||||
| 
 | ||||
|   If you convey a covered work, knowingly relying on a patent license, | ||||
| and the Corresponding Source of the work is not available for anyone | ||||
| to copy, free of charge and under the terms of this License, through a | ||||
| publicly available network server or other readily accessible means, | ||||
| then you must either (1) cause the Corresponding Source to be so | ||||
| available, or (2) arrange to deprive yourself of the benefit of the | ||||
| patent license for this particular work, or (3) arrange, in a manner | ||||
| consistent with the requirements of this License, to extend the patent | ||||
| license to downstream recipients.  "Knowingly relying" means you have | ||||
| actual knowledge that, but for the patent license, your conveying the | ||||
| covered work in a country, or your recipient's use of the covered work | ||||
| in a country, would infringe one or more identifiable patents in that | ||||
| country that you have reason to believe are valid. | ||||
| 
 | ||||
|   If, pursuant to or in connection with a single transaction or | ||||
| arrangement, you convey, or propagate by procuring conveyance of, a | ||||
| covered work, and grant a patent license to some of the parties | ||||
| receiving the covered work authorizing them to use, propagate, modify | ||||
| or convey a specific copy of the covered work, then the patent license | ||||
| you grant is automatically extended to all recipients of the covered | ||||
| work and works based on it. | ||||
| 
 | ||||
|   A patent license is "discriminatory" if it does not include within | ||||
| the scope of its coverage, prohibits the exercise of, or is | ||||
| conditioned on the non-exercise of one or more of the rights that are | ||||
| specifically granted under this License.  You may not convey a covered | ||||
| work if you are a party to an arrangement with a third party that is | ||||
| in the business of distributing software, under which you make payment | ||||
| to the third party based on the extent of your activity of conveying | ||||
| the work, and under which the third party grants, to any of the | ||||
| parties who would receive the covered work from you, a discriminatory | ||||
| patent license (a) in connection with copies of the covered work | ||||
| conveyed by you (or copies made from those copies), or (b) primarily | ||||
| for and in connection with specific products or compilations that | ||||
| contain the covered work, unless you entered into that arrangement, | ||||
| or that patent license was granted, prior to 28 March 2007. | ||||
| 
 | ||||
|   Nothing in this License shall be construed as excluding or limiting | ||||
| any implied license or other defenses to infringement that may | ||||
| otherwise be available to you under applicable patent law. | ||||
| 
 | ||||
|   12. No Surrender of Others' Freedom. | ||||
| 
 | ||||
|   If conditions are imposed on you (whether by court order, agreement or | ||||
| otherwise) that contradict the conditions of this License, they do not | ||||
| excuse you from the conditions of this License.  If you cannot convey a | ||||
| covered work so as to satisfy simultaneously your obligations under this | ||||
| License and any other pertinent obligations, then as a consequence you may | ||||
| not convey it at all.  For example, if you agree to terms that obligate you | ||||
| to collect a royalty for further conveying from those to whom you convey | ||||
| the Program, the only way you could satisfy both those terms and this | ||||
| License would be to refrain entirely from conveying the Program. | ||||
| 
 | ||||
|   13. Use with the GNU Affero General Public License. | ||||
| 
 | ||||
|   Notwithstanding any other provision of this License, you have | ||||
| permission to link or combine any covered work with a work licensed | ||||
| under version 3 of the GNU Affero General Public License into a single | ||||
| combined work, and to convey the resulting work.  The terms of this | ||||
| License will continue to apply to the part which is the covered work, | ||||
| but the special requirements of the GNU Affero General Public License, | ||||
| section 13, concerning interaction through a network will apply to the | ||||
| combination as such. | ||||
| 
 | ||||
|   14. Revised Versions of this License. | ||||
| 
 | ||||
|   The Free Software Foundation may publish revised and/or new versions of | ||||
| the GNU General Public License from time to time.  Such new versions will | ||||
| be similar in spirit to the present version, but may differ in detail to | ||||
| address new problems or concerns. | ||||
| 
 | ||||
|   Each version is given a distinguishing version number.  If the | ||||
| Program specifies that a certain numbered version of the GNU General | ||||
| Public License "or any later version" applies to it, you have the | ||||
| option of following the terms and conditions either of that numbered | ||||
| version or of any later version published by the Free Software | ||||
| Foundation.  If the Program does not specify a version number of the | ||||
| GNU General Public License, you may choose any version ever published | ||||
| by the Free Software Foundation. | ||||
| 
 | ||||
|   If the Program specifies that a proxy can decide which future | ||||
| versions of the GNU General Public License can be used, that proxy's | ||||
| public statement of acceptance of a version permanently authorizes you | ||||
| to choose that version for the Program. | ||||
| 
 | ||||
|   Later license versions may give you additional or different | ||||
| permissions.  However, no additional obligations are imposed on any | ||||
| author or copyright holder as a result of your choosing to follow a | ||||
| later version. | ||||
| 
 | ||||
|   15. Disclaimer of Warranty. | ||||
| 
 | ||||
|   THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY | ||||
| APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT | ||||
| HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY | ||||
| OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, | ||||
| THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||||
| PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM | ||||
| IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF | ||||
| ALL NECESSARY SERVICING, REPAIR OR CORRECTION. | ||||
| 
 | ||||
|   16. Limitation of Liability. | ||||
| 
 | ||||
|   IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | ||||
| WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS | ||||
| THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY | ||||
| GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE | ||||
| USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF | ||||
| DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD | ||||
| PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), | ||||
| EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF | ||||
| SUCH DAMAGES. | ||||
| 
 | ||||
|   17. Interpretation of Sections 15 and 16. | ||||
| 
 | ||||
|   If the disclaimer of warranty and limitation of liability provided | ||||
| above cannot be given local legal effect according to their terms, | ||||
| reviewing courts shall apply local law that most closely approximates | ||||
| an absolute waiver of all civil liability in connection with the | ||||
| Program, unless a warranty or assumption of liability accompanies a | ||||
| copy of the Program in return for a fee. | ||||
| 
 | ||||
|                      END OF TERMS AND CONDITIONS | ||||
| 
 | ||||
|             How to Apply These Terms to Your New Programs | ||||
| 
 | ||||
|   If you develop a new program, and you want it to be of the greatest | ||||
| possible use to the public, the best way to achieve this is to make it | ||||
| free software which everyone can redistribute and change under these terms. | ||||
| 
 | ||||
|   To do so, attach the following notices to the program.  It is safest | ||||
| to attach them to the start of each source file to most effectively | ||||
| state the exclusion of warranty; and each file should have at least | ||||
| the "copyright" line and a pointer to where the full notice is found. | ||||
| 
 | ||||
|     <one line to give the program's name and a brief idea of what it does.> | ||||
|     Copyright (C) <year>  <name of author> | ||||
| 
 | ||||
|     This program is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License | ||||
|     along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| Also add information on how to contact you by electronic and paper mail. | ||||
| 
 | ||||
|   If the program does terminal interaction, make it output a short | ||||
| notice like this when it starts in an interactive mode: | ||||
| 
 | ||||
|     <program>  Copyright (C) <year>  <name of author> | ||||
|     This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | ||||
|     This is free software, and you are welcome to redistribute it | ||||
|     under certain conditions; type `show c' for details. | ||||
| 
 | ||||
| The hypothetical commands `show w' and `show c' should show the appropriate | ||||
| parts of the General Public License.  Of course, your program's commands | ||||
| might be different; for a GUI interface, you would use an "about box". | ||||
| 
 | ||||
|   You should also get your employer (if you work as a programmer) or school, | ||||
| if any, to sign a "copyright disclaimer" for the program, if necessary. | ||||
| For more information on this, and how to apply and follow the GNU GPL, see | ||||
| <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
|   The GNU General Public License does not permit incorporating your program | ||||
| into proprietary programs.  If your program is a subroutine library, you | ||||
| may consider it more useful to permit linking proprietary applications with | ||||
| the library.  If this is what you want to do, use the GNU Lesser General | ||||
| Public License instead of this License.  But first, please read | ||||
| <https://www.gnu.org/licenses/why-not-lgpl.html>. | ||||
							
								
								
									
										56
									
								
								conf.d/01-modules.cfg.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								conf.d/01-modules.cfg.lua
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | ||||
| plugin_paths = { "/usr/local/lib/prosody/custom-modules/" }; | ||||
| 
 | ||||
| modules_enabled = { | ||||
|     -- Generally required | ||||
|     "roster"; -- Allow users to have a roster. Recommended ;) | ||||
|     "saslauth"; -- Authentication for clients and servers. Recommended if you want to log in. | ||||
|     "tls"; -- Add support for secure TLS on c2s/s2s connections | ||||
|     "dialback"; -- s2s dialback support | ||||
|     "disco"; -- Service discovery | ||||
| 
 | ||||
|     -- Not essential, but recommended | ||||
|     "blocklist"; -- Simple modern protocol for blocking remote JIDs (XEP-0191) | ||||
|     "private"; -- Private XML storage (for room bookmarks, etc.) | ||||
|     "vcard4"; -- Allow users to set vCards | ||||
|     "vcard_legacy"; -- Support older clients | ||||
|     "mam"; -- Message Archive (XEP-0313) | ||||
|     "carbons"; -- Share and sync conversations (XEP-0280) | ||||
|     "csi_simple"; -- Buffer unimportant traffic for simple optimisation for clients using state indication | ||||
|     "limits"; -- Enable bandwidth limiting for XMPP connections | ||||
| 
 | ||||
|     -- Nice to have | ||||
|     "version"; -- Replies to server version requests | ||||
|     "uptime"; -- Report how long server has been running | ||||
|     "time"; -- Let others know the time here on this server | ||||
|     "ping"; -- Replies to XMPP pings with pongs | ||||
|     "pep"; -- Enables users to publish their mood, activity, playing music and more | ||||
|     "register"; -- Allow users to register on this server using a client and change passwords | ||||
|     --"muc"; -- [Loaded as component, therefore commented here] Multi-user chats (XEP-0045) | ||||
| 
 | ||||
|     -- Admin interfaces | ||||
|     "admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands | ||||
|     --"admin_telnet"; -- Opens telnet console interface on localhost port 5582 | ||||
| 
 | ||||
|     -- HTTP modules | ||||
|     --"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP" | ||||
|     --"http_files"; -- Serve static files from a directory over HTTP | ||||
| 
 | ||||
|     -- Other specific functionality | ||||
|     "posix"; -- POSIX functionality, sends server to background, enables syslog, etc. | ||||
|     --"groups"; -- Shared roster support | ||||
|     "announce"; -- Send announcement to all online users | ||||
|     --"welcome"; -- Welcome users who register accounts | ||||
|     --"watchregistrations"; -- Alert admins of registrations | ||||
|     --"motd"; -- Send a message to users when they log in | ||||
|     --"legacyauth"; -- Legacy authentication. Only used by some old clients and bots. | ||||
|     "lastactivity"; | ||||
|     "server_contact_info"; -- This module lets you advertise various contact addresses for your XMPP service via XEP-0157. | ||||
| }; | ||||
| 
 | ||||
| -- These modules are auto-loaded, but should you want | ||||
| -- to disable them then uncomment them here: | ||||
| modules_disabled = { | ||||
|     -- "offline"; -- Store offline messages | ||||
|     -- "c2s"; -- Handle client connections | ||||
|     -- "s2s"; -- Handle server-to-server connections | ||||
| }; | ||||
							
								
								
									
										31
									
								
								conf.d/02-storage.cfg.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								conf.d/02-storage.cfg.lua
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| default_storage = "sql" | ||||
| 
 | ||||
| sql = { | ||||
|   driver = os.getenv("DB_DRIVER"); | ||||
|   database = os.getenv("DB_DATABASE"); | ||||
|   host = os.getenv("DB_HOST"); | ||||
|   port = os.getenv("DB_PORT"); | ||||
|   username = os.getenv("DB_USERNAME"); | ||||
|   password = os.getenv("DB_PASSWORD"); | ||||
| } | ||||
| 
 | ||||
| -- make 0.10-distributed mod_mam use sql store | ||||
| archive_store = "archive2" -- Use the same data store as prosody-modules mod_mam | ||||
| 
 | ||||
| storage = { | ||||
|   -- this makes mod_mam use the sql storage backend | ||||
|   archive2 = "sql"; | ||||
| } | ||||
| 
 | ||||
| -- https://modules.prosody.im/mod_mam.html | ||||
| archive_expires_after = "1y" | ||||
| 
 | ||||
| -- bandwith limits | ||||
| limits = { | ||||
|     c2s = { | ||||
|         rate = "10kb/s"; | ||||
|     }; | ||||
|     s2sin = { | ||||
|         rate = "30kb/s"; | ||||
|     }; | ||||
| } | ||||
							
								
								
									
										9
									
								
								conf.d/03-e2e-policy.cfg.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								conf.d/03-e2e-policy.cfg.lua
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| local stringy = require "stringy"  | ||||
| 
 | ||||
| e2e_policy_chat = os.getenv("E2E_POLICY_CHAT") | ||||
| e2e_policy_muc = os.getenv("E2E_POLICY_MUC") | ||||
| e2e_policy_whitelist = stringy.split(os.getenv("E2E_POLICY_WHITELIST"), ", ") | ||||
| e2e_policy_message_optional_chat = "For security reasons, OMEMO, OTR or PGP encryption is STRONGLY recommended for conversations on this server." | ||||
| e2e_policy_message_required_chat = "For security reasons, OMEMO, OTR or PGP encryption is required for conversations on this server." | ||||
| e2e_policy_message_optional_muc = "For security reasons, OMEMO, OTR or PGP encryption is STRONGLY recommended for MUC on this server." | ||||
| e2e_policy_message_required_muc = "For security reasons, OMEMO, OTR or PGP encryption is required for MUC on this server." | ||||
							
								
								
									
										12
									
								
								conf.d/04-server_contact_info.cfg.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								conf.d/04-server_contact_info.cfg.lua
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| local stringy = require "stringy"  | ||||
| 
 | ||||
| contact_info = { | ||||
|   abuse = stringy.split(os.getenv("SERVER_CONTACT_INFO_ABUSE"), ", "); | ||||
|   admin = stringy.split(os.getenv("SERVER_CONTACT_INFO_ADMIN"), ", "); | ||||
|   feedback = stringy.split(os.getenv("SERVER_CONTACT_INFO_FEEDBACK"), ", "); | ||||
|   sales = stringy.split(os.getenv("SERVER_CONTACT_INFO_SALES"), ", "); | ||||
|   security = stringy.split(os.getenv("SERVER_CONTACT_INFO_SECURITY"), ", "); | ||||
|   support = stringy.split(os.getenv("SERVER_CONTACT_INFO_SUPPORT"), ", "); | ||||
| } | ||||
| 
 | ||||
| welcome_message = "Kaixo $username, ongi etorri $host IM zerbitzura! Mesedez irakurri itzazu ondorengo <a href='https://lainoa.eus/terms/tos.html'>Erabilpen baldintzak</a>." | ||||
							
								
								
									
										43
									
								
								conf.d/05-vhost.cfg.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								conf.d/05-vhost.cfg.lua
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | ||||
| local domain = os.getenv("DOMAIN") | ||||
| local domain_http_upload = os.getenv("DOMAIN_HTTP_UPLOAD") | ||||
| local domain_muc = os.getenv("DOMAIN_MUC") | ||||
| local domain_proxy = os.getenv("DOMAIN_PROXY") | ||||
| local domain_pubsub = os.getenv("DOMAIN_PUBSUB") | ||||
| 
 | ||||
| -- XEP-0368: SRV records for XMPP over TLS | ||||
| -- https://compliance.conversations.im/test/xep0368/ | ||||
| legacy_ssl_ssl = { | ||||
| 	certificate = "certs/" .. domain .. "/fullchain.pem"; | ||||
| 	key = "certs/" .. domain .. "/privkey.pem"; | ||||
| } | ||||
| legacy_ssl_ports = { 5223 } | ||||
| 
 | ||||
| -- https://prosody.im/doc/certificates#service_certificates | ||||
| -- https://prosody.im/doc/ports#ssl_configuration | ||||
| https_ssl = { | ||||
| 	certificate = "certs/" .. domain .. "/fullchain.pem"; | ||||
| 	key = "certs/" .. domain .. "/privkey.pem"; | ||||
| } | ||||
| 
 | ||||
| VirtualHost (domain) | ||||
| 
 | ||||
| -- Set up a http file upload because proxy65 is not working in muc | ||||
| Component (domain_http_upload) "http_upload" | ||||
| 	http_upload_expire_after = 60 * 60 * 24 * 7 -- a week in seconds | ||||
| 
 | ||||
| Component (domain_muc) "muc" | ||||
| 	name = "Prosody Chatrooms" | ||||
| 	restrict_room_creation = false | ||||
| 	max_history_messages = 20 | ||||
| 	modules_enabled = { | ||||
| 		"muc_mam", | ||||
| 		"vcard_muc" | ||||
| 	} | ||||
| 
 | ||||
| -- Set up a SOCKS5 bytestream proxy for server-proxied file transfers | ||||
| Component (domain_proxy) "proxy65" | ||||
| 	proxy65_address = domain_proxy | ||||
| 	proxy65_acl = { domain } | ||||
| 
 | ||||
| -- Implements a XEP-0060 pubsub service. | ||||
| Component (domain_pubsub) "pubsub" | ||||
							
								
								
									
										40
									
								
								docker-entrypoint.bash
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										40
									
								
								docker-entrypoint.bash
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,40 @@ | ||||
| #!/bin/bash | ||||
| set -e | ||||
| 
 | ||||
| export ALLOW_REGISTRATION=${ALLOW_REGISTRATION:-true} | ||||
| export DOMAIN_HTTP_UPLOAD=${DOMAIN_HTTP_UPLOAD:-"upload.$DOMAIN"} | ||||
| export DOMAIN_MUC=${DOMAIN_MUC:-"conference.$DOMAIN"} | ||||
| export DOMAIN_PROXY=${DOMAIN_PROXY:-"proxy.$DOMAIN"} | ||||
| export DOMAIN_PUBSUB=${DOMAIN_PUBSUB:-"pubsub.$DOMAIN"} | ||||
| export DB_DRIVER=${DB_DRIVER:-"SQLite3"} | ||||
| export DB_DATABASE=${DB_DATABASE:-"prosody.sqlite"} | ||||
| export E2E_POLICY_CHAT=${E2E_POLICY_CHAT:-"required"} | ||||
| export E2E_POLICY_MUC=${E2E_POLICY_MUC:-"required"} | ||||
| export E2E_POLICY_WHITELIST=${E2E_POLICY_WHITELIST:-""} | ||||
| export LOG_LEVEL=${LOG_LEVEL:-"info"} | ||||
| export C2S_REQUIRE_ENCRYPTION=${C2S_REQUIRE_ENCRYPTION:-true} | ||||
| export S2S_REQUIRE_ENCRYPTION=${S2S_REQUIRE_ENCRYPTION:-true} | ||||
| export S2S_SECURE_AUTH=${S2S_SECURE_AUTH:-true} | ||||
| export SERVER_CONTACT_INFO_ABUSE=${SERVER_CONTACT_INFO_ABUSE:-"xmpp:abuse@$DOMAIN"} | ||||
| export SERVER_CONTACT_INFO_ADMIN=${SERVER_CONTACT_INFO_ADMIN:-"xmpp:admin@$DOMAIN"} | ||||
| export SERVER_CONTACT_INFO_FEEDBACK=${SERVER_CONTACT_INFO_FEEDBACK:-"xmpp:feedback@$DOMAIN"} | ||||
| export SERVER_CONTACT_INFO_SALES=${SERVER_CONTACT_INFO_SALES:-"xmpp:sales@$DOMAIN"} | ||||
| export SERVER_CONTACT_INFO_SECURITY=${SERVER_CONTACT_INFO_SECURITY:-"xmpp:security@$DOMAIN"} | ||||
| export SERVER_CONTACT_INFO_SUPPORT=${SERVER_CONTACT_INFO_SUPPORT:-"xmpp:support@$DOMAIN"} | ||||
| export PROSODY_ADMINS=${PROSODY_ADMINS:-""} | ||||
| 
 | ||||
| if [[ "$1" != "prosody" ]]; then | ||||
|     exec prosodyctl $* | ||||
|     exit 0; | ||||
| fi | ||||
| 
 | ||||
| if [ "$LOCAL" -a "$PASSWORD" -a "$DOMAIN" ] ; then | ||||
|     prosodyctl register $LOCAL $DOMAIN $PASSWORD | ||||
| fi | ||||
| 
 | ||||
| if [ -z "$DOMAIN" ]; then | ||||
|   echo "[ERROR] DOMAIN must be set!" | ||||
|   exit 1 | ||||
| fi | ||||
| 
 | ||||
| exec "$@" | ||||
							
								
								
									
										49
									
								
								docker-prosody-module-install.bash
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										49
									
								
								docker-prosody-module-install.bash
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,49 @@ | ||||
| #!/bin/bash | ||||
| set -e | ||||
| 
 | ||||
| source="/usr/src/prosody-modules" | ||||
| target="/usr/local/lib/prosody/custom-modules" | ||||
| config="/usr/local/etc/prosody/conf.d/01-modules.cfg.lua" | ||||
| 
 | ||||
| cd ${source} | ||||
| 
 | ||||
| usage() { | ||||
| 	echo "usage: $0 ext-name [ext-name ...]" | ||||
| 	echo "   ie: $0 carbons e2e_policy proxy65" | ||||
| 	echo | ||||
| 	echo 'Possible values for ext-name:' | ||||
| 	find . -mindepth 1 -maxdepth 1 -type d | sort | sed s/\.\\/mod_//g | xargs | ||||
| } | ||||
| 
 | ||||
| exts= | ||||
| for ext; do | ||||
| 	if [ -z "mod_$ext" ]; then | ||||
| 		continue | ||||
| 	fi | ||||
| 	if [ ! -d "mod_$ext" ]; then | ||||
| 		echo >&2 "error: $PWD/mod_$ext does not exist" | ||||
| 		echo >&2 | ||||
| 		usage >&2 | ||||
| 		exit 1 | ||||
| 	fi | ||||
| 	exts="$exts $ext" | ||||
| done | ||||
| 
 | ||||
| if [ -z "$exts" ]; then | ||||
| 	usage >&2 | ||||
| 	exit 1 | ||||
| fi | ||||
| 
 | ||||
| for ext in $exts; do | ||||
| 	echo "Installing mod_${ext}" | ||||
| 
 | ||||
| 	echo " - copying to ${target}" | ||||
| 	cp -r "${source}/mod_${ext}" "${target}/" | ||||
| 
 | ||||
| 	# Skip this if the modules should not be added to modules_enabled. | ||||
| 	if [ "$ext" != "http_upload" ] && [ "$ext" != "vcard_muc" ] ; then | ||||
| 		echo " - enabling within ${config}" | ||||
| 		new_config=$(cat "${config}" | module="${ext}" perl -0pe 's/(modules_enabled[ ]*=[ ]*{[^}]*)};/$1\n\t"$ENV{module}";\n};/') | ||||
| 		echo "${new_config}" > "${config}" | ||||
| 	fi | ||||
| done | ||||
							
								
								
									
										9
									
								
								download-prosody-modules.bash
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										9
									
								
								download-prosody-modules.bash
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,9 @@ | ||||
| #!/bin/bash | ||||
| set -e | ||||
| 
 | ||||
| dir="/usr/src/prosody-modules" | ||||
| 
 | ||||
| mkdir -p "${dir}" | ||||
| wget https://hg.prosody.im/prosody-modules/archive/tip.tar.gz | ||||
| tar -xzf tip.tar.gz -C "${dir}" --strip-components=1 | ||||
| rm tip.tar.gz | ||||
							
								
								
									
										0
									
								
								matterbridge/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								matterbridge/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										84
									
								
								matterbridge/matterbridge.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								matterbridge/matterbridge.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,84 @@ | ||||
| #https://github.com/42wim/matterbridge | ||||
| 
 | ||||
| ################################################################### | ||||
| #XMPP section - berriketak | ||||
| ################################################################### | ||||
| [xmpp] | ||||
| [xmpp.telegram_berriketak] | ||||
| #Server="lainoa.eus:5222" | ||||
| Server="server:5222" | ||||
| #Jid="admin@lainoa.eus" | ||||
| Jid="admin@lainoa.eus" | ||||
| #Password="sagastarri996X" | ||||
| Password="sagastarri996" | ||||
| Muc="conference.lainoa.eus" | ||||
| Nick="Admin" | ||||
| SkipTLSVerify=true | ||||
| #IgnoreNicks="ircspammer1 ircspammer2" | ||||
| #RemoteNickFormat="[{NICK}] " | ||||
| RemoteNickFormat="{TENGO}({PROTOCOL}) " | ||||
| ShowJoinPart=false | ||||
| 
 | ||||
| ################################################################### | ||||
| #telegram section - berriketak | ||||
| ################################################################### | ||||
| [telegram] | ||||
| [telegram.berriket_xmppBot] | ||||
| Token="434963747:AAHRbJAw9oN30b9KdjWacnyYyHS22r056SM" #token berriket_xmppBot | ||||
| MessageFormat="HTMLNick" | ||||
| EditDisable=false | ||||
| EditSuffix=" (edited)" | ||||
| IgnoreNicks="spammer1 spammer2" | ||||
| RemoteNickFormat="{NICK}: " | ||||
| ShowJoinPart=false | ||||
| UseInsecureURL=true | ||||
| MediaConvertWebPToPNG=true | ||||
| DisableWebPagePreview=false | ||||
| 
 | ||||
| [tengo] | ||||
| #RemoteNickFormat="remotenickformat.tengo" | ||||
| RemoteNickFormat="/etc/matterbridge/nicks.tengo" | ||||
| #InMessage="/etc/matterbridge/in.tengo" | ||||
| OutMessage="/etc/matterbridge/out.tengo" | ||||
| 
 | ||||
| ################################################################### | ||||
| #gateway section | ||||
| ################################################################### | ||||
| [[gateway]] | ||||
| name="gateway_berriketak" | ||||
| enable=true | ||||
| 
 | ||||
| 	[[gateway.inout]] | ||||
| 	account="xmpp.telegram_berriketak" | ||||
| 	channel="berriketak" | ||||
|         #channel="test" | ||||
| 
 | ||||
| 	[[gateway.inout]] | ||||
| 	account="telegram.berriket_xmppBot" | ||||
| 	channel="-183435536" #Telegram berriketak taldearen ID-a | ||||
| 
 | ||||
| [[gateway]] | ||||
| name="test" | ||||
| enable=true | ||||
| 
 | ||||
|         [[gateway.inout]] | ||||
|         account="xmpp.telegram_berriketak" | ||||
|         channel="test" | ||||
| 
 | ||||
|         [[gateway.inout]] | ||||
|         account="telegram.berriket_xmppBot" | ||||
|         #channel="-241666435" #Telegram xmpp_test taldearen ID-a | ||||
| 	channel="-1001617641457" | ||||
| 
 | ||||
| #[[gateway]] | ||||
| #name="etxekok" | ||||
| #enable=true | ||||
| 
 | ||||
| #        [[gateway.inout]] | ||||
| #        account="xmpp.telegram_berriketak" | ||||
| #        channel="etxekok" | ||||
| 
 | ||||
| #        [[gateway.inout]] | ||||
| #        account="telegram.berriket_xmppBot" | ||||
| #        channel="-523032" #Telegram etxekok taldearen ID-a | ||||
| 
 | ||||
							
								
								
									
										5
									
								
								matterbridge/nicks.tengo
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								matterbridge/nicks.tengo
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| /*Customize nicks from X to XMPP*/ | ||||
| result = nick | ||||
| if(nick == "Nekane Nekane") { | ||||
|    result = "Amona Nekane" | ||||
| } | ||||
							
								
								
									
										30
									
								
								matterbridge/out.tengo
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								matterbridge/out.tengo
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| text := import("text") | ||||
| fmt := import("fmt") | ||||
| 
 | ||||
| fmt.println(msgText) | ||||
| 
 | ||||
| if(inProtocol == "telegram"){ | ||||
| 	if text.index(msgText, "https") > 1 { | ||||
| 		media_array := text.re_split(":", msgText, 2) | ||||
| 		fmt.println(media_array)     | ||||
| 		if len(media_array) > 1 { | ||||
| 			//TG desktop | ||||
| 			media := text.trim_prefix(media_array[1]," ") | ||||
| 			//msgText=media | ||||
| 			bold := "**"+media+"**" | ||||
| 			//link := "[link]("+media+")" | ||||
| 			//msgText =link | ||||
| 			msgText = text.re_replace("MEDIA", bold, msgText) | ||||
| 			//msgText="" | ||||
| 			//msgText=text.re_replace("matterbridge",msgText,"matterbridge (https://github.com/42wim/matterbridge)") | ||||
| 		}else{ | ||||
| 			//TG android | ||||
| 			msgText="https:"+media_array[0] | ||||
| 		} | ||||
| 
 | ||||
| 	} | ||||
| }else{ | ||||
| 	//capitalize + bold | ||||
|         msgUsername = "<strong>"+text.title(msgUsername)+"</strong>" | ||||
| 	msgText = msgText | ||||
| } | ||||
							
								
								
									
										53
									
								
								migrator.cfg.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								migrator.cfg.lua
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| local domain = os.getenv("DOMAIN") | ||||
| local domain_muc = os.getenv("DOMAIN_MUC") | ||||
| 
 | ||||
| local data_path = '/usr/local/var/lib/prosody'; | ||||
| 
 | ||||
| local vhost = { | ||||
| 	"accounts", | ||||
| 	"account_details", | ||||
| 	"roster", | ||||
| 	"vcard", | ||||
| 	"private", | ||||
| 	"blocklist", | ||||
| 	"privacy", | ||||
| 	"archive-archive", | ||||
| 	"offline-archive", | ||||
| 	"pubsub_nodes-pubsub", | ||||
| 	"pep-pubsub", | ||||
| } | ||||
| local muc = { | ||||
| 	"persistent", | ||||
| 	"config", | ||||
| 	"state", | ||||
| 	"muc_log-archive", | ||||
| }; | ||||
| 
 | ||||
| input { | ||||
| 	hosts = { | ||||
| 		[domain] = vhost; | ||||
| 		[domain_muc] = muc; | ||||
| 	}; | ||||
| 	type = "internal"; | ||||
| 	path = data_path; | ||||
| } | ||||
| 
 | ||||
| output { | ||||
| 	type = "sql"; | ||||
| 	driver = "SQLite3"; | ||||
| 	database = data_path.."/prosody.sqlite"; | ||||
| } | ||||
| 
 | ||||
| --[[ | ||||
| 
 | ||||
| input { | ||||
| 	type = "internal"; | ||||
| 	path = data_path; | ||||
| } | ||||
| output { | ||||
| 	type = "sql"; | ||||
| 	driver = "SQLite3"; | ||||
| 	database = data_path.."/prosody.sqlite"; | ||||
| } | ||||
| 
 | ||||
| ]] | ||||
							
								
								
									
										34
									
								
								prosody.cfg.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								prosody.cfg.lua
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| -- see example config at https://hg.prosody.im/-1.9/file/0.9.10/prosody.cfg.lua.dist | ||||
| -- easily extendable by putting into different config files within conf.d folder | ||||
| 
 | ||||
| local stringy = require "stringy"  | ||||
| 
 | ||||
| admins = stringy.split(os.getenv("PROSODY_ADMINS"), ", "); | ||||
| 
 | ||||
| pidfile = "/var/run/prosody/prosody.pid" | ||||
| 
 | ||||
| use_libevent = true; -- improves performance | ||||
| 
 | ||||
| allow_registration = os.getenv("ALLOW_REGISTRATION"); | ||||
| 
 | ||||
| c2s_require_encryption = os.getenv("C2S_REQUIRE_ENCRYPTION"); | ||||
| s2s_require_encryption = os.getenv("S2S_REQUIRE_ENCRYPTION"); | ||||
| s2s_secure_auth = os.getenv("S2S_SECURE_AUTH"); | ||||
| 
 | ||||
| authentication = os.getenv("AUTHENTICATION") or "internal_hashed"; | ||||
| 
 | ||||
| ldap_base = os.getenv("LDAP_BASE"); | ||||
| ldap_server = os.getenv("LDAP_SERVER") or "localhost"; | ||||
| ldap_rootdn = os.getenv("LDAP_ROOTDN") or ""; | ||||
| ldap_password = os.getenv("LDAP_PASSWORD") or ""; | ||||
| ldap_filter = os.getenv("LDAP_FILTER") or "(uid=$user)"; | ||||
| ldap_scope = os.getenv("LDAP_SCOPE") or "subtree"; | ||||
| ldap_tls = os.getenv("LDAP_TLS") or "false"; | ||||
| ldap_mode = os.getenv("LDAP_MODE") or "bind"; | ||||
| ldap_admin_filter = os.getenv("LDAP_ADMIN_FILTER") or ""; | ||||
| 
 | ||||
| log = { | ||||
|     {levels = {min = os.getenv("LOG_LEVEL")}, to = "console"}; | ||||
| }; | ||||
| 
 | ||||
| Include "conf.d/*.cfg.lua"; | ||||
							
								
								
									
										307
									
								
								readme.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										307
									
								
								readme.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,307 @@ | ||||
| # Prosody XMPP Docker image | ||||
| 
 | ||||
|  | ||||
|  | ||||
| [](https://microbadger.com/images/sarasmiseth/prosody:latest) | ||||
| [](https://microbadger.com/images/sarasmiseth/prosody:latest) | ||||
| [](https://hub.docker.com/r/sarasmiseth/prosody/) | ||||
| [](https://hub.docker.com/r/sarasmiseth/prosody/) | ||||
| [](https://github.com/SaraSmiseth/prosody/issues) | ||||
| [](https://github.com/SaraSmiseth/prosody/pulls) | ||||
| 
 | ||||
| This docker image forked from [SaraSmiseth](https://github.com/SaraSmiseth)'s [repository](https://github.com/SaraSmiseth/prosody) provides you with a configured [Prosody](https://prosody.im/) XMPP server. Includes the _prosody-migrator_ tool for data migrations between different database types and there is also an option to create a bridges between the XMPP server and the most popular messaging services like Telegram or Matrix, via [Matterbridge](https://github.com/42wim/matterbridge). The image is based on `debian:bullseye-slim`. | ||||
| The server was tested using the Android App [Conversations](https://conversations.im/) and the Desktop client [Gajim](https://gajim.org). | ||||
| Multiple [architectures](https://hub.docker.com/r/sarasmiseth/prosody/tags) are supported. I use it on my raspberry pi 4. | ||||
| 
 | ||||
| While Conversations got everything set-up out-of-the-box, Gajim was used with the following extensions: | ||||
| 
 | ||||
| * HttpUpload | ||||
| * Off-The-Record Encryption | ||||
| * OMEMO (requires _python-axolotl_ to be installed) | ||||
| * Url Image preview | ||||
| 
 | ||||
| ## Table of Contents | ||||
| 
 | ||||
| - [Prosody XMPP Docker image](#prosody-xmpp-docker-image) | ||||
|   - [Table of Contents](#table-of-contents) | ||||
|   - [Features](#features) | ||||
|   - [Requirements](#requirements) | ||||
|   - [Image Details](#image-details) | ||||
|     - [Ports](#ports) | ||||
|     - [Directories](#directories) | ||||
|       - [Data](#data) | ||||
|       - [Bundled modules](#bundled-modules) | ||||
|       - [Additionally installed prosody modules](#additionally-installed-prosody-modules) | ||||
|       - [Config](#config) | ||||
|       - [SSL certificates](#ssl-certificates) | ||||
|         - [Folder structure](#folder-structure) | ||||
|         - [Symlinks](#symlinks) | ||||
|         - [Permissions](#permissions) | ||||
|     - [Run](#run) | ||||
|     - [Volumes permissions](#volumes-permissions) | ||||
|     - [Docker tags](#docker-tags) | ||||
|     - [Configuration](#configuration) | ||||
|       - [Environment variables](#environment-variables) | ||||
|       - [DNS](#dns) | ||||
|     - [Extend](#extend) | ||||
|     - [Upgrade](#upgrade) | ||||
|   - [Matterbridge](#matterbridge) | ||||
|   - [Migration tool](#migration-tool) | ||||
|   - [Test your server](#test-your-server) | ||||
| 
 | ||||
| ## Features | ||||
| 
 | ||||
| * Secure by default | ||||
|   * SSL certificate required | ||||
|   * End-to-end encryption required (using [OMEMO](https://conversations.im/omemo/) or [OTR](https://en.wikipedia.org/wiki/Off-the-Record_Messaging)) | ||||
| * Data storage | ||||
|   * SQLite message store | ||||
|   * Configured file upload and image sharing | ||||
| * Multi-user chat (MUC) | ||||
| 
 | ||||
| ## Requirements | ||||
| 
 | ||||
| * You need a SSL certificate. I recommend [LetsEncrypt](https://letsencrypt.org/) for that. | ||||
| 
 | ||||
| ## Image Details | ||||
| 
 | ||||
| ### Ports | ||||
| 
 | ||||
| The following ports are exposed: | ||||
| 
 | ||||
| * 5000: proxy65 port used for file sharing | ||||
| * 5222: c2s port (client to server) | ||||
| * 5223: c2s legacy ssl port (client to server) | ||||
| * 5269: s2s port (server to server) | ||||
| * 5347: XMPP component port | ||||
| * 5280: BOSH / websocket port | ||||
| * 5281: Secure BOSH / websocket port | ||||
| 
 | ||||
| ### Directories | ||||
| 
 | ||||
| #### Data | ||||
| 
 | ||||
| Path: ```/usr/local/var/lib/prosody/```. | ||||
| 
 | ||||
| * used for SQLite file | ||||
| * used for HTTP uploads | ||||
| * this is exposed as docker volume | ||||
|    | ||||
| #### Bundled modules | ||||
| 
 | ||||
| Path: ```/usr/local/lib/prosody/modules/```. | ||||
| 
 | ||||
| #### Additionally installed prosody modules | ||||
| 
 | ||||
| Path: ```/usr/local/lib/prosody/custom-modules/```. | ||||
| 
 | ||||
| #### Config | ||||
| 
 | ||||
| Path: ```/usr/local/etc/prosody/```. | ||||
| 
 | ||||
| * containing the main config file called ```prosody.cfg.lua``` | ||||
| * containing additional config files within ```conf.d/``` | ||||
| 
 | ||||
| #### SSL certificates | ||||
| 
 | ||||
| Path: ```/usr/local/etc/prosody/certs/```. | ||||
| 
 | ||||
| Uses [automatic location](https://prosody.im/doc/certificates#automatic_location) to find your certs. | ||||
| 
 | ||||
| The http_upload module and the legacy_ssl module do not use the same search algorithm for the certificates. See [service certificates](https://prosody.im/doc/certificates#service_certificates). | ||||
| 
 | ||||
| The settings https_ssl and legacy_ssl_ssl in [05-vhost.cfg.lua](./conf.d/05-vhost.cfg.lua) configures the certificates to ```certs/domain.tld/fullchain.pem``` and ```certs/domain.tld/privkey.pem``` for legacy_ssl and to ```certs/DOMAIN_HTTP_UPLOAD/fullchain.pem``` and ```certs/DOMAIN_HTTP_UPLOAD/privkey.pem``` for http_upload where DOMAIN_HTTP_UPLOAD is an environtment variable. | ||||
| 
 | ||||
| ##### Folder structure | ||||
| 
 | ||||
| An example certificate folder structure could look like this: | ||||
| 
 | ||||
| ``` zsh | ||||
| certs | ||||
| ├── conference.domain.tld | ||||
| │   ├── fullchain.pem | ||||
| │   └── privkey.pem | ||||
| ├── proxy.domain.tld | ||||
| │   ├── fullchain.pem | ||||
| │   └── privkey.pem | ||||
| ├── upload.domain.tld | ||||
| │   ├── fullchain.pem | ||||
| │   └── privkey.pem | ||||
| └── domain.tld | ||||
|     ├── fullchain.pem | ||||
|     └── privkey.pem | ||||
| ``` | ||||
| 
 | ||||
| Thats how Let's encrypt certbot does it out of the box. | ||||
| 
 | ||||
| ##### Symlinks | ||||
| 
 | ||||
| certbot creates the structure and uses symlinks to the actual certificates. | ||||
| If you mount them like that prosody somehow does not find them. | ||||
| I copied them to a folder named ```certs``` next to my ```docker-compose.yml``` and made sure to use the ```-L``` flag of ```cp```. | ||||
| This makes cp follow symbolic links when copying from them. | ||||
| For example ```cp -L src dest```. | ||||
| 
 | ||||
| ##### Permissions | ||||
| 
 | ||||
| See official [documentation](https://prosody.im/doc/certificates#permissions) for more information. | ||||
| Check [Volumes permissions](#volumes-permissions) as well. | ||||
| 
 | ||||
| ### Run | ||||
| 
 | ||||
| First you need to build the image: | ||||
| ```bash | ||||
| docker build -t prosody/xmpp . | ||||
| ``` | ||||
| 
 | ||||
| Next I recommend using a ```docker-compose.yml``` file: | ||||
| 
 | ||||
| ```yaml | ||||
| version: '3.7' | ||||
| 
 | ||||
| services: | ||||
|   server: | ||||
|     image: sarasmiseth/prosody:latest | ||||
|     restart: unless-stopped | ||||
|     ports: | ||||
|       - "5000:5000" | ||||
|       - "5222:5222" | ||||
|       - "5223:5223" | ||||
|       - "5269:5269" | ||||
|       - "5281:5281" | ||||
|     environment: | ||||
|       DOMAIN: domain.tld | ||||
|     volumes: | ||||
|       - ./certs:/usr/local/etc/prosody/certs | ||||
|       - ./data:/usr/local/var/lib/prosody | ||||
| ``` | ||||
| 
 | ||||
| Boot it via: ```docker-compose up -d```. | ||||
| 
 | ||||
| Inspect logs: ```docker-compose logs -f```. | ||||
| 
 | ||||
| ### Volumes permissions | ||||
| 
 | ||||
| The prosody user inside the container has the `uid=999` and `gid=999`. If you use the example `docker-compose.yml` from above make sure, that the `./data` folder and the `./certs` folder have the correct permissions. | ||||
| 
 | ||||
| ``` shell | ||||
| sudo chown 999:999 ./certs | ||||
| sudo chown 999:999 ./data | ||||
| ``` | ||||
| 
 | ||||
| ### Docker tags | ||||
| 
 | ||||
| <https://hub.docker.com/r/sarasmiseth/prosody/tags> | ||||
| 
 | ||||
| | Tag      | Description                                                                                                                                                              | | ||||
| | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | ||||
| | edge     | This tag points to the latest version build from the newest [commit](https://github.com/SaraSmiseth/prosody/commits/dev) in the dev branch.                              | | ||||
| | nightly  | This tag points to the latest version build from the newest [commit](https://github.com/SaraSmiseth/prosody/commits/dev) in the dev branch. It gets rebuild every night. | | ||||
| | latest   | This tag points to the latest version build from the latest commit that is tagged in git. See [releases](https://github.com/SaraSmiseth/prosody/releases).               | | ||||
| | *vX.Y.Z* | There is a tag for each [release](https://github.com/SaraSmiseth/prosody/releases).                                                                                      | | ||||
| 
 | ||||
| ### Configuration | ||||
| 
 | ||||
| #### Environment variables | ||||
| 
 | ||||
| | Variable                         | Description                                                                                                          | Type                                         | Default value              | | ||||
| | -------------------------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | -------------------------- | | ||||
| | **ALLOW_REGISTRATION**           | Whether to allow registration of new accounts via Jabber clients                                                     | *optional*                                   | true                       | | ||||
| | **DOMAIN**                       | domain                                                                                                               | **required**                                 | null                       | | ||||
| | **DOMAIN_HTTP_UPLOAD**           | Domain which lets clients upload files over HTTP                                                                     | *optional*                                   | upload.**DOMAIN**          | | ||||
| | **DOMAIN_MUC**                   | Domain for Multi-user chat (MUC) for allowing you to create hosted chatrooms/conferences for XMPP users              | *optional*                                   | conference.**DOMAIN**      | | ||||
| | **DOMAIN_PROXY**                 | Domain for SOCKS5 bytestream proxy for server-proxied file transfers                                                 | *optional*                                   | proxy.**DOMAIN**           | | ||||
| | **DOMAIN_PUBSUB**                | Domain for a XEP-0060 pubsub service                                                                                 | *optional*                                   | pubsub.**DOMAIN**          | | ||||
| | **AUTHENTICATION**               | authentication                                                                                                       | *optional*                                   | "internal_hashed"          | | ||||
| | **LDAP_BASE**                    | LDAP base directory which stores user accounts                                                                       | **required** if **AUTHENTICATION** is "ldap" |                            | | ||||
| | **LDAP_SERVER**                  | Space-separated list of hostnames or IPs, optionally with port numbers (e.g. “localhost:8389”)                       | *optional*                                   | "localhost"                | | ||||
| | **LDAP_ROOTDN**                  | The distinguished name to auth against                                                                               | *optional*                                   | ""                         | | ||||
| | **LDAP_PASSWORD**                | Password for rootdn                                                                                                  | *optional*                                   | ""                         | | ||||
| | **LDAP_FILTER**                  | Search filter, with $user and $host substituted for user- and hostname                                               | *optional*                                   | "(uid=$user)"              | | ||||
| | **LDAP_SCOPE**                   | Search scope. other values: “base” and “onelevel”                                                                    | *optional*                                   | "subtree"                  | | ||||
| | **LDAP_TLS**                     | Enable TLS (StartTLS) to connect to LDAP (can be true or false). The non-standard ‘LDAPS’ protocol is not supported. | *optional*                                   | "false"                    | | ||||
| | **LDAP_MODE**                    | How passwords are validated.                                                                                         | *optional*                                   | "bind"                     | | ||||
| | **LDAP_ADMIN_FILTER**            | Search filter to match admins, works like ldap_filter                                                                | *optional*                                   | ""                         | | ||||
| | **DB_DRIVER**                    | May also be "PostgreSQL" or "MySQL" or "SQLite3" (case sensitive!)                                                   | *optional*                                   | SQLite3                    | | ||||
| | **DB_DATABASE**                  | The database name to use. For SQLite3 this the database filename (relative to the data storage directory).           | *optional*                                   | prosody.sqlite             | | ||||
| | **DB_HOST**                      | The address of the database server                                                                                   | *optional*                                   |                            | | ||||
| | **DB_PORT**                      | Port on which the database is listening                                                                              | *optional*                                   |                            | | ||||
| | **DB_USERNAME**                  | The username to authenticate to the database                                                                         | *optional*                                   |                            | | ||||
| | **DB_PASSWORD**                  | The password to authenticate to the database                                                                         | *optional*                                   |                            | | ||||
| | **E2E_POLICY_CHAT**              | Policy for chat messages. Possible values: "none", "optional" and "required".                                        | *optional*                                   | "required"                 | | ||||
| | **E2E_POLICY_MUC**               | Policy for MUC messages. Possible values: "none", "optional" and "required".                                         | *optional*                                   | "required"                 | | ||||
| | **E2E_POLICY_WHITELIST**         | Make this module ignore messages sent to and from this JIDs or MUCs.                                                 | *optional*                                   | ""                         | | ||||
| | **LOG_LEVEL**                    | Min log level. Change to debug for more information                                                                  | *optional*                                   | info                       | | ||||
| | **C2S_REQUIRE_ENCRYPTION**       | Whether to force all client-to-server connections to be encrypted or not                                             | *optional*                                   | true                       | | ||||
| | **S2S_REQUIRE_ENCRYPTION**       | Whether to force all server-to-server connections to be encrypted or not                                             | *optional*                                   | true                       | | ||||
| | **S2S_SECURE_AUTH**              | Require encryption and certificate authentication                                                                    | *optional*                                   | true                       | | ||||
| | **SERVER_CONTACT_INFO_ABUSE**    | A list of strings. Each string should be an URI. See [here](https://prosody.im/doc/modules/mod_server_contact_info). | *optional*                                   | "xmpp:abuse@**DOMAIN**"    | | ||||
| | **SERVER_CONTACT_INFO_ADMIN**    | A list of strings. Each string should be an URI. See [here](https://prosody.im/doc/modules/mod_server_contact_info). | *optional*                                   | "xmpp:admin@**DOMAIN**"    | | ||||
| | **SERVER_CONTACT_INFO_FEEDBACK** | A list of strings. Each string should be an URI. See [here](https://prosody.im/doc/modules/mod_server_contact_info). | *optional*                                   | "xmpp:feedback@**DOMAIN**" | | ||||
| | **SERVER_CONTACT_INFO_SALES**    | A list of strings. Each string should be an URI. See [here](https://prosody.im/doc/modules/mod_server_contact_info). | *optional*                                   | "xmpp:sales@**DOMAIN**"    | | ||||
| | **SERVER_CONTACT_INFO_SECURITY** | A list of strings. Each string should be an URI. See [here](https://prosody.im/doc/modules/mod_server_contact_info). | *optional*                                   | "xmpp:security@**DOMAIN**" | | ||||
| | **SERVER_CONTACT_INFO_SUPPORT**  | A list of strings. Each string should be an URI. See [here](https://prosody.im/doc/modules/mod_server_contact_info). | *optional*                                   | "xmpp:support@**DOMAIN**"  | | ||||
| | **PROSODY_ADMINS**               | Specify who is an administrator. List of adresses. Eg. "me@example.com", "admin@example.net"                         | *optional*                                   | ""                         | | ||||
| 
 | ||||
| #### DNS | ||||
| 
 | ||||
| You need these DNS record pointing to your server: | ||||
| 
 | ||||
| * domain.tld | ||||
| * conference.domain.tld | ||||
| * proxy.domain.tld | ||||
| * pubsub.domain.tld | ||||
| * upload.domain.tld | ||||
| * A SRV record for _xmpps-client._tcp.domain.tld for port 5223. | ||||
| 
 | ||||
| where domain.tld is the environment variable DOMAIN. | ||||
| 
 | ||||
| ### Extend | ||||
| 
 | ||||
| There is a helper script that eases installing additional prosody modules: ```docker-prosody-module-install``` | ||||
| 
 | ||||
| It downloads the current [prosody-modules](https://hg.prosody.im/prosody-modules/) repository. The specified modules are copied and its name is added to the ```modules_enabled``` variable within ```conf.d/01-modules.cfg.lua```. | ||||
| 
 | ||||
| There is also ```docker-prosody-module-copy``` which copies the specified modules but does not add them to the ```modules_enabled``` variable within ```conf.d/01-modules.cfg.lua```. | ||||
| 
 | ||||
| If you need additional configuration just overwrite the respective _cfg.lua_ file or add new ones. | ||||
| 
 | ||||
| ### Upgrade | ||||
| 
 | ||||
| When migrating from prosody 0.10, you need to update the database once: | ||||
| 
 | ||||
| ```bash | ||||
| docker-compose exec server bash | ||||
| prosodyctl mod_storage_sql upgrade | ||||
| ``` | ||||
| 
 | ||||
| ## Matterbridge | ||||
| 
 | ||||
| To enable bridges using **Matterbridge** simply add the service in the docker-compose.yml file. Then you need to add _toml_ config file in ```matterbridge``` directory spedifying protocols and gateways. Check [documentation](https://github.com/42wim/matterbridge#readme). | ||||
| 
 | ||||
| ``` yaml | ||||
|   matterbridge: | ||||
|     image: 42wim/matterbridge:latest | ||||
|     restart: unless-stopped | ||||
|     volumes: | ||||
|     - ./matterbridge/matterbridge.toml:/etc/matterbridge/matterbridge.toml:ro | ||||
|     - ./matterbridge/nicks.tengo:/etc/matterbridge/nicks.tengo:ro | ||||
|     - ./matterbridge/out.tengo:/etc/matterbridge/out.tengo:ro | ||||
|     depends_on: | ||||
|       - server | ||||
| 
 | ||||
| ``` | ||||
| ## Migration tool | ||||
| 
 | ||||
| You need to set up ```migrator.cfg.lua``` config file before build the image. Once your server is working you could use | ||||
| the migration tool in this way: | ||||
| ``` bash | ||||
| docker exec -it prosody-docker_server_1 prosody-migrator -h | ||||
| ``` | ||||
| 
 | ||||
| ## Test your server | ||||
| 
 | ||||
| You can test your server with these websites: | ||||
| 
 | ||||
| * [IM Observatory](https://www.xmpp.net/) | ||||
| * [XMPP Compliance Tester](https://compliance.conversations.im/) | ||||
							
								
								
									
										82
									
								
								tests/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								tests/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,82 @@ | ||||
| version: "3.9" | ||||
| 
 | ||||
| services: | ||||
|   prosody: | ||||
|     image: prosody | ||||
|     restart: unless-stopped | ||||
|     ports: | ||||
|       - "5000:5000" | ||||
|       - "5222:5222" | ||||
|       - "5223:5223" | ||||
|       - "5269:5269" | ||||
|       - "5281:5281" | ||||
|     environment: | ||||
|       DOMAIN: example.com | ||||
|       E2E_POLICY_WHITELIST: "admin@example.com, user1@example.com" | ||||
|       LOG_LEVEL: debug | ||||
|       PROSODY_ADMINS: "admin@example.com, admin2@example.com" | ||||
|     volumes: | ||||
|       - ./certs:/usr/local/etc/prosody/certs | ||||
| 
 | ||||
|   prosody_postgres: | ||||
|     image: prosody | ||||
|     restart: unless-stopped | ||||
|     ports: | ||||
|       - "5000:5000" | ||||
|       - "5222:5222" | ||||
|       - "5223:5223" | ||||
|       - "5269:5269" | ||||
|       - "5281:5281" | ||||
|     environment: | ||||
|       DOMAIN: example.com | ||||
|       E2E_POLICY_WHITELIST: "admin@example.com, user1@example.com" | ||||
|       LOG_LEVEL: debug | ||||
|       PROSODY_ADMINS: "admin@example.com, admin2@example.com" | ||||
|       #DB_DRIVER: "MySQL" | ||||
|       DB_DRIVER: "PostgreSQL" | ||||
|       DB_DATABASE: "prosody" | ||||
|       DB_HOST: "postgres" | ||||
|       DB_PORT: "5432" | ||||
|       DB_USERNAME: "prosody" | ||||
|       DB_PASSWORD: "prosody" | ||||
|     volumes: | ||||
|       - ./certs:/usr/local/etc/prosody/certs | ||||
|     depends_on: | ||||
|       - postgres | ||||
| 
 | ||||
|   postgres: | ||||
|     image: postgres:15-alpine | ||||
|     restart: unless-stopped | ||||
|     environment: | ||||
|       POSTGRES_DB: prosody | ||||
|       POSTGRES_USER: prosody | ||||
|       POSTGRES_PASSWORD: prosody | ||||
| 
 | ||||
|   prosody_ldap: | ||||
|     image: prosody | ||||
|     restart: unless-stopped | ||||
|     ports: | ||||
|       - "5000:5000" | ||||
|       - "5222:5222" | ||||
|       - "5223:5223" | ||||
|       - "5269:5269" | ||||
|       - "5281:5281" | ||||
|     environment: | ||||
|       DOMAIN: example.com | ||||
|       E2E_POLICY_WHITELIST: "admin@example.com, user1@example.com" | ||||
|       LOG_LEVEL: debug | ||||
|       PROSODY_ADMINS: "admin@example.com, admin2@example.com" | ||||
|       AUTHENTICATION: "ldap" | ||||
|       LDAP_BASE: "dc=example,dc=com" | ||||
|       LDAP_SERVER: "glauth" | ||||
|       LDAP_ROOTDN: "cn=svc,dc=example,dc=com" | ||||
|       LDAP_PASSWORD: "12345678" | ||||
|     volumes: | ||||
|       - ./certs:/usr/local/etc/prosody/certs | ||||
|     depends_on: | ||||
|       - glauth | ||||
| 
 | ||||
|   glauth: | ||||
|     image: glauth/glauth | ||||
|     volumes: | ||||
|       - "./glauth/config.cfg:/app/config/config.cfg" | ||||
							
								
								
									
										52
									
								
								tests/glauth/config.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								tests/glauth/config.cfg
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | ||||
| [ldap] | ||||
|   enabled = true | ||||
|   listen = "0.0.0.0:389" | ||||
| 
 | ||||
| [ldaps] | ||||
|   enabled = false | ||||
| 
 | ||||
| [backend] | ||||
|   datastore = "config" | ||||
|   baseDN = "dc=example,dc=com" | ||||
| 
 | ||||
| [[groups]] | ||||
|   name = "svc" | ||||
|   gidnumber = 5500 | ||||
| 
 | ||||
| [[groups]] | ||||
|   name = "people" | ||||
|   gidnumber = 5501 | ||||
| 
 | ||||
| [[users]] | ||||
|   name = "svc" | ||||
|   uidnumber = 5000 | ||||
|   primarygroup = 5500 | ||||
|   passsha256 = "ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f" | ||||
| 
 | ||||
| [[users.capabilities]] | ||||
|   action = "search" | ||||
|   object = "*" | ||||
| 
 | ||||
| [[users]] | ||||
|   name = "admin" | ||||
|   uidnumber = 5001 | ||||
|   primarygroup = 5501 | ||||
|   passsha256 = "ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f" | ||||
| 
 | ||||
| [[users]] | ||||
|   name = "user1" | ||||
|   uidnumber = 5002 | ||||
|   primarygroup = 5501 | ||||
|   passsha256 = "ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f" | ||||
| 
 | ||||
| [[users]] | ||||
|   name = "user2" | ||||
|   uidnumber = 5003 | ||||
|   primarygroup = 5501 | ||||
|   passsha256 = "ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f" | ||||
| 
 | ||||
| [[users]] | ||||
|   name = "user3" | ||||
|   uidnumber = 5004 | ||||
|   primarygroup = 5501 | ||||
|   passsha256 = "ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f" | ||||
							
								
								
									
										28
									
								
								tests/readme.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								tests/readme.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| # Tests | ||||
| 
 | ||||
| Pytest is used to login and send messages to other accounts. | ||||
| Bats is used to check the log for debug messages. | ||||
| 
 | ||||
| ## Dependencies | ||||
| 
 | ||||
| * docker | ||||
| * docker-compose | ||||
| * python 3 | ||||
| 
 | ||||
| ## Run tests | ||||
| 
 | ||||
| Execute [`test.bash`](test.bash). | ||||
| 
 | ||||
| ## Upgrade python packages | ||||
| 
 | ||||
| The following will install the newest version of packages in requirements.txt. | ||||
| 
 | ||||
| ``` bash | ||||
| cat requirements.txt | sed 's/==.*//g' | xargs pip install -U | ||||
| ``` | ||||
| 
 | ||||
| If updates are available --> update and create new version with: | ||||
| 
 | ||||
| ``` bash | ||||
| pip-chill > requirements.txt | ||||
| ``` | ||||
							
								
								
									
										4
									
								
								tests/requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								tests/requirements.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| aioxmpp==0.13.3 | ||||
| pip-chill==1.0.1 | ||||
| pytest-asyncio==0.21.0 | ||||
| pytz==2022.7.1 | ||||
							
								
								
									
										77
									
								
								tests/test.bash
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										77
									
								
								tests/test.bash
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,77 @@ | ||||
| #!/bin/bash | ||||
| 
 | ||||
| set -e | ||||
| 
 | ||||
| # generate certs for testing | ||||
| 
 | ||||
| generateCert() { | ||||
|     local DOMAIN="$1" | ||||
|     if [[ ! -d certs/"$DOMAIN" ]] ; then | ||||
|         mkdir -p certs/"$DOMAIN" | ||||
|         cd certs/"$DOMAIN" | ||||
|         openssl req -x509 -newkey rsa:4096 -keyout privkey.pem -out fullchain.pem -days 365 -subj "/CN=$DOMAIN" -nodes | ||||
|         chmod 777 *.pem | ||||
|         cd ../../ | ||||
|     fi | ||||
| } | ||||
| 
 | ||||
| registerTestUser() { | ||||
|     local userName="$1" | ||||
|     local containerName="$2" | ||||
|     echo "Registering TestUser '$userName' in container '$containerName'" | ||||
|     sudo docker compose exec "$containerName" /bin/bash -c "/entrypoint.bash register $userName example.com 12345678" | ||||
| } | ||||
| 
 | ||||
| registerTestUsers() { | ||||
|     local containerName="$1" | ||||
|     registerTestUser admin "$containerName" | ||||
|     registerTestUser user1 "$containerName" | ||||
|     registerTestUser user2 "$containerName" | ||||
|     registerTestUser user3 "$containerName" | ||||
| } | ||||
| 
 | ||||
| runTests() { | ||||
|     local containerName="$1" | ||||
|     python --version \ | ||||
|     && python3 --version \ | ||||
|     && python3 -m venv venv \ | ||||
|     && source venv/bin/activate \ | ||||
|     && python --version \ | ||||
|     && pip --version \ | ||||
|     && pip install -r requirements.txt \ | ||||
|     && pytest \ | ||||
|     && deactivate \ | ||||
|     && sleep 5 \ | ||||
|     && sudo docker-compose logs "$containerName" \ | ||||
|     && export batsContainerName="$containerName" \ | ||||
|     && ./bats/bats-core/bin/bats tests.bats \ | ||||
|     && ./bats/bats-core/bin/bats tests-"$containerName".bats | ||||
| } | ||||
| 
 | ||||
| generateCert "example.com" | ||||
| generateCert "conference.example.com" | ||||
| generateCert "proxy.example.com" | ||||
| generateCert "pubsub.example.com" | ||||
| generateCert "upload.example.com" | ||||
| 
 | ||||
| # Run tests for first container with postgres | ||||
| # Start postgres first and wait for 10 seconds before starting prosody. | ||||
| sudo docker-compose down | ||||
| sudo docker-compose up -d postgres | ||||
| sleep 10 | ||||
| sudo docker-compose up -d prosody_postgres | ||||
| 
 | ||||
| registerTestUsers prosody_postgres | ||||
| runTests prosody_postgres | ||||
| sudo docker-compose down | ||||
| 
 | ||||
| # Run tests for second container with SQLite | ||||
| sudo docker-compose up -d prosody | ||||
| registerTestUsers prosody | ||||
| runTests prosody | ||||
| sudo docker-compose down | ||||
| 
 | ||||
| # Run tests for prosody with ldap | ||||
| sudo docker-compose up -d prosody_ldap | ||||
| runTests prosody_ldap | ||||
| sudo docker-compose down | ||||
							
								
								
									
										120
									
								
								tests/test_prosody.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								tests/test_prosody.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,120 @@ | ||||
| import aiosasl | ||||
| import aioxmpp | ||||
| import aioxmpp.dispatcher | ||||
| import asyncio | ||||
| import pytest | ||||
| 
 | ||||
| @pytest.fixture | ||||
| def client(client_username, password): | ||||
| 
 | ||||
|     jid = aioxmpp.JID.fromstr(client_username) | ||||
| 
 | ||||
|     client = aioxmpp.PresenceManagedClient( | ||||
|         jid, | ||||
|         aioxmpp.make_security_layer( | ||||
|             password, | ||||
|             no_verify=True | ||||
|         ), | ||||
|         override_peer=[("localhost", 5222, aioxmpp.connector.STARTTLSConnector())], | ||||
|     ) | ||||
|     return client | ||||
| 
 | ||||
| @pytest.fixture | ||||
| def client_with_message_dispatcher(client): | ||||
|     def message_received(msg): | ||||
|         print(msg) | ||||
|         print(msg.body) | ||||
|         assert msg.body == "Hello World!" | ||||
| 
 | ||||
|     # obtain an instance of the service | ||||
|     message_dispatcher = client.summon( | ||||
|     aioxmpp.dispatcher.SimpleMessageDispatcher | ||||
|     ) | ||||
| 
 | ||||
|     # register a message callback here | ||||
|     message_dispatcher.register_callback( | ||||
|         aioxmpp.MessageType.CHAT, | ||||
|         None, | ||||
|         message_received, | ||||
|     ) | ||||
|     return client | ||||
| 
 | ||||
| @pytest.mark.asyncio | ||||
| @pytest.mark.parametrize("client_username, password", [("admin@example.com", "12345678")]) | ||||
| async def test_send_message_from_admin_to_user1(client): | ||||
|     recipient_jid = aioxmpp.JID.fromstr("user1@example.com") | ||||
|     async with client.connected() as stream: | ||||
|         msg = aioxmpp.Message( | ||||
|             to=recipient_jid, | ||||
|             type_=aioxmpp.MessageType.CHAT, | ||||
|         ) | ||||
|         # None is for "default language" | ||||
|         msg.body[None] = "Hello World!" | ||||
| 
 | ||||
|         await client.send(msg) | ||||
| 
 | ||||
| @pytest.mark.asyncio | ||||
| @pytest.mark.parametrize("client_username, password", [("admin@example.com", "12345678")]) | ||||
| async def test_send_message_from_admin_to_user2(client): | ||||
|     recipient_jid = aioxmpp.JID.fromstr("user2@example.com") | ||||
|     async with client.connected() as stream: | ||||
|         msg = aioxmpp.Message( | ||||
|             to=recipient_jid, | ||||
|             type_=aioxmpp.MessageType.CHAT, | ||||
|         ) | ||||
|         msg.body[None] = "Hello World!" | ||||
| 
 | ||||
|         await client.send(msg) | ||||
| 
 | ||||
| @pytest.mark.asyncio | ||||
| @pytest.mark.parametrize("client_username, password", [("user1@example.com", "12345678")]) | ||||
| async def test_send_message_from_user1_to_user2(client): | ||||
|     recipient_jid = aioxmpp.JID.fromstr("user2@example.com") | ||||
|     async with client.connected() as stream: | ||||
|         msg = aioxmpp.Message( | ||||
|             to=recipient_jid, | ||||
|             type_=aioxmpp.MessageType.CHAT, | ||||
|         ) | ||||
|         msg.body[None] = "Hello World!" | ||||
| 
 | ||||
|         await client.send(msg) | ||||
| 
 | ||||
| @pytest.mark.asyncio | ||||
| @pytest.mark.parametrize("client_username, password", [("user2@example.com", "12345678")]) | ||||
| async def test_send_message_from_user2_to_user3(client): | ||||
|     recipient_jid = aioxmpp.JID.fromstr("user3@example.com") | ||||
|     async with client.connected() as stream: | ||||
|         msg = aioxmpp.Message( | ||||
|             to=recipient_jid, | ||||
|             type_=aioxmpp.MessageType.CHAT, | ||||
|         ) | ||||
|         msg.body[None] = "Hello World!" | ||||
| 
 | ||||
|         await client.send(msg) | ||||
| 
 | ||||
| @pytest.mark.asyncio | ||||
| @pytest.mark.parametrize("client_username, password", [("user2@example.com", "12345678")]) | ||||
| async def test_send_message_from_user2_to_nonexisting(client): | ||||
|     recipient_jid = aioxmpp.JID.fromstr("nonexisting@example.com") | ||||
|     async with client.connected() as stream: | ||||
|         msg = aioxmpp.Message( | ||||
|             to=recipient_jid, | ||||
|             type_=aioxmpp.MessageType.CHAT, | ||||
|         ) | ||||
|         msg.body[None] = "Hello World!" | ||||
| 
 | ||||
|         await client.send(msg) | ||||
| 
 | ||||
| @pytest.mark.asyncio | ||||
| @pytest.mark.parametrize("client_username, password", [("user2@example.com", "wrong password")]) | ||||
| async def test_can_not_log_in_with_wrong_password(client): | ||||
|     with pytest.raises(aiosasl.AuthenticationFailure): | ||||
|         recipient_jid = aioxmpp.JID.fromstr("nonexisting@example.com") | ||||
|         async with client.connected() as stream: | ||||
|             msg = aioxmpp.Message( | ||||
|                 to=recipient_jid, | ||||
|                 type_=aioxmpp.MessageType.CHAT, | ||||
|             ) | ||||
|             msg.body[None] = "Hello World!" | ||||
| 
 | ||||
|             await client.send(msg) | ||||
							
								
								
									
										10
									
								
								tests/tests-prosody.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tests/tests-prosody.bats
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| # For tests with pipes see: https://github.com/sstephenson/bats/issues/10 | ||||
| 
 | ||||
| load 'bats/bats-support/load' | ||||
| load 'bats/bats-assert/load' | ||||
| 
 | ||||
| @test "Should use sqlite" { | ||||
|   run bash -c "sudo docker-compose logs $batsContainerName | grep -E \"Connecting to \[SQLite3\] \/usr\/local\/var\/lib\/prosody\/prosody\.sqlite\.\.\.\"" | ||||
|   assert_success | ||||
|   assert_output | ||||
| } | ||||
							
								
								
									
										16
									
								
								tests/tests-prosody_ldap.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tests/tests-prosody_ldap.bats
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| # For tests with pipes see: https://github.com/sstephenson/bats/issues/10 | ||||
| 
 | ||||
| load 'bats/bats-support/load' | ||||
| load 'bats/bats-assert/load' | ||||
| 
 | ||||
| @test "Should use sqlite" { | ||||
|   run bash -c "sudo docker-compose logs $batsContainerName | grep -E \"Connecting to \[SQLite3\] \/usr\/local\/var\/lib\/prosody\/prosody\.sqlite\.\.\.\"" | ||||
|   assert_success | ||||
|   assert_output | ||||
| } | ||||
| 
 | ||||
| @test "Should use ldap" { | ||||
|   run bash -c "sudo docker-compose logs $batsContainerName | grep -E \"Host 'example.com' now set to use user provider 'ldap'\"" | ||||
|   assert_success | ||||
|   assert_output | ||||
| } | ||||
							
								
								
									
										10
									
								
								tests/tests-prosody_postgres.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tests/tests-prosody_postgres.bats
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| # For tests with pipes see: https://github.com/sstephenson/bats/issues/10 | ||||
| 
 | ||||
| load 'bats/bats-support/load' | ||||
| load 'bats/bats-assert/load' | ||||
| 
 | ||||
| @test "Should use postgres" { | ||||
|   run bash -c "sudo docker-compose logs $batsContainerName | grep -E \"Connecting to \[PostgreSQL\] prosody\.\.\.\"" | ||||
|   assert_success | ||||
|   assert_output | ||||
| } | ||||
							
								
								
									
										88
									
								
								tests/tests.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								tests/tests.bats
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,88 @@ | ||||
| # For tests with pipes see: https://github.com/sstephenson/bats/issues/10 | ||||
| 
 | ||||
| load 'bats/bats-support/load' | ||||
| load 'bats/bats-assert/load' | ||||
| 
 | ||||
| @test "Should send 5 messages" { | ||||
|   run bash -c "sudo docker-compose logs $batsContainerName | grep -E \"Received\[c2s\]: <message\" | wc -l" | ||||
|   assert_success | ||||
|   assert_output "5" | ||||
| } | ||||
| 
 | ||||
| @test "Should select certificate for example.com" { | ||||
|   run bash -c "sudo docker-compose logs $batsContainerName | grep \"Certificates loaded\" | grep \" example.com:tls\" | wc -l" | ||||
|   assert_success | ||||
|   assert_output "1" | ||||
| } | ||||
| 
 | ||||
| @test "Should select certificate for conference.example.com" { | ||||
|   run bash -c "sudo docker-compose logs $batsContainerName | grep \"Certificates loaded\" | grep \"conference.example.com:tls\" | wc -l" | ||||
|   assert_success | ||||
|   assert_output "1" | ||||
| } | ||||
| 
 | ||||
| @test "Should select certificate for proxy.example.com" { | ||||
|   run bash -c "sudo docker-compose logs $batsContainerName | grep \"Certificates loaded\" | grep \"proxy.example.com:tls\" | wc -l" | ||||
|   assert_success | ||||
|   assert_output "1" | ||||
| } | ||||
| 
 | ||||
| @test "Should select certificate for pubsub.example.com" { | ||||
|   run bash -c "sudo docker-compose logs $batsContainerName | grep \"Certificates loaded\" | grep \"pubsub.example.com:tls\" | wc -l" | ||||
|   assert_success | ||||
|   assert_output "1" | ||||
| } | ||||
| 
 | ||||
| @test "Should select certificate for upload.example.com" { | ||||
|   run bash -c "sudo docker-compose logs $batsContainerName | grep \"Certificates loaded\" | grep \"upload.example.com:tls\" | wc -l" | ||||
|   assert_success | ||||
|   assert_output "1" | ||||
| } | ||||
| 
 | ||||
| @test "Should log error for user with wrong password" { | ||||
|   run bash -c "sudo docker-compose logs $batsContainerName | grep \"Session closed by remote with error: undefined-condition (user intervention: authentication failed: authentication aborted by user)\"" | ||||
|   assert_success | ||||
|   assert_output | ||||
| } | ||||
| 
 | ||||
| @test "Should activate s2s" { | ||||
|   run bash -c "sudo docker-compose logs $batsContainerName | grep -E \"Activated service 's2s' on (\[::\]:5269|\[\*\]:5269), (\[::\]:5269|\[\*\]:5269)\"" | ||||
|   assert_success | ||||
|   assert_output | ||||
| } | ||||
| 
 | ||||
| @test "Should activate c2s" { | ||||
|   run bash -c "sudo docker-compose logs $batsContainerName | grep -E \"Activated service 'c2s' on (\[::\]:5222|\[\*\]:5222), (\[::\]:5222|\[\*\]:5222)\"" | ||||
|   assert_success | ||||
|   assert_output | ||||
| } | ||||
| 
 | ||||
| @test "Should activate legacy_ssl" { | ||||
|   run bash -c "sudo docker-compose logs $batsContainerName | grep -E \"Activated service 'legacy_ssl' on (\[::\]:5223|\[\*\]:5223), (\[::\]:5223|\[\*\]:5223)\"" | ||||
|   assert_success | ||||
|   assert_output | ||||
| } | ||||
| 
 | ||||
| @test "Should activate proxy65" { | ||||
|   run bash -c "sudo docker-compose logs $batsContainerName | grep -E \"Activated service 'proxy65' on (\[::\]:5000|\[\*\]:5000), (\[::\]:5000|\[\*\]:5000)\"" | ||||
|   assert_success | ||||
|   assert_output | ||||
| } | ||||
| 
 | ||||
| @test "Should activate https" { | ||||
|   run bash -c "sudo docker-compose logs $batsContainerName | grep -E \"Activated service 'https' on (\[::\]:5281|\[\*\]:5281), (\[::\]:5281|\[\*\]:5281)\"" | ||||
|   assert_success | ||||
|   assert_output | ||||
| } | ||||
| 
 | ||||
| @test "Should load module cloud_notify" { | ||||
|   run bash -c "sudo docker-compose logs $batsContainerName | grep \"example.com:cloud_notify.*info.*Module loaded\"" | ||||
|   assert_success | ||||
|   assert_output | ||||
| } | ||||
| 
 | ||||
| @test "Should show upload URL" { | ||||
|   run bash -c "sudo docker-compose logs $batsContainerName | grep \"URL: <https:\/\/upload.example.com:5281\/upload> - Ensure this can be reached by users\"" | ||||
|   assert_success | ||||
|   assert_output | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user