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/xmpp/jid.h" 29 30 #include <ctype.h> 31 32 #include <algorithm> 33 #include <string> 34 35 #include "talk/xmpp/constants.h" 36 #include "webrtc/base/common.h" 37 #include "webrtc/base/logging.h" 38 39 namespace buzz { 40 41 Jid::Jid() { 42 } 43 44 Jid::Jid(const std::string& jid_string) { 45 if (jid_string.empty()) 46 return; 47 48 // First find the slash and slice off that part 49 size_t slash = jid_string.find('/'); 50 resource_name_ = (slash == std::string::npos ? STR_EMPTY : 51 jid_string.substr(slash + 1)); 52 53 // Now look for the node 54 size_t at = jid_string.find('@'); 55 size_t domain_begin; 56 if (at < slash && at != std::string::npos) { 57 node_name_ = jid_string.substr(0, at); 58 domain_begin = at + 1; 59 } else { 60 domain_begin = 0; 61 } 62 63 // Now take what is left as the domain 64 size_t domain_length = (slash == std::string::npos) ? 65 (jid_string.length() - domain_begin) : (slash - domain_begin); 66 domain_name_ = jid_string.substr(domain_begin, domain_length); 67 68 ValidateOrReset(); 69 } 70 71 Jid::Jid(const std::string& node_name, 72 const std::string& domain_name, 73 const std::string& resource_name) 74 : node_name_(node_name), 75 domain_name_(domain_name), 76 resource_name_(resource_name) { 77 ValidateOrReset(); 78 } 79 80 void Jid::ValidateOrReset() { 81 bool valid_node; 82 bool valid_domain; 83 bool valid_resource; 84 85 node_name_ = PrepNode(node_name_, &valid_node); 86 domain_name_ = PrepDomain(domain_name_, &valid_domain); 87 resource_name_ = PrepResource(resource_name_, &valid_resource); 88 89 if (!valid_node || !valid_domain || !valid_resource) { 90 node_name_.clear(); 91 domain_name_.clear(); 92 resource_name_.clear(); 93 } 94 } 95 96 std::string Jid::Str() const { 97 if (!IsValid()) 98 return STR_EMPTY; 99 100 std::string ret; 101 102 if (!node_name_.empty()) 103 ret = node_name_ + "@"; 104 105 ASSERT(domain_name_ != STR_EMPTY); 106 ret += domain_name_; 107 108 if (!resource_name_.empty()) 109 ret += "/" + resource_name_; 110 111 return ret; 112 } 113 114 Jid::~Jid() { 115 } 116 117 bool Jid::IsEmpty() const { 118 return (node_name_.empty() && domain_name_.empty() && 119 resource_name_.empty()); 120 } 121 122 bool Jid::IsValid() const { 123 return !domain_name_.empty(); 124 } 125 126 bool Jid::IsBare() const { 127 if (IsEmpty()) { 128 LOG(LS_VERBOSE) << "Warning: Calling IsBare() on the empty jid."; 129 return true; 130 } 131 return IsValid() && resource_name_.empty(); 132 } 133 134 bool Jid::IsFull() const { 135 return IsValid() && !resource_name_.empty(); 136 } 137 138 Jid Jid::BareJid() const { 139 if (!IsValid()) 140 return Jid(); 141 if (!IsFull()) 142 return *this; 143 return Jid(node_name_, domain_name_, STR_EMPTY); 144 } 145 146 bool Jid::BareEquals(const Jid& other) const { 147 return other.node_name_ == node_name_ && 148 other.domain_name_ == domain_name_; 149 } 150 151 void Jid::CopyFrom(const Jid& jid) { 152 this->node_name_ = jid.node_name_; 153 this->domain_name_ = jid.domain_name_; 154 this->resource_name_ = jid.resource_name_; 155 } 156 157 bool Jid::operator==(const Jid& other) const { 158 return other.node_name_ == node_name_ && 159 other.domain_name_ == domain_name_ && 160 other.resource_name_ == resource_name_; 161 } 162 163 int Jid::Compare(const Jid& other) const { 164 int compare_result; 165 compare_result = node_name_.compare(other.node_name_); 166 if (0 != compare_result) 167 return compare_result; 168 compare_result = domain_name_.compare(other.domain_name_); 169 if (0 != compare_result) 170 return compare_result; 171 compare_result = resource_name_.compare(other.resource_name_); 172 return compare_result; 173 } 174 175 // --- JID parsing code: --- 176 177 // Checks and normalizes the node part of a JID. 178 std::string Jid::PrepNode(const std::string& node, bool* valid) { 179 *valid = false; 180 std::string result; 181 182 for (std::string::const_iterator i = node.begin(); i < node.end(); ++i) { 183 bool char_valid = true; 184 unsigned char ch = *i; 185 if (ch <= 0x7F) { 186 result += PrepNodeAscii(ch, &char_valid); 187 } 188 else { 189 // TODO: implement the correct stringprep protocol for these 190 result += tolower(ch); 191 } 192 if (!char_valid) { 193 return STR_EMPTY; 194 } 195 } 196 197 if (result.length() > 1023) { 198 return STR_EMPTY; 199 } 200 *valid = true; 201 return result; 202 } 203 204 205 // Returns the appropriate mapping for an ASCII character in a node. 206 char Jid::PrepNodeAscii(char ch, bool* valid) { 207 *valid = true; 208 switch (ch) { 209 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 210 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': 211 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': 212 case 'V': case 'W': case 'X': case 'Y': case 'Z': 213 return (char)(ch + ('a' - 'A')); 214 215 case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: 216 case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: 217 case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: 218 case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: 219 case ' ': case '&': case '/': case ':': case '<': case '>': case '@': 220 case '\"': case '\'': 221 case 0x7F: 222 *valid = false; 223 return 0; 224 225 default: 226 return ch; 227 } 228 } 229 230 231 // Checks and normalizes the resource part of a JID. 232 std::string Jid::PrepResource(const std::string& resource, bool* valid) { 233 *valid = false; 234 std::string result; 235 236 for (std::string::const_iterator i = resource.begin(); 237 i < resource.end(); ++i) { 238 bool char_valid = true; 239 unsigned char ch = *i; 240 if (ch <= 0x7F) { 241 result += PrepResourceAscii(ch, &char_valid); 242 } 243 else { 244 // TODO: implement the correct stringprep protocol for these 245 result += ch; 246 } 247 } 248 249 if (result.length() > 1023) { 250 return STR_EMPTY; 251 } 252 *valid = true; 253 return result; 254 } 255 256 // Returns the appropriate mapping for an ASCII character in a resource. 257 char Jid::PrepResourceAscii(char ch, bool* valid) { 258 *valid = true; 259 switch (ch) { 260 case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: 261 case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: 262 case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: 263 case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: 264 case 0x7F: 265 *valid = false; 266 return 0; 267 268 default: 269 return ch; 270 } 271 } 272 273 // Checks and normalizes the domain part of a JID. 274 std::string Jid::PrepDomain(const std::string& domain, bool* valid) { 275 *valid = false; 276 std::string result; 277 278 // TODO: if the domain contains a ':', then we should parse it 279 // as an IPv6 address rather than giving an error about illegal domain. 280 PrepDomain(domain, &result, valid); 281 if (!*valid) { 282 return STR_EMPTY; 283 } 284 285 if (result.length() > 1023) { 286 return STR_EMPTY; 287 } 288 *valid = true; 289 return result; 290 } 291 292 293 // Checks and normalizes an IDNA domain. 294 void Jid::PrepDomain(const std::string& domain, std::string* buf, bool* valid) { 295 *valid = false; 296 std::string::const_iterator last = domain.begin(); 297 for (std::string::const_iterator i = domain.begin(); i < domain.end(); ++i) { 298 bool label_valid = true; 299 char ch = *i; 300 switch (ch) { 301 case 0x002E: 302 #if 0 // FIX: This isn't UTF-8-aware. 303 case 0x3002: 304 case 0xFF0E: 305 case 0xFF61: 306 #endif 307 PrepDomainLabel(last, i, buf, &label_valid); 308 *buf += '.'; 309 last = i + 1; 310 break; 311 } 312 if (!label_valid) { 313 return; 314 } 315 } 316 PrepDomainLabel(last, domain.end(), buf, valid); 317 } 318 319 // Checks and normalizes a domain label. 320 void Jid::PrepDomainLabel( 321 std::string::const_iterator start, std::string::const_iterator end, 322 std::string* buf, bool* valid) { 323 *valid = false; 324 325 int start_len = static_cast<int>(buf->length()); 326 for (std::string::const_iterator i = start; i < end; ++i) { 327 bool char_valid = true; 328 unsigned char ch = *i; 329 if (ch <= 0x7F) { 330 *buf += PrepDomainLabelAscii(ch, &char_valid); 331 } 332 else { 333 // TODO: implement ToASCII for these 334 *buf += ch; 335 } 336 if (!char_valid) { 337 return; 338 } 339 } 340 341 int count = static_cast<int>(buf->length() - start_len); 342 if (count == 0) { 343 return; 344 } 345 else if (count > 63) { 346 return; 347 } 348 349 // Is this check needed? See comment in PrepDomainLabelAscii. 350 if ((*buf)[start_len] == '-') { 351 return; 352 } 353 if ((*buf)[buf->length() - 1] == '-') { 354 return; 355 } 356 *valid = true; 357 } 358 359 360 // Returns the appropriate mapping for an ASCII character in a domain label. 361 char Jid::PrepDomainLabelAscii(char ch, bool* valid) { 362 *valid = true; 363 // TODO: A literal reading of the spec seems to say that we do 364 // not need to check for these illegal characters (an "internationalized 365 // domain label" runs ToASCII with UseSTD3... set to false). But that 366 // can't be right. We should at least be checking that there are no '/' 367 // or '@' characters in the domain. Perhaps we should see what others 368 // do in this case. 369 370 switch (ch) { 371 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 372 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': 373 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': 374 case 'V': case 'W': case 'X': case 'Y': case 'Z': 375 return (char)(ch + ('a' - 'A')); 376 377 case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: 378 case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: 379 case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: 380 case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: 381 case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: 382 case 0x1E: case 0x1F: case 0x20: case 0x21: case 0x22: case 0x23: 383 case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: 384 case 0x2A: case 0x2B: case 0x2C: case 0x2E: case 0x2F: case 0x3A: 385 case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F: case 0x40: 386 case 0x5B: case 0x5C: case 0x5D: case 0x5E: case 0x5F: case 0x60: 387 case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F: 388 *valid = false; 389 return 0; 390 391 default: 392 return ch; 393 } 394 } 395 396 } // namespace buzz 397