package stanza import ( "encoding/xml" "strconv" "time" ) // ============================================================================ // MUC Presence extension // MucPresence implements XEP-0045: Multi-User Chat - 19.1 type MucPresence struct { PresExtension XMLName xml.Name `xml:"http://jabber.org/protocol/muc x"` Password string `xml:"password,omitempty"` History History `xml:"history,omitempty"` } const timeLayout = "2006-01-02T15:04:05Z" // History implements XEP-0045: Multi-User Chat - 19.1 type History struct { XMLName xml.Name MaxChars NullableInt `xml:"maxchars,attr,omitempty"` MaxStanzas NullableInt `xml:"maxstanzas,attr,omitempty"` Seconds NullableInt `xml:"seconds,attr,omitempty"` Since time.Time `xml:"since,attr,omitempty"` } type NullableInt struct { Value int isSet bool } func NewNullableInt(val int) NullableInt { return NullableInt{val, true} } func (n NullableInt) Get() (v int, ok bool) { return n.Value, n.isSet } // UnmarshalXML implements custom parsing for history element func (h *History) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { h.XMLName = start.Name // Extract attributes for _, attr := range start.Attr { switch attr.Name.Local { case "maxchars": v, err := strconv.Atoi(attr.Value) if err != nil { return err } h.MaxChars = NewNullableInt(v) case "maxstanzas": v, err := strconv.Atoi(attr.Value) if err != nil { return err } h.MaxStanzas = NewNullableInt(v) case "seconds": v, err := strconv.Atoi(attr.Value) if err != nil { return err } h.Seconds = NewNullableInt(v) case "since": t, err := time.Parse(timeLayout, attr.Value) if err != nil { return err } h.Since = t } } // Consume remaining data until element end for { t, err := d.Token() if err != nil { return err } switch tt := t.(type) { case xml.EndElement: if tt == start.End() { return nil } } } } func (h History) MarshalXML(e *xml.Encoder, start xml.StartElement) (err error) { mc, isMcSet := h.MaxChars.Get() ms, isMsSet := h.MaxStanzas.Get() s, isSSet := h.Seconds.Get() // We do not have any value, ignore history element if h.Since.IsZero() && !isMcSet && !isMsSet && !isSSet { return nil } // Encode start element and attributes start.Name = xml.Name{Local: "history"} if isMcSet { attr := xml.Attr{ Name: xml.Name{Local: "maxchars"}, Value: strconv.Itoa(mc), } start.Attr = append(start.Attr, attr) } if isMsSet { attr := xml.Attr{ Name: xml.Name{Local: "maxstanzas"}, Value: strconv.Itoa(ms), } start.Attr = append(start.Attr, attr) } if isSSet { attr := xml.Attr{ Name: xml.Name{Local: "seconds"}, Value: strconv.Itoa(s), } start.Attr = append(start.Attr, attr) } if !h.Since.IsZero() { attr := xml.Attr{ Name: xml.Name{Local: "since"}, Value: h.Since.Format(timeLayout), } start.Attr = append(start.Attr, attr) } if err := e.EncodeToken(start); err != nil { return err } return e.EncodeToken(xml.EndElement{Name: start.Name}) } func init() { TypeRegistry.MapExtension(PKTPresence, xml.Name{Space: "http://jabber.org/protocol/muc", Local: "x"}, MucPresence{}) }