xmpp/xmpp.go
author Chris Jones <chris@cjones.org>
Wed, 06 Nov 2013 20:39:36 -0700
changeset 176 52c100897e9d
parent 175 fc8702a8572e
parent 170 d496de556c9a
child 178 ccfebbd9f49b
permissions -rw-r--r--
Merge.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
2
4dabfef08c8c Forgot to add the new xmpp.go from my last commit. Also added some
Chris Jones <chris@cjones.org>
parents:
diff changeset
     1
// This package implements a simple XMPP client according to RFCs 3920
119
712aa5780660 Documentation cleanup.
Chris Jones <christian.jones@sri.com>
parents: 118
diff changeset
     2
// and 3921, plus the various XEPs at http://xmpp.org/protocols/. The
712aa5780660 Documentation cleanup.
Chris Jones <christian.jones@sri.com>
parents: 118
diff changeset
     3
// implementation is structured as a stack of layers, with TCP at the
712aa5780660 Documentation cleanup.
Chris Jones <christian.jones@sri.com>
parents: 118
diff changeset
     4
// bottom and the application at the top. The application receives and
712aa5780660 Documentation cleanup.
Chris Jones <christian.jones@sri.com>
parents: 118
diff changeset
     5
// sends structures representing XMPP stanzas. Additional stanza
712aa5780660 Documentation cleanup.
Chris Jones <christian.jones@sri.com>
parents: 118
diff changeset
     6
// parsers can be inserted into the stack of layers as extensions.
2
4dabfef08c8c Forgot to add the new xmpp.go from my last commit. Also added some
Chris Jones <chris@cjones.org>
parents:
diff changeset
     7
package xmpp
4dabfef08c8c Forgot to add the new xmpp.go from my last commit. Also added some
Chris Jones <chris@cjones.org>
parents:
diff changeset
     8
4dabfef08c8c Forgot to add the new xmpp.go from my last commit. Also added some
Chris Jones <chris@cjones.org>
parents:
diff changeset
     9
import (
105
aa895dfae3f6 Allow the user to override the TLS config. Also fixed up some log statements.
Chris Jones <christian.jones@sri.com>
parents: 102
diff changeset
    10
	"crypto/tls"
98
c9cc4eda6dce Updated for Go 1.0 + upcoming XML fixes.
Chris Jones <chris@cjones.org>
parents: 72
diff changeset
    11
	"encoding/xml"
2
4dabfef08c8c Forgot to add the new xmpp.go from my last commit. Also added some
Chris Jones <chris@cjones.org>
parents:
diff changeset
    12
	"fmt"
4
a8fbec71a194 Added an interactive test and made Client implement io.Closer.
Chris Jones <chris@cjones.org>
parents: 2
diff changeset
    13
	"io"
2
4dabfef08c8c Forgot to add the new xmpp.go from my last commit. Also added some
Chris Jones <chris@cjones.org>
parents:
diff changeset
    14
	"net"
128
8342afcffc92 Use reflection instead of constructor functions to create extended stanza structures.
Chris Jones <christian.jones@sri.com>
parents: 127
diff changeset
    15
	"reflect"
168
f4ccc2f29777 Don't call Close() twice.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
    16
	"sync"
2
4dabfef08c8c Forgot to add the new xmpp.go from my last commit. Also added some
Chris Jones <chris@cjones.org>
parents:
diff changeset
    17
)
4dabfef08c8c Forgot to add the new xmpp.go from my last commit. Also added some
Chris Jones <chris@cjones.org>
parents:
diff changeset
    18
4dabfef08c8c Forgot to add the new xmpp.go from my last commit. Also added some
Chris Jones <chris@cjones.org>
parents:
diff changeset
    19
