xmpp.go
changeset 36 9fe022261dcc
parent 34 7b1f924c75e2
child 38 2839fece923e
equal deleted inserted replaced
35:569833f08780 36:9fe022261dcc
    81 // with the given password. This function will return as soon as a TCP
    81 // with the given password. This function will return as soon as a TCP
    82 // connection has been established, but before XMPP stream negotiation
    82 // connection has been established, but before XMPP stream negotiation
    83 // has completed. The negotiation will occur asynchronously, and any
    83 // has completed. The negotiation will occur asynchronously, and any
    84 // send operation to Client.Out will block until negotiation (resource
    84 // send operation to Client.Out will block until negotiation (resource
    85 // binding) is complete.
    85 // binding) is complete.
    86 func NewClient(jid *JID, password string) (*Client, os.Error) {
    86 func NewClient(jid *JID, password string,
       
    87 	extStanza map[string] func(*xml.Name) ExtendedStanza) (*Client, os.Error) {
    87 	// Resolve the domain in the JID.
    88 	// Resolve the domain in the JID.
    88 	_, srvs, err := net.LookupSRV(clientSrv, "tcp", jid.Domain)
    89 	_, srvs, err := net.LookupSRV(clientSrv, "tcp", jid.Domain)
    89 	if err != nil {
    90 	if err != nil {
    90 		return nil, os.NewError("LookupSrv " + jid.Domain +
    91 		return nil, os.NewError("LookupSrv " + jid.Domain +
    91 			": " + err.String())
    92 			": " + err.String())
   118 	cl.handlers = make(chan *stanzaHandler, 100)
   119 	cl.handlers = make(chan *stanzaHandler, 100)
   119 	cl.inputControl = make(chan int)
   120 	cl.inputControl = make(chan int)
   120 	idCh := make(chan string)
   121 	idCh := make(chan string)
   121 	cl.Id = idCh
   122 	cl.Id = idCh
   122 
   123 
       
   124 	if extStanza == nil {
       
   125 		extStanza = make(map[string] func(*xml.Name) ExtendedStanza)
       
   126 	}
       
   127 	extStanza[NsRoster] = rosterStanza
       
   128 
   123 	// Start the unique id generator.
   129 	// Start the unique id generator.
   124 	go makeIds(idCh)
   130 	go makeIds(idCh)
   125 
   131 
   126 	// Start the transport handler, initially unencrypted.
   132 	// Start the transport handler, initially unencrypted.
   127 	tlsr, tlsw := cl.startTransport()
   133 	tlsr, tlsw := cl.startTransport()
   128 
   134 
   129 	// Start the reader and writers that convert to and from XML.
   135 	// Start the reader and writers that convert to and from XML.
   130 	xmlIn := startXmlReader(tlsr)
   136 	xmlIn := startXmlReader(tlsr, extStanza)
   131 	cl.xmlOut = startXmlWriter(tlsw)
   137 	cl.xmlOut = startXmlWriter(tlsw)
   132 
   138 
   133 	// Start the XMPP stream handler which filters stream-level
   139 	// Start the XMPP stream handler which filters stream-level
   134 	// events and responds to them.
   140 	// events and responds to them.
   135 	clIn := cl.startStreamReader(xmlIn, cl.xmlOut)
   141 	clIn := cl.startStreamReader(xmlIn, cl.xmlOut)
   156 	go cl.readTransport(inw)
   162 	go cl.readTransport(inw)
   157 	go cl.writeTransport(outr)
   163 	go cl.writeTransport(outr)
   158 	return inr, outw
   164 	return inr, outw
   159 }
   165 }
   160 
   166 
   161 func startXmlReader(r io.Reader) <-chan interface{} {
   167 func startXmlReader(r io.Reader,
       
   168 	extStanza map[string] func(*xml.Name) ExtendedStanza) <-chan interface{} {
   162 	ch := make(chan interface{})
   169 	ch := make(chan interface{})
   163 	go readXml(r, ch)
   170 	go readXml(r, ch, extStanza)
   164 	return ch
   171 	return ch
   165 }
   172 }
   166 
   173 
   167 func startXmlWriter(w io.Writer) chan<- interface{} {
   174 func startXmlWriter(w io.Writer) chan<- interface{} {
   168 	ch := make(chan interface{})
   175 	ch := make(chan interface{})
   285 	if pr != nil {
   292 	if pr != nil {
   286 		cl.Out <- pr
   293 		cl.Out <- pr
   287 	}
   294 	}
   288 	return nil
   295 	return nil
   289 }
   296 }
   290 
       
   291 // Synchronously fetch this entity's roster from the server and cache
       
   292 // that information.
       
   293 func (cl *Client) fetchRoster() os.Error {
       
   294 	iq := &Iq{From: cl.Jid.String(), Id: <- cl.Id, Type: "get",
       
   295 		Query: &RosterQuery{XMLName: xml.Name{Local: "query",
       
   296 			Space: NsRoster}}}
       
   297 	ch := make(chan os.Error)
       
   298 	f := func(st Stanza) bool {
       
   299 		iq, ok := st.(*Iq)
       
   300 		if !ok {
       
   301 			ch <- os.NewError(fmt.Sprintf(
       
   302 				"Roster query result not iq: %v", st))
       
   303 			return false
       
   304 		}
       
   305 		if iq.Type == "error" {
       
   306 			ch <- iq.Error
       
   307 			return false
       
   308 		}
       
   309 		q := iq.Query
       
   310 		if q == nil {
       
   311 			ch <- os.NewError(fmt.Sprintf(
       
   312 				"Roster query result nil query: %v",
       
   313 				iq))
       
   314 			return false
       
   315 		}
       
   316 		cl.roster = make(map[string] *RosterItem, len(q.Item))
       
   317 		for _, item := range(q.Item) {
       
   318 			cl.roster[item.Jid] = &item
       
   319 		}
       
   320 		ch <- nil
       
   321 		return false
       
   322 	}
       
   323 	cl.HandleStanza(iq.Id, f)
       
   324 	cl.Out <- iq
       
   325 	// Wait for f to complete.
       
   326 	return <- ch
       
   327 }
       
   328 
       
   329 // BUG(cjyar) The roster isn't actually updated when things change.
       
   330 
       
   331 // Returns the current roster of other entities which this one has a
       
   332 // relationship with. Changes to the roster will be signaled by an
       
   333 // appropriate Iq appearing on Client.In. See RFC 3921, Section 7.4.
       
   334 func (cl *Client) Roster() map[string] *RosterItem {
       
   335 	r := make(map[string] *RosterItem)
       
   336 	for key, val := range(cl.roster) {
       
   337 		r[key] = val
       
   338 	}
       
   339 	return r
       
   340 }