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/stringencode.h"
     33 #include "talk/p2p/base/constants.h"
     34 #include "talk/p2p/base/session.h"
     35 #include "talk/p2p/base/sessionmessages.h"
     36 #include "talk/xmpp/constants.h"
     37 #include "talk/xmpp/jid.h"
     38 
     39 namespace cricket {
     40 
     41 SessionManager::SessionManager(PortAllocator *allocator,
     42                                talk_base::Thread *worker) {
     43   allocator_ = allocator;
     44   signaling_thread_ = talk_base::Thread::Current();
     45   if (worker == NULL) {
     46     worker_thread_ = talk_base::Thread::Current();
     47   } else {
     48     worker_thread_ = worker;
     49   }
     50   timeout_ = 50;
     51 }
     52 
     53 SessionManager::~SessionManager() {
     54   // Note: Session::Terminate occurs asynchronously, so it's too late to
     55   // delete them now.  They better be all gone.
     56   ASSERT(session_map_.empty());
     57   // TerminateAll();
     58 }
     59 
     60 void SessionManager::AddClient(const std::string& content_type,
     61                                SessionClient* client) {
     62   ASSERT(client_map_.find(content_type) == client_map_.end());
     63   client_map_[content_type] = client;
     64 }
     65 
     66 void SessionManager::RemoveClient(const std::string& content_type) {
     67   ClientMap::iterator iter = client_map_.find(content_type);
     68   ASSERT(iter != client_map_.end());
     69   client_map_.erase(iter);
     70 }
     71 
     72 SessionClient* SessionManager::GetClient(const std::string& content_type) {
     73   ClientMap::iterator iter = client_map_.find(content_type);
     74   return (iter != client_map_.end()) ? iter->second : NULL;
     75 }
     76 
     77 Session* SessionManager::CreateSession(const std::string& local_name,
     78                                        const std::string& content_type) {
     79   return CreateSession(local_name, local_name,
     80                        talk_base::ToString(talk_base::CreateRandomId()),
     81                        content_type, false);
     82 }
     83 
     84 Session* SessionManager::CreateSession(
     85     const std::string& local_name, const std::string& initiator_name,
     86     const std::string& sid, const std::string& content_type,
     87     bool received_initiate) {
     88   SessionClient* client = GetClient(content_type);
     89   ASSERT(client != NULL);
     90 
     91   Session* session = new Session(this, local_name, initiator_name,
     92                                  sid, content_type, client);
     93   session_map_[session->id()] = session;
     94   session->SignalRequestSignaling.connect(
     95       this, &SessionManager::OnRequestSignaling);
     96   session->SignalOutgoingMessage.connect(
     97       this, &SessionManager::OnOutgoingMessage);
     98   session->SignalErrorMessage.connect(this, &SessionManager::OnErrorMessage);
     99   SignalSessionCreate(session, received_initiate);
    100   session->client()->OnSessionCreate(session, received_initiate);
    101   return session;
    102 }
    103 
    104 void SessionManager::DestroySession(Session* session) {
    105   if (session != NULL) {
    106     SessionMap::iterator it = session_map_.find(session->id());
    107     if (it != session_map_.end()) {
    108       SignalSessionDestroy(session);
    109       session->client()->OnSessionDestroy(session);
    110       session_map_.erase(it);
    111       delete session;
    112     }
    113   }
    114 }
    115 
    116 Session* SessionManager::GetSession(const std::string& sid) {
    117   SessionMap::iterator it = session_map_.find(sid);
    118   if (it != session_map_.end())
    119     return it->second;
    120   return NULL;
    121 }
    122 
    123 void SessionManager::TerminateAll() {
    124   while (session_map_.begin() != session_map_.end()) {
    125     Session* session = session_map_.begin()->second;
    126     session->Terminate();
    127   }
    128 }
    129 
    130 bool SessionManager::IsSessionMessage(const buzz::XmlElement* stanza) {
    131   return cricket::IsSessionMessage(stanza);
    132 }
    133 
    134 Session* SessionManager::FindSession(const std::string& sid,
    135                                      const std::string& remote_name) {
    136   SessionMap::iterator iter = session_map_.find(sid);
    137   if (iter == session_map_.end())
    138     return NULL;
    139 
    140   Session* session = iter->second;
    141   if (buzz::Jid(remote_name) != buzz::Jid(session->remote_name()))
    142     return NULL;
    143 
    144   return session;
    145 }
    146 
    147 void SessionManager::OnIncomingMessage(const buzz::XmlElement* stanza) {
    148   SessionMessage msg;
    149   ParseError error;
    150 
    151   if (!ParseSessionMessage(stanza, &msg, &error)) {
    152     SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
    153                      error.text, NULL);
    154     return;
    155   }
    156 
    157   Session* session = FindSession(msg.sid, msg.from);
    158   if (session) {
    159     session->OnIncomingMessage(msg);
    160     return;
    161   }
    162   if (msg.type != ACTION_SESSION_INITIATE) {
    163     SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
    164                      "unknown session", NULL);
    165     return;
    166   }
    167 
    168   std::string content_type;
    169   if (!ParseContentType(msg.protocol, msg.action_elem,
    170                         &content_type, &error)) {
    171     SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
    172                      error.text, NULL);
    173     return;
    174   }
    175 
    176   if (!GetClient(content_type)) {
    177     SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify",
    178                      "unknown content type: " + content_type, NULL);
    179     return;
    180   }
    181 
    182   session = CreateSession(msg.to, msg.initiator, msg.sid,
    183                           content_type, true);
    184   session->OnIncomingMessage(msg);
    185 }
    186 
    187 void SessionManager::OnIncomingResponse(const buzz::XmlElement* orig_stanza,
    188     const buzz::XmlElement* response_stanza) {
    189   // We don't do anything with the response now.  If we need to we can forward
    190   // it to the session.
    191   return;
    192 }
    193 
    194 void SessionManager::OnFailedSend(const buzz::XmlElement* orig_stanza,
    195                                   const buzz::XmlElement* error_stanza) {
    196   SessionMessage msg;
    197   ParseError error;
    198   if (!ParseSessionMessage(orig_stanza, &msg, &error)) {
    199     return;  // TODO: log somewhere?
    200   }
    201 
    202   Session* session = FindSession(msg.sid, msg.to);
    203   if (session) {
    204     talk_base::scoped_ptr<buzz::XmlElement> synthetic_error;
    205     if (!error_stanza) {
    206       // A failed send is semantically equivalent to an error response, so we
    207       // can just turn the former into the latter.
    208       synthetic_error.reset(
    209         CreateErrorMessage(orig_stanza, buzz::QN_STANZA_ITEM_NOT_FOUND,
    210                            "cancel", "Recipient did not respond", NULL));
    211       error_stanza = synthetic_error.get();
    212     }
    213 
    214     session->OnFailedSend(orig_stanza, error_stanza);
    215   }
    216 }
    217 
    218 void SessionManager::SendErrorMessage(const buzz::XmlElement* stanza,
    219                                       const buzz::QName& name,
    220                                       const std::string& type,
    221                                       const std::string& text,
    222                                       const buzz::XmlElement* extra_info) {
    223   talk_base::scoped_ptr<buzz::XmlElement> msg(
    224       CreateErrorMessage(stanza, name, type, text, extra_info));
    225   SignalOutgoingMessage(this, msg.get());
    226 }
    227 
    228 buzz::XmlElement* SessionManager::CreateErrorMessage(
    229     const buzz::XmlElement* stanza,
    230     const buzz::QName& name,
    231     const std::string& type,
    232     const std::string& text,
    233     const buzz::XmlElement* extra_info) {
    234   buzz::XmlElement* iq = new buzz::XmlElement(buzz::QN_IQ);
    235   iq->SetAttr(buzz::QN_TO, stanza->Attr(buzz::QN_FROM));
    236   iq->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID));
    237   iq->SetAttr(buzz::QN_TYPE, "error");
    238 
    239   CopyXmlChildren(stanza, iq);
    240 
    241   buzz::XmlElement* error = new buzz::XmlElement(buzz::QN_ERROR);
    242   error->SetAttr(buzz::QN_TYPE, type);
    243   iq->AddElement(error);
    244 
    245   // If the error name is not in the standard namespace, we have to first add
    246   // some error from that namespace.
    247   if (name.Namespace() != buzz::NS_STANZA) {
    248      error->AddElement(
    249          new buzz::XmlElement(buzz::QN_STANZA_UNDEFINED_CONDITION));
    250   }
    251   error->AddElement(new buzz::XmlElement(name));
    252 
    253   if (extra_info)
    254     error->AddElement(new buzz::XmlElement(*extra_info));
    255 
    256   if (text.size() > 0) {
    257     // It's okay to always use English here.  This text is for debugging
    258     // purposes only.
    259     buzz::XmlElement* text_elem = new buzz::XmlElement(buzz::QN_STANZA_TEXT);
    260     text_elem->SetAttr(buzz::QN_XML_LANG, "en");
    261     text_elem->SetBodyText(text);
    262     error->AddElement(text_elem);
    263   }
    264 
    265   // TODO: Should we include error codes as well for SIP compatibility?
    266 
    267   return iq;
    268 }
    269 
    270 void SessionManager::OnOutgoingMessage(Session* session,
    271                                        const buzz::XmlElement* stanza) {
    272   SignalOutgoingMessage(this, stanza);
    273 }
    274 
    275 void SessionManager::OnErrorMessage(BaseSession* session,
    276                                     const buzz::XmlElement* stanza,
    277                                     const buzz::QName& name,
    278                                     const std::string& type,
    279                                     const std::string& text,
    280                                     const buzz::XmlElement* extra_info) {
    281   SendErrorMessage(stanza, name, type, text, extra_info);
    282 }
    283 
    284 void SessionManager::OnSignalingReady() {
    285   for (SessionMap::iterator it = session_map_.begin();
    286       it != session_map_.end();
    287       ++it) {
    288     it->second->OnSignalingReady();
    289   }
    290 }
    291 
    292 void SessionManager::OnRequestSignaling(Session* session) {
    293   SignalRequestSignaling();
    294 }
    295 
    296 }  // namespace cricket
    297