Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package go-sendxmpp for openSUSE:Factory 
checked in at 2026-07-28 17:56:59
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/go-sendxmpp (Old)
 and      /work/SRC/openSUSE:Factory/.go-sendxmpp.new.2004 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "go-sendxmpp"

Tue Jul 28 17:56:59 2026 rev:30 rq:1367927 version:0.17.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/go-sendxmpp/go-sendxmpp.changes  2026-07-07 
21:07:09.294491457 +0200
+++ /work/SRC/openSUSE:Factory/.go-sendxmpp.new.2004/go-sendxmpp.changes        
2026-07-28 18:03:47.884894149 +0200
@@ -1,0 +2,19 @@
+Mon Jul 27 07:01:18 UTC 2026 - Michael Vetter <[email protected]>
+
+- Update to 0.17.0:
+  Added:
+  * Add --ox-transfer-private-key to transfer the encrypted private key to PEP
+    to transfer it to other devices (requires go-xmpp >= v0.3.7).
+  * Add --ox-receive-private-key to receive the encrypted private key from PEP.
+  * Add config option no_root_warning.
+  * Add config option no_legacy_pgp_warning.
+  * Also disable legacy PGP when running as root (Ox was already disabled).
+  * Disable pinning for not using PLAIN when running as root.
+  * Add config option ox_trust_mode with settings blind and tofu.
+  Changed:
+  * Due to new tofu trust mode for Ox, only one public key per contact is 
accepted for easier ID handling.
+  * Ox: Check that fingerprint of received key equals the advertised one.
+  * CVE-2026-39821: Failure to reject ASCII-only Punycode-encoded labels 
allows for validation bypass and privilege escalation (bsc#1266617)
+    Bump net to 0.57.0
+
+-------------------------------------------------------------------
@@ -21,2 +39,0 @@
-  * CVE-2026-39821: Failure to reject ASCII-only Punycode-encoded labels 
allows for validation bypass and privilege escalation (bsc#1266617)
-    Bump net to 0.56.0

Old:
----
  go-sendxmpp-v0.16.0.tar.gz

New:
----
  go-sendxmpp-v0.17.0.tar.gz

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

Other differences:
------------------
++++++ go-sendxmpp.spec ++++++
--- /var/tmp/diff_new_pack.maIKK8/_old  2026-07-28 18:03:51.313014099 +0200
+++ /var/tmp/diff_new_pack.maIKK8/_new  2026-07-28 18:03:51.313014099 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           go-sendxmpp
-Version:        0.16.0
+Version:        0.17.0
 Release:        0
 Summary:        A little tool to send messages to an XMPP contact or MUC
 License:        BSD-2-Clause

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.maIKK8/_old  2026-07-28 18:03:51.373016199 +0200
+++ /var/tmp/diff_new_pack.maIKK8/_new  2026-07-28 18:03:51.385016618 +0200
@@ -1,6 +1,6 @@
-mtime: 1783404265
-commit: 55e82266f2336aff9f274818472c120c2ec9d17035846b4f25f3d4445124edae
+mtime: 1785135948
+commit: 2fe2da162f0cf1f4ccce5fbf6f2423185d09d733a6b5316f26b4bb82833684e0
 url: https://src.opensuse.org/xmpp/go-sendxmpp
-revision: 55e82266f2336aff9f274818472c120c2ec9d17035846b4f25f3d4445124edae
+revision: 2fe2da162f0cf1f4ccce5fbf6f2423185d09d733a6b5316f26b4bb82833684e0
 projectscmsync: https://src.opensuse.org/xmpp/_ObsPrj.git
 

++++++ build.specials.obscpio ++++++

++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore      1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore      2026-07-27 09:05:48.000000000 +0200
@@ -0,0 +1 @@
+.osc

++++++ go-sendxmpp-v0.16.0.tar.gz -> go-sendxmpp-v0.17.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/go-sendxmpp-v0.16.0/.gitlab-ci.yml 
new/go-sendxmpp-v0.17.0/.gitlab-ci.yml
--- old/go-sendxmpp-v0.16.0/.gitlab-ci.yml      2026-06-20 09:54:34.000000000 
+0200
+++ new/go-sendxmpp-v0.17.0/.gitlab-ci.yml      2026-07-24 21:50:15.000000000 
+0200
@@ -42,6 +42,7 @@
     - codespell *.go *.md man/*.ronn
     - gofumpt -d .
     - gosec ./...
+    - sh -c "! grep -r println *.go"
     # Taken from 
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20404/diffs#diff-content-8071267ea32ba69f24a8bd50bcbddf972c295ce3
     # Use default .golangci.yml file from the image if one is not present in 
the project root.
     #- '[ -e .golangci.yml ] || cp /golangci/.golangci.yml .'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/go-sendxmpp-v0.16.0/CHANGELOG.md 
new/go-sendxmpp-v0.17.0/CHANGELOG.md
--- old/go-sendxmpp-v0.16.0/CHANGELOG.md        2026-06-20 09:54:34.000000000 
+0200
+++ new/go-sendxmpp-v0.17.0/CHANGELOG.md        2026-07-24 21:50:15.000000000 
+0200
@@ -1,4 +1,18 @@
 # Changelog
+## [v0.17.0] 2026-07-24
+### Added
+- Add `--ox-transfer-private-key` to transfer the encrypted private key to PEP 
to transfer it to other devices (requires go-xmpp >= v0.3.7).
+- Add `--ox-receive-private-key` to receive the encrypted private key from PEP.
+- Add config option `no_root_warning`.
+- Add config option `no_legacy_pgp_warning`.
+- Also disable legacy PGP when running as root (Ox was already disabled).
+- Disable pinning for not using PLAIN when running as root.
+- Add config option `ox_trust_mode` with settings `blind` and `tofu`.
+
+### Changed
+- Due to new `tofu` trust mode for Ox, only one public key per contact is 
accepted for easier ID handling.
+- Ox: Check that fingerprint of received key equals the advertised one.
+
 ## [v0.16.0] 2026-06-20
 ### Added
 - Add Ox support to http-upload.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/go-sendxmpp-v0.16.0/README.md 
new/go-sendxmpp-v0.17.0/README.md
--- old/go-sendxmpp-v0.16.0/README.md   2026-06-20 09:54:34.000000000 +0200
+++ new/go-sendxmpp-v0.17.0/README.md   2026-07-24 21:50:15.000000000 +0200
@@ -77,7 +77,7 @@
 the account details via command line options:
 
 ```plain
-Usage: go-sendxmpp [-cdilntv] [-a value] [--allow-plain] [--anonymous] 
[--fast-invalidate] [--fast-off] [-f value] [--headline] [--help] [-h value] 
[-j value] [--legacy-pgp] [-m value] [--muc-password value] [--no-sasl-upgrade] 
[--oob-file value] [--ox] [--ox-delete-nodes] [--ox-genprivkey-rsa] 
[--ox-genprivkey-x25519] [--ox-import-privkey value] [--ox-passphrase value] 
[-p value] [--raw] [-r value] [--retry-connect value] [--retry-connect-max 
value] [--scram-mech-pinning value] [--ssdp-off] [-s value] 
[--suppress-root-warning] [--timeout value] [--tls-version value] [-u value] 
[--version] [recipients…]
+Usage: go-sendxmpp [-cdilntv] [-a value] [--allow-plain] [--anonymous] 
[--fast-invalidate] [--fast-off] [-f value] [--headline] [--help] [-h value] 
[-j value] [--legacy-pgp] [-m value] [--muc-password value] [--no-sasl-upgrade] 
[--oob-file value] [--ox] [--ox-delete-nodes] [--ox-genprivkey-rsa] 
[--ox-genprivkey-x25519] [--ox-import-privkey value] [--ox-passphrase value] 
[--ox-receive-private-key value] [--ox-transfer-private-key] [-p value] [--raw] 
[-r value] [--retry-connect value] [--retry-connect-max value] 
[--scram-mech-pinning value] [--ssdp-off] [-s value] [--suppress-root-warning] 
[--timeout value] [--tls-version value] [-u value] [--version] [recipients…]
  -a, --alias=value  Set alias/nicknamefor chatrooms.
      --allow-plain  Allow PLAIN authentication.
      --anonymous    Use anonymous authentication.
@@ -122,6 +122,12 @@
      --ox-passphrase=value
                     Passphrase for locking and unlocking the private OpenPGP
                     key.
+     --ox-receive-private-key=value
+                    Import your encrypted private Ox key from the server.
+                    Requires the transfer code.
+     --ox-transfer-private-key
+                    Upload your encrypted private Ox key to the server for key
+                    transfer.
  -p, --password=value
                     Password for XMPP account.
      --raw          Send raw XML.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/go-sendxmpp-v0.16.0/const.go 
new/go-sendxmpp-v0.17.0/const.go
--- old/go-sendxmpp-v0.16.0/const.go    2026-06-20 09:54:34.000000000 +0200
+++ new/go-sendxmpp-v0.17.0/const.go    2026-07-24 21:50:15.000000000 +0200
@@ -5,7 +5,7 @@
 package main
 
 const (
-       version = "0.16.0"
+       version = "0.17.0"
        // defaults
        defaultBufferSize      = 100
        defaultConfigColumnSep = 2
@@ -23,7 +23,7 @@
        defaultTLS11           = 11
        defaultTLS12           = 12
        defaultTLS13           = 13
-       // namespace
+       // namespaces
        nsC2SdTLS         = "urn:xmpp:alt-connections:tls"
        nsDiscoInfo       = "http://jabber.org/protocol/disco#info";
        nsDiscoItems      = "http://jabber.org/protocol/disco#items";
@@ -36,18 +36,24 @@
        nsMUCAdmin        = "http://jabber.org/protocol/muc#admin";
        nsOx              = "urn:xmpp:openpgp:0"
        nsOxPubKeys       = "urn:xmpp:openpgp:0:public-keys"
-       nsPubsub          = "http://jabber.org/protocol/pubsub";
-       nsPubsubOwner     = "http://jabber.org/protocol/pubsub#owner";
-       nsSid             = "urn:xmpp:sid:0"
-       nsSigned          = "jabber:x:signed"
-       nsVersion         = "jabber:iq:version"
-       nsXMPPStanzas     = "urn:ietf:params:xml:ns:xmpp-stanzas"
-       // strings
+       // #nosec G101 -- This is a namespace and not a hardcoded secret
+       nsOxSecretKey = "urn:xmpp:openpgp:0:secret-key"
+       nsPubsub      = "http://jabber.org/protocol/pubsub";
+       nsPubsubOwner = "http://jabber.org/protocol/pubsub#owner";
+       nsSid         = "urn:xmpp:sid:0"
+       nsSigned      = "jabber:x:signed"
+       nsVersion     = "jabber:iq:version"
+       nsXMPPStanzas = "urn:ietf:params:xml:ns:xmpp-stanzas"
+       // strings and numbers
        legacyPGPMsgBegin = "-----BEGIN PGP MESSAGE-----\n\n"
        legacyPGPMsgEnd   = "\n-----END PGP MESSAGE-----"
        legacyPGPSigBegin = "-----BEGIN PGP SIGNATURE-----\n\n"
        legacyPGPSigEnd   = "\n-----END PGP SIGNATURE-----"
        oxAltBody         = "This message is encrypted (XEP-0373: OpenPGP for 
XMPP)."
+       oxSecretAlphabet  = "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ"
+       oxSecretLength    = 24
+       oxSecretChunkSize = 4
+       pubsubNodeConfig  = "http://jabber.org/protocol/pubsub#node_config";
        pubsubPubOptions  = "http://jabber.org/protocol/pubsub#publish-options";
        strChat           = "chat"
        strEmpty          = ""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/go-sendxmpp-v0.16.0/go.mod 
new/go-sendxmpp-v0.17.0/go.mod
--- old/go-sendxmpp-v0.16.0/go.mod      2026-06-20 09:54:34.000000000 +0200
+++ new/go-sendxmpp-v0.17.0/go.mod      2026-07-24 21:50:15.000000000 +0200
@@ -4,19 +4,19 @@
 
 require (
        github.com/ProtonMail/gopenpgp/v3 v3.4.1
-       github.com/beevik/etree v1.6.0
-       github.com/gabriel-vasile/mimetype v1.4.13
+       github.com/beevik/etree v1.7.0
+       github.com/gabriel-vasile/mimetype v1.4.15
        github.com/google/uuid v1.6.0
        github.com/pborman/getopt/v2 v2.1.0
-       github.com/xmppo/go-xmpp v0.3.6
-       golang.org/x/crypto v0.53.0
-       golang.org/x/net v0.56.0
+       github.com/xmppo/go-xmpp v0.3.7
+       golang.org/x/crypto v0.54.0
+       golang.org/x/net v0.57.0
        salsa.debian.org/mdosch/xmppsrv v0.3.3
 )
 
 require (
        github.com/ProtonMail/go-crypto v1.4.1 // indirect
-       github.com/cloudflare/circl v1.6.3 // indirect
-       golang.org/x/sys v0.46.0 // indirect
-       golang.org/x/text v0.38.0 // indirect
+       github.com/cloudflare/circl v1.6.4 // indirect
+       golang.org/x/sys v0.47.0 // indirect
+       golang.org/x/text v0.40.0 // indirect
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/go-sendxmpp-v0.16.0/go.sum 
new/go-sendxmpp-v0.17.0/go.sum
--- old/go-sendxmpp-v0.16.0/go.sum      2026-06-20 09:54:34.000000000 +0200
+++ new/go-sendxmpp-v0.17.0/go.sum      2026-07-24 21:50:15.000000000 +0200
@@ -2,14 +2,14 @@
 github.com/ProtonMail/go-crypto v1.4.1/go.mod 
h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
 github.com/ProtonMail/gopenpgp/v3 v3.4.1 
h1:K7uUhSHSJxORZ+RuHpilTT6S4MA2whCRlXNwLqd0+ys=
 github.com/ProtonMail/gopenpgp/v3 v3.4.1/go.mod 
h1:bGdV9f6edhmd581wzXsQCTKdH8bXBbyhkgDKPjwPc6U=
-github.com/beevik/etree v1.6.0 h1:u8Kwy8pp9D9XeITj2Z0XtA5qqZEmtJtuXZRQi+j03eE=
-github.com/beevik/etree v1.6.0/go.mod 
h1:bh4zJxiIr62SOf9pRzN7UUYaEDa9HEKafK25+sLc0Gc=
-github.com/cloudflare/circl v1.6.3 
h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
-github.com/cloudflare/circl v1.6.3/go.mod 
h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
+github.com/beevik/etree v1.7.0 h1:xjBk9O4p4x7D1YajePjfLzdaFC4/uYUENA7P0pv6gXA=
+github.com/beevik/etree v1.7.0/go.mod 
h1:bh4zJxiIr62SOf9pRzN7UUYaEDa9HEKafK25+sLc0Gc=
+github.com/cloudflare/circl v1.6.4 
h1:pOXuDTCEYyzydgUpQ0CQz3LsinKjiSk6nNP5Lt5K64U=
+github.com/cloudflare/circl v1.6.4/go.mod 
h1:YxarevkLlbaHuWsxG6vmYNWBEsSp4pnp7j+4VljMavY=
 github.com/davecgh/go-spew v1.1.1 
h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/gabriel-vasile/mimetype v1.4.13 
h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
-github.com/gabriel-vasile/mimetype v1.4.13/go.mod 
h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
+github.com/gabriel-vasile/mimetype v1.4.15 
h1:05iP/CYtZ/w455R/KZM6rZ5ieAdh99UPtd+d3YzLmaI=
+github.com/gabriel-vasile/mimetype v1.4.15/go.mod 
h1:azpTcoLcDZRNgFou5j+APrqQx9HqVPWa6ijYQIIVswQ=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod 
h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/pborman/getopt/v2 v2.1.0 
h1:eNfR+r+dWLdWmV8g5OlpyrTYHkhVNxHBdN2cCrJmOEA=
@@ -18,16 +18,16 @@
 github.com/pmezard/go-difflib v1.0.0/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/stretchr/testify v1.10.0 
h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
 github.com/stretchr/testify v1.10.0/go.mod 
h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
-github.com/xmppo/go-xmpp v0.3.6 h1:CbTrXAotlt7UsjGqpqfNIXfmoSziP73eSFqhH4IS8ng=
-github.com/xmppo/go-xmpp v0.3.6/go.mod 
h1:YD5roZgj385upOjjG4RNNQ1kdhk5JtvA944MpMAb+jo=
-golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
-golang.org/x/crypto v0.53.0/go.mod 
h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
-golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
-golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
-golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
-golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
-golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
-golang.org/x/text v0.38.0/go.mod 
h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
+github.com/xmppo/go-xmpp v0.3.7 h1:UHM/pL+MioIMBWaFYU5TNcH8RaMWJaCbI/Yg2VW2WdM=
+github.com/xmppo/go-xmpp v0.3.7/go.mod 
h1:YD5roZgj385upOjjG4RNNQ1kdhk5JtvA944MpMAb+jo=
+golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
+golang.org/x/crypto v0.54.0/go.mod 
h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
+golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
+golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
+golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
+golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
+golang.org/x/text v0.40.0/go.mod 
h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 salsa.debian.org/mdosch/xmppsrv v0.3.3 
h1:F8FGyw1Q1LkAs/UbIXd6Obd33q2CKWrIxxrzvuLSVuM=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/go-sendxmpp-v0.16.0/helpers.go 
new/go-sendxmpp-v0.17.0/helpers.go
--- old/go-sendxmpp-v0.16.0/helpers.go  2026-06-20 09:54:34.000000000 +0200
+++ new/go-sendxmpp-v0.17.0/helpers.go  2026-07-24 21:50:15.000000000 +0200
@@ -298,8 +298,56 @@
        return uuid.NewString()[:6]
 }
 
+func getSecret(alphabet string, length int, chunkSize int) (string, error) {
+       if length%chunkSize != 0 {
+               return strEmpty, fmt.Errorf("%d is not a multiple of %d", 
length, chunkSize)
+       }
+       secret := make([]rune, length+length/chunkSize-1)
+       secretRunes := []rune(alphabet)
+       delimiter := []rune("-")
+       max := big.NewInt(int64(len(secretRunes)))
+       chunks := length / chunkSize
+       secretLength := length + chunks - 1
+       count := 0
+       for range chunks {
+               for range chunkSize {
+                       randInt, err := rand.Int(rand.Reader, max)
+                       if err != nil {
+                               return strEmpty, err
+                       }
+                       secret[count] = secretRunes[randInt.Int64()]
+                       count++
+               }
+               if count >= secretLength {
+                       break
+               }
+               secret[count] = delimiter[0]
+               count++
+       }
+       return string(secret), nil
+}
+
 // Remove @ and dots
 func fsFriendlyJid(jid string) string {
        jid = strings.ReplaceAll(jid, "@", "_at_")
        return strings.ReplaceAll(jid, ".", "_")
 }
+
+func checkPEPsupport(jid string, client *xmpp.Client, iqc chan xmpp.IQ, drc 
chan xmpp.DiscoResult) error {
+       var pep bool
+       slog.Info("check pep support:", "target", jid)
+       discoResult, err := getDiscoInfo(client, drc, jid)
+       if err != nil {
+               return fmt.Errorf("check pep support: %w", err)
+       }
+       slog.Info("ox: check disco info reply for:", "type", "pep", "category", 
"pubsub")
+       for _, identity := range discoResult.Identities {
+               if identity.Category == "pubsub" && identity.Type == "pep" {
+                       pep = true
+               }
+       }
+       if !pep {
+               return fmt.Errorf("no pep support")
+       }
+       return nil
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/go-sendxmpp-v0.16.0/legacypgp.go 
new/go-sendxmpp-v0.17.0/legacypgp.go
--- old/go-sendxmpp-v0.16.0/legacypgp.go        2026-06-20 09:54:34.000000000 
+0200
+++ new/go-sendxmpp-v0.17.0/legacypgp.go        2026-07-24 21:50:15.000000000 
+0200
@@ -120,7 +120,9 @@
        return false
 }
 
-func legacyPGPDecrypt(m xmpp.Chat, client *xmpp.Client, iqc chan xmpp.IQ, user 
string, oxPrivKey *crypto.Key) (string, time.Time, error) {
+func legacyPGPDecrypt(m xmpp.Chat, client *xmpp.Client, iqc chan xmpp.IQ, user 
string, oxPrivKey *crypto.Key,
+       oxTrustMode string,
+) (string, time.Time, error) {
        var legacyPGPMsg *crypto.PGPMessage
        var err error
        sender := strings.Split(m.Remote, "/")[0]
@@ -143,7 +145,7 @@
                return strError, time.Now(), err
        }
        slog.Info("legacy pgp: decrypting: getting senders key", "sender", 
sender)
-       senderKeyRing, err := oxGetPublicKeyRing(client, iqc, sender)
+       senderKeyRing, err := oxGetPublicKeyRing(client, iqc, sender, 
oxTrustMode)
        if err != nil {
                return strError, time.Now(), err
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/go-sendxmpp-v0.16.0/main.go 
new/go-sendxmpp-v0.17.0/main.go
--- old/go-sendxmpp-v0.16.0/main.go     2026-06-20 09:54:34.000000000 +0200
+++ new/go-sendxmpp-v0.17.0/main.go     2026-07-24 21:50:15.000000000 +0200
@@ -30,12 +30,15 @@
 )
 
 type configuration struct {
-       username   string
-       jserver    string
-       port       string
-       password   string
-       alias      string
-       allowPLAIN bool
+       username    string
+       jserver     string
+       port        string
+       password    string
+       alias       string
+       oxTrustMode string
+       allowPLAIN  bool
+       noRootWarn  bool
+       noLPGPWarn  bool
 }
 
 func closeAndExit(client *xmpp.Client, err error) {
@@ -98,12 +101,12 @@
        }
 
        var (
-               err                                    error
-               message, user, server, password, alias string
-               allowPLAIN, isRoot                     bool
-               oxPrivKey                              *crypto.Key
-               recipients                             []recipientsType
-               fast                                   xmpp.Fast
+               err                                                    error
+               message, user, server, password, alias, oxTrustMode    string
+               allowPLAIN, pinNoPLAIN, isRoot, noRootWarn, noLPGPWarn bool
+               oxPrivKey                                              
*crypto.Key
+               recipients                                             
[]recipientsType
+               fast                                                   xmpp.Fast
                // There are some errors that we ignore as we do not want to
                // stop the execution. Failure is used to track those to exit
                // with a non-success return value.
@@ -150,6 +153,8 @@
        flagOxImportPrivKey := getopt.StringLong("ox-import-privkey", 0, "",
                "Import an existing private OpenPGP key.")
        flagOxDeleteNodes := getopt.BoolLong("ox-delete-nodes", 0, "Delete 
existing OpenPGP nodes on the server.")
+       flagOxTransPrivKey := getopt.BoolLong("ox-transfer-private-key", 0, 
"Upload your encrypted private Ox key to the server for key transfer.")
+       flagOxRecvPrivKey := getopt.StringLong("ox-receive-private-key", 0, "", 
"Import your encrypted private Ox key from the server. Requires the transfer 
code.")
        flagOOBFile := getopt.StringLong("oob-file", 0, "", "URL to send a file 
as out of band data.")
        flagHeadline := getopt.BoolLong("headline", 0, "Send message as type 
headline.")
        flagSCRAMPinning := getopt.StringLong("scram-mech-pinning", 0, "", 
"Enforce the use of a certain SCRAM authentication mechanism.")
@@ -212,37 +217,6 @@
                slog.SetLogLoggerLevel(slog.LevelDebug)
        }
 
-       if *flagLegacyPGP {
-               slog.Warn("Legacy PGP is deprecated and will be removed in a 
future version, " +
-                       "please use 'OpenPGP for XMPP' (OX) instead.")
-       }
-
-       // Print a warning if go-sendxmpp is run by the user root on 
non-windows systems.
-       if runtime.GOOS != "windows" && !*flagNoRootWarning {
-               // Get the current user.
-               slog.Info("checking for current OS user")
-               currUser, err := osUser.Current()
-               if err != nil {
-                       log.Fatal("Failed to get current user: ", err)
-               }
-               slog.Info("checking if OS user is root")
-               slog.Info("user identification", "UID", syscall.Getuid())
-               slog.Info("user identification", "GID", syscall.Getgid())
-               if currUser.Username == "root" {
-                       fmt.Println("WARNING: It seems you are running 
go-sendxmpp as root user.\n" +
-                               "This is not recommended as go-sendxmpp does 
not require root " +
-                               "privileges. Please consider using a less 
privileged user. For an " +
-                               "example how to do this with sudo please 
consult the manpage chapter " +
-                               "TIPS.")
-                       // Logging that we're running as root
-                       isRoot = true
-                       *flagFastOff = true
-                       slog.Info("disabling fast as we're running as root")
-                       *flagOx = false
-                       slog.Info("disabling OX as we're running as root")
-               }
-       }
-
        switch *flagSCRAMPinning {
        case "", "SCRAM-SHA-1", "SCRAM-SHA-1-PLUS", "SCRAM-SHA-256", 
"SCRAM-SHA-256-PLUS",
                "SCRAM-SHA-512", "SCRAM-SHA-512-PLUS":
@@ -251,6 +225,10 @@
                log.Fatal("Unknown SCRAM mechanism: ", *flagSCRAMPinning)
        }
 
+       if *flagOxRecvPrivKey != "" && len(*flagOxRecvPrivKey) != 29 {
+               log.Fatal("OX: passphrase to decrypt private key has wrong 
length: ", len(*flagOxRecvPrivKey), " instead of 29")
+       }
+
        // Read recipients from command line, if no recipients list is 
specified, and quit
        // if none are specified. For listening or sending raw XML it's not 
required to
        // specify a recipient except when sending raw messages to MUCs 
(go-sendxmpp will
@@ -275,7 +253,8 @@
 
        if (len(recipientsList) == 0 && !*flagRaw && !*flagListen && 
!*flagOxGenPrivKeyX25519 &&
                !*flagOxGenPrivKeyRSA && *flagOxImportPrivKey == "" && 
!*flagFastInvalidate) &&
-               !*flagOxDeleteNodes || (len(recipientsList) == 0 && 
*flagChatroom) {
+               !*flagOxDeleteNodes && *flagOxRecvPrivKey == "" && 
!*flagOxTransPrivKey ||
+               (len(recipientsList) == 0 && *flagChatroom) {
                log.Fatal("No recipient specified.")
        }
 
@@ -303,9 +282,47 @@
                        slog.Info("setting", "server", server)
                }
                allowPLAIN = config.allowPLAIN
+               noRootWarn = config.noRootWarn
+               noLPGPWarn = config.noLPGPWarn
+               oxTrustMode = config.oxTrustMode
                slog.Info("config", "allow-plain", config.allowPLAIN)
        }
 
+       // Print a warning if go-sendxmpp is run by the user root on 
non-windows systems.
+       if runtime.GOOS != "windows" && !*flagNoRootWarning && !noRootWarn {
+               // Get the current user.
+               slog.Info("checking for current OS user")
+               currUser, err := osUser.Current()
+               if err != nil {
+                       log.Fatal("Failed to get current user: ", err)
+               }
+               slog.Info("checking if OS user is root")
+               slog.Info("user identification", "UID", syscall.Getuid())
+               slog.Info("user identification", "GID", syscall.Getgid())
+               if currUser.Username == "root" {
+                       fmt.Println("WARNING: It seems you are running 
go-sendxmpp as root user.\n" +
+                               "This is not recommended as go-sendxmpp does 
not require root " +
+                               "privileges. Please consider using a less 
privileged user. For an " +
+                               "example how to do this with sudo please 
consult the manpage chapter " +
+                               "TIPS.")
+                       // Logging that we're running as root
+                       isRoot = true
+                       // Disabling FAST, legacy PGP and OX when running as 
root as both features
+                       // need to read/write local date in the users context 
and access the network
+                       // but we drop root privileges prior to network 
activity.
+                       *flagFastOff = true
+                       slog.Info("disabling fast as we're running as root")
+                       *flagOx = false
+                       *flagLegacyPGP = false
+                       slog.Info("disabling OX as we're running as root")
+               }
+       }
+
+       if *flagLegacyPGP && !noLPGPWarn {
+               slog.Warn("Legacy PGP is deprecated and will be removed in a 
future version, " +
+                       "please use 'OpenPGP for XMPP' (OX) instead.")
+       }
+
        // Overwrite user if specified via command line flag.
        if *flagUser != "" {
                user = *flagUser
@@ -432,7 +449,9 @@
        resource := "go-sendxmpp." + getShortID()
 
        // Check whether PLAIN authentication is disabled.
-       pinNoPLAIN, _ := parseAuthPinFile(user)
+       if !isRoot {
+               pinNoPLAIN, _ = parseAuthPinFile(user)
+       }
        noPLAIN := pinNoPLAIN || (!*flagPLAINAllow && !allowPLAIN)
 
        // Periodic server ping settings
@@ -509,7 +528,8 @@
        if !*flagInteractive && !*flagListen && len(*flagHTTPUpload) == 0 &&
                !*flagOxDeleteNodes && *flagOxImportPrivKey == "" &&
                !*flagOxGenPrivKeyX25519 && !*flagOxGenPrivKeyRSA && 
*flagOOBFile == "" &&
-               !*flagFastInvalidate && message == "" {
+               !*flagFastInvalidate && *flagOxRecvPrivKey == "" && 
!*flagOxTransPrivKey &&
+               message == "" {
                slog.Info("reading message from stdin")
                scanner := bufio.NewScanner(os.Stdin)
                for scanner.Scan() {
@@ -536,7 +556,7 @@
        if message == "" && !*flagInteractive && !*flagListen && 
!*flagOxGenPrivKeyRSA &&
                !*flagOxGenPrivKeyX25519 && *flagOxImportPrivKey == "" &&
                !*flagOxDeleteNodes && len(*flagHTTPUpload) == 0 && 
*flagOOBFile == "" &&
-               !*flagFastInvalidate {
+               !*flagFastInvalidate && !*flagOxTransPrivKey && 
*flagOxRecvPrivKey == "" {
                slog.Info("exiting due to empty message")
                os.Exit(0)
        }
@@ -587,7 +607,7 @@
        }
        // If authentication is not yet pinned to not use PLAIN and a SCRAM 
mechanism is
        // used, write the auth pin file.
-       if !pinNoPLAIN && (strings.HasPrefix(client.Mechanism, "SCRAM") ||
+       if !pinNoPLAIN && !isRoot && (strings.HasPrefix(client.Mechanism, 
"SCRAM") ||
                strings.HasPrefix(client.Mechanism, "HT")) {
                slog.Info("writing auth pin file")
                err = writeAuthPinFile(user)
@@ -613,14 +633,14 @@
                switch {
                case (*flagOx || *flagLegacyPGP) && !*flagChatroom:
                        slog.Info("requesting OX key for", "JID", re.Jid)
-                       re.OxKeyRing, err = oxGetPublicKeyRing(client, iqc, 
re.Jid)
+                       re.OxKeyRing, err = oxGetPublicKeyRing(client, iqc, 
re.Jid, oxTrustMode)
                        if err != nil {
                                re.OxKeyRing = nil
                                fmt.Printf("ox: error fetching key for %s: 
%v\n", re.Jid, err)
                                failure = err
                        }
                case *flagOx && *flagChatroom:
-                       re.OxKeyRing, err = oxGetPublicKeyRingChatroom(client, 
iqc, drc, re.Jid)
+                       re.OxKeyRing, err = oxGetPublicKeyRingChatroom(client, 
iqc, drc, re.Jid, oxTrustMode)
                        if err != nil {
                                re.OxKeyRing = nil
                                fmt.Println("Couldn't receive keys for:", 
re.Jid)
@@ -640,14 +660,15 @@
                recipients[i].Jid = validatedJid
        }
 
+       slog.Info("checking validity", "JID", user)
+       validatedOwnJid, err := MarshalJID(user)
+       if err != nil {
+               cancel()
+               closeAndExit(client, err)
+       }
+
        switch {
        case *flagOxGenPrivKeyX25519:
-               slog.Info("checking validity", "JID", user)
-               validatedOwnJid, err := MarshalJID(user)
-               if err != nil {
-                       cancel()
-                       closeAndExit(client, err)
-               }
                slog.Info("generating X25519 private OX key")
                err = oxGenPrivKey(validatedOwnJid, client, iqc, 
*flagOxPassphrase,
                        crypto.KeyGenerationCurve25519Legacy)
@@ -660,12 +681,6 @@
                }
                os.Exit(0)
        case *flagOxGenPrivKeyRSA:
-               slog.Info("checking validity", "JID", user)
-               validatedOwnJid, err := MarshalJID(user)
-               if err != nil {
-                       cancel()
-                       closeAndExit(client, err)
-               }
                slog.Info("generating RSA4096 private OX key")
                err = oxGenPrivKey(validatedOwnJid, client, iqc, 
*flagOxPassphrase, crypto.KeyGenerationRSA4096)
                if err != nil {
@@ -674,41 +689,44 @@
                }
                os.Exit(0)
        case *flagOxImportPrivKey != "":
-               slog.Info("checking validity", "JID", user)
-               validatedOwnJid, err := MarshalJID(user)
+               slog.Info("importing private OX key:", "file", 
*flagOxImportPrivKey)
+               err = oxImportPrivKey(validatedOwnJid, *flagOxImportPrivKey,
+                       client, iqc, oxTrustMode)
                if err != nil {
                        cancel()
                        closeAndExit(client, err)
                }
-               slog.Info("importing private OX key:", "file", 
*flagOxImportPrivKey)
-               err = oxImportPrivKey(validatedOwnJid, *flagOxImportPrivKey,
-                       client, iqc)
+               os.Exit(0)
+       case *flagOxDeleteNodes:
+               slog.Info("delete OX pep nodes")
+               err = oxDeleteNodes(validatedOwnJid, client, iqc, disc)
                if err != nil {
                        cancel()
                        closeAndExit(client, err)
                }
                os.Exit(0)
-       case *flagOxDeleteNodes:
-               slog.Info("checking validity", "JID", user)
-               validatedOwnJid, err := MarshalJID(user)
+       case *flagOxTransPrivKey:
+               slog.Info("transferring private OX key to the server")
+               oxPrivKey, err = oxGetPrivKey(validatedOwnJid, 
*flagOxPassphrase)
                if err != nil {
                        cancel()
                        closeAndExit(client, err)
                }
-               slog.Info("delete OX pep nodes")
-               err = oxDeleteNodes(validatedOwnJid, client, iqc, disc)
+               err = oxTransPrivKey(validatedOwnJid, oxPrivKey, client, iqc, 
drc)
                if err != nil {
                        cancel()
                        closeAndExit(client, err)
                }
                os.Exit(0)
-       case *flagOx, *flagLegacyPGP:
-               slog.Info("checking validity", "JID", user)
-               validatedOwnJid, err := MarshalJID(user)
+       case *flagOxRecvPrivKey != "":
+               slog.Info("requesting private OX key from the server")
+               err = oxRecvPrivKey(validatedOwnJid, *flagOxRecvPrivKey, 
client, iqc, drc, oxTrustMode)
                if err != nil {
                        cancel()
                        closeAndExit(client, err)
                }
+               os.Exit(0)
+       case *flagOx, *flagLegacyPGP:
                slog.Info("reading private OX key")
                oxPrivKey, err = oxGetPrivKey(validatedOwnJid, 
*flagOxPassphrase)
                if err != nil {
@@ -910,7 +928,7 @@
                                var t time.Time
                                if isOx {
                                        slog.Info("message is OX encrypted")
-                                       msg, t, err = oxDecrypt(v, client, iqc, 
drc, user, oxPrivKey)
+                                       msg, t, err = oxDecrypt(v, client, iqc, 
drc, user, oxPrivKey, oxTrustMode)
                                        if err != nil {
                                                log.Println(err)
                                                continue
@@ -921,7 +939,7 @@
                                        }
                                        prefix = "[OX]"
                                } else {
-                                       msg, t, err = legacyPGPDecrypt(v, 
client, iqc, user, oxPrivKey)
+                                       msg, t, err = legacyPGPDecrypt(v, 
client, iqc, user, oxPrivKey, oxTrustMode)
                                        if err != nil {
                                                log.Println(err)
                                                continue
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/go-sendxmpp-v0.16.0/man/go-sendxmpp.1 
new/go-sendxmpp-v0.17.0/man/go-sendxmpp.1
--- old/go-sendxmpp-v0.16.0/man/go-sendxmpp.1   2026-06-20 09:54:34.000000000 
+0200
+++ new/go-sendxmpp-v0.17.0/man/go-sendxmpp.1   2026-07-24 21:50:15.000000000 
+0200
@@ -1,10 +1,10 @@
 .\" generated with Ronn-NG/v0.10.1
 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
-.TH "GO\-SENDXMPP" "1" "June 2026" ""
+.TH "GO\-SENDXMPP" "1" "July 2026" ""
 .SH "NAME"
 \fBgo\-sendxmpp\fR \- A tool to send messages to an XMPP contact or MUC\.
 .SH "SYNOPSIS"
-\fBgo\-sendxmpp [\-cdilntv] [\-a value] [\-\-allow\-plain] [\-\-anonymous] 
[\-\-fast\-invalidate] [\-\-fast\-off] [\-f value] [\-\-headline] [\-\-help] 
[\-h value] [\-j value] [\-\-legacy\-pgp] [\-m value] [\-\-muc\-password value] 
[\-\-no\-sasl\-upgrade] [\-\-oob\-file value] [\-\-ox] [\-\-ox\-delete\-nodes] 
[\-\-ox\-genprivkey\-rsa] [\-\-ox\-genprivkey\-x25519] [\-\-ox\-import\-privkey 
value] [\-\-ox\-passphrase value] [\-p value] [\-\-raw] [\-r value] 
[\-\-retry\-connect value] [\-\-retry\-connect\-max value] 
[\-\-scram\-mech\-pinning value] [\-\-ssdp\-off] [\-s value] 
[\-\-suppress\-root\-warning] [\-\-timeout value] [\-\-tls\-version value] [\-u 
value] [\-\-version] [recipients…]\fR
+\fBgo\-sendxmpp [\-cdilntv] [\-a value] [\-\-allow\-plain] [\-\-anonymous] 
[\-\-fast\-invalidate] [\-\-fast\-off] [\-f value] [\-\-headline] [\-\-help] 
[\-h value] [\-j value] [\-\-legacy\-pgp] [\-m value] [\-\-muc\-password value] 
[\-\-no\-sasl\-upgrade] [\-\-oob\-file value] [\-\-ox] [\-\-ox\-delete\-nodes] 
[\-\-ox\-genprivkey\-rsa] [\-\-ox\-genprivkey\-x25519] [\-\-ox\-import\-privkey 
value] [\-\-ox\-passphrase value] [\-\-ox\-receive\-private\-key value] 
[\-\-ox\-transfer\-private\-key] [\-p value] [\-\-raw] [\-r value] 
[\-\-retry\-connect value] [\-\-retry\-connect\-max value] 
[\-\-scram\-mech\-pinning value] [\-\-ssdp\-off] [\-s value] 
[\-\-suppress\-root\-warning] [\-\-timeout value] [\-\-tls\-version value] [\-u 
value] [\-\-version] [recipients…]\fR
 .SH "DESCRIPTION"
 A tool to send messages to an XMPP contact or MUC inspired by \fBsendxmpp\fR\.
 .br
@@ -84,7 +84,7 @@
 There is no check whether the recipients key is trusted as there is no local 
keyring used\. Go\-sendxmpp just uses the most recent key that is provided via 
pubsub and checks that it is not expired\. As a user facing client a 
notification would be shown that a new key is available and ask the user 
whether to use the new key or stick to the old one\. As go\-sendxmpp is usually 
used in scripts it just accepts the new key to prevent the user from missing a 
new notification due to changed keys\.
 .TP
 \fB\-\-ox\-delete\-nodes\fR
-Delete existing OpenPGP nodes on the server\.
+Delete existing OpenPGP nodes (public key and private key backup) on the 
server\.
 .TP
 \fB\-\-ox\-genprivkey\-rsa\fR
 Generate a private OpenPGP key (RSA 4096 bit) for the configured account (via 
config file or \fB\-u\fR and \fB\-p\fR) and publish the corresponding public 
key\. Go\-sendxmpp will save the key in 
\fB$XDG_DATA_HOME/go\-sendxmpp/oxprivkeys\fR or 
\fB$HOME/\.local/share/go\-sendxmpp/oxprivkeys\fR\. To protect the key a 
passphrase might be set using \fB\-\-ox\-passphrase\fR while generating the 
key\.
@@ -102,6 +102,12 @@
 \fB\-\-ox\-passphrase\fR=[\fIvalue\fR]
 Passphrase for locking and unlocking the private OpenPGP key\.
 .TP
+\fB\-\-ox\-receive\-private\-key\fR=[\fIvalue\fR]
+Import your encrypted private Ox key from the server\. Requires the transfer 
code\.
+.TP
+\fB\-\-ox\-transfer\-private\-key\fR
+Upload your encrypted private Ox key to the server for key transfer\. The 
private key will be encrypted by a random generated passphrase\. Please make 
sure to not lose the passphrase as it can not be recovered\.
+.TP
 \fB\-p\fR, \fB\-\-password\fR=[\fIvalue\fR]
 Password for XMPP account\.
 .TP
@@ -127,7 +133,7 @@
 Set message subject\.
 .TP
 \fB\-\-suppress\-root\-warning\fR
-Suppress warning when run as root\.
+Suppress warning when run as root\. Note that running as root is discouraged 
although go\-sendxmpp tries to drop the root privileges before doing network 
calls\. Be aware that \fBOX\fR, \fBlegacy PGP\fR and the pinning to not use 
\fBPLAIN\fR authentication after a successful \fBSCRAM\fR authentication are 
disabled as those need file system access in the private user scope after 
dropping the privileges\.
 .TP
 \fB\-\-timeout=\fR[\fIvalue\fR]
 Connection timeout in seconds\. (Default: 10)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/go-sendxmpp-v0.16.0/man/go-sendxmpp.1.html 
new/go-sendxmpp-v0.17.0/man/go-sendxmpp.1.html
--- old/go-sendxmpp-v0.16.0/man/go-sendxmpp.1.html      2026-06-20 
09:54:34.000000000 +0200
+++ new/go-sendxmpp-v0.17.0/man/go-sendxmpp.1.html      2026-07-24 
21:50:15.000000000 +0200
@@ -81,7 +81,7 @@
 </p>
 <h2 id="SYNOPSIS">SYNOPSIS</h2>
 
-<p><code>go-sendxmpp [-cdilntv] [-a value] [--allow-plain] [--anonymous] 
[--fast-invalidate] [--fast-off] [-f value] [--headline] [--help] [-h value] 
[-j value] [--legacy-pgp] [-m value] [--muc-password value] [--no-sasl-upgrade] 
[--oob-file value] [--ox] [--ox-delete-nodes] [--ox-genprivkey-rsa] 
[--ox-genprivkey-x25519] [--ox-import-privkey value] [--ox-passphrase value] 
[-p value] [--raw] [-r value] [--retry-connect value] [--retry-connect-max 
value] [--scram-mech-pinning value] [--ssdp-off] [-s value] 
[--suppress-root-warning] [--timeout value] [--tls-version value] [-u value] 
[--version] [recipients…]</code></p>
+<p><code>go-sendxmpp [-cdilntv] [-a value] [--allow-plain] [--anonymous] 
[--fast-invalidate] [--fast-off] [-f value] [--headline] [--help] [-h value] 
[-j value] [--legacy-pgp] [-m value] [--muc-password value] [--no-sasl-upgrade] 
[--oob-file value] [--ox] [--ox-delete-nodes] [--ox-genprivkey-rsa] 
[--ox-genprivkey-x25519] [--ox-import-privkey value] [--ox-passphrase value] 
[--ox-receive-private-key value] [--ox-transfer-private-key] [-p value] [--raw] 
[-r value] [--retry-connect value] [--retry-connect-max value] 
[--scram-mech-pinning value] [--ssdp-off] [-s value] [--suppress-root-warning] 
[--timeout value] [--tls-version value] [-u value] [--version] 
[recipients…]</code></p>
 
 <h2 id="DESCRIPTION">DESCRIPTION</h2>
 
@@ -176,7 +176,7 @@
 used in scripts it just accepts the new key to prevent the user from missing a 
new notification
 due to changed keys.</dd>
 <dt><code>--ox-delete-nodes</code></dt>
-<dd>Delete existing OpenPGP nodes on the server.</dd>
+<dd>Delete existing OpenPGP nodes (public key and private key backup) on the 
server.</dd>
 <dt><code>--ox-genprivkey-rsa</code></dt>
 <dd>Generate a private OpenPGP key (RSA 4096 bit) for the configured account 
(via config file or <code>-u</code> and <code>-p</code>)
 and publish the corresponding public key.
@@ -200,6 +200,12 @@
 <code>--ox-passphrase</code>=[<var>value</var>]</dt>
 <dd>Passphrase for locking and unlocking the private OpenPGP key.</dd>
 <dt>
+<code>--ox-receive-private-key</code>=[<var>value</var>]</dt>
+<dd>Import your encrypted private Ox key from the server. Requires the 
transfer code.</dd>
+<dt><code>--ox-transfer-private-key</code></dt>
+<dd>Upload your encrypted private Ox key to the server for key transfer. The 
private key will be encrypted
+by a random generated passphrase. Please make sure to not lose the passphrase 
as it can not be recovered.</dd>
+<dt>
 <code>-p</code>, <code>--password</code>=[<var>value</var>]</dt>
 <dd>Password for XMPP account.</dd>
 <dt><code>--raw</code></dt>
@@ -232,7 +238,11 @@
 <code>-s</code>, <code>--subject</code>=[<var>value</var>]</dt>
 <dd>Set message subject.</dd>
 <dt><code>--suppress-root-warning</code></dt>
-<dd>Suppress warning when run as root.</dd>
+<dd>Suppress warning when run as root.
+Note that running as root is discouraged although go-sendxmpp tries to drop 
the root privileges
+before doing network calls. Be aware that <strong>OX</strong>, <strong>legacy 
PGP</strong> and the pinning to not use <strong>PLAIN</strong>
+authentication after a successful <strong>SCRAM</strong> authentication are 
disabled as those need file system access
+in the private user scope after dropping the privileges.</dd>
 <dt>
 <code>--timeout=</code>[<var>value</var>]</dt>
 <dd>Connection timeout in seconds. (Default: 10)</dd>
@@ -323,11 +333,11 @@
 
 <h2 id="SEE-ALSO">SEE ALSO</h2>
 
-<p><span class="man-ref">go-sendxmpp<span class="s">(5)</span></span>, <span 
class="man-ref">xmppc<span class="s">(1)</span></span>, <span 
class="man-ref">sendxmpp<span class="s">(1)</span></span></p>
+<p><a class="man-ref" href="go-sendxmpp.5.html">go-sendxmpp<span 
class="s">(5)</span></a>, <span class="man-ref">xmppc<span 
class="s">(1)</span></span>, <span class="man-ref">sendxmpp<span 
class="s">(1)</span></span></p>
 
   <ol class='man-decor man-foot man foot'>
     <li class='tl'></li>
-    <li class='tc'>June 2026</li>
+    <li class='tc'>July 2026</li>
     <li class='tr'>go-sendxmpp(1)</li>
   </ol>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/go-sendxmpp-v0.16.0/man/go-sendxmpp.1.ronn 
new/go-sendxmpp-v0.17.0/man/go-sendxmpp.1.ronn
--- old/go-sendxmpp-v0.16.0/man/go-sendxmpp.1.ronn      2026-06-20 
09:54:34.000000000 +0200
+++ new/go-sendxmpp-v0.17.0/man/go-sendxmpp.1.ronn      2026-07-24 
21:50:15.000000000 +0200
@@ -3,7 +3,7 @@
 
 ## SYNOPSIS
 
-`go-sendxmpp [-cdilntv] [-a value] [--allow-plain] [--anonymous] 
[--fast-invalidate] [--fast-off] [-f value] [--headline] [--help] [-h value] 
[-j value] [--legacy-pgp] [-m value] [--muc-password value] [--no-sasl-upgrade] 
[--oob-file value] [--ox] [--ox-delete-nodes] [--ox-genprivkey-rsa] 
[--ox-genprivkey-x25519] [--ox-import-privkey value] [--ox-passphrase value] 
[-p value] [--raw] [-r value] [--retry-connect value] [--retry-connect-max 
value] [--scram-mech-pinning value] [--ssdp-off] [-s value] 
[--suppress-root-warning] [--timeout value] [--tls-version value] [-u value] 
[--version] [recipients…]`
+`go-sendxmpp [-cdilntv] [-a value] [--allow-plain] [--anonymous] 
[--fast-invalidate] [--fast-off] [-f value] [--headline] [--help] [-h value] 
[-j value] [--legacy-pgp] [-m value] [--muc-password value] [--no-sasl-upgrade] 
[--oob-file value] [--ox] [--ox-delete-nodes] [--ox-genprivkey-rsa] 
[--ox-genprivkey-x25519] [--ox-import-privkey value] [--ox-passphrase value] 
[--ox-receive-private-key value] [--ox-transfer-private-key] [-p value] [--raw] 
[-r value] [--retry-connect value] [--retry-connect-max value] 
[--scram-mech-pinning value] [--ssdp-off] [-s value] [--suppress-root-warning] 
[--timeout value] [--tls-version value] [-u value] [--version] [recipients…]`
 
 ## DESCRIPTION
 
@@ -102,7 +102,7 @@
 due to changed keys.
 
 * `--ox-delete-nodes`:
-Delete existing OpenPGP nodes on the server.
+Delete existing OpenPGP nodes (public key and private key backup) on the 
server.
 
 * `--ox-genprivkey-rsa`:
 Generate a private OpenPGP key (RSA 4096 bit) for the configured account (via 
config file or `-u` and `-p`)
@@ -128,6 +128,13 @@
 * `--ox-passphrase`=[<value>]:
 Passphrase for locking and unlocking the private OpenPGP key.
 
+* `--ox-receive-private-key`=[<value>]:
+Import your encrypted private Ox key from the server. Requires the transfer 
code.
+
+* `--ox-transfer-private-key`:
+Upload your encrypted private Ox key to the server for key transfer. The 
private key will be encrypted
+by a random generated passphrase. Please make sure to not lose the passphrase 
as it can not be recovered.
+
 * `-p`, `--password`=[<value>]:
 Password for XMPP account.
 
@@ -165,6 +172,10 @@
 
 * `--suppress-root-warning`:
 Suppress warning when run as root.
+Note that running as root is discouraged although go-sendxmpp tries to drop 
the root privileges
+before doing network calls. Be aware that **OX**, **legacy PGP** and the 
pinning to not use **PLAIN**
+authentication after a successful **SCRAM** authentication are disabled as 
those need file system access
+in the private user scope after dropping the privileges.    
 
 * `--timeout=`[<value>]:
 Connection timeout in seconds. (Default: 10)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/go-sendxmpp-v0.16.0/man/go-sendxmpp.5 
new/go-sendxmpp-v0.17.0/man/go-sendxmpp.5
--- old/go-sendxmpp-v0.16.0/man/go-sendxmpp.5   2026-06-20 09:54:34.000000000 
+0200
+++ new/go-sendxmpp-v0.17.0/man/go-sendxmpp.5   2026-07-24 21:50:15.000000000 
+0200
@@ -1,6 +1,6 @@
 .\" generated with Ronn-NG/v0.10.1
 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
-.TH "GO\-SENDXMPP" "5" "May 2026" ""
+.TH "GO\-SENDXMPP" "5" "July 2026" ""
 .SH "NAME"
 \fBgo\-sendxmpp\fR \- A tool to send messages to an XMPP contact or MUC\.
 .SH "LOCATION"
@@ -19,6 +19,12 @@
 alias: [\fIyour_alias\fR]
 .br
 allow_plain: [\fIbool\fR]
+.br
+no_root_warning: [\fIbool\fR]
+.br
+no_legacy_pgp_warning: [\fIbool\fR]
+.br
+ox_trust_mode: [blind|tofu]
 .SH "REQUIRED SETTINGS"
 If all necessary settings are supplied as command line arguments no config 
file is needed at all\. Setting \fBjserver\fR and \fBport\fR might not be 
necessary depending on the used server\.
 .br
@@ -47,6 +53,21 @@
 Boolean to allow PLAIN authentication\. Note that this setting has no effect 
if there was a successful connection using a SCRAM authentication mechanism 
before\. In that case PLAIN is not allowed to prevent downgrades by a 
man\-in\-the\-middle attack\.
 .br
 Allowed values: \fBtrue\fR/\fBfalse\fR (Default: false)
+.TP
+\fBno_root_warning\fR
+Boolean to disable the warning when running as root\. Note that running as 
root is discouraged although go\-sendxmpp tries to drop the root privileges 
before doing network calls\. Be aware that \fBOX\fR, \fBlegacy PGP\fR and the 
pinning to not use \fBPLAIN\fR authentication after a successful \fBSCRAM\fR 
authentication are disabled as those need file system access in the private 
user scope after dropping the privileges\.
+.br
+Allowed values: \fBtrue\fR/\fBfalse\fR (Default: false)
+.TP
+\fBno_legacy_pgp_warning\fR
+Boolean to disable the warning when using \fBlegacy PGP\fR\. Please note, that 
*legacy PGP** is deprecated and will be removed in an upcoming update\. It is 
highly recommended to use OX instead\.
+.br
+Allowed values: \fBtrue\fR/\fBfalse\fR (Default: false)
+.TP
+\fBox_trust_mode\fR
+Accepts the strings \fBtofu\fR and \fBblind\fR\. Tofu means trust on first 
usage and accepts any OX key if none is stored in the cache yet\. If the key ID 
changes go\-sendxmpp will refuse to encrypt\. Blind means go\-sendxmpp will 
import and trust any new key that is advertised by the recipient\.
+.br
+Allowed values: \fBtofu\fR/\fBblind\fR (Default: blind)
 .SH "AUTHOR"
 Written by Martin Dosch\.
 .SH "REPORTING BUGS"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/go-sendxmpp-v0.16.0/man/go-sendxmpp.5.html 
new/go-sendxmpp-v0.17.0/man/go-sendxmpp.5.html
--- old/go-sendxmpp-v0.16.0/man/go-sendxmpp.5.html      2026-06-20 
09:54:34.000000000 +0200
+++ new/go-sendxmpp-v0.17.0/man/go-sendxmpp.5.html      2026-07-24 
21:50:15.000000000 +0200
@@ -90,7 +90,10 @@
 password: [<var>your_jabber_password</var>]  <br>
 eval_password: [<var>command_to_unlock_your_password</var>]  <br>
 alias: [<var>your_alias</var>]  <br>
-allow_plain: [<var>bool</var>]</p>
+allow_plain: [<var>bool</var>]  <br>
+no_root_warning: [<var>bool</var>]  <br>
+no_legacy_pgp_warning: [<var>bool</var>]  <br>
+ox_trust_mode: [blind|tofu]</p>
 
 <h2 id="REQUIRED-SETTINGS">REQUIRED SETTINGS</h2>
 
@@ -123,6 +126,22 @@
 connection using a SCRAM authentication mechanism before. In that case PLAIN 
is not allowed to prevent 
 downgrades by a man-in-the-middle attack.  <br>
 Allowed values: <strong>true</strong>/<strong>false</strong> (Default: 
false)</dd>
+<dt><code>no_root_warning</code></dt>
+<dd>Boolean to disable the warning when running as root. Note that running as 
root is discouraged although 
+go-sendxmpp tries to drop the root privileges before doing network calls. Be 
aware that <strong>OX</strong>,
+<strong>legacy PGP</strong> and the pinning to not use <strong>PLAIN</strong> 
authentication after a successful <strong>SCRAM</strong>
+authentication are disabled as those need file system access in the private 
user scope after dropping
+the privileges.  <br>
+Allowed values: <strong>true</strong>/<strong>false</strong> (Default: 
false)</dd>
+<dt><code>no_legacy_pgp_warning</code></dt>
+<dd>Boolean to disable the warning when using <strong>legacy PGP</strong>. 
Please note, that *legacy PGP** is deprecated
+and will be removed in an upcoming update. It is highly recommended to use OX 
instead.  <br>
+Allowed values: <strong>true</strong>/<strong>false</strong> (Default: 
false)</dd>
+<dt><code>ox_trust_mode</code></dt>
+<dd>Accepts the strings <strong>tofu</strong> and <strong>blind</strong>. Tofu 
means trust on first usage and accepts any OX key if none
+is stored in the cache yet. If the key ID changes go-sendxmpp will refuse to 
encrypt. Blind means
+go-sendxmpp will import and trust any new key that is advertised by the 
recipient.  <br>
+Allowed values: <strong>tofu</strong>/<strong>blind</strong> (Default: 
blind)</dd>
 </dl>
 
 <h2 id="AUTHOR">AUTHOR</h2>
@@ -144,7 +163,7 @@
 
   <ol class='man-decor man-foot man foot'>
     <li class='tl'></li>
-    <li class='tc'>May 2026</li>
+    <li class='tc'>July 2026</li>
     <li class='tr'>go-sendxmpp(5)</li>
   </ol>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/go-sendxmpp-v0.16.0/man/go-sendxmpp.5.ronn 
new/go-sendxmpp-v0.17.0/man/go-sendxmpp.5.ronn
--- old/go-sendxmpp-v0.16.0/man/go-sendxmpp.5.ronn      2026-06-20 
09:54:34.000000000 +0200
+++ new/go-sendxmpp-v0.17.0/man/go-sendxmpp.5.ronn      2026-07-24 
21:50:15.000000000 +0200
@@ -15,7 +15,10 @@
 password: [<your_jabber_password>]    
 eval_password: [<command_to_unlock_your_password>]    
 alias: [<your_alias>]    
-allow_plain: [<bool>]
+allow_plain: [<bool>]    
+no_root_warning: [<bool>]    
+no_legacy_pgp_warning: [<bool>]    
+ox_trust_mode: [blind|tofu]    
 
 ## REQUIRED SETTINGS
 
@@ -54,6 +57,25 @@
 downgrades by a man-in-the-middle attack.    
 Allowed values: **true**/**false** (Default: false)
 
+* `no_root_warning`:
+Boolean to disable the warning when running as root. Note that running as root 
is discouraged although 
+go-sendxmpp tries to drop the root privileges before doing network calls. Be 
aware that **OX**,
+**legacy PGP** and the pinning to not use **PLAIN** authentication after a 
successful **SCRAM**
+authentication are disabled as those need file system access in the private 
user scope after dropping
+the privileges.    
+Allowed values: **true**/**false** (Default: false)
+
+* `no_legacy_pgp_warning`:
+Boolean to disable the warning when using **legacy PGP**. Please note, that 
*legacy PGP** is deprecated
+and will be removed in an upcoming update. It is highly recommended to use OX 
instead.    
+Allowed values: **true**/**false** (Default: false)
+
+* `ox_trust_mode`:
+Accepts the strings **tofu** and **blind**. Tofu means trust on first usage 
and accepts any OX key if none
+is stored in the cache yet. If the key ID changes go-sendxmpp will refuse to 
encrypt. Blind means
+go-sendxmpp will import and trust any new key that is advertised by the 
recipient.    
+Allowed values: **tofu**/**blind** (Default: blind)
+
 ## AUTHOR
 
 Written by Martin Dosch.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/go-sendxmpp-v0.16.0/ox.go 
new/go-sendxmpp-v0.17.0/ox.go
--- old/go-sendxmpp-v0.16.0/ox.go       2026-06-20 09:54:34.000000000 +0200
+++ new/go-sendxmpp-v0.17.0/ox.go       2026-07-24 21:50:15.000000000 +0200
@@ -3,6 +3,8 @@
 // license that can be found in the LICENSE file.
 
 // TODO: Add more logging
+// TODO: Deduplicate code
+
 package main
 
 import (
@@ -50,7 +52,7 @@
 }
 
 func oxDecrypt(m xmpp.Chat, client *xmpp.Client, iqc chan xmpp.IQ, drc chan 
xmpp.DiscoResult,
-       user string, oxPrivKey *crypto.Key,
+       user string, oxPrivKey *crypto.Key, oxTrustMode string,
 ) (string, time.Time, error) {
        var cryptMsgByte []byte
        var senderKeyRing *crypto.KeyRing
@@ -72,12 +74,12 @@
        }
        slog.Info("ox: decrypting: getting senders key", "sender", sender)
        if m.Type == "groupchat" {
-               senderKeyRing, err = oxGetPublicKeyRingChatroom(client, iqc, 
drc, sender)
+               senderKeyRing, err = oxGetPublicKeyRingChatroom(client, iqc, 
drc, sender, oxTrustMode)
                if err != nil {
                        return strError, time.Now(), err
                }
        } else {
-               senderKeyRing, err = oxGetPublicKeyRing(client, iqc, sender)
+               senderKeyRing, err = oxGetPublicKeyRing(client, iqc, sender, 
oxTrustMode)
                if err != nil {
                        return strError, time.Now(), err
                }
@@ -166,7 +168,9 @@
        return false
 }
 
-func oxImportPrivKey(jid string, privKeyLocation string, client *xmpp.Client, 
iqc chan xmpp.IQ) error {
+func oxImportPrivKey(jid string, privKeyLocation string, client *xmpp.Client, 
iqc chan xmpp.IQ,
+       oxTrustMode string,
+) error {
        xmppURI := "xmpp:" + jid
        slog.Info("ox: importing private key:", "xmpp-uri", xmppURI)
        slog.Info("ox: importing private key:", "location", privKeyLocation)
@@ -235,7 +239,7 @@
                return fmt.Errorf("ox: import privkey: failed to store private 
key: %w", err)
        }
        slog.Info("ox: importing private key: requesting public key ring")
-       pubKeyRing, err := oxGetPublicKeyRing(client, iqc, jid)
+       pubKeyRing, err := oxGetPublicKeyRing(client, iqc, jid, oxTrustMode)
        if err == nil {
                slog.Info("ox: importing private key: checking if public key 
ring contains", "fingerprint", fingerprint)
                pubKeys := pubKeyRing.GetKeys()
@@ -420,13 +424,13 @@
        return dataFile, nil
 }
 
-func oxGetPubKeyLoc(fingerprint string) (string, error) {
+func oxGetPubKeyLoc(jid string) (string, error) {
        dataDir, err := getDataPath("oxpubkeys/", true)
        slog.Info("ox: getting public key", "location", dataDir)
        if err != nil {
                return strError, fmt.Errorf("ox: get pubkey location: %w", err)
        }
-       dataFile := dataDir + fingerprint
+       dataFile := dataDir + fsFriendlyJid(jid)
        slog.Info("ox: getting public key location", "file", dataFile)
        return dataFile, nil
 }
@@ -593,34 +597,37 @@
                return nil, fmt.Errorf("ox: no item element in reply to public 
key request")
        }
        slog.Info("ox: receiving public keys: received public keys")
-       oxPublicKeyXMLPubkeys := oxPublicKeyXMLItem.SelectElements("pubkey")
-       for _, r := range oxPublicKeyXMLPubkeys {
-               slog.Info("ox: receiving public keys: decoding public", "key", 
r)
-               data := r.SelectElement("data")
-               if data == nil {
-                       continue
-               }
-               decodedPubKey, err := 
base64.StdEncoding.DecodeString(data.Text())
-               if err != nil {
-                       return nil, fmt.Errorf("ox: receive public keys: failed 
to decode public key: %w", err)
-               }
-               key, err := crypto.NewKey(decodedPubKey)
-               if err != nil {
-                       return nil, fmt.Errorf("ox: receive public keys: failed 
to decode public key: %w", err)
-               }
-               slog.Info("ox: receiving public keys: checking for expiry:", 
"fingerprint", fingerprint)
-               if key.IsExpired(time.Now().Unix()) {
-                       return nil, fmt.Errorf("ox: key is expired: %s", 
fingerprint)
-               }
-               err = keyring.AddKey(key)
-               if err != nil {
-                       return nil, fmt.Errorf("ox: receive public keys: failed 
adding public key to keyring: %w", err)
-               }
+       oxPublicKeyXMLPubkey := oxPublicKeyXMLItem.SelectElements("pubkey")
+       slog.Info("ox: receiving public keys: decoding public", "key", 
oxPublicKeyXMLPubkey[0])
+       data := oxPublicKeyXMLPubkey[0].SelectElement("data")
+       if data == nil {
+               return nil, fmt.Errorf("ox: receive public keys: no data 
element")
+       }
+       decodedPubKey, err := base64.StdEncoding.DecodeString(data.Text())
+       if err != nil {
+               return nil, fmt.Errorf("ox: receive public keys: failed to 
decode public key: %w", err)
+       }
+       key, err := crypto.NewKey(decodedPubKey)
+       if err != nil {
+               return nil, fmt.Errorf("ox: receive public keys: failed to 
decode public key: %w", err)
+       }
+       if strings.ToUpper(key.GetFingerprint()) != fingerprint {
+               return nil, fmt.Errorf("ox: receive public keys: received key 
has fingerprint %s but %s was advertised",
+                       strings.ToUpper(key.GetFingerprint()), fingerprint)
+       }
+       slog.Info("ox: receiving public keys: checking for expiry:", 
"fingerprint", fingerprint)
+       if key.IsExpired(time.Now().Unix()) {
+               return nil, fmt.Errorf("ox: key is expired: %s", fingerprint)
+       }
+       err = keyring.AddKey(key)
+       if err != nil {
+               return nil, fmt.Errorf("ox: receive public keys: failed adding 
public key to keyring: %w", err)
        }
        return keyring, nil
 }
 
-func oxGetPublicKeyRing(client *xmpp.Client, iqc chan xmpp.IQ, recipient 
string) (*crypto.KeyRing, error) {
+func oxGetPublicKeyRing(client *xmpp.Client, iqc chan xmpp.IQ, recipient 
string, oxTrustMode string,
+) (*crypto.KeyRing, error) {
        publicKeyRing, err := crypto.NewKeyRing(nil)
        if err != nil {
                return nil, fmt.Errorf("ox: get public keyring: failed to 
create a new keyring: %w", err)
@@ -696,7 +703,7 @@
                return nil, fmt.Errorf("ox: server didn't provide public key 
fingerprints for %s", recipient)
        }
 
-       pubKeyRingLocation, err := oxGetPubKeyLoc(pubKeyRingID)
+       pubKeyRingLocation, err := oxGetPubKeyLoc(recipient)
        if err != nil {
                return nil, fmt.Errorf("ox: get public keyring: failed to get 
public key ring location: %w", err)
        }
@@ -710,31 +717,37 @@
                        if err != nil {
                                return nil, fmt.Errorf("ox: get public keyring: 
failed to parse time for saved key: %w", err)
                        }
-                       if !savedKeysDate.Before(newestKey) {
-                               pubKeys := 
pubKeyReadXML.SelectElements("pubkey")
-                               if pubKeys == nil {
-                                       return nil, fmt.Errorf("ox: couldn't 
read public keys from cache")
-                               }
-                               for _, r := range pubKeys {
-                                       keyByte, err := 
base64.StdEncoding.DecodeString(r.Text())
-                                       if err != nil {
-                                               return nil, fmt.Errorf("ox: get 
public keyring: failed to decode saved key: %w", err)
-                                       }
-                                       key, err := crypto.NewKey(keyByte)
-                                       if err != nil {
-                                               return nil, fmt.Errorf("ox: get 
public keyring: failed to parse saved key: %w", err)
-                                       }
-                                       if !key.IsExpired(time.Now().Unix()) {
-                                               err = publicKeyRing.AddKey(key)
-                                               if err != nil {
-                                                       return nil, 
fmt.Errorf("ox: get public keyring: failed to add key to public keyring: %w", 
err)
-                                               }
-                                       }
+                       pubKey := pubKeyReadXML.SelectElements("pubkey")
+                       if pubKey == nil {
+                               return nil, fmt.Errorf("ox: couldn't read 
public key from cache")
+                       }
+                       keyByte, err := 
base64.StdEncoding.DecodeString(pubKey[0].Text())
+                       if err != nil {
+                               return nil, fmt.Errorf("ox: get public keyring: 
failed to decode saved key: %w", err)
+                       }
+                       key, err := crypto.NewKey(keyByte)
+                       if err != nil {
+                               return nil, fmt.Errorf("ox: get public keyring: 
failed to parse saved key: %w", err)
+                       }
+                       if !key.IsExpired(time.Now().Unix()) {
+                               err = publicKeyRing.AddKey(key)
+                               if err != nil {
+                                       return nil, fmt.Errorf("ox: get public 
keyring: failed to add key to public keyring: %w", err)
                                }
+                       }
+                       storedKeyFingerprint := 
strings.ToUpper(key.GetFingerprint())
+                       if oxTrustMode != "blind" && storedKeyFingerprint != 
pubKeyRingID {
+                               return nil, fmt.Errorf("ox: get public keyring: 
failed to use stored key %s for %s as server advertises %s but trust mode is 
set to \"%s\"",
+                                       storedKeyFingerprint, recipient, 
pubKeyRingID, oxTrustMode)
+                       }
+                       if !savedKeysDate.Before(newestKey) {
                                if publicKeyRing.CanEncrypt(time.Now().Unix()) {
                                        return publicKeyRing, nil
                                }
                        }
+               } else if oxTrustMode != "blind" {
+                       return nil, fmt.Errorf("ox: get public keyring: failed 
to parse date of store key for %s but trust mode is set to \"%s\"",
+                               recipient, oxTrustMode)
                }
        }
        pubKeyRing, err := oxRecvPublicKeys(client, iqc, recipient, 
pubKeyRingID)
@@ -834,7 +847,7 @@
 }
 
 func oxGetPublicKeyRingChatroom(client *xmpp.Client, iqc chan xmpp.IQ, drc 
chan xmpp.DiscoResult,
-       chatroom string,
+       chatroom string, oxTrustMode string,
 ) (*crypto.KeyRing, error) {
        keyRing, err := crypto.NewKeyRing(nil)
        if err != nil {
@@ -886,7 +899,7 @@
                        if jid == nil {
                                continue
                        }
-                       memberKeyRing, err := oxGetPublicKeyRing(client, iqc, 
jid.Value)
+                       memberKeyRing, err := oxGetPublicKeyRing(client, iqc, 
jid.Value, oxTrustMode)
                        if err != nil {
                                continue
                        }
@@ -903,3 +916,229 @@
        }
        return keyRing, nil
 }
+
+func oxCheckPrivKeyNode(jid string, client *xmpp.Client, iqc chan xmpp.IQ) 
(xmpp.IQ, error) {
+       slog.Info("ox: parsing private key request to IQ")
+       recvPrivKeyRequest := etree.NewDocument()
+       recvPrivKeyRequest.WriteSettings.AttrSingleQuote = true
+       rpkrPubsub := recvPrivKeyRequest.CreateElement("pubsub")
+       rpkrPubsub.CreateAttr("xmlns", nsPubsub)
+       rpkrItems := rpkrPubsub.CreateElement("items")
+       rpkrItems.CreateAttr("node", nsOxSecretKey)
+       rpkrItems.CreateAttr("max_items", "1")
+       rpkr, err := recvPrivKeyRequest.WriteToString()
+       if err != nil {
+               return xmpp.IQ{}, fmt.Errorf("ox: check private key node: 
failed to create private key request: %w", err)
+       }
+       slog.Info("ox: sending IQ to request private key", "node", 
nsOxSecretKey)
+       oxPrivKeyReply, err := sendIQ(client, iqc, jid, "get", rpkr)
+       if err != nil {
+               return xmpp.IQ{}, fmt.Errorf("ox: check private key node: 
failed parsing iq reply to private key request: %w", err)
+       }
+       if oxPrivKeyReply.Type != strResult {
+               slog.Info("ox: received iq reply", "error", 
oxPrivKeyReply.Error.Condition)
+               if oxPrivKeyReply.Error.Condition == "item-not-found" {
+                       slog.Info("ox: creating pep node")
+                       createPrivKeyNodeRequest := etree.NewDocument()
+                       createPrivKeyNodeRequest.WriteSettings.AttrSingleQuote 
= true
+                       cpnrPubsub := 
createPrivKeyNodeRequest.CreateElement("pubsub")
+                       cpnrPubsub.CreateAttr("xmlns", nsPubsub)
+                       cpnrCreate := cpnrPubsub.CreateElement("create")
+                       cpnrCreate.CreateAttr("node", nsOxSecretKey)
+                       cpnrConfigure := cpnrPubsub.CreateElement("configure")
+                       cpnrX := cpnrConfigure.CreateElement("x")
+                       cpnrX.CreateAttr("xmlns", nsJabberData)
+                       cpnrX.CreateAttr("type", "submit")
+                       cpnrField1 := cpnrX.CreateElement("field")
+                       cpnrField1.CreateAttr("var", "FORM_TYPE")
+                       cpnrField1.CreateAttr("type", "hidden")
+                       cpnrField1Val := cpnrField1.CreateElement("value")
+                       cpnrField1Val.CreateText(pubsubNodeConfig)
+                       cpnrField2 := cpnrX.CreateElement("field")
+                       cpnrField2.CreateAttr("var", "pubsub#access_model")
+                       cpnrField2Val := cpnrField2.CreateElement("value")
+                       cpnrField2Val.CreateText("whitelist")
+                       cpnrField3 := cpnrX.CreateElement("field")
+                       cpnrField3.CreateAttr("var", 
"pubsub#send_last_published_item")
+                       cpnrField3Val := cpnrField3.CreateElement("value")
+                       cpnrField3Val.CreateText("on_sub")
+                       cpnr, err := createPrivKeyNodeRequest.WriteToString()
+                       if err != nil {
+                               return xmpp.IQ{}, fmt.Errorf("ox: failed to 
create private key node: %w", err)
+                       }
+                       slog.Info("ox: sending IQ to create private key node", 
"node", nsOxSecretKey)
+                       oxCreateNodeReply, err := sendIQ(client, iqc, jid, 
"set", cpnr)
+                       if err != nil {
+                               return xmpp.IQ{}, fmt.Errorf("ox: create 
private key node: failed parsing iq reply to private key node creation request: 
%w", err)
+                       }
+                       if oxCreateNodeReply.Type != strResult {
+                               return xmpp.IQ{}, fmt.Errorf("ox: failed to 
create private key node: %s", oxCreateNodeReply.Error)
+                       }
+
+               }
+       }
+       return oxPrivKeyReply, nil
+}
+
+func oxRecvPrivKey(jid string, secret string, client *xmpp.Client, iqc chan 
xmpp.IQ, drc chan xmpp.DiscoResult,
+       oxTrustMode string,
+) error {
+       err := checkPEPsupport(jid, client, iqc, drc)
+       if err != nil {
+               return fmt.Errorf("ox: receive private key: %w", err)
+       }
+       oxPrivKeyReply, err := oxCheckPrivKeyNode(jid, client, iqc)
+       if err != nil {
+               return fmt.Errorf("ox: receive private key: %w", err)
+       }
+       slog.Info("ox: receiving private key: received result")
+       slog.Info("ox: receiving private key: receiving own private key from 
pubsub")
+       oxPrivKeyXML := etree.NewDocument()
+       err = oxPrivKeyXML.ReadFromBytes(oxPrivKeyReply.Query)
+       if err != nil {
+               return fmt.Errorf("ox: receive private key: failed parsing iq 
reply to private key request: %w", err)
+       }
+       oxPrivKeyXMLPubsub := oxPrivKeyXML.SelectElement("pubsub")
+       if oxPrivKeyXMLPubsub == nil {
+               return fmt.Errorf("ox: receive private key: no pubsub element 
in reply to private key request")
+       }
+       oxPrivKeyXMLItems := oxPrivKeyXMLPubsub.SelectElement("items")
+       if oxPrivKeyXMLItems == nil {
+               return fmt.Errorf("ox: receive private key: no items element in 
reply to private key request")
+       }
+       oxPrivKeyXMLItemsNode := oxPrivKeyXMLItems.SelectAttr("node")
+       if oxPrivKeyXMLItemsNode.Value != nsOxSecretKey {
+               return fmt.Errorf("ox: receive private key: wrong node: %s 
instead of %s", oxPrivKeyXMLItemsNode.Value, nsOxSecretKey)
+       }
+       oxPrivKeyXMLItem := oxPrivKeyXMLItems.SelectElement("item")
+       if oxPrivKeyXMLItem == nil {
+               return fmt.Errorf("ox: receive private key: no item element in 
reply to private key request")
+       }
+       oxPrivKeyXMLSecretKey := oxPrivKeyXMLItem.SelectElement("secretkey")
+       if oxPrivKeyXMLSecretKey == nil {
+               return fmt.Errorf("ox: receive private key: no secretkey 
element in reply to private key request")
+       }
+       if oxPrivKeyXMLSecretKey.NamespaceURI() != nsOx {
+               return fmt.Errorf("ox: receive private key: wrong namespace: %s 
instead of %s", oxPrivKeyXMLSecretKey.NamespaceURI(), nsOx)
+       }
+       decodedPrivKey, err := 
base64.StdEncoding.DecodeString(oxPrivKeyXMLSecretKey.Text())
+       if err != nil {
+               return fmt.Errorf("ox: receive private key: failed to decode 
private key: %w", err)
+       }
+       pgpDecrypt, err := crypto.PGP().
+               Decryption().
+               Password([]byte(secret)).
+               New()
+       if err != nil {
+               return fmt.Errorf("ox: receive private key: failed to decode 
private key: %w", err)
+       }
+       oxPrivKey, err := pgpDecrypt.Decrypt(decodedPrivKey, crypto.Bytes)
+       if err != nil {
+               return fmt.Errorf("ox receive private key: failed to decrypt 
private key: %w", err)
+       }
+       location, err := oxGetPrivKeyLoc(jid)
+       if err != nil {
+               return fmt.Errorf("ox: receive private key: failed to determine 
private key location: %w", err)
+       }
+       slog.Info("ox: receive private key: getting private key location", 
"jid", jid, "location", location)
+       key, err := crypto.NewKey(oxPrivKey.Bytes())
+       if err != nil {
+               return fmt.Errorf("ox: receive private key: failed to read 
private key: %w", err)
+       }
+       keySerialized, err := key.Serialize()
+       if err != nil {
+               return fmt.Errorf("ox: receive private key: failed to serialize 
private key: %w", err)
+       }
+       err = oxStoreKey(location, 
base64.StdEncoding.EncodeToString(keySerialized))
+       if err != nil {
+               return fmt.Errorf("ox: receive private key: failed to store 
private key: %w", err)
+       }
+       slog.Info("ox: receive private key: requesting public key ring")
+       fingerprint := key.GetFingerprint()
+       pubKeyRing, err := oxGetPublicKeyRing(client, iqc, jid, oxTrustMode)
+       if err == nil {
+               slog.Info("ox: receive private key: checking if public key ring 
contains", "fingerprint", fingerprint)
+               pubKeys := pubKeyRing.GetKeys()
+               for _, r := range pubKeys {
+                       fp := strings.ToUpper(r.GetFingerprint())
+                       slog.Info("ox: receive private key: checking", 
"fingerprint", fp)
+                       if fp == fingerprint {
+                               slog.Info("ox: receive private key: checking if 
public key ring contains fingerprint: success")
+                               return nil
+                       }
+               }
+       }
+       pk, err := key.GetPublicKey()
+       if err != nil {
+               return fmt.Errorf("ox: receive private key: failed to derive 
public key: %w", err)
+       }
+       pubKey, err := crypto.NewKey(pk)
+       if err != nil {
+               return fmt.Errorf("ox: receive private key: failed to serialize 
public key: %w", err)
+       }
+       slog.Info("ox: import private key: publishing public key")
+       err = oxPublishPubKey(jid, client, iqc, pubKey)
+       if err != nil {
+               return fmt.Errorf("ox: receive private key: failed to publish 
public key: %w", err)
+       }
+       return nil
+}
+
+func oxTransPrivKey(jid string, oxPrivKey *crypto.Key, client *xmpp.Client, 
iqc chan xmpp.IQ, drc chan xmpp.DiscoResult) error {
+       err := checkPEPsupport(jid, client, iqc, drc)
+       if err != nil {
+               return fmt.Errorf("ox: transfer private key: %w", err)
+       }
+       slog.Info("ox: transferring private key: generating secret")
+       secret, err := getSecret(oxSecretAlphabet, oxSecretLength, 
oxSecretChunkSize)
+       if err != nil {
+               return fmt.Errorf("ox: transfer private key: failed to get 
secret: %w", err)
+       }
+       slog.Info("ox: transferring private key: creating encryption interface")
+       pgpEncrypt, err := crypto.PGP().
+               Encryption().
+               Password([]byte(secret)).
+               New()
+       if err != nil {
+               return fmt.Errorf("ox: transfer private key: failed to create 
pgp encryption interface: %w", err)
+       }
+       slog.Info("ox: transferring private key: serializing private key")
+       oxPrivKeySerialized, err := oxPrivKey.Serialize()
+       if err != nil {
+               return fmt.Errorf("ox: transfer private key: failed to 
serialize private key: %w", err)
+       }
+       slog.Info("ox: transferring private key: encrypting private key")
+       pgpMessage, err := pgpEncrypt.Encrypt(oxPrivKeySerialized)
+       if err != nil {
+               return fmt.Errorf("ox: transfer private key: failed to create 
pgp message: %w", err)
+       }
+       _, err = oxCheckPrivKeyNode(jid, client, iqc)
+       if err != nil {
+               return fmt.Errorf("ox: transfer private key: %w", err)
+       }
+       slog.Info("ox: publishing private key")
+       publishPrivKeyRequest := etree.NewDocument()
+       publishPrivKeyRequest.WriteSettings.AttrSingleQuote = true
+       ppkrPubsub := publishPrivKeyRequest.CreateElement("pubsub")
+       ppkrPubsub.CreateAttr("xmlns", nsPubsub)
+       ppkrPublish := ppkrPubsub.CreateElement("publish")
+       ppkrPublish.CreateAttr("node", nsOxSecretKey)
+       ppkrItem := ppkrPublish.CreateElement("item")
+       ppkrSecretKey := ppkrItem.CreateElement("secretkey")
+       ppkrSecretKey.CreateAttr("xmlns", nsOx)
+       
ppkrSecretKey.CreateText(base64.StdEncoding.EncodeToString(pgpMessage.Bytes()))
+       ppkr, err := publishPrivKeyRequest.WriteToString()
+       if err != nil {
+               return fmt.Errorf("ox: failed to create private key publish 
request: %w", err)
+       }
+       slog.Info("ox: sending IQ to publish private key")
+       oxPublishPrivateKeyReply, err := sendIQ(client, iqc, jid, "set", ppkr)
+       if err != nil {
+               return fmt.Errorf("ox: publish private key: failed parsing iq 
reply to publish private key request: %w", err)
+       }
+       if oxPublishPrivateKeyReply.Type != strResult {
+               return fmt.Errorf("ox: publish private key: %s", 
oxPublishPrivateKeyReply.Error.Condition)
+       }
+       fmt.Printf("OX: Secret key published: Encrypted with the secret phrase: 
%s\n", secret)
+       return nil
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/go-sendxmpp-v0.16.0/parseconfig.go 
new/go-sendxmpp-v0.17.0/parseconfig.go
--- old/go-sendxmpp-v0.16.0/parseconfig.go      2026-06-20 09:54:34.000000000 
+0200
+++ new/go-sendxmpp-v0.17.0/parseconfig.go      2026-07-24 21:50:15.000000000 
+0200
@@ -52,6 +52,8 @@
 // Opens the config file and returns the specified values
 // for username, server and port.
 func parseConfig(configPath string) (output configuration, err error) {
+       // Set default OX trust mode to "blind"
+       output.oxTrustMode = "blind"
        // Use $XDG_CONFIG_HOME/.config/go-sendxmpp/config,
        // $XDG_CONFIG_HOME/.config/go-sendxmpp/sendxmpprc or
        // ~/.sendxmpprc if no config path is specified.
@@ -95,7 +97,7 @@
                column := strings.SplitN(scanner.Text(), " ", 
defaultConfigColumnSep)
 
                if len(column) > 1 {
-                       switch column[0] {
+                       switch strings.ToLower(column[0]) {
                        case "username:":
                                output.username = column[1]
                        case "jserver:":
@@ -131,6 +133,23 @@
                                } else {
                                        output.allowPLAIN = false
                                }
+                       case "no_root_warning:":
+                               if strings.ToLower(column[1]) == "true" {
+                                       output.noRootWarn = true
+                               } else {
+                                       output.noRootWarn = false
+                               }
+                       case "no_legacy_pgp_warning:":
+                               if strings.ToLower(column[1]) == "true" {
+                                       output.noLPGPWarn = true
+                               } else {
+                                       output.noLPGPWarn = false
+                               }
+                       case "ox_trust_mode:":
+                               switch strings.ToLower(column[1]) {
+                               case "tofu", "blind":
+                                       output.oxTrustMode = 
strings.ToLower(column[1])
+                               }
                        default:
                                // Try to parse legacy sendxmpp config files.
                                if len(column) >= defaultConfigColumnSep && 
strings.Contains(column[0], "@") {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/go-sendxmpp-v0.16.0/stanzahandling.go 
new/go-sendxmpp-v0.17.0/stanzahandling.go
--- old/go-sendxmpp-v0.16.0/stanzahandling.go   2026-06-20 09:54:34.000000000 
+0200
+++ new/go-sendxmpp-v0.17.0/stanzahandling.go   2026-07-24 21:50:15.000000000 
+0200
@@ -171,8 +171,8 @@
                Text    string   `xml:"text"`
        }
        var me messageError
-       var received interface{}
-       r := make(chan interface{}, defaultBufferSize)
+       var received any
+       r := make(chan any, defaultBufferSize)
        e := make(chan error, defaultBufferSize)
        go func() {
                for {

++++++ vendor.tar.zst ++++++
++++ 3196 lines of diff (skipped)

Reply via email to