Home | History | Annotate | Download | only in xmpp
      1 /*
      2  *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include <algorithm>
     12 #include <vector>
     13 #include "webrtc/libjingle/xmpp/constants.h"
     14 #include "webrtc/libjingle/xmpp/xmppengineimpl.h"
     15 #include "webrtc/base/common.h"
     16 
     17 namespace buzz {
     18 
     19 class XmppIqEntry {
     20   XmppIqEntry(const std::string & id, const std::string & to,
     21                XmppEngine * pxce, XmppIqHandler * iq_handler) :
     22     id_(id),
     23     to_(to),
     24     engine_(pxce),
     25     iq_handler_(iq_handler) {
     26   }
     27 
     28 private:
     29   friend class XmppEngineImpl;
     30 
     31   const std::string id_;
     32   const std::string to_;
     33   XmppEngine * const engine_;
     34   XmppIqHandler * const iq_handler_;
     35 };
     36 
     37 
     38 XmppReturnStatus
     39 XmppEngineImpl::SendIq(const XmlElement * element, XmppIqHandler * iq_handler,
     40   XmppIqCookie* cookie) {
     41   if (state_ == STATE_CLOSED)
     42     return XMPP_RETURN_BADSTATE;
     43   if (NULL == iq_handler)
     44     return XMPP_RETURN_BADARGUMENT;
     45   if (!element || element->Name() != QN_IQ)
     46     return XMPP_RETURN_BADARGUMENT;
     47 
     48   const std::string& type = element->Attr(QN_TYPE);
     49   if (type != "get" && type != "set")
     50     return XMPP_RETURN_BADARGUMENT;
     51 
     52   if (!element->HasAttr(QN_ID))
     53     return XMPP_RETURN_BADARGUMENT;
     54   const std::string& id = element->Attr(QN_ID);
     55 
     56   XmppIqEntry * iq_entry = new XmppIqEntry(id,
     57                                               element->Attr(QN_TO),
     58                                               this, iq_handler);
     59   iq_entries_->push_back(iq_entry);
     60   SendStanza(element);
     61 
     62   if (cookie)
     63     *cookie = iq_entry;
     64 
     65   return XMPP_RETURN_OK;
     66 }
     67 
     68 
     69 XmppReturnStatus
     70 XmppEngineImpl::RemoveIqHandler(XmppIqCookie cookie,
     71     XmppIqHandler ** iq_handler) {
     72 
     73   std::vector<XmppIqEntry*, std::allocator<XmppIqEntry*> >::iterator pos;
     74 
     75   pos = std::find(iq_entries_->begin(),
     76                   iq_entries_->end(),
     77                   reinterpret_cast<XmppIqEntry*>(cookie));
     78 
     79   if (pos == iq_entries_->end())
     80     return XMPP_RETURN_BADARGUMENT;
     81 
     82   XmppIqEntry* entry = *pos;
     83   iq_entries_->erase(pos);
     84   if (iq_handler)
     85     *iq_handler = entry->iq_handler_;
     86   delete entry;
     87 
     88   return XMPP_RETURN_OK;
     89 }
     90 
     91 void
     92 XmppEngineImpl::DeleteIqCookies() {
     93   for (size_t i = 0; i < iq_entries_->size(); i += 1) {
     94     XmppIqEntry * iq_entry_ = (*iq_entries_)[i];
     95     (*iq_entries_)[i] = NULL;
     96     delete iq_entry_;
     97   }
     98   iq_entries_->clear();
     99 }
    100 
    101 static void
    102 AecImpl(XmlElement * error_element, const QName & name,
    103         const char * type, const char * code) {
    104   error_element->AddElement(new XmlElement(QN_ERROR));
    105   error_element->AddAttr(QN_CODE, code, 1);
    106   error_element->AddAttr(QN_TYPE, type, 1);
    107   error_element->AddElement(new XmlElement(name, true), 1);
    108 }
    109 
    110 
    111 static void
    112 AddErrorCode(XmlElement * error_element, XmppStanzaError code) {
    113   switch (code) {
    114     case XSE_BAD_REQUEST:
    115       AecImpl(error_element, QN_STANZA_BAD_REQUEST, "modify", "400");
    116       break;
    117     case XSE_CONFLICT:
    118       AecImpl(error_element, QN_STANZA_CONFLICT, "cancel", "409");
    119       break;
    120     case XSE_FEATURE_NOT_IMPLEMENTED:
    121       AecImpl(error_element, QN_STANZA_FEATURE_NOT_IMPLEMENTED,
    122               "cancel", "501");
    123       break;
    124     case XSE_FORBIDDEN:
    125       AecImpl(error_element, QN_STANZA_FORBIDDEN, "auth", "403");
    126       break;
    127     case XSE_GONE:
    128       AecImpl(error_element, QN_STANZA_GONE, "modify", "302");
    129       break;
    130     case XSE_INTERNAL_SERVER_ERROR:
    131       AecImpl(error_element, QN_STANZA_INTERNAL_SERVER_ERROR, "wait", "500");
    132       break;
    133     case XSE_ITEM_NOT_FOUND:
    134       AecImpl(error_element, QN_STANZA_ITEM_NOT_FOUND, "cancel", "404");
    135       break;
    136     case XSE_JID_MALFORMED:
    137       AecImpl(error_element, QN_STANZA_JID_MALFORMED, "modify", "400");
    138       break;
    139     case XSE_NOT_ACCEPTABLE:
    140       AecImpl(error_element, QN_STANZA_NOT_ACCEPTABLE, "cancel", "406");
    141       break;
    142     case XSE_NOT_ALLOWED:
    143       AecImpl(error_element, QN_STANZA_NOT_ALLOWED, "cancel", "405");
    144       break;
    145     case XSE_PAYMENT_REQUIRED:
    146       AecImpl(error_element, QN_STANZA_PAYMENT_REQUIRED, "auth", "402");
    147       break;
    148     case XSE_RECIPIENT_UNAVAILABLE:
    149       AecImpl(error_element, QN_STANZA_RECIPIENT_UNAVAILABLE, "wait", "404");
    150       break;
    151     case XSE_REDIRECT:
    152       AecImpl(error_element, QN_STANZA_REDIRECT, "modify", "302");
    153       break;
    154     case XSE_REGISTRATION_REQUIRED:
    155       AecImpl(error_element, QN_STANZA_REGISTRATION_REQUIRED, "auth", "407");
    156       break;
    157     case XSE_SERVER_NOT_FOUND:
    158       AecImpl(error_element, QN_STANZA_REMOTE_SERVER_NOT_FOUND,
    159               "cancel", "404");
    160       break;
    161     case XSE_SERVER_TIMEOUT:
    162       AecImpl(error_element, QN_STANZA_REMOTE_SERVER_TIMEOUT, "wait", "502");
    163       break;
    164     case XSE_RESOURCE_CONSTRAINT:
    165       AecImpl(error_element, QN_STANZA_RESOURCE_CONSTRAINT, "wait", "500");
    166       break;
    167     case XSE_SERVICE_UNAVAILABLE:
    168       AecImpl(error_element, QN_STANZA_SERVICE_UNAVAILABLE, "cancel", "503");
    169       break;
    170     case XSE_SUBSCRIPTION_REQUIRED:
    171       AecImpl(error_element, QN_STANZA_SUBSCRIPTION_REQUIRED, "auth", "407");
    172       break;
    173     case XSE_UNDEFINED_CONDITION:
    174       AecImpl(error_element, QN_STANZA_UNDEFINED_CONDITION, "wait", "500");
    175       break;
    176     case XSE_UNEXPECTED_REQUEST:
    177       AecImpl(error_element, QN_STANZA_UNEXPECTED_REQUEST, "wait", "400");
    178       break;
    179   }
    180 }
    181 
    182 
    183 XmppReturnStatus
    184 XmppEngineImpl::SendStanzaError(const XmlElement * element_original,
    185                                 XmppStanzaError code,
    186                                 const std::string & text) {
    187 
    188   if (state_ == STATE_CLOSED)
    189     return XMPP_RETURN_BADSTATE;
    190 
    191   XmlElement error_element(element_original->Name());
    192   error_element.AddAttr(QN_TYPE, "error");
    193 
    194   // copy attrs, copy 'from' to 'to' and strip 'from'
    195   for (const XmlAttr * attribute = element_original->FirstAttr();
    196        attribute; attribute = attribute->NextAttr()) {
    197     QName name = attribute->Name();
    198     if (name == QN_TO)
    199       continue; // no need to put a from attr.  Server will stamp stanza
    200     else if (name == QN_FROM)
    201       name = QN_TO;
    202     else if (name == QN_TYPE)
    203       continue;
    204     error_element.AddAttr(name, attribute->Value());
    205   }
    206 
    207   // copy children
    208   for (const XmlChild * child = element_original->FirstChild();
    209        child;
    210        child = child->NextChild()) {
    211     if (child->IsText()) {
    212       error_element.AddText(child->AsText()->Text());
    213     } else {
    214       error_element.AddElement(new XmlElement(*(child->AsElement())));
    215     }
    216   }
    217 
    218   // add error information
    219   AddErrorCode(&error_element, code);
    220   if (text != STR_EMPTY) {
    221     XmlElement * text_element = new XmlElement(QN_STANZA_TEXT, true);
    222     text_element->AddText(text);
    223     error_element.AddElement(text_element);
    224   }
    225 
    226   SendStanza(&error_element);
    227 
    228   return XMPP_RETURN_OK;
    229 }
    230 
    231 
    232 bool
    233 XmppEngineImpl::HandleIqResponse(const XmlElement * element) {
    234   if (iq_entries_->empty())
    235     return false;
    236   if (element->Name() != QN_IQ)
    237     return false;
    238   std::string type = element->Attr(QN_TYPE);
    239   if (type != "result" && type != "error")
    240     return false;
    241   if (!element->HasAttr(QN_ID))
    242     return false;
    243   std::string id = element->Attr(QN_ID);
    244   std::string from = element->Attr(QN_FROM);
    245 
    246   for (std::vector<XmppIqEntry *>::iterator it = iq_entries_->begin();
    247        it != iq_entries_->end(); it += 1) {
    248     XmppIqEntry * iq_entry = *it;
    249     if (iq_entry->id_ == id && iq_entry->to_ == from) {
    250       iq_entries_->erase(it);
    251       iq_entry->iq_handler_->IqResponse(iq_entry, element);
    252       delete iq_entry;
    253       return true;
    254     }
    255   }
    256 
    257   return false;
    258 }
    259 
    260 }
    261