Added support for SASL PLAIN, from "Ildar Hizbulin" <hizel@vyborg.ru>.
// Track the current status of the connection to the server.packagexmppimport("fmt")// Status of the connection.typeStatusintconst(statusUnconnected=iotastatusConnectedstatusConnectedTlsstatusAuthenticatedstatusBoundstatusRunningstatusShutdownstatusError)var(// The client has not yet connected, or it has been// disconnected from the server.StatusUnconnectedStatus=statusUnconnected// Initial connection established.StatusConnectedStatus=statusConnected// Like StatusConnected, but with TLS.StatusConnectedTlsStatus=statusConnectedTls// Authentication succeeded.StatusAuthenticatedStatus=statusAuthenticated// Resource binding complete.StatusBoundStatus=statusBound// Session has started and normal message traffic can be sent// and received.StatusRunningStatus=statusRunning// The session has closed, or is in the process of closing.StatusShutdownStatus=statusShutdown// The session has encountered an error. Otherwise identical// to StatusShutdown.StatusErrorStatus=statusError)// Does the status value indicate that the client is or has// disconnected?func(sStatus)Fatal()bool{switchs{default:returnfalsecaseStatusShutdown,StatusError:returntrue}}typestatmgrstruct{newStatuschanStatusnewlistenerchanchanStatus}funcnewStatmgr(clientchan<-Status)*statmgr{s:=statmgr{}s.newStatus=make(chanStatus)s.newlistener=make(chanchanStatus)gos.manager(client)return&s}func(s*statmgr)manager(clientchan<-Status){// We handle this specially, in case the client doesn't read// our final status message.deferfunc(){ifclient!=nil{select{caseclient<-StatusShutdown:default:}close(client)}}()stat:=StatusUnconnectedlisteners:=[]chanStatus{}for{select{casestat=<-s.newStatus:for_,l:=rangelisteners{sendToListener(l,stat)}ifclient!=nil&&stat!=StatusShutdown{client<-stat}casel,ok:=<-s.newlistener:if!ok{return}deferclose(l)sendToListener(l,stat)listeners=append(listeners,l)}}}funcsendToListener(listenchanStatus,statStatus){for{select{case<-listen:caselisten<-stat:return}}}func(cl*Client)setStatus(statStatus){cl.statmgr.setStatus(stat)}func(s*statmgr)setStatus(statStatus){s.newStatus<-stat}func(s*statmgr)newListener()<-chanStatus{l:=make(chanStatus,1)s.newlistener<-lreturnl}func(s*statmgr)close(){close(s.newlistener)}func(s*statmgr)awaitStatus(waitForStatus)error{// BUG(chris): This routine leaks one channel each time it's// called. Listeners are never removed.l:=s.newListener()forcurrent:=rangel{ifcurrent==waitFor{returnnil}ifcurrent.Fatal(){break}ifcurrent>waitFor{returnnil}}returnfmt.Errorf("shut down waiting for status change")}