const (
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents: 9
diff changeset
    20
	// Version of RFC 3920 that we implement.
118
fb9bb98a8d70 Code cleanup: Renamed the Version constant. Moved the unique-ID generator into its own file.
Chris Jones <christian.jones@sri.com>
parents: 116
diff changeset
    21
	XMPPVersion = "1.0"
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents: 9
diff changeset
    22
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents: 9
diff changeset
    23
	// Various XML namespaces.
98
c9cc4eda6dce Updated for Go 1.0 + upcoming XML fixes.
Chris Jones <chris@cjones.org>
parents: 72
diff changeset
    24
	NsClient  = "jabber:client"
34
7b1f924c75e2 Made the namespace constants public.
Chris Jones <chris@cjones.org>
parents: 33
diff changeset
    25
	NsStreams = "urn:ietf:params:xml:ns:xmpp-streams"
72
Chris Jones <christian.jones@sri.com>
parents: 67
diff changeset
    26
	NsStream  = "http://etherx.jabber.org/streams"
Chris Jones <christian.jones@sri.com>
parents: 67
diff changeset
    27
	NsTLS     = "urn:ietf:params:xml:ns:xmpp-tls"
Chris Jones <christian.jones@sri.com>
parents: 67
diff changeset
    28
	NsSASL    = "urn:ietf:params:xml:ns:xmpp-sasl"
Chris Jones <christian.jones@sri.com>
parents: 67
diff changeset
    29
	NsBind    = "urn:ietf:params:xml:ns:xmpp-bind"
34
7b1f924c75e2 Made the namespace constants public.
Chris Jones <chris@cjones.org>
parents: 33
diff changeset
    30
	NsSession = "urn:ietf:params:xml:ns:xmpp-session"
72
Chris Jones <christian.jones@sri.com>
parents: 67
diff changeset
    31
	NsRoster  = "jabber:iq:roster"
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents: 9
diff changeset
    32
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents: 9
diff changeset
    33
	// DNS SRV names
2
4dabfef08c8c Forgot to add the new xmpp.go from my last commit. Also added some
Chris Jones <chris@cjones.org>
parents:
diff changeset
    34
	serverSrv = "xmpp-server"
4dabfef08c8c Forgot to add the new xmpp.go from my last commit. Also added some
Chris Jones <chris@cjones.org>
parents:
diff changeset
    35
	clientSrv = "xmpp-client"
62
6e2eea62ccca Added global variables for logging.
Chris Jones <chris@cjones.org>
parents: 61
diff changeset
    36
)
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents: 9
diff changeset
    37
121
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
    38
// A filter can modify the XMPP traffic to or from the remote
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
    39
// server. It's part of an Extension. The filter function will be
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
    40
// called in a new goroutine, so it doesn't need to return. The filter
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
    41
// should close its output when its input is closed.
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
    42
type Filter func(in <-chan Stanza, out chan<- Stanza)
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
    43
60
6d4f43f7dc19 Made a generic extension interface.
Chris Jones <chris@cjones.org>
parents: 59
diff changeset
    44
// Extensions can add stanza filters and/or new XML element types.
6d4f43f7dc19 Made a generic extension interface.
Chris Jones <chris@cjones.org>
parents: 59
diff changeset
    45
type Extension struct {
158
2d948fcbb5d7 Renamed Extension.StanzaHandlers to StanzaTypes, and updated the doc comment.
Chris Jones <chris@cjones.org>
parents: 157
diff changeset
    46
	// Maps from an XML name to a structure which holds stanza
2d948fcbb5d7 Renamed Extension.StanzaHandlers to StanzaTypes, and updated the doc comment.
Chris Jones <chris@cjones.org>
parents: 157
diff changeset
    47
	// contents with that name.
2d948fcbb5d7 Renamed Extension.StanzaHandlers to StanzaTypes, and updated the doc comment.
Chris Jones <chris@cjones.org>
parents: 157
diff changeset
    48
	StanzaTypes map[xml.Name]reflect.Type
121
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
    49
	// If non-nil, will be called once to start the filter
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
    50
	// running. RecvFilter intercepts incoming messages on their
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
    51
	// way from the remote server to the application; SendFilter
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
    52
	// intercepts messages going the other direction.
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
    53
	RecvFilter Filter
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
    54
	SendFilter Filter
60
6d4f43f7dc19 Made a generic extension interface.
Chris Jones <chris@cjones.org>
parents: 59
diff changeset
    55
}
6d4f43f7dc19 Made a generic extension interface.
Chris Jones <chris@cjones.org>
parents: 59
diff changeset
    56
