xmpp.go
changeset 60 6d4f43f7dc19
parent 59 be6815a9653a
child 61 16513974d273
equal deleted inserted replaced
59:be6815a9653a 60:6d4f43f7dc19
    58 			str := fmt.Sprintf("id_%d", id)
    58 			str := fmt.Sprintf("id_%d", id)
    59 			ch <- str
    59 			ch <- str
    60 			id++
    60 			id++
    61 		}
    61 		}
    62 	}(idCh)
    62 	}(idCh)
       
    63 }
       
    64 
       
    65 // Extensions can add stanza filters and/or new XML element types.
       
    66 type Extension struct {
       
    67 	StanzaHandlers map[string] func(*xml.Name) interface{}
       
    68 	Start func(*Client)
    63 }
    69 }
    64 
    70 
    65 // The client in a client-server XMPP connection.
    71 // The client in a client-server XMPP connection.
    66 type Client struct {
    72 type Client struct {
    67 	// This client's unique ID. It's unique within the context of
    73 	// This client's unique ID. It's unique within the context of
    94 	filterOut chan<- <-chan Stanza
   100 	filterOut chan<- <-chan Stanza
    95 	filterIn <-chan <-chan Stanza
   101 	filterIn <-chan <-chan Stanza
    96 }
   102 }
    97 var _ io.Closer = &Client{}
   103 var _ io.Closer = &Client{}
    98 
   104 
    99 // BUG(cjyar): Replace extStanza with a generalized extension interface
       
   100 // that handles starting filters, registering extended stanzes, and
       
   101 // anything else an extension has to do.
       
   102 
       
   103 // Connect to the appropriate server and authenticate as the given JID
   105 // Connect to the appropriate server and authenticate as the given JID
   104 // with the given password. This function will return as soon as a TCP
   106 // with the given password. This function will return as soon as a TCP
   105 // connection has been established, but before XMPP stream negotiation
   107 // connection has been established, but before XMPP stream negotiation
   106 // has completed. The negotiation will occur asynchronously, and any
   108 // has completed. The negotiation will occur asynchronously, and any
   107 // send operation to Client.Out will block until negotiation (resource
   109 // send operation to Client.Out will block until negotiation (resource
   108 // binding) is complete.
   110 // binding) is complete.
   109 func NewClient(jid *JID, password string,
   111 func NewClient(jid *JID, password string, exts []Extension) (*Client,
   110 	extStanza map[string] func(*xml.Name) interface{}) (*Client, os.Error) {
   112 	os.Error) {
       
   113 	// Include the mandatory extensions.
       
   114 	exts = append(exts, rosterExt)
       
   115 	exts = append(exts, bindExt)
       
   116 
   111 	// Resolve the domain in the JID.
   117 	// Resolve the domain in the JID.
   112 	_, srvs, err := net.LookupSRV(clientSrv, "tcp", jid.Domain)
   118 	_, srvs, err := net.LookupSRV(clientSrv, "tcp", jid.Domain)
   113 	if err != nil {
   119 	if err != nil {
   114 		return nil, os.NewError("LookupSrv " + jid.Domain +
   120 		return nil, os.NewError("LookupSrv " + jid.Domain +
   115 			": " + err.String())
   121 			": " + err.String())
   141 	cl.Jid = *jid
   147 	cl.Jid = *jid
   142 	cl.socket = tcp
   148 	cl.socket = tcp
   143 	cl.handlers = make(chan *stanzaHandler, 100)
   149 	cl.handlers = make(chan *stanzaHandler, 100)
   144 	cl.inputControl = make(chan int)
   150 	cl.inputControl = make(chan int)
   145 
   151 
   146 	if extStanza == nil {
   152 	extStanza := make(map[string] func(*xml.Name) interface{})
   147 		extStanza = make(map[string] func(*xml.Name) interface{})
   153 	for _, ext := range(exts) {
   148 	}
   154 		for k, v := range(ext.StanzaHandlers) {
   149 	extStanza[NsRoster] = newRosterQuery
   155 			extStanza[k] = v
   150 	extStanza[NsBind] = newBind
   156 		}
       
   157 	}
   151 
   158 
   152 	// Start the transport handler, initially unencrypted.
   159 	// Start the transport handler, initially unencrypted.
   153 	tlsr, tlsw := cl.startTransport()
   160 	tlsr, tlsw := cl.startTransport()
   154 
   161 
   155 	// Start the reader and writers that convert to and from XML.
   162 	// Start the reader and writers that convert to and from XML.
   158 
   165 
   159 	// Start the XMPP stream handler which filters stream-level
   166 	// Start the XMPP stream handler which filters stream-level
   160 	// events and responds to them.
   167 	// events and responds to them.
   161 	stIn := cl.startStreamReader(xmlIn, cl.xmlOut)
   168 	stIn := cl.startStreamReader(xmlIn, cl.xmlOut)
   162 	clOut := cl.startStreamWriter(cl.xmlOut)
   169 	clOut := cl.startStreamWriter(cl.xmlOut)
       
   170 	cl.Out = clOut
   163 
   171 
   164 	// Start the manager for the filters that can modify what the
   172 	// Start the manager for the filters that can modify what the
   165 	// app sees.
   173 	// app sees.
   166 	clIn := cl.startFilter(stIn)
   174 	clIn := cl.startFilter(stIn)
   167 	startRosterFilter(cl)
   175 	cl.In = clIn
       
   176 
       
   177 	// Add filters for our extensions.
       
   178 	for _, ext := range(exts) {
       
   179 		ext.Start(cl)
       
   180 	}
   168 
   181 
   169 	// Initial handshake.
   182 	// Initial handshake.
   170 	hsOut := &stream{To: jid.Domain, Version: Version}
   183 	hsOut := &stream{To: jid.Domain, Version: Version}
   171 	cl.xmlOut <- hsOut
   184 	cl.xmlOut <- hsOut
   172 
       
   173 	cl.In = clIn
       
   174 	cl.Out = clOut
       
   175 
   185 
   176 	return cl, nil
   186 	return cl, nil
   177 }
   187 }
   178 
   188 
   179 func (c *Client) Close() os.Error {
   189 func (c *Client) Close() os.Error {