xmpp/layer3.go
changeset 183 b4bd77d58a3e
parent 178 ccfebbd9f49b
equal deleted inserted replaced
182:626c390682fc 183:b4bd77d58a3e
     3 
     3 
     4 package xmpp
     4 package xmpp
     5 
     5 
     6 import (
     6 import (
     7 	"encoding/xml"
     7 	"encoding/xml"
       
     8 	"fmt"
       
     9 	"log"
     8 )
    10 )
     9 
    11 
    10 // Callback to handle a stanza with a particular id.
    12 // Callback to handle a stanza with a particular id.
    11 type callback struct {
    13 type callback struct {
    12 	id string
    14 	id string
    24 	defer close(sendXml)
    26 	defer close(sendXml)
    25 
    27 
    26 	var input <-chan Stanza
    28 	var input <-chan Stanza
    27 	for {
    29 	for {
    28 		select {
    30 		select {
    29 		case stat := <-status:
    31 		case stat, ok := <-status:
       
    32 			if !ok {
       
    33 				return
       
    34 			}
    30 			switch stat {
    35 			switch stat {
    31 			default:
    36 			default:
    32 				input = nil
    37 				input = nil
    33 			case StatusRunning:
    38 			case StatusRunning:
    34 				input = recvXmpp
    39 				input = recvXmpp
    36 		case x, ok := <-input:
    41 		case x, ok := <-input:
    37 			if !ok {
    42 			if !ok {
    38 				return
    43 				return
    39 			}
    44 			}
    40 			if x == nil {
    45 			if x == nil {
    41 				Info.Log("Refusing to send nil stanza")
    46 				if Debug {
       
    47 					log.Println("Won't send nil stanza")
       
    48 				}
    42 				continue
    49 				continue
    43 			}
    50 			}
    44 			sendXml <- x
    51 			sendXml <- x
    45 		}
    52 		}
    46 	}
    53 	}
    72 			}
    79 			}
    73 			switch obj := x.(type) {
    80 			switch obj := x.(type) {
    74 			case *stream:
    81 			case *stream:
    75 				// Do nothing.
    82 				// Do nothing.
    76 			case *streamError:
    83 			case *streamError:
    77 				cl.handleStreamError(obj)
    84 				cl.setError(fmt.Errorf("%#v", obj))
       
    85 				return
    78 			case *Features:
    86 			case *Features:
    79 				cl.handleFeatures(obj)
    87 				cl.handleFeatures(obj)
    80 			case *starttls:
    88 			case *starttls:
    81 				cl.handleTls(obj)
    89 				cl.handleTls(obj)
    82 			case *auth:
    90 			case *auth:
    90 				}
    98 				}
    91 				if doSend {
    99 				if doSend {
    92 					sendXmpp <- obj
   100 					sendXmpp <- obj
    93 				}
   101 				}
    94 			default:
   102 			default:
    95 				Warn.Logf("Unhandled non-stanza: %T %#v", x, x)
   103 				if Debug {
       
   104 					log.Printf("Unrecognized input: %T %#v",
       
   105 						x, x)
       
   106 				}
    96 			}
   107 			}
    97 		}
   108 		}
    98 	}
   109 	}
    99 }
       
   100 
       
   101 func (cl *Client) handleStreamError(se *streamError) {
       
   102 	Info.Logf("Received stream error: %v", se)
       
   103 	cl.setStatus(StatusShutdown)
       
   104 }
   110 }
   105 
   111 
   106 func (cl *Client) handleFeatures(fe *Features) {
   112 func (cl *Client) handleFeatures(fe *Features) {
   107 	cl.Features = fe
   113 	cl.Features = fe
   108 	if fe.Starttls != nil {
   114 	if fe.Starttls != nil {
   109 		start := &starttls{XMLName: xml.Name{Space: NsTLS,
   115 		start := &starttls{XMLName: xml.Name{Space: NsTLS,
   110 			Local: "starttls"}}
   116 			Local: "starttls"}}
   111 		cl.sendXml <- start
   117 		cl.sendRaw <- start
   112 		return
   118 		return
   113 	}
   119 	}
   114 
   120 
   115 	if len(fe.Mechanisms.Mechanism) > 0 {
   121 	if len(fe.Mechanisms.Mechanism) > 0 {
   116 		cl.chooseSasl(fe)
   122 		cl.chooseSasl(fe)
   128 
   134 
   129 	cl.setStatus(StatusConnectedTls)
   135 	cl.setStatus(StatusConnectedTls)
   130 
   136 
   131 	// Now re-send the initial handshake message to start the new
   137 	// Now re-send the initial handshake message to start the new
   132 	// session.
   138 	// session.
   133 	cl.sendXml <- &stream{To: cl.Jid.Domain, Version: XMPPVersion}
   139 	cl.sendRaw <- &stream{To: cl.Jid.Domain(), Version: XMPPVersion}
   134 }
   140 }
   135 
   141 
   136 // Send a request to bind a resource. RFC 3920, section 7.
   142 // Send a request to bind a resource. RFC 3920, section 7.
   137 func (cl *Client) bind() {
   143 func (cl *Client) bind() {
   138 	res := cl.Jid.Resource
   144 	res := cl.Jid.Resource()
   139 	bindReq := &bindIq{}
   145 	bindReq := &bindIq{}
   140 	if res != "" {
   146 	if res != "" {
   141 		bindReq.Resource = &res
   147 		bindReq.Resource = &res
   142 	}
   148 	}
   143 	msg := &Iq{Header: Header{Type: "set", Id: NextId(),
   149 	msg := &Iq{Header: Header{Type: "set", Id: NextId(),
   144 		Nested: []interface{}{bindReq}}}
   150 		Nested: []interface{}{bindReq}}}
   145 	f := func(st Stanza) {
   151 	f := func(st Stanza) {
   146 		iq, ok := st.(*Iq)
   152 		iq, ok := st.(*Iq)
   147 		if !ok {
   153 		if !ok {
   148 			Warn.Log("non-iq response")
   154 			cl.setError(fmt.Errorf("non-iq response to bind %#v",
       
   155 				st))
       
   156 			return
   149 		}
   157 		}
   150 		if iq.Type == "error" {
   158 		if iq.Type == "error" {
   151 			Warn.Log("Resource binding failed")
   159 			cl.setError(fmt.Errorf("Resource binding failed"))
       
   160 			return
   152 		}
   161 		}
   153 		var bindRepl *bindIq
   162 		var bindRepl *bindIq
   154 		for _, ele := range iq.Nested {
   163 		for _, ele := range iq.Nested {
   155 			if b, ok := ele.(*bindIq); ok {
   164 			if b, ok := ele.(*bindIq); ok {
   156 				bindRepl = b
   165 				bindRepl = b
   157 				break
   166 				break
   158 			}
   167 			}
   159 		}
   168 		}
   160 		if bindRepl == nil {
   169 		if bindRepl == nil {
   161 			Warn.Logf("Bad bind reply: %#v", iq)
   170 			cl.setError(fmt.Errorf("Bad bind reply: %#v", iq))
       
   171 			return
   162 		}
   172 		}
   163 		jidStr := bindRepl.Jid
   173 		jid := bindRepl.Jid
   164 		if jidStr == nil || *jidStr == "" {
   174 		if jid == nil || *jid == "" {
   165 			Warn.Log("Can't bind empty resource")
   175 			cl.setError(fmt.Errorf("empty resource in bind %#v",
       
   176 				iq))
       
   177 			return
   166 		}
   178 		}
   167 		jid := new(JID)
   179 		cl.Jid = JID(*jid)
   168 		if err := jid.Set(*jidStr); err != nil {
       
   169 			Warn.Logf("Can't parse JID %s: %s", *jidStr, err)
       
   170 		}
       
   171 		cl.Jid = *jid
       
   172 		Info.Logf("Bound resource: %s", cl.Jid.String())
       
   173 		cl.setStatus(StatusBound)
   180 		cl.setStatus(StatusBound)
   174 	}
   181 	}
   175 	cl.SetCallback(msg.Id, f)
   182 	cl.SetCallback(msg.Id, f)
   176 	cl.sendXml <- msg
   183 	cl.sendRaw <- msg
   177 }
   184 }
   178 
   185 
   179 // Register a callback to handle the next XMPP stanza (iq, message, or
   186 // Register a callback to handle the next XMPP stanza (iq, message, or
   180 // presence) with a given id. The provided function will not be called
   187 // presence) with a given id. The provided function will not be called
   181 // more than once. If it returns false, the stanza will not be made
   188 // more than once. If it returns false, the stanza will not be made