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