stream.go
author Chris Jones <chris@cjones.org>
Thu, 29 Dec 2011 11:02:21 -0700
changeset 29 a456133ed0ac
parent 26 4d0a369079ce
child 30 a77fc342e013
permissions -rw-r--r--
Don't accept data on Client.Out until resource binding is complete. StartSession() won't do its work until after this happens. That means the app can call StartSession() and wait for it to return before checking Client.Jid.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
     1
// Copyright 2011 The Go Authors.  All rights reserved.
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
     2
// Use of this source code is governed by a BSD-style
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
     3
// license that can be found in the LICENSE file.
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
     4
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
     5
// This file contains the three layers of processing for the
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
     6
// communication with the server: transport (where TLS happens), XML
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
     7
// (where strings are converted to go structures), and Stream (where
17
d269d9c0fc8e Code review.
Chris Jones <chris@cjones.org>
parents: 16
diff changeset
     8
// we respond to XMPP events on behalf of the library client), or send
d269d9c0fc8e Code review.
Chris Jones <chris@cjones.org>
parents: 16
diff changeset
     9
// those events to the client.
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    11
package xmpp
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    12
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    13
import (
11
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
    14
	"big"
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
    15
	"crypto/md5"
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
    16
	"crypto/rand"
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    17
	"crypto/tls"
11
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
    18
	"encoding/base64"
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
    19
	"fmt"
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    20
	"io"
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    21
	"log"
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    22
	"net"
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    23
	"os"
11
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
    24
	"regexp"
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
    25
	"strings"
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    26
	"time"
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    27
	"xml"
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    28
)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    29
17
d269d9c0fc8e Code review.
Chris Jones <chris@cjones.org>
parents: 16
diff changeset
    30
// Callback to handle a stanza with a particular id.
13
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
    31
type stanzaHandler struct {
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
    32
	id string
17
d269d9c0fc8e Code review.
Chris Jones <chris@cjones.org>
parents: 16
diff changeset
    33
	// Return true means pass this to the application
13
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
    34
	f func(Stanza) bool
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
    35
}
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
    36
20
e119444a1119 Replaced TODO comments with Go-style BUG(me) comments.
Chris Jones <chris@cjones.org>
parents: 19
diff changeset
    37
// BUG(cjyar) Review all these *Client receiver methods. They should
17
d269d9c0fc8e Code review.
Chris Jones <chris@cjones.org>
parents: 16
diff changeset
    38
