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