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 "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