// probably either all be receivers, or none.
d269d9c0fc8e Code review.
Chris Jones <chris@cjones.org>
parents: 16
diff changeset
    39
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    40
func (cl *Client) readTransport(w io.Writer) {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    41
	defer tryClose(cl.socket, w)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    42
	cl.socket.SetReadTimeout(1e8)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    43
	p := make([]byte, 1024)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    44
	for {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    45
		if cl.socket == nil {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    46
			cl.waitForSocket()
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    47
		}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    48
		nr, err := cl.socket.Read(p)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    49
		if nr == 0 {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    50
			if errno, ok := err.(*net.OpError) ; ok {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    51
				if errno.Timeout() {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    52
					continue
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    53
				}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    54
			}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    55
			log.Printf("read: %s", err.String())
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    56
			break
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    57
		}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    58
		nw, err := w.Write(p[:nr])
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    59
		if nw < nr {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    60
			log.Println("read: %s", err.String())
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    61
			break
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    62
		}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    63
	}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    64
}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    65
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    66
func (cl *Client) writeTransport(r io.Reader) {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    67
	defer tryClose(r, cl.socket)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    68
	p := make([]byte, 1024)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    69
	for {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    70
		nr, err := r.Read(p)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    71
		if nr == 0 {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    72
			log.Printf("write: %s", err.String())
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    73
			break
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    74
		}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    75
		nw, err := cl.socket.Write(p[:nr])
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    76
		if nw < nr {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    77
			log.Println("write: %s", err.String())
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    78
			break
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    79
		}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    80
	}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    81
}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    82
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    83
func readXml(r io.Reader, ch chan<- interface{}) {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    84
	if debug {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    85
		pr, pw := io.Pipe()
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    86
		go tee(r, pw, "S: ")
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    87
		r = pr
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    88
	}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    89
	defer tryClose(r, ch)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    90
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    91
	p := xml.NewParser(r)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    92
	for {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    93
		// Sniff the next token on the stream.
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    94
		t, err := p.Token()
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    95
		if t == nil {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    96
			if err != os.EOF {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    97
				log.Printf("read: %v", err)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    98
			}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
    99
			break
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   100
		}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   101
		var se xml.StartElement
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   102
		var ok bool
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   103
		if se, ok = t.(xml.StartElement) ; !ok {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   104
			continue
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   105
		}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   106
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   107
		// Allocate the appropriate structure for this token.
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   108
		var obj interface{}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   109
		switch se.Name.Space + " " + se.Name.Local {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   110
		case nsStream + " stream":
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   111
			st, err := parseStream(se)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   112
			if err != nil {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   113
				log.Printf("unmarshal stream: %v",
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   114
					err)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   115
				break
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   116
			}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   117
			ch <- st
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   118
			continue
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   119
		case "stream error", nsStream + " error":
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   120
			obj = &StreamError{}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   121
		case nsStream + " features":
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   122
			obj = &Features{}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   123
		case nsTLS + " proceed", nsTLS + " failure":
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   124
			obj = &starttls{}
11
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   125
		case nsSASL + " challenge", nsSASL + " failure",
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   126
			nsSASL + " success":
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   127
			obj = &auth{}
12
122ab6208c3c Added resource binding and structures for <iq>, <message>, and <presence>.
Chris Jones <chris@cjones.org>
parents: 11
diff changeset
   128
		case "jabber:client iq":
122ab6208c3c Added resource binding and structures for <iq>, <message>, and <presence>.
Chris Jones <chris@cjones.org>
parents: 11
diff changeset
   129
			obj = &Iq{}
16
b839e37b3f29 Parse <presence> and <message> stanzas.
Chris Jones <chris@cjones.org>
parents: 15
diff changeset
   130
		case "jabber:client message":
b839e37b3f29 Parse <presence> and <message> stanzas.
Chris Jones <chris@cjones.org>
parents: 15
diff changeset
   131
			obj = &Message{}
b839e37b3f29 Parse <presence> and <message> stanzas.
Chris Jones <chris@cjones.org>
parents: 15
diff changeset
   132
		case "jabber:client presence":
b839e37b3f29 Parse <presence> and <message> stanzas.
Chris Jones <chris@cjones.org>
parents: 15
diff changeset
   133
			obj = &Presence{}
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   134
		default:
21
8f6ae5cfc9b9 Renamed Unrecognized to Generic.
Chris Jones <chris@cjones.org>
parents: 20
diff changeset
   135
			obj = &Generic{}
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   136
			log.Printf("Ignoring unrecognized: %s %s\n",
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   137
				se.Name.Space, se.Name.Local)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   138
		}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   139
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   140
		// Read the complete XML stanza.
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   141
		err = p.Unmarshal(obj, &se)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   142
		if err != nil {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   143
			log.Printf("unmarshal: %v", err)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   144
			break
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   145
		}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   146
20
e119444a1119 Replaced TODO comments with Go-style BUG(me) comments.
Chris Jones <chris@cjones.org>
parents: 19
diff changeset
   147
		// BUG(cjyar) If it's a Stanza, use reflection to
21
8f6ae5cfc9b9 Renamed Unrecognized to Generic.
Chris Jones <chris@cjones.org>
parents: 20
diff changeset
   148
		// search for any Generic elements and fill in
20
e119444a1119 Replaced TODO comments with Go-style BUG(me) comments.
Chris Jones <chris@cjones.org>
parents: 19
diff changeset
   149
		// their attributes.
17
d269d9c0fc8e Code review.
Chris Jones <chris@cjones.org>
parents: 16
diff changeset
   150
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   151
		// Put it on the channel.
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   152
		ch <- obj
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   153
	}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   154
}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   155
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   156
func writeXml(w io.Writer, ch <-chan interface{}) {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   157
	if debug {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   158
		pr, pw := io.Pipe()
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   159
		go tee(pr, w, "C: ")
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   160
		w = pw
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   161
	}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   162
	defer tryClose(w, ch)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   163
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   164
	for obj := range ch {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   165
		err := xml.Marshal(w, obj)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   166
		if err != nil {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   167
			log.Printf("write: %v", err)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   168
			break
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   169
		}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   170
	}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   171
}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   172
23
b5de44679389 Made the input and output channels of type Stanza rather than
Chris Jones <chris@cjones.org>
parents: 22
diff changeset
   173
