structs.go
author Chris Jones <chris@cjones.org>
Sat, 24 Dec 2011 13:11:36 -0700
changeset 5 faef59c8db05
parent 3 6121aa2f21b1
child 6 8e425e340ca1
permissions -rw-r--r--
Added a goroutine to read data from the remote and parse it into appropriate structures.

// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package xmpp

// This file contains data structures.

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"os"
	"regexp"
	"strings"
	"xml"
)

const (
	// Version of RFC 3920 that we implement.
	Version = "1.0"
	nsStreams = "urn:ietf:params:xml:ns:xmpp-streams"
	nsStream = "http://etherx.jabber.org/streams"
	nsTLS = "urn:ietf:params:xml:ns:xmpp-tls"
)

// JID represents an entity that can communicate with other
// entities. It looks like node@domain/resource. Node and resource are
// sometimes optional.
type JID struct {
	Node *string
	Domain string
	Resource *string
}
var _ fmt.Stringer = &JID{}
var _ flag.Value = &JID{}

// XMPP's <stream:stream> XML element
type Stream struct {
	to string `xml:"attr"`
	from string `xml:"attr"`
	id string `xml:"attr"`
	lang string `xml:"attr"`
	version string `xml:"attr"`
}
var _ xml.Marshaler = &Stream{}

type StreamError struct {
	cond definedCondition
	text *errText
}
var _ xml.Marshaler = &StreamError{}

type definedCondition struct {
	// Must always be in namespace nsStreams
	XMLName xml.Name
}

type errText struct {
	Lang string
	text string `xml:"chardata"`
}
var _ xml.Marshaler = &errText{}

type Unrecognized struct {
	XMLName xml.Name
}

func (jid *JID) String() string {
	result := jid.Domain
	if jid.Node != nil {
		result = *jid.Node + "@" + result
	}
	if jid.Resource != nil {
		result = result + "/" + *jid.Resource
	}
	return result
}

func (jid *JID) Set(val string) bool {
	r := regexp.MustCompile("^(([^@/]+)@)?([^@/]+)(/([^@/]+))?$")
	parts := r.FindStringSubmatch(val)
	if parts == nil {
		return false
	}
	if parts[2] == "" {
		jid.Node = nil
	} else {
		jid.Node = &parts[2]
	}
	jid.Domain = parts[3]
	if parts[5] == "" {
		jid.Resource = nil
	} else {
		jid.Resource = &parts[5]
	}
	return true
}

func (s *Stream) MarshalXML() ([]byte, os.Error) {
	buf := bytes.NewBuffer(nil)
	buf.WriteString("<stream:stream")
	writeField(buf, "to", s.to)
	writeField(buf, "from", s.from)
	writeField(buf, "id", s.id)
	writeField(buf, "xml:lang", s.lang)
	writeField(buf, "version", s.version)
	buf.WriteString(">")
	// We never write </stream:stream>
	return buf.Bytes(), nil
}

func parseStream(se xml.StartElement) (*Stream, os.Error) {
	s := &Stream{}
	se = se.Copy()
	for _, attr := range se.Attr {
		switch strings.ToLower(attr.Name.Local) {
		case "to":
			s.to = attr.Value
		case "from":
			s.from = attr.Value
		case "id":
			s.id = attr.Value
		case "lang":
			s.lang = attr.Value
		case "version":
			s.version = attr.Value
		}
	}
	return s, nil
}

func (s *StreamError) MarshalXML() ([]byte, os.Error) {
	buf := bytes.NewBuffer(nil)
	buf.WriteString("<stream:error>")
	xml.Marshal(buf, s.cond)
	if s.text != nil {
		xml.Marshal(buf, s.text)
	}
	buf.WriteString("</stream:error>")
	return buf.Bytes(), nil
}

func (e *errText) MarshalXML() ([]byte, os.Error) {
	buf := bytes.NewBuffer(nil)
	buf.WriteString("<text")
	writeField(buf, "xmlns", nsStreams)
	writeField(buf, "xml:lang", e.Lang)
	buf.WriteString(">")
	xml.Escape(buf, []byte(e.text))
	buf.WriteString("</text>")
	return buf.Bytes(), nil
}

func writeField(w io.Writer, field, value string) {
	if value != "" {
		io.WriteString(w, " ")
		io.WriteString(w, field)
		io.WriteString(w, `="`)
		xml.Escape(w, []byte(value))
		io.WriteString(w, `"`)
	}
}