Compare commits

...
Author SHA1 Message Date
jcoffey-dev b62713bb8d Merge pull request 'Release 2026.10.6.1' (#159) from release/2026.10.6.1-pr into main
ci / fork-checks (push) Skipped
ci / build (push) Skipped
publish / version (push) Skipped
publish / publish-amd64 (push) Skipped
publish / publish-arm64 (push) Skipped
publish / release (push) Skipped
publish / binaries (push) Skipped
github/ci (branch) GitHub Actions
ci / github (push) Successful in 42m8s
announce / announce (release) Successful in 9s
publish / github (push) Failing after 1h19m31s
publish / announce (push) Skipped
github/ci (tag) GitHub Actions
2026-10-06 14:24:52 +00:00
jcoffey-dev ef56eb33ed Release 2026.10.6.1
ci / fork-checks (pull_request) Skipped
ci / build (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 7m26s
2026-10-06 07:05:59 -07:00
jcoffey-dev b64f6690f6 Merge pull request 'Merge upstream v0.16.25' (#155) from merge/upstream-v0.16.25 into main
ci / fork-checks (push) Skipped
ci / build (push) Skipped
github/ci (branch) GitHub Actions
ci / github (push) Successful in 42m47s
2026-10-06 07:30:12 +00:00
jcoffey-dev bf7c84bc15 Merge pull request 'Send security and conduct reports to Coffey Labs LLC addresses' (#158) from chore/company-contacts into main
ci / fork-checks (push) Skipped
ci / build (push) Skipped
github/ci (branch) GitHub Actions
ci / github (push) Canceled after 7s
2026-10-06 07:30:09 +00:00
jcoffey-dev b07e69b5ed Send security and conduct reports to Coffey Labs LLC addresses
ci / fork-checks (pull_request) Skipped
ci / build (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 7m47s
Security reports now go to [email protected] and code-of-conduct reports to [email protected], replacing a personal address. Written in the same obfuscated form as before.
2026-10-06 00:22:10 -07:00
jcoffey-dev ce8284df6c Merge pull request 'Name Coffey Labs LLC as the copyright holder' (#157) from chore/copyright-coffey-labs-llc into main
ci / fork-checks (push) Skipped
ci / build (push) Skipped
github/ci (branch) GitHub Actions
ci / github (push) Successful in 43m21s
2026-10-06 06:42:02 +00:00
jcoffey-dev 966241070e Name Coffey Labs LLC as the copyright holder
ci / fork-checks (pull_request) Skipped
ci / build (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 6m29s
Coffey Labs is now Coffey Labs LLC, an Arizona limited liability company. Copyright lines and SPDX-FileCopyrightText headers naming Coffey Labs or John Coffey now name Coffey Labs LLC. Upstream copyright notices are unchanged.
2026-10-05 23:33:00 -07:00
jcoffey-dev b25258330c Give the spam classifier test its own upload limits
ci / fork-checks (pull_request) Skipped
ci / build (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 7m48s
The quota test, which runs earlier in the same system suite, leaves
uploads expiring after one second, at most three at a time, and the
spam classifier test inherited that. It imports twenty samples, each an
upload followed by an import; when the step between them takes longer
than a second, the import fails with BlobNotFound.

After the v0.16.25 merge this failed in two runs out of three, where
main passed three out of three. Putting back the old midnight rounding
of a training sample's deadline (upstream now uses now() + hold, which
is now() in this suite since the purge test holds samples for zero
seconds) made it pass, so that change is what tips the timing; exactly
how it slows the import loop wasn't traced.

The test now resets the upload TTL, count and quota to their defaults
before it starts. Production isn't affected: uploads are kept for an
hour by default and samples for ninety days.
2026-10-05 22:16:30 -07:00
jcoffey-dev db4135e481 Merge upstream v0.16.25
Brings in the stripped v0.16.25 snapshot (72f8ddd), a bug-fix release:
DKIM rotation (keys retired before their successor is published, keys
made under manual DNS never rotating, manual DNS activating an
unpublished key), IMAP answering failed logins with an untagged NO,
DNSBL scoring only the first return code and caching "not listed" for
24 hours, Pyzor digesting empty input, queue quotas with an empty match
never enforced, RocksDB's info log growing without limit, MaskedEmail
creation with several domains, and Autodiscover answering other schemas
with the Outlook settings.

Conflicts:
- crates/main/Cargo.toml: inbuxa's name and AGPL-only license, version
  0.16.25.
- SECURITY.md and .github/PULL_REQUEST_TEMPLATE.md: inbuxa's own.
- Cargo.lock: upstream's, re-resolved against inbuxa's manifests.
- crates/common/src/network/autoconfig/autodiscover.rs: upstream now
  parses the request into a struct, so the legacy-protocol switch
  (LP-7) reads the address from request.email; ActiveSync and other
  schemas get upstream's error 601 untouched.
- resources/schema: unchanged upstream apart from two labels the rename
  pass now covers, which main already had.

AGENTS.md, new upstream, is left out: it is about contributing to
upstream, which doesn't apply to this repository.
2026-10-05 21:20:26 -07:00
jcoffey-dev 72f8ddd5e7 Import upstream v0.16.25, stripped
Upstream commit: 3f657330c0f49a015a3a372fb59669b5cccbca6d
Enterprise-only files removed or emptied: 63
Enterprise-only snippets removed: 118 in 50 files
Dangling module declarations removed: 5
Edits turning enterprise off: 25
Third-party code: 14 files, 0 not in THIRD-PARTY.md
Renamed identifiers: 67 in 19 files
Verification: clean

The same Enterprise footprint as v0.16.24. The build check is clean
apart from the expected errors in the rebuilt-feature tests. A
bug-fix release: DKIM rotation, IMAP failed-login answers, DNSBL
multi-code scoring and negative TTLs, Pyzor on short messages, queue
quotas with an empty match, RocksDB info-log rotation, and
Autodiscover schema handling.
2026-10-05 21:15:02 -07:00
jcoffey-dev 88e756bc3a Merge pull request 'Release 2026.10.6' (#154) from release/2026.10.6-pr into main
ci / fork-checks (push) Skipped
ci / build (push) Skipped
publish / version (push) Skipped
publish / publish-amd64 (push) Skipped
publish / publish-arm64 (push) Skipped
publish / release (push) Skipped
publish / binaries (push) Skipped
github/ci (branch) GitHub Actions
ci / github (push) Successful in 39m48s
announce / announce (release) Successful in 10s
github/ci (tag) GitHub Actions
publish / github (push) Successful in 1h6m10s
publish / announce (push) Failing after 8s
2026-10-06 03:27:45 +00:00
jcoffey-dev f77d171063 Release 2026.10.6
ci / fork-checks (pull_request) Skipped
ci / build (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 8m6s
2026-10-05 20:19:28 -07:00
jcoffey-dev 79db6c537b Merge pull request 'Domains menu: Deliverability' (#153) from feat/deliverability-menu into main
ci / fork-checks (push) Skipped
ci / build (push) Skipped
github/ci (branch) GitHub Actions
ci / github (push) Successful in 40m48s
2026-10-06 01:28:09 +00:00
jcoffey-dev ca4bf75c1b Domains menu: Deliverability, after DKIM Signatures
ci / fork-checks (pull_request) Skipped
ci / build (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 6m50s
The console's Domains › Deliverability page (deliverability spec, DL-17),
for the reports #150 added.
2026-10-05 16:33:17 -07:00
318 changed files with 1851 additions and 748 deletions

No files matched your search

-5
View File
@@ -5,11 +5,6 @@
version: 2 version: 2
updates: updates:
- package-ecosystem: "cargo" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
# Enable version updates for GitHub Actions # Enable version updates for GitHub Actions
- package-ecosystem: "github-actions" - package-ecosystem: "github-actions"
# Workflow files stored in the default location of `.github/workflows` # Workflow files stored in the default location of `.github/workflows`
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Close issues from non-allowed authors - name: Close issues from non-allowed authors
uses: actions/github-script@v7 uses: actions/github-script@v9
with: with:
script: | script: |
// Users allowed to open issues directly. All other authors will have // Users allowed to open issues directly. All other authors will have
@@ -18,7 +18,7 @@ jobs:
sparse-checkout-cone-mode: false sparse-checkout-cone-mode: false
- name: Close PRs from non-allowed authors - name: Close PRs from non-allowed authors
uses: actions/github-script@v7 uses: actions/github-script@v9
with: with:
script: | script: |
const fs = require('fs'); const fs = require('fs');
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Post support portal redirect - name: Post support portal redirect
uses: actions/github-script@v7 uses: actions/github-script@v9
with: with:
script: | script: |
const discussion = context.payload.discussion; const discussion = context.payload.discussion;
+1 -1
View File
@@ -73,6 +73,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard (optional). # Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard # Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning" - name: "Upload to code-scanning"
uses: github/codeql-action/[email protected]7.4 uses: github/codeql-action/[email protected]8.2
with: with:
sarif_file: results.sarif sarif_file: results.sarif
+1 -1
View File
@@ -36,6 +36,6 @@ jobs:
severity: 'CRITICAL,HIGH' severity: 'CRITICAL,HIGH'
- name: Upload Trivy scan results to GitHub Security tab - name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/[email protected]7.4 uses: github/codeql-action/[email protected]8.2
with: with:
sarif_file: 'trivy-results.sarif' sarif_file: 'trivy-results.sarif'
+27
View File
@@ -2,6 +2,33 @@
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).
## [0.16.25] - 2026-10-05
If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If you are upgrading from v0.15.x and below, please read the [upgrading documentation](https://github.com/stalwartlabs/stalwart/blob/main/UPGRADING/v0_16.md) for more information on how to upgrade from previous versions.
## Added
## Changed
## Fixed
- JMAP: Creating a `MaskedEmail` with `emailDomain` fails with `forbidden` for every domain when the account has addresses on more than one domain.
- Autodiscover: Requests for a response schema other than Outlook's, such as ActiveSync (`mobilesync`), are answered with the Outlook settings instead of error 601.
- IMAP:
- `LOGIN` and `AUTHENTICATE` with a wrong, expired or unknown app password or API key are answered with an untagged `NO`, so clients keep waiting for the command to complete until the connection times out.
- The failed login that exceeds the maximum number of authentication failures is answered with an untagged `NO` before the connection is closed.
- DKIM:
- A rotation moves the active key to retiring even when its successor fails to publish or propagate, so outgoing mail is sent unsigned until a retry publishes the new key. The DNS write failure is also not logged and the task reports success.
- Keys created while DNS management was manual, or before DKIM was added to the published records, are never rotated after DNS management becomes automatic. Domains already affected start rotating once a `DkimManagement` task is created for them.
- After switching DNS management from automatic to manual, a due rotation activates a new key that was never published in DNS, so signatures fail verification, and retiring the old key is retried forever.
- Spam filter:
- Messages with no text line long enough for a Pyzor digest are checked with the digest of empty input and tagged `PYZOR`.
- DNSBL answers with several return codes, such as a Spamhaus ZEN listing in both SBL and PBL, are scored for only the first code returned.
- DNSBL lookups that return "not listed" are cached for 24 hours regardless of the zone's negative TTL.
- Removing a duplicate training sample of a message reclassified on the same day clears the blob link of the sample that is kept.
- MTA: Queue quotas with an empty `match` expression are never enforced, including the global queue quota created on first start.
- RocksDB: The info log (`LOG`, `LOG.old.*`) grows without limit because log rotation and retention are left at RocksDB defaults.
- WebUI: A blob store read error at startup, such as an S3 authentication failure, stops the web interface from being downloaded.
## [0.16.24] - 2026-09-27 ## [0.16.24] - 2026-09-27
If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If you are upgrading from v0.15.x and below, please read the [upgrading documentation](https://github.com/stalwartlabs/stalwart/blob/main/UPGRADING/v0_16.md) for more information on how to upgrade from previous versions. If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If you are upgrading from v0.15.x and below, please read the [upgrading documentation](https://github.com/stalwartlabs/stalwart/blob/main/UPGRADING/v0_16.md) for more information on how to upgrade from previous versions.
+1 -1
View File
@@ -60,7 +60,7 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at reported to the community leaders responsible for enforcement at
**johnellisATlinuxDOTcom**. **communityATcoffeylabsDOTorg**.
All complaints will be reviewed and investigated promptly and fairly. All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the All community leaders are obligated to respect the privacy and security of the
+1 -1
View File
@@ -54,7 +54,7 @@ Coffey Labs" line in place. New files carry:
``` ```
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
Generated
+79 -69
View File
@@ -277,9 +277,9 @@ dependencies = [
[[package]] [[package]]
name = "async-compression" name = "async-compression"
version = "0.4.48" version = "0.4.50"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb61aea1a7def73ee7c350a184f0e70b32c182344e2e75bf70c9b621b83417fd" checksum = "ee19bd99b43e3691acbad4e840420a4881cea6c0b66a208125a824f8fd53f5a1"
dependencies = [ dependencies = [
"compression-codecs", "compression-codecs",
"compression-core", "compression-core",
@@ -1292,7 +1292,7 @@ dependencies = [
[[package]] [[package]]
name = "common" name = "common"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"aes-gcm-siv", "aes-gcm-siv",
"ahash", "ahash",
@@ -1392,9 +1392,9 @@ dependencies = [
[[package]] [[package]]
name = "compression-codecs" name = "compression-codecs"
version = "0.4.43" version = "0.4.45"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef16c47ba2797aa6a909cc37d39911f3a6743811fe7408ac0b0cc0276b656e9" checksum = "98fc98460ba0ad5317075d3632b8dfc45d0be8c4a49347c2a38272019717614a"
dependencies = [ dependencies = [
"compression-core", "compression-core",
"flate2", "flate2",
@@ -1477,7 +1477,7 @@ checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
[[package]] [[package]]
name = "coordinator" name = "coordinator"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"async-nats", "async-nats",
"futures", "futures",
@@ -1889,7 +1889,7 @@ checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06"
[[package]] [[package]]
name = "dav" name = "dav"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"calcard", "calcard",
"chrono", "chrono",
@@ -1912,7 +1912,7 @@ dependencies = [
[[package]] [[package]]
name = "dav-proto" name = "dav-proto"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"calcard", "calcard",
"chrono", "chrono",
@@ -2125,7 +2125,7 @@ dependencies = [
[[package]] [[package]]
name = "directory" name = "directory"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"ahash", "ahash",
"argon2 0.6.0", "argon2 0.6.0",
@@ -2366,7 +2366,7 @@ dependencies = [
[[package]] [[package]]
name = "email" name = "email"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"aes 0.9.3", "aes 0.9.3",
"aes-gcm 0.11.1", "aes-gcm 0.11.1",
@@ -2406,6 +2406,16 @@ dependencies = [
"log", "log",
] ]
[[package]]
name = "encodify"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "798c447647dd23f673748f2b868ef309a01dd86aaae999182559d36f06ac82f0"
dependencies = [
"memchr",
"simdutf8",
]
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.42" version = "0.8.42"
@@ -2474,7 +2484,7 @@ dependencies = [
[[package]] [[package]]
name = "event_macro" name = "event_macro"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"quote", "quote",
"syn 3.0.6", "syn 3.0.6",
@@ -3002,7 +3012,7 @@ dependencies = [
[[package]] [[package]]
name = "groupware" name = "groupware"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"ahash", "ahash",
"calcard", "calcard",
@@ -3289,7 +3299,7 @@ dependencies = [
[[package]] [[package]]
name = "http" name = "http"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"async-stream", "async-stream",
"base64 0.23.1", "base64 0.23.1",
@@ -3385,7 +3395,7 @@ dependencies = [
[[package]] [[package]]
name = "http_proto" name = "http_proto"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"common", "common",
"compact_str", "compact_str",
@@ -3872,7 +3882,7 @@ checksum = "65b27460c2c92b037f3f94c538ed9a3342f3fdf923606781629ccb35f82d042a"
[[package]] [[package]]
name = "imap" name = "imap"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"ahash", "ahash",
"common", "common",
@@ -3897,7 +3907,7 @@ dependencies = [
[[package]] [[package]]
name = "imap_proto" name = "imap_proto"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"ahash", "ahash",
"base64 0.23.1", "base64 0.23.1",
@@ -3912,7 +3922,7 @@ dependencies = [
[[package]] [[package]]
name = "inbuxa" name = "inbuxa"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"common", "common",
"coordinator", "coordinator",
@@ -3920,7 +3930,7 @@ dependencies = [
"directory", "directory",
"email", "email",
"groupware", "groupware",
"http 0.16.24", "http 0.16.25",
"http_proto", "http_proto",
"imap", "imap",
"jmap", "jmap",
@@ -4209,7 +4219,7 @@ dependencies = [
[[package]] [[package]]
name = "jmap" name = "jmap"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"async-stream", "async-stream",
"base64 0.23.1", "base64 0.23.1",
@@ -4258,14 +4268,14 @@ dependencies = [
[[package]] [[package]]
name = "jmap-client" name = "jmap-client"
version = "0.4.2" version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4deab22e057d24e32122f0fc6e2d667a124fdd6a0d8ef3ed4f8a89923c11084f" checksum = "f5b5bc66252cc8e779ef1238f40ab93971d54ad00b5d7afb5c1d447a9647ed62"
dependencies = [ dependencies = [
"ahash", "ahash",
"async-stream", "async-stream",
"base64 0.22.1",
"chrono", "chrono",
"encodify",
"futures-util", "futures-util",
"maybe-async", "maybe-async",
"parking_lot", "parking_lot",
@@ -4292,7 +4302,7 @@ dependencies = [
[[package]] [[package]]
name = "jmap_proto" name = "jmap_proto"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"ahash", "ahash",
"calcard", "calcard",
@@ -4502,9 +4512,9 @@ dependencies = [
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.5.0" version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" checksum = "20870f649af7073d53e38067b2a84312175d56ea15217e1b15bc83506ec50afb"
dependencies = [ dependencies = [
"spin 0.9.9", "spin 0.9.9",
] ]
@@ -4798,7 +4808,7 @@ dependencies = [
[[package]] [[package]]
name = "managesieve" name = "managesieve"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"common", "common",
"compact_str", "compact_str",
@@ -4933,7 +4943,7 @@ checksum = "c797b9d6bb23aab2fc369c65f871be49214f5c759af65bde26ffaaa2b646b492"
[[package]] [[package]]
name = "migration" name = "migration"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"common", "common",
"email", "email",
@@ -5183,7 +5193,7 @@ dependencies = [
[[package]] [[package]]
name = "nlp" name = "nlp"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"ahash", "ahash",
"hashify", "hashify",
@@ -5503,8 +5513,8 @@ dependencies = [
[[package]] [[package]]
name = "opentelemetry" name = "opentelemetry"
version = "0.32.0" version = "0.33.0"
source = "git+https://github.com/stalwartlabs/opentelemetry-rust#80a14a3b6846f62f85506d68d2600c948fccc9d2" source = "git+https://github.com/stalwartlabs/opentelemetry-rust#ae66e97b140f70e477ab710686aafce665cc2f8b"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-sink", "futures-sink",
@@ -5516,8 +5526,8 @@ dependencies = [
[[package]] [[package]]
name = "opentelemetry-http" name = "opentelemetry-http"
version = "0.32.0" version = "0.33.0"
source = "git+https://github.com/stalwartlabs/opentelemetry-rust#80a14a3b6846f62f85506d68d2600c948fccc9d2" source = "git+https://github.com/stalwartlabs/opentelemetry-rust#ae66e97b140f70e477ab710686aafce665cc2f8b"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"bytes", "bytes",
@@ -5528,8 +5538,8 @@ dependencies = [
[[package]] [[package]]
name = "opentelemetry-otlp" name = "opentelemetry-otlp"
version = "0.32.0" version = "0.33.0"
source = "git+https://github.com/stalwartlabs/opentelemetry-rust#80a14a3b6846f62f85506d68d2600c948fccc9d2" source = "git+https://github.com/stalwartlabs/opentelemetry-rust#ae66e97b140f70e477ab710686aafce665cc2f8b"
dependencies = [ dependencies = [
"http 1.5.0", "http 1.5.0",
"httpdate", "httpdate",
@@ -5547,8 +5557,8 @@ dependencies = [
[[package]] [[package]]
name = "opentelemetry-proto" name = "opentelemetry-proto"
version = "0.32.0" version = "0.33.0"
source = "git+https://github.com/stalwartlabs/opentelemetry-rust#80a14a3b6846f62f85506d68d2600c948fccc9d2" source = "git+https://github.com/stalwartlabs/opentelemetry-rust#ae66e97b140f70e477ab710686aafce665cc2f8b"
dependencies = [ dependencies = [
"opentelemetry", "opentelemetry",
"opentelemetry_sdk", "opentelemetry_sdk",
@@ -5559,13 +5569,13 @@ dependencies = [
[[package]] [[package]]
name = "opentelemetry-semantic-conventions" name = "opentelemetry-semantic-conventions"
version = "0.32.1" version = "0.33.0"
source = "git+https://github.com/stalwartlabs/opentelemetry-rust#80a14a3b6846f62f85506d68d2600c948fccc9d2" source = "git+https://github.com/stalwartlabs/opentelemetry-rust#ae66e97b140f70e477ab710686aafce665cc2f8b"
[[package]] [[package]]
name = "opentelemetry_sdk" name = "opentelemetry_sdk"
version = "0.32.1" version = "0.33.0"
source = "git+https://github.com/stalwartlabs/opentelemetry-rust#80a14a3b6846f62f85506d68d2600c948fccc9d2" source = "git+https://github.com/stalwartlabs/opentelemetry-rust#ae66e97b140f70e477ab710686aafce665cc2f8b"
dependencies = [ dependencies = [
"futures-channel", "futures-channel",
"futures-executor", "futures-executor",
@@ -6015,7 +6025,7 @@ dependencies = [
[[package]] [[package]]
name = "pop3" name = "pop3"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"common", "common",
"directory", "directory",
@@ -6385,9 +6395,9 @@ dependencies = [
[[package]] [[package]]
name = "quinn-proto" name = "quinn-proto"
version = "0.11.18" version = "0.11.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9746dbde176634f4f2f1faf2404e30a31b2bc1e9cafb5329c95d8177a18c9fc" checksum = "0e750cca55fe4f0439a15d0bb529da9651e79993e8e72c61a899a36d462befbe"
dependencies = [ dependencies = [
"aws-lc-rs", "aws-lc-rs",
"bytes", "bytes",
@@ -6410,9 +6420,9 @@ dependencies = [
[[package]] [[package]]
name = "quinn-udp" name = "quinn-udp"
version = "0.5.15" version = "0.5.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" checksum = "af66907df18639dcf4db56ca65490cabc4b27a97dbadd96f2926cca73298f016"
dependencies = [ dependencies = [
"cfg_aliases", "cfg_aliases",
"libc", "libc",
@@ -6835,7 +6845,7 @@ checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
[[package]] [[package]]
name = "registry" name = "registry"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"ahash", "ahash",
"hashify", "hashify",
@@ -7392,7 +7402,7 @@ dependencies = [
[[package]] [[package]]
name = "scim" name = "scim"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"ahash", "ahash",
"base64 0.23.1", "base64 0.23.1",
@@ -7418,7 +7428,7 @@ dependencies = [
[[package]] [[package]]
name = "scim-proto" name = "scim-proto"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"hashify", "hashify",
"serde", "serde",
@@ -7672,9 +7682,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_with" name = "serde_with"
version = "3.23.0" version = "3.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "935177bb8c0cd8ca1a4e6d1a2ac8988bea69cab4f9d3a31311e012ad27868ea4" checksum = "df9adc193c780ef8f159aee8b61e2d5801aaa555e6eb0947fe45530ec506296f"
dependencies = [ dependencies = [
"base64 0.23.1", "base64 0.23.1",
"bs58", "bs58",
@@ -7693,9 +7703,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_with_macros" name = "serde_with_macros"
version = "3.23.0" version = "3.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d607aa01a3cb0ad757d6fd216136910db3c97b102fe686585689615a02dbcdc" checksum = "3e17bbc68e28663bbbb90df47e058aa7eda4fb445b89fe70457bb94fbccf6e49"
dependencies = [ dependencies = [
"darling 0.24.1", "darling 0.24.1",
"proc-macro2", "proc-macro2",
@@ -7753,7 +7763,7 @@ dependencies = [
[[package]] [[package]]
name = "services" name = "services"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"aes-gcm 0.11.1", "aes-gcm 0.11.1",
"aho-corasick", "aho-corasick",
@@ -8068,7 +8078,7 @@ checksum = "f9395f0f0eee849a9b707b2f06bb92a6a422090e2123bb2ef8e87a0e61892a8e"
[[package]] [[package]]
name = "smtp" name = "smtp"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"ahash", "ahash",
"base64 0.23.1", "base64 0.23.1",
@@ -8107,9 +8117,9 @@ dependencies = [
[[package]] [[package]]
name = "smtp-proto" name = "smtp-proto"
version = "0.2.4" version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707104487221ff447b5b796b5049e5c09cf52ff9fc1c8abba4695b89ac4b0f37" checksum = "142a5a642c6bd7ffd7e1b525ad6a6e9921ccef992dba4663974f8519209a00c1"
dependencies = [ dependencies = [
"memchr", "memchr",
"rkyv", "rkyv",
@@ -8159,7 +8169,7 @@ dependencies = [
[[package]] [[package]]
name = "spam-filter" name = "spam-filter"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"common", "common",
"compact_str", "compact_str",
@@ -8279,7 +8289,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]] [[package]]
name = "store" name = "store"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"ahash", "ahash",
"arc-swap", "arc-swap",
@@ -8539,7 +8549,7 @@ dependencies = [
[[package]] [[package]]
name = "tests" name = "tests"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"ahash", "ahash",
"aws-lc-rs", "aws-lc-rs",
@@ -8561,7 +8571,7 @@ dependencies = [
"form_urlencoded", "form_urlencoded",
"futures", "futures",
"groupware", "groupware",
"http 0.16.24", "http 0.16.25",
"http_proto", "http_proto",
"hyper", "hyper",
"hyper-util", "hyper-util",
@@ -8818,9 +8828,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio-rustls" name = "tokio-rustls"
version = "0.26.5" version = "0.26.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c85f2c3ef0b1cd58b36682f4b17aaa995f0e5db534d85692b4903abce21f67" checksum = "c9cc2678c2cdd569ef8215e2afd7954ada2ae20b4fdd2c5fe6139a3b02d105db"
dependencies = [ dependencies = [
"rustls", "rustls",
"tokio", "tokio",
@@ -9154,7 +9164,7 @@ dependencies = [
[[package]] [[package]]
name = "trc" name = "trc"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"ahash", "ahash",
"base64 0.23.1", "base64 0.23.1",
@@ -9263,7 +9273,7 @@ checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]] [[package]]
name = "types" name = "types"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"blake3", "blake3",
"compact_str", "compact_str",
@@ -9432,7 +9442,7 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]] [[package]]
name = "utils" name = "utils"
version = "0.16.24" version = "0.16.25"
dependencies = [ dependencies = [
"ahash", "ahash",
"arcstr", "arcstr",
@@ -10117,9 +10127,9 @@ dependencies = [
[[package]] [[package]]
name = "xxhash-rust" name = "xxhash-rust"
version = "0.8.18" version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aee1b19627c7c60102ab80d3a9cbe18de90bfe03bfa6c3715447681f0e8c8af6" checksum = "550a2b930b62486a393c52d5c3b84bff264b28aa437ed64694d31e93b1757af7"
[[package]] [[package]]
name = "yasna" name = "yasna"
@@ -10144,9 +10154,9 @@ dependencies = [
[[package]] [[package]]
name = "yoke-derive" name = "yoke-derive"
version = "0.8.3" version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33811428bee40dbceb6d545e95754741d17a6aef9a4849f0fd62e2ba4f412a78" checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
+1 -1
View File
@@ -4,7 +4,7 @@
# ***************** # *****************
# Base image for planner & builder # Base image for planner & builder
# ***************** # *****************
FROM --platform=$BUILDPLATFORM rust:slim-trixie AS base FROM --platform=$BUILDPLATFORM rust:1.98.1-slim-trixie AS base
ENV DEBIAN_FRONTEND="noninteractive" \ ENV DEBIAN_FRONTEND="noninteractive" \
BINSTALL_DISABLE_TELEMETRY=true \ BINSTALL_DISABLE_TELEMETRY=true \
+1 -1
View File
@@ -17,7 +17,7 @@ visible to everyone, including whoever would use it, before there is a fix.
Report it privately by email to: Report it privately by email to:
**johnellisATlinuxDOTcom** **securityATcoffeylabsDOTorg**
Include as much as you can of: Include as much as you can of:
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "common" name = "common"
version = "0.16.24" version = "0.16.25"
edition = "2024" edition = "2024"
build = "build.rs" build = "build.rs"
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -225,7 +225,7 @@ pub struct Caches {
pub dns_ipv6: CacheWithTtl<Box<str>, RecordSet<Ipv6Addr>>, pub dns_ipv6: CacheWithTtl<Box<str>, RecordSet<Ipv6Addr>>,
pub dns_tlsa: CacheWithTtl<Box<str>, Arc<Tlsa>>, pub dns_tlsa: CacheWithTtl<Box<str>, Arc<Tlsa>>,
pub dns_mta_sts: CacheWithTtl<Box<str>, Arc<Policy>>, pub dns_mta_sts: CacheWithTtl<Box<str>, Arc<Policy>>,
pub dns_rbl: CacheWithTtl<Box<str>, Option<Arc<IpResolver>>>, pub dns_rbl: CacheWithTtl<Box<str>, Option<Arc<[IpResolver]>>>,
pub negative_cache_ttl: Duration, pub negative_cache_ttl: Duration,
} }
+14 -2
View File
@@ -227,10 +227,22 @@ impl WebApplicationManager {
let cached = if force_refresh { let cached = if force_refresh {
None None
} else { } else {
server match server
.blob_store() .blob_store()
.get_blob(self.blob_key.as_slice(), 0..usize::MAX) .get_blob(self.blob_key.as_slice(), 0..usize::MAX)
.await? .await
{
Ok(cached) => cached,
Err(err) => {
trc::event!(
Resource(trc::ResourceEvent::Error),
Reason = err,
Url = self.url.clone(),
Details = "Failed to read cached application bundle, downloading it again"
);
None
}
}
}; };
let is_cached = cached.is_some(); let is_cached = cached.is_some();
let bundle = match cached { let bundle = match cached {
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,6 +1,6 @@
/* /*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]> * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
* *
@@ -11,7 +11,7 @@ use quick_xml::Reader;
use quick_xml::XmlVersion; use quick_xml::XmlVersion;
use quick_xml::events::Event; use quick_xml::events::Event;
use registry::schema::{enums::ServiceProtocol, structs::Service}; use registry::schema::{enums::ServiceProtocol, structs::Service};
use std::fmt::Write; use std::{borrow::Cow, fmt::Write};
use utils::map::vec_map::VecMap; use utils::map::vec_map::VecMap;
impl Server { impl Server {
@@ -20,32 +20,78 @@ impl Server {
body: Option<Vec<u8>>, body: Option<Vec<u8>>,
) -> trc::Result<Resource<Vec<u8>>> { ) -> trc::Result<Resource<Vec<u8>>> {
// Obtain parameters // Obtain parameters
let emailaddress = parse_autodiscover_request(body.as_deref().unwrap_or_default()) let request =
.map_err(|err| { parse_autodiscover_request(body.as_deref().unwrap_or_default()).map_err(|err| {
trc::ResourceEvent::BadParameters trc::ResourceEvent::BadParameters
.into_err() .into_err()
.details("Failed to parse autodiscover request") .details("Failed to parse autodiscover request")
.ctx(trc::Key::Reason, err) .ctx(trc::Key::Reason, err)
})?; })?;
// inbuxa: legacy-protocols LP-7, LP-14a // inbuxa: legacy-protocols LP-7, LP-14a
let legacy_off = match emailaddress.rsplit_once('@') { let legacy_off = match request.email.rsplit_once('@') {
Some((_, domain)) => self.legacy_off_for(domain).await?, Some((_, domain)) => self.legacy_off_for(domain).await?,
None => self.legacy_off_for("").await?, None => self.legacy_off_for("").await?,
}; };
Ok(Resource::new( let response = match request.response_schema {
"application/xml; charset=utf-8", ResponseSchema::Outlook => build_autodiscover_response(
build_autodiscover_response( &request.email,
&emailaddress,
&self.core.network.server_name, &self.core.network.server_name,
&self.core.network.info.services, &self.core.network.info.services,
|protocol| legacy_off.service(protocol), |protocol| legacy_off.service(protocol),
) )
.into_bytes(), .into_bytes(),
)) ResponseSchema::Unsupported => PROVIDER_NOT_AVAILABLE_RESPONSE.as_bytes().to_vec(),
};
Ok(Resource::new("application/xml; charset=utf-8", response))
} }
} }
const OUTLOOK_RESPONSE_SCHEMA: &str =
"http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a";
const PROVIDER_NOT_AVAILABLE_RESPONSE: &str = concat!(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
"<Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006\">\n",
"\t<Response>\n",
"\t\t<Error>\n",
"\t\t\t<ErrorCode>601</ErrorCode>\n",
"\t\t\t<Message>Provider is not available</Message>\n",
"\t\t\t<DebugData />\n",
"\t\t</Error>\n",
"\t</Response>\n",
"</Autodiscover>\n",
);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ResponseSchema {
Outlook,
Unsupported,
}
impl ResponseSchema {
fn parse(value: &str) -> Self {
if value.trim().eq_ignore_ascii_case(OUTLOOK_RESPONSE_SCHEMA) {
ResponseSchema::Outlook
} else {
ResponseSchema::Unsupported
}
}
}
#[derive(Debug, PartialEq, Eq)]
struct AutodiscoverRequest {
email: String,
response_schema: ResponseSchema,
}
#[derive(Clone, Copy)]
enum RequestField {
EmailAddress,
ResponseSchema,
}
fn build_autodiscover_response( fn build_autodiscover_response(
emailaddress: &str, emailaddress: &str,
default_host: &str, default_host: &str,
@@ -124,7 +170,7 @@ fn build_autodiscover_response(
config config
} }
fn parse_autodiscover_request(bytes: &[u8]) -> Result<String, String> { fn parse_autodiscover_request(bytes: &[u8]) -> Result<AutodiscoverRequest, String> {
if bytes.is_empty() { if bytes.is_empty() {
return Err("Empty request body".to_string()); return Err("Empty request body".to_string());
} }
@@ -132,8 +178,9 @@ fn parse_autodiscover_request(bytes: &[u8]) -> Result<String, String> {
let mut reader = Reader::from_reader(bytes); let mut reader = Reader::from_reader(bytes);
reader.config_mut().trim_text(true); reader.config_mut().trim_text(true);
let mut buf = Vec::with_capacity(128); let mut buf = Vec::with_capacity(128);
let mut value_buf = Vec::with_capacity(128);
'outer: for tag_name in ["Autodiscover", "Request", "EMailAddress"] { 'outer: for tag_name in ["Autodiscover", "Request"] {
loop { loop {
match reader.read_event_into(&mut buf) { match reader.read_event_into(&mut buf) {
Ok(Event::Start(e)) => { Ok(Event::Start(e)) => {
@@ -143,30 +190,6 @@ fn parse_autodiscover_request(bytes: &[u8]) -> Result<String, String> {
.eq_ignore_ascii_case(found_tag_name.as_ref()) .eq_ignore_ascii_case(found_tag_name.as_ref())
{ {
continue 'outer; continue 'outer;
} else if tag_name == "EMailAddress" {
// Skip unsupported tags under Request, such as AcceptableResponseSchema
let mut tag_count = 0;
loop {
match reader.read_event_into(&mut buf) {
Ok(Event::End(_)) => {
if tag_count == 0 {
break;
} else {
tag_count -= 1;
}
}
Ok(Event::Start(_)) => {
tag_count += 1;
}
Ok(Event::Eof) => {
return Err(format!(
"Expected value, found unexpected EOF at position {}.",
reader.buffer_position()
));
}
_ => (),
}
}
} else { } else {
return Err(format!( return Err(format!(
"Expected tag {}, found unexpected tag {} at position {}.", "Expected tag {}, found unexpected tag {} at position {}.",
@@ -195,36 +218,170 @@ fn parse_autodiscover_request(bytes: &[u8]) -> Result<String, String> {
} }
} }
if let Ok(Event::Text(text)) = reader.read_event_into(&mut buf) let mut email = None;
&& let Ok(text) = text.xml_content(XmlVersion::Implicit1_0) let mut response_schema = ResponseSchema::Outlook;
&& text.contains('@')
{ loop {
return Ok(text.trim().to_lowercase()); match reader.read_event_into(&mut buf) {
Ok(Event::Start(e)) => {
let local_name = e.local_name();
let field = hashify::tiny_map_ignore_case!(local_name.as_ref(),
b"EMailAddress" => RequestField::EmailAddress,
b"AcceptableResponseSchema" => RequestField::ResponseSchema,
);
let value = match reader.read_event_into(&mut value_buf) {
Ok(Event::End(_)) => None,
Ok(event) => {
let value = match event {
Event::Text(text) => text
.xml_content(XmlVersion::Implicit1_0)
.ok()
.map(Cow::into_owned),
_ => None,
};
reader
.read_to_end_into(e.name(), &mut value_buf)
.map_err(|err| {
format!("Error at position {}: {:?}", reader.buffer_position(), err)
})?;
value
}
Err(err) => {
return Err(format!(
"Error at position {}: {:?}",
reader.buffer_position(),
err
));
}
};
match (field, value) {
(Some(RequestField::EmailAddress), Some(value)) => {
email = Some(value);
}
(Some(RequestField::ResponseSchema), Some(value)) => {
response_schema = ResponseSchema::parse(&value);
}
_ => (),
}
}
Ok(Event::End(_) | Event::Eof) => break,
Ok(_) => (),
Err(e) => {
return Err(format!(
"Error at position {}: {:?}",
reader.buffer_position(),
e
));
}
}
} }
Err(format!( match email {
"Expected email address, found unexpected value at position {}.", Some(email) if email.contains('@') => Ok(AutodiscoverRequest {
reader.buffer_position() email: email.trim().to_lowercase(),
)) response_schema,
}),
_ => Err(format!(
"Expected email address, found unexpected value at position {}.",
reader.buffer_position()
)),
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{AutodiscoverRequest, ResponseSchema, parse_autodiscover_request};
#[test] #[test]
fn parse_autodiscover() { fn parse_autodiscover() {
let r = r#"<?xml version="1.0" encoding="utf-8"?> const OUTLOOK: &str =
"http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a";
const MOBILESYNC: &str =
"http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006";
for (request, expected) in [
(
format!(
r#"<?xml version="1.0" encoding="utf-8"?>
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006"> <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
<Request> <Request>
<EMailAddress>email@example.com</EMailAddress> <EMailAddress>Email@Example.com</EMailAddress>
<AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema> <AcceptableResponseSchema>{OUTLOOK}</AcceptableResponseSchema>
</Request> </Request>
</Autodiscover>"#; </Autodiscover>"#
),
ResponseSchema::Outlook,
),
(
format!(
r#"<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
<Request>
<AcceptableResponseSchema>{OUTLOOK}</AcceptableResponseSchema>
<EMailAddress>[email protected]</EMailAddress>
</Request>
</Autodiscover>"#
),
ResponseSchema::Outlook,
),
(
r#"<Autodiscover>
<Request>
<EMailAddress>[email protected]</EMailAddress>
</Request>
</Autodiscover>"#
.to_string(),
ResponseSchema::Outlook,
),
(
format!(
r#"<?xml version="1.0" encoding="utf-8"?>
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/mobilesync/requestschema/2006">
<Request>
<EMailAddress>[email protected]</EMailAddress>
<AcceptableResponseSchema>{MOBILESYNC}</AcceptableResponseSchema>
</Request>
</Autodiscover>"#
),
ResponseSchema::Unsupported,
),
(
format!(
r#"<Autodiscover>
<Request>
<LegacyDN>/o=Example/ou=Users/cn=email</LegacyDN>
<Unknown><Nested>value</Nested><Empty/></Unknown>
<AcceptableResponseSchema>{MOBILESYNC}</AcceptableResponseSchema>
<EMailAddress>[email protected]</EMailAddress>
</Request>
</Autodiscover>"#
),
ResponseSchema::Unsupported,
),
] {
assert_eq!(
parse_autodiscover_request(request.as_bytes()).expect("valid request"),
AutodiscoverRequest {
email: "[email protected]".to_string(),
response_schema: expected,
},
"{request}"
);
}
assert_eq!( for request in [
super::parse_autodiscover_request(r.as_bytes()).unwrap(), "",
"[email protected]" "<Autodiscover><Request></Request></Autodiscover>",
); "<Autodiscover><Request><EMailAddress>no-domain</EMailAddress></Request></Autodiscover>",
"<Autodiscover><Request><EMailAddress>[email protected]</Request></Autodiscover>",
"<Request><EMailAddress>[email protected]</EMailAddress></Request>",
] {
assert!(
parse_autodiscover_request(request.as_bytes()).is_err(),
"{request}"
);
}
} }
#[test] #[test]
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "coordinator" name = "coordinator"
version = "0.16.24" version = "0.16.25"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "dav-proto" name = "dav-proto"
version = "0.16.24" version = "0.16.25"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "dav" name = "dav"
version = "0.16.24" version = "0.16.25"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "directory" name = "directory"
version = "0.16.24" version = "0.16.25"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "email" name = "email"
version = "0.16.24" version = "0.16.25"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+2 -6
View File
@@ -20,7 +20,7 @@ use groupware::{
scheduling::{ItipError, ItipMessages}, scheduling::{ItipError, ItipMessages},
}; };
use mail_parser::{ use mail_parser::{
DateTime, Header, HeaderName, HeaderValue, Message, MessageParser, MimeHeaders, PartType, Header, HeaderName, HeaderValue, Message, MessageParser, MimeHeaders, PartType,
parsers::fields::thread::thread_name, parsers::fields::thread::thread_name,
}; };
use registry::{ use registry::{
@@ -924,11 +924,7 @@ impl EmailIngest for Server {
span_id: u64, span_id: u64,
) { ) {
if let Some(config) = &self.core.spam.classifier { if let Some(config) = &self.core.spam.classifier {
let mut dt = DateTime::from_timestamp(now() as i64); let until = now() + config.hold_samples_for;
dt.hour = 0;
dt.minute = 0;
dt.second = 0;
let until = dt.to_timestamp() as u64 + config.hold_samples_for;
let sample = SpamTrainingSample { let sample = SpamTrainingSample {
account_id: Some(Id::from(account_id)), account_id: Some(Id::from(account_id)),
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Coffey Labs * SPDX-FileCopyrightText: 2026 Coffey Labs LLC
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
Loaded 100 of 318 files, more files were not shown because too many files have changed in this diff. Show more