func (cl *Client) readStream(srvIn <-chan interface{}, cliOut chan<- Stanza) {
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   174
	defer tryClose(srvIn, cliOut)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   175
13
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   176
	handlers := make(map[string] func(Stanza) bool)
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   177
	for {
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   178
		select {
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   179
		case h := <- cl.handlers:
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   180
			handlers[h.id] = h.f
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   181
		case x := <- srvIn:
26
4d0a369079ce Removed the TextOut channel.
Chris Jones <chris@cjones.org>
parents: 25
diff changeset
   182
			if x == nil {
4d0a369079ce Removed the TextOut channel.
Chris Jones <chris@cjones.org>
parents: 25
diff changeset
   183
				break
4d0a369079ce Removed the TextOut channel.
Chris Jones <chris@cjones.org>
parents: 25
diff changeset
   184
			}
13
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   185
			send := false
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   186
			switch obj := x.(type) {
22
d6b7b4cbf50d Made the stream type non-public.
Chris Jones <chris@cjones.org>
parents: 21
diff changeset
   187
			case *stream:
13
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   188
				handleStream(obj)
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   189
			case *Features:
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   190
				cl.handleFeatures(obj)
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   191
			case *starttls:
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   192
				cl.handleTls(obj)
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   193
			case *auth:
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   194
				cl.handleSasl(obj)
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   195
			default:
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   196
				send = true
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   197
			}
29
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   198
			if !send {
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   199
				continue
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   200
			}
23
b5de44679389 Made the input and output channels of type Stanza rather than
Chris Jones <chris@cjones.org>
parents: 22
diff changeset
   201
			st, ok := x.(Stanza)
b5de44679389 Made the input and output channels of type Stanza rather than
Chris Jones <chris@cjones.org>
parents: 22
diff changeset
   202
			if !ok {
b5de44679389 Made the input and output channels of type Stanza rather than
Chris Jones <chris@cjones.org>
parents: 22
diff changeset
   203
				log.Printf("Unhandled non-stanza: %v",
b5de44679389 Made the input and output channels of type Stanza rather than
Chris Jones <chris@cjones.org>
parents: 22
diff changeset
   204
					x)
b5de44679389 Made the input and output channels of type Stanza rather than
Chris Jones <chris@cjones.org>
parents: 22
diff changeset
   205
				continue
b5de44679389 Made the input and output channels of type Stanza rather than
Chris Jones <chris@cjones.org>
parents: 22
diff changeset
   206
			}
b5de44679389 Made the input and output channels of type Stanza rather than
Chris Jones <chris@cjones.org>
parents: 22
diff changeset
   207
			if handlers[st.XId()] != nil {
13
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   208
				f := handlers[st.XId()]
14
fd6781a41e6f Don't forget to remove the stanza handler after it's been used.
Chris Jones <chris@cjones.org>
parents: 13
diff changeset
   209
				handlers[st.XId()] = nil
13
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   210
				send = f(st)
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   211
			}
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   212
			if send {
23
b5de44679389 Made the input and output channels of type Stanza rather than
Chris Jones <chris@cjones.org>
parents: 22
diff changeset
   213
				cliOut <- st
13
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   214
			}
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   215
		}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   216
	}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   217
}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   218
29
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   219
// This loop is paused until resource binding is complete. Otherwise
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   220
// the app might inject something inappropriate into our negotiations
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   221
// with the server. The control channel controls this loop's
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   222
// activity.
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   223
func writeStream(srvOut chan<- interface{}, cliIn <-chan Stanza,
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   224
	control <-chan int) {
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   225
	defer tryClose(srvOut, cliIn)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   226
29
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   227
	var input <-chan Stanza
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   228
	for {
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   229
		select {
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   230
		case status := <- control:
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   231
			switch status {
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   232
			case 0:
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   233
				input = nil
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   234
			case 1:
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   235
				input = cliIn
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   236
			case -1:
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   237
				break
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   238
			}
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   239
		case x := <- input:
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   240
			srvOut <- x
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   241
		}
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   242
	}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   243
}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   244
22
d6b7b4cbf50d Made the stream type non-public.
Chris Jones <chris@cjones.org>
parents: 21
diff changeset
   245
