/// <summary> /// Handle the last set of stream:features we have received, /// based on the current state. /// </summary> protected virtual void ProcessFeatures() { // don't do starttls if we're already on an SSL socket. // bad server setup, but no skin off our teeth, we're already // SSL'd. Also, start-tls won't work when polling. if ((bool)this[Options.AUTO_TLS] && (m_features.StartTLS != null) && (!m_sslOn) && m_stanzas.SupportsTLS) { // start-tls lock (m_stateLock) { State = StartTLSState.Instance; } this.Write(new StartTLS(m_doc)); return; } Compression comp = m_features.Compression; if ((bool)this[Options.AUTO_COMPRESS] && (comp != null) && comp.HasMethod("zlib") && (!m_compressionOn) && m_stanzas.SupportsCompression) { // start compression lock (m_stateLock) { State = CompressionState.Instance; } Compress c = new Compress(m_doc); c.Method = "zlib"; this.Write(c); return; } // not authenticated yet. Note: we'll get a stream:features // after the last sasl restart, so we shouldn't try to iq:auth // at that point. if (!IsAuthenticated) { Mechanisms ms = m_features.Mechanisms; m_saslProc = null; MechanismType types = MechanismType.NONE; if (ms != null) { // if SASL_MECHANISMS is set in the options, it is the limited set // of mechanisms we're willing to try. Mask them off of the offered set. object smt = this[Options.SASL_MECHANISMS]; if (smt != null) types = (MechanismType)smt & ms.Types; else types = ms.Types; } // If we're doing SASL, and there are mechanisms implemented by both // client and server. if ((types != MechanismType.NONE) && ((bool)this[Options.SASL])) { lock (m_stateLock) { State = SASLState.Instance; } m_saslProc = SASLProcessor.createProcessor(types, m_sslOn || (bool)this[Options.PLAINTEXT], ms); if (m_saslProc == null) { FireOnError(new NotImplementedException("No implemented mechanisms in: " + types.ToString())); return; } if (OnSASLStart != null) OnSASLStart(this, m_saslProc); lock (m_stateLock) { // probably manual authentication if (State != SASLState.Instance) return; } try { Step s = m_saslProc.step(null, this.Document); if (s != null) this.Write(s); } catch (Exception e) { FireOnError(new SASLException(e.Message)); return; } } if (m_saslProc == null) { // no SASL mechanisms. Try iq:auth. if ((bool)this[Options.REQUIRE_SASL]) { FireOnError(new SASLException("No SASL mechanisms available")); return; } lock (m_stateLock) { State = NonSASLAuthState.Instance; } if (OnSASLStart != null) OnSASLStart(this, null); // HACK: old-style auth for jabberclient. } } }
/// <summary> /// Informs the client that an XML element was received and /// invokes the OnProtocol event. /// </summary> /// <param name="sender">The object that called this method.</param> /// <param name="tag">XML element that contains the new tag.</param> protected virtual void OnElement(object sender, System.Xml.XmlElement tag) { //Debug.WriteLine(tag.OuterXml); if (tag is Kixeye.Jabber.Protocol.Stream.Error) { // Stream error. Race condition! Two cases: // 1) OnClose has already fired, in which case we are in ClosedState, and the reconnect timer is pending. // 2) OnClose hasn't fired, in which case we trick it into not starting the reconnect timer. lock (m_stateLock) { if (m_state != ClosedState.Instance) { State = ClosingState.Instance; } else if (m_reconnectTimer != null) { Debug.WriteLine("Disposing of timer"); m_reconnectTimer.Dispose(); } } if (OnStreamError != null) { OnStreamError(this, tag); } return; } if (State == ServerFeaturesState.Instance) { Features f = tag as Features; if (f == null) { FireOnError(new InvalidOperationException("Expecting stream:features from a version='1.0' server")); return; } m_features = f; ProcessFeatures(); return; } else if (State == SASLState.Instance) { if (tag is Success) { // restart the stream again SendNewStreamHeader(); } else if (tag is SASLFailure) { m_saslProc = null; lock (m_stateLock) { State = SASLFailedState.Instance; } SASLFailure sf = tag as SASLFailure; // TODO: I18N if (OnSASLError != null) { m_reconnect = false; OnSASLError(this, sf); } else FireOnError(new SASLException("SASL failure: " + sf.InnerXml)); return; } else if (tag is Step) { try { Step s = m_saslProc.step(tag as Step, this.Document); if (s != null) Write(s); } catch (Exception e) { FireOnError(new SASLException(e.Message)); return; } } else { m_saslProc = null; FireOnError(new SASLException("Invalid SASL protocol")); return; } } else if (State == StartTLSState.Instance) { switch (tag.Name) { case "proceed": if (!StartTLS()) return; SendNewStreamHeader(); break; case "failure": FireOnError(new AuthenticationFailedException()); return; } } else if (State == CompressionState.Instance) { switch (tag.Name) { case "compressed": if (!StartCompression()) return; SendNewStreamHeader(); break; case "failure": CompressionFailure fail = tag as CompressionFailure; FireOnError(new Kixeye.Bedrock.IO.CompressionFailedException(fail.Error)); return; } } else if (State == SASLAuthedState.Instance) { Features f = tag as Features; if (f == null) { FireOnError(new InvalidOperationException("Expecting stream:features from a version='1.0' server")); return; } if (OnSASLEnd != null) OnSASLEnd(this, f); m_saslProc = null; } else { if (OnProtocol != null) { OnProtocol(this, tag); } } CheckAll(tag); }