Home | History | Annotate | Download | only in base
      1 /*
      2  * libjingle
      3  * Copyright 2004--2005, Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include "talk/p2p/base/sessionmanager.h"
     29 
     30 #include "talk/base/common.h"
     31 #include "talk/base/helpers.h"
     32 #include "talk/base/logging.h"
     33 #include "talk/base/scoped_ptr.h"
     34 #include "talk/base/stringencode.h"
     35 #include "talk/p2p/base/constants.h"
     36 #include "talk/p2p/base/session.h"
     37 #include "talk/p2p/base/sessionmessages.h"
     38 #include "talk/xmpp/constants.h"
     39 #include "talk/xmpp/jid.h"
     40 
     41 namespace cricket {
     42 
     43 SessionManager::SessionManager(PortAllocator *allocator,
     44                                talk_base::Thread *worker) {
     45   allocator_ = allocator;
     46   signaling_thread_ = talk_base::Thread::Current();
     47   if (worker == NULL) {
     48     worker_thread_ = talk_base::Thread::Current();
     49   } else {
     50     worker_thread_ = worker;
     51   }
     52   timeout_ = 50;
     53 }
     54 
     55 SessionManager::~SessionManager() {
     56   // Note: Session::Terminate occurs asynchronously, so it's too late to
     57   // delete them now.  They better be all gone.
     58   ASSERT(session_map_.empty());
     59   // TerminateAll();
     60   SignalDestroyed();
     61 }
     62 
     63 void SessionManager::AddClient(const std::string& content_type,
     64                                SessionClient* client) {
     65   ASSERT(client_map_.find(content_type) == client_map_.end());
     66   client_map_[content_type] = client;
     67 }
     68 
     69 void SessionManager::RemoveClient(const std::string& content_type) {
     70   ClientMap::iterator iter = client_map_.find(content_type);
     71   ASSERT(iter != client_map_.end());
     72   client_map_.erase(iter);
     73 }
     74 
     75 SessionClient* SessionManager::GetClient(const std::string& content_type) {
     76   ClientMap::iterator iter = client_map_.find(content_type);
     77   return (iter != client_map_.end()) ? iter->second : NULL;
     78 }
     79 
     80 Session* SessionManager::CreateSession(const std::string& local_name,
     81                                        const std::string& content_type) {
     82   std::string id;
     83   return CreateSession(id, local_name, content_type);
     84 }
     85 
     86 Session* SessionManager::CreateSession(const std::string& id,
     87                                        const std::string& local_name,
     88                                        const std::string& content_type) {
     89   std::string sid =
     90       id.empty() ? talk_base::ToString(talk_base::CreateRandomId64()) : id;
     91   return CreateSession(local_name, local_name, sid, content_type, false);
     92 }
     93 
     94 Session* SessionManager::CreateSession(
     95     const std::string& local_name, const std::string& initiator_name,
     96     const std::string& sid, const std::string& content_type,
     97     bool received_initiate) {
     98   SessionClient* client = GetClient(content_type);
     99   ASSERT(client != NULL);
    100 
    101   Session* session = new Session(this, local_name, initiator_name,
    102                                  sid, content_type, client);
    103   session->SetIdentity(transport_desc_factory_.identity());
    104   session_map_[session->id()] = session;
    105   session->SignalRequestSignaling.connect(
    106       this, &SessionManager::OnRequestSignaling);
    107   session->SignalOutgoingMessage.connect(
    108       this, &SessionManager::OnOutgoingMessage);
    109   session->SignalErrorMessage.connect(this, &SessionManager::OnErrorMessage);
    110   SignalSessionCreate(session, received_initiate);
    111   session->client()->OnSessionCreate(session, received_initiate);
    112   return session;
    113 }
    114 
    115 void SessionManager::DestroySession(Session* session) {
    116   if (session != NULL) {
    117     SessionMap::iterator it = session_map_.find(session->id());
    118     if (it != session_map_.end()) {
    119       SignalSessionDestroy(session);
    120       session->client()->OnSessionDestroy(session);
    121       session_map_.erase(it);
    122       delete session;
    123     }
    124   }
    125 }
    126 
    127 Session* SessionManager::GetSession(const std::string& sid) {
    128   SessionMap::iterator it = session_map_.find(sid);
    129   if (it != session_map_.end())
    130     return it->second;
    131   return NULL;
    132 }
    133 
    134 void SessionManager::TerminateAll() {
    135   while (session_map_.begin() != session_map_.end()) {
    136     Session* session = session_map_.begin()->second;
    137     session->Terminate();
    138   }
    139 }
    140 
    141 bool SessionManager::IsSessionMessage(const buzz::XmlElement* stanza) {
    142   return cricket::IsSessionMessage(stanza);
    143 }
    144 
    145 Session* SessionManager::FindSession(const std::string& sid,
    146                                      const std::string& remote_name) {
    147   SessionMap::iterator iter = session_map_.find(sid);
    148   if (iter == session_map_.end())
    149     return NULL;
    150 
    151   Session* session = iter->second;
    152   if (buzz::Jid(remote_name) != buzz::Jid(session->remote_name()))
    153     return NULL;
    154 
    155   return session;
    156 }
    157 
    158 void SessionManager::OnIncomingMessage(const buzz::XmlElement* stanza) {
    159   SessionMessage msg;
    160   ParseError error;
    161 
    162   if (!ParseSessionMessage(stanza, &msg, &error)) {
    163     SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
    164                      error.text, NULL);
    165     return;
    166   }
    167 
    168   Session* session = FindSession(msg.sid, msg.from);
    169   if (session) {
    170     session->OnIncomingMessage(msg);
    171     return;
    172   }
    173   if (msg.type != ACTION_SESSION_INITIATE) {
    174     SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
    175                      "unknown session", NULL);
    176     return;
    177   }
    178 
    179   std::string content_type;
    180   if (!ParseContentType(msg.protocol, msg.action_elem,
    181                         &content_type, &error)) {
    182     SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
    183                      error.text, NULL);
    184     return;
    185   }
    186 
    187   if (!GetClient(content_type)) {
    188     SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
    189                      "unknown content type: " + content_type, NULL);
    190     return;
    191   }
    192 
    193   session = CreateSession(msg.to, msg.initiator, msg.sid,
    194                           content_type, true);
    195   session->OnIncomingMessage(msg);
    196 }
    197 
    198 void SessionManager::OnIncomingResponse(const buzz::XmlElement* orig_stanza,
    199     const buzz::XmlElement* response_stanza) {
    200   if (orig_stanza == NULL || response_stanza == NULL) {
    201     return;
    202   }
    203 
    204   SessionMessage msg;
    205   ParseError error;
    206   if (!ParseSessionMessage(orig_stanza, &msg, &error)) {
    207     LOG(LS_WARNING) << "Error parsing incoming response: " << error.text
    208                     << ":" << orig_stanza;
    209     return;
    210   }
    211 
    212   Session* session = FindSession(msg.sid, msg.to);
    213   if (session) {
    214     session->OnIncomingResponse(orig_stanza, response_stanza, msg);
    215   }
    216 }
    217 
    218 void SessionManager::OnFailedSend(const buzz::XmlElement* orig_stanza,
    219                                   const buzz::XmlElement* error_stanza) {
    220   SessionMessage msg;
    221   ParseError error;
    222   if (!ParseSessionMessage(orig_stanza, &msg, &error)) {
    223     return;  // TODO: log somewhere?
    224   }
    225 
    226   Session* session = FindSession(msg.sid, msg.to);
    227   if (session) {
    228     talk_base::scoped_ptr<buzz::XmlElement> synthetic_error;
    229     if (!error_stanza) {
    230       // A failed send is semantically equivalent to an error response, so we
    231       // can just turn the former into the latter.
    232       synthetic_error.reset(
    233         CreateErrorMessage(orig_stanza, buzz::QN_STANZA_ITEM_NOT_FOUND,
    234                            "cancel", "Recipient did not respond", NULL));
    235       error_stanza = synthetic_error.get();
    236     }
    237 
    238     session->OnFailedSend(orig_stanza, error_stanza);
    239   }
    240 }
    241 
    242 void SessionManager::SendErrorMessage(const buzz::XmlElement* stanza,
    243                                       const buzz::QName& name,
    244                                       const std::string& type,
    245                                       const std::string& text,
    246                                       const buzz::XmlElement* extra_info) {
    247   talk_base::scoped_ptr<buzz::XmlElement> msg(
    248       CreateErrorMessage(stanza, name, type, text, extra_info));
    249   SignalOutgoingMessage(this, msg.get());
    250 }
    251 
    252 buzz::XmlElement* SessionManager::CreateErrorMessage(
    253     const buzz::XmlElement* stanza,
    254     const buzz::QName& name,
    255     const std::string& type,
    256     const std::string& text,
    257     const buzz::XmlElement* extra_info) {
    258   buzz::XmlElement* iq = new buzz::XmlElement(buzz::QN_IQ);
    259   iq->SetAttr(buzz::QN_TO, stanza->Attr(buzz::QN_FROM));
    260   iq->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID));
    261   iq->SetAttr(buzz::QN_TYPE, "error");
    262 
    263   CopyXmlChildren(stanza, iq);
    264 
    265   buzz::XmlElement* error = new buzz::XmlElement(buzz::QN_ERROR);
    266   error->SetAttr(buzz::QN_TYPE, type);
    267   iq->AddElement(error);
    268 
    269   // If the error name is not in the standard namespace, we have to first add
    270   // some error from that namespace.
    271   if (name.Namespace() != buzz::NS_STANZA) {
    272      error->AddElement(
    273          new buzz::XmlElement(buzz::QN_STANZA_UNDEFINED_CONDITION));
    274   }
    275   error->AddElement(new buzz::XmlElement(name));
    276 
    277   if (extra_info)
    278     error->AddElement(new buzz::XmlElement(*extra_info));
    279 
    280   if (text.size() > 0) {
    281     // It's okay to always use English here.  This text is for debugging
    282     // purposes only.
    283     buzz::XmlElement* text_elem = new buzz::XmlElement(buzz::QN_STANZA_TEXT);
    284     text_elem->SetAttr(buzz::QN_XML_LANG, "en");
    285     text_elem->SetBodyText(text);
    286     error->AddElement(text_elem);
    287   }
    288 
    289   // TODO: Should we include error codes as well for SIP compatibility?
    290 
    291   return iq;
    292 }
    293 
    294 void SessionManager::OnOutgoingMessage(Session* session,
    295                                        const buzz::XmlElement* stanza) {
    296   SignalOutgoingMessage(this, stanza);
    297 }
    298 
    299 void SessionManager::OnErrorMessage(BaseSession* session,
    300                                     const buzz::XmlElement* stanza,
    301                                     const buzz::QName& name,
    302                                     const std::string& type,
    303                                     const std::string& text,
    304                                     const buzz::XmlElement* extra_info) {
    305   SendErrorMessage(stanza, name, type, text, extra_info);
    306 }
    307 
    308 void SessionManager::OnSignalingReady() {
    309   for (SessionMap::iterator it = session_map_.begin();
    310       it != session_map_.end();
    311       ++it) {
    312     it->second->OnSignalingReady();
    313   }
    314 }
    315 
    316 void SessionManager::OnRequestSignaling(Session* session) {
    317   SignalRequestSignaling();
    318 }
    319 
    320 }  // namespace cricket
    321