~samwhited/xmpp

075b015c37056b42c97f8c4e221dd39f477e642c — Sam Whited 5 years ago 419d6d4
Make stream a "dumb" XML element.
4 files changed, 97 insertions(+), 116 deletions(-)

R stream/version.go => internal/version.go
R stream/version_test.go => internal/version_test.go
M stream/options.go
M stream/stream.go
R stream/version.go => internal/version.go +0 -0
R stream/version_test.go => internal/version_test.go +0 -0
M stream/options.go => stream/options.go +18 -17
@@ 5,20 5,15 @@
package stream

import (
	"encoding/xml"
	"net"

	"golang.org/x/text/language"
)

// Option's can be used to configure the stream.
type Option func(*options)
type options struct {
	lang    language.Tag
	conn    net.Conn
	xmlns   string
	encoder *xml.Encoder
	decoder *xml.Decoder
	lang          language.Tag
	noVersionAttr bool
	s2sStream     bool
}

func getOpts(o ...Option) (res options) {


@@ 38,13 33,19 @@ func Language(l language.Tag) Option {
	}
}

// Conn is the connection which the stream will use for sending and receiving
// data. To manually manage streams (not recommended), don't provide a
// connection and marshal and send the stream yourself.
func Conn(c net.Conn) Option {
	return func(o *options) {
		o.conn = c
		o.encoder = xml.NewEncoder(c)
		o.decoder = xml.NewDecoder(c)
var (
	// The NoVersion option leaves the version attribute off the stream. When the
	// version attribute is missing, servers and clients treat the XMPP version as
	// if it were 0.9. This is an advanced option and generally should not be used
	// except when responding to an incomming stream that has done the same. It
	// does not change the behavior of the stream otherwise (XMPP 1.0 is still
	// used), and may cause problems.
	NoVersion Option = func(o *options) {
		o.noVersionAttr = true
	}
}
	// The ServerToServer option configures the stream to use the jabber:server
	// namespace.
	ServerToServer = func(o *options) {
		o.s2sStream = true
	}
)

M stream/stream.go => stream/stream.go +79 -99
@@ 8,6 8,7 @@ import (
	"encoding/xml"
	"errors"

	"bitbucket.org/mellium/xmpp/internal"
	"bitbucket.org/mellium/xmpp/jid"
	"golang.org/x/text/language"
)


@@ 21,130 22,109 @@ type Stream struct {
	options
	to      *jid.JID
	from    *jid.JID
	version Version
	id      string
	authed  bool
	bound   bool
	version internal.Version
}

// New creats an XMPP stream that will be used to initiate a new XMPP
// connection. This should always be used by clients to create a new stream, and
// by the initiating server in a server-to-server connection.
func New(to, from jid.JID, opts ...Option) Stream {
// New creates a stream that will be used to initiate a new XMPP connection.
// This should always be used by clients to create a new stream, and by the
// initiating server in server-to-server connections.
func New(to, from *jid.JID, opts ...Option) Stream {
	return Stream{
		to:      &to,
		from:    &from,
		version: DefaultVersion,
		to:      to,
		from:    from,
		version: internal.DefaultVersion,
		options: getOpts(opts...),
	}
}

// ReadResponse constructs a new Steam that acts as a response stream to the
// provided initiating stream. Respond should always be used by clients and
// servers to to initiate new streams after receiving a client-to-server or
// server-to-server connection.
func ReadResponse(responds, replaces *Stream, opts ...Option) (Stream, error) {
	s := Stream{
		options: getOpts(opts...),
	}

	switch {
	case replaces != nil:
		s.version = replaces.version
	// RFC 6120 §4.7.5  version.
	//    2.  The receiving entity MUST set the value of the 'version'
	//        attribute in the response stream header to either the value
	//        supplied by the initiating entity or the highest version number
	//        supported by the receiving entity, whichever is lower.
	case replaces == nil && responds.version.Less(DefaultVersion):
		s.version = responds.version
	default:
		s.version = DefaultVersion
	}

	if responds.from != nil {
		s.to = responds.from
	}

	if responds.to != nil {
		// TODO: Verify that we serve this domain, possibly set this to a canonical
		// domain if there is one.
		s.from = responds.to
	}

	return s, nil
}

// fromStartElement constructs a new Stream from the given xml.StartElement.
func fromStartElement(start xml.StartElement) (*Stream, error) {
// FromStartElement constructs a new Stream from the given XML StartElement.
func FromStartElement(start xml.StartElement) (Stream, error) {

	stream := Stream{}
	if start.Name.Local != "stream" || start.Name.Space != "stream" {
		return nil, errors.New("Incorrect XML name on stream start element.")
		return stream, errors.New("Incorrect XML name on stream start element.")
	}

	stream := Stream{}
	for _, attr := range start.Attr {
		switch attr.Name.Local {
		case "from":
		switch attr.Name {
		case xml.Name{"", "from"}:
			j, err := jid.ParseString(attr.Value)
			if err != nil {
				return nil, err
				return stream, err
			}
			stream.from = j
		case "to":
		case xml.Name{"", "to"}:
			j, err := jid.ParseString(attr.Value)
			if err != nil {
				return nil, err
				return stream, err
			}
			stream.to = j
		case "xmlns":
			stream.xmlns = attr.Value
		case "lang":
			if attr.Name.Space == "xml" {
				stream.lang = language.Make(attr.Value)
		case xml.Name{"", "xmlns"}:
			switch attr.Value {
			case "jabber:server":
				stream.options.s2sStream = true
			case "jabber:client":
				stream.options.s2sStream = false
			default:
				return stream, errors.New("Stream has invalid xmlns.")
			}
		case "id":
		case xml.Name{"xml", "lang"}:
			var err error
			stream.lang, err = language.Parse(attr.Value)
			if err != nil {
				return stream, err
			}
		case xml.Name{"", "id"}:
			stream.id = attr.Value
		case xml.Name{"", "version"}:
			v, err := internal.ParseVersion(attr.Value)
			if err != nil {
				return stream, err
			}
			stream.version = v
		}
	}

	return &stream, nil
	return stream, nil
}

// // StartElement creates an XML start element from the given stream which is
// // suitable for starting an XMPP stream.
// func (s *Stream) StartElement() xml.StartElement {
// 	return xml.StartElement{
// 		Name: xml.Name{"stream", "stream"},
// 		Attr: []xml.Attr{
// 			xml.Attr{
// 				xml.Name{"", "to"},
// 				s.to.String(),
// 			},
// 			xml.Attr{
// 				xml.Name{"", "from"},
// 				s.from.String(),
// 			},
// 			xml.Attr{
// 				xml.Name{"", "version"},
// 				s.version,
// 			},
// 			xml.Attr{
// 				xml.Name{"xml", "lang"},
// 				s.lang,
// 			},
// 			xml.Attr{
// 				xml.Name{"", "id"},
// 				s.id,
// 			},
// 			xml.Attr{
// 				xml.Name{"", "xmlns"},
// 				s.xmlns,
// 			},
// 		},
// 	}
// }

// func (s *Stream) Handle(encoder *xml.Encoder, decoder *xml.Decoder) error {
// 	return errors.New("Test me")
// }
// StartElement creates an XML start element from the given stream which is
// suitable for encoding and transmitting over the wire.
func (s Stream) StartElement() xml.StartElement {
	var xmlns string
	if s.options.s2sStream {
		xmlns = "jabber:server"
	} else {
		xmlns = "jabber:client"
	}
	return xml.StartElement{
		Name: xml.Name{"stream", "stream"},
		Attr: []xml.Attr{
			xml.Attr{
				xml.Name{"", "to"},
				s.to.String(),
			},
			xml.Attr{
				xml.Name{"", "from"},
				s.from.String(),
			},
			xml.Attr{
				xml.Name{"", "version"},
				s.version.String(),
			},
			xml.Attr{
				xml.Name{"xml", "lang"},
				s.options.lang.String(),
			},
			xml.Attr{
				xml.Name{"", "id"},
				s.id,
			},
			xml.Attr{
				xml.Name{"", "xmlns"},
				xmlns,
			},
		},
	}
}