2
4dabfef08c8c Forgot to add the new xmpp.go from my last commit. Also added some
Chris Jones <chris@cjones.org>
parents:
diff changeset
    57
// The client in a client-server XMPP connection.
4dabfef08c8c Forgot to add the new xmpp.go from my last commit. Also added some
Chris Jones <chris@cjones.org>
parents:
diff changeset
    58
type Client struct {
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
    59
	// This client's full JID, including resource
72
Chris Jones <christian.jones@sri.com>
parents: 67
diff changeset
    60
	Jid          JID
Chris Jones <christian.jones@sri.com>
parents: 67
diff changeset
    61
	password     string
11
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
    62
	saslExpected string
72
Chris Jones <christian.jones@sri.com>
parents: 67
diff changeset
    63
	authDone     bool
144
9d7fdb1d2fc1 Renamed HandleStanza to SetCallback.
Chris Jones <christian.jones@sri.com>
parents: 142
diff changeset
    64
	handlers     chan *callback
119
712aa5780660 Documentation cleanup.
Chris Jones <christian.jones@sri.com>
parents: 118
diff changeset
    65
	// Incoming XMPP stanzas from the remote will be published on
712aa5780660 Documentation cleanup.
Chris Jones <christian.jones@sri.com>
parents: 118
diff changeset
    66
	// this channel. Information which is used by this library to
712aa5780660 Documentation cleanup.
Chris Jones <christian.jones@sri.com>
parents: 118
diff changeset
    67
	// set up the XMPP stream will not appear here.
127
a8f9a0c07fc8 Renamed client.In and client.Out to Recv and Send, respectively.
Chris Jones <christian.jones@sri.com>
parents: 126
diff changeset
    68
	Recv <-chan Stanza
17
d269d9c0fc8e Code review.
Chris Jones <chris@cjones.org>
parents: 15
diff changeset
    69
	// Outgoing XMPP stanzas to the server should be sent to this
170
d496de556c9a Trying to be smarter about closing channels: Ask the application not to close the Send channel, but let us do that from Close() instead.
Chris Jones <chris@cjones.org>
parents: 169
diff changeset
    70
	// channel. The application should not close this channel;
d496de556c9a Trying to be smarter about closing channels: Ask the application not to close the Send channel, but let us do that from Close() instead.
Chris Jones <chris@cjones.org>
parents: 169
diff changeset
    71
	// rather, call Close().
127
a8f9a0c07fc8 Renamed client.In and client.Out to Recv and Send, respectively.
Chris Jones <christian.jones@sri.com>
parents: 126
diff changeset
    72
	Send    chan<- Stanza
163
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
    73
	sendRaw chan<- interface{}
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
    74
	statmgr *statmgr
121
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
    75
	// The client's roster is also known as the buddy list. It's
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
    76
	// the set of contacts which are known to this JID, or which
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
    77
	// this JID is known to.
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
    78
	Roster Roster
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
    79
	// Features advertised by the remote.
128
8342afcffc92 Use reflection instead of constructor functions to create extended stanza structures.
Chris Jones <christian.jones@sri.com>
parents: 127
diff changeset
    80
	Features                     *Features
121
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
    81
	sendFilterAdd, recvFilterAdd chan Filter
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
    82
	tlsConfig                    tls.Config
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
    83
	layer1                       *layer1
163
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
    84
	error                        chan error
168
f4ccc2f29777 Don't call Close() twice.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
    85
	shutdownOnce                 sync.Once
2
4dabfef08c8c Forgot to add the new xmpp.go from my last commit. Also added some
Chris Jones <chris@cjones.org>
parents:
diff changeset
    86
}
4dabfef08c8c Forgot to add the new xmpp.go from my last commit. Also added some
Chris Jones <chris@cjones.org>
parents:
diff changeset
    87
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
    88
