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