func handleStream(ss *stream) {
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   246
}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   247
11
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   248
func (cl *Client) handleFeatures(fe *Features) {
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   249
	if fe.Starttls != nil {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   250
		start := &starttls{XMLName: xml.Name{Space: nsTLS,
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   251
			Local: "starttls"}}
11
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   252
		cl.xmlOut <- start
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   253
		return
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   254
	}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   255
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   256
	if len(fe.Mechanisms.Mechanism) > 0 {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   257
		cl.chooseSasl(fe)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   258
		return
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   259
	}
12
122ab6208c3c Added resource binding and structures for <iq>, <message>, and <presence>.
Chris Jones <chris@cjones.org>
parents: 11
diff changeset
   260
122ab6208c3c Added resource binding and structures for <iq>, <message>, and <presence>.
Chris Jones <chris@cjones.org>
parents: 11
diff changeset
   261
	if fe.Bind != nil {
122ab6208c3c Added resource binding and structures for <iq>, <message>, and <presence>.
Chris Jones <chris@cjones.org>
parents: 11
diff changeset
   262
		cl.bind(fe.Bind)
17
d269d9c0fc8e Code review.
Chris Jones <chris@cjones.org>
parents: 16
diff changeset
   263
		return
12
122ab6208c3c Added resource binding and structures for <iq>, <message>, and <presence>.
Chris Jones <chris@cjones.org>
parents: 11
diff changeset
   264
	}
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   265
}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   266
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   267
// readTransport() is running concurrently. We need to stop it,
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   268
// negotiate TLS, then start it again. It calls waitForSocket() in
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   269
// its inner loop; see below.
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   270
func (cl *Client) handleTls(t *starttls) {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   271
	tcp := cl.socket
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   272
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   273
	// Set the socket to nil, and wait for the reader routine to
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   274
	// signal that it's paused.
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   275
	cl.socket = nil
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   276
	cl.socketSync.Add(1)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   277
	cl.socketSync.Wait()
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   278
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   279
	// Negotiate TLS with the server.
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   280
	tls := tls.Client(tcp, nil)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   281
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   282
	// Make the TLS connection available to the reader, and wait
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   283
	// for it to signal that it's working again.
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   284
	cl.socketSync.Add(1)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   285
	cl.socket = tls
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   286
	cl.socketSync.Wait()
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   287
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   288
	// Reset the read timeout on the (underlying) socket so the
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   289
	// reader doesn't get woken up unnecessarily.
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   290
	tcp.SetReadTimeout(0)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   291
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   292
	log.Println("TLS negotiation succeeded.")
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   293
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   294
	// Now re-send the initial handshake message to start the new
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   295
	// session.
22
d6b7b4cbf50d Made the stream type non-public.
Chris Jones <chris@cjones.org>
parents: 21
diff changeset
   296
	hsOut := &stream{To: cl.Jid.Domain, Version: Version}
10
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   297
	cl.xmlOut <- hsOut
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   298
}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   299
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   300
// Synchronize with handleTls(). Called from readTransport() when
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   301
// cl.socket is nil.
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   302
func (cl *Client) waitForSocket() {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   303
	// Signal that we've stopped reading from the socket.
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   304
	cl.socketSync.Done()
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   305
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   306
	// Wait until the socket is available again.
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   307
	for cl.socket == nil {
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   308
		time.Sleep(1e8)
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   309
	}
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   310
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   311
	// Signal that we're going back to the read loop.
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   312
	cl.socketSync.Done()
f38b0ee7b1c1 Added TLS negotiation.
Chris Jones <chris@cjones.org>
parents:
diff changeset
   313
}
11
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   314
19
e923f28d65aa Added an example of the use of godoc's BUG comment.
Chris Jones <chris@cjones.org>
parents: 17
diff changeset
   315
// BUG(cjyar) Doesn't implement TLS/SASL EXTERNAL.
11
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   316
func (cl *Client) chooseSasl(fe *Features) {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   317
	var digestMd5 bool
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   318
	for _, m := range(fe.Mechanisms.Mechanism) {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   319
		switch strings.ToLower(m) {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   320
		case "digest-md5":
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   321
			digestMd5 = true
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   322
		}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   323
	}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   324
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   325
	if digestMd5 {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   326
		auth := &auth{XMLName: xml.Name{Space: nsSASL, Local:
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   327
				"auth"}, Mechanism: "DIGEST-MD5"}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   328
		cl.xmlOut <- auth
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   329
	}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   330
}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   331
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   332
func (cl *Client) handleSasl(srv *auth) {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   333
	switch strings.ToLower(srv.XMLName.Local) {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   334
	case "challenge":
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   335
		b64 := base64.StdEncoding
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   336
		str, err := b64.DecodeString(srv.Chardata)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   337
		if err != nil {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   338
			log.Printf("SASL challenge decode: %s",
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   339
				err.String())
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   340
			return;
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   341
		}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   342
		srvMap := parseSasl(string(str))
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   343
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   344
		if cl.saslExpected == "" {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   345
			cl.saslDigest1(srvMap)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   346
		} else {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   347
			cl.saslDigest2(srvMap)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   348
		}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   349
	case "failure":
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   350
		log.Println("SASL authentication failed")
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   351
	case "success":
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   352
		log.Println("SASL authentication succeeded")
22
d6b7b4cbf50d Made the stream type non-public.
Chris Jones <chris@cjones.org>
parents: 21
diff changeset
   353
		ss := &stream{To: cl.Jid.Domain, Version: Version}
11
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   354
		cl.xmlOut <- ss
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   355
	}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   356
}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   357
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   358
func (cl *Client) saslDigest1(srvMap map[string] string) {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   359
	// Make sure it supports qop=auth
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   360
	var hasAuth bool
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   361
	for _, qop := range(strings.Fields(srvMap["qop"])) {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   362
		if qop == "auth" {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   363
			hasAuth = true
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   364
		}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   365
	}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   366
	if !hasAuth {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   367
		log.Println("Server doesn't support SASL auth")
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   368
		return;
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   369
	}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   370
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   371
	// Pick a realm.
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   372
	var realm string
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   373
	if srvMap["realm"] != "" {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   374
		realm = strings.Fields(srvMap["realm"])[0]
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   375
	}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   376
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   377
	passwd := cl.password
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   378
	nonce := srvMap["nonce"]
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   379
	digestUri := "xmpp/" + cl.Jid.Domain
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   380
	nonceCount := int32(1)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   381
	nonceCountStr := fmt.Sprintf("%08x", nonceCount)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   382
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   383
	// Begin building the response. Username is
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   384
	// user@domain or just domain.
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   385
	var username string
25
7437d6eed227 Made JID.Node a string rather than *string. This is more appropriate
Chris Jones <chris@cjones.org>
parents: 23
diff changeset
   386
	if cl.Jid.Node == "" {
11
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   387
		username = cl.Jid.Domain
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   388
	} else {
25
7437d6eed227 Made JID.Node a string rather than *string. This is more appropriate
Chris Jones <chris@cjones.org>
parents: 23
diff changeset
   389
		username = cl.Jid.Node
11
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   390
	}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   391
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   392
	// Generate our own nonce from random data.
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   393
	randSize := big.NewInt(0)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   394
	randSize.Lsh(big.NewInt(1), 64)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   395
	cnonce, err := rand.Int(rand.Reader, randSize)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   396
	if err != nil {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   397
		log.Println("SASL rand: %s", err.String())
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   398
		return
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   399
	}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   400
	cnonceStr := fmt.Sprintf("%016x", cnonce)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   401
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   402
	/* Now encode the actual password response, as well as the
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   403
	 * expected next challenge from the server. */
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   404
	response := saslDigestResponse(username, realm, passwd, nonce,
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   405
		cnonceStr, "AUTHENTICATE", digestUri, nonceCountStr)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   406
	next := saslDigestResponse(username, realm, passwd, nonce,
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   407
		cnonceStr, "", digestUri, nonceCountStr)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   408
	cl.saslExpected = next
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   409
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   410
	// Build the map which will be encoded.
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   411
	clMap := make(map[string]string)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   412
	clMap["realm"] = `"` + realm + `"`
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   413
	clMap["username"] = `"` + username + `"`
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   414
	clMap["nonce"] = `"` + nonce + `"`
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   415
	clMap["cnonce"] = `"` + cnonceStr + `"`
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   416
	clMap["nc"] =  nonceCountStr
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   417
	clMap["qop"] = "auth"
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   418
	clMap["digest-uri"] = `"` + digestUri + `"`
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   419
	clMap["response"] = response
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   420
	if srvMap["charset"] == "utf-8" {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   421
		clMap["charset"] = "utf-8"
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   422
	}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   423
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   424
	// Encode the map and send it.
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   425
	clStr := packSasl(clMap)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   426
	b64 := base64.StdEncoding
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   427
	clObj := &auth{XMLName: xml.Name{Space: nsSASL, Local:
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   428
			"response"}, Chardata:
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   429
		b64.EncodeToString([]byte(clStr))}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   430
	cl.xmlOut <- clObj
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   431
}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   432
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   433
func (cl *Client) saslDigest2(srvMap map[string] string) {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   434
	if cl.saslExpected == srvMap["rspauth"] {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   435
		clObj := &auth{XMLName: xml.Name{Space: nsSASL, Local:
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   436
				"response"}}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   437
		cl.xmlOut <- clObj
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   438
	} else {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   439
		clObj := &auth{XMLName: xml.Name{Space: nsSASL, Local:
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   440
				"failure"}, Any:
21
8f6ae5cfc9b9 Renamed Unrecognized to Generic.
Chris Jones <chris@cjones.org>
parents: 20
diff changeset
   441
			&Generic{XMLName: xml.Name{Space: nsSASL,
11
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   442
				Local: "abort"}}}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   443
		cl.xmlOut <- clObj
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   444
	}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   445
}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   446
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   447
// Takes a string like `key1=value1,key2="value2"...` and returns a
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   448
// key/value map.
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   449
func parseSasl(in string) map[string]string {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   450
	re := regexp.MustCompile(`([^=]+)="?([^",]+)"?,?`)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   451
	strs := re.FindAllStringSubmatch(in, -1)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   452
	m := make(map[string]string)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   453
	for _, pair := range(strs) {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   454
		key := strings.ToLower(string(pair[1]))
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   455
		value := string(pair[2])
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   456
		m[key] = value
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   457
	}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   458
	return m
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   459
}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   460
17
d269d9c0fc8e Code review.
Chris Jones <chris@cjones.org>
parents: 16
diff changeset
   461
// Inverse of parseSasl().
11
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   462
func packSasl(m map[string]string) string {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   463
	var terms []string
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   464
	for key, value := range(m) {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   465
		if key == "" || value == "" || value == `""` {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   466
			continue
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   467
		}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   468
		terms = append(terms, key + "=" + value)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   469
	}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   470
	return strings.Join(terms, ",")
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   471
}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   472
17
d269d9c0fc8e Code review.
Chris Jones <chris@cjones.org>
parents: 16
diff changeset
   473
// Computes the response string for digest authentication.
11
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   474
func saslDigestResponse(username, realm, passwd, nonce, cnonceStr,
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   475
	authenticate, digestUri, nonceCountStr string) string {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   476
	h := func(text string) []byte {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   477
		h := md5.New()
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   478
		h.Write([]byte(text))
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   479
		return h.Sum()
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   480
	}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   481
	hex := func(bytes []byte) string {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   482
		return fmt.Sprintf("%x", bytes)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   483
	}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   484
	kd := func(secret, data string) []byte {
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   485
		return h(secret + ":" + data)
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   486
	}
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   487
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   488
	a1 := string(h(username + ":" + realm + ":" + passwd)) + ":" +
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   489
		nonce + ":" + cnonceStr
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   490
	a2 := authenticate + ":" + digestUri
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   491
	response := hex(kd(hex(h(a1)), nonce + ":" +
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   492
		nonceCountStr + ":" + cnonceStr + ":auth:" +
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   493
		hex(h(a2))))
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   494
	return response
48be1ae93fd4 Added SASL digest authentication.
Chris Jones <chris@cjones.org>
parents: 10
diff changeset
   495
}
12
122ab6208c3c Added resource binding and structures for <iq>, <message>, and <presence>.
Chris Jones <chris@cjones.org>
parents: 11
diff changeset
   496
17
d269d9c0fc8e Code review.
Chris Jones <chris@cjones.org>
parents: 16
diff changeset
   497
// Send a request to bind a resource. RFC 3920, section 7.
21
8f6ae5cfc9b9 Renamed Unrecognized to Generic.
Chris Jones <chris@cjones.org>
parents: 20
diff changeset
   498