// Creates an XMPP client identified by the given JID, authenticating
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
    89
// with the provided password and TLS config. Zero or more extensions
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
    90
// may be specified. The initial presence will be broadcast. If status
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
    91
// is non-nil, connection progress information will be sent on it.
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
    92
func NewClient(jid *JID, password string, tlsconf tls.Config, exts []Extension,
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
    93
	pr Presence, status chan<- Status) (*Client, error) {
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
    94
175
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
    95
	// Resolve the domain in the JID.
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
    96
	_, srvs, err := net.LookupSRV(clientSrv, "tcp", jid.Domain)
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
    97
	if err != nil {
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
    98
		return nil, fmt.Errorf("LookupSrv %s: %v", jid.Domain, err)
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
    99
	}
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   100
	if len(srvs) == 0 {
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   101
		return nil, fmt.Errorf("LookupSrv %s: no results", jid.Domain)
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   102
	}
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   103
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   104
	var tcp *net.TCPConn
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   105
	for _, srv := range srvs {
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   106
		addrStr := fmt.Sprintf("%s:%d", srv.Target, srv.Port)
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   107
		var addr *net.TCPAddr
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   108
		addr, err = net.ResolveTCPAddr("tcp", addrStr)
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   109
		if err != nil {
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   110
			err = fmt.Errorf("ResolveTCPAddr(%s): %s",
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   111
				addrStr, err.Error())
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   112
			continue
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   113
		}
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   114
		tcp, err = net.DialTCP("tcp", nil, addr)
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   115
		if tcp != nil {
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   116
			break
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   117
		}
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   118
	}
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   119
	if tcp == nil {
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   120
		return nil, err
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   121
	}
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   122
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   123
	return newClient(tcp, jid, password, tlsconf, exts, pr, status)
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   124
}
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   125
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   126
// Connect to the specified host and port. This is otherwise identical
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   127
// to NewClient.
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   128
func NewClientFromHost(jid *JID, password string, tlsconf tls.Config,
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   129
	exts []Extension, pr Presence, status chan<- Status, host string,
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   130
	port int) (*Client, error) {
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   131
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   132
	addrStr := fmt.Sprintf("%s:%d", host, port)
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   133
	addr, err := net.ResolveTCPAddr("tcp", addrStr)
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   134
	if err != nil {
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   135
		return nil, err
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   136
	}
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   137
	tcp, err := net.DialTCP("tcp", nil, addr)
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   138
	if err != nil {
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   139
		return nil, err
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   140
	}
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   141
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   142
	return newClient(tcp, jid, password, tlsconf, exts, pr, status)
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   143
}
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   144
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   145
func newClient(tcp *net.TCPConn, jid *JID, password string, tlsconf tls.Config,
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   146
	exts []Extension, pr Presence, status chan<- Status) (*Client, error) {
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   147
60
6d4f43f7dc19 Made a generic extension interface.
Chris Jones <chris@cjones.org>
parents: 59
diff changeset
   148
	// Include the mandatory extensions.
121
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
   149
	roster := newRosterExt()
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
   150
	exts = append(exts, roster.Extension)
60
6d4f43f7dc19 Made a generic extension interface.
Chris Jones <chris@cjones.org>
parents: 59
diff changeset
   151
	exts = append(exts, bindExt)
6d4f43f7dc19 Made a generic extension interface.
Chris Jones <chris@cjones.org>
parents: 59
diff changeset
   152
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   153
	cl := new(Client)
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   154
	cl.Roster = *roster
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   155
	cl.password = password
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   156
	cl.Jid = *jid
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   157
	cl.handlers = make(chan *callback, 100)
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   158
	cl.tlsConfig = tlsconf
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   159
	cl.sendFilterAdd = make(chan Filter)
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   160
	cl.recvFilterAdd = make(chan Filter)
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   161
	cl.statmgr = newStatmgr(status)
163
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   162
	cl.error = make(chan error, 1)
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   163
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   164
	extStanza := make(map[xml.Name]reflect.Type)
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   165
	for _, ext := range exts {
158
2d948fcbb5d7 Renamed Extension.StanzaHandlers to StanzaTypes, and updated the doc comment.
Chris Jones <chris@cjones.org>
parents: 157
diff changeset
   166
		for k, v := range ext.StanzaTypes {
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   167
			if _, ok := extStanza[k]; ok {
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   168
				return nil, fmt.Errorf("duplicate handler %s",
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   169
					k)
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   170
			}
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   171
			extStanza[k] = v
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   172
		}
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   173
	}
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   174
175
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   175
	// The thing that called this made a TCP connection, so now we
fc8702a8572e Allow the client to specify a host and port to connect to, rather than using SRV records.
Chris Jones <chris@cjones.org>
parents: 166
diff changeset
   176
	// can signal that it's connected.
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   177
	cl.setStatus(StatusConnected)
36
9fe022261dcc Added a capability to use extensions. There are still some bugs with
Chris Jones <chris@cjones.org>
parents: 34
diff changeset
   178
9
4fe926b03827 Reorganize so we have a layered approach to IO with the server.
Chris Jones <chris@cjones.org>
parents: 8
diff changeset
   179
	// Start the transport handler, initially unencrypted.
121
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
   180
	recvReader, recvWriter := io.Pipe()
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
   181
	sendReader, sendWriter := io.Pipe()
163
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   182
	cl.layer1 = cl.startLayer1(tcp, recvWriter, sendReader,
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   183
		cl.statmgr.newListener())
9
4fe926b03827 Reorganize so we have a layered approach to IO with the server.
Chris Jones <chris@cjones.org>
parents: 8
diff changeset
   184
121
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
   185
	// Start the reader and writer that convert to and from XML.
147
d7679d991b17 Function renames and improved doc.
Chris Jones <christian.jones@sri.com>
parents: 146
diff changeset
   186
	recvXmlCh := make(chan interface{})
163
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   187
	go cl.recvXml(recvReader, recvXmlCh, extStanza)
147
d7679d991b17 Function renames and improved doc.
Chris Jones <christian.jones@sri.com>
parents: 146
diff changeset
   188
	sendXmlCh := make(chan interface{})
163
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   189
	cl.sendRaw = sendXmlCh
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   190
	go cl.sendXml(sendWriter, sendXmlCh)
121
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
   191
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
   192
	// Start the reader and writer that convert between XML and
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
   193
	// XMPP stanzas.
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
   194
	recvRawXmpp := make(chan Stanza)
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   195
	go cl.recvStream(recvXmlCh, recvRawXmpp, cl.statmgr.newListener())
121
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
   196
	sendRawXmpp := make(chan Stanza)
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   197
	go sendStream(sendXmlCh, sendRawXmpp, cl.statmgr.newListener())
5
faef59c8db05 Added a goroutine to read data from the remote and parse it into
Chris Jones <chris@cjones.org>
parents: 4
diff changeset
   198
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   199
	// Start the managers for the filters that can modify what the
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   200
	// app sees or sends.
121
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
   201
	recvFiltXmpp := make(chan Stanza)
127
a8f9a0c07fc8 Renamed client.In and client.Out to Recv and Send, respectively.
Chris Jones <christian.jones@sri.com>
parents: 126
diff changeset
   202
	cl.Recv = recvFiltXmpp
121
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
   203
	go filterMgr(cl.recvFilterAdd, recvRawXmpp, recvFiltXmpp)
ebb86cbdd218 Changed the way filters work. They're now symmetrical, consisting of a paired send filter and receive filter.
Chris Jones <christian.jones@sri.com>
parents: 120
diff changeset
   204
	sendFiltXmpp := make(chan Stanza)
127
a8f9a0c07fc8 Renamed client.In and client.Out to Recv and Send, respectively.
Chris Jones <christian.jones@sri.com>
parents: 126
diff changeset
   205
	cl.Send = sendFiltXmpp
134
80b764fa2f08 Fixed some logic errors and initialized some things that needed to be initialized.
Chris Jones <christian.jones@sri.com>
parents: 132
diff changeset
   206
	go filterMgr(cl.sendFilterAdd, sendFiltXmpp, sendRawXmpp)
80b764fa2f08 Fixed some logic errors and initialized some things that needed to be initialized.
Chris Jones <christian.jones@sri.com>
parents: 132
diff changeset
   207
	// Set up the initial filters.
80b764fa2f08 Fixed some logic errors and initialized some things that needed to be initialized.
Chris Jones <christian.jones@sri.com>
parents: 132
diff changeset
   208
	for _, ext := range exts {
80b764fa2f08 Fixed some logic errors and initialized some things that needed to be initialized.
Chris Jones <christian.jones@sri.com>
parents: 132
diff changeset
   209
		cl.AddRecvFilter(ext.RecvFilter)
80b764fa2f08 Fixed some logic errors and initialized some things that needed to be initialized.
Chris Jones <christian.jones@sri.com>
parents: 132
diff changeset
   210
		cl.AddSendFilter(ext.SendFilter)
80b764fa2f08 Fixed some logic errors and initialized some things that needed to be initialized.
Chris Jones <christian.jones@sri.com>
parents: 132
diff changeset
   211
	}
45
abf958bcc201 Added a stack of filters which can intercept data before it gets to
Chris Jones <chris@cjones.org>
parents: 44
diff changeset
   212
8
30a7752cf8f7 Added the ability to parse <stream:features>.
Chris Jones <chris@cjones.org>
parents: 6
diff changeset
   213
	// Initial handshake.
118
fb9bb98a8d70 Code cleanup: Renamed the Version constant. Moved the unique-ID generator into its own file.
Chris Jones <christian.jones@sri.com>
parents: 116
diff changeset
   214
	hsOut := &stream{To: jid.Domain, Version: XMPPVersion}
163
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   215
	cl.sendRaw <- hsOut
9
4fe926b03827 Reorganize so we have a layered approach to IO with the server.
Chris Jones <chris@cjones.org>
parents: 8
diff changeset
   216
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   217
	// Wait until resource binding is complete.
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   218
	if err := cl.statmgr.awaitStatus(StatusBound); err != nil {
163
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   219
		return nil, cl.getError(err)
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   220
	}
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   221
157
eadf15a06ff5 Don't keep the password around after we've used it.
Chris Jones <chris@cjones.org>
parents: 153
diff changeset
   222
	// Forget about the password, for paranoia's sake.
eadf15a06ff5 Don't keep the password around after we've used it.
Chris Jones <chris@cjones.org>
parents: 153
diff changeset
   223
	cl.password = ""
eadf15a06ff5 Don't keep the password around after we've used it.
Chris Jones <chris@cjones.org>
parents: 153
diff changeset
   224
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   225
	// Initialize the session.
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   226
	id := NextId()
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   227
	iq := &Iq{Header: Header{To: cl.Jid.Domain, Id: id, Type: "set",
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   228
		Nested: []interface{}{Generic{XMLName: xml.Name{Space: NsSession, Local: "session"}}}}}
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   229
	ch := make(chan error)
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   230
	f := func(st Stanza) {
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   231
		iq, ok := st.(*Iq)
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   232
		if !ok {
163
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   233
			ch <- fmt.Errorf("bad session start reply: %#v", st)
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   234
		}
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   235
		if iq.Type == "error" {
163
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   236
			ch <- fmt.Errorf("Can't start session: %v", iq.Error)
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   237
		}
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   238
		ch <- nil
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   239
	}
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   240
	cl.SetCallback(id, f)
163
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   241
	cl.sendRaw <- iq
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   242
	// Now wait until the callback is called.
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   243
	if err := <-ch; err != nil {
163
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   244
		return nil, cl.getError(err)
153
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   245
	}
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   246
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   247
	// This allows the client to receive stanzas.
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   248
	cl.setStatus(StatusRunning)
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   249
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   250
	// Request the roster.
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   251
	cl.Roster.update()
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   252
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   253
	// Send the initial presence.
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   254
	cl.Send <- &pr
bbd4166df95d Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
Chris Jones <christian.jones@sri.com>
parents: 148
diff changeset
   255
163
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   256
	return cl, cl.getError(nil)
6
8e425e340ca1 Implemented writing to the remote. Now we have bidirectional communication.
Chris Jones <christian.jones@sri.com>
parents: 5
diff changeset
   257
}
162
7b5586a5e109 Added a Close() function and fixed a couple of shutdown bugs.
Chris Jones <chris@cjones.org>
parents: 158
diff changeset
   258
7b5586a5e109 Added a Close() function and fixed a couple of shutdown bugs.
Chris Jones <chris@cjones.org>
parents: 158
diff changeset
   259
func (cl *Client) Close() {
7b5586a5e109 Added a Close() function and fixed a couple of shutdown bugs.
Chris Jones <chris@cjones.org>
parents: 158
diff changeset
   260
	// Shuts down the receivers:
7b5586a5e109 Added a Close() function and fixed a couple of shutdown bugs.
Chris Jones <chris@cjones.org>
parents: 158
diff changeset
   261
	cl.setStatus(StatusShutdown)
169
f59b79d032da Make an attempt to only close the send channel if it's still open.
Chris Jones <chris@cjones.org>
parents: 168
diff changeset
   262
162
7b5586a5e109 Added a Close() function and fixed a couple of shutdown bugs.
Chris Jones <chris@cjones.org>
parents: 158
diff changeset
   263
	// Shuts down the senders:
170
d496de556c9a Trying to be smarter about closing channels: Ask the application not to close the Send channel, but let us do that from Close() instead.
Chris Jones <chris@cjones.org>
parents: 169
diff changeset
   264
	cl.shutdownOnce.Do(func() { close(cl.Send) })
162
7b5586a5e109 Added a Close() function and fixed a couple of shutdown bugs.
Chris Jones <chris@cjones.org>
parents: 158
diff changeset
   265
}
163
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   266
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   267
// If there's a buffered error in the channel, return it. Otherwise,
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   268
// return what was passed to us. The idea is that the error in the
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   269
// channel probably preceded (and caused) the one that's passed as an
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   270
// argument here.
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   271
func (cl *Client) getError(err1 error) error {
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   272
	select {
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   273
	case err0 := <-cl.error:
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   274
		return err0
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   275
	default:
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   276
		return err1
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   277
	}
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   278
}
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   279
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   280
// Register an error that happened in the internals somewhere. If
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   281
// there's already an error in the channel, discard the newer one in
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   282
// favor of the older.
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   283
func (cl *Client) setError(err error) {
170
d496de556c9a Trying to be smarter about closing channels: Ask the application not to close the Send channel, but let us do that from Close() instead.
Chris Jones <chris@cjones.org>
parents: 169
diff changeset
   284
	defer cl.Close()
d496de556c9a Trying to be smarter about closing channels: Ask the application not to close the Send channel, but let us do that from Close() instead.
Chris Jones <chris@cjones.org>
parents: 169
diff changeset
   285
	defer cl.setStatus(StatusError)
164
6b527647086c Changed order of operations in setError() so that the underlying error is reported.
Chris Jones <chris@cjones.org>
parents: 163
diff changeset
   286
163
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   287
	if len(cl.error) > 0 {
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   288
		return
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   289
	}
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   290
	// If we're in a race between two calls to this function,
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   291
	// trying to set the "first" error, just arbitrarily let one
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   292
	// of them win.
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   293
	select {
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   294
	case cl.error <- err:
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   295
	default:
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   296
	}
3f891f7fe817 Removed the weirdo logging facility. There's now a Debug variable which can be set which replaces the former debug log. NewClient() will return an error if something goes wrong setting up the connection.
Chris Jones <chris@cjones.org>
parents: 162
diff changeset
   297
}