• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Jid()24 Jid::Jid() {
25 }
26 
Jid(const std::string & jid_string)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 
Jid(const std::string & node_name,const std::string & domain_name,const std::string & resource_name)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 
ValidateOrReset()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 
Str() const79 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 
~Jid()97 Jid::~Jid() {
98 }
99 
IsEmpty() const100 bool Jid::IsEmpty() const {
101   return (node_name_.empty() && domain_name_.empty() &&
102           resource_name_.empty());
103 }
104 
IsValid() const105 bool Jid::IsValid() const {
106   return !domain_name_.empty();
107 }
108 
IsBare() const109 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 
IsFull() const117 bool Jid::IsFull() const {
118   return IsValid() && !resource_name_.empty();
119 }
120 
BareJid() const121 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 
BareEquals(const Jid & other) const129 bool Jid::BareEquals(const Jid& other) const {
130   return other.node_name_ == node_name_ &&
131       other.domain_name_ == domain_name_;
132 }
133 
CopyFrom(const Jid & jid)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 
operator ==(const Jid & other) const140 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 
Compare(const Jid & other) const146 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.
PrepNode(const std::string & node,bool * valid)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.
PrepNodeAscii(char ch,bool * valid)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.
PrepResource(const std::string & resource,bool * valid)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.
PrepResourceAscii(char ch,bool * valid)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.
PrepDomain(const std::string & domain,bool * valid)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.
PrepDomain(const std::string & domain,std::string * buf,bool * valid)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.
PrepDomainLabel(std::string::const_iterator start,std::string::const_iterator end,std::string * buf,bool * valid)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.
PrepDomainLabelAscii(char ch,bool * valid)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