~samwhited/xmpp

c98da397314a57419fb1041f927b83246ed7366d — Sam Whited 2 months ago 3383439
carbons: new package

Add basic functionality for enabling and disabling carbons.

Fixes #153

Signed-off-by: Sam Whited <sam@samwhited.com>
3 files changed, 127 insertions(+), 1 deletions(-)

M CHANGELOG.md
A carbons/carbons.go
A carbons/carbons_test.go
M CHANGELOG.md => CHANGELOG.md +3 -1
@@ 20,6 20,7 @@ All notable changes to this project will be documented in this file.
### Added

- blocklist: new package implementing [XEP-0191: Blocking Command]
- carbons: new package implementing [XEP-0280: Message Carbons]
- commands: new package implementing [XEP-0050: Ad-Hoc Commands]
- muc: new package implementing [XEP-0045: Multi-User Chat] and [XEP-0249: Direct MUC Invitations]
- stanza: implement [XEP-0203: Delayed Delivery]


@@ 42,10 43,11 @@ All notable changes to this project will be documented in this file.


[XEP-0045: Multi-User Chat]: https://xmpp.org/extensions/xep-0045.html
[XEP-0191: Blocking Command]: https://xmpp.org/extensions/xep-0191.html
[XEP-0050: Ad-Hoc Commands]: https://xmpp.org/extensions/xep-0050.html
[XEP-0191: Blocking Command]: https://xmpp.org/extensions/xep-0191.html
[XEP-0203: Delayed Delivery]: https://xmpp.org/extensions/xep-0203.html
[XEP-0249: Direct MUC Invitations]: https://xmpp.org/extensions/xep-0249.html
[XEP-0280: Message Carbons]: https://xmpp.org/extensions/xep-0280.html


## v0.19.0 — 2021-05-02

A carbons/carbons.go => carbons/carbons.go +63 -0
@@ 0,0 1,63 @@
// Copyright 2021 The Mellium Contributors.
// Use of this source code is governed by the BSD 2-clause
// license that can be found in the LICENSE file.

// Package carbons implements carbon copying messages to all interested clients.
package carbons // import "mellium.im/xmpp/carbons"

import (
	"context"
	"encoding/xml"

	"mellium.im/xmlstream"
	"mellium.im/xmpp"
	"mellium.im/xmpp/stanza"
)

// Namespaces used by this package, provided as a convenience.
const (
	NS      = `urn:xmpp:carbons:2`
	NSRules = `urn:xmpp:carbons:rules:0`
)

// Enable instructs the server to start carbon copying messages on the given
// session.
func Enable(ctx context.Context, s *xmpp.Session) error {
	return EnableIQ(ctx, s, stanza.IQ{})
}

// EnableIQ is like Enable but it allows you to customize the IQ stanza being
// sent.
// Changing the type of the IQ has no effect.
func EnableIQ(ctx context.Context, s *xmpp.Session, iq stanza.IQ) error {
	iq.Type = stanza.SetIQ
	v := struct {
		XMLName xml.Name
	}{}
	err := s.UnmarshalIQ(ctx, iq.Wrap(xmlstream.Wrap(
		nil,
		xml.StartElement{Name: xml.Name{Space: NS, Local: "enable"}},
	)), &v)
	return err
}

// Disable instructs the server to stop carbon copying messages on the given
// session.
func Disable(ctx context.Context, s *xmpp.Session) error {
	return DisableIQ(ctx, s, stanza.IQ{})
}

// DisableIQ is like Disable but it allows you to customize the IQ stanza being
// sent.
// Changing the type of the IQ has no effect.
func DisableIQ(ctx context.Context, s *xmpp.Session, iq stanza.IQ) error {
	iq.Type = stanza.SetIQ
	v := struct {
		XMLName xml.Name
	}{}
	err := s.UnmarshalIQ(ctx, iq.Wrap(xmlstream.Wrap(
		nil,
		xml.StartElement{Name: xml.Name{Space: NS, Local: "disable"}},
	)), &v)
	return err
}

A carbons/carbons_test.go => carbons/carbons_test.go +61 -0
@@ 0,0 1,61 @@
// Copyright 2021 The Mellium Contributors.
// Use of this source code is governed by the BSD 2-clause
// license that can be found in the LICENSE file.

package carbons_test

import (
	"bytes"
	"context"
	"encoding/xml"
	"errors"
	"testing"

	"mellium.im/xmlstream"
	"mellium.im/xmpp/carbons"
	"mellium.im/xmpp/internal/xmpptest"
	"mellium.im/xmpp/stanza"
)

func TestEnableDisable(t *testing.T) {
	var out bytes.Buffer
	stop := make(chan struct{}, 2)
	cs := xmpptest.NewClientServer(
		xmpptest.ServerHandlerFunc(func(t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
			defer func() {
				stop <- struct{}{}
			}()
			e := xml.NewEncoder(&out)
			err := e.EncodeToken(*start)
			if err != nil {
				return err
			}
			_, err = xmlstream.Copy(e, t)
			if err != nil {
				return err
			}
			return e.Flush()
		}),
	)
	err := carbons.EnableIQ(context.Background(), cs.Client, stanza.IQ{
		Type: stanza.GetIQ,
		ID:   "000",
	})
	if !errors.Is(err, stanza.Error{Condition: stanza.ServiceUnavailable}) {
		t.Fatalf("unexpected error enabling carbons: %v", err)
	}
	err = carbons.DisableIQ(context.Background(), cs.Client, stanza.IQ{
		Type: stanza.GetIQ,
		ID:   "000",
	})
	if !errors.Is(err, stanza.Error{Condition: stanza.ServiceUnavailable}) {
		t.Fatalf("unexpected error disabling carbons: %v", err)
	}

	<-stop
	output := out.String()
	const expected = `<iq xmlns="jabber:client" xmlns="jabber:client" type="set" id="000"><enable xmlns="urn:xmpp:carbons:2" xmlns="urn:xmpp:carbons:2"></enable></iq><iq xmlns="jabber:client" xmlns="jabber:client" type="set" id="000"><disable xmlns="urn:xmpp:carbons:2" xmlns="urn:xmpp:carbons:2"></disable></iq>`
	if output != expected {
		t.Errorf("wrong XML:\nwant=%s,\n got=%s", expected, output)
	}
}