func (cl *Client) bind(bind *Generic) {
12
122ab6208c3c Added resource binding and structures for <iq>, <message>, and <presence>.
Chris Jones <chris@cjones.org>
parents: 11
diff changeset
   499
	res := cl.Jid.Resource
122ab6208c3c Added resource binding and structures for <iq>, <message>, and <presence>.
Chris Jones <chris@cjones.org>
parents: 11
diff changeset
   500
	msg := &Iq{Type: "set", Id: cl.NextId(), Any:
21
8f6ae5cfc9b9 Renamed Unrecognized to Generic.
Chris Jones <chris@cjones.org>
parents: 20
diff changeset
   501
		&Generic{XMLName: xml.Name{Space: nsBind, Local:
12
122ab6208c3c Added resource binding and structures for <iq>, <message>, and <presence>.
Chris Jones <chris@cjones.org>
parents: 11
diff changeset
   502
					"bind"}}}
122ab6208c3c Added resource binding and structures for <iq>, <message>, and <presence>.
Chris Jones <chris@cjones.org>
parents: 11
diff changeset
   503
	if res != "" {
21
8f6ae5cfc9b9 Renamed Unrecognized to Generic.
Chris Jones <chris@cjones.org>
parents: 20
diff changeset
   504
		msg.Any.Any = &Generic{XMLName: xml.Name{Local:
12
122ab6208c3c Added resource binding and structures for <iq>, <message>, and <presence>.
Chris Jones <chris@cjones.org>
parents: 11
diff changeset
   505
				"resource"}, Chardata: res}
122ab6208c3c Added resource binding and structures for <iq>, <message>, and <presence>.
Chris Jones <chris@cjones.org>
parents: 11
diff changeset
   506
	}
15
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   507
	f := func(st Stanza) bool {
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   508
		if st.XType() == "error" {
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   509
			log.Println("Resource binding failed")
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   510
			return false
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   511
		}
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   512
		bind := st.XChild()
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   513
		if bind == nil {
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   514
			log.Println("nil resource bind")
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   515
			return false
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   516
		}
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   517
		jidEle := bind.Any
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   518
		if jidEle == nil {
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   519
			log.Println("nil resource")
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   520
			return false
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   521
		}
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   522
		jidStr := jidEle.Chardata
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   523
		if jidStr == "" {
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   524
			log.Println("empty resource")
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   525
			return false
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   526
		}
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   527
		jid := new(JID)
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   528
		if !jid.Set(jidStr) {
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   529
			log.Println("Can't parse JID %s", jidStr)
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   530
			return false
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   531
		}
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   532
		cl.Jid = *jid
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   533
		log.Printf("Bound resource: %s", cl.Jid.String())
29
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   534
		cl.bindDone()
a456133ed0ac Don't accept data on Client.Out until resource binding is
Chris Jones <chris@cjones.org>
parents: 26
diff changeset
   535
		return false
15
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   536
	}
aa2cf77f0ed3 When the server sends us our newly bound resource, update Client.Jid
Chris Jones <chris@cjones.org>
parents: 14
diff changeset
   537
	cl.HandleStanza(msg.Id, f)
12
122ab6208c3c Added resource binding and structures for <iq>, <message>, and <presence>.
Chris Jones <chris@cjones.org>
parents: 11
diff changeset
   538
	cl.xmlOut <- msg
122ab6208c3c Added resource binding and structures for <iq>, <message>, and <presence>.
Chris Jones <chris@cjones.org>
parents: 11
diff changeset
   539
}
13
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   540
17
d269d9c0fc8e Code review.
Chris Jones <chris@cjones.org>
parents: 16
diff changeset
   541
// Register a callback to handle the next XMPP stanza (iq, message, or
d269d9c0fc8e Code review.
Chris Jones <chris@cjones.org>
parents: 16
diff changeset
   542
// presence) with a given id. The provided function will not be called
d269d9c0fc8e Code review.
Chris Jones <chris@cjones.org>
parents: 16
diff changeset
   543
// more than once. If it returns false, the stanza will not be made
d269d9c0fc8e Code review.
Chris Jones <chris@cjones.org>
parents: 16
diff changeset
   544
// available on the normal Client.In channel.
13
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   545
func (cl *Client) HandleStanza(id string, f func(Stanza) bool) {
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   546
	h := &stanzaHandler{id: id, f: f}
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   547
	cl.handlers <- h
c9527bbe99a6 Added a callback handler which will handle a stanza with a particular id.
Chris Jones <chris@cjones.org>
parents: 12
diff changeset
   548
}