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 2024-04-02 16:40:23 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/go-sendxmpp (Old) and /work/SRC/openSUSE:Factory/.go-sendxmpp.new.1905 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "go-sendxmpp" Tue Apr 2 16:40:23 2024 rev:14 rq:1163539 version:0.9.0 Changes: -------- --- /work/SRC/openSUSE:Factory/go-sendxmpp/go-sendxmpp.changes 2024-03-10 20:26:15.213600167 +0100 +++ /work/SRC/openSUSE:Factory/.go-sendxmpp.new.1905/go-sendxmpp.changes 2024-04-02 16:40:40.313382480 +0200 @@ -1,0 +2,17 @@ +Fri Mar 29 14:06:47 UTC 2024 - Michael Vetter <mvet...@suse.com> + +- Update to 0.9.0: + Changed: + * Properly close stream if Ctrl+C is pressed in interactive mode. + * Properly close stream if Ctrl+C is pressed in listening mode. + * Print OS, architecture and go version for flag --version. + * Improve closing of connection (via go-xmpp v0.1.4). + * Don't send stanzas that exceed the size limit provided by + XEP-0478 (requires go-xmpp >= v0.1.4). + * Fixed hanging forever in stream close if the server doesn't + reply with a closing stream element (via go-xmpp >= v0.1.4). + Added: + * New command line flag ssdp-off to disable XEP-0474: SASL SCRAM + Downgrade Protection (requires go-xmpp >= v0.1.4). + +------------------------------------------------------------------- Old: ---- go-sendxmpp-0.8.4.tar.gz New: ---- go-sendxmpp-0.9.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ go-sendxmpp.spec ++++++ --- /var/tmp/diff_new_pack.Wz4zwV/_old 2024-04-02 16:40:41.861439529 +0200 +++ /var/tmp/diff_new_pack.Wz4zwV/_new 2024-04-02 16:40:41.861439529 +0200 @@ -17,7 +17,7 @@ Name: go-sendxmpp -Version: 0.8.4 +Version: 0.9.0 Release: 0 Summary: A little tool to send messages to an XMPP contact or MUC License: BSD-2-Clause ++++++ _service ++++++ --- /var/tmp/diff_new_pack.Wz4zwV/_old 2024-04-02 16:40:41.889440561 +0200 +++ /var/tmp/diff_new_pack.Wz4zwV/_new 2024-04-02 16:40:41.893440708 +0200 @@ -3,7 +3,7 @@ <param name="url">https://salsa.debian.org/mdosch/go-sendxmpp.git</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v0.8.4</param> + <param name="revision">v0.9.0</param> <param name="versionformat">@PARENT_TAG@</param> <param name="changesgenerate">disable</param> <param name="versionrewrite-pattern">v(.*)</param> ++++++ go-sendxmpp-0.8.4.tar.gz -> go-sendxmpp-0.9.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-sendxmpp-0.8.4/CHANGELOG.md new/go-sendxmpp-0.9.0/CHANGELOG.md --- old/go-sendxmpp-0.8.4/CHANGELOG.md 2024-03-09 21:13:48.000000000 +0100 +++ new/go-sendxmpp-0.9.0/CHANGELOG.md 2024-03-28 19:14:05.000000000 +0100 @@ -1,5 +1,17 @@ # Changelog +## [v0.9.0] 2024-03-28 +### Changed +- Properly close stream if `Ctrl+C` is pressed in interactive mode. +- Properly close stream if `Ctrl+C` is pressed in listening mode. +- Print OS, architecture and go version for flag `--version`. +- Improve closing of connection (via go-xmpp v0.1.4). +- Don't send stanzas that exceed the size limit provided by XEP-0478 (requires go-xmpp >= v0.1.4). +- Fixed hanging forever in stream close if the server doesn't reply with a closing stream element (via go-xmpp >= v0.1.4). + +### Added +- New command line flag `ssdp-off` to disable XEP-0474: SASL SCRAM Downgrade Protection (requires go-xmpp >= v0.1.4). + ## [v0.8.4] 2024-03-09 ### Changed - Properly handle lost connection. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-sendxmpp-0.8.4/README.md new/go-sendxmpp-0.9.0/README.md --- old/go-sendxmpp-0.8.4/README.md 2024-03-09 21:13:48.000000000 +0100 +++ new/go-sendxmpp-0.9.0/README.md 2024-03-28 19:14:05.000000000 +0100 @@ -75,7 +75,7 @@ the account details via command line options: ```plain -Usage: go-sendxmpp [-cdilnt] [-a value] [-f value] [--headline] [--help] [-h value] [-j value] [-m value] [--muc-password value] [--oob-file value] [--ox] [--ox-delete-nodes] [--ox-genprivkey-rsa] [--ox-genprivkey-x25519] [--ox-import-privkey value] [--ox-passphrase value] [-p value] [--raw] [--scram-mech-pinning value] [--timeout value] [--tls-version value] [-u value] [--version] [recipientsâ¦] +Usage: go-sendxmpp [-cdilnt] [-a value] [-f value] [--headline] [--help] [-h value] [-j value] [-m value] [--muc-password value] [--oob-file value] [--ox] [--ox-delete-nodes] [--ox-genprivkey-rsa] [--ox-genprivkey-x25519] [--ox-import-privkey value] [--ox-passphrase value] [-p value] [--raw] [--scram-mech-pinning value] [--ssdp-off] [--timeout value] [--tls-version value] [-u value] [--version] [recipientsâ¦] -a, --alias=value Set alias/nicknamefor chatrooms. -c, --chatroom Send message to a chatroom. -d, --debug Show debugging info. @@ -116,6 +116,7 @@ --raw Send raw XML. --scram-mech-pinning=value Enforce the use of a certain SCRAM authentication mechanism. + --ssdp-off Disable XEP-0474: SASL SCRAM Downgrade Protection. --timeout=value Connection timeout in seconds. [10] -t, --tls Use direct TLS. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-sendxmpp-0.8.4/const.go new/go-sendxmpp-0.9.0/const.go --- old/go-sendxmpp-0.8.4/const.go 2024-03-09 21:13:48.000000000 +0100 +++ new/go-sendxmpp-0.9.0/const.go 2024-03-28 19:14:05.000000000 +0100 @@ -5,7 +5,7 @@ package main const ( - version = "0.8.4" + version = "0.9.0" // defaults defaultBufferSize = 100 defaultConfigRowSep = 2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-sendxmpp-0.8.4/go.mod new/go-sendxmpp-0.9.0/go.mod --- old/go-sendxmpp-0.8.4/go.mod 2024-03-09 21:13:48.000000000 +0100 +++ new/go-sendxmpp-0.9.0/go.mod 2024-03-28 19:14:05.000000000 +0100 @@ -7,7 +7,7 @@ github.com/beevik/etree v1.3.0 github.com/gabriel-vasile/mimetype v1.4.3 github.com/pborman/getopt/v2 v2.1.0 - github.com/xmppo/go-xmpp v0.1.2 + github.com/xmppo/go-xmpp v0.1.4 salsa.debian.org/mdosch/xmppsrv v0.2.6 ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-sendxmpp-0.8.4/go.sum new/go-sendxmpp-0.9.0/go.sum --- old/go-sendxmpp-0.8.4/go.sum 2024-03-09 21:13:48.000000000 +0100 +++ new/go-sendxmpp-0.9.0/go.sum 2024-03-28 19:14:05.000000000 +0100 @@ -25,8 +25,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/xmppo/go-xmpp v0.1.2 h1:oQPd/wVUBb53AdbpsuIYwFPtBQC3gYaHaWbaLDev+oA= -github.com/xmppo/go-xmpp v0.1.2/go.mod h1:yyTnJMs6I6KUKv3BjXc4i3NU/iWBxY3yBGiUvUcW0Qg= +github.com/xmppo/go-xmpp v0.1.4 h1:1tsKAS6o8arxZl/29qFNfhzfMExmCTABSZQ6V1hJXv0= +github.com/xmppo/go-xmpp v0.1.4/go.mod h1:yyTnJMs6I6KUKv3BjXc4i3NU/iWBxY3yBGiUvUcW0Qg= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-sendxmpp-0.8.4/main.go new/go-sendxmpp-0.9.0/main.go --- old/go-sendxmpp-0.8.4/main.go 2024-03-09 21:13:48.000000000 +0100 +++ new/go-sendxmpp-0.9.0/main.go 2024-03-28 19:14:05.000000000 +0100 @@ -15,6 +15,7 @@ "net" "os" "os/signal" + "runtime" "strings" "time" @@ -37,6 +38,7 @@ if err != nil { log.Fatal(err) } + os.Exit(0) } func readMessage(messageFilePath string) (string, error) { @@ -132,6 +134,7 @@ 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.") + flagSSDPOff := getopt.BoolLong("ssdp-off", 0, "Disable XEP-0474: SASL SCRAM Downgrade Protection.") // Parse command line flags. getopt.Parse() @@ -143,7 +146,9 @@ os.Exit(0) case *flagVersion: // If requested, show version and quit. - fmt.Println("go-sendxmpp", version) + fmt.Println("Go-sendxmpp", version) + system := runtime.GOOS + "/" + runtime.GOARCH + fmt.Println("System:", system, runtime.Version()) fmt.Println("License: BSD-2-clause") os.Exit(0) // Quit if Ox (OpenPGP for XMPP) is requested for unsupported operations like @@ -281,6 +286,7 @@ Debug: *flagDebug, TLSConfig: &tlsConfig, Mechanism: *flagSCRAMPinning, + SSDP: !*flagSSDPOff, } // Read message from file. @@ -466,17 +472,20 @@ signal.Notify(c, os.Interrupt) go func() { for range c { - cancel() - client.Close() - os.Exit(0) + closeAndExit(client, cancel, nil) } }() for { message, err = reader.ReadString('\n') - message = strings.TrimSuffix(message, "\n") - if err != nil { - closeAndExit(client, cancel, errors.New("failed to read from stdin")) + select { + case <-ctx.Done(): + return + default: + if err != nil { + closeAndExit(client, cancel, errors.New("failed to read from stdin")) + } } + message = strings.TrimSuffix(message, "\n") // Remove invalid code points. message = validUTF8(message) @@ -513,6 +522,14 @@ } case *flagListen: tz := time.Now().Location() + // Quit if ^C is pressed. + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + go func() { + for range c { + closeAndExit(client, cancel, nil) + } + }() for { v := <-msgc switch { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-sendxmpp-0.8.4/man/go-sendxmpp.1 new/go-sendxmpp-0.9.0/man/go-sendxmpp.1 --- old/go-sendxmpp-0.8.4/man/go-sendxmpp.1 2024-03-09 21:13:48.000000000 +0100 +++ new/go-sendxmpp-0.9.0/man/go-sendxmpp.1 2024-03-28 19:14:05.000000000 +0100 @@ -1,12 +1,12 @@ .\" generated with Ronn-NG/v0.9.1 .\" http://github.com/apjanke/ronn-ng/tree/0.9.1 -.TH "GO\-SENDXMPP" "1" "January 2024" "" +.TH "GO\-SENDXMPP" "1" "March 2024" "" .SH "NAME" \fBgo\-sendxmpp\fR \- A tool to send messages to an XMPP contact or MUC\. .SH "SYNOPSIS" -\fBgo\-sendxmpp [\-cdilnt] [\-a value] [\-f value] [\-\-headline] [\-\-help] [\-h value] [\-j value] [\-m value] [\-\-muc\-password value] [\-\-oob\-file value] [\-\-ox] [\-\-ox\-delete\-nodes] [\-\-ox\-genprivkey\-rsa] [\-\-ox\-genprivkey\-x25519] [\-\-ox\-import\-privkey value] [\-\-ox\-passphrase value] [\-p value] [\-\-raw] [\-\-scram\-mech\-pinning value] [\-\-timeout value] [\-\-tls\-version value] [\-u value] [\-\-version] [recipientsâ¦]\fR +\fBgo\-sendxmpp [\-cdilnt] [\-a value] [\-f value] [\-\-headline] [\-\-help] [\-h value] [\-j value] [\-m value] [\-\-muc\-password value] [\-\-oob\-file value] [\-\-ox] [\-\-ox\-delete\-nodes] [\-\-ox\-genprivkey\-rsa] [\-\-ox\-genprivkey\-x25519] [\-\-ox\-import\-privkey value] [\-\-ox\-passphrase value] [\-p value] [\-\-raw] [\-\-scram\-mech\-pinning value] [\-\-ssdp\-off] [\-\-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 (but not as powerful as) \fBsendxmpp\fR\. +A tool to send messages to an XMPP contact or MUC inspired by \fBsendxmpp\fR\. .br You can either pipe a programs output to \fBgo\-sendxmpp\fR, write in your terminal (put \fB^D\fR in a new line to finish) or send the input from a file (\fB\-m\fR or \fB\-\-message\fR)\. The account data is expected at \fB~/\.config/go\-sendxmpp/config\fR (preferred), \fB~/\.config/go\-sendxmpp/sendxmpprc\fR (deprecated) \fB~/\.sendxmpprc\fR (for compatibility with the original perl sendxmpp) if no other configuration file location is specified with \fB\-f\fR or \fB\-\-file\fR\. .SH "OPTIONS" @@ -89,6 +89,9 @@ \fB\-\-scram\-mech\-pinning=[<value>]\fR Enforce the use of a certain SCRAM authentication mechanism\. Currently go\-sendxmpp supports \fBSCRAM\-SHA\-1\fR, \fBSCRAM\-SHA\-1\-PLUS\fR, \fBSCRAM\-SHA\-256\fR, \fBSCRAM\-SHA\-256\-PLUS\fR, \fBSCRAM\-SHA\-512\fR and \fBSCRAM\-SHA\-512\-PLUS\fR\. You should know what you are doing when using this setting and make sure the chosen mechanism is supported by the server\. If not set, go\-sendxmpp will use XEP\-0474 to prevent downgrade attacks (needs server support)\. .TP +\fB\-\-ssdp\-off\fR +Disable XEP\-0474: SASL SCRAM Downgrade Protection\. +.TP \fB\-\-timeout=\fR[\fIvalue\fR] Connection timeout in seconds\. (Default: 10) .TP diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-sendxmpp-0.8.4/man/go-sendxmpp.1.html new/go-sendxmpp-0.9.0/man/go-sendxmpp.1.html --- old/go-sendxmpp-0.8.4/man/go-sendxmpp.1.html 2024-03-09 21:13:48.000000000 +0100 +++ new/go-sendxmpp-0.9.0/man/go-sendxmpp.1.html 2024-03-28 19:14:05.000000000 +0100 @@ -82,12 +82,12 @@ <p><code>go-sendxmpp [-cdilnt] [-a value] [-f value] [--headline] [--help] [-h value] [-j value] [-m value] [--muc-password value] [--oob-file value] [--ox] [--ox-delete-nodes] [--ox-genprivkey-rsa] [--ox-genprivkey-x25519] [--ox-import-privkey value] -[--ox-passphrase value] [-p value] [--raw] [--scram-mech-pinning value] [--timeout value] [--tls-version value] [-u value] +[--ox-passphrase value] [-p value] [--raw] [--scram-mech-pinning value] [--ssdp-off] [--timeout value] [--tls-version value] [-u value] [--version] [recipientsâ¦]</code></p> <h2 id="DESCRIPTION">DESCRIPTION</h2> -<p>A tool to send messages to an XMPP contact or MUC inspired by (but not as powerful as) <code>sendxmpp</code>. <br> +<p>A tool to send messages to an XMPP contact or MUC inspired by <code>sendxmpp</code>. <br> You can either pipe a programs output to <code>go-sendxmpp</code>, write in your terminal (put <code>^D</code> in a new line to finish) or send the input from a file (<code>-m</code> or <code>--message</code>). The account data is expected at <code>~/.config/go-sendxmpp/config</code> (preferred), <code>~/.config/go-sendxmpp/sendxmpprc</code> @@ -191,6 +191,8 @@ and <strong>SCRAM-SHA-512-PLUS</strong>. You should know what you are doing when using this setting and make sure the chosen mechanism is supported by the server. If not set, go-sendxmpp will use XEP-0474 to prevent downgrade attacks (needs server support).</dd> +<dt><code>--ssdp-off</code></dt> +<dd>Disable XEP-0474: SASL SCRAM Downgrade Protection.</dd> <dt> <code>--timeout=</code>[<var>value</var>]</dt> <dd>Connection timeout in seconds. (Default: 10)</dd> @@ -254,7 +256,7 @@ <ol class='man-decor man-foot man foot'> <li class='tl'></li> - <li class='tc'>January 2024</li> + <li class='tc'>March 2024</li> <li class='tr'>go-sendxmpp(1)</li> </ol> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-sendxmpp-0.8.4/man/go-sendxmpp.1.ronn new/go-sendxmpp-0.9.0/man/go-sendxmpp.1.ronn --- old/go-sendxmpp-0.8.4/man/go-sendxmpp.1.ronn 2024-03-09 21:13:48.000000000 +0100 +++ new/go-sendxmpp-0.9.0/man/go-sendxmpp.1.ronn 2024-03-28 19:14:05.000000000 +0100 @@ -5,12 +5,12 @@ `go-sendxmpp [-cdilnt] [-a value] [-f value] [--headline] [--help] [-h value] [-j value] [-m value] [--muc-password value] [--oob-file value] [--ox] [--ox-delete-nodes] [--ox-genprivkey-rsa] [--ox-genprivkey-x25519] [--ox-import-privkey value] -[--ox-passphrase value] [-p value] [--raw] [--scram-mech-pinning value] [--timeout value] [--tls-version value] [-u value] +[--ox-passphrase value] [-p value] [--raw] [--scram-mech-pinning value] [--ssdp-off] [--timeout value] [--tls-version value] [-u value] [--version] [recipientsâ¦]` ## DESCRIPTION -A tool to send messages to an XMPP contact or MUC inspired by (but not as powerful as) `sendxmpp`. +A tool to send messages to an XMPP contact or MUC inspired by `sendxmpp`. You can either pipe a programs output to `go-sendxmpp`, write in your terminal (put `^D` in a new line to finish) or send the input from a file (`-m` or `--message`). The account data is expected at `~/.config/go-sendxmpp/config` (preferred), `~/.config/go-sendxmpp/sendxmpprc` @@ -117,6 +117,9 @@ make sure the chosen mechanism is supported by the server. If not set, go-sendxmpp will use XEP-0474 to prevent downgrade attacks (needs server support). +* `--ssdp-off`: +Disable XEP-0474: SASL SCRAM Downgrade Protection. + * `--timeout=`[<value>]: Connection timeout in seconds. (Default: 10) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/go-sendxmpp-0.8.4/stanzahandling.go new/go-sendxmpp-0.9.0/stanzahandling.go --- old/go-sendxmpp-0.8.4/stanzahandling.go 2024-03-09 21:13:48.000000000 +0100 +++ new/go-sendxmpp-0.9.0/stanzahandling.go 2024-03-28 19:14:05.000000000 +0100 @@ -45,8 +45,15 @@ } func rcvStanzas(client *xmpp.Client, iqc chan xmpp.IQ, msgc chan xmpp.Chat, ctx context.Context, cancel context.CancelFunc) { + var err error + var received interface{} for { - received, err := client.Recv() + select { + case <-ctx.Done(): + return + default: + received, err = client.Recv() + } // Don't print errors if the program is getting shut down, // as the errors might be triggered from trying to read from // a closed connection. ++++++ vendor.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/xmppo/go-xmpp/xmpp.go new/vendor/github.com/xmppo/go-xmpp/xmpp.go --- old/vendor/github.com/xmppo/go-xmpp/xmpp.go 2024-03-10 08:18:29.000000000 +0100 +++ new/vendor/github.com/xmppo/go-xmpp/xmpp.go 2024-03-29 15:06:34.000000000 +0100 @@ -38,6 +38,7 @@ "slices" "strconv" "strings" + "sync" "time" "golang.org/x/crypto/pbkdf2" @@ -45,13 +46,14 @@ ) const ( - nsStream = "http://etherx.jabber.org/streams" - nsTLS = "urn:ietf:params:xml:ns:xmpp-tls" - nsSASL = "urn:ietf:params:xml:ns:xmpp-sasl" - nsBind = "urn:ietf:params:xml:ns:xmpp-bind" - nsSASLCB = "urn:xmpp:sasl-cb:0" - nsClient = "jabber:client" - nsSession = "urn:ietf:params:xml:ns:xmpp-session" + nsStream = "http://etherx.jabber.org/streams" + nsTLS = "urn:ietf:params:xml:ns:xmpp-tls" + nsSASL = "urn:ietf:params:xml:ns:xmpp-sasl" + nsBind = "urn:ietf:params:xml:ns:xmpp-bind" + nsSASLCB = "urn:xmpp:sasl-cb:0" + nsClient = "jabber:client" + nsSession = "urn:ietf:params:xml:ns:xmpp-session" + nsStreamLimits = "urn:xmpp:stream-limits:0" ) // Default TLS configuration options @@ -73,12 +75,15 @@ // Client holds XMPP connection options type Client struct { - conn net.Conn // connection to server - jid string // Jabber ID for our connection - domain string - p *xml.Decoder - stanzaWriter io.Writer - Mechanism string + conn net.Conn // connection to server + jid string // Jabber ID for our connection + domain string + nextMutex sync.Mutex // Mutex to prevent multiple access to xml.Decoder + p *xml.Decoder + stanzaWriter io.Writer + LimitMaxBytes int // Maximum stanza size (XEP-0478: Stream Limits Advertisement) + LimitIdleSeconds int // Maximum idle seconds (XEP-0478: Stream Limits Advertisement) + Mechanism string } func (c *Client) JID() string { @@ -228,6 +233,9 @@ // Auth mechanism to use Mechanism string + + // XEP-0474: SASL SCRAM Downgrade Protection + SSDP bool } // NewClient establishes a new Client connection based on a set of Options. @@ -331,23 +339,22 @@ func (c *Client) Close() error { if c.conn != (*tls.Conn)(nil) { fmt.Fprintf(c.stanzaWriter, "</stream:stream>\n") + go func() { + <-time.After(10 * time.Second) + c.conn.Close() + }() // Wait for the server also closing the stream. for { - select { - case <-time.After(10 * time.Second): - break - default: - ee, err := nextEnd(c.p) - // If the server already closed the stream it is - // likely to receive an error when trying to parse - // the stream. Therefore the connection is also closed - // if an error is received. - if err != nil { - return c.conn.Close() - } - if ee.Name.Local == "stream" { - return c.conn.Close() - } + ee, err := c.nextEnd() + // If the server already closed the stream it is + // likely to receive an error when trying to parse + // the stream. Therefore the connection is also closed + // if an error is received. + if err != nil { + return c.conn.Close() + } + if ee.Name.Local == "stream" { + return c.conn.Close() } } } @@ -387,6 +394,20 @@ if err != nil { return err } + // Make the max. stanza size limit available. + if f.Limits.MaxBytes != "" { + c.LimitMaxBytes, err = strconv.Atoi(f.Limits.MaxBytes) + if err != nil { + c.LimitMaxBytes = 0 + } + } + // Make the servers time limit after which it might consider the stream idle available. + if f.Limits.IdleSeconds != "" { + c.LimitIdleSeconds, err = strconv.Atoi(f.Limits.IdleSeconds) + if err != nil { + c.LimitIdleSeconds = 0 + } + } // If the server requires we STARTTLS, attempt to do so. if f, err = c.startTLSIfRequired(f, o, domain); err != nil { @@ -536,7 +557,7 @@ fmt.Fprintf(c.stanzaWriter, "<auth xmlns='%s' mechanism='%s'>%s</auth>\n", nsSASL, mechanism, base64.StdEncoding.EncodeToString([]byte(clientFirstMessage))) var sfm string - _, val, err := next(c.p) + _, val, err := c.next() if err != nil { return err } @@ -580,7 +601,7 @@ if err != nil { return err } - case strings.HasPrefix(serverReply, "d="): + case strings.HasPrefix(serverReply, "d=") && o.SSDP: serverDgProtectHash := strings.SplitN(serverReply, "=", 2)[1] slices.Sort(f.Mechanisms.Mechanism) for _, mech := range f.Mechanisms.Mechanism { @@ -702,7 +723,7 @@ return fmt.Errorf("no viable authentication method available: %v", f.Mechanisms.Mechanism) } // Next message should be either success or failure. - name, val, err := next(c.p) + name, val, err := c.next() if err != nil { return err } @@ -743,6 +764,20 @@ if f, err = c.startStream(o, domain); err != nil { return err } + // Make the max. stanza size limit available. + if f.Limits.MaxBytes != "" { + c.LimitMaxBytes, err = strconv.Atoi(f.Limits.MaxBytes) + if err != nil { + c.LimitMaxBytes = 0 + } + } + // Make the servers time limit after which it might consider the stream idle available. + if f.Limits.IdleSeconds != "" { + c.LimitIdleSeconds, err = strconv.Atoi(f.Limits.IdleSeconds) + if err != nil { + c.LimitIdleSeconds = 0 + } + } // Generate a unique cookie cookie := getCookie() @@ -753,18 +788,30 @@ } else { fmt.Fprintf(c.stanzaWriter, "<iq type='set' id='%x'><bind xmlns='%s'><resource>%s</resource></bind></iq>\n", cookie, nsBind, o.Resource) } - var iq clientIQ - if err = c.p.DecodeElement(&iq, nil); err != nil { - return errors.New("unmarshal <iq>: " + err.Error()) + _, val, err = c.next() + if err != nil { + return err } - if &iq.Bind == nil { - return errors.New("<iq> result missing <bind>") + switch v := val.(type) { + case *streamError: + errorMessage := v.Text.Text + if errorMessage == "" { + // v.Any is type of sub-element in failure, + // which gives a description of what failed if there was no text element + errorMessage = v.Any.Space + } + return errors.New("stream error: " + errorMessage) + case *clientIQ: + if v.Bind.XMLName.Space == nsBind { + c.jid = v.Bind.Jid // our local id + c.domain = domain + } else { + return errors.New("bind: unexpected reply to xmpp-bind IQ") + } } - c.jid = iq.Bind.Jid // our local id - c.domain = domain - if o.Session { // if server support session, open it + cookie = getCookie() // generate new id value for session fmt.Fprintf(c.stanzaWriter, "<iq to='%s' type='set' id='%x'><session xmlns='%s'/></iq>\n", xmlEscape(domain), cookie, nsSession) } @@ -839,7 +886,7 @@ } // We expect the server to start a <stream>. - se, err := nextStart(c.p) + se, err := c.nextStart() if err != nil { return nil, err } @@ -906,10 +953,9 @@ } // Recv waits to receive the next XMPP stanza. -// Return type is either a presence notification or a chat message. func (c *Client) Recv() (stanza interface{}, err error) { for { - _, val, err := next(c.p) + _, val, err := c.next() if err != nil { return Chat{}, err } @@ -1164,11 +1210,15 @@ oobtext += `</x>` } - stanza := "<message to='%s' type='%s' id='%s' xml:lang='en'>" + subtext + "<body>%s</body>" + oobtext + thdtext + "</message>\n" - chat.Text = validUTF8(chat.Text) - return fmt.Fprintf(c.stanzaWriter, stanza, + stanza := fmt.Sprintf("<message to='%s' type='%s' id='%s' xml:lang='en'>"+subtext+"<body>%s</body>"+oobtext+thdtext+"</message>\n", xmlEscape(chat.Remote), xmlEscape(chat.Type), cnonce(), xmlEscape(chat.Text)) + if c.LimitMaxBytes != 0 && len(stanza) > c.LimitMaxBytes { + return 0, fmt.Errorf("stanza size (%v bytes) exceeds server limit (%v bytes)", + len(stanza), c.LimitMaxBytes) + } + + return fmt.Fprint(c.stanzaWriter, stanza) } // SendOOB sends OOB data wrapped inside an XMPP message stanza, without actual body. @@ -1184,13 +1234,23 @@ } oobtext += `</x>` } - return fmt.Fprintf(c.stanzaWriter, "<message to='%s' type='%s' id='%s' xml:lang='en'>"+oobtext+thdtext+"</message>\n", + stanza := fmt.Sprintf("<message to='%s' type='%s' id='%s' xml:lang='en'>"+oobtext+thdtext+"</message>\n", xmlEscape(chat.Remote), xmlEscape(chat.Type), cnonce()) + if c.LimitMaxBytes != 0 && len(stanza) > c.LimitMaxBytes { + return 0, fmt.Errorf("stanza size (%v bytes) exceeds server limit (%v bytes)", + len(stanza), c.LimitMaxBytes) + } + return fmt.Fprint(c.stanzaWriter, stanza) } // SendOrg sends the original text without being wrapped in an XMPP message stanza. func (c *Client) SendOrg(org string) (n int, err error) { - return fmt.Fprint(c.stanzaWriter, org+"\n") + stanza := fmt.Sprint(org + "\n") + if c.LimitMaxBytes != 0 && len(stanza) > c.LimitMaxBytes { + return 0, fmt.Errorf("stanza size (%v bytes) exceeds server limit (%v bytes)", + len(stanza), c.LimitMaxBytes) + } + return fmt.Fprint(c.stanzaWriter, stanza) } // SendPresence sends Presence wrapped inside XMPP presence stanza. @@ -1234,9 +1294,12 @@ buf = buf + fmt.Sprintf("<status>%s</status>", xmlEscape(presence.Status)) } - buf = buf + "</presence>" - - return fmt.Fprint(c.stanzaWriter, buf) + stanza := fmt.Sprintf(buf + "</presence>") + if c.LimitMaxBytes != 0 && len(stanza) > c.LimitMaxBytes { + return 0, fmt.Errorf("stanza size (%v bytes) exceeds server limit (%v bytes)", + len(stanza), c.LimitMaxBytes) + } + return fmt.Fprint(c.stanzaWriter, stanza) } // SendKeepAlive sends a "whitespace keepalive" as described in chapter 4.6.1 of RFC6120. @@ -1246,10 +1309,14 @@ // SendHtml sends the message as HTML as defined by XEP-0071 func (c *Client) SendHtml(chat Chat) (n int, err error) { - return fmt.Fprintf(c.stanzaWriter, "<message to='%s' type='%s' xml:lang='en'>"+ - "<body>%s</body>"+ + stanza := fmt.Sprintf("<message to='%s' type='%s' xml:lang='en'><body>%s</body>"+ "<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>%s</body></html></message>\n", xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text), chat.Text) + if c.LimitMaxBytes != 0 && len(stanza) > c.LimitMaxBytes { + return 0, fmt.Errorf("stanza size (%v bytes) exceeds server limit (%v bytes)", + len(stanza), c.LimitMaxBytes) + } + return fmt.Fprint(c.stanzaWriter, stanza) } // Roster asks for the chat roster. @@ -1266,12 +1333,17 @@ ChannelBindings saslChannelBindings Bind bindBind Session bool + Limits streamLimits } type streamError struct { XMLName xml.Name `xml:"http://etherx.jabber.org/streams error"` Any xml.Name - Text string + Text struct { + Text string `xml:",chardata"` + Lang string `xml:"lang,attr"` + Xmlns string `xml:"xmlns,attr"` + } `xml:"text"` } // RFC 3920 C.3 TLS name space @@ -1324,6 +1396,14 @@ Text string `xml:",chardata"` } +type streamLimits struct { + XMLName xml.Name `xml:"limits"` + Text string `xml:",chardata"` + Xmlns string `xml:"xmlns,attr"` + MaxBytes string `xml:"max-bytes"` + IdleSeconds string `xml:"idle-seconds"` +} + // RFC 3920 C.5 Resource binding name space type bindBind struct { XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"` @@ -1444,40 +1524,58 @@ } // Scan XML token stream to find next StartElement. -func nextStart(p *xml.Decoder) (xml.StartElement, error) { +func (c *Client) nextStart() (xml.StartElement, error) { for { - t, err := p.Token() - if err != nil || t == nil { + c.nextMutex.Lock() + to, err := c.p.Token() + if err != nil || to == nil { + c.nextMutex.Unlock() return xml.StartElement{}, err } + t := xml.CopyToken(to) switch t := t.(type) { case xml.StartElement: + c.nextMutex.Unlock() return t, nil + // Also check for stream end element and stop waiting + // for new start elements if we received a closing stream + // element. + case xml.EndElement: + if t.Name.Local == "stream" { + c.nextMutex.Unlock() + return xml.StartElement{}, nil + } } + c.nextMutex.Unlock() } } // Scan XML token stream to find next EndElement -func nextEnd(p *xml.Decoder) (xml.EndElement, error) { - p.Strict = false +func (c *Client) nextEnd() (xml.EndElement, error) { + c.p.Strict = false for { - t, err := p.Token() - if err != nil || t == nil { + c.nextMutex.Lock() + to, err := c.p.RawToken() + if err != nil || to == nil { + c.nextMutex.Unlock() return xml.EndElement{}, err } + t := xml.CopyToken(to) switch t := t.(type) { case xml.EndElement: + c.nextMutex.Unlock() return t, nil } + c.nextMutex.Unlock() } } // Scan XML token stream for next element and save into val. // If val == nil, allocate new element based on proto map. // Either way, return val. -func next(p *xml.Decoder) (xml.Name, interface{}, error) { +func (c *Client) next() (xml.Name, interface{}, error) { // Read start element to find out what type we want. - se, err := nextStart(p) + se, err := c.nextStart() if err != nil { return xml.Name{}, nil, err } @@ -1525,7 +1623,7 @@ } // Unmarshal into that storage. - if err = p.DecodeElement(nv, &se); err != nil { + if err = c.p.DecodeElement(nv, &se); err != nil { return xml.Name{}, nil, err } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/modules.txt new/vendor/modules.txt --- old/vendor/modules.txt 2024-03-10 08:18:29.000000000 +0100 +++ new/vendor/modules.txt 2024-03-29 15:06:34.000000000 +0100 @@ -57,7 +57,7 @@ # github.com/pkg/errors v0.9.1 ## explicit github.com/pkg/errors -# github.com/xmppo/go-xmpp v0.1.2 +# github.com/xmppo/go-xmpp v0.1.4 ## explicit; go 1.21.5 github.com/xmppo/go-xmpp # golang.org/x/crypto v0.21.0