#if defined( WIN32 ) #include #endif #include #include "rutil/ResipAssert.h" #include #include "../Version.hxx" #include "JabberComponent.hxx" #include "IChatUser.hxx" #ifndef RESIP_CONTRIB_GLOOX #include #include #include #else #include #include #include #endif using namespace gateway; using namespace gloox; using namespace std; extern void sleepSeconds(unsigned int seconds); void IChatCallRequest::sendIChatVCRequest(const std::string& fullTo) { // Only allow one VCRequest per resource - this guards against receiving multiple // presence messages from the same resource if(mPendingVCRequestSet.find(fullTo) == mPendingVCRequestSet.end()) { mJabberComponent->notifyIChatCallProceeding(mB2BSessionHandle, fullTo); resip_assert(mJabberComponent); mJabberComponent->sendPresence(fullTo, mJabberComponent->mControlJID, false /* advertiseIChatSupport */, true /* available */); // Doing this let's us push the control presence as well send calls std::string id = mJabberComponent->mComponent->getID(); Tag *iq = new Tag("iq"); iq->addAttribute("type", "set"); iq->addAttribute("id", id); iq->addAttribute("to", fullTo.c_str()); iq->addAttribute("from", mFrom.c_str()); Tag *query = new Tag(iq, "query"); query->addAttribute("xmlns", "apple:iq:vc:request"); new Tag(query, "VCNewCallerIPPortData", mJabberComponent->mLocalIChatPortListBlob); new Tag(query, "isAudioOnly", "1"); new Tag(query, "extSIPPort", "0"); new Tag(query, "extIPAddr", "127.0.0.1"); new Tag(query, "VCProtocolVersion", "1"); mJabberComponent->mComponent->send(iq); mPendingVCRequestSet.insert(fullTo); } } void IChatCallRequest::receivedIChatVCResponse(const std::string& from) { std::set::iterator it = mPendingVCRequestSet.find(from); mPendingVCRequestSet.erase(it); // Remove responded JID from list and cancel all others sendIChatVCCancelToAll(); } void IChatCallRequest::sendIChatVCCancelToAll() { std::set::iterator it = mPendingVCRequestSet.begin(); std::string id = mJabberComponent->mComponent->getID(); // Send a cancel out for each VCRequest for(;it!=mPendingVCRequestSet.end();it++) { Tag *iq = new Tag("iq"); iq->addAttribute("type", "set"); iq->addAttribute("id", id); iq->addAttribute("to", it->c_str()); iq->addAttribute("from", mFrom.c_str()); Tag *query = new Tag(iq, "query"); query->addAttribute("xmlns", "apple:iq:vc:cancel"); new Tag(query, "VCProtocolVersion", "1"); mJabberComponent->mComponent->send(iq); } mPendingVCRequestSet.clear(); } void IChatCallRequest::sendIChatVCResponse(bool accept) { Tag *iq = new Tag( "iq" ); iq->addAttribute( "type", "set" ); iq->addAttribute( "id", mJabberComponent->mComponent->getID() ); iq->addAttribute( "to", mFrom ); iq->addAttribute( "from", mTo ); Tag *query = new Tag( iq, "query" ); query->addAttribute( "xmlns", "apple:iq:vc:response"); if(accept) { new Tag( query, "response", "0" ); new Tag( query, "connectData", mJabberComponent->mLocalIChatPortListBlob); } else { new Tag( query, "response", "1" ); new Tag( query, "connectData", ""); } //new Tag( query, "responseData", "1" ); // doesn't appear to be required new Tag( query, "VCProtocolVersion", "1" ); mJabberComponent->mComponent->send(iq); } class IPCMutexGloox : public IPCMutex { public: IPCMutexGloox() {} virtual ~IPCMutexGloox() {} virtual void lock() { mMutex.lock(); } virtual void unlock() { mMutex.unlock(); } private: gloox::util::Mutex mMutex; }; IPCMutexGloox g_IPCGlooxMutex; JabberComponent::JabberComponent(unsigned short jabberConnectorIPCPort, unsigned short gatewayIPCPort, const std::string& server, const std::string& component, const std::string& password, int port, unsigned int serverPingDuration, const std::string& controlUser, const std::string& localIChatPortListBlob) : mStopping(false), mServerPingDuration(serverPingDuration), mLocalIChatPortListBlob(localIChatPortListBlob), mIPCThread(jabberConnectorIPCPort, gatewayIPCPort, this, &g_IPCGlooxMutex) { mIPCThread.run(); mComponent = new Component("jabber:component:accept", server, component, password, port); mComponent->registerMessageHandler(this); mComponent->registerConnectionListener(this); mComponent->registerPresenceHandler(this); mComponent->registerSubscriptionHandler(this); mComponent->registerIqHandler(this, "apple:iq:vc:request"); mComponent->registerIqHandler(this, "apple:iq:vc:cancel"); mComponent->registerIqHandler(this, "apple:iq:vc:response"); mComponent->registerIqHandler(this, "apple:iq:vc:counterProposal"); mComponent->logInstance().registerLogHandler(LogLevelDebug, LogAreaAll, this); mComponent->disco()->registerNodeHandler(this, "apple:ichat:caps#avavail"); mComponent->disco()->registerNodeHandler(this, "apple:ichat:caps#audio"); mComponent->disco()->registerNodeHandler(this, "apple:ichat:caps#avcap"); mComponent->disco()->registerNodeHandler(this, "apple:ichat:caps#448"); mComponent->disco()->setIdentity("server", "ichat-gw"); mComponent->disco()->setVersion("ichat-gw", ICHATGW_VERSION_STRING); //mComponent->setTls(tlsPolicy); // This setting appears to have no effect on Component connections mControlJID = controlUser + "@" + component; } JabberComponent::~JabberComponent() { mIPCThread.shutdown(); mIPCThread.join(); } void JabberComponent::thread() { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::thread - starting..."); while(!mStopping) { mComponent->connect(false); ConnectionError rc; time_t lastRecvTime=time(0); while((rc=mComponent->recv(mServerPingDuration * 1000000)) == ConnNoError) { time_t now = time(0); if(now-lastRecvTime >= mServerPingDuration) { mComponent->whitespacePing(); } lastRecvTime = now; } if(!mStopping) { std::ostringstream oss; oss << "JabberComponent::thread - recv error, rc=" << rc; handleLog(gloox::LogLevelError, gloox::LogAreaUser, oss.str()); // Wait 10 seconds then try again sleepSeconds(10); if(mComponent->state() != gloox::StateDisconnected) { mComponent->disconnect(); } } } handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::thread - shutdown."); } void JabberComponent::stop() { mStopping = true; disconnect(); } void JabberComponent::disconnect() { if(mComponent->state() == gloox::StateDisconnected) return; mIChatUserMutex.lock(); // Tell users that control user is now offline IChatUserMap::iterator it = mIChatUsers.begin(); for(;it!=mIChatUsers.end();it++) { // Notify user that subscribed users are now offline const IChatUser::SubscribedGatewayUserList& subscribedUserList = it->second->getSubscribedGatewayUserList(); IChatUser::SubscribedGatewayUserList::const_iterator it2 = subscribedUserList.begin(); for(;it2!=subscribedUserList.end();it2++) { sendPresence(it->first, *it2, false, false /* available? */); } delete it->second; } mIChatUsers.clear(); mIChatUserMutex.unlock(); mComponent->disconnect(); } void JabberComponent::initiateIChatCall(const std::string& to, const std::string& from, unsigned int handle, bool alertOneOnly) { JID jid(to); mOutstandingClientIChatCallRequestsMutex.lock(); // Add call request data to map std::string key = makeVCRequestKey(jid.bare(),from); IChatCallRequest* iChatCallRequest = &(mOutstandingClientIChatCallRequests[key] = IChatCallRequest(this, to, from, handle)); handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::initiateiChatCall - key=" + key); // If there is no resource, then we need to query the bare JID and find iChat resources if(jid.resource().empty()) { bool found = false; if(alertOneOnly) { // First check if we have any info cached locally std::string fullJID; if(getMostAvailableIChatUserFullJID(jid, fullJID)) { found = true; iChatCallRequest->sendIChatVCRequest(fullJID); } } else { std::list fullJIDList; if(getMostAvailableIChatUserFullJIDList(jid, fullJIDList)) { found = true; std::list::iterator it = fullJIDList.begin(); for(;it!=fullJIDList.end();it++) { iChatCallRequest->sendIChatVCRequest(*it); } } } if(!found) { // No local info - try to probe Tag *iq = new Tag("presence"); iq->addAttribute("type", "probe"); iq->addAttribute("to", to.c_str()); iq->addAttribute("from", mControlJID); mComponent->send(iq); } } else // We have full JID - send the vc-request directly to the resource { iChatCallRequest->sendIChatVCRequest(to); } mOutstandingClientIChatCallRequestsMutex.unlock(); } void JabberComponent::cancelIChatCall(const std::string& to, const std::string& from) { JID jid(to); mOutstandingClientIChatCallRequestsMutex.lock(); IChatCallRequestMap::iterator it = findOutstandingClientIChatCallRequest(jid.bare(), from); if(it != mOutstandingClientIChatCallRequests.end()) { it->second.sendIChatVCCancelToAll(); handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::cancelIChatCall - success"); mOutstandingClientIChatCallRequests.erase(it); } else { handleLog(gloox::LogLevelWarning, gloox::LogAreaUser, "JabberComponent::cancelIChatCall - not found"); } mOutstandingClientIChatCallRequestsMutex.unlock(); } void JabberComponent::proceedingIChatCall(const std::string& to, const std::string& from, unsigned int handle) { mOutstandingServerIChatCallRequestsMutex.lock(); IChatCallRequestMap::iterator it = findOutstandingServerIChatCallRequest(to, from); if(it!=mOutstandingServerIChatCallRequests.end()) { // Store session handle for notifications to SIP layer resip_assert(it->second.mB2BSessionHandle == 0); std::ostringstream oss; oss << "JabberComponent::proceedingIChatCall - set handle to " << handle; handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, oss.str()); it->second.mB2BSessionHandle = handle; // If already cancelled, then notify SIP layer now if(it->second.mCancelled) { notifyIChatCallCancelled(it->second.mB2BSessionHandle); mOutstandingServerIChatCallRequests.erase(it); } } mOutstandingServerIChatCallRequestsMutex.unlock(); } void JabberComponent::acceptIChatCall(const std::string& to, const std::string& from) { mOutstandingServerIChatCallRequestsMutex.lock(); IChatCallRequestMap::iterator it = findOutstandingServerIChatCallRequest(to, from); if(it!=mOutstandingServerIChatCallRequests.end()) { it->second.sendIChatVCResponse(true); mOutstandingServerIChatCallRequests.erase(it); } mOutstandingServerIChatCallRequestsMutex.unlock(); } void JabberComponent::rejectIChatCall(const std::string& to, const std::string& from) { mOutstandingServerIChatCallRequestsMutex.lock(); IChatCallRequestMap::iterator it = findOutstandingServerIChatCallRequest(to, from); if(it!=mOutstandingServerIChatCallRequests.end()) { it->second.sendIChatVCResponse(false); mOutstandingServerIChatCallRequests.erase(it); } mOutstandingServerIChatCallRequestsMutex.unlock(); } std::string JabberComponent::makeVCRequestKey(const std::string& bareTo, const std::string& bareFrom) { std::string key = bareTo + "|" + bareFrom; #ifdef WIN32 std::transform(key.begin(), key.end(), key.begin(), tolower); #else std::transform(key.begin(), key.end(), key.begin(), (int(*)(int))std::tolower); #endif return key; } JabberComponent::IChatCallRequestMap::iterator JabberComponent::findOutstandingClientIChatCallRequest(const std::string& bareTo, const std::string& bareFrom) { std::string key = makeVCRequestKey(bareTo, bareFrom); handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::findOutstandingClientIChatCallRequest - key=" + key); return mOutstandingClientIChatCallRequests.find(key); } void JabberComponent::failOutstandingClientIChatCallRequest(const std::string& bareTo, const std::string& bareFrom, unsigned int code) { mOutstandingClientIChatCallRequestsMutex.lock(); IChatCallRequestMap::iterator it = findOutstandingClientIChatCallRequest(bareTo, bareFrom); if(it!=mOutstandingClientIChatCallRequests.end()) { notifyIChatCallFailed(it->second.mB2BSessionHandle, code); mOutstandingClientIChatCallRequests.erase(it); } mOutstandingClientIChatCallRequestsMutex.unlock(); } void JabberComponent::failOutstandingClientIChatCallRequest(const std::string& bareTo, unsigned int code) { mOutstandingClientIChatCallRequestsMutex.lock(); IChatCallRequestMap::iterator it = mOutstandingClientIChatCallRequests.begin(); while(it != mOutstandingClientIChatCallRequests.end()) { if(it->second.mTo == bareTo) { notifyIChatCallFailed(it->second.mB2BSessionHandle, code); mOutstandingClientIChatCallRequests.erase(it++); } else { it++; } } mOutstandingClientIChatCallRequestsMutex.unlock(); } JabberComponent::IChatCallRequestMap::iterator JabberComponent::findOutstandingServerIChatCallRequest(const std::string& bareTo, const std::string& bareFrom) { std::string key = makeVCRequestKey(bareTo, bareFrom); handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::findOutstandingServerIChatCallRequest - key=" + key); return mOutstandingServerIChatCallRequests.find(key); } void JabberComponent::cancelOutstandingServerIChatCallRequest(const std::string& bareTo, const std::string& bareFrom) { mOutstandingServerIChatCallRequestsMutex.lock(); IChatCallRequestMap::iterator it = findOutstandingServerIChatCallRequest(bareTo, bareFrom); if(it!=mOutstandingServerIChatCallRequests.end()) { if(it->second.mB2BSessionHandle != 0) { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::cancelOutstandingServerIChatCallRequest - to=" + bareTo + " from=" + bareFrom); notifyIChatCallCancelled(it->second.mB2BSessionHandle); mOutstandingServerIChatCallRequests.erase(it); } else { // We don't have a session handle yet, so flag request as cancelled, // when session handle arrives, call notifyIChatCallCancelled and remove entry it->second.mCancelled = true; handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::cancelOutstandingServerIChatCallRequest - no session handle yet, pending - to=" + bareTo + " from=" + bareFrom); } } mOutstandingServerIChatCallRequestsMutex.unlock(); } void JabberComponent::probePresence(const std::string& to) { Tag *presence = new Tag("presence"); presence->addAttribute("type", "probe"); presence->addAttribute("to", to); presence->addAttribute("from", mControlJID); mComponent->send(presence); } void JabberComponent::sendPresenceForRequest(const Presence& presence) { sendPresence(presence.from().bare(), presence.to().full(), presence.to().bare() != mControlJID, true /* available */); } void JabberComponent::sendPresence(const std::string& to, const std::string& from, bool advertiseIChatSupport, bool available) { Tag *presence = new Tag("presence"); if(!available) { presence->addAttribute("type", "unavailable"); } presence->addAttribute("to", to); presence->addAttribute("from", from); new Tag(presence, "priority", "0"); if(advertiseIChatSupport) { Tag* c = new Tag(presence, "c"); c->addAttribute("xmlns", "http://jabber.org/protocol/caps"); c->addAttribute("node", "apple:ichat:caps"); c->addAttribute("ver", "448"); // Note: Use version 448 so we don't collide with actual iChat version c->addAttribute("ext", "avavail avcap audio"); } mComponent->send(presence); } void JabberComponent::sendSubscriptionResponse(const std::string& to, const std::string& from, bool success) { if(success) { Tag *p = new Tag( "presence" ); p->addAttribute( "type", "subscribed" ); p->addAttribute( "to", to ); p->addAttribute( "from", from ); mComponent->send( p ); mUserDb.addSubscribedJID(to.c_str(), from.c_str()); storeIChatSubscribedUser(to, from); sendPresence(to, from, from != mControlJID, true); // Subscribe back to client to ensure we are subscribed to client Tag *iq = new Tag("presence"); iq->addAttribute("type", "subscribe"); iq->addAttribute("to", to); iq->addAttribute("from", mControlJID); mComponent->send(iq); } else { Tag *p = new Tag( "presence" ); p->addAttribute( "type", "unsubscribed" ); p->addAttribute( "to", to ); p->addAttribute( "from", from ); mComponent->send( p ); } } void JabberComponent::removeIChatSubscribedUser(const std::string& user, const std::string& subscribedJID) { mIChatUserMutex.lock(); IChatUserMap::iterator it = mIChatUsers.find(user); if(it != mIChatUsers.end()) { it->second->removeSubscribedGatewayUser(subscribedJID); } mIChatUserMutex.unlock(); } void JabberComponent::storeIChatSubscribedUser(const std::string& user, const std::string& subscribedJID) { mIChatUserMutex.lock(); IChatUserMap::iterator it = mIChatUsers.find(user); if(it == mIChatUsers.end()) { // User is not yet present in map IChatUser* iChatUser = new IChatUser(*this, user); iChatUser->addSubscribedGatewayUser(subscribedJID); mIChatUsers[user] = iChatUser; } else { it->second->addSubscribedGatewayUser(subscribedJID); } mIChatUserMutex.unlock(); } void JabberComponent::storeIChatPresence(const gloox::JID& jid, const gloox::Presence& presence, int priority, bool avAvail) { mIChatUserMutex.lock(); IChatUserMap::iterator it = mIChatUsers.find(jid.bare()); if(it == mIChatUsers.end()) { if(presence.presence() != gloox::Presence::Unavailable && !jid.resource().empty()) { // User is not yet present in map IChatUser* iChatUser = new IChatUser(*this, jid.bare()); iChatUser->updateResourceInfo(jid.resource(), presence, priority, avAvail); mIChatUsers[jid.bare()] = iChatUser; } } else { it->second->updateResourceInfo(jid.resource(), presence, priority, avAvail); } mIChatUserMutex.unlock(); } bool JabberComponent::getMostAvailableIChatUserFullJID(const gloox::JID& jid, std::string& fullJID) { bool ret = false; mIChatUserMutex.lock(); IChatUserMap::iterator it = mIChatUsers.find(jid.bare()); if(it != mIChatUsers.end()) { std::string resource = it->second->getMostAvailableResource(); if(!resource.empty()) { JID temp(jid); temp.setResource(resource); fullJID = temp.full(); ret = true; } } mIChatUserMutex.unlock(); return ret; } bool JabberComponent::getMostAvailableIChatUserFullJIDList(const gloox::JID& jid, std::list& fullJIDList) { bool ret = false; mIChatUserMutex.lock(); IChatUserMap::iterator it = mIChatUsers.find(jid.bare()); if(it != mIChatUsers.end()) { std::list resourceList; if(it->second->getMostAvailableResourceList(resourceList)) { std::list::iterator listIt = resourceList.begin(); for(;listIt!=resourceList.end();listIt++) { if(!listIt->empty()) { JID temp(jid); temp.setResource(*listIt); fullJIDList.push_back(temp.full()); ret = true; } } } } mIChatUserMutex.unlock(); return ret; } void JabberComponent::notifyIChatCallRequest(const std::string& to, const std::string& from) { IPCMsg msg; msg.addArg("notifyIChatCallRequest"); msg.addArg(to.c_str()); msg.addArg(from.c_str()); mIPCThread.sendIPCMsg(msg); } void JabberComponent::notifyIChatCallCancelled(unsigned int handle) { IPCMsg msg; msg.addArg("notifyIChatCallCancelled"); msg.addArg(handle); mIPCThread.sendIPCMsg(msg); } void JabberComponent::notifyIChatCallProceeding(unsigned int handle, const std::string& to) { IPCMsg msg; msg.addArg("notifyIChatCallProceeding"); msg.addArg(handle); msg.addArg(to.c_str()); mIPCThread.sendIPCMsg(msg); } void JabberComponent::notifyIChatCallFailed(unsigned int handle, unsigned int statusCode) { IPCMsg msg; msg.addArg("notifyIChatCallFailed"); msg.addArg(handle); msg.addArg(statusCode); mIPCThread.sendIPCMsg(msg); } void JabberComponent::continueIChatCall(unsigned int handle, const std::string& remoteIPPortListBlob) { IPCMsg msg; msg.addArg("continueIChatCall"); msg.addArg(handle); msg.addArg(remoteIPPortListBlob.c_str()); mIPCThread.sendIPCMsg(msg); } void JabberComponent::sipRegisterJabberUser(const std::string& jidToRegister) { IPCMsg msg; msg.addArg("sipRegisterJabberUser"); msg.addArg(jidToRegister.c_str()); mIPCThread.sendIPCMsg(msg); } void JabberComponent::sipUnregisterJabberUser(const std::string& jidToUnregister) { IPCMsg msg; msg.addArg("sipUnregisterJabberUser"); msg.addArg(jidToUnregister.c_str()); mIPCThread.sendIPCMsg(msg); } void JabberComponent::checkSubscription(const std::string& to, const std::string& from) { IPCMsg msg; msg.addArg("checkSubscription"); msg.addArg(to.c_str()); msg.addArg(from.c_str()); mIPCThread.sendIPCMsg(msg); } void JabberComponent::onNewIPCMsg(const IPCMsg& msg) { const std::vector& args = msg.getArgs(); resip_assert(args.size() >= 1); if(args.at(0) == "initiateIChatCall") { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::onNewIPCMsg - initiateIChatCall"); resip_assert(args.size() == 4); initiateIChatCall(args.at(1).c_str(), args.at(2).c_str(), atoi(args.at(3).c_str()), false /* TODO - make setting? */); } else if(args.at(0) == "cancelIChatCall") { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::onNewIPCMsg - cancelIChatCall"); resip_assert(args.size() == 3); cancelIChatCall(args.at(1).c_str(), args.at(2).c_str()); } else if(args.at(0) == "proceedingIChatCall") { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::onNewIPCMsg - proceedingIChatCall"); resip_assert(args.size() == 4); proceedingIChatCall(args.at(1).c_str(), args.at(2).c_str(), atoi(args.at(3).c_str())); } else if(args.at(0) == "acceptIChatCall") { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::onNewIPCMsg - acceptIChatCall"); resip_assert(args.size() == 3); acceptIChatCall(args.at(1).c_str(), args.at(2).c_str()); } else if(args.at(0) == "rejectIChatCall") { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::onNewIPCMsg - rejectIChatCall"); resip_assert(args.size() == 3); rejectIChatCall(args.at(1).c_str(), args.at(2).c_str()); } else if(args.at(0) == "sendSubscriptionResponse") { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::onNewIPCMsg - sendSubscriptionResponse"); resip_assert(args.size() == 4); sendSubscriptionResponse(args.at(1).c_str(), args.at(2).c_str(), atoi(args.at(3).c_str()) != 0); } else { resip_assert(false); } } void JabberComponent::handleLog(LogLevel level, LogArea area, const std::string& message) { IPCMsg msg; msg.addArg("log"); switch(level) { case LogLevelWarning: /**< Non-crititcal warning messages. */ msg.addArg("warning"); break; case LogLevelError: /**< Critical, unrecoverable errors. */ msg.addArg("error"); break; case LogLevelDebug: /**< Debug messages. */ default: msg.addArg("info"); break; } msg.addArg(message.c_str()); mIPCThread.sendIPCMsg(msg); } void JabberComponent::onConnect() { // connection established, auth done (see API docs for exceptions) handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::onConnect"); // Get persisent User subscriptions - and send on-line message const JabberUserDb::UserMap& users = mUserDb.getUserSubscriptions(); JabberUserDb::UserMap::const_iterator it = users.begin(); for(;it!=users.end();it++) { probePresence(it->first.c_str()); // Probe for users presence JabberUserDb::SubscribeSet::const_iterator it2 = it->second.begin(); for(;it2!=it->second.end();it2++) { sendPresence(it->first.c_str(), it2->c_str(), it2->c_str() != mControlJID /* advertise iChat audio support */, true /* available */); storeIChatSubscribedUser(it->first.c_str(), it2->c_str()); } } } void JabberComponent::onDisconnect(ConnectionError e) { // connection established, auth done (see API docs for exceptions) std::ostringstream oss; oss << "JabberComponent::onDisconnect - error=" << e; if(mStopping) { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, oss.str()); } else { handleLog(gloox::LogLevelWarning, gloox::LogAreaUser, oss.str()); } } bool JabberComponent::onTLSConnect(const CertInfo& info) { // Note: currently gloox components to not support TLS connections // examine certificate info handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::onTLSConnect"); return true; } void JabberComponent::handleSubscription(const Subscription& subscription) { switch( subscription.subtype() ) { case Subscription::Subscribe: { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handleSubscription - Subscribe"); if(subscription.to().full() == mControlJID) { sendSubscriptionResponse(subscription.from().bare(), subscription.to().full(), true); } else { checkSubscription(subscription.to().full(), subscription.from().bare()); } break; } case Subscription::Unsubscribe: { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handleSubscription - Unsubscribe"); Tag *p = new Tag( "presence" ); p->addAttribute( "type", "unsubscribed" ); p->addAttribute( "to", subscription.from().bare() ); p->addAttribute( "from", subscription.to().full() ); mComponent->send( p ); mUserDb.removeSubscribedJID(subscription.from().bare().c_str(), subscription.to().bare().c_str()); removeIChatSubscribedUser(subscription.from().bare(), subscription.to().bare()); break; } default: { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handleSubscription - subtype=" + subscription.subtype()); break; } } } void JabberComponent::handlePresence(const Presence& presence) { bool iChatResource = false; bool avAvail=false; // Check if iChat endpoint Tag* c = presence.tag()->findChild("c"); if(c) { std::string node = c->findAttribute("node"); if(node == "apple:ichat:caps" || node == "http://www.apple.com/ichat/caps") { iChatResource = true; // Check if caps include AV Available capability (note: if iChat is already on a call then it does not have avavail capability - since iChat only allows one call at a time) std::string ext = c->findAttribute("ext"); if(ext.find("avavail") != std::string::npos) { avAvail = true; } } } // presence info switch(presence.subtype()) { case gloox::Presence::Probe: // A request for an entity's current presence handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handlePresence - Probe from " + presence.from().full()); sendPresenceForRequest(presence); break; case gloox::Presence::Available: // Signals to the server that the sender is online and available for communication. if(iChatResource) { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handlePresence - PresenceAvailable from " + presence.from().full()); // Ensure we are tracking client storeIChatPresence(presence.from(), presence, presence.priority(), avAvail); // Check if we have an outstanding iChat Call request if(avAvail) { mOutstandingClientIChatCallRequestsMutex.lock(); IChatCallRequestMap::iterator it = mOutstandingClientIChatCallRequests.begin(); bool callRequestFound = false; while(it != mOutstandingClientIChatCallRequests.end()) { if(it->second.mTo == presence.from().bare()) { callRequestFound = true; handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handlePresence - Available for " + presence.from().full() + " - outstanding iChat call request - continuing call"); it->second.sendIChatVCRequest(presence.from().full()); } it++; } mOutstandingClientIChatCallRequestsMutex.unlock(); } } break; case gloox::Presence::Unavailable: // Signals that the entity is no longer available for communication. handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handlePresence - Unavailable for " + presence.from().full()); // Ensure we are tracking client storeIChatPresence(presence.from(), presence, presence.priority(), avAvail); // Check if we have an outstanding iChat Call request to fail failOutstandingClientIChatCallRequest(presence.from().bare(), 404); break; case gloox::Presence::Error: handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handlePresence - Error for " + presence.from().full()); { unsigned int code = 0; Tag *error = presence.tag()->findChild( "error" ); if(error) { code = atoi(error->findAttribute("code").c_str()); } // Check if we have an outstanding iChat Call request to fail failOutstandingClientIChatCallRequest(presence.from().bare(), code); } break; default: break; } } void JabberComponent::handleMessage(const Message& msg, MessageSession* session) { std::ostringstream oss; oss << "JabberComponent::handlePresence - " << &msg; handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, oss.str()); Message *s = new Message(Message::Normal, msg.from().full(), "You have reached the ichat gateway!" ); s->tag()->addAttribute("from", msg.to().full()); mComponent->send(*s); } bool JabberComponent::handleIq(const IQ& iq) { if(iq.subtype() == IQ::Set && iq.tag()->xmlns() == "apple:iq:vc:request") { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "Jabber::handleIq - apple:iq:vc:request from=" + iq.from().full() + ", to=" + iq.to().full()); TagList tlist = iq.tag()->children(); Tag* query = iq.tag()->findChild("query"); Tag* vcNewCallerIPPortDataTag; if(query) { vcNewCallerIPPortDataTag = query->findChild("VCNewCallerIPPortData"); if(vcNewCallerIPPortDataTag) { std::ostringstream oss; oss << "Jabber::handleIq: vc:request, VCNewCallerIPPortData=" << vcNewCallerIPPortDataTag->cdata() << " size=" << vcNewCallerIPPortDataTag->cdata().size(); handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, oss.str()); } Tag* extSIPPort = query->findChild("extSIPPort"); if(extSIPPort) { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "Jabber::handleIq - apple:iq:vc:request extSIPPort=" + extSIPPort->cdata()); } Tag* extIPAddr = query->findChild("extIPAddr"); if(extIPAddr) { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "Jabber::handleIq - apple:iq:vc:request extIPAddr=" + extIPAddr->cdata()); } Tag* vcProtocolVersion = query->findChild("VCProtocolVersion"); if(vcProtocolVersion) { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "Jabber::handleIq - apple:iq:vc:request VCProtocolVersion=" + vcProtocolVersion->cdata()); } } // Create entry in outstanding server requests map std::string key = makeVCRequestKey(iq.to().bare(),iq.from().bare()); mOutstandingServerIChatCallRequestsMutex.lock(); IChatCallRequest* iChatCallRequest = &(mOutstandingServerIChatCallRequests[key] = IChatCallRequest(this, iq.to().bare(), iq.from().full(), 0)); mOutstandingServerIChatCallRequestsMutex.unlock(); // Pass request to SIP side notifyIChatCallRequest(iq.to().bare(), iq.from().bare()); } else if(iq.subtype() == IQ::Set && iq.tag()->xmlns() == "apple:iq:vc:counterProposal") { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "Jabber::handleIq - apple:iq:vc:counterProposal from=" + iq.from().full() + ", to=" + iq.to().full()); // Build response - Do we even need to respond??? { Tag *iqtag = new Tag( "iq" ); iqtag->addAttribute( "type", "set" ); iqtag->addAttribute( "id", mComponent->getID() ); iqtag->addAttribute( "to", iq.from().full() ); iqtag->addAttribute( "from", iq.to().full() ); Tag *query = new Tag( iqtag, "query" ); query->addAttribute( "xmlns", "apple:iq:vc:counterProposal"); new Tag( query, "connectData", mLocalIChatPortListBlob); new Tag( query, "VCProtocolVersion", "1" ); mComponent->send(iq); } } else if(iq.subtype() == IQ::Set && iq.tag()->xmlns() == "apple:iq:vc:cancel") { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "Jabber::handleIq - apple:iq:vc:cancel from=" + iq.from().full() + ", to=" + iq.to().full()); cancelOutstandingServerIChatCallRequest(iq.to().bare(), iq.from().bare()); } else if(iq.subtype() == IQ::Set && iq.tag()->xmlns() == "apple:iq:vc:response") { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "Jabber::handleIq - apple:iq:vc:response from=" + iq.from().full() + ", to=" + iq.to().full()); TagList tlist = iq.tag()->children(); Tag* query = iq.tag()->findChild("query"); Tag* vcNewCallerIPPortDataTag; if(query) { vcNewCallerIPPortDataTag = query->findChild("connectData"); if(vcNewCallerIPPortDataTag) { mOutstandingClientIChatCallRequestsMutex.lock(); std::ostringstream oss; oss << "Jabber::handleIq - connectData=" << vcNewCallerIPPortDataTag->cdata() << " size=" << vcNewCallerIPPortDataTag->cdata().size(); handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, oss.str()); IChatCallRequestMap::iterator it = findOutstandingClientIChatCallRequest(iq.from().bare(), iq.to().bare()); if(it!=mOutstandingClientIChatCallRequests.end()) { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "Jabber::handleIq - apple:iq:vc:response received - continuing SIP portion of call..."); it->second.receivedIChatVCResponse(iq.from().full()); continueIChatCall(it->second.mB2BSessionHandle, vcNewCallerIPPortDataTag->cdata()); mOutstandingClientIChatCallRequests.erase(it); } mOutstandingClientIChatCallRequestsMutex.unlock(); } } } else if(iq.subtype() == IQ::Error && iq.tag()->xmlns() == "apple:iq:vc:request") { unsigned int code = 0; Tag *error = iq.tag()->findChild( "error" ); if(error) { code = atoi(error->findAttribute("code").c_str()); } failOutstandingClientIChatCallRequest(iq.from().bare(), iq.to().bare(), code); handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "Jabber::handleIq - apple:iq:vc:request error received - notifying SIP portion of call..."); } else { std::ostringstream oss; oss << "Jabber::handleIq - " << &iq; handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, oss.str()); } return true; } void JabberComponent::handleIqID(const IQ& iq, int context) { std::ostringstream oss; oss << "Jabber::handleIqID - context=" << context; handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, oss.str()); //return false; } StringList JabberComponent::handleDiscoNodeFeatures(const JID& from, const std::string& node) { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handleDiscoNodeFeatures - node=" + node); StringList slist; if(node == "apple:ichat:caps#avavail") { slist.push_back("apple:iq:vc:available"); } else if(node == "apple:ichat:caps#audio") { slist.push_back("apple:iq:vc:audio"); } else if(node == "apple:ichat:caps#avcap") { slist.push_back("apple:iq:vc:capable"); } else if(node == "apple:ichat:caps#448") { slist.push_back("jabber:iq:version"); } return slist; } Disco::IdentityList JabberComponent::handleDiscoNodeIdentities(const JID& from, const std::string& node) { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handleDiscoNodeIdentities - node=" + node + ", JID=" + from.full()); Disco::IdentityList smap; //smap["client"] = "pc"; return smap; } Disco::ItemList JabberComponent::handleDiscoNodeItems(const JID& from, const JID& to, const std::string& node) { handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handleDiscoNodeItems - node=" + node); Disco::ItemList dlist; return dlist; } /* ==================================================================== Copyright (c) 2009, SIP Spectrum, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of SIP Spectrum nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================== */