├── .dockerignore ├── .gitignore ├── .gitlab-ci.yml ├── LICENSE ├── Makefile ├── README.md ├── VERSION ├── build.sh ├── config.json.dist ├── docs ├── README.md ├── architecture.md ├── configuration.md ├── development.md ├── faq.md ├── setup.md └── setup_with_postfix.md ├── email2matrix-server.go ├── email2matrix ├── configuration │ └── configuration.go ├── container │ └── container.go ├── matrix │ ├── email_relayer.go │ ├── message_generator.go │ └── message_generator_test.go ├── mime │ ├── parser.go │ ├── parser_test.go │ └── testdata │ │ ├── geary-plain.txt │ │ ├── gmail-base64.txt │ │ └── phpmailer-quoted-printable.txt ├── resolver │ ├── model.go │ └── resolver.go └── smtp │ └── email2matrix_processor.go ├── etc ├── docker │ ├── Dockerfile │ └── README.md └── services │ ├── docker-compose.yaml │ ├── matrix-riot-web │ ├── config.json │ └── nginx.conf │ └── matrix-synapse │ ├── email2matrix.127.0.0.1.xip.io.log.config │ ├── email2matrix.127.0.0.1.xip.io.signing.key │ └── homeserver.yaml ├── go.mod └── go.sum /.dockerignore: -------------------------------------------------------------------------------- 1 | /var 2 | /vendor/ 3 | /pkg 4 | /email2matrix-server 5 | /config.json 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /var 2 | /vendor 3 | /email2matrix-server 4 | /config.json 5 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | # We opt for the fastest compression, because our artifacts are bzip2-compressed already. 3 | ARTIFACT_COMPRESSION_LEVEL: "fastest" 4 | 5 | CONTAINER_IMAGE_NAME: devture/email2matrix 6 | CONTAINER_IMAGE_NAME_DASHED: devture-email2matrix 7 | 8 | LATEST_REPOSITORY_BRANCH_NAME: master 9 | LATEST_CONTAINER_IMAGE_TAG_PREFIX: latest 10 | 11 | CONTAINER_REGISTRY: 'docker.io' 12 | CONTAINER_REGISTRY_USER: '' 13 | CONTAINER_REGISTRY_PASSWORD: '' 14 | 15 | # This pipeline is only triggered for tags, the default branch and the `LATEST_REPOSITORY_BRANCH_NAME` branch (which likely matches the default). 16 | # Publishing may follow different rules. 17 | workflow: 18 | rules: 19 | - if: $CI_COMMIT_TAG 20 | - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH 21 | - if: $CI_COMMIT_BRANCH == $LATEST_REPOSITORY_BRANCH_NAME 22 | 23 | stages: 24 | - build-container-images 25 | - publish-container-images 26 | 27 | build-container-image-amd64: 28 | extends: .build-container-image 29 | variables: 30 | PLATFORM: linux/amd64 31 | PLATFORM_IDENTIFIER: amd64 32 | 33 | build-container-image-arm64v8: 34 | extends: .build-container-image 35 | variables: 36 | PLATFORM: linux/arm64/v8 37 | PLATFORM_IDENTIFIER: arm64v8 38 | 39 | build-container-image-arm32v7: 40 | extends: .build-container-image 41 | variables: 42 | PLATFORM: linux/arm/v7 43 | PLATFORM_IDENTIFIER: arm32v7 44 | 45 | # This private job spec that we invoke via `build-container-image-*` jobs: 46 | # - builds the container image for the given platform (`PLATFORM`, `PLATFORM_IDENTIFIER`) 47 | # - tags it as `CONTAINER_IMAGE_NAME:CONTAINER_IMAGE_TAG` (`CONTAINER_IMAGE_TAG` is dynamically built below) 48 | # - exports it as an artifact to a file (`${CONTAINER_IMAGE_NAME_DASHED}-${CI_COMMIT_SHORT_SHA}-${PLATFORM_IDENTIFIER}-container-image.tbz2`) 49 | # 50 | # This build job is inspired by: https://medium.com/prgcont/using-buildah-in-gitlab-ci-9b529af19e42 51 | .build-container-image: 52 | stage: build-container-images 53 | needs: [] 54 | 55 | image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/tomkukral/buildah:0.30 56 | 57 | before_script: 58 | # Log in to the Gitlab Dependency Proxy container regsitry, so we can use it below. 59 | - podman login ${CI_DEPENDENCY_PROXY_SERVER} -u ${CI_DEPENDENCY_PROXY_USER} -p ${CI_DEPENDENCY_PROXY_PASSWORD} 60 | 61 | # Log in to Docker Hub regardless of whether we'll be pushing images or not, because rate limits for logged in users are higher. 62 | - podman login ${CONTAINER_REGISTRY} -u ${CONTAINER_REGISTRY_USER} -p ${CONTAINER_REGISTRY_PASSWORD} 63 | 64 | - podman run --rm --network=none --privileged ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/multiarch/qemu-user-static --reset -p yes 65 | 66 | - CONTAINER_IMAGE_TAG="${CI_COMMIT_SHORT_SHA}-${PLATFORM_IDENTIFIER}" 67 | 68 | # We might potentially modify the Dockerfile we'll build from (below), 69 | # so we'd like to modify a copy of it, instead of dirtying up the git worktree. 70 | # The reason we care about keeping the git worktree clean is that `govvv` (see the Dockerfile) will say "dirty" if we don't. 71 | - cp etc/docker/Dockerfile /tmp/Dockerfile 72 | 73 | # Use the Gitlab Dependency Proxy instead of docker.io to speed things up and avoid rate limits. 74 | # 75 | # Pulling from Dependency Proxy is only enabled on amd64 for now, 76 | # because using it to pull multiarch images (such as the ones we have in the Dockerfile) causes issues. 77 | # See: https://gitlab.com/gitlab-org/gitlab/-/issues/349466 78 | - | 79 | if [ "$PLATFORM_IDENTIFIER" == "amd64" ]; then 80 | sed --in-place 's|docker.io|'${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}'|g' /tmp/Dockerfile 81 | fi 82 | 83 | script: 84 | - echo Building for ${PLATFORM} and tagging as ${CONTAINER_IMAGE_NAME}:${CONTAINER_IMAGE_TAG} 85 | - buildah bud -f /tmp/Dockerfile --format=docker --platform=${PLATFORM} -t ${CONTAINER_IMAGE_NAME}:${CONTAINER_IMAGE_TAG} . 86 | - podman save ${CONTAINER_IMAGE_NAME}:${CONTAINER_IMAGE_TAG} | bzip2 > ${CONTAINER_IMAGE_NAME_DASHED}-${CI_COMMIT_SHORT_SHA}-${PLATFORM_IDENTIFIER}-container-image.tbz2 87 | 88 | artifacts: 89 | name: ${CONTAINER_IMAGE_NAME_DASHED}-${CI_COMMIT_SHORT_SHA}-${PLATFORM_IDENTIFIER}-container-image.tbz2 90 | paths: 91 | - ${CONTAINER_IMAGE_NAME_DASHED}-${CI_COMMIT_SHORT_SHA}-${PLATFORM_IDENTIFIER}-container-image.tbz2 92 | 93 | # This publishing job: 94 | # - takes all platform-specific images from artifact files 95 | # - imports them all locally 96 | # - re-tags them and publishes to Docker Hub 97 | publish-container-images: 98 | stage: publish-container-images 99 | needs: 100 | - build-container-image-amd64 101 | - build-container-image-arm64v8 102 | - build-container-image-arm32v7 103 | dependencies: 104 | - build-container-image-amd64 105 | - build-container-image-arm64v8 106 | - build-container-image-arm32v7 107 | 108 | image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/tomkukral/buildah:0.30 109 | 110 | before_script: 111 | - | 112 | for platform_identifier in amd64 arm64v8 arm32v7; do 113 | podman load -i "${CONTAINER_IMAGE_NAME_DASHED}-${CI_COMMIT_SHORT_SHA}-${platform_identifier}-container-image.tbz2" 114 | done 115 | 116 | # Repository pushes to `LATEST_REPOSITORY_BRANCH_NAME` will trigger an image push for `image:LATEST` and `image:LATEST-PLATFORM_IDENTIFIER`. 117 | # Repository pushes to a tag (e.g. `VERSION_TAG`) will trigger an image push for `image:VERSION_TAG` and `image:VERSION_TAG-PLATFORM_IDENTIFIER` 118 | - | 119 | container_image_published_tag_prefix="" 120 | 121 | if [ "$CI_COMMIT_BRANCH" == "$LATEST_REPOSITORY_BRANCH_NAME" ]; then 122 | container_image_published_tag_prefix=${LATEST_CONTAINER_IMAGE_TAG_PREFIX} 123 | fi 124 | 125 | if [ "$CI_COMMIT_TAG" ]; then 126 | container_image_published_tag_prefix=${CI_COMMIT_TAG} 127 | fi 128 | 129 | # We push a manifest in the v2s2 format, instead of the default (oci). Otherwise Docker Hub's UI does not render it correctly. 130 | script: 131 | - | 132 | if [ "$container_image_published_tag_prefix" ]; then 133 | podman login ${CONTAINER_REGISTRY} -u ${CONTAINER_REGISTRY_USER} -p ${CONTAINER_REGISTRY_PASSWORD} 134 | 135 | set -x 136 | 137 | for platform_identifier in amd64 arm64v8 arm32v7; do 138 | podman tag ${CONTAINER_IMAGE_NAME}:${CI_COMMIT_SHORT_SHA}-${platform_identifier} ${CONTAINER_REGISTRY}/${CONTAINER_IMAGE_NAME}:${container_image_published_tag_prefix}-${platform_identifier} 139 | podman push ${CONTAINER_REGISTRY}/${CONTAINER_IMAGE_NAME}:${container_image_published_tag_prefix}-${platform_identifier} 140 | done 141 | 142 | podman manifest create ${CONTAINER_REGISTRY}/${CONTAINER_IMAGE_NAME}:${container_image_published_tag_prefix} ${CONTAINER_REGISTRY}/${CONTAINER_IMAGE_NAME}:${container_image_published_tag_prefix}-amd64 ${CONTAINER_REGISTRY}/${CONTAINER_IMAGE_NAME}:${container_image_published_tag_prefix}-arm64v8 ${CONTAINER_REGISTRY}/${CONTAINER_IMAGE_NAME}:${container_image_published_tag_prefix}-arm32v7 143 | 144 | podman manifest push --format=v2s2 ${CONTAINER_REGISTRY}/${CONTAINER_IMAGE_NAME}:${container_image_published_tag_prefix} ${CONTAINER_REGISTRY}/${CONTAINER_IMAGE_NAME}:${container_image_published_tag_prefix} 145 | fi 146 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU AFFERO GENERAL PUBLIC LICENSE 2 | Version 3, 19 November 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU Affero General Public License is a free, copyleft license for 11 | software and other kinds of works, specifically designed to ensure 12 | cooperation with the community in the case of network server software. 13 | 14 | The licenses for most software and other practical works are designed 15 | to take away your freedom to share and change the works. By contrast, 16 | our General Public Licenses are intended to guarantee your freedom to 17 | share and change all versions of a program--to make sure it remains free 18 | software for all its users. 19 | 20 | When we speak of free software, we are referring to freedom, not 21 | price. Our General Public Licenses are designed to make sure that you 22 | have the freedom to distribute copies of free software (and charge for 23 | them if you wish), that you receive source code or can get it if you 24 | want it, that you can change the software or use pieces of it in new 25 | free programs, and that you know you can do these things. 26 | 27 | Developers that use our General Public Licenses protect your rights 28 | with two steps: (1) assert copyright on the software, and (2) offer 29 | you this License which gives you legal permission to copy, distribute 30 | and/or modify the software. 31 | 32 | A secondary benefit of defending all users' freedom is that 33 | improvements made in alternate versions of the program, if they 34 | receive widespread use, become available for other developers to 35 | incorporate. Many developers of free software are heartened and 36 | encouraged by the resulting cooperation. However, in the case of 37 | software used on network servers, this result may fail to come about. 38 | The GNU General Public License permits making a modified version and 39 | letting the public access it on a server without ever releasing its 40 | source code to the public. 41 | 42 | The GNU Affero General Public License is designed specifically to 43 | ensure that, in such cases, the modified source code becomes available 44 | to the community. It requires the operator of a network server to 45 | provide the source code of the modified version running there to the 46 | users of that server. Therefore, public use of a modified version, on 47 | a publicly accessible server, gives the public access to the source 48 | code of the modified version. 49 | 50 | An older license, called the Affero General Public License and 51 | published by Affero, was designed to accomplish similar goals. This is 52 | a different license, not a version of the Affero GPL, but Affero has 53 | released a new version of the Affero GPL which permits relicensing under 54 | this license. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | TERMS AND CONDITIONS 60 | 61 | 0. Definitions. 62 | 63 | "This License" refers to version 3 of the GNU Affero General Public License. 64 | 65 | "Copyright" also means copyright-like laws that apply to other kinds of 66 | works, such as semiconductor masks. 67 | 68 | "The Program" refers to any copyrightable work licensed under this 69 | License. Each licensee is addressed as "you". "Licensees" and 70 | "recipients" may be individuals or organizations. 71 | 72 | To "modify" a work means to copy from or adapt all or part of the work 73 | in a fashion requiring copyright permission, other than the making of an 74 | exact copy. The resulting work is called a "modified version" of the 75 | earlier work or a work "based on" the earlier work. 76 | 77 | A "covered work" means either the unmodified Program or a work based 78 | on the Program. 79 | 80 | To "propagate" a work means to do anything with it that, without 81 | permission, would make you directly or secondarily liable for 82 | infringement under applicable copyright law, except executing it on a 83 | computer or modifying a private copy. Propagation includes copying, 84 | distribution (with or without modification), making available to the 85 | public, and in some countries other activities as well. 86 | 87 | To "convey" a work means any kind of propagation that enables other 88 | parties to make or receive copies. Mere interaction with a user through 89 | a computer network, with no transfer of a copy, is not conveying. 90 | 91 | An interactive user interface displays "Appropriate Legal Notices" 92 | to the extent that it includes a convenient and prominently visible 93 | feature that (1) displays an appropriate copyright notice, and (2) 94 | tells the user that there is no warranty for the work (except to the 95 | extent that warranties are provided), that licensees may convey the 96 | work under this License, and how to view a copy of this License. If 97 | the interface presents a list of user commands or options, such as a 98 | menu, a prominent item in the list meets this criterion. 99 | 100 | 1. Source Code. 101 | 102 | The "source code" for a work means the preferred form of the work 103 | for making modifications to it. "Object code" means any non-source 104 | form of a work. 105 | 106 | A "Standard Interface" means an interface that either is an official 107 | standard defined by a recognized standards body, or, in the case of 108 | interfaces specified for a particular programming language, one that 109 | is widely used among developers working in that language. 110 | 111 | The "System Libraries" of an executable work include anything, other 112 | than the work as a whole, that (a) is included in the normal form of 113 | packaging a Major Component, but which is not part of that Major 114 | Component, and (b) serves only to enable use of the work with that 115 | Major Component, or to implement a Standard Interface for which an 116 | implementation is available to the public in source code form. A 117 | "Major Component", in this context, means a major essential component 118 | (kernel, window system, and so on) of the specific operating system 119 | (if any) on which the executable work runs, or a compiler used to 120 | produce the work, or an object code interpreter used to run it. 121 | 122 | The "Corresponding Source" for a work in object code form means all 123 | the source code needed to generate, install, and (for an executable 124 | work) run the object code and to modify the work, including scripts to 125 | control those activities. However, it does not include the work's 126 | System Libraries, or general-purpose tools or generally available free 127 | programs which are used unmodified in performing those activities but 128 | which are not part of the work. For example, Corresponding Source 129 | includes interface definition files associated with source files for 130 | the work, and the source code for shared libraries and dynamically 131 | linked subprograms that the work is specifically designed to require, 132 | such as by intimate data communication or control flow between those 133 | subprograms and other parts of the work. 134 | 135 | The Corresponding Source need not include anything that users 136 | can regenerate automatically from other parts of the Corresponding 137 | Source. 138 | 139 | The Corresponding Source for a work in source code form is that 140 | same work. 141 | 142 | 2. Basic Permissions. 143 | 144 | All rights granted under this License are granted for the term of 145 | copyright on the Program, and are irrevocable provided the stated 146 | conditions are met. This License explicitly affirms your unlimited 147 | permission to run the unmodified Program. The output from running a 148 | covered work is covered by this License only if the output, given its 149 | content, constitutes a covered work. This License acknowledges your 150 | rights of fair use or other equivalent, as provided by copyright law. 151 | 152 | You may make, run and propagate covered works that you do not 153 | convey, without conditions so long as your license otherwise remains 154 | in force. You may convey covered works to others for the sole purpose 155 | of having them make modifications exclusively for you, or provide you 156 | with facilities for running those works, provided that you comply with 157 | the terms of this License in conveying all material for which you do 158 | not control copyright. Those thus making or running the covered works 159 | for you must do so exclusively on your behalf, under your direction 160 | and control, on terms that prohibit them from making any copies of 161 | your copyrighted material outside their relationship with you. 162 | 163 | Conveying under any other circumstances is permitted solely under 164 | the conditions stated below. Sublicensing is not allowed; section 10 165 | makes it unnecessary. 166 | 167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 168 | 169 | No covered work shall be deemed part of an effective technological 170 | measure under any applicable law fulfilling obligations under article 171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 172 | similar laws prohibiting or restricting circumvention of such 173 | measures. 174 | 175 | When you convey a covered work, you waive any legal power to forbid 176 | circumvention of technological measures to the extent such circumvention 177 | is effected by exercising rights under this License with respect to 178 | the covered work, and you disclaim any intention to limit operation or 179 | modification of the work as a means of enforcing, against the work's 180 | users, your or third parties' legal rights to forbid circumvention of 181 | technological measures. 182 | 183 | 4. Conveying Verbatim Copies. 184 | 185 | You may convey verbatim copies of the Program's source code as you 186 | receive it, in any medium, provided that you conspicuously and 187 | appropriately publish on each copy an appropriate copyright notice; 188 | keep intact all notices stating that this License and any 189 | non-permissive terms added in accord with section 7 apply to the code; 190 | keep intact all notices of the absence of any warranty; and give all 191 | recipients a copy of this License along with the Program. 192 | 193 | You may charge any price or no price for each copy that you convey, 194 | and you may offer support or warranty protection for a fee. 195 | 196 | 5. Conveying Modified Source Versions. 197 | 198 | You may convey a work based on the Program, or the modifications to 199 | produce it from the Program, in the form of source code under the 200 | terms of section 4, provided that you also meet all of these conditions: 201 | 202 | a) The work must carry prominent notices stating that you modified 203 | it, and giving a relevant date. 204 | 205 | b) The work must carry prominent notices stating that it is 206 | released under this License and any conditions added under section 207 | 7. This requirement modifies the requirement in section 4 to 208 | "keep intact all notices". 209 | 210 | c) You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | 218 | d) If the work has interactive user interfaces, each must display 219 | Appropriate Legal Notices; however, if the Program has interactive 220 | interfaces that do not display Appropriate Legal Notices, your 221 | work need not make them do so. 222 | 223 | A compilation of a covered work with other separate and independent 224 | works, which are not by their nature extensions of the covered work, 225 | and which are not combined with it such as to form a larger program, 226 | in or on a volume of a storage or distribution medium, is called an 227 | "aggregate" if the compilation and its resulting copyright are not 228 | used to limit the access or legal rights of the compilation's users 229 | beyond what the individual works permit. Inclusion of a covered work 230 | in an aggregate does not cause this License to apply to the other 231 | parts of the aggregate. 232 | 233 | 6. Conveying Non-Source Forms. 234 | 235 | You may convey a covered work in object code form under the terms 236 | of sections 4 and 5, provided that you also convey the 237 | machine-readable Corresponding Source under the terms of this License, 238 | in one of these ways: 239 | 240 | a) Convey the object code in, or embodied in, a physical product 241 | (including a physical distribution medium), accompanied by the 242 | Corresponding Source fixed on a durable physical medium 243 | customarily used for software interchange. 244 | 245 | b) Convey the object code in, or embodied in, a physical product 246 | (including a physical distribution medium), accompanied by a 247 | written offer, valid for at least three years and valid for as 248 | long as you offer spare parts or customer support for that product 249 | model, to give anyone who possesses the object code either (1) a 250 | copy of the Corresponding Source for all the software in the 251 | product that is covered by this License, on a durable physical 252 | medium customarily used for software interchange, for a price no 253 | more than your reasonable cost of physically performing this 254 | conveying of source, or (2) access to copy the 255 | Corresponding Source from a network server at no charge. 256 | 257 | c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | 263 | d) Convey the object code by offering access from a designated 264 | place (gratis or for a charge), and offer equivalent access to the 265 | Corresponding Source in the same way through the same place at no 266 | further charge. You need not require recipients to copy the 267 | Corresponding Source along with the object code. If the place to 268 | copy the object code is a network server, the Corresponding Source 269 | may be on a different server (operated by you or a third party) 270 | that supports equivalent copying facilities, provided you maintain 271 | clear directions next to the object code saying where to find the 272 | Corresponding Source. Regardless of what server hosts the 273 | Corresponding Source, you remain obligated to ensure that it is 274 | available for as long as needed to satisfy these requirements. 275 | 276 | e) Convey the object code using peer-to-peer transmission, provided 277 | you inform other peers where the object code and Corresponding 278 | Source of the work are being offered to the general public at no 279 | charge under subsection 6d. 280 | 281 | A separable portion of the object code, whose source code is excluded 282 | from the Corresponding Source as a System Library, need not be 283 | included in conveying the object code work. 284 | 285 | A "User Product" is either (1) a "consumer product", which means any 286 | tangible personal property which is normally used for personal, family, 287 | or household purposes, or (2) anything designed or sold for incorporation 288 | into a dwelling. In determining whether a product is a consumer product, 289 | doubtful cases shall be resolved in favor of coverage. For a particular 290 | product received by a particular user, "normally used" refers to a 291 | typical or common use of that class of product, regardless of the status 292 | of the particular user or of the way in which the particular user 293 | actually uses, or expects or is expected to use, the product. A product 294 | is a consumer product regardless of whether the product has substantial 295 | commercial, industrial or non-consumer uses, unless such uses represent 296 | the only significant mode of use of the product. 297 | 298 | "Installation Information" for a User Product means any methods, 299 | procedures, authorization keys, or other information required to install 300 | and execute modified versions of a covered work in that User Product from 301 | a modified version of its Corresponding Source. The information must 302 | suffice to ensure that the continued functioning of the modified object 303 | code is in no case prevented or interfered with solely because 304 | modification has been made. 305 | 306 | If you convey an object code work under this section in, or with, or 307 | specifically for use in, a User Product, and the conveying occurs as 308 | part of a transaction in which the right of possession and use of the 309 | User Product is transferred to the recipient in perpetuity or for a 310 | fixed term (regardless of how the transaction is characterized), the 311 | Corresponding Source conveyed under this section must be accompanied 312 | by the Installation Information. But this requirement does not apply 313 | if neither you nor any third party retains the ability to install 314 | modified object code on the User Product (for example, the work has 315 | been installed in ROM). 316 | 317 | The requirement to provide Installation Information does not include a 318 | requirement to continue to provide support service, warranty, or updates 319 | for a work that has been modified or installed by the recipient, or for 320 | the User Product in which it has been modified or installed. Access to a 321 | network may be denied when the modification itself materially and 322 | adversely affects the operation of the network or violates the rules and 323 | protocols for communication across the network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders of 351 | that material) supplement the terms of this License with terms: 352 | 353 | a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | 356 | b) Requiring preservation of specified reasonable legal notices or 357 | author attributions in that material or in the Appropriate Legal 358 | Notices displayed by works containing it; or 359 | 360 | c) Prohibiting misrepresentation of the origin of that material, or 361 | requiring that modified versions of such material be marked in 362 | reasonable ways as different from the original version; or 363 | 364 | d) Limiting the use for publicity purposes of names of licensors or 365 | authors of the material; or 366 | 367 | e) Declining to grant rights under trademark law for use of some 368 | trade names, trademarks, or service marks; or 369 | 370 | f) Requiring indemnification of licensors and authors of that 371 | material by anyone who conveys the material (or modified versions of 372 | it) with contractual assumptions of liability to the recipient, for 373 | any liability that these contractual assumptions directly impose on 374 | those licensors and authors. 375 | 376 | All other non-permissive additional terms are considered "further 377 | restrictions" within the meaning of section 10. If the Program as you 378 | received it, or any part of it, contains a notice stating that it is 379 | governed by this License along with a term that is a further 380 | restriction, you may remove that term. If a license document contains 381 | a further restriction but permits relicensing or conveying under this 382 | License, you may add to a covered work material governed by the terms 383 | of that license document, provided that the further restriction does 384 | not survive such relicensing or conveying. 385 | 386 | If you add terms to a covered work in accord with this section, you 387 | must place, in the relevant source files, a statement of the 388 | additional terms that apply to those files, or a notice indicating 389 | where to find the applicable terms. 390 | 391 | Additional terms, permissive or non-permissive, may be stated in the 392 | form of a separately written license, or stated as exceptions; 393 | the above requirements apply either way. 394 | 395 | 8. Termination. 396 | 397 | You may not propagate or modify a covered work except as expressly 398 | provided under this License. Any attempt otherwise to propagate or 399 | modify it is void, and will automatically terminate your rights under 400 | this License (including any patent licenses granted under the third 401 | paragraph of section 11). 402 | 403 | However, if you cease all violation of this License, then your 404 | license from a particular copyright holder is reinstated (a) 405 | provisionally, unless and until the copyright holder explicitly and 406 | finally terminates your license, and (b) permanently, if the copyright 407 | holder fails to notify you of the violation by some reasonable means 408 | prior to 60 days after the cessation. 409 | 410 | Moreover, your license from a particular copyright holder is 411 | reinstated permanently if the copyright holder notifies you of the 412 | violation by some reasonable means, this is the first time you have 413 | received notice of violation of this License (for any work) from that 414 | copyright holder, and you cure the violation prior to 30 days after 415 | your receipt of the notice. 416 | 417 | Termination of your rights under this section does not terminate the 418 | licenses of parties who have received copies or rights from you under 419 | this License. If your rights have been terminated and not permanently 420 | reinstated, you do not qualify to receive new licenses for the same 421 | material under section 10. 422 | 423 | 9. Acceptance Not Required for Having Copies. 424 | 425 | You are not required to accept this License in order to receive or 426 | run a copy of the Program. Ancillary propagation of a covered work 427 | occurring solely as a consequence of using peer-to-peer transmission 428 | to receive a copy likewise does not require acceptance. However, 429 | nothing other than this License grants you permission to propagate or 430 | modify any covered work. These actions infringe copyright if you do 431 | not accept this License. Therefore, by modifying or propagating a 432 | covered work, you indicate your acceptance of this License to do so. 433 | 434 | 10. Automatic Licensing of Downstream Recipients. 435 | 436 | Each time you convey a covered work, the recipient automatically 437 | receives a license from the original licensors, to run, modify and 438 | propagate that work, subject to this License. You are not responsible 439 | for enforcing compliance by third parties with this License. 440 | 441 | An "entity transaction" is a transaction transferring control of an 442 | organization, or substantially all assets of one, or subdividing an 443 | organization, or merging organizations. If propagation of a covered 444 | work results from an entity transaction, each party to that 445 | transaction who receives a copy of the work also receives whatever 446 | licenses to the work the party's predecessor in interest had or could 447 | give under the previous paragraph, plus a right to possession of the 448 | Corresponding Source of the work from the predecessor in interest, if 449 | the predecessor has it or can get it with reasonable efforts. 450 | 451 | You may not impose any further restrictions on the exercise of the 452 | rights granted or affirmed under this License. For example, you may 453 | not impose a license fee, royalty, or other charge for exercise of 454 | rights granted under this License, and you may not initiate litigation 455 | (including a cross-claim or counterclaim in a lawsuit) alleging that 456 | any patent claim is infringed by making, using, selling, offering for 457 | sale, or importing the Program or any portion of it. 458 | 459 | 11. Patents. 460 | 461 | A "contributor" is a copyright holder who authorizes use under this 462 | License of the Program or a work on which the Program is based. The 463 | work thus licensed is called the contributor's "contributor version". 464 | 465 | A contributor's "essential patent claims" are all patent claims 466 | owned or controlled by the contributor, whether already acquired or 467 | hereafter acquired, that would be infringed by some manner, permitted 468 | by this License, of making, using, or selling its contributor version, 469 | but do not include claims that would be infringed only as a 470 | consequence of further modification of the contributor version. For 471 | purposes of this definition, "control" includes the right to grant 472 | patent sublicenses in a manner consistent with the requirements of 473 | this License. 474 | 475 | Each contributor grants you a non-exclusive, worldwide, royalty-free 476 | patent license under the contributor's essential patent claims, to 477 | make, use, sell, offer for sale, import and otherwise run, modify and 478 | propagate the contents of its contributor version. 479 | 480 | In the following three paragraphs, a "patent license" is any express 481 | agreement or commitment, however denominated, not to enforce a patent 482 | (such as an express permission to practice a patent or covenant not to 483 | sue for patent infringement). To "grant" such a patent license to a 484 | party means to make such an agreement or commitment not to enforce a 485 | patent against the party. 486 | 487 | If you convey a covered work, knowingly relying on a patent license, 488 | and the Corresponding Source of the work is not available for anyone 489 | to copy, free of charge and under the terms of this License, through a 490 | publicly available network server or other readily accessible means, 491 | then you must either (1) cause the Corresponding Source to be so 492 | available, or (2) arrange to deprive yourself of the benefit of the 493 | patent license for this particular work, or (3) arrange, in a manner 494 | consistent with the requirements of this License, to extend the patent 495 | license to downstream recipients. "Knowingly relying" means you have 496 | actual knowledge that, but for the patent license, your conveying the 497 | covered work in a country, or your recipient's use of the covered work 498 | in a country, would infringe one or more identifiable patents in that 499 | country that you have reason to believe are valid. 500 | 501 | If, pursuant to or in connection with a single transaction or 502 | arrangement, you convey, or propagate by procuring conveyance of, a 503 | covered work, and grant a patent license to some of the parties 504 | receiving the covered work authorizing them to use, propagate, modify 505 | or convey a specific copy of the covered work, then the patent license 506 | you grant is automatically extended to all recipients of the covered 507 | work and works based on it. 508 | 509 | A patent license is "discriminatory" if it does not include within 510 | the scope of its coverage, prohibits the exercise of, or is 511 | conditioned on the non-exercise of one or more of the rights that are 512 | specifically granted under this License. You may not convey a covered 513 | work if you are a party to an arrangement with a third party that is 514 | in the business of distributing software, under which you make payment 515 | to the third party based on the extent of your activity of conveying 516 | the work, and under which the third party grants, to any of the 517 | parties who would receive the covered work from you, a discriminatory 518 | patent license (a) in connection with copies of the covered work 519 | conveyed by you (or copies made from those copies), or (b) primarily 520 | for and in connection with specific products or compilations that 521 | contain the covered work, unless you entered into that arrangement, 522 | or that patent license was granted, prior to 28 March 2007. 523 | 524 | Nothing in this License shall be construed as excluding or limiting 525 | any implied license or other defenses to infringement that may 526 | otherwise be available to you under applicable patent law. 527 | 528 | 12. No Surrender of Others' Freedom. 529 | 530 | If conditions are imposed on you (whether by court order, agreement or 531 | otherwise) that contradict the conditions of this License, they do not 532 | excuse you from the conditions of this License. If you cannot convey a 533 | covered work so as to satisfy simultaneously your obligations under this 534 | License and any other pertinent obligations, then as a consequence you may 535 | not convey it at all. For example, if you agree to terms that obligate you 536 | to collect a royalty for further conveying from those to whom you convey 537 | the Program, the only way you could satisfy both those terms and this 538 | License would be to refrain entirely from conveying the Program. 539 | 540 | 13. Remote Network Interaction; Use with the GNU General Public License. 541 | 542 | Notwithstanding any other provision of this License, if you modify the 543 | Program, your modified version must prominently offer all users 544 | interacting with it remotely through a computer network (if your version 545 | supports such interaction) an opportunity to receive the Corresponding 546 | Source of your version by providing access to the Corresponding Source 547 | from a network server at no charge, through some standard or customary 548 | means of facilitating copying of software. This Corresponding Source 549 | shall include the Corresponding Source for any work covered by version 3 550 | of the GNU General Public License that is incorporated pursuant to the 551 | following paragraph. 552 | 553 | Notwithstanding any other provision of this License, you have 554 | permission to link or combine any covered work with a work licensed 555 | under version 3 of the GNU General Public License into a single 556 | combined work, and to convey the resulting work. The terms of this 557 | License will continue to apply to the part which is the covered work, 558 | but the work with which it is combined will remain governed by version 559 | 3 of the GNU General Public License. 560 | 561 | 14. Revised Versions of this License. 562 | 563 | The Free Software Foundation may publish revised and/or new versions of 564 | the GNU Affero General Public License from time to time. Such new versions 565 | will be similar in spirit to the present version, but may differ in detail to 566 | address new problems or concerns. 567 | 568 | Each version is given a distinguishing version number. If the 569 | Program specifies that a certain numbered version of the GNU Affero General 570 | Public License "or any later version" applies to it, you have the 571 | option of following the terms and conditions either of that numbered 572 | version or of any later version published by the Free Software 573 | Foundation. If the Program does not specify a version number of the 574 | GNU Affero General Public License, you may choose any version ever published 575 | by the Free Software Foundation. 576 | 577 | If the Program specifies that a proxy can decide which future 578 | versions of the GNU Affero General Public License can be used, that proxy's 579 | public statement of acceptance of a version permanently authorizes you 580 | to choose that version for the Program. 581 | 582 | Later license versions may give you additional or different 583 | permissions. However, no additional obligations are imposed on any 584 | author or copyright holder as a result of your choosing to follow a 585 | later version. 586 | 587 | 15. Disclaimer of Warranty. 588 | 589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 597 | 598 | 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 608 | SUCH DAMAGES. 609 | 610 | 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these terms. 626 | 627 | To do so, attach the following notices to the program. It is safest 628 | to attach them to the start of each source file to most effectively 629 | state the exclusion of warranty; and each file should have at least 630 | the "copyright" line and a pointer to where the full notice is found. 631 | 632 | 633 | Copyright (C) 634 | 635 | This program is free software: you can redistribute it and/or modify 636 | it under the terms of the GNU Affero General Public License as published 637 | by the Free Software Foundation, either version 3 of the License, or 638 | (at your option) any later version. 639 | 640 | This program is distributed in the hope that it will be useful, 641 | but WITHOUT ANY WARRANTY; without even the implied warranty of 642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 643 | GNU Affero General Public License for more details. 644 | 645 | You should have received a copy of the GNU Affero General Public License 646 | along with this program. If not, see . 647 | 648 | Also add information on how to contact you by electronic and paper mail. 649 | 650 | If your software can interact with users remotely through a computer 651 | network, you should also make sure that it provides a way for users to 652 | get its source. For example, if your program is a web application, its 653 | interface could display a "Source" link that leads users to an archive 654 | of the code. There are many ways you could offer source, and different 655 | solutions will be better for different programs; see section 13 for the 656 | specific requirements. 657 | 658 | You should also get your employer (if you work as a programmer) or school, 659 | if any, to sign a "copyright disclaimer" for the program, if necessary. 660 | For more information on this, and how to apply and follow the GNU AGPL, see 661 | . 662 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | help: ## Show this help. 2 | @fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//' 3 | 4 | _prepare_services: var/.env 5 | mkdir -p var/matrix-synapse-media-store var/matrix-synapse-postgres 6 | 7 | var/.env: 8 | mkdir -p var 9 | echo 'CURRENT_USER_UID='`id -u` > var/.env; 10 | echo 'CURRENT_USER_GID='`id -g` >> var/.env 11 | 12 | services-start: _prepare_services ## Starts all services (Postgres, Synapse, Riot) 13 | docker-compose --project-directory var -f etc/services/docker-compose.yaml -p email2matrix up -d 14 | 15 | services-stop: _prepare_services ## Stops all services (Postgres, Synapse, Riot) 16 | docker-compose --project-directory var -f etc/services/docker-compose.yaml -p email2matrix down 17 | 18 | services-tail-logs: _prepare_services ## Tails the logs for all running services 19 | docker-compose --project-directory var -f etc/services/docker-compose.yaml -p email2matrix logs -f 20 | 21 | create-sample-receiver-user: _prepare_services ## Creates a receiver user 22 | docker-compose --project-directory var -f etc/services/docker-compose.yaml -p email2matrix \ 23 | exec synapse \ 24 | register_new_matrix_user \ 25 | -a \ 26 | -u receiver \ 27 | -p password \ 28 | -c /data/homeserver.yaml \ 29 | http://localhost:8008 30 | 31 | create-sample-sender-user: _prepare_services ## Creates a sender user 32 | docker-compose --project-directory var -f etc/services/docker-compose.yaml -p email2matrix \ 33 | exec synapse \ 34 | register_new_matrix_user \ 35 | -a \ 36 | -u sender \ 37 | -p password \ 38 | -c /data/homeserver.yaml \ 39 | http://localhost:8008 40 | 41 | obtain-sample-sender-access-token: _prepare_services ## Obtain an access token for the sender user 42 | docker run \ 43 | -it \ 44 | --rm \ 45 | --network=email2matrix_default \ 46 | alpine:3.10 \ 47 | /bin/sh -c "apk add --no-cache curl && curl --data '{\"identifier\": {\"type\": \"m.id.user\", \"user\": \"sender\" }, \"password\": \"password\", \"type\": \"m.login.password\", \"device_id\": \"Sender\", \"initial_device_display_name\": \"Sender\"}' http://synapse:8008/_matrix/client/r0/login" 48 | 49 | run-locally: build-locally ## Builds and runs email2matrix-server locally (no containers) 50 | ./email2matrix-server 51 | 52 | build-locally: ## Builds the email2matrix-server code locally (no containers) 53 | go get -u -v github.com/ahmetb/govvv 54 | rm -f email2matrix-server 55 | go build -a -ldflags "`~/go/bin/govvv -flags`" email2matrix-server.go 56 | 57 | test: ## Runs the tests locally (no containers) 58 | go test ./... 59 | 60 | build-container-image: ## Builds a Docker container image 61 | docker build -t devture/email2matrix:latest -f etc/docker/Dockerfile . 62 | 63 | run-in-container: build-container-image ## Runs email2matrix in a container 64 | docker run \ 65 | -it \ 66 | --rm \ 67 | --name=email2matrix \ 68 | -p 40025:2525 \ 69 | --mount type=bind,src=`pwd`/config.json,dst=/config.json,ro \ 70 | --network=email2matrix_default \ 71 | devture/email2matrix:latest 72 | 73 | send-sample-email-to-test-mailbox: ## Sends a sample email to email2matrix 74 | docker run \ 75 | -it \ 76 | --rm \ 77 | --network=email2matrix_default \ 78 | alpine:3.10 \ 79 | /bin/sh -c "apk add --no-cache ssmtp && sed -i s/mailhub=mail/mailhub=email2matrix:2525/ /etc/ssmtp/ssmtp.conf && echo -e \"Subject: this is the subject\n\nthis is the body\" | ssmtp test@email2matrix.127.0.0.1.xip.io" 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Email2Matrix: SMTP server relaying messages to Matrix rooms 2 | 3 | --------------------------------------- 4 | 5 | ⚠️ **Warning**: This application is outdated and no longer maintained. We recommend that you use [postmoogle](https://github.com/etkecc/postmoogle) instead. 6 | 7 | --------------------------------------- 8 | 9 | [email2matrix](https://github.com/devture/email2matrix) is an SMTP server (powered by [Go-Guerrilla](https://github.com/flashmob/go-guerrilla)), which receives messages to certain special (predefined) mailboxes and relays them to [Matrix](http://matrix.org/) rooms. 10 | 11 | This is useful when you've got a system which is capable of sending email (notifications, reminders, etc.) and you'd like for that system to actually send a Matrix message instead. 12 | 13 | Instead of redoing such systems (adding support for sending messages over the [Matrix](https://matrix.org) protocol to each one), you can just configure them to send emails to the Email2Matrix server and have those email messages relayed over to Matrix. 14 | 15 | To learn more, refer to the [Documentation](./docs/README.md). 16 | 17 | 18 | ## Support 19 | 20 | Matrix room: [#email2matrix:devture.com](https://matrix.to/#/#email2matrix:devture.com) 21 | 22 | Github issues: [devture/email2matrix/issues](https://github.com/devture/email2matrix/issues) 23 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.1.0 2 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -f ${PWD}/email2matrix-server 4 | 5 | docker run --rm -ti \ 6 | -v ${PWD}:/work \ 7 | -w /work \ 8 | golang:1.12.7-buster \ 9 | sh -c 'go get -u -v github.com/ahmetb/govvv && go build -a -ldflags "$(govvv -flags)" email2matrix-server.go' 10 | -------------------------------------------------------------------------------- /config.json.dist: -------------------------------------------------------------------------------- 1 | { 2 | "Smtp": { 3 | "ListenInterface": "0.0.0.0:2525", 4 | "Hostname": "email2matrix.127.0.0.1.xip.io", 5 | "Workers": 10 6 | }, 7 | "Matrix": { 8 | "Mappings": [ 9 | { 10 | "MailboxName": "test", 11 | "MatrixRoomId": "!ABCD:email2matrix.127.0.0.1.xip.io", 12 | "MatrixHomeserverUrl": "http://synapse:8008", 13 | "MatrixUserId": "@sender:email2matrix.127.0.0.1.xip.io", 14 | "MatrixAccessToken": "TOKEN_GOES_HERE", 15 | "IgnoreSubject": false, 16 | "IgnoreBody": false, 17 | "SkipMarkdown": false 18 | } 19 | ] 20 | }, 21 | "Misc": { 22 | "Debug": true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Email2Matrix documentation 2 | 3 | Email2Matrix forwards emails as [Matrix](http://matrix.org/) messages according to a [configuration policy](configuration.md). 4 | 5 | 6 | ## Table of Contents 7 | 8 | - Learning 9 | 10 | - [Architecture](architecture.md) 11 | 12 | - [FAQ](faq.md) 13 | 14 | - [Configuration](configuration.md) 15 | - [Setup](setup.md) 16 | - [Running on the same host with postfix](setup_with_postfix.md) 17 | - [Development](development.md) 18 | -------------------------------------------------------------------------------- /docs/architecture.md: -------------------------------------------------------------------------------- 1 | # Architecture 2 | 3 | 4 | ``` 5 | Email sending system 6 | | 7 | | 8 | | +---------------------+ +----------------------+ 9 | | | | | | 10 | | SMTP 25 | Email2Matrix | HTTPS (client API) | Matrix Homeserver | 11 | +--------> | | ------------------> | (e.g. Synapse) | 12 | | | | | 13 | +---------------------+ +----------------------+ 14 | ``` 15 | 16 | Things to note: 17 | 18 | - `email2matrix` receives email messages sent from another system 19 | 20 | - the mailbox that a message gets delivered to (e.g. `mailbox5@email2matrix.example.com`) designates where the message will be forwarded to on the Matrix side (such mappings are defined in `config.json` manually) 21 | 22 | - `email2matrix` then uses the [Matrix Client-Server API](https://matrix.org/docs/spec/client_server/r0.5.0) with a pre-created user and access token in order to send a Matrix message to a specific room (as defined in `config.json`) 23 | -------------------------------------------------------------------------------- /docs/configuration.md: -------------------------------------------------------------------------------- 1 | # Email2Matrix Configuration 2 | 3 | The `email2matrix` server configuration is a JSON document that looks like this: 4 | 5 | ```json 6 | { 7 | "Smtp": { 8 | "ListenInterface": "0.0.0.0:25", 9 | "Hostname": "email2matrix.example.com", 10 | "Workers": 10 11 | }, 12 | "Matrix": { 13 | "Mappings": [ 14 | { 15 | "MailboxName": "test", 16 | "MatrixRoomId": "!ABCD:example.com", 17 | "MatrixHomeserverUrl": "https://matrix.example.com", 18 | "MatrixUserId": "@svc.test.sender:example.com", 19 | "MatrixAccessToken": "TOKEN_GOES_HERE", 20 | "IgnoreSubject": false, 21 | "IgnoreBody": false, 22 | "SkipMarkdown": false 23 | } 24 | ] 25 | }, 26 | "Misc": { 27 | "Debug": true 28 | } 29 | } 30 | 31 | ``` 32 | 33 | ## Fields 34 | 35 | The configuration contains the following fields: 36 | 37 | - `Smtp` - SMTP server related configuration 38 | 39 | - `ListenInterface` - the network address and port to listen on. If you're running this inside a container, use something like `0.0.0.0:25` (or `0.0.0.0:2525` with `docker run -p 25:2525 ...`). 40 | 41 | - `Hostname` - the hostname of this email server 42 | 43 | - `Workers` - the number of workers to run. Controls how many messages can be received simultaneously. 44 | 45 | 46 | - `Matrix` - Matrix-related configuration 47 | 48 | - `Mappings` - a list of mappings specifying messages going to which mailbox should get sent to which Matrix room, using what kind of credentials, etc. 49 | 50 | - `MailboxName` - the mailbox name (e.g. `mailbox5`). Its full email address would be `MailboxName@Hostname` (`Hostname` is the `Smtp.Hostname` configuration value). All emails received to this mailbox will be forwarded to Matrix. Use a long and ungueassable name for the mailbox to prevent needless spam. 51 | 52 | - `MatrixRoomId` - the Matrix room id where messages should be sent 53 | 54 | - `MatrixHomeserverUrl` - the homeserver through which to send Matrix messages 55 | 56 | - `MatrixUserId` - the full user id through which the Matrix messages would be sent 57 | 58 | - `MatrixAccessToken` - a Matrix access token corresponding to the sender Matrix user (`MatrixUserId`) 59 | 60 | - `IgnoreSubject` - (`true` or `false`) specifies whether the subject should be forwarded to Matrix 61 | 62 | - `IgnoreBody` - (`true` or `false`) specifies whether the message body should be forwarded to Matrix 63 | 64 | - `SkipMarkdown` - (`true` or `false`) specifies whether to send a plain text message or a Markdown (actually HTML) message to Matrix 65 | 66 | - `Misc` - miscellaneous configuration 67 | 68 | - `Debug` - whether to enable debug mode or not (enable for more verbose logs) 69 | -------------------------------------------------------------------------------- /docs/development.md: -------------------------------------------------------------------------------- 1 | # Development / Experimenting 2 | 3 | If you'd like to contribute code to this project or give it a try locally (before deploying it), you need to: 4 | 5 | - clone this repository 6 | 7 | - get [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/) -- used for running a local Matrix Synapse + riot-web setup, for testing 8 | 9 | - start all dependency services (Postgres, Matrix Synapse, riot-web): `make services-start`. You can stop them later with `make services-stop` or tail their logs with `make services-tail-logs` 10 | 11 | - create a sample "sender" user: `make create-sample-sender-user` 12 | 13 | - create a sample "receiver" user: `make create-sample-receiver-user` 14 | 15 | - you should now be able to log in with user `receiver` and password `password` to the [riot-web instance](http://email2matrix.127.0.0.1.xip.io:41465) 16 | 17 | - using riot-web, from that receiver (`receiver`) user: create a room or two with the `sender` user (full Matrix user id is: `@sender:email2matrix.127.0.0.1.xip.io`) 18 | 19 | - in another browser session (new container tab, private tab, another browser, etc.), log in to [riot-web](http://email2matrix.127.0.0.1.xip.io:41465) with user `sender` and password `password` and accept those room invitations 20 | 21 | - copy the sample configuration: `cp config.json.dist config.json` 22 | 23 | - obtain an access token for the `sender` user using `make obtain-sample-sender-access-token`. You will need the value of the `access_token` field below 24 | 25 | - create a new mapping in `config.json` (see the [Configuration documentation](configuration.md)) 26 | 27 | Example: 28 | ```json 29 | { 30 | "Mappings": [ 31 | { 32 | "MailboxName": "test", 33 | "MatrixRoomId": "!ROOM_ID_HERE:email2matrix.127.0.0.1.xip.io", 34 | "MatrixHomeserverUrl": "http://synapse:8008", 35 | "MatrixUserId": "@sender:email2matrix.127.0.0.1.xip.io", 36 | "MatrixAccessToken": "SENDER_ACCESS_TOKEN_HERE", 37 | "IgnoreSubject": false, 38 | "IgnoreBody": false, 39 | "SkipMarkdown": false 40 | } 41 | ] 42 | } 43 | ``` 44 | 45 | - build and run the `email2matrix` program by executing: `make run-in-container` 46 | 47 | - send a test email by executing: `make send-sample-email-to-test-mailbox` 48 | 49 | - you should now see that email message relayed to the Matrix room created above 50 | 51 | 52 | For local development, it's best to install a [Go](https://golang.org/) compiler (version 1.12 or later is required) locally. 53 | Some tests are available and can be executed with: `make test`. 54 | -------------------------------------------------------------------------------- /docs/faq.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | ## What is Email2Matrix? 4 | 5 | Email2Matrix is an SMTP email server program, which receives messages to special predefined mailboxes (in your `config.json` [configuration file](./configuration.md)) and forwards those messages to rooms over the [Matrix](https://matrix.org) protocol. 6 | 7 | 8 | ## When do I need Email2Matrix? 9 | 10 | You need Email2Matrix when you've got a system which is capable of sending email (notifications, reminders, etc.) and you'd like for that system to actually send a Matrix message instead. 11 | 12 | Instead of redoing such systems (adding support for sending messages over the [Matrix](https://matrix.org) protocol to each one), you can just configure them to send emails to the Email2Matrix server and have those email messages relayed over to Matrix. 13 | 14 | 15 | ## Do I need to use a specific homeserver for this to work? 16 | 17 | No. You can send via any homeserver to any room, even across federation. 18 | 19 | 20 | ## Why do I need to create rooms, access tokens, etc., manually? 21 | 22 | This is built in the simplest way possible. 23 | You need to do more manual work, so that Email2Matrix doesn't and can be kept simple. 24 | 25 | A more easy to use version can be created. 26 | One which acts as a bot, auto-accepts room invitations, manages mailbox mappings in a database, etc., but that hasn't been done yet. 27 | 28 | 29 | ## Do I need special DNS configuration to use this? 30 | 31 | Not necessarily. 32 | 33 | You can use `MX` DNS records if you wish, but you can avoid it as well. 34 | 35 | If you're hosting Email2Matrix on another server which already has a DNS mapping for its hostname, you can [Configure](configuration.md) your `Smtp.Hostname` to match the hostname of the machine (example: `matrix.example.com`). 36 | 37 | On the other hand, if you'd like to have more indirection, feel free to use `MX` DNS records. 38 | 39 | ## Can I run email2matrix on the same host with postfix? 40 | 41 | Yes. Here is the [documentation describing that](setup_with_postfix.md). 42 | -------------------------------------------------------------------------------- /docs/setup.md: -------------------------------------------------------------------------------- 1 | # Email2Matrix Setup 2 | 3 | The easiest way to run Email2Matrix is using a [Docker](https://www.docker.com/) (or other) container. 4 | 5 | If your Matrix server is installed using [matrix-docker-ansible-deploy](https://github.com/spantaleev/matrix-docker-ansible-deploy), it's easiest if you refer to [the playbook's setup instructions for Email2Matrix](https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/docs/configuring-playbook-email2matrix.md) instead of following these. 6 | 7 | 8 | ## Prerequisites 9 | 10 | - [Docker](https://www.docker.com/) or other (e.g. [Podman](https://podman.io/)) container runtime engine 11 | 12 | - **port 25 available** on the host machine where you'd like to run this. If not available, you may also be able to make your existing email server on port 25 relay messages to Email2Matrix running on another port (e.g. 2525), but that's a more complicated setup. A simplified documentation can be found [here](setup_with_postfix.md) 13 | 14 | - Matrix server configuration: 15 | 16 | - you would most likely wish to create one or more dedicated users that would be doing the sending (e.g. `@email2matrix:DOMAIN`) 17 | 18 | - for each of those sender users, you would need to obtain a Matrix Access Token manually. This can happen with a command like this: 19 | 20 | ``` 21 | curl \ 22 | --data '{"identifier": {"type": "m.id.user", "user": "email2matrix" }, "password": "MATRIX_PASSWORD_FOR_THE_USER", "type": "m.login.password", "device_id": "Email2Matrix", "initial_device_display_name": "Email2Matrix"}' \ 23 | https://matrix.DOMAIN/_matrix/client/r0/login 24 | ``` 25 | 26 | - A configuration file (`config.json`) created as shown in the [Configuration](configuration.md) documentation page 27 | 28 | - `Smtp.Hostname` in your configuration file should either match the hostname leading to your server (using a regular `A` record), or it should be an MX record that eventually leads to your server. In any case, what you see in `Smtp.Hostname` is the domain that needs to be in the `to` field of the emails you would be sending 29 | 30 | - some mappings need to be defined (mailbox names and where those mailboxes lead on the Matrix side) 31 | 32 | 33 | ## Running 34 | 35 | ``` 36 | docker run \ 37 | -it \ 38 | --rm \ 39 | --name=email2matrix \ 40 | -p 25:2525 \ 41 | --mount type=bind,src=/path/to/your/config.json,dst=/config.json,ro \ 42 | --network=email2matrix_default \ 43 | devture/email2matrix:latest 44 | ``` 45 | 46 | It's better to use a specific Docker image tag (not `:latest`). 47 | -------------------------------------------------------------------------------- /docs/setup_with_postfix.md: -------------------------------------------------------------------------------- 1 | # Running email2matrix on the same host as postfix 2 | 3 | It's possible to run email2matrix on the same host with postfix. email2matrix can run on a loopback interface and postfix will forward the messges to email2matrix. 4 | This is a simple example what needs to be configured on email2matrix and postfix side to work together on one host. 5 | 6 | ## Architecture 7 | 8 | ``` 9 | Email sending system 10 | | 11 | | 12 | | +--------------------+ 13 | | SMTP 25 | | 14 | +--------> | Postfix | 15 | | | | 16 | | .......|.......... | +----------------------+ 17 | | | | | | 18 | | V | HTTPS (client API) | Matrix Homeserver | 19 | | email2matrix | ------------------> | (e.g. Synapse) | 20 | | | | | 21 | +--------------------+ +----------------------+ 22 | ``` 23 | 24 | ## Configuration 25 | 26 | ## email2maitrix 27 | 28 | Make sure you run email2matrix on a unused port. For example on port 2525. You can even run it on a lookback interface. 29 | 30 | Example config (partly): 31 | ``` 32 | { 33 | "Smtp": { 34 | "ListenInterface": "127.0.0.1:2525", 35 | "Hostname": "email2matrix.example.com", 36 | "Workers": 10 37 | }, 38 | "Matrix": ... 39 | ``` 40 | 41 | ## Postfix 42 | 43 | * Postfix needs to know that he is responsible for the domain 44 | * main.cf: `mydestination = email2matrix.example.com, localhost` 45 | * Set the transport maps on the destination domain 46 | * main.cf: `transport_maps = hash:/etc/postfix/transport` 47 | * transport `email2matrix.example.com smtp:127.0.0.1:2525` 48 | * create lookup db from plain file `postmap transport` (this creates transport.db) 49 | * Restart postfix 50 | * Look at `/var/log/mail.log` for errors 51 | -------------------------------------------------------------------------------- /email2matrix-server.go: -------------------------------------------------------------------------------- 1 | // email2matrix is an Email (SMTP) server relaying received messages to a Matrix room. 2 | // Copyright (C) 2019 Slavi Pantaleev 3 | // 4 | // https://devture.com/ 5 | // 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU Affero General Public License as published 8 | // by the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU Affero General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU Affero General Public License 17 | // along with this program. If not, see . 18 | 19 | package main 20 | 21 | import ( 22 | "devture-email2matrix/email2matrix/configuration" 23 | "devture-email2matrix/email2matrix/container" 24 | "flag" 25 | "os" 26 | "os/signal" 27 | "syscall" 28 | "fmt" 29 | 30 | guerrilla "github.com/flashmob/go-guerrilla" 31 | log "github.com/sirupsen/logrus" 32 | ) 33 | 34 | // Following variables will be statically linked at the time of compiling 35 | // Source: https://oddcode.daveamit.com/2018/08/17/embed-versioning-information-in-golang-binary/ 36 | 37 | // GitCommit holds short commit hash of source tree 38 | var GitCommit string 39 | 40 | // GitBranch holds current branch name the code is built off 41 | var GitBranch string 42 | 43 | // GitState shows whether there are uncommitted changes 44 | var GitState string 45 | 46 | // GitSummary holds output of git describe --tags --dirty --always 47 | var GitSummary string 48 | 49 | // BuildDate holds RFC3339 formatted UTC date (build time) 50 | var BuildDate string 51 | 52 | // Version holds contents of ./VERSION file, if exists, or the value passed via the -version option 53 | var Version string 54 | 55 | func main() { 56 | fmt.Printf(` 57 | _ _ ___ _ _ 58 | (_) |__ \ | | (_) 59 | ___ _ __ ___ __ _ _| | ) |_ __ ___ __ _| |_ _ __ ___ __ 60 | / _ \ '_ ' _ \ / _' | | | / /| '_ ' _ \ / _' | __| '__| \ \/ / 61 | | __/ | | | | | (_| | | |/ /_| | | | | | (_| | |_| | | |> < 62 | \___|_| |_| |_|\__,_|_|_|____|_| |_| |_|\__,_|\__|_| |_/_/\_\ 63 | ----------------------------------------------[ Version: %s ] 64 | GitCommit: %s 65 | GitBranch: %s 66 | GitState: %s 67 | GitSummary: %s 68 | BuildDate: %s 69 | 70 | `, Version, GitCommit, GitBranch, GitState, GitSummary, BuildDate) 71 | configPath := flag.String("config", "config.json", "configuration file to use") 72 | flag.Parse() 73 | 74 | configuration, err := configuration.LoadConfiguration(*configPath) 75 | if err != nil { 76 | panic(err) 77 | } 78 | 79 | container, shutdownHandler := container.BuildContainer(*configuration) 80 | 81 | d := container.Get("smtp.server").(guerrilla.Daemon) 82 | err = d.Start() 83 | if err != nil { 84 | log.Panic("start error", err) 85 | } 86 | 87 | channelComplete := make(chan bool) 88 | setupSignalHandling( 89 | channelComplete, 90 | shutdownHandler, 91 | ) 92 | 93 | <-channelComplete 94 | } 95 | 96 | func setupSignalHandling( 97 | channelComplete chan bool, 98 | shutdownHandler *container.ContainerShutdownHandler, 99 | ) { 100 | signalChannel := make(chan os.Signal, 2) 101 | signal.Notify(signalChannel, os.Interrupt, syscall.SIGTERM) 102 | go func() { 103 | <-signalChannel 104 | 105 | shutdownHandler.Shutdown() 106 | 107 | channelComplete <- true 108 | }() 109 | } 110 | -------------------------------------------------------------------------------- /email2matrix/configuration/configuration.go: -------------------------------------------------------------------------------- 1 | package configuration 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | type Configuration struct { 10 | Smtp ConfigurationSmtp 11 | Misc ConfigurationMisc 12 | Matrix ConfigurationMatrix 13 | } 14 | 15 | type ConfigurationSmtp struct { 16 | ListenInterface string 17 | Hostname string 18 | Workers int 19 | } 20 | 21 | type ConfigurationMatrix struct { 22 | Mappings []ConfigurationMatrixMapping 23 | } 24 | 25 | type ConfigurationMatrixMapping struct { 26 | MailboxName string 27 | MatrixRoomId string 28 | MatrixHomeserverUrl string 29 | MatrixUserId string 30 | MatrixAccessToken string 31 | IgnoreSubject bool 32 | IgnoreBody bool 33 | SkipMarkdown bool 34 | } 35 | 36 | type ConfigurationMisc struct { 37 | Debug bool 38 | } 39 | 40 | func LoadConfiguration(filePath string) (*Configuration, error) { 41 | file, err := os.Open(filePath) 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | decoder := json.NewDecoder(file) 47 | configuration := Configuration{} 48 | err = decoder.Decode(&configuration) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | if len(configuration.Matrix.Mappings) == 0 { 54 | return nil, fmt.Errorf("There should be at least one entry in Matrix.Mappings") 55 | } 56 | 57 | return &configuration, nil 58 | } 59 | -------------------------------------------------------------------------------- /email2matrix/container/container.go: -------------------------------------------------------------------------------- 1 | package container 2 | 3 | import ( 4 | "devture-email2matrix/email2matrix/configuration" 5 | "devture-email2matrix/email2matrix/resolver" 6 | "devture-email2matrix/email2matrix/smtp" 7 | "fmt" 8 | 9 | "github.com/euskadi31/go-service" 10 | guerrilla "github.com/flashmob/go-guerrilla" 11 | "github.com/flashmob/go-guerrilla/backends" 12 | "github.com/sirupsen/logrus" 13 | ) 14 | 15 | type ContainerShutdownHandler struct { 16 | destructors []func() 17 | } 18 | 19 | func (me *ContainerShutdownHandler) Add(destructor func()) { 20 | me.destructors = append(me.destructors, destructor) 21 | } 22 | 23 | func (me *ContainerShutdownHandler) Shutdown() { 24 | for i, _ := range me.destructors { 25 | me.destructors[len(me.destructors)-i-1]() 26 | } 27 | } 28 | 29 | func BuildContainer( 30 | configuration configuration.Configuration, 31 | ) (service.Container, *ContainerShutdownHandler) { 32 | container := service.New() 33 | shutdownHandler := &ContainerShutdownHandler{} 34 | 35 | // The logger is very crucial, so we're defining it outside 36 | logger := logrus.New() 37 | if configuration.Misc.Debug { 38 | logger.Level = logrus.DebugLevel 39 | } 40 | 41 | container.Set("logger", func(c service.Container) interface{} { 42 | return logger 43 | }) 44 | 45 | container.Set("resolver", func(c service.Container) interface{} { 46 | return resolver.NewConfigurationBackedMailboxMappingInfoProvider(configuration.Matrix.Mappings) 47 | }) 48 | 49 | container.Set("smtp.processor.email2matrix", func(c service.Container) interface{} { 50 | return smtp.Email2MatrixProcessor(logger, c.Get("resolver").(resolver.MailboxMappingInfoProvider)) 51 | }) 52 | 53 | container.Set("smtp.server", func(c service.Container) interface{} { 54 | cfg := &guerrilla.AppConfig{ 55 | AllowedHosts: []string{configuration.Smtp.Hostname}, 56 | } 57 | 58 | if configuration.Misc.Debug { 59 | cfg.LogLevel = "debug" 60 | } else { 61 | cfg.LogLevel = "info" 62 | } 63 | 64 | sc := guerrilla.ServerConfig{ 65 | ListenInterface: configuration.Smtp.ListenInterface, 66 | IsEnabled: true, 67 | } 68 | cfg.Servers = append(cfg.Servers, sc) 69 | 70 | additionalSaveProcess := "" 71 | if configuration.Misc.Debug { 72 | additionalSaveProcess = "|Debugger" 73 | } 74 | bcfg := backends.BackendConfig{ 75 | "save_workers_size": configuration.Smtp.Workers, 76 | "save_process": fmt.Sprintf("HeadersParser|Header|Hasher%s|Email2Matrix", additionalSaveProcess), 77 | "log_received_mails": true, 78 | } 79 | cfg.BackendConfig = bcfg 80 | 81 | d := guerrilla.Daemon{Config: cfg} 82 | 83 | d.AddProcessor("Email2Matrix", c.Get("smtp.processor.email2matrix").(backends.ProcessorConstructor)) 84 | 85 | shutdownHandler.Add(func() { 86 | logger.Debug("Shutdown SMTP server") 87 | d.Shutdown() 88 | }) 89 | 90 | return d 91 | }) 92 | 93 | return container, shutdownHandler 94 | } 95 | -------------------------------------------------------------------------------- /email2matrix/matrix/email_relayer.go: -------------------------------------------------------------------------------- 1 | package matrix 2 | 3 | import ( 4 | "devture-email2matrix/email2matrix/mime" 5 | "devture-email2matrix/email2matrix/resolver" 6 | "errors" 7 | "fmt" 8 | "strings" 9 | 10 | "github.com/matrix-org/gomatrix" 11 | blackfriday "gopkg.in/russross/blackfriday.v2" 12 | 13 | "github.com/flashmob/go-guerrilla/mail" 14 | ) 15 | 16 | func Relay(envelope *mail.Envelope, mappingInfo resolver.MailboxMappingInfo) (string, error) { 17 | subject, body, err := mime.ExtractContentFromEmail(envelope.NewReader()) 18 | if err != nil { 19 | return "", fmt.Errorf("Failed to parse email: %s", err) 20 | } 21 | 22 | messagePlainOrMarkdown := GenerateMessage( 23 | subject, 24 | body, 25 | mappingInfo.IgnoreSubject, 26 | mappingInfo.IgnoreBody, 27 | mappingInfo.SkipMarkdown, 28 | ) 29 | 30 | if messagePlainOrMarkdown == "" { 31 | return "", fmt.Errorf("Refusing to send an empty message") 32 | } 33 | 34 | client, err := gomatrix.NewClient(mappingInfo.MatrixHomeserverUrl, mappingInfo.MatrixUserId, mappingInfo.MatrixAccessToken) 35 | if err != nil { 36 | return "", err 37 | } 38 | 39 | var event *gomatrix.RespSendEvent 40 | if mappingInfo.SkipMarkdown { 41 | event, err = client.SendText(mappingInfo.MatrixRoomId, messagePlainOrMarkdown) 42 | } else { 43 | messageAsHtml := string(blackfriday.Run([]byte(messagePlainOrMarkdown))) 44 | 45 | event, err = client.SendMessageEvent(mappingInfo.MatrixRoomId, "m.room.message", gomatrix.HTMLMessage{ 46 | Body: messagePlainOrMarkdown, 47 | MsgType: "m.text", 48 | Format: "org.matrix.custom.html", 49 | FormattedBody: messageAsHtml, 50 | }) 51 | } 52 | 53 | if err != nil { 54 | errMessage := strings.Replace(err.Error(), mappingInfo.MatrixAccessToken, "REDACTED", -1) 55 | return "", errors.New(errMessage) 56 | } 57 | 58 | return event.EventID, nil 59 | } 60 | -------------------------------------------------------------------------------- /email2matrix/matrix/message_generator.go: -------------------------------------------------------------------------------- 1 | package matrix 2 | 3 | import "fmt" 4 | 5 | func GenerateMessage( 6 | subject string, 7 | body string, 8 | ignoreSubject, ignoreBody, skipMarkdown bool, 9 | ) string { 10 | if skipMarkdown { 11 | if ignoreBody || body == "" { 12 | if subject == "" { 13 | return "" 14 | } 15 | return fmt.Sprintf("%s", subject) 16 | } 17 | 18 | if ignoreSubject || subject == "" { 19 | return body 20 | } 21 | 22 | return fmt.Sprintf("%s\n\n%s", subject, body) 23 | } 24 | 25 | if ignoreBody || body == "" { 26 | if subject == "" { 27 | return "" 28 | } 29 | return fmt.Sprintf("# %s", subject) 30 | } 31 | 32 | if ignoreSubject || subject == "" { 33 | return body 34 | } 35 | 36 | return fmt.Sprintf("# %s\n\n%s", subject, body) 37 | } 38 | -------------------------------------------------------------------------------- /email2matrix/matrix/message_generator_test.go: -------------------------------------------------------------------------------- 1 | package matrix 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | type testData struct { 9 | subject string 10 | body string 11 | 12 | ignoreSubject bool 13 | ignoreBody bool 14 | skipMarkdown bool 15 | 16 | expectedOutput string 17 | } 18 | 19 | func TestGenerate(t *testing.T) { 20 | tests := []testData{ 21 | testData{ 22 | subject: "test", 23 | body: "content", 24 | ignoreSubject: false, 25 | ignoreBody: false, 26 | skipMarkdown: false, 27 | expectedOutput: "# test\n\ncontent", 28 | }, 29 | testData{ 30 | subject: "test", 31 | body: "", 32 | ignoreSubject: false, 33 | ignoreBody: false, 34 | skipMarkdown: false, 35 | expectedOutput: "# test", 36 | }, 37 | testData{ 38 | subject: "test", 39 | body: "content", 40 | ignoreSubject: false, 41 | ignoreBody: true, 42 | skipMarkdown: false, 43 | expectedOutput: "# test", 44 | }, 45 | testData{ 46 | subject: "test", 47 | body: "content", 48 | ignoreSubject: false, 49 | ignoreBody: true, 50 | skipMarkdown: true, 51 | expectedOutput: "test", 52 | }, 53 | testData{ 54 | subject: "test", 55 | body: "content", 56 | ignoreSubject: true, 57 | ignoreBody: false, 58 | skipMarkdown: false, 59 | expectedOutput: "content", 60 | }, 61 | testData{ 62 | subject: "test", 63 | body: "content", 64 | ignoreSubject: true, 65 | ignoreBody: false, 66 | skipMarkdown: true, 67 | expectedOutput: "content", 68 | }, 69 | testData{ 70 | subject: "test", 71 | body: "content", 72 | ignoreSubject: false, 73 | ignoreBody: false, 74 | skipMarkdown: true, 75 | expectedOutput: "test\n\ncontent", 76 | }, 77 | } 78 | 79 | for idx, test := range tests { 80 | test := test // capture range variable 81 | 82 | t.Run(fmt.Sprintf("test-%d", idx), func(t *testing.T) { 83 | t.Parallel() 84 | 85 | output := GenerateMessage(test.subject, test.body, test.ignoreSubject, test.ignoreBody, test.skipMarkdown) 86 | 87 | if output != test.expectedOutput { 88 | t.Errorf("Expected output `%s`, but got `%s`", test.expectedOutput, output) 89 | return 90 | } 91 | }) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /email2matrix/mime/parser.go: -------------------------------------------------------------------------------- 1 | package mime 2 | 3 | import ( 4 | "io" 5 | "strings" 6 | 7 | "github.com/jhillyerd/enmime" 8 | ) 9 | 10 | func ExtractContentFromEmail(reader io.Reader) ( /*subject*/ string /*body*/, string, error) { 11 | envelope, err := enmime.ReadEnvelope(reader) 12 | if err != nil { 13 | return "", "", err 14 | } 15 | 16 | subject := envelope.GetHeader("Subject") 17 | body := strings.TrimSpace(envelope.Text) 18 | return subject, body, nil 19 | } 20 | -------------------------------------------------------------------------------- /email2matrix/mime/parser_test.go: -------------------------------------------------------------------------------- 1 | package mime 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | type TestData struct { 9 | filePath string 10 | expectedSubject string 11 | expectedContent string 12 | } 13 | 14 | func TestParser(t *testing.T) { 15 | tests := []TestData{ 16 | TestData{ 17 | filePath: "./testdata/phpmailer-quoted-printable.txt", 18 | expectedSubject: "Reminder from example.com", 19 | expectedContent: "Тест", 20 | }, 21 | TestData{ 22 | filePath: "./testdata/geary-plain.txt", 23 | expectedSubject: "some test", 24 | expectedContent: "test", 25 | }, 26 | TestData{ 27 | filePath: "./testdata/gmail-base64.txt", 28 | expectedSubject: "тест от gmail", 29 | expectedContent: "тест от gmail", 30 | }, 31 | } 32 | 33 | for _, test := range tests { 34 | test := test // capture range variable 35 | 36 | t.Run(test.filePath, func(t *testing.T) { 37 | t.Parallel() 38 | 39 | f, err := os.Open(test.filePath) 40 | if err != nil { 41 | t.Errorf("Failed to open file: %s: %s", test.filePath, err) 42 | return 43 | } 44 | defer f.Close() 45 | 46 | subject, content, err := ExtractContentFromEmail(f) 47 | if err != nil { 48 | t.Error(err) 49 | return 50 | } 51 | 52 | if subject != test.expectedSubject { 53 | t.Errorf("Expected subject `%s`, but got `%s`", test.expectedSubject, subject) 54 | return 55 | } 56 | 57 | if content != test.expectedContent { 58 | t.Errorf("Expected content `%s`, but got `%s`", test.expectedContent, content) 59 | return 60 | } 61 | }) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /email2matrix/mime/testdata/geary-plain.txt: -------------------------------------------------------------------------------- 1 | Date: Mon, 01 Jan 2018 15:20:14 +0200 2 | From: Slavi Pantaleev 3 | Subject: some test 4 | To: something@another 5 | Message-Id: <1514812814.31047.2@whatever> 6 | X-Mailer: geary/0.12-dev 7 | MIME-Version: 1.0 8 | Content-Type: multipart/alternative; boundary="=-euby2brpLBXRGzTY2+7t" 9 | 10 | --=-euby2brpLBXRGzTY2+7t 11 | Content-Type: text/plain; charset=us-ascii; format=flowed 12 | 13 | test 14 | 15 | --=-euby2brpLBXRGzTY2+7t 16 | Content-Type: text/html; charset=us-ascii 17 | 18 |
test
19 | --=-euby2brpLBXRGzTY2+7t-- 20 | -------------------------------------------------------------------------------- /email2matrix/mime/testdata/gmail-base64.txt: -------------------------------------------------------------------------------- 1 | Received: by mail-ua0-f182.google.com with SMTP id a25so27075635uak.3 2 | for ; Tue, 02 Jan 2018 02:31:31 -0800 (PST) 3 | DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; 4 | d=gmail.com; s=20161025; 5 | h=mime-version:from:date:message-id:subject:to; 6 | bh=HdjriWLjqV+NydQqVN3sfYBjHdmOr6jUVApAjJui/l0=; 7 | b=lJPrOS23KxW/2jK0IbA6lXtaWV3mDYDl7qvsE1CBFoVhtqXyPtGdEH38bKyY2ycSab 8 | +6ApFY8dij8/0XORPZrfWKfLz3ylVorEUw047NKPKf1UmyLoc4vCj2HzcV/orINtW6IJ 9 | hnv9cw55Wp9sQHEPVy1hvBT42baI3c4PU2JJUv6wkXK6XgoskVfV5EFZwGlPYOifuU46 10 | of1FDqO4OfV19qxstS4pNtEAJ/dBCBzbHbq3XUo820yf0n+LODy59tGknxDeQ3PuiVSX 11 | 5Cf6bWeZj8nSPz9AiDemRcqheR7q22Y+RpBqY1/a2vWFoJNWDWnAKIoEKP2zuel/UIQA 12 | 0T9A== 13 | X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; 14 | d=1e100.net; s=20161025; 15 | h=x-gm-message-state:mime-version:from:date:message-id:subject:to; 16 | bh=HdjriWLjqV+NydQqVN3sfYBjHdmOr6jUVApAjJui/l0=; 17 | b=C8r4xqQ8CvQcNHkoag7Kw2TAnnbdhaUIKmkXo6x0B8fdZ3bU0Hzwrx4sbYCL89ekdi 18 | v/k+R9l6GGJQuKdnv31ZDi+4OuQFWxtK3A10hNIcv88slSfsL6mBzgbUISM8KmWDSH7w 19 | R251a01e3vbe+FBVGwmlqjAcA+rd3NAPTOxHnsYgoXhbtO5hLlQ7znzSpqqnrWErYLSL 20 | QeSCVa1ZMs146c7Nh82Dh3bArpJ1JKO0r2pxvEJH+4N+/9poFqq89kP9/SQeAv9HlzAN 21 | UTdqptHZm6ZhzcLqrNjwR8KOrfdoJCnO1bN1v5rBD4RbfgGdW1FCz+3Ci2ZOCHM4G0fh 22 | ICBw== 23 | X-Gm-Message-State: AKGB3mJErtbNN2iOzJG3clIj5rfKAF09PWgqeLpeROzmUsXiMmw8YFo9 24 | xq8O4YFJOAfEyV5N6rhQFcKdjcDlW1Bw6en6wH3weA== 25 | X-Google-Smtp-Source: ACJfBou2gAw7kHabK6TfRiSUqvVyphcL9EEP4Wib+GGKnpApmUfsxO6eJTaKVwcWdXt3OLFiqBV91xNT9XOtSfJFb3o= 26 | X-Received: by 10.176.22.139 with SMTP id e11mr1487238uaf.44.1514889091008; 27 | Tue, 02 Jan 2018 02:31:31 -0800 (PST) 28 | MIME-Version: 1.0 29 | Received: by 10.176.29.23 with HTTP; Tue, 2 Jan 2018 02:31:30 -0800 (PST) 30 | From: Someone 31 | Date: Tue, 2 Jan 2018 12:31:30 +0200 32 | Message-ID: 33 | Subject: =?UTF-8?B?0YLQtdGB0YIg0L7RgiBnbWFpbA==?= 34 | To: something@whatever 35 | Content-Type: multipart/alternative; boundary="f403045f85ee24cc720561c89a52" 36 | 37 | --f403045f85ee24cc720561c89a52 38 | Content-Type: text/plain; charset="UTF-8" 39 | Content-Transfer-Encoding: base64 40 | 41 | 0YLQtdGB0YIg0L7RgiBnbWFpbA0K 42 | --f403045f85ee24cc720561c89a52 43 | Content-Type: text/html; charset="UTF-8" 44 | Content-Transfer-Encoding: base64 45 | 46 | PGRpdiBkaXI9Imx0ciI+0YLQtdGB0YIg0L7RgiBnbWFpbDxicj48L2Rpdj4NCg== 47 | --f403045f85ee24cc720561c89a52-- 48 | -------------------------------------------------------------------------------- /email2matrix/mime/testdata/phpmailer-quoted-printable.txt: -------------------------------------------------------------------------------- 1 | Received: from localhost.localdomain (localhost [IPv6:::1]) 2 | by example.com (Postfix) with ESMTP id 393A2600D1 3 | for ; Mon, 1 Jan 2018 17:40:02 +0000 (UTC) 4 | Date: Mon, 1 Jan 2018 17:40:02 +0000 5 | To: slavi 6 | From: Reminders sender 7 | Subject: Reminder from example.com 8 | Message-ID: <7f90123dd371040cafb37be91086b558@localhost.localdomain> 9 | X-Priority: 3 10 | X-Mailer: PHPMailer 5.1 (phpmailer.sourceforge.net) 11 | MIME-Version: 1.0 12 | Content-Type: text/plain; charset="utf-8" 13 | Content-Transfer-Encoding: quoted-printable 14 | 15 | =D0=A2=D0=B5=D1=81=D1=82 16 | -------------------------------------------------------------------------------- /email2matrix/resolver/model.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | type MailboxMappingInfoProvider interface { 4 | Resolve(mailboxName string) *MailboxMappingInfo 5 | } 6 | 7 | type MailboxMappingInfo struct { 8 | MailboxName string 9 | MatrixHomeserverUrl string 10 | MatrixRoomId string 11 | MatrixUserId string 12 | MatrixAccessToken string 13 | IgnoreSubject bool 14 | IgnoreBody bool 15 | SkipMarkdown bool 16 | } 17 | -------------------------------------------------------------------------------- /email2matrix/resolver/resolver.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "devture-email2matrix/email2matrix/configuration" 5 | ) 6 | 7 | type ConfigurationBackedMailboxMappingInfoProvider struct { 8 | mappings []configuration.ConfigurationMatrixMapping 9 | } 10 | 11 | func NewConfigurationBackedMailboxMappingInfoProvider(mappings []configuration.ConfigurationMatrixMapping) *ConfigurationBackedMailboxMappingInfoProvider { 12 | return &ConfigurationBackedMailboxMappingInfoProvider{ 13 | mappings: mappings, 14 | } 15 | } 16 | 17 | func (me *ConfigurationBackedMailboxMappingInfoProvider) Resolve(mailboxName string) *MailboxMappingInfo { 18 | for _, item := range me.mappings { 19 | if item.MailboxName == mailboxName { 20 | return &MailboxMappingInfo{ 21 | MailboxName: item.MailboxName, 22 | MatrixHomeserverUrl: item.MatrixHomeserverUrl, 23 | MatrixRoomId: item.MatrixRoomId, 24 | MatrixUserId: item.MatrixUserId, 25 | MatrixAccessToken: item.MatrixAccessToken, 26 | IgnoreSubject: item.IgnoreSubject, 27 | IgnoreBody: item.IgnoreBody, 28 | SkipMarkdown: item.SkipMarkdown, 29 | } 30 | } 31 | } 32 | 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /email2matrix/smtp/email2matrix_processor.go: -------------------------------------------------------------------------------- 1 | package smtp 2 | 3 | import ( 4 | "devture-email2matrix/email2matrix/matrix" 5 | "devture-email2matrix/email2matrix/resolver" 6 | "errors" 7 | "fmt" 8 | 9 | "github.com/sirupsen/logrus" 10 | "github.com/flashmob/go-guerrilla/backends" 11 | "github.com/flashmob/go-guerrilla/mail" 12 | ) 13 | 14 | var Email2MatrixProcessor = func( 15 | logger *logrus.Logger, 16 | resolver resolver.MailboxMappingInfoProvider, 17 | ) backends.ProcessorConstructor { 18 | return func() backends.Decorator { 19 | return func(p backends.Processor) backends.Processor { 20 | return backends.ProcessWith( 21 | func(e *mail.Envelope, task backends.SelectTask) (backends.Result, error) { 22 | if task == backends.TaskSaveMail { 23 | receiverMailbox := e.RcptTo[len(e.RcptTo)-1] 24 | 25 | mappingInfo := resolver.Resolve(receiverMailbox.User) 26 | if mappingInfo == nil { 27 | err := errors.New("Cannot find user") 28 | return backends.NewResult(fmt.Sprintf("450 Error: %s", err)), err 29 | } 30 | 31 | matrixEventId, err := matrix.Relay(e, *mappingInfo) 32 | if err != nil { 33 | return backends.NewResult(fmt.Sprintf("554 Error: %s", err)), err 34 | } 35 | 36 | logger.WithFields(logrus.Fields{ 37 | "emailId": e.QueuedId, 38 | "matrixRoomId": mappingInfo.MatrixRoomId, 39 | "matrixEventId": matrixEventId, 40 | }).Infoln("Delivered") 41 | 42 | return p.Process(e, task) 43 | } 44 | return p.Process(e, task) 45 | }, 46 | ) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /etc/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker.io/golang:1.19.0-alpine3.16 2 | WORKDIR /build 3 | RUN apk --no-cache add git 4 | RUN go install github.com/ahmetb/govvv@v0.3.0 5 | COPY . /build/. 6 | RUN CGO_ENABLED=0 go build -a -installsuffix cgo -ldflags "$(govvv -flags)" email2matrix-server.go 7 | 8 | FROM docker.io/alpine:3.16.1 9 | WORKDIR / 10 | RUN apk --no-cache add ca-certificates 11 | COPY --from=0 /build/email2matrix-server . 12 | CMD ["./email2matrix-server"] 13 | -------------------------------------------------------------------------------- /etc/docker/README.md: -------------------------------------------------------------------------------- 1 | # Email2Matrix: SMTP server relaying messages to Matrix rooms 2 | 3 | [email2matrix](https://github.com/devture/email2matrix) is an SMTP server (powered by [Go-Guerrilla](https://github.com/flashmob/go-guerrilla)), which receives messages to certain special (predefined) mailboxes and relays them to [Matrix](http://matrix.org/) rooms. 4 | 5 | 6 | # Using this Docker image 7 | 8 | Start off by [creating your configuration](https://github.com/devture/email2matrix/blob/master/docs/configuration.md). 9 | 10 | Since you're running this in a container, make sure `ListenInterface` configuration uses the `0.0.0.0` interface. 11 | 12 | To start the container: 13 | 14 | ```bash 15 | docker run \ 16 | -it \ 17 | --rm \ 18 | -p 127.0.0.1:41080:41080 \ 19 | -v /local/path/to/config.json:/config.json:ro \ 20 | devture/email2matrix:latest 21 | ``` 22 | 23 | **Hint**: using a tagged/versioned image, instead of `latest` is recommended. 24 | -------------------------------------------------------------------------------- /etc/services/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '2.1' 2 | 3 | services: 4 | postgres: 5 | image: postgres:11.5-alpine 6 | user: ${CURRENT_USER_UID}:${CURRENT_USER_GID} 7 | environment: 8 | POSTGRES_USER: synapse 9 | POSTGRES_PASSWORD: synapse-password 10 | POSTGRES_DB: homeserver 11 | volumes: 12 | - ./matrix-synapse-postgres:/var/lib/postgresql/data 13 | - /etc/passwd:/etc/passwd:ro 14 | 15 | synapse: 16 | image: matrixdotorg/synapse:v1.3.1 17 | user: ${CURRENT_USER_UID}:${CURRENT_USER_GID} 18 | entrypoint: python 19 | command: "-m synapse.app.homeserver -c /data/homeserver.yaml" 20 | restart: on-failure 21 | ports: 22 | # This server does not federate, so we don't expose any more ports. 23 | - "41408:8008" 24 | links: 25 | - postgres:postgres 26 | volumes: 27 | - ../etc/services/matrix-synapse:/data 28 | - ./matrix-synapse-media-store:/media_store 29 | 30 | riot-web: 31 | image: bubuntux/riot-web:v1.3.3 32 | user: ${CURRENT_USER_UID}:${CURRENT_USER_GID} 33 | ports: 34 | - "41465:8080" 35 | volumes: 36 | - ../etc/services/matrix-riot-web/nginx.conf:/etc/nginx/nginx.conf:ro 37 | - /dev/null:/etc/nginx/conf.d/default.conf:ro 38 | - ../etc/services/matrix-riot-web/config.json:/etc/riot-web/config.json:ro 39 | -------------------------------------------------------------------------------- /etc/services/matrix-riot-web/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "default_hs_url": "http://email2matrix.127.0.0.1.xip.io:41408", 3 | "default_is_url": "https://vector.im", 4 | "brand": "Riot", 5 | "integrations_ui_url": "https://scalar.vector.im/", 6 | "integrations_rest_url": "https://scalar.vector.im/api", 7 | "bug_report_endpoint_url": "https://riot.im/bugreports/submit", 8 | "enableLabs": true, 9 | "roomDirectory": { 10 | "servers": [ 11 | "matrix.org" 12 | ] 13 | }, 14 | "welcomeUserId": "@riot-bot:matrix.org" 15 | } 16 | -------------------------------------------------------------------------------- /etc/services/matrix-riot-web/nginx.conf: -------------------------------------------------------------------------------- 1 | # This is a custom nginx configuration file that we use in the container (instead of the default one), 2 | # because it allows us to run nginx with a non-root user. 3 | # 4 | # For this to work, the default vhost file (`/etc/nginx/conf.d/default.conf`) also needs to be removed. 5 | # (mounting `/dev/null` over `/etc/nginx/conf.d/default.conf` works well) 6 | # 7 | # The following changes have been done compared to a default nginx configuration file: 8 | # - default server port is changed (80 -> 8080), so that a non-root user can bind it 9 | # - various temp paths are changed to `/tmp`, so that a non-root user can write to them 10 | # - the `user` directive was removed, as we don't want nginx to switch users 11 | 12 | worker_processes 1; 13 | 14 | error_log /var/log/nginx/error.log warn; 15 | pid /tmp/nginx.pid; 16 | 17 | 18 | events { 19 | worker_connections 1024; 20 | } 21 | 22 | 23 | http { 24 | client_body_temp_path /tmp/client_body_temp; 25 | proxy_temp_path /tmp/proxy_temp; 26 | fastcgi_temp_path /tmp/fastcgi_temp; 27 | uwsgi_temp_path /tmp/uwsgi_temp; 28 | scgi_temp_path /tmp/scgi_temp; 29 | 30 | include /etc/nginx/mime.types; 31 | default_type application/octet-stream; 32 | 33 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 34 | '$status $body_bytes_sent "$http_referer" ' 35 | '"$http_user_agent" "$http_x_forwarded_for"'; 36 | 37 | access_log /var/log/nginx/access.log main; 38 | 39 | sendfile on; 40 | #tcp_nopush on; 41 | 42 | keepalive_timeout 65; 43 | 44 | #gzip on; 45 | 46 | server { 47 | listen 8080; 48 | server_name localhost; 49 | 50 | location / { 51 | root /usr/share/nginx/html; 52 | index index.html index.htm; 53 | } 54 | 55 | error_page 500 502 503 504 /50x.html; 56 | location = /50x.html { 57 | root /usr/share/nginx/html; 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /etc/services/matrix-synapse/email2matrix.127.0.0.1.xip.io.log.config: -------------------------------------------------------------------------------- 1 | version: 1 2 | 3 | formatters: 4 | precise: 5 | format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s' 6 | 7 | filters: 8 | context: 9 | (): synapse.logging.context.LoggingContextFilter 10 | request: "" 11 | 12 | handlers: 13 | console: 14 | class: logging.StreamHandler 15 | formatter: precise 16 | filters: [context] 17 | 18 | loggers: 19 | synapse.storage.SQL: 20 | # beware: increasing this to DEBUG will make synapse log sensitive 21 | # information such as access tokens. 22 | level: INFO 23 | 24 | root: 25 | level: INFO 26 | handlers: [console] -------------------------------------------------------------------------------- /etc/services/matrix-synapse/email2matrix.127.0.0.1.xip.io.signing.key: -------------------------------------------------------------------------------- 1 | ed25519 a_QLuA VClhyAChZfgG0Igdfi7cn2vpkZFnU7gh+/F22/F3nbg 2 | -------------------------------------------------------------------------------- /etc/services/matrix-synapse/homeserver.yaml: -------------------------------------------------------------------------------- 1 | # vim:ft=yaml 2 | 3 | ## Server ## 4 | 5 | # The domain name of the server, with optional explicit port. 6 | # This is used by remote servers to connect to this server, 7 | # e.g. matrix.org, localhost:8080, etc. 8 | # This is also the last part of your UserID. 9 | # 10 | server_name: "email2matrix.127.0.0.1.xip.io" 11 | 12 | # When running as a daemon, the file to store the pid in 13 | # 14 | pid_file: /homeserver.pid 15 | 16 | # The path to the web client which will be served at /_matrix/client/ 17 | # if 'webclient' is configured under the 'listeners' configuration. 18 | # 19 | #web_client_location: "/path/to/web/root" 20 | 21 | # The public-facing base URL that clients use to access this HS 22 | # (not including _matrix/...). This is the same URL a user would 23 | # enter into the 'custom HS URL' field on their client. If you 24 | # use synapse with a reverse proxy, this should be the URL to reach 25 | # synapse via the proxy. 26 | # 27 | #public_baseurl: https://example.com/ 28 | 29 | # Set the soft limit on the number of file descriptors synapse can use 30 | # Zero is used to indicate synapse should set the soft limit to the 31 | # hard limit. 32 | # 33 | #soft_file_limit: 0 34 | 35 | # Set to false to disable presence tracking on this homeserver. 36 | # 37 | #use_presence: false 38 | 39 | # Whether to require authentication to retrieve profile data (avatars, 40 | # display names) of other users through the client API. Defaults to 41 | # 'false'. Note that profile data is also available via the federation 42 | # API, so this setting is of limited value if federation is enabled on 43 | # the server. 44 | # 45 | #require_auth_for_profile_requests: true 46 | 47 | # If set to 'false', requires authentication to access the server's public rooms 48 | # directory through the client API. Defaults to 'true'. 49 | # 50 | #allow_public_rooms_without_auth: false 51 | 52 | # If set to 'false', forbids any other homeserver to fetch the server's public 53 | # rooms directory via federation. Defaults to 'true'. 54 | # 55 | #allow_public_rooms_over_federation: false 56 | 57 | # The default room version for newly created rooms. 58 | # 59 | # Known room versions are listed here: 60 | # https://matrix.org/docs/spec/#complete-list-of-room-versions 61 | # 62 | # For example, for room version 1, default_room_version should be set 63 | # to "1". 64 | # 65 | #default_room_version: "4" 66 | 67 | # The GC threshold parameters to pass to `gc.set_threshold`, if defined 68 | # 69 | #gc_thresholds: [700, 10, 10] 70 | 71 | # Set the limit on the returned events in the timeline in the get 72 | # and sync operations. The default value is -1, means no upper limit. 73 | # 74 | #filter_timeline_limit: 5000 75 | 76 | # Whether room invites to users on this server should be blocked 77 | # (except those sent by local server admins). The default is False. 78 | # 79 | #block_non_admin_invites: True 80 | 81 | # Room searching 82 | # 83 | # If disabled, new messages will not be indexed for searching and users 84 | # will receive errors when searching for messages. Defaults to enabled. 85 | # 86 | #enable_search: false 87 | 88 | # Restrict federation to the following whitelist of domains. 89 | # N.B. we recommend also firewalling your federation listener to limit 90 | # inbound federation traffic as early as possible, rather than relying 91 | # purely on this application-layer restriction. If not specified, the 92 | # default is to whitelist everything. 93 | # 94 | #federation_domain_whitelist: 95 | # - lon.example.com 96 | # - nyc.example.com 97 | # - syd.example.com 98 | 99 | # Prevent federation requests from being sent to the following 100 | # blacklist IP address CIDR ranges. If this option is not specified, or 101 | # specified with an empty list, no ip range blacklist will be enforced. 102 | # 103 | # (0.0.0.0 and :: are always blacklisted, whether or not they are explicitly 104 | # listed here, since they correspond to unroutable addresses.) 105 | # 106 | federation_ip_range_blacklist: 107 | - '127.0.0.0/8' 108 | - '10.0.0.0/8' 109 | - '172.16.0.0/12' 110 | - '192.168.0.0/16' 111 | - '100.64.0.0/10' 112 | - '169.254.0.0/16' 113 | - '::1/128' 114 | - 'fe80::/64' 115 | - 'fc00::/7' 116 | 117 | # List of ports that Synapse should listen on, their purpose and their 118 | # configuration. 119 | # 120 | # Options for each listener include: 121 | # 122 | # port: the TCP port to bind to 123 | # 124 | # bind_addresses: a list of local addresses to listen on. The default is 125 | # 'all local interfaces'. 126 | # 127 | # type: the type of listener. Normally 'http', but other valid options are: 128 | # 'manhole' (see docs/manhole.md), 129 | # 'metrics' (see docs/metrics-howto.rst), 130 | # 'replication' (see docs/workers.rst). 131 | # 132 | # tls: set to true to enable TLS for this listener. Will use the TLS 133 | # key/cert specified in tls_private_key_path / tls_certificate_path. 134 | # 135 | # x_forwarded: Only valid for an 'http' listener. Set to true to use the 136 | # X-Forwarded-For header as the client IP. Useful when Synapse is 137 | # behind a reverse-proxy. 138 | # 139 | # resources: Only valid for an 'http' listener. A list of resources to host 140 | # on this port. Options for each resource are: 141 | # 142 | # names: a list of names of HTTP resources. See below for a list of 143 | # valid resource names. 144 | # 145 | # compress: set to true to enable HTTP comression for this resource. 146 | # 147 | # additional_resources: Only valid for an 'http' listener. A map of 148 | # additional endpoints which should be loaded via dynamic modules. 149 | # 150 | # Valid resource names are: 151 | # 152 | # client: the client-server API (/_matrix/client), and the synapse admin 153 | # API (/_synapse/admin). Also implies 'media' and 'static'. 154 | # 155 | # consent: user consent forms (/_matrix/consent). See 156 | # docs/consent_tracking.md. 157 | # 158 | # federation: the server-server API (/_matrix/federation). Also implies 159 | # 'media', 'keys', 'openid' 160 | # 161 | # keys: the key discovery API (/_matrix/keys). 162 | # 163 | # media: the media API (/_matrix/media). 164 | # 165 | # metrics: the metrics interface. See docs/metrics-howto.rst. 166 | # 167 | # openid: OpenID authentication. 168 | # 169 | # replication: the HTTP replication API (/_synapse/replication). See 170 | # docs/workers.rst. 171 | # 172 | # static: static resources under synapse/static (/_matrix/static). (Mostly 173 | # useful for 'fallback authentication'.) 174 | # 175 | # webclient: A web client. Requires web_client_location to be set. 176 | # 177 | listeners: 178 | # TLS-enabled listener: for when matrix traffic is sent directly to synapse. 179 | # 180 | # Disabled by default. To enable it, uncomment the following. (Note that you 181 | # will also need to give Synapse a TLS key and certificate: see the TLS section 182 | # below.) 183 | # 184 | #- port: 8448 185 | # type: http 186 | # tls: true 187 | # resources: 188 | # - names: [client, federation] 189 | 190 | # Unsecure HTTP listener: for when matrix traffic passes through a reverse proxy 191 | # that unwraps TLS. 192 | # 193 | # If you plan to use a reverse proxy, please see 194 | # https://github.com/matrix-org/synapse/blob/master/docs/reverse_proxy.rst. 195 | # 196 | - port: 8008 197 | tls: false 198 | type: http 199 | x_forwarded: true 200 | 201 | resources: 202 | - names: [client, federation] 203 | compress: false 204 | 205 | # example additional_resources: 206 | # 207 | #additional_resources: 208 | # "/_matrix/my/custom/endpoint": 209 | # module: my_module.CustomRequestHandler 210 | # config: {} 211 | 212 | # Turn on the twisted ssh manhole service on localhost on the given 213 | # port. 214 | # 215 | #- port: 9000 216 | # bind_addresses: ['::1', '127.0.0.1'] 217 | # type: manhole 218 | 219 | 220 | ## Homeserver blocking ## 221 | 222 | # How to reach the server admin, used in ResourceLimitError 223 | # 224 | #admin_contact: 'mailto:admin@server.com' 225 | 226 | # Global blocking 227 | # 228 | #hs_disabled: False 229 | #hs_disabled_message: 'Human readable reason for why the HS is blocked' 230 | #hs_disabled_limit_type: 'error code(str), to help clients decode reason' 231 | 232 | # Monthly Active User Blocking 233 | # 234 | # Used in cases where the admin or server owner wants to limit to the 235 | # number of monthly active users. 236 | # 237 | # 'limit_usage_by_mau' disables/enables monthly active user blocking. When 238 | # anabled and a limit is reached the server returns a 'ResourceLimitError' 239 | # with error type Codes.RESOURCE_LIMIT_EXCEEDED 240 | # 241 | # 'max_mau_value' is the hard limit of monthly active users above which 242 | # the server will start blocking user actions. 243 | # 244 | # 'mau_trial_days' is a means to add a grace period for active users. It 245 | # means that users must be active for this number of days before they 246 | # can be considered active and guards against the case where lots of users 247 | # sign up in a short space of time never to return after their initial 248 | # session. 249 | # 250 | #limit_usage_by_mau: False 251 | #max_mau_value: 50 252 | #mau_trial_days: 2 253 | 254 | # If enabled, the metrics for the number of monthly active users will 255 | # be populated, however no one will be limited. If limit_usage_by_mau 256 | # is true, this is implied to be true. 257 | # 258 | #mau_stats_only: False 259 | 260 | # Sometimes the server admin will want to ensure certain accounts are 261 | # never blocked by mau checking. These accounts are specified here. 262 | # 263 | #mau_limit_reserved_threepids: 264 | # - medium: 'email' 265 | # address: 'reserved_user@example.com' 266 | 267 | # Used by phonehome stats to group together related servers. 268 | #server_context: context 269 | 270 | # Whether to require a user to be in the room to add an alias to it. 271 | # Defaults to 'true'. 272 | # 273 | #require_membership_for_aliases: false 274 | 275 | # Whether to allow per-room membership profiles through the send of membership 276 | # events with profile information that differ from the target's global profile. 277 | # Defaults to 'true'. 278 | # 279 | #allow_per_room_profiles: false 280 | 281 | 282 | ## TLS ## 283 | 284 | # PEM-encoded X509 certificate for TLS. 285 | # This certificate, as of Synapse 1.0, will need to be a valid and verifiable 286 | # certificate, signed by a recognised Certificate Authority. 287 | # 288 | # See 'ACME support' below to enable auto-provisioning this certificate via 289 | # Let's Encrypt. 290 | # 291 | # If supplying your own, be sure to use a `.pem` file that includes the 292 | # full certificate chain including any intermediate certificates (for 293 | # instance, if using certbot, use `fullchain.pem` as your certificate, 294 | # not `cert.pem`). 295 | # 296 | #tls_certificate_path: "/data/email2matrix.127.0.0.1.xip.io.tls.crt" 297 | 298 | # PEM-encoded private key for TLS 299 | # 300 | #tls_private_key_path: "/data/email2matrix.127.0.0.1.xip.io.tls.key" 301 | 302 | # Whether to verify TLS server certificates for outbound federation requests. 303 | # 304 | # Defaults to `true`. To disable certificate verification, uncomment the 305 | # following line. 306 | # 307 | #federation_verify_certificates: false 308 | 309 | # The minimum TLS version that will be used for outbound federation requests. 310 | # 311 | # Defaults to `1`. Configurable to `1`, `1.1`, `1.2`, or `1.3`. Note 312 | # that setting this value higher than `1.2` will prevent federation to most 313 | # of the public Matrix network: only configure it to `1.3` if you have an 314 | # entirely private federation setup and you can ensure TLS 1.3 support. 315 | # 316 | #federation_client_minimum_tls_version: 1.2 317 | 318 | # Skip federation certificate verification on the following whitelist 319 | # of domains. 320 | # 321 | # This setting should only be used in very specific cases, such as 322 | # federation over Tor hidden services and similar. For private networks 323 | # of homeservers, you likely want to use a private CA instead. 324 | # 325 | # Only effective if federation_verify_certicates is `true`. 326 | # 327 | #federation_certificate_verification_whitelist: 328 | # - lon.example.com 329 | # - *.domain.com 330 | # - *.onion 331 | 332 | # List of custom certificate authorities for federation traffic. 333 | # 334 | # This setting should only normally be used within a private network of 335 | # homeservers. 336 | # 337 | # Note that this list will replace those that are provided by your 338 | # operating environment. Certificates must be in PEM format. 339 | # 340 | #federation_custom_ca_list: 341 | # - myCA1.pem 342 | # - myCA2.pem 343 | # - myCA3.pem 344 | 345 | # ACME support: This will configure Synapse to request a valid TLS certificate 346 | # for your configured `server_name` via Let's Encrypt. 347 | # 348 | # Note that provisioning a certificate in this way requires port 80 to be 349 | # routed to Synapse so that it can complete the http-01 ACME challenge. 350 | # By default, if you enable ACME support, Synapse will attempt to listen on 351 | # port 80 for incoming http-01 challenges - however, this will likely fail 352 | # with 'Permission denied' or a similar error. 353 | # 354 | # There are a couple of potential solutions to this: 355 | # 356 | # * If you already have an Apache, Nginx, or similar listening on port 80, 357 | # you can configure Synapse to use an alternate port, and have your web 358 | # server forward the requests. For example, assuming you set 'port: 8009' 359 | # below, on Apache, you would write: 360 | # 361 | # ProxyPass /.well-known/acme-challenge http://localhost:8009/.well-known/acme-challenge 362 | # 363 | # * Alternatively, you can use something like `authbind` to give Synapse 364 | # permission to listen on port 80. 365 | # 366 | acme: 367 | # ACME support is disabled by default. Uncomment the following line 368 | # (and tls_certificate_path and tls_private_key_path above) to enable it. 369 | # 370 | #enabled: true 371 | 372 | # Endpoint to use to request certificates. If you only want to test, 373 | # use Let's Encrypt's staging url: 374 | # https://acme-staging.api.letsencrypt.org/directory 375 | # 376 | #url: https://acme-v01.api.letsencrypt.org/directory 377 | 378 | # Port number to listen on for the HTTP-01 challenge. Change this if 379 | # you are forwarding connections through Apache/Nginx/etc. 380 | # 381 | #port: 80 382 | 383 | # Local addresses to listen on for incoming connections. 384 | # Again, you may want to change this if you are forwarding connections 385 | # through Apache/Nginx/etc. 386 | # 387 | #bind_addresses: ['::', '0.0.0.0'] 388 | 389 | # How many days remaining on a certificate before it is renewed. 390 | # 391 | #reprovision_threshold: 30 392 | 393 | # The domain that the certificate should be for. Normally this 394 | # should be the same as your Matrix domain (i.e., 'server_name'), but, 395 | # by putting a file at 'https:///.well-known/matrix/server', 396 | # you can delegate incoming traffic to another server. If you do that, 397 | # you should give the target of the delegation here. 398 | # 399 | # For example: if your 'server_name' is 'example.com', but 400 | # 'https://example.com/.well-known/matrix/server' delegates to 401 | # 'matrix.example.com', you should put 'matrix.example.com' here. 402 | # 403 | # If not set, defaults to your 'server_name'. 404 | # 405 | #domain: matrix.example.com 406 | 407 | # file to use for the account key. This will be generated if it doesn't 408 | # exist. 409 | # 410 | # If unspecified, we will use CONFDIR/client.key. 411 | # 412 | account_key_file: /data/acme_account.key 413 | 414 | # List of allowed TLS fingerprints for this server to publish along 415 | # with the signing keys for this server. Other matrix servers that 416 | # make HTTPS requests to this server will check that the TLS 417 | # certificates returned by this server match one of the fingerprints. 418 | # 419 | # Synapse automatically adds the fingerprint of its own certificate 420 | # to the list. So if federation traffic is handled directly by synapse 421 | # then no modification to the list is required. 422 | # 423 | # If synapse is run behind a load balancer that handles the TLS then it 424 | # will be necessary to add the fingerprints of the certificates used by 425 | # the loadbalancers to this list if they are different to the one 426 | # synapse is using. 427 | # 428 | # Homeservers are permitted to cache the list of TLS fingerprints 429 | # returned in the key responses up to the "valid_until_ts" returned in 430 | # key. It may be necessary to publish the fingerprints of a new 431 | # certificate and wait until the "valid_until_ts" of the previous key 432 | # responses have passed before deploying it. 433 | # 434 | # You can calculate a fingerprint from a given TLS listener via: 435 | # openssl s_client -connect $host:$port < /dev/null 2> /dev/null | 436 | # openssl x509 -outform DER | openssl sha256 -binary | base64 | tr -d '=' 437 | # or by checking matrix.org/federationtester/api/report?server_name=$host 438 | # 439 | #tls_fingerprints: [{"sha256": ""}] 440 | 441 | 442 | 443 | ## Database ## 444 | 445 | database: 446 | # The database engine name 447 | name: "psycopg2" 448 | args: 449 | user: "synapse" 450 | password: "synapse-password" 451 | database: "homeserver" 452 | host: "postgres" 453 | cp_min: 5 454 | cp_max: 10 455 | 456 | # Number of events to cache in memory. 457 | # 458 | #event_cache_size: 10K 459 | 460 | 461 | ## Logging ## 462 | 463 | # A yaml python logging config file 464 | # 465 | log_config: "/data/email2matrix.127.0.0.1.xip.io.log.config" 466 | 467 | 468 | ## Ratelimiting ## 469 | 470 | # Ratelimiting settings for client actions (registration, login, messaging). 471 | # 472 | # Each ratelimiting configuration is made of two parameters: 473 | # - per_second: number of requests a client can send per second. 474 | # - burst_count: number of requests a client can send before being throttled. 475 | # 476 | # Synapse currently uses the following configurations: 477 | # - one for messages that ratelimits sending based on the account the client 478 | # is using 479 | # - one for registration that ratelimits registration requests based on the 480 | # client's IP address. 481 | # - one for login that ratelimits login requests based on the client's IP 482 | # address. 483 | # - one for login that ratelimits login requests based on the account the 484 | # client is attempting to log into. 485 | # - one for login that ratelimits login requests based on the account the 486 | # client is attempting to log into, based on the amount of failed login 487 | # attempts for this account. 488 | # 489 | # The defaults are as shown below. 490 | # 491 | #rc_message: 492 | # per_second: 0.2 493 | # burst_count: 10 494 | # 495 | #rc_registration: 496 | # per_second: 0.17 497 | # burst_count: 3 498 | # 499 | #rc_login: 500 | # address: 501 | # per_second: 0.17 502 | # burst_count: 3 503 | # account: 504 | # per_second: 0.17 505 | # burst_count: 3 506 | # failed_attempts: 507 | # per_second: 0.17 508 | # burst_count: 3 509 | 510 | 511 | # Ratelimiting settings for incoming federation 512 | # 513 | # The rc_federation configuration is made up of the following settings: 514 | # - window_size: window size in milliseconds 515 | # - sleep_limit: number of federation requests from a single server in 516 | # a window before the server will delay processing the request. 517 | # - sleep_delay: duration in milliseconds to delay processing events 518 | # from remote servers by if they go over the sleep limit. 519 | # - reject_limit: maximum number of concurrent federation requests 520 | # allowed from a single server 521 | # - concurrent: number of federation requests to concurrently process 522 | # from a single server 523 | # 524 | # The defaults are as shown below. 525 | # 526 | #rc_federation: 527 | # window_size: 1000 528 | # sleep_limit: 10 529 | # sleep_delay: 500 530 | # reject_limit: 50 531 | # concurrent: 3 532 | 533 | # Target outgoing federation transaction frequency for sending read-receipts, 534 | # per-room. 535 | # 536 | # If we end up trying to send out more read-receipts, they will get buffered up 537 | # into fewer transactions. 538 | # 539 | #federation_rr_transactions_per_room_per_second: 50 540 | 541 | 542 | 543 | # Directory where uploaded images and attachments are stored. 544 | # 545 | media_store_path: "/media_store" 546 | 547 | # Media storage providers allow media to be stored in different 548 | # locations. 549 | # 550 | #media_storage_providers: 551 | # - module: file_system 552 | # # Whether to write new local files. 553 | # store_local: false 554 | # # Whether to write new remote media 555 | # store_remote: false 556 | # # Whether to block upload requests waiting for write to this 557 | # # provider to complete 558 | # store_synchronous: false 559 | # config: 560 | # directory: /mnt/some/other/directory 561 | 562 | # Directory where in-progress uploads are stored. 563 | # 564 | uploads_path: "/tmp" 565 | 566 | # The largest allowed upload size in bytes 567 | # 568 | #max_upload_size: 10M 569 | 570 | # Maximum number of pixels that will be thumbnailed 571 | # 572 | #max_image_pixels: 32M 573 | 574 | # Whether to generate new thumbnails on the fly to precisely match 575 | # the resolution requested by the client. If true then whenever 576 | # a new resolution is requested by the client the server will 577 | # generate a new thumbnail. If false the server will pick a thumbnail 578 | # from a precalculated list. 579 | # 580 | #dynamic_thumbnails: false 581 | 582 | # List of thumbnails to precalculate when an image is uploaded. 583 | # 584 | #thumbnail_sizes: 585 | # - width: 32 586 | # height: 32 587 | # method: crop 588 | # - width: 96 589 | # height: 96 590 | # method: crop 591 | # - width: 320 592 | # height: 240 593 | # method: scale 594 | # - width: 640 595 | # height: 480 596 | # method: scale 597 | # - width: 800 598 | # height: 600 599 | # method: scale 600 | 601 | # Is the preview URL API enabled? 602 | # 603 | # 'false' by default: uncomment the following to enable it (and specify a 604 | # url_preview_ip_range_blacklist blacklist). 605 | # 606 | #url_preview_enabled: true 607 | 608 | # List of IP address CIDR ranges that the URL preview spider is denied 609 | # from accessing. There are no defaults: you must explicitly 610 | # specify a list for URL previewing to work. You should specify any 611 | # internal services in your network that you do not want synapse to try 612 | # to connect to, otherwise anyone in any Matrix room could cause your 613 | # synapse to issue arbitrary GET requests to your internal services, 614 | # causing serious security issues. 615 | # 616 | # (0.0.0.0 and :: are always blacklisted, whether or not they are explicitly 617 | # listed here, since they correspond to unroutable addresses.) 618 | # 619 | # This must be specified if url_preview_enabled is set. It is recommended that 620 | # you uncomment the following list as a starting point. 621 | # 622 | #url_preview_ip_range_blacklist: 623 | # - '127.0.0.0/8' 624 | # - '10.0.0.0/8' 625 | # - '172.16.0.0/12' 626 | # - '192.168.0.0/16' 627 | # - '100.64.0.0/10' 628 | # - '169.254.0.0/16' 629 | # - '::1/128' 630 | # - 'fe80::/64' 631 | # - 'fc00::/7' 632 | 633 | # List of IP address CIDR ranges that the URL preview spider is allowed 634 | # to access even if they are specified in url_preview_ip_range_blacklist. 635 | # This is useful for specifying exceptions to wide-ranging blacklisted 636 | # target IP ranges - e.g. for enabling URL previews for a specific private 637 | # website only visible in your network. 638 | # 639 | #url_preview_ip_range_whitelist: 640 | # - '192.168.1.1' 641 | 642 | # Optional list of URL matches that the URL preview spider is 643 | # denied from accessing. You should use url_preview_ip_range_blacklist 644 | # in preference to this, otherwise someone could define a public DNS 645 | # entry that points to a private IP address and circumvent the blacklist. 646 | # This is more useful if you know there is an entire shape of URL that 647 | # you know that will never want synapse to try to spider. 648 | # 649 | # Each list entry is a dictionary of url component attributes as returned 650 | # by urlparse.urlsplit as applied to the absolute form of the URL. See 651 | # https://docs.python.org/2/library/urlparse.html#urlparse.urlsplit 652 | # The values of the dictionary are treated as an filename match pattern 653 | # applied to that component of URLs, unless they start with a ^ in which 654 | # case they are treated as a regular expression match. If all the 655 | # specified component matches for a given list item succeed, the URL is 656 | # blacklisted. 657 | # 658 | #url_preview_url_blacklist: 659 | # # blacklist any URL with a username in its URI 660 | # - username: '*' 661 | # 662 | # # blacklist all *.google.com URLs 663 | # - netloc: 'google.com' 664 | # - netloc: '*.google.com' 665 | # 666 | # # blacklist all plain HTTP URLs 667 | # - scheme: 'http' 668 | # 669 | # # blacklist http(s)://www.acme.com/foo 670 | # - netloc: 'www.acme.com' 671 | # path: '/foo' 672 | # 673 | # # blacklist any URL with a literal IPv4 address 674 | # - netloc: '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' 675 | 676 | # The largest allowed URL preview spidering size in bytes 677 | # 678 | #max_spider_size: 10M 679 | 680 | 681 | ## Captcha ## 682 | # See docs/CAPTCHA_SETUP for full details of configuring this. 683 | 684 | # This Home Server's ReCAPTCHA public key. 685 | # 686 | #recaptcha_public_key: "YOUR_PUBLIC_KEY" 687 | 688 | # This Home Server's ReCAPTCHA private key. 689 | # 690 | #recaptcha_private_key: "YOUR_PRIVATE_KEY" 691 | 692 | # Enables ReCaptcha checks when registering, preventing signup 693 | # unless a captcha is answered. Requires a valid ReCaptcha 694 | # public/private key. 695 | # 696 | #enable_registration_captcha: false 697 | 698 | # A secret key used to bypass the captcha test entirely. 699 | # 700 | #captcha_bypass_secret: "YOUR_SECRET_HERE" 701 | 702 | # The API endpoint to use for verifying m.login.recaptcha responses. 703 | # 704 | #recaptcha_siteverify_api: "https://www.recaptcha.net/recaptcha/api/siteverify" 705 | 706 | 707 | ## TURN ## 708 | 709 | # The public URIs of the TURN server to give to clients 710 | # 711 | #turn_uris: [] 712 | 713 | # The shared secret used to compute passwords for the TURN server 714 | # 715 | #turn_shared_secret: "YOUR_SHARED_SECRET" 716 | 717 | # The Username and password if the TURN server needs them and 718 | # does not use a token 719 | # 720 | #turn_username: "TURNSERVER_USERNAME" 721 | #turn_password: "TURNSERVER_PASSWORD" 722 | 723 | # How long generated TURN credentials last 724 | # 725 | #turn_user_lifetime: 1h 726 | 727 | # Whether guests should be allowed to use the TURN server. 728 | # This defaults to True, otherwise VoIP will be unreliable for guests. 729 | # However, it does introduce a slight security risk as it allows users to 730 | # connect to arbitrary endpoints without having first signed up for a 731 | # valid account (e.g. by passing a CAPTCHA). 732 | # 733 | #turn_allow_guests: True 734 | 735 | 736 | ## Registration ## 737 | # 738 | # Registration can be rate-limited using the parameters in the "Ratelimiting" 739 | # section of this file. 740 | 741 | # Enable registration for new users. 742 | # 743 | #enable_registration: false 744 | 745 | # Optional account validity configuration. This allows for accounts to be denied 746 | # any request after a given period. 747 | # 748 | # ``enabled`` defines whether the account validity feature is enabled. Defaults 749 | # to False. 750 | # 751 | # ``period`` allows setting the period after which an account is valid 752 | # after its registration. When renewing the account, its validity period 753 | # will be extended by this amount of time. This parameter is required when using 754 | # the account validity feature. 755 | # 756 | # ``renew_at`` is the amount of time before an account's expiry date at which 757 | # Synapse will send an email to the account's email address with a renewal link. 758 | # This needs the ``email`` and ``public_baseurl`` configuration sections to be 759 | # filled. 760 | # 761 | # ``renew_email_subject`` is the subject of the email sent out with the renewal 762 | # link. ``%(app)s`` can be used as a placeholder for the ``app_name`` parameter 763 | # from the ``email`` section. 764 | # 765 | # Once this feature is enabled, Synapse will look for registered users without an 766 | # expiration date at startup and will add one to every account it found using the 767 | # current settings at that time. 768 | # This means that, if a validity period is set, and Synapse is restarted (it will 769 | # then derive an expiration date from the current validity period), and some time 770 | # after that the validity period changes and Synapse is restarted, the users' 771 | # expiration dates won't be updated unless their account is manually renewed. This 772 | # date will be randomly selected within a range [now + period - d ; now + period], 773 | # where d is equal to 10% of the validity period. 774 | # 775 | #account_validity: 776 | # enabled: True 777 | # period: 6w 778 | # renew_at: 1w 779 | # renew_email_subject: "Renew your %(app)s account" 780 | 781 | # Time that a user's session remains valid for, after they log in. 782 | # 783 | # Note that this is not currently compatible with guest logins. 784 | # 785 | # Note also that this is calculated at login time: changes are not applied 786 | # retrospectively to users who have already logged in. 787 | # 788 | # By default, this is infinite. 789 | # 790 | #session_lifetime: 24h 791 | 792 | # The user must provide all of the below types of 3PID when registering. 793 | # 794 | #registrations_require_3pid: 795 | # - email 796 | # - msisdn 797 | 798 | # Explicitly disable asking for MSISDNs from the registration 799 | # flow (overrides registrations_require_3pid if MSISDNs are set as required) 800 | # 801 | #disable_msisdn_registration: true 802 | 803 | # Mandate that users are only allowed to associate certain formats of 804 | # 3PIDs with accounts on this server. 805 | # 806 | #allowed_local_3pids: 807 | # - medium: email 808 | # pattern: '.*@matrix\.org' 809 | # - medium: email 810 | # pattern: '.*@vector\.im' 811 | # - medium: msisdn 812 | # pattern: '\+44' 813 | 814 | # Enable 3PIDs lookup requests to identity servers from this server. 815 | # 816 | #enable_3pid_lookup: true 817 | 818 | # If set, allows registration of standard or admin accounts by anyone who 819 | # has the shared secret, even if registration is otherwise disabled. 820 | # 821 | registration_shared_secret: "St@nZP7wE5v;U&9s#fCY3BAkk._DMgRZM7dMVl&FYlcTZGnz.3" 822 | 823 | # Set the number of bcrypt rounds used to generate password hash. 824 | # Larger numbers increase the work factor needed to generate the hash. 825 | # The default number is 12 (which equates to 2^12 rounds). 826 | # N.B. that increasing this will exponentially increase the time required 827 | # to register or login - e.g. 24 => 2^24 rounds which will take >20 mins. 828 | # 829 | #bcrypt_rounds: 12 830 | 831 | # Allows users to register as guests without a password/email/etc, and 832 | # participate in rooms hosted on this server which have been made 833 | # accessible to anonymous users. 834 | # 835 | #allow_guest_access: false 836 | 837 | # The identity server which we suggest that clients should use when users log 838 | # in on this server. 839 | # 840 | # (By default, no suggestion is made, so it is left up to the client. 841 | # This setting is ignored unless public_baseurl is also set.) 842 | # 843 | #default_identity_server: https://matrix.org 844 | 845 | # The list of identity servers trusted to verify third party 846 | # identifiers by this server. 847 | # 848 | # Also defines the ID server which will be called when an account is 849 | # deactivated (one will be picked arbitrarily). 850 | # 851 | #trusted_third_party_id_servers: 852 | # - matrix.org 853 | # - vector.im 854 | 855 | # Users who register on this homeserver will automatically be joined 856 | # to these rooms 857 | # 858 | #auto_join_rooms: 859 | # - "#example:example.com" 860 | 861 | # Where auto_join_rooms are specified, setting this flag ensures that the 862 | # the rooms exist by creating them when the first user on the 863 | # homeserver registers. 864 | # Setting to false means that if the rooms are not manually created, 865 | # users cannot be auto-joined since they do not exist. 866 | # 867 | #autocreate_auto_join_rooms: true 868 | 869 | 870 | ## Metrics ### 871 | 872 | # Enable collection and rendering of performance metrics 873 | # 874 | #enable_metrics: False 875 | 876 | # Enable sentry integration 877 | # NOTE: While attempts are made to ensure that the logs don't contain 878 | # any sensitive information, this cannot be guaranteed. By enabling 879 | # this option the sentry server may therefore receive sensitive 880 | # information, and it in turn may then diseminate sensitive information 881 | # through insecure notification channels if so configured. 882 | # 883 | #sentry: 884 | # dsn: "..." 885 | 886 | # Whether or not to report anonymized homeserver usage statistics. 887 | report_stats: false 888 | 889 | 890 | ## API Configuration ## 891 | 892 | # A list of event types that will be included in the room_invite_state 893 | # 894 | #room_invite_state_types: 895 | # - "m.room.join_rules" 896 | # - "m.room.canonical_alias" 897 | # - "m.room.avatar" 898 | # - "m.room.encryption" 899 | # - "m.room.name" 900 | 901 | 902 | # A list of application service config files to use 903 | # 904 | #app_service_config_files: 905 | # - app_service_1.yaml 906 | # - app_service_2.yaml 907 | 908 | # Uncomment to enable tracking of application service IP addresses. Implicitly 909 | # enables MAU tracking for application service users. 910 | # 911 | #track_appservice_user_ips: True 912 | 913 | 914 | # a secret which is used to sign access tokens. If none is specified, 915 | # the registration_shared_secret is used, if one is given; otherwise, 916 | # a secret key is derived from the signing key. 917 | # 918 | macaroon_secret_key: "pr9zwi876nQt*#9+PADX.0KOb6=JGg#,@o&~,sHj5VLpLoR+,:" 919 | 920 | # Used to enable access token expiration. 921 | # 922 | #expire_access_token: False 923 | 924 | # a secret which is used to calculate HMACs for form values, to stop 925 | # falsification of values. Must be specified for the User Consent 926 | # forms to work. 927 | # 928 | form_secret: "94eqA^p81n_@ZTWZUov^RyXSjA~zQv:y9#PB*pdQZU6@OwHPnS" 929 | 930 | ## Signing Keys ## 931 | 932 | # Path to the signing key to sign messages with 933 | # 934 | signing_key_path: "/data/email2matrix.127.0.0.1.xip.io.signing.key" 935 | 936 | # The keys that the server used to sign messages with but won't use 937 | # to sign new messages. E.g. it has lost its private key 938 | # 939 | #old_signing_keys: 940 | # "ed25519:auto": 941 | # # Base64 encoded public key 942 | # key: "The public part of your old signing key." 943 | # # Millisecond POSIX timestamp when the key expired. 944 | # expired_ts: 123456789123 945 | 946 | # How long key response published by this server is valid for. 947 | # Used to set the valid_until_ts in /key/v2 APIs. 948 | # Determines how quickly servers will query to check which keys 949 | # are still valid. 950 | # 951 | #key_refresh_interval: 1d 952 | 953 | # The trusted servers to download signing keys from. 954 | # 955 | # When we need to fetch a signing key, each server is tried in parallel. 956 | # 957 | # Normally, the connection to the key server is validated via TLS certificates. 958 | # Additional security can be provided by configuring a `verify key`, which 959 | # will make synapse check that the response is signed by that key. 960 | # 961 | # This setting supercedes an older setting named `perspectives`. The old format 962 | # is still supported for backwards-compatibility, but it is deprecated. 963 | # 964 | # Options for each entry in the list include: 965 | # 966 | # server_name: the name of the server. required. 967 | # 968 | # verify_keys: an optional map from key id to base64-encoded public key. 969 | # If specified, we will check that the response is signed by at least 970 | # one of the given keys. 971 | # 972 | # accept_keys_insecurely: a boolean. Normally, if `verify_keys` is unset, 973 | # and federation_verify_certificates is not `true`, synapse will refuse 974 | # to start, because this would allow anyone who can spoof DNS responses 975 | # to masquerade as the trusted key server. If you know what you are doing 976 | # and are sure that your network environment provides a secure connection 977 | # to the key server, you can set this to `true` to override this 978 | # behaviour. 979 | # 980 | # An example configuration might look like: 981 | # 982 | #trusted_key_servers: 983 | # - server_name: "my_trusted_server.example.com" 984 | # verify_keys: 985 | # "ed25519:auto": "abcdefghijklmnopqrstuvwxyzabcdefghijklmopqr" 986 | # - server_name: "my_other_trusted_server.example.com" 987 | # 988 | # The default configuration is: 989 | # 990 | #trusted_key_servers: 991 | # - server_name: "matrix.org" 992 | 993 | 994 | # Enable SAML2 for registration and login. Uses pysaml2. 995 | # 996 | # `sp_config` is the configuration for the pysaml2 Service Provider. 997 | # See pysaml2 docs for format of config. 998 | # 999 | # Default values will be used for the 'entityid' and 'service' settings, 1000 | # so it is not normally necessary to specify them unless you need to 1001 | # override them. 1002 | # 1003 | # Once SAML support is enabled, a metadata file will be exposed at 1004 | # https://:/_matrix/saml2/metadata.xml, which you may be able to 1005 | # use to configure your SAML IdP with. Alternatively, you can manually configure 1006 | # the IdP to use an ACS location of 1007 | # https://:/_matrix/saml2/authn_response. 1008 | # 1009 | #saml2_config: 1010 | # sp_config: 1011 | # # point this to the IdP's metadata. You can use either a local file or 1012 | # # (preferably) a URL. 1013 | # metadata: 1014 | # #local: ["saml2/idp.xml"] 1015 | # remote: 1016 | # - url: https://our_idp/metadata.xml 1017 | # 1018 | # # By default, the user has to go to our login page first. If you'd like to 1019 | # # allow IdP-initiated login, set 'allow_unsolicited: True' in a 1020 | # # 'service.sp' section: 1021 | # # 1022 | # #service: 1023 | # # sp: 1024 | # # allow_unsolicited: True 1025 | # 1026 | # # The examples below are just used to generate our metadata xml, and you 1027 | # # may well not need it, depending on your setup. Alternatively you 1028 | # # may need a whole lot more detail - see the pysaml2 docs! 1029 | # 1030 | # description: ["My awesome SP", "en"] 1031 | # name: ["Test SP", "en"] 1032 | # 1033 | # organization: 1034 | # name: Example com 1035 | # display_name: 1036 | # - ["Example co", "en"] 1037 | # url: "http://example.com" 1038 | # 1039 | # contact_person: 1040 | # - given_name: Bob 1041 | # sur_name: "the Sysadmin" 1042 | # email_address": ["admin@example.com"] 1043 | # contact_type": technical 1044 | # 1045 | # # Instead of putting the config inline as above, you can specify a 1046 | # # separate pysaml2 configuration file: 1047 | # # 1048 | # config_path: "/data/sp_conf.py" 1049 | # 1050 | # # the lifetime of a SAML session. This defines how long a user has to 1051 | # # complete the authentication process, if allow_unsolicited is unset. 1052 | # # The default is 5 minutes. 1053 | # # 1054 | # # saml_session_lifetime: 5m 1055 | 1056 | 1057 | 1058 | # Enable CAS for registration and login. 1059 | # 1060 | #cas_config: 1061 | # enabled: true 1062 | # server_url: "https://cas-server.com" 1063 | # service_url: "https://homeserver.domain.com:8448" 1064 | # #required_attributes: 1065 | # # name: value 1066 | 1067 | 1068 | # The JWT needs to contain a globally unique "sub" (subject) claim. 1069 | # 1070 | #jwt_config: 1071 | # enabled: true 1072 | # secret: "a secret" 1073 | # algorithm: "HS256" 1074 | 1075 | 1076 | password_config: 1077 | # Uncomment to disable password login 1078 | # 1079 | #enabled: false 1080 | 1081 | # Uncomment to disable authentication against the local password 1082 | # database. This is ignored if `enabled` is false, and is only useful 1083 | # if you have other password_providers. 1084 | # 1085 | #localdb_enabled: false 1086 | 1087 | # Uncomment and change to a secret random string for extra security. 1088 | # DO NOT CHANGE THIS AFTER INITIAL SETUP! 1089 | # 1090 | #pepper: "EVEN_MORE_SECRET" 1091 | 1092 | 1093 | 1094 | # Enable sending emails for password resets, notification events or 1095 | # account expiry notices 1096 | # 1097 | # If your SMTP server requires authentication, the optional smtp_user & 1098 | # smtp_pass variables should be used 1099 | # 1100 | #email: 1101 | # enable_notifs: false 1102 | # smtp_host: "localhost" 1103 | # smtp_port: 25 # SSL: 465, STARTTLS: 587 1104 | # smtp_user: "exampleusername" 1105 | # smtp_pass: "examplepassword" 1106 | # require_transport_security: False 1107 | # notif_from: "Your Friendly %(app)s Home Server " 1108 | # app_name: Matrix 1109 | # 1110 | # # Enable email notifications by default 1111 | # # 1112 | # notif_for_new_users: True 1113 | # 1114 | # # Defining a custom URL for Riot is only needed if email notifications 1115 | # # should contain links to a self-hosted installation of Riot; when set 1116 | # # the "app_name" setting is ignored 1117 | # # 1118 | # riot_base_url: "http://localhost/riot" 1119 | # 1120 | # # Enable sending password reset emails via the configured, trusted 1121 | # # identity servers 1122 | # # 1123 | # # IMPORTANT! This will give a malicious or overtaken identity server 1124 | # # the ability to reset passwords for your users! Make absolutely sure 1125 | # # that you want to do this! It is strongly recommended that password 1126 | # # reset emails be sent by the homeserver instead 1127 | # # 1128 | # # If this option is set to false and SMTP options have not been 1129 | # # configured, resetting user passwords via email will be disabled 1130 | # # 1131 | # #trust_identity_server_for_password_resets: false 1132 | # 1133 | # # Configure the time that a validation email or text message code 1134 | # # will expire after sending 1135 | # # 1136 | # # This is currently used for password resets 1137 | # # 1138 | # #validation_token_lifetime: 1h 1139 | # 1140 | # # Template directory. All template files should be stored within this 1141 | # # directory. If not set, default templates from within the Synapse 1142 | # # package will be used 1143 | # # 1144 | # # For the list of default templates, please see 1145 | # # https://github.com/matrix-org/synapse/tree/master/synapse/res/templates 1146 | # # 1147 | # #template_dir: res/templates 1148 | # 1149 | # # Templates for email notifications 1150 | # # 1151 | # notif_template_html: notif_mail.html 1152 | # notif_template_text: notif_mail.txt 1153 | # 1154 | # # Templates for account expiry notices 1155 | # # 1156 | # expiry_template_html: notice_expiry.html 1157 | # expiry_template_text: notice_expiry.txt 1158 | # 1159 | # # Templates for password reset emails sent by the homeserver 1160 | # # 1161 | # #password_reset_template_html: password_reset.html 1162 | # #password_reset_template_text: password_reset.txt 1163 | # 1164 | # # Templates for password reset success and failure pages that a user 1165 | # # will see after attempting to reset their password 1166 | # # 1167 | # #password_reset_template_success_html: password_reset_success.html 1168 | # #password_reset_template_failure_html: password_reset_failure.html 1169 | 1170 | 1171 | #password_providers: 1172 | # - module: "ldap_auth_provider.LdapAuthProvider" 1173 | # config: 1174 | # enabled: true 1175 | # uri: "ldap://ldap.example.com:389" 1176 | # start_tls: true 1177 | # base: "ou=users,dc=example,dc=com" 1178 | # attributes: 1179 | # uid: "cn" 1180 | # mail: "email" 1181 | # name: "givenName" 1182 | # #bind_dn: 1183 | # #bind_password: 1184 | # #filter: "(objectClass=posixAccount)" 1185 | 1186 | 1187 | 1188 | # Clients requesting push notifications can either have the body of 1189 | # the message sent in the notification poke along with other details 1190 | # like the sender, or just the event ID and room ID (`event_id_only`). 1191 | # If clients choose the former, this option controls whether the 1192 | # notification request includes the content of the event (other details 1193 | # like the sender are still included). For `event_id_only` push, it 1194 | # has no effect. 1195 | # 1196 | # For modern android devices the notification content will still appear 1197 | # because it is loaded by the app. iPhone, however will send a 1198 | # notification saying only that a message arrived and who it came from. 1199 | # 1200 | #push: 1201 | # include_content: true 1202 | 1203 | 1204 | #spam_checker: 1205 | # module: "my_custom_project.SuperSpamChecker" 1206 | # config: 1207 | # example_option: 'things' 1208 | 1209 | 1210 | # Uncomment to allow non-server-admin users to create groups on this server 1211 | # 1212 | #enable_group_creation: true 1213 | 1214 | # If enabled, non server admins can only create groups with local parts 1215 | # starting with this prefix 1216 | # 1217 | #group_creation_prefix: "unofficial/" 1218 | 1219 | 1220 | 1221 | # User Directory configuration 1222 | # 1223 | # 'enabled' defines whether users can search the user directory. If 1224 | # false then empty responses are returned to all queries. Defaults to 1225 | # true. 1226 | # 1227 | # 'search_all_users' defines whether to search all users visible to your HS 1228 | # when searching the user directory, rather than limiting to users visible 1229 | # in public rooms. Defaults to false. If you set it True, you'll have to 1230 | # rebuild the user_directory search indexes, see 1231 | # https://github.com/matrix-org/synapse/blob/master/docs/user_directory.md 1232 | # 1233 | #user_directory: 1234 | # enabled: true 1235 | # search_all_users: false 1236 | 1237 | 1238 | # User Consent configuration 1239 | # 1240 | # for detailed instructions, see 1241 | # https://github.com/matrix-org/synapse/blob/master/docs/consent_tracking.md 1242 | # 1243 | # Parts of this section are required if enabling the 'consent' resource under 1244 | # 'listeners', in particular 'template_dir' and 'version'. 1245 | # 1246 | # 'template_dir' gives the location of the templates for the HTML forms. 1247 | # This directory should contain one subdirectory per language (eg, 'en', 'fr'), 1248 | # and each language directory should contain the policy document (named as 1249 | # '.html') and a success page (success.html). 1250 | # 1251 | # 'version' specifies the 'current' version of the policy document. It defines 1252 | # the version to be served by the consent resource if there is no 'v' 1253 | # parameter. 1254 | # 1255 | # 'server_notice_content', if enabled, will send a user a "Server Notice" 1256 | # asking them to consent to the privacy policy. The 'server_notices' section 1257 | # must also be configured for this to work. Notices will *not* be sent to 1258 | # guest users unless 'send_server_notice_to_guests' is set to true. 1259 | # 1260 | # 'block_events_error', if set, will block any attempts to send events 1261 | # until the user consents to the privacy policy. The value of the setting is 1262 | # used as the text of the error. 1263 | # 1264 | # 'require_at_registration', if enabled, will add a step to the registration 1265 | # process, similar to how captcha works. Users will be required to accept the 1266 | # policy before their account is created. 1267 | # 1268 | # 'policy_name' is the display name of the policy users will see when registering 1269 | # for an account. Has no effect unless `require_at_registration` is enabled. 1270 | # Defaults to "Privacy Policy". 1271 | # 1272 | #user_consent: 1273 | # template_dir: res/templates/privacy 1274 | # version: 1.0 1275 | # server_notice_content: 1276 | # msgtype: m.text 1277 | # body: >- 1278 | # To continue using this homeserver you must review and agree to the 1279 | # terms and conditions at %(consent_uri)s 1280 | # send_server_notice_to_guests: True 1281 | # block_events_error: >- 1282 | # To continue using this homeserver you must review and agree to the 1283 | # terms and conditions at %(consent_uri)s 1284 | # require_at_registration: False 1285 | # policy_name: Privacy Policy 1286 | # 1287 | 1288 | 1289 | 1290 | # Local statistics collection. Used in populating the room directory. 1291 | # 1292 | # 'bucket_size' controls how large each statistics timeslice is. It can 1293 | # be defined in a human readable short form -- e.g. "1d", "1y". 1294 | # 1295 | # 'retention' controls how long historical statistics will be kept for. 1296 | # It can be defined in a human readable short form -- e.g. "1d", "1y". 1297 | # 1298 | # 1299 | #stats: 1300 | # enabled: true 1301 | # bucket_size: 1d 1302 | # retention: 1y 1303 | 1304 | 1305 | # Server Notices room configuration 1306 | # 1307 | # Uncomment this section to enable a room which can be used to send notices 1308 | # from the server to users. It is a special room which cannot be left; notices 1309 | # come from a special "notices" user id. 1310 | # 1311 | # If you uncomment this section, you *must* define the system_mxid_localpart 1312 | # setting, which defines the id of the user which will be used to send the 1313 | # notices. 1314 | # 1315 | # It's also possible to override the room name, the display name of the 1316 | # "notices" user, and the avatar for the user. 1317 | # 1318 | #server_notices: 1319 | # system_mxid_localpart: notices 1320 | # system_mxid_display_name: "Server Notices" 1321 | # system_mxid_avatar_url: "mxc://server.com/oumMVlgDnLYFaPVkExemNVVZ" 1322 | # room_name: "Server Notices" 1323 | 1324 | 1325 | 1326 | # Uncomment to disable searching the public room list. When disabled 1327 | # blocks searching local and remote room lists for local and remote 1328 | # users by always returning an empty list for all queries. 1329 | # 1330 | #enable_room_list_search: false 1331 | 1332 | # The `alias_creation` option controls who's allowed to create aliases 1333 | # on this server. 1334 | # 1335 | # The format of this option is a list of rules that contain globs that 1336 | # match against user_id, room_id and the new alias (fully qualified with 1337 | # server name). The action in the first rule that matches is taken, 1338 | # which can currently either be "allow" or "deny". 1339 | # 1340 | # Missing user_id/room_id/alias fields default to "*". 1341 | # 1342 | # If no rules match the request is denied. An empty list means no one 1343 | # can create aliases. 1344 | # 1345 | # Options for the rules include: 1346 | # 1347 | # user_id: Matches against the creator of the alias 1348 | # alias: Matches against the alias being created 1349 | # room_id: Matches against the room ID the alias is being pointed at 1350 | # action: Whether to "allow" or "deny" the request if the rule matches 1351 | # 1352 | # The default is: 1353 | # 1354 | #alias_creation_rules: 1355 | # - user_id: "*" 1356 | # alias: "*" 1357 | # room_id: "*" 1358 | # action: allow 1359 | 1360 | # The `room_list_publication_rules` option controls who can publish and 1361 | # which rooms can be published in the public room list. 1362 | # 1363 | # The format of this option is the same as that for 1364 | # `alias_creation_rules`. 1365 | # 1366 | # If the room has one or more aliases associated with it, only one of 1367 | # the aliases needs to match the alias rule. If there are no aliases 1368 | # then only rules with `alias: *` match. 1369 | # 1370 | # If no rules match the request is denied. An empty list means no one 1371 | # can publish rooms. 1372 | # 1373 | # Options for the rules include: 1374 | # 1375 | # user_id: Matches agaisnt the creator of the alias 1376 | # room_id: Matches against the room ID being published 1377 | # alias: Matches against any current local or canonical aliases 1378 | # associated with the room 1379 | # action: Whether to "allow" or "deny" the request if the rule matches 1380 | # 1381 | # The default is: 1382 | # 1383 | #room_list_publication_rules: 1384 | # - user_id: "*" 1385 | # alias: "*" 1386 | # room_id: "*" 1387 | # action: allow 1388 | 1389 | 1390 | # Server admins can define a Python module that implements extra rules for 1391 | # allowing or denying incoming events. In order to work, this module needs to 1392 | # override the methods defined in synapse/events/third_party_rules.py. 1393 | # 1394 | # This feature is designed to be used in closed federations only, where each 1395 | # participating server enforces the same rules. 1396 | # 1397 | #third_party_event_rules: 1398 | # module: "my_custom_project.SuperRulesSet" 1399 | # config: 1400 | # example_option: 'things' 1401 | 1402 | 1403 | ## Opentracing ## 1404 | 1405 | # These settings enable opentracing, which implements distributed tracing. 1406 | # This allows you to observe the causal chains of events across servers 1407 | # including requests, key lookups etc., across any server running 1408 | # synapse or any other other services which supports opentracing 1409 | # (specifically those implemented with Jaeger). 1410 | # 1411 | opentracing: 1412 | # tracing is disabled by default. Uncomment the following line to enable it. 1413 | # 1414 | #enabled: true 1415 | 1416 | # The list of homeservers we wish to send and receive span contexts and span baggage. 1417 | # See docs/opentracing.rst 1418 | # This is a list of regexes which are matched against the server_name of the 1419 | # homeserver. 1420 | # 1421 | # By defult, it is empty, so no servers are matched. 1422 | # 1423 | #homeserver_whitelist: 1424 | # - ".*" 1425 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module devture-email2matrix 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef // indirect 7 | github.com/euskadi31/go-service v1.4.0 8 | github.com/flashmob/go-guerrilla v1.6.1 9 | github.com/go-sql-driver/mysql v1.4.1 // indirect 10 | github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect 11 | github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba // indirect 12 | github.com/jhillyerd/enmime v0.9.3 13 | github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 14 | github.com/mattn/go-runewidth v0.0.13 // indirect 15 | github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect 16 | github.com/sirupsen/logrus v1.8.1 17 | golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect 18 | golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a // indirect 19 | google.golang.org/appengine v1.6.7 // indirect 20 | gopkg.in/russross/blackfriday.v2 v2.1.0 21 | ) 22 | 23 | replace gopkg.in/russross/blackfriday.v2 => github.com/russross/blackfriday/v2 v2.0.1 24 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef h1:2JGTg6JapxP9/R33ZaagQtAM4EkkSYnIAlOG5EI8gkM= 2 | github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef/go.mod h1:JS7hed4L1fj0hXcyEejnW57/7LCetXggd+vwrRnYeII= 3 | github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI= 4 | github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/euskadi31/go-service v1.4.0 h1:Wz5pR7osrSw+jGOkX+KZ3TxIIVrAqm/o8FB9T00V+E0= 9 | github.com/euskadi31/go-service v1.4.0/go.mod h1:Ug06GLlnDDvnMXc9+nkyitFYa6qdMHZp9vMwFUWE1uU= 10 | github.com/flashmob/go-guerrilla v1.6.1 h1:MLkqzRFUJveVAWuQ3s2MNPTAWbvXLt8EFsBoraS6qHA= 11 | github.com/flashmob/go-guerrilla v1.6.1/go.mod h1:ZT9TRggRsSY4ZVndoyx8TRUxi3tM/nOYtKWKDX94H0I= 12 | github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= 13 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 14 | github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M= 15 | github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= 16 | github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14= 17 | github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs= 18 | github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14= 19 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 20 | github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= 21 | github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba h1:QFQpJdgbON7I0jr2hYW7Bs+XV0qjc3d5tZoDnRFnqTg= 22 | github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= 23 | github.com/jhillyerd/enmime v0.9.3 h1:XKqSbnX3AV+MbzM2NnhRlO6BBa123sPTdwtGl2pySik= 24 | github.com/jhillyerd/enmime v0.9.3/go.mod h1:S5ge4lnv/dDDBbAWwtoOFlj14NHiXdw/EqMB2lJz3b8= 25 | github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 h1:ZtO5uywdd5dLDCud4r0r55eP4j9FuUNpl60Gmntcop4= 26 | github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s= 27 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 28 | github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= 29 | github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= 30 | github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 31 | github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 32 | github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= 33 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 34 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 35 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 36 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 37 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 38 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 39 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 40 | github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= 41 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 42 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 43 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 44 | github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= 45 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 46 | github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= 47 | github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= 48 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 49 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 50 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 51 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 52 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 53 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 54 | golang.org/x/net v0.0.0-20210501142056-aec3718b3fa0/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= 55 | golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= 56 | golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 57 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 58 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 59 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 60 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 61 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 62 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 63 | golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a h1:ppl5mZgokTT8uPkmYOyEUmPTr3ypaKkg5eFOGrAmxxE= 64 | golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 65 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 66 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 67 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 68 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 69 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 70 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 71 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 72 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 73 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 74 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 75 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 76 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 77 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 78 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 79 | --------------------------------------------------------------------------------