[Opalvoip-svn] SF.net SVN: opalvoip:[31336] opal/trunk
Brought to you by:
csoutheren,
rjongbloed
From: <rjo...@us...> - 2014-01-31 22:15:05
|
Revision: 31336 http://sourceforge.net/p/opalvoip/code/31336 Author: rjongbloed Date: 2014-01-31 22:15:00 +0000 (Fri, 31 Jan 2014) Log Message: ----------- Support multiple skinny devices Modified Paths: -------------- opal/trunk/include/ep/skinnyep.h opal/trunk/include/opal/console_mgr.h opal/trunk/samples/server/main.cxx opal/trunk/samples/server/status.cxx opal/trunk/src/ep/skinnyep.cxx opal/trunk/src/opal/console_mgr.cxx opal/trunk/src/opal/opal_c.cxx Modified: opal/trunk/include/ep/skinnyep.h =================================================================== --- opal/trunk/include/ep/skinnyep.h 2014-01-31 11:25:02 UTC (rev 31335) +++ opal/trunk/include/ep/skinnyep.h 2014-01-31 22:15:00 UTC (rev 31336) @@ -49,31 +49,31 @@ { PCLASSINFO(OpalSkinnyEndPoint, OpalRTPEndPoint); public: - /**@name Construction */ - //@{ + /**@name Construction */ + //@{ /**Create a new endpoint. */ OpalSkinnyEndPoint( OpalManager & manager, const char *prefix = "sccp" - ); + ); /**Destroy endpoint. */ virtual ~OpalSkinnyEndPoint(); - //@} + //@} - /**@name Overrides from OpalEndPoint */ - //@{ + /**@name Overrides from OpalEndPoint */ + //@{ /** Shut down the endpoint, this is called by the OpalManager just before destroying the object and can be handy to make sure some things are stopped before the vtable gets clobbered. - */ + */ virtual void ShutDown(); /** Get the default transports for the endpoint type. Overrides the default behaviour to return udp and tcp. - */ + */ virtual PString GetDefaultTransport() const; /** Get the default signal port for this endpoint. @@ -86,7 +86,7 @@ Note that a specific connection may not actually support all of the media formats returned here, but should return no more. - */ + */ virtual OpalMediaFormatList GetMediaFormats() const; /** Handle new incoming connection from listener. @@ -94,7 +94,7 @@ virtual void NewIncomingConnection( OpalListener & listener, ///< Listner that created transport const OpalTransportPtr & transport ///< Transport connection came in on - ); + ); /** Set up a connection to a remote party. This is called from the OpalManager::MakeConnection() function once @@ -122,52 +122,98 @@ then it may return immediately. Returning a non-NULL value does not mean that the connection will succeed, only that an attempt is being made. - */ + */ virtual PSafePtr<OpalConnection> MakeConnection( OpalCall & call, ///< Owner of connection const PString & party, ///< Remote party to call void * userData, ///< Arbitrary data to pass to connection unsigned int options, ///< options to pass to conneciton OpalConnection::StringOptions * stringOptions ///< complex string options - ); + ); /** Execute garbage collection for endpoint. Returns true if all garbage has been collected. - */ + */ virtual PBoolean GarbageCollection(); - //@} + //@} - /**@name Customisation call backs */ - //@{ + class PhoneDevice; + + /**@name Customisation call backs */ + //@{ /** Create a connection for the skinny endpoint. */ virtual OpalSkinnyConnection * CreateConnection( OpalCall & call, ///< Owner of connection - const PString & token, ///< Token to identify call + PhoneDevice & client, ///< Registered client for call + unsigned callIdentifier, ///< Identifier for call const PString & dialNumber, ///< Number to dial out, empty if incoming void * userData, ///< Arbitrary data to pass to connection unsigned int options, OpalConnection::StringOptions * stringOptions = NULL - ); - //@} + ); + //@} - /**@name Protocol handling routines */ - //@{ + /**@name Protocol handling routines */ + //@{ + enum { DefaultDeviceType = 30016 }; // Cisco IP Communicator + /** Register with skinny server. */ bool Register( const PString & server, ///< Server to register with - unsigned maxStreams = 1, ///< Max "lines" for client - unsigned deviceType = 30016 ///< Device type code (Cisco IP Communicator) + const PString & name, ///< Name of cient "psuedo device" to register + unsigned deviceType = DefaultDeviceType ///< Device type code ); /** Unregister from server. */ - void Unregister(); + bool Unregister( + const PString & name ///< Name of client "psuedo device" to unregister + ); - const PString & GetRegistrationStatus() const { return m_registrationStatus; } + PhoneDevice * GetPhoneDevice( + const PString & name ///< Name of client "psuedo device" to unregister + ) const { return m_phoneDevices.GetAt(name); } + PArray<PString> GetPhoneDeviceNames() const { return m_phoneDevices.GetKeys(); } + //@} + + + class SkinnyMsg; + + class PhoneDevice : public PObject + { + PCLASSINFO(PhoneDevice, PObject); + public: + PhoneDevice(OpalSkinnyEndPoint & ep, const PString & name, unsigned deviceType); + + virtual void PrintOn(ostream & strm) const; + + bool Start(const PString & server); + void Stop(); + + bool SendSkinnyMsg(const SkinnyMsg & msg); + + const PString & GetName() const { return m_name; } + const PString & GetStatus() const { return m_status; } + + protected: + void HandleTransport(); + bool SendRegisterMsg(); + + OpalSkinnyEndPoint & m_endpoint; + PString m_name; + unsigned m_deviceType; + OpalTransportTCP m_transport; + PString m_status; + + friend class OpalSkinnyEndPoint; + friend class OpalSkinnyConnection; + }; + + #pragma pack(1) class SkinnyMsg { @@ -176,9 +222,18 @@ void Construct(const PBYTEArray & pdu); public: - PINDEX GetLength() const { return m_length+8; } - void SetLength(PINDEX len) { PAssert(len>= 8, PInvalidParameter); m_length = len-8; } - uint32_t GetID() const { return m_messageId; } + PINDEX GetLength() const + { + return m_length + 8; + } + void SetLength(PINDEX len) + { + PAssert(len >= 8, PInvalidParameter); m_length = len - 8; + } + uint32_t GetID() const + { + return m_messageId; + } private: PUInt32l m_length; @@ -186,16 +241,16 @@ PUInt32l m_messageId; }; - #define OPAL_SKINNY_MSG(cls, id, vars) \ - class cls : public SkinnyMsg \ - { \ - public: \ - enum { ID = id }; \ - cls() : SkinnyMsg(ID, sizeof(*this)) { } \ - cls(const PBYTEArray & pdu) : SkinnyMsg(ID, sizeof(*this)) { Construct(pdu); } \ - vars \ - }; \ - virtual bool OnReceiveMsg(const cls & msg) +#define OPAL_SKINNY_MSG(cls, id, vars) \ + class cls : public SkinnyMsg \ + { \ + public: \ + enum { ID = id }; \ + cls() : SkinnyMsg(ID, sizeof(*this)) { } \ + cls(const PBYTEArray & pdu) : SkinnyMsg(ID, sizeof(*this)) { Construct(pdu); } \ + vars \ + }; \ + virtual bool OnReceiveMsg(PhoneDevice & client, const cls & msg) OPAL_SKINNY_MSG(KeepAliveMsg, 0x0000, PUInt32l m_unknown; @@ -205,7 +260,8 @@ ); OPAL_SKINNY_MSG(RegisterMsg, 0x0001, - char m_deviceName[16]; + enum { MaxNameSize = 15 }; + char m_deviceName[MaxNameSize+1]; PUInt32l m_userId; PUInt32l m_instance; in_addr m_ip; @@ -213,7 +269,7 @@ PUInt32l m_maxStreams; BYTE m_unknown[16]; char m_macAddress[12]; - ); + ); OPAL_SKINNY_MSG(RegisterAckMsg, 0x0081, PUInt32l m_keepAlive; @@ -444,23 +500,18 @@ /** Send a message to server. */ - bool SendSkinnyMsg(const SkinnyMsg & msg); //@} - const OpalTransport & GetServerTransport() const { return m_serverTransport; } - protected: - void HandleServerTransport(); - PSafePtr<OpalSkinnyConnection> GetSkinnyConnection(uint32_t callIdentifier, PSafetyMode mode = PSafeReadWrite); - template <class MSG> bool DelegateMsg(const MSG & msg); + PSafePtr<OpalSkinnyConnection> GetSkinnyConnection(const PhoneDevice & client, uint32_t callIdentifier, PSafetyMode mode = PSafeReadWrite); + template <class MSG> bool DelegateMsg(const PhoneDevice & client, const MSG & msg); - OpalTransportTCP m_serverTransport; - RegisterMsg m_registerMsg; - PString m_registrationStatus; + typedef PDictionary<PString, PhoneDevice> PhoneDeviceDict; + PhoneDeviceDict m_phoneDevices; }; -/**Connection for interfacing Cisco SCCP (Skinny Client Control Protocol). +/**Connection for interfacing Cisco SCCP (Skinny PhoneDevice Control Protocol). */ class OpalSkinnyConnection : public OpalRTPConnection { @@ -473,7 +524,8 @@ OpalSkinnyConnection( OpalCall & call, OpalSkinnyEndPoint & ep, - const PString & token, + OpalSkinnyEndPoint::PhoneDevice & client, + unsigned callIdentifier, const PString & dialNumber, void * /*userData*/, unsigned options, @@ -568,6 +620,7 @@ void DelayCloseMediaStream(OpalMediaStreamPtr mediaStream); OpalSkinnyEndPoint & m_endpoint; + OpalSkinnyEndPoint::PhoneDevice & m_client; uint32_t m_lineInstance; uint32_t m_callIdentifier; @@ -580,9 +633,9 @@ }; -template <class MSG> bool OpalSkinnyEndPoint::DelegateMsg(const MSG & msg) +template <class MSG> bool OpalSkinnyEndPoint::DelegateMsg(const PhoneDevice & client, const MSG & msg) { - PSafePtr<OpalSkinnyConnection> connection = GetSkinnyConnection(msg.m_callIdentifier); + PSafePtr<OpalSkinnyConnection> connection = GetSkinnyConnection(client, msg.m_callIdentifier); return connection == NULL || connection->OnReceiveMsg(msg); } Modified: opal/trunk/include/opal/console_mgr.h =================================================================== --- opal/trunk/include/opal/console_mgr.h 2014-01-31 11:25:02 UTC (rev 31335) +++ opal/trunk/include/opal/console_mgr.h 2014-01-31 22:15:00 UTC (rev 31336) @@ -178,7 +178,8 @@ virtual bool Initialise(PArgList & args, bool verbose, const PString & defaultRoute); #if P_CLI - PDECLARE_NOTIFIER(PCLI::Arguments, OpalConsoleSkinnyEndPoint, CmdServer); + PDECLARE_NOTIFIER(PCLI::Arguments, OpalConsoleSkinnyEndPoint, CmdRegister); + PDECLARE_NOTIFIER(PCLI::Arguments, OpalConsoleSkinnyEndPoint, CmdStatus); virtual void AddCommands(PCLI & cli); #endif // P_CLI }; Modified: opal/trunk/samples/server/main.cxx =================================================================== --- opal/trunk/samples/server/main.cxx 2014-01-31 11:25:02 UTC (rev 31335) +++ opal/trunk/samples/server/main.cxx 2014-01-31 22:15:00 UTC (rev 31336) @@ -73,7 +73,7 @@ #if OPAL_SKINNY static const char SkinnyServerKey[] = "SCCP Server"; -static const char SkinnyStreamsKey[] = "SCCP Max Streams"; +static const char SkinnyNamesKey[] = "SCCP Device Names"; #endif #if OPAL_LID @@ -547,10 +547,33 @@ #if OPAL_SKINNY { + OpalSkinnyEndPoint * ep = FindEndPointAs<OpalSkinnyEndPoint>(OPAL_PREFIX_SKINNY); PString server = rsrc->AddStringField(SkinnyServerKey, 20, PString::Empty(), "Server for Skinny Client Control Protocol"); - unsigned maxStreams = rsrc->AddIntegerField(SkinnyStreamsKey, 1, 1000, 100, "", "Max Streams for Skinny Client Control Protocol"); - if (!server.IsEmpty() && !FindEndPointAs<OpalSkinnyEndPoint>(OPAL_PREFIX_SKINNY)->Register(server, maxStreams)) { - PSYSTEMLOG(Error, "Could not register with skinny server."); + PStringArray names = ep->GetPhoneDeviceNames(); + names = rsrc->AddStringArrayField(SkinnyNamesKey, false, 15, names, "Max Streams for Skinny Client Control Protocol"); + if (!server.IsEmpty()) { + for (PINDEX i = 0; i < names.GetSize(); ++i) { + PString name = names[i]; + + static PRegularExpression const Wildcards("\\[([0-9]+)-([0-9]+)\\]", PRegularExpression::Extended); + PIntArray starts(3), ends(3); + if (Wildcards.Execute(name, starts, ends)) { + unsigned number = name(starts[1], ends[1]-1).AsUnsigned(); + unsigned lastNumber = name(starts[2], ends[2]-1).AsUnsigned(); + unsigned digits = ends[2] - starts[2]; + while (number <= lastNumber) { + PString calculatedName = name.Left(starts[0]) + psprintf("%0*u", digits, number++) + name.Mid(ends[0]); + if (!ep->Register(server, calculatedName)) { + PSYSTEMLOG(Error, "Could not register " << calculatedName << " with skinny server \"" << server << '"'); + } + } + } + else { + if (!ep->Register(server, name)) { + PSYSTEMLOG(Error, "Could not register " << name << " with skinny server \"" << server << '"'); + } + } + } } } #endif Modified: opal/trunk/samples/server/status.cxx =================================================================== --- opal/trunk/samples/server/status.cxx 2014-01-31 11:25:02 UTC (rev 31335) +++ opal/trunk/samples/server/status.cxx 2014-01-31 22:15:00 UTC (rev 31336) @@ -198,13 +198,15 @@ #endif // OPAL_SIP #if OPAL_SKINNY + << "<!--#macrostart SkinnyRegistrationStatus-->" << PHTML::TableRow() << PHTML::TableHeader(PHTML::NoWrap) - << " SCCP Call Manager " + << " Skinny Client " << PHTML::TableData(PHTML::NoWrap) - << "<!--#macro SkinnyServerAddress-->" + << "<!--#status Name-->" << PHTML::TableData(PHTML::NoWrap) - << "<!--#macro SkinnyRegistrationStatus-->" + << "<!--#status Status-->" + << "<!--#macroend SkinnyRegistrationStatus-->" #endif // OPAL_SKINNY << PHTML::TableEnd(); } @@ -280,28 +282,43 @@ #endif // OPAL_SIP #if OPAL_SKINNY -PCREATE_SERVICE_MACRO(SkinnyServerAddress, resource, P_EMPTY) +PCREATE_SERVICE_MACRO_BLOCK(SkinnyRegistrationStatus, resource, P_EMPTY, htmlBlock) { RegistrationStatusPage * status = dynamic_cast<RegistrationStatusPage *>(resource.m_resource); if (PAssertNULL(status) == NULL) return PString::Empty(); + PString substitution; + OpalSkinnyEndPoint * ep = status->m_manager.FindEndPointAs<OpalSkinnyEndPoint>(OPAL_PREFIX_SKINNY); - if (ep == NULL) - return " "; - - return ep->GetServerTransport().GetRemoteAddress().GetHostName(true); -} + if (ep != NULL) { + PStringArray names = ep->GetPhoneDeviceNames(); + for (PINDEX i = 0; i < names.GetSize(); ++i) { + OpalSkinnyEndPoint::PhoneDevice * phoneDevice = ep->GetPhoneDevice(names[i]); + if (phoneDevice != NULL) { + // make a copy of the repeating html chunk + PString insert = htmlBlock; + PStringStream str; + str << *phoneDevice; + PString name, status; + str.Split('-', name, status); + PServiceHTML::SpliceMacro(insert, "status Name", name); + PServiceHTML::SpliceMacro(insert, "status Status", status); -PCREATE_SERVICE_MACRO(SkinnyRegistrationStatus, resource, P_EMPTY) -{ - RegistrationStatusPage * status = dynamic_cast<RegistrationStatusPage *>(resource.m_resource); - if (PAssertNULL(status) == NULL) - return PString::Empty(); + // Then put it into the page, moving insertion point along after it. + substitution += insert; + } + } + } - OpalSkinnyEndPoint * ep = status->m_manager.FindEndPointAs<OpalSkinnyEndPoint>(OPAL_PREFIX_SKINNY); - return ep != NULL ? ep->GetRegistrationStatus() : "Not registered"; + if (substitution.IsEmpty()) { + substitution = htmlBlock; + PServiceHTML::SpliceMacro(substitution, "status Name", " "); + PServiceHTML::SpliceMacro(substitution, "status Status", "Nothing registered"); + } + + return substitution; } #endif // OPAL_SKINNY Modified: opal/trunk/src/ep/skinnyep.cxx =================================================================== --- opal/trunk/src/ep/skinnyep.cxx 2014-01-31 11:25:02 UTC (rev 31335) +++ opal/trunk/src/ep/skinnyep.cxx 2014-01-31 22:15:00 UTC (rev 31336) @@ -37,13 +37,15 @@ #define PTraceModule() "Skinny" -static PString CreateToken(unsigned callIdentifier) +static PString CreateToken(const OpalSkinnyEndPoint::PhoneDevice & client, unsigned callIdentifier) { - return psprintf("Skinny/%u", callIdentifier); + PString token = "Skinny:" + client.GetName(); + if (callIdentifier > 0) + token.sprintf("/%u", callIdentifier); + + return token; } -static PConstString const OutgoingConnectionToken("SkinnyOut"); - static POrdinalToString::Initialiser const CodecCodes[] = { { 2, OPAL_G711_ALAW_64K }, { 4, OPAL_G711_ULAW_64K }, @@ -93,10 +95,7 @@ OpalSkinnyEndPoint::OpalSkinnyEndPoint(OpalManager & manager, const char *prefix) : OpalRTPEndPoint(manager, prefix, CanTerminateCall | SupportsE164) - , P_DISABLE_MSVC_WARNINGS(4355, m_serverTransport(*this)) - , m_registrationStatus("Not registered") { - m_serverTransport.SetPDULengthFormat(-4, 4); } @@ -108,7 +107,9 @@ void OpalSkinnyEndPoint::ShutDown() { PTRACE(3, prefixName << " endpoint shutting down."); - Unregister(); + + for (PhoneDeviceDict::iterator it = m_phoneDevices.begin(); it != m_phoneDevices.end(); ++it) + it->second.Stop(); } @@ -144,23 +145,38 @@ unsigned int options, OpalConnection::StringOptions * stringOptions) { - if (!connectionsActive.Contains(OutgoingConnectionToken)) { - PTRACE(2, "Can only dial out one call at a time."); - return NULL; - } - - PString number; + PString numberName; if (party.NumCompare(GetPrefixName() + ':') == EqualTo) - number = party.Mid(GetPrefixName().GetLength() + 1); + numberName = party.Mid(GetPrefixName().GetLength() + 1); else - number = party; + numberName = party; + PString number, name; + if (!numberName.Split('@', number, name)) + number = numberName; + if (!OpalIsE164(number)) { PTRACE(2, "Remote party \"" << number << "\" is not an E.164 number."); return NULL; } - return AddConnection(CreateConnection(call, OutgoingConnectionToken, number, userData, options, stringOptions)); + PhoneDevice * client; + if (name.IsEmpty()) { + if (m_phoneDevices.IsEmpty()) { + PTRACE(2, "Cannot call, nothing registered."); + return NULL; + } + client = &m_phoneDevices.begin()->second; + } + else { + client = m_phoneDevices.GetAt(name); + if (client == NULL) { + PTRACE(2, "Local client \"" << name << "\" does not exist."); + return NULL; + } + } + + return AddConnection(CreateConnection(call, *client, 0, number, userData, options, stringOptions)); } @@ -171,81 +187,150 @@ OpalSkinnyConnection * OpalSkinnyEndPoint::CreateConnection(OpalCall & call, - const PString & token, + OpalSkinnyEndPoint::PhoneDevice & client, + unsigned callIdentifier, const PString & dialNumber, void * userData, unsigned int options, OpalConnection::StringOptions * stringOptions) { - return new OpalSkinnyConnection(call, *this, token, dialNumber, userData, options, stringOptions); + return new OpalSkinnyConnection(call, *this, client, callIdentifier, dialNumber, userData, options, stringOptions); } -bool OpalSkinnyEndPoint::Register(const PString & server, unsigned maxStreams, unsigned deviceType) +bool OpalSkinnyEndPoint::Register(const PString & server, const PString & name, unsigned deviceType) { - Unregister(); + if (name.IsEmpty() || name.GetLength() > RegisterMsg::MaxNameSize) { + PTRACE(2, "Illegal client device anme \"" << name << '"'); + return false; + } - OpalTransportAddress addr(server, GetDefaultSignalPort(), GetDefaultTransport()); - if (!m_serverTransport.ConnectTo(addr)) { - m_registrationStatus = "Transport error: " + m_serverTransport.GetErrorText(); + if (m_phoneDevices.Contains(name)) { + PTRACE(3, "PhoneDevice \"" << name << "\" already registered"); return false; } - m_serverTransport.AttachThread(new PThreadObj<OpalSkinnyEndPoint>(*this, &OpalSkinnyEndPoint::HandleServerTransport, false, "Skinny")); + PhoneDevice * phoneDevice = new PhoneDevice(*this, name, deviceType); + if (phoneDevice->Start(server)) { + m_phoneDevices.SetAt(name, phoneDevice); + return true; + } + delete phoneDevice; + return false; +} + + +bool OpalSkinnyEndPoint::Unregister(const PString & name) +{ + PhoneDevice * phoneDevice = m_phoneDevices.GetAt(name); + if (phoneDevice == NULL) { + PTRACE(3, "PhoneDevice \"" << name << "\" not registered"); + return false; + } + + phoneDevice->Stop(); + m_phoneDevices.RemoveAt(name); + return true; +} + + +OpalSkinnyEndPoint::PhoneDevice::PhoneDevice(OpalSkinnyEndPoint & ep, const PString & name, unsigned deviceType) + : m_endpoint(ep) + , m_name(name) + , m_deviceType(deviceType) + , m_transport(ep) +{ + m_transport.SetPDULengthFormat(-4, 4); +} + + +void OpalSkinnyEndPoint::PhoneDevice::PrintOn(ostream & strm) const +{ + strm << m_name << '@' << m_transport.GetRemoteAddress().GetHostName() << " - " << m_status; +} + + +bool OpalSkinnyEndPoint::PhoneDevice::Start(const PString & server) +{ + OpalTransportAddress addr(server, m_endpoint.GetDefaultSignalPort(), m_endpoint.GetDefaultTransport()); + if (!m_transport.SetRemoteAddress(addr)) { + m_status = "Transport error: " + m_transport.GetErrorText(); + return false; + } + + m_transport.AttachThread(new PThreadObj<PhoneDevice>(*this, &PhoneDevice::HandleTransport, false, "Skinny")); + return SendRegisterMsg(); +} + + +bool OpalSkinnyEndPoint::PhoneDevice::SendRegisterMsg() +{ + RegisterMsg msg; + PIPSocket::Address ip; - m_serverTransport.GetLocalAddress().GetIpAddress(ip); + m_transport.GetLocalAddress().GetIpAddress(ip); - PString macAddress = PIPSocket::GetInterfaceMACAddress().ToUpper(); - PString deviceName = "SEP" + macAddress; - strncpy(m_registerMsg.m_deviceName, deviceName, sizeof(m_registerMsg.m_deviceName) - 1); - strncpy(m_registerMsg.m_macAddress, macAddress, sizeof(m_registerMsg.m_macAddress)); - m_registerMsg.m_ip = ip; - m_registerMsg.m_maxStreams = maxStreams; - m_registerMsg.m_deviceType = deviceType; + strncpy(msg.m_deviceName, m_name, sizeof(msg.m_deviceName) - 1); + strncpy(msg.m_macAddress, PIPSocket::GetInterfaceMACAddress().ToUpper(), sizeof(msg.m_macAddress)); + msg.m_ip = ip; + msg.m_maxStreams = 2; + msg.m_deviceType = m_deviceType; - if (!SendSkinnyMsg(m_registerMsg)) + if (!SendSkinnyMsg(msg)) return false; - PTRACE(4, "Registering client: " << deviceName << ", type=" << deviceType); + PTRACE(4, "Sent registermessage for " << m_name << ", type=" << m_deviceType); return true; } -void OpalSkinnyEndPoint::Unregister() +void OpalSkinnyEndPoint::PhoneDevice::Stop() { - m_registerMsg.m_deviceName[0] = '\0'; + m_name.MakeEmpty(); - if (!m_serverTransport.IsOpen()) + if (!m_transport.IsOpen()) return; - PTRACE(4, "Unregistering from " << m_serverTransport.GetRemoteAddress().GetHostName()); - SendSkinnyMsg(UnregisterMsg()); + PTRACE(4, "Unregistering from " << m_transport.GetRemoteAddress().GetHostName()); + if (!SendSkinnyMsg(UnregisterMsg())) + return; // Wait a bit for ack reply. for (PINDEX wait = 0; wait < 10; ++wait) { - if (!m_serverTransport.IsOpen()) + if (!m_transport.IsOpen()) break; PThread::Sleep(200); } - m_serverTransport.CloseWait(); + m_transport.CloseWait(); } +bool OpalSkinnyEndPoint::PhoneDevice::SendSkinnyMsg(const SkinnyMsg & msg) +{ + if (m_transport.Write(&msg, msg.GetLength())) + return true; + + PTRACE(3, "Error writing message to " << m_transport.GetRemoteAddress().GetHostName()); + return false; +} + + #define ON_RECEIVE_MSG(cls) \ case cls::ID : \ PTRACE(4, "Received " << typeid(cls).name()); \ - ok = OnReceiveMsg(cls(pdu)); \ + ok = m_endpoint.OnReceiveMsg(*this, cls(pdu)); \ break -void OpalSkinnyEndPoint::HandleServerTransport() +void OpalSkinnyEndPoint::PhoneDevice::HandleTransport() { PTRACE(4, "Started client handler thread"); - while (m_serverTransport.IsOpen()) { + + while (!m_name.IsEmpty()) { PBYTEArray pdu; - if (m_serverTransport.ReadPDU(pdu)) { + if (m_transport.ReadPDU(pdu)) { bool ok; unsigned msgId = pdu.GetAs<PUInt32l>(4); switch (msgId) { @@ -280,20 +365,21 @@ break; } if (!ok) - m_serverTransport.Close(); + m_transport.Close(); } else { - switch (m_serverTransport.GetErrorCode(PChannel::LastReadError)) { - case PChannel::NoError : - PTRACE(3, "Lost transport to " << m_serverTransport); + switch (m_transport.GetErrorCode(PChannel::LastReadError)) { + case PChannel::NoError: + PTRACE(3, "Lost transport to " << m_transport); + case PChannel::NotOpen: // Remote close of TCP for (;;) { - m_serverTransport.Close(); - if (m_registerMsg.m_deviceName[0] == '\0') + m_transport.Close(); + if (m_name.IsEmpty()) break; - if (m_serverTransport.Connect() && SendSkinnyMsg(m_registerMsg)) + if (m_transport.Connect() && SendRegisterMsg()) break; - PTRACE(2, "Server transport reconnect error: " << m_serverTransport.GetErrorText()); + PTRACE(2, "Server transport reconnect error: " << m_transport.GetErrorText()); PThread::Sleep(10000); } break; @@ -303,8 +389,8 @@ break; default : - PTRACE(2, "Server transport error: " << m_serverTransport.GetErrorText(PChannel::LastReadError)); - m_serverTransport.Close(); + PTRACE(2, "Server transport error: " << m_transport.GetErrorText(PChannel::LastReadError)); + m_transport.Close(); } } } @@ -312,67 +398,67 @@ } -bool OpalSkinnyEndPoint::OnReceiveMsg(const KeepAliveMsg &) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice & client, const KeepAliveMsg &) { - return SendSkinnyMsg(KeepAliveAckMsg()); + return client.SendSkinnyMsg(KeepAliveAckMsg()); } -bool OpalSkinnyEndPoint::OnReceiveMsg(const KeepAliveAckMsg &) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice &, const KeepAliveAckMsg &) { return true; } -bool OpalSkinnyEndPoint::OnReceiveMsg(const RegisterMsg &) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice &, const RegisterMsg &) { return true; } -bool OpalSkinnyEndPoint::OnReceiveMsg(const RegisterAckMsg & ack) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice & client, const RegisterAckMsg & ack) { - m_registrationStatus = "Registered"; + client.m_status = "Registered"; PIPSocket::AddressAndPort ap; - if (m_serverTransport.GetLocalAddress().GetIpAndPort(ap)) - SendSkinnyMsg(PortMsg(ap.GetPort())); + if (client.m_transport.GetLocalAddress().GetIpAndPort(ap)) + client.SendSkinnyMsg(PortMsg(ap.GetPort())); KeepAliveMsg msg; - m_serverTransport.SetKeepAlive(PTimeInterval(0, ack.m_keepAlive), PBYTEArray((const BYTE *)&msg, sizeof(msg))); + client.m_transport.SetKeepAlive(PTimeInterval(0, ack.m_keepAlive), PBYTEArray((const BYTE *)&msg, sizeof(msg))); return true; } -bool OpalSkinnyEndPoint::OnReceiveMsg(const RegisterRejectMsg & msg) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice & client, const RegisterRejectMsg & msg) { PTRACE(2, "Server rejected registration: " << msg.m_errorText); - m_registrationStatus = PConstString("Rejected: ") + msg.m_errorText; + client.m_status = PConstString("Rejected: ") + msg.m_errorText; return false; } -bool OpalSkinnyEndPoint::OnReceiveMsg(const UnregisterMsg &) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice &, const UnregisterMsg &) { return true; } -bool OpalSkinnyEndPoint::OnReceiveMsg(const UnregisterAckMsg &) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice & client, const UnregisterAckMsg &) { PTRACE(2, "Unregistered from server"); - m_registrationStatus = "Unregistered"; + client.m_status = "Unregistered"; return false; } -bool OpalSkinnyEndPoint::OnReceiveMsg(const PortMsg &) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice &, const PortMsg &) { return true; } -bool OpalSkinnyEndPoint::OnReceiveMsg(const CapabilityRequestMsg &) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice & client, const CapabilityRequestMsg &) { CapabilityResponseMsg msg; @@ -395,30 +481,31 @@ msg.SetLength(sizeof(SkinnyMsg)+sizeof(msg.m_count) + count*sizeof(CapabilityResponseMsg::Info)); } - return SendSkinnyMsg(msg); + return client.SendSkinnyMsg(msg); } -bool OpalSkinnyEndPoint::OnReceiveMsg(const CapabilityResponseMsg &) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice &, const CapabilityResponseMsg &) { return true; } -bool OpalSkinnyEndPoint::OnReceiveMsg(const CallStateMsg & msg) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice & client, const CallStateMsg & msg) { // See if we are dialling out - PSafePtr<OpalSkinnyConnection> connection = PSafePtrCast<OpalConnection, OpalSkinnyConnection>(connectionsActive.FindWithLock(OutgoingConnectionToken, PSafeReadWrite)); + PString dialOutToken = CreateToken(client, 0); + PSafePtr<OpalSkinnyConnection> connection = PSafePtrCast<OpalConnection, OpalSkinnyConnection>(connectionsActive.FindWithLock(dialOutToken, PSafeReadWrite)); if (connection != NULL) { if (!connection->OnReceiveMsg(msg)) return false; // Now have call/line id's do token swapping - connectionsActive.Move(OutgoingConnectionToken, connection->GetToken()); + connectionsActive.Move(dialOutToken, connection->GetToken()); return true; } - connection = GetSkinnyConnection(msg.m_callIdentifier); + connection = GetSkinnyConnection(client, msg.m_callIdentifier); if (connection != NULL) return connection->OnReceiveMsg(msg); @@ -434,7 +521,7 @@ return true; } - connection = CreateConnection(*call, CreateToken(msg.m_callIdentifier), PString::Empty(), NULL, 0, NULL); + connection = CreateConnection(*call, client, msg.m_callIdentifier, PString::Empty(), NULL, 0, NULL); if (AddConnection(connection) == NULL) return true; @@ -442,109 +529,105 @@ } -bool OpalSkinnyEndPoint::OnReceiveMsg(const CallInfoMsg & msg) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice & client, const CallInfoMsg & msg) { - return DelegateMsg(msg); + return DelegateMsg(client, msg); } -bool OpalSkinnyEndPoint::OnReceiveMsg(const SetRingerMsg & msg) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice & client, const SetRingerMsg & msg) { - return DelegateMsg(msg); + return DelegateMsg(client, msg); } -bool OpalSkinnyEndPoint::OnReceiveMsg(const OffHookMsg &) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice &, const OffHookMsg &) { return true; } -bool OpalSkinnyEndPoint::OnReceiveMsg(const OnHookMsg &) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice &, const OnHookMsg &) { return true; } -bool OpalSkinnyEndPoint::OnReceiveMsg(const StartToneMsg &) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice &, const StartToneMsg &) { return true; } -bool OpalSkinnyEndPoint::OnReceiveMsg(const StopToneMsg &) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice &, const StopToneMsg &) { return true; } -bool OpalSkinnyEndPoint::OnReceiveMsg(const KeyPadButtonMsg &) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice &, const KeyPadButtonMsg &) { return true; } -bool OpalSkinnyEndPoint::OnReceiveMsg(const SoftKeyEventMsg &) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice &, const SoftKeyEventMsg &) { return true; } -bool OpalSkinnyEndPoint::OnReceiveMsg(const OpenReceiveChannelMsg & msg) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice & client, const OpenReceiveChannelMsg & msg) { - return DelegateMsg(msg); + return DelegateMsg(client, msg); } -bool OpalSkinnyEndPoint::OnReceiveMsg(const OpenReceiveChannelAckMsg &) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice &, const OpenReceiveChannelAckMsg &) { return true; } -bool OpalSkinnyEndPoint::OnReceiveMsg(const CloseReceiveChannelMsg & msg) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice & client, const CloseReceiveChannelMsg & msg) { - return DelegateMsg(msg); + return DelegateMsg(client, msg); } -bool OpalSkinnyEndPoint::OnReceiveMsg(const StartMediaTransmissionMsg & msg) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice & client, const StartMediaTransmissionMsg & msg) { - return DelegateMsg(msg); + return DelegateMsg(client, msg); } -bool OpalSkinnyEndPoint::OnReceiveMsg(const StopMediaTransmissionMsg & msg) +bool OpalSkinnyEndPoint::OnReceiveMsg(PhoneDevice & client, const StopMediaTransmissionMsg & msg) { - return DelegateMsg(msg); + return DelegateMsg(client, msg); } -bool OpalSkinnyEndPoint::SendSkinnyMsg(const SkinnyMsg & msg) +PSafePtr<OpalSkinnyConnection> OpalSkinnyEndPoint::GetSkinnyConnection(const PhoneDevice & client, uint32_t callIdentifier, PSafetyMode mode) { - return m_serverTransport.Write(&msg, msg.GetLength()); + return PSafePtrCast<OpalConnection, OpalSkinnyConnection>(connectionsActive.FindWithLock(CreateToken(client, callIdentifier), mode)); } -PSafePtr<OpalSkinnyConnection> OpalSkinnyEndPoint::GetSkinnyConnection(uint32_t callIdentifier, PSafetyMode mode) -{ - return PSafePtrCast<OpalConnection, OpalSkinnyConnection>(connectionsActive.FindWithLock(CreateToken(callIdentifier), mode)); -} - - /////////////////////////////////////////////////////////////////////////////// OpalSkinnyConnection::OpalSkinnyConnection(OpalCall & call, OpalSkinnyEndPoint & ep, - const PString & token, + OpalSkinnyEndPoint::PhoneDevice & client, + unsigned callIdentifier, const PString & dialNumber, void * /*userData*/, unsigned options, OpalConnection::StringOptions * stringOptions) - : OpalRTPConnection(call, ep, token, options, stringOptions) + : OpalRTPConnection(call, ep, CreateToken(client, callIdentifier), options, stringOptions) , m_endpoint(ep) + , m_client(client) , m_lineInstance(0) - , m_callIdentifier(0) + , m_callIdentifier(callIdentifier) , m_audioId(0) , m_videoId(0) { @@ -559,7 +642,7 @@ OnApplyStringOptions(); // At this point we just go off hook, wait for CallStateMsg which creates the connection - m_endpoint.SendSkinnyMsg(OpalSkinnyEndPoint::OffHookMsg()); + m_client.SendSkinnyMsg(OpalSkinnyEndPoint::OffHookMsg()); return true; } @@ -570,7 +653,7 @@ msg.m_event = OpalSkinnyEndPoint::eSoftKeyEndcall; msg.m_callIdentifier = m_callIdentifier; msg.m_lineInstance = m_lineInstance; - m_endpoint.SendSkinnyMsg(msg); + m_client.SendSkinnyMsg(msg); OpalRTPConnection::OnReleased(); } @@ -593,7 +676,7 @@ msg.m_event = OpalSkinnyEndPoint::eSoftKeyAnswer; msg.m_callIdentifier = m_callIdentifier; msg.m_lineInstance = m_lineInstance; - return m_endpoint.SendSkinnyMsg(msg); + return m_client.SendSkinnyMsg(msg); } @@ -615,7 +698,7 @@ if (m_callIdentifier != msg.m_callIdentifier) { m_callIdentifier = msg.m_callIdentifier; PTRACE(3, "Call identifier set to " << m_callIdentifier); - PString newToken = CreateToken(m_callIdentifier); + PString newToken = CreateToken(m_client, m_callIdentifier); if (callToken != newToken) { callToken = newToken; PTRACE(3, "Set incoming calls new token to \"" << callToken << '"'); @@ -730,7 +813,7 @@ return NULL; } - if (!mediaSession->Open(m_endpoint.GetServerTransport().GetInterface(), m_endpoint.GetServerTransport().GetRemoteAddress(), false)) { + if (!mediaSession->Open(m_client.m_transport.GetInterface(), m_client.m_transport.GetRemoteAddress(), false)) { PTRACE(2, "Could not open media session for " << mediaFormat); return NULL; } @@ -756,7 +839,7 @@ ack.m_passThruPartyId = msg.m_passThruPartyId; ack.m_ip = ap.GetAddress(); ack.m_port = ap.GetPort(); - m_endpoint.SendSkinnyMsg(ack); + m_client.SendSkinnyMsg(ack); SetFromIdMediaType(mediaSession->GetMediaType(), msg.m_passThruPartyId); PTRACE(3, "Opened receive channel for " << *this); Modified: opal/trunk/src/opal/console_mgr.cxx =================================================================== --- opal/trunk/src/opal/console_mgr.cxx 2014-01-31 11:25:02 UTC (rev 31335) +++ opal/trunk/src/opal/console_mgr.cxx 2014-01-31 22:15:00 UTC (rev 31336) @@ -632,7 +632,8 @@ strm << "[PSTN options:]" "-no-sccp. Disable Skinny Client Control Protocol\n" "-sccp-server: Set Skinny server address.\n" - "-sccp-streams: Set max number of streams for Skinny client.\n"; + "-sccp-name: Set device name for Skinny client, may be present multiple times.\n" + "-sccp-device: Set device type code for Skinny clients.\n"; } @@ -648,13 +649,17 @@ return true; } - unsigned maxStreams = args.GetOptionAs("sccp-streams", 1); + unsigned deviceType = args.GetOptionAs<unsigned>("sccp-device", OpalSkinnyEndPoint::DefaultDeviceType); PString server = args.GetOptionString("sccp-server"); if (!server.IsEmpty()) { - if (!Register(server, maxStreams)) - output << "Could not register with skinny server \"" << server << '"' << endl; - else if (verbose) - output << "Skinny server: " << server << '\n'; + PStringArray names = args.GetOptionString("sccp-name").Lines(); + for (PINDEX i = 0; i < names.GetSize(); ++i) { + PString name = names[i]; + if (!Register(server, name, deviceType)) + output << "Could not register " << name << " with skinny server \"" << server << '"' << endl; + else if (verbose) + output << "Skinny client: " << name << '@' << server << '\n'; + } } AddRoutesFor(this, defaultRoute); @@ -663,18 +668,39 @@ #if P_CLI -void OpalConsoleSkinnyEndPoint::CmdServer(PCLI::Arguments & args, P_INT_PTR) +void OpalConsoleSkinnyEndPoint::CmdRegister(PCLI::Arguments & args, P_INT_PTR) { - if (args.GetCount() < 1) + if (args.GetCount() < 2) args.WriteUsage(); - else if (!Register(args[0])) - args.WriteError() << "Could not register with skinny server \"" << args[0] << '"' << endl; + else if (!Register(args[0], args[1])) + args.WriteError() << "Could not register \"" << args[1] << "\" with skinny server \"" << args[0] << '"' << endl; } +void OpalConsoleSkinnyEndPoint::CmdStatus(PCLI::Arguments & args, P_INT_PTR) +{ + ostream & out = args.GetContext(); + + bool none = true; + + PStringArray names = GetPhoneDeviceNames(); + for (PINDEX i = 0; i < names.GetSize(); ++i) { + OpalSkinnyEndPoint::PhoneDevice * phoneDevice = GetPhoneDevice(names[i]); + if (phoneDevice != NULL) { + out << *phoneDevice << endl; + none = false; + } + } + + if (none) + out << "No phone devices registered" << endl; +} + + void OpalConsoleSkinnyEndPoint::AddCommands(PCLI & cli) { - cli.SetCommand("sccp server", PCREATE_NOTIFIER(CmdServer), "Set skinny server", "[ <host> ]"); + cli.SetCommand("sccp register", PCREATE_NOTIFIER(CmdRegister), "Set skinny server", "[ <host> <name> ]"); + cli.SetCommand("sccp status", PCREATE_NOTIFIER(CmdStatus), "Display status of registered Skinny phone devices"); } #endif // P_CLI #endif // OPAL_SKINNY Modified: opal/trunk/src/opal/opal_c.cxx =================================================================== --- opal/trunk/src/opal/opal_c.cxx 2014-01-31 11:25:02 UTC (rev 31335) +++ opal/trunk/src/opal/opal_c.cxx 2014-01-31 22:15:00 UTC (rev 31336) @@ -1577,7 +1577,7 @@ } else { OpalMediaType mediaType = mediaName.ToLower(); - if (OpalMediaType::GetDefinition(mediaType) != NULL) { + if (!mediaType.empty()) { // Known media type name, change all codecs of that type for (OpalMediaFormatList::iterator it = allCodecs.begin(); it != allCodecs.end(); ++it) { if (it->GetMediaType() == mediaType) { @@ -2021,7 +2021,7 @@ #if OPAL_SKINNY OpalSkinnyEndPoint * skinnyEP = dynamic_cast<OpalSkinnyEndPoint *>(ep); if (skinnyEP != NULL) { - if (!skinnyEP->Register(command.m_param.m_registrationInfo.m_hostName)) + if (!skinnyEP->Register(command.m_param.m_registrationInfo.m_hostName, command.m_param.m_registrationInfo.m_identifier)) response.SetError("Failed to initiate SCCP registration."); return; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |