📄 xmpp_c2s.cxx
字号:
/* * xmpp_c2s.cxx * * Extensible Messaging and Presence Protocol (XMPP) Core * Client to Server communication classes * * Portable Windows Library * * Copyright (c) 2004 Reitek S.p.A. * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Portable Windows Library. * * The Initial Developer of the Original Code is Post Increment * * Contributor(s): ______________________________________. * * $Log: xmpp_c2s.cxx,v $ * Revision 1.7 2004/05/13 14:51:30 csoutheren * Fixed problems when comiling without SSL * * Revision 1.6 2004/05/09 07:23:50 rjongbloed * More work on XMPP, thanks Federico Pinna and Reitek S.p.A. * * Revision 1.5 2004/05/02 08:58:15 csoutheren * Removed warnings when compling without SASL * * Revision 1.4 2004/04/28 11:26:43 csoutheren * Hopefully fixed SASL and SASL2 problems * * Revision 1.3 2004/04/26 01:51:58 rjongbloed * More implementation of XMPP, thanks a lot to Federico Pinna & Reitek S.p.A. * * Revision 1.2 2004/04/23 06:07:25 csoutheren * Added #if P_SASL to allow operation without SASL * * Revision 1.1 2004/04/22 12:31:00 rjongbloed * Added PNotifier extensions and XMPP (Jabber) support, * thanks to Federico Pinna and Reitek S.p.A. * * */#ifdef __GNUC__#pragma implementation "xmpp_c2s.h"#endif#include <ptlib.h>#include <ptclib/xmpp_c2s.h>#if P_EXPAT#if P_DNS#include <ptclib/pdns.h>#endif// If DNS resolver is enabled, we look for a matching SRV recordXMPP::C2S::TCPTransport::TCPTransport(const PString& hostname) : m_Hostname(hostname), m_Port(5222){#if P_DNS PDNS::SRVRecordList srvRecords; if (PDNS::GetSRVRecords(PString("_xmpp-client._tcp.") + hostname, srvRecords)) { PDNS::SRVRecord * rec = srvRecords.GetFirst(); if (rec) { m_Hostname = rec->hostName; m_Port = rec->port; } }#endif}// A port was specified, so well connect exactly where we're toldXMPP::C2S::TCPTransport::TCPTransport(const PString& hostname, WORD port) : m_Hostname(hostname), m_Port(port){}XMPP::C2S::TCPTransport::~TCPTransport(){ Close();}BOOL XMPP::C2S::TCPTransport::Open(){ if (IsOpen()) Close(); PTCPSocket * s = new PTCPSocket(m_Hostname, m_Port); return PIndirectChannel::Open(s);}BOOL XMPP::C2S::TCPTransport::Close(){ return PIndirectChannel::Close();}///////////////////////////////////////////////////////XMPP::C2S::StreamHandler::StreamHandler(const JID& jid, const PString& pwd, BOOL newAccount) : m_VersionMajor(1), m_VersionMinor(0), m_NewAccount(newAccount), m_JID(jid), m_Password(pwd),#if P_SASL2 m_SASL("xmpp", BareJID(m_JID), m_JID.GetUser(), m_Password),#endif m_HasBind(FALSE), m_HasSession(FALSE), m_State(XMPP::C2S::StreamHandler::Null){ m_PendingIQs.DisallowDeleteObjects();}XMPP::C2S::StreamHandler::~StreamHandler(){ m_PendingIQsLock.Wait(); while (m_PendingIQs.GetSize() > 0) delete m_PendingIQs.RemoveAt(0); m_PendingIQsLock.Signal();}BOOL XMPP::C2S::StreamHandler::Start(Transport * transport){ if (!transport) transport = new XMPP::C2S::TCPTransport(m_JID.GetServer()); return BaseStreamHandler::Start(transport);}BOOL XMPP::C2S::StreamHandler::Send(XMPP::Stanza * stanza){ if (!stanza) return FALSE; if (PIsDescendant(stanza, XMPP::IQ)) { XMPP::IQ * iq = (XMPP::IQ *)stanza; if (iq->GetResponseHandlers().GetSize() > 0) { if (Write(*iq)) { m_PendingIQsLock.Wait(); m_PendingIQs.Append(iq); m_PendingIQsLock.Signal(); return TRUE; } else { delete iq; return FALSE; } } } BOOL res = Write(*stanza); delete stanza; return res;}void XMPP::C2S::StreamHandler::SetVersion(WORD major, WORD minor){ m_VersionMajor = major; m_VersionMinor = minor;}void XMPP::C2S::StreamHandler::GetVersion(WORD& major, WORD& minor) const{ major = m_VersionMajor; minor = m_VersionMinor;}void XMPP::C2S::StreamHandler::SetState(XMPP::C2S::StreamHandler::StreamState s){ if (s == XMPP::C2S::StreamHandler::Null && m_State == XMPP::C2S::StreamHandler::Established) OnSessionReleased(); else if (s == XMPP::C2S::StreamHandler::Established && m_State != XMPP::C2S::StreamHandler::Established) OnSessionEstablished(); m_State = s;}PNotifierList& XMPP::C2S::StreamHandler::IQNamespaceHandlers(const PString& xml_namespace){ if (!m_IQNamespaceHandlers.Contains(xml_namespace)) m_IQNamespaceHandlers.SetAt(xml_namespace, new PNotifierList); return m_IQNamespaceHandlers[xml_namespace];}PNotifierList& XMPP::C2S::StreamHandler::MessageSenderHandlers(const JID& from){ if (!m_MessageSenderHandlers.Contains(from)) m_MessageSenderHandlers.SetAt(from, new PNotifierList); return m_MessageSenderHandlers[from];}BOOL XMPP::C2S::StreamHandler::Discover(const PString& xmlns, const PString& jid, PNotifier * responseHandler, const PString& node){ if (!IsEstablished()) { PTRACE(1, "XMPP\tDisco: invalid stream state"); return FALSE; } else if (responseHandler == NULL) { PTRACE(1, "XMPP\tDisco: invalid response handler"); return FALSE; } PXMLElement * query = new PXMLElement(NULL, XMPP::IQQuery); query->SetAttribute(XMPP::Namespace, xmlns); if (!node.IsEmpty()) query->SetAttribute("node", node); XMPP::IQ * iq = new XMPP::IQ(XMPP::IQ::Get, query); iq->SetTo(jid); iq->GetResponseHandlers().Add(responseHandler); return Send(iq);}BOOL XMPP::C2S::StreamHandler::DiscoverItems(const PString& jid, PNotifier * responseHandler, const PString& node){ if (node.IsEmpty()) PTRACE(3, "XMPP\tDisco: discovering items for " << jid); else { PTRACE(3, "XMPP\tDisco: discovering items for " << jid << ", node " << node); } return Discover("http://jabber.org/protocol/disco#items", jid, responseHandler, node);}BOOL XMPP::C2S::StreamHandler::DiscoverInfo(const PString& jid, PNotifier * responseHandler, const PString& node){ if (node.IsEmpty()) PTRACE(3, "XMPP\tDisco: discovering info for " << jid); else { PTRACE(3, "XMPP\tDisco: discovering info for " << jid << ", node " << node); } return Discover("http://jabber.org/protocol/disco#info", jid, responseHandler, node);}void XMPP::C2S::StreamHandler::OnOpen(XMPP::Stream& stream, INT extra){ PString streamOn(PString::Printf, "<?xml version='1.0' encoding='UTF-8' ?>" "<stream:stream to='%s' xmlns='jabber:client' " "xmlns:stream='http://etherx.jabber.org/streams'", (const char *)m_JID.GetServer()); if (m_VersionMajor >= 1) streamOn.sprintf(" version='%d.%d'>", (int)m_VersionMajor, (int)m_VersionMinor); else // old jabber protocol streamOn += ">"; stream.Reset(); stream.Write(streamOn); /* Now read the server response: we need this as we must figure out whether the server support the new XMPP protocol or it is an old jabber server */ PString data; PString buf; PINDEX beg = P_MAX_INDEX, end = P_MAX_INDEX; while(beg == P_MAX_INDEX || end == P_MAX_INDEX) { if (!((PChannel&)stream).Read(buf.GetPointer(256), 255)) { // Error!!! stream.Close(); return; } data += buf; if (beg == P_MAX_INDEX) beg = data.Find("<stream:stream "); if (beg != P_MAX_INDEX) end = data.Find('>', beg); if (end != P_MAX_INDEX) { buf = data.Mid(beg, end - beg + 1); PINDEX v = buf.Find("version='"); if (v == P_MAX_INDEX) { m_VersionMajor = 0; m_VersionMinor = 9; } else { buf = buf.Mid(v + 9); int maj, min; if (sscanf(buf.GetPointer(), "%d.%d", &maj, &min) != 2) { m_VersionMajor = 0; m_VersionMinor = 9; } else { m_VersionMajor = maj < 1 ? (WORD)0 : (WORD)1; m_VersionMinor = maj < 1 ? (WORD)9 : (WORD)0; } } } } PXMLStreamParser * parser = stream.GetParser(); // Now we have to feed to the parser whatever we read so far if (parser == NULL || !parser->Parse(data, data.GetLength(), FALSE)) { // Error!!! stream.Close(); return; } PXMLElement * root = parser->GetXMLTree(); if (root != NULL) m_StreamID = root->GetAttribute("id"); BaseStreamHandler::OnOpen(stream, extra); if (m_VersionMajor == 0) StartAuthNegotiation();}void XMPP::C2S::StreamHandler::OnClose(XMPP::Stream& stream, INT extra){ SetState(XMPP::C2S::StreamHandler::Null); m_HasBind = FALSE; m_HasSession = FALSE; PString streamOff("</stream:stream>"); stream.Write(streamOff); BaseStreamHandler::OnClose(stream, extra);}void XMPP::C2S::StreamHandler::StartRegistration(){ PString reg(PString::Printf, "<iq type='set' to='%s' id='reg1'>" "<query xmlns='jabber:iq:register'>" "<username>%s</username>" "<password>%s</password>" "</query></iq>", (const char *)m_JID.GetServer(), (const char *)m_JID.GetUser(), (const char *)m_Password); m_Stream->Write(reg); SetState(XMPP::C2S::StreamHandler::RegStarted);}void XMPP::C2S::StreamHandler::StartAuthNegotiation(){ if (m_NewAccount) { StartRegistration(); return; }#if P_SASL2 // We have SASL, but we might have not found a mechanism in // common, or we are just supporting the old jabber protocol if (m_VersionMajor == 0 || m_Mechanism.IsEmpty())#endif { // JEP-0078 Non SASL authentication PString auth(PString::Printf, "<iq type='get' to='%s' id='auth1'>" "<query xmlns='jabber:iq:auth'>" "<username>%s</username>" "</query></iq>", (const char *)m_JID.GetServer(), (const char *)m_JID.GetUser()); m_Stream->Write(auth); SetState(XMPP::C2S::StreamHandler::NonSASLStarted); }#if P_SASL2 else { // Go with SASL! PString output; if (!m_SASL.Start(m_Mechanism, output)) { Stop(); return; } PString auth("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='"); auth += m_Mechanism; if (output.IsEmpty()) auth += "'/>"; else { auth += "'>"; auth += output; auth += "</auth>"; } m_Stream->Write(auth); SetState(XMPP::C2S::StreamHandler::SASLStarted); }#endif}void XMPP::C2S::StreamHandler::OnSessionEstablished(){ m_SessionEstablishedHandlers.Fire(*this);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -