xmpp.go
changeset 33 571713f49494
parent 32 4e68d8f89dc3
child 34 7b1f924c75e2
equal deleted inserted replaced
32:4e68d8f89dc3 33:571713f49494
    22 
    22 
    23 const (
    23 const (
    24 	// Version of RFC 3920 that we implement.
    24 	// Version of RFC 3920 that we implement.
    25 	Version = "1.0"
    25 	Version = "1.0"
    26 
    26 
       
    27 	// BUG(cjyar) These should be public.
    27 	// Various XML namespaces.
    28 	// Various XML namespaces.
    28 	nsStreams = "urn:ietf:params:xml:ns:xmpp-streams"
    29 	nsStreams = "urn:ietf:params:xml:ns:xmpp-streams"
    29 	nsStream = "http://etherx.jabber.org/streams"
    30 	nsStream = "http://etherx.jabber.org/streams"
    30 	nsTLS = "urn:ietf:params:xml:ns:xmpp-tls"
    31 	nsTLS = "urn:ietf:params:xml:ns:xmpp-tls"
    31 	nsSASL = "urn:ietf:params:xml:ns:xmpp-sasl"
    32 	nsSASL = "urn:ietf:params:xml:ns:xmpp-sasl"
    32 	nsBind = "urn:ietf:params:xml:ns:xmpp-bind"
    33 	nsBind = "urn:ietf:params:xml:ns:xmpp-bind"
    33 	nsSession = "urn:ietf:params:xml:ns:xmpp-session"
    34 	nsSession = "urn:ietf:params:xml:ns:xmpp-session"
       
    35 	NsRoster = "jabber:iq:roster"
    34 
    36 
    35 	// DNS SRV names
    37 	// DNS SRV names
    36 	serverSrv = "xmpp-server"
    38 	serverSrv = "xmpp-server"
    37 	clientSrv = "xmpp-client"
    39 	clientSrv = "xmpp-client"
    38 
    40 
    51 	password string
    53 	password string
    52 	socket net.Conn
    54 	socket net.Conn
    53 	socketSync sync.WaitGroup
    55 	socketSync sync.WaitGroup
    54 	saslExpected string
    56 	saslExpected string
    55 	authDone bool
    57 	authDone bool
    56 	idMutex sync.Mutex
       
    57 	nextId int64
       
    58 	handlers chan *stanzaHandler
    58 	handlers chan *stanzaHandler
    59 	inputControl chan int
    59 	inputControl chan int
    60 	// This channel may be used as a convenient way to generate a
    60 	// This channel may be used as a convenient way to generate a
    61 	// unique id for an iq, message, or presence stanza.
    61 	// unique id for an iq, message, or presence stanza.
    62 	Id <-chan string
    62 	Id <-chan string
    71 	// Features advertised by the remote. This will be updated
    71 	// Features advertised by the remote. This will be updated
    72 	// asynchronously as new features are received throughout the
    72 	// asynchronously as new features are received throughout the
    73 	// connection process. It should not be updated once
    73 	// connection process. It should not be updated once
    74 	// StartSession() returns.
    74 	// StartSession() returns.
    75 	Features *Features
    75 	Features *Features
       
    76 	roster map[string] *RosterItem
    76 }
    77 }
    77 var _ io.Closer = &Client{}
    78 var _ io.Closer = &Client{}
    78 
    79 
    79 // Connect to the appropriate server and authenticate as the given JID
    80 // Connect to the appropriate server and authenticate as the given JID
    80 // 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
   112 
   113 
   113 	cl := new(Client)
   114 	cl := new(Client)
   114 	cl.password = password
   115 	cl.password = password
   115 	cl.Jid = *jid
   116 	cl.Jid = *jid
   116 	cl.socket = tcp
   117 	cl.socket = tcp
   117 	cl.handlers = make(chan *stanzaHandler, 1)
   118 	cl.handlers = make(chan *stanzaHandler, 100)
   118 	cl.inputControl = make(chan int)
   119 	cl.inputControl = make(chan int)
   119 	idCh := make(chan string)
   120 	idCh := make(chan string)
   120 	cl.Id = idCh
   121 	cl.Id = idCh
   121 
   122 
   122 	// Start the unique id generator.
   123 	// Start the unique id generator.
   246 // traffic from the app.
   247 // traffic from the app.
   247 func (cl *Client) bindDone() {
   248 func (cl *Client) bindDone() {
   248 	cl.inputControl <- 1
   249 	cl.inputControl <- 1
   249 }
   250 }
   250 
   251 
   251 // Start an XMPP session. This should typically be done immediately
   252 // Start an XMPP session. A typical XMPP client should call this
   252 // after creating the new Client. Once the session has been
   253 // immediately after creating the Client in order to start the
   253 // established, pr will be sent as an initial presence; nil means
   254 // session, retrieve the roster, and broadcast an initial
   254 // don't send initial presence. The initial presence can be a
   255 // presence. The presence can be as simple as a newly-initialized
   255 // newly-initialized Presence struct. See RFC 3921, Section 3.
   256 // Presence struct.  See RFC 3921, Section 3.
   256 func (cl *Client) StartSession(pr *Presence) os.Error {
   257 func (cl *Client) StartSession(getRoster bool, pr *Presence) os.Error {
   257 	id := <- cl.Id
   258 	id := <- cl.Id
   258 	iq := &Iq{To: cl.Jid.Domain, Id: id, Type: "set", Any:
   259 	iq := &Iq{To: cl.Jid.Domain, Id: id, Type: "set", Any:
   259 		&Generic{XMLName: xml.Name{Space: nsSession, Local:
   260 		&Generic{XMLName: xml.Name{Space: nsSession, Local:
   260 				"session"}}}
   261 				"session"}}}
   261 	ch := make(chan os.Error)
   262 	ch := make(chan os.Error)
   263 		if st.XType() == "error" {
   264 		if st.XType() == "error" {
   264 			log.Printf("Can't start session: %v", st)
   265 			log.Printf("Can't start session: %v", st)
   265 			ch <- st.XError()
   266 			ch <- st.XError()
   266 			return false
   267 			return false
   267 		}
   268 		}
   268 		if pr != nil {
       
   269 			cl.Out <- pr
       
   270 		}
       
   271 		ch <- nil
   269 		ch <- nil
   272 		return false
   270 		return false
   273 	}
   271 	}
   274 	cl.HandleStanza(id, f)
   272 	cl.HandleStanza(id, f)
   275 	cl.Out <- iq
   273 	cl.Out <- iq
       
   274 
   276 	// Now wait until the callback is called.
   275 	// Now wait until the callback is called.
   277 	return <-ch
   276 	if err := <- ch ; err != nil {
   278 }
   277 		return err
       
   278 	}
       
   279 	if getRoster {
       
   280 		err := cl.fetchRoster()
       
   281 		if err != nil {
       
   282 			return err
       
   283 		}
       
   284 	}
       
   285 	if pr != nil {
       
   286 		cl.Out <- pr
       
   287 	}
       
   288 	return nil
       
   289 }
       
   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 }