~samwhited/xmpp

xmpp/stanza/delay.go -rw-r--r-- 2.2 KiB
e9b0a2deSam Whited docs: do a quick editing pass over the docs 2 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// 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 stanza

import (
	"encoding/xml"
	"time"

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

// Delay can be added to a stanza to indicate that stanza delivery was delayed.
// For example, when you joing a chat and request history, a delay might be
// added to indicate that the chat messages were sent in the past and are not
// live.
type Delay struct {
	From   jid.JID
	Stamp  time.Time
	Reason string
}

// TokenReader satisfies the xmlstream.Marshaler interface.
func (d Delay) TokenReader() xml.TokenReader {
	return xmlstream.Wrap(xmlstream.Token(xml.CharData(d.Reason)), xml.StartElement{
		Name: xml.Name{Space: NSDelay, Local: "delay"},
		Attr: []xml.Attr{
			{Name: xml.Name{Local: "from"}, Value: d.From.String()},
			{Name: xml.Name{Local: "stamp"}, Value: d.Stamp.UTC().Format(time.RFC3339Nano)},
		},
	})
}

// WriteXML satisfies the xmlstream.WriterTo interface.
// It is like MarshalXML except it writes tokens to w.
func (d Delay) WriteXML(w xmlstream.TokenWriter) (n int, err error) {
	return xmlstream.Copy(w, d.TokenReader())
}

// MarshalXML implements xml.Marshaler.
func (d Delay) MarshalXML(e *xml.Encoder, _ xml.StartElement) error {
	_, err := d.WriteXML(e)
	return err
}

// UnmarshalXML implements xml.Unmarshaler.
func (d *Delay) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error {
	var foundFrom, foundStamp bool
	var err error
	for _, attr := range start.Attr {
		switch attr.Name.Local {
		case "from":
			d.From, err = jid.Parse(attr.Value)
			if err != nil {
				return err
			}
			foundFrom = true
		case "stamp":
			d.Stamp, err = time.Parse(time.RFC3339Nano, attr.Value)
			if err != nil {
				return err
			}
			foundStamp = true
		}
		if foundFrom && foundStamp {
			break
		}
	}
	tok, err := dec.Token()
	if err != nil {
		return err
	}
	switch t := tok.(type) {
	case xml.EndElement:
		return nil
	case xml.CharData:
		d.Reason = string(t)
	case xml.StartElement:
		// There shouldn't be a start element in here, but tolerate unknown future
		// extensions and skip it if we find one.
		err = dec.Skip()
		if err != nil {
			return err
		}
	default:
	}
	return dec.Skip()
}