📄 jid.java
字号:
/**
* Copyright (C) 2004-2007 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution, or a commercial license
* agreement with Jive.
*/
package org.xmpp.packet;
import org.jivesoftware.stringprep.IDNA;
import org.jivesoftware.stringprep.Stringprep;
import org.jivesoftware.stringprep.StringprepException;
import org.jivesoftware.util.cache.ExternalizableUtil;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* An XMPP address (JID). A JID is made up of a node (generally a username), a domain,
* and a resource. The node and resource are optional; domain is required. In simple
* ABNF form:
*
* <ul><tt>jid = [ node "@" ] domain [ "/" resource ]</tt></ul>
*
* Some sample JID's:
* <ul>
* <li><tt>user@example.com</tt></li>
* <li><tt>user@example.com/home</tt></li>
* <li><tt>example.com</tt></li>
* </ul>
*
* Each allowable portion of a JID (node, domain, and resource) must not be more
* than 1023 bytes in length, resulting in a maximum total size (including the '@'
* and '/' separators) of 3071 bytes.
*
* @author Matt Tucker
*/
public class JID implements Comparable<JID>, Externalizable {
// Stringprep operations are very expensive. Therefore, we cache node, domain and
// resource values that have already had stringprep applied so that we can check
// incoming values against the cache.
private static Map<String,Object> stringprepCache = Collections.synchronizedMap(new Cache(10000));
private String node;
private String domain;
private String resource;
private String cachedFullJID;
private String cachedBareJID;
/**
* Escapes the node portion of a JID according to "JID Escaping" (JEP-0106).
* Escaping replaces characters prohibited by node-prep with escape sequences,
* as follows:<p>
*
* <table border="1">
* <tr><td><b>Unescaped Character</b></td><td><b>Encoded Sequence</b></td></tr>
* <tr><td><space></td><td>\20</td></tr>
* <tr><td>"</td><td>\22</td></tr>
* <tr><td>&</td><td>\26</td></tr>
* <tr><td>'</td><td>\27</td></tr>
* <tr><td>/</td><td>\2f</td></tr>
* <tr><td>:</td><td>\3a</td></tr>
* <tr><td><</td><td>\3c</td></tr>
* <tr><td>></td><td>\3e</td></tr>
* <tr><td>@</td><td>\40</td></tr>
* <tr><td>\</td><td>\5c</td></tr>
* </table><p>
*
* This process is useful when the node comes from an external source that doesn't
* conform to nodeprep. For example, a username in LDAP may be "Joe Smith". Because
* the <space> character isn't a valid part of a node, the username should
* be escaped to "Joe\20Smith" before being made into a JID (e.g. "joe\20smith@example.com"
* after case-folding, etc. has been applied).<p>
*
* All node escaping and un-escaping must be performed manually at the appropriate
* time; the JID class will not escape or un-escape automatically.
*
* @param node the node.
* @return the escaped version of the node.
*/
public static String escapeNode(String node) {
if (node == null) {
return null;
}
StringBuilder buf = new StringBuilder(node.length() + 8);
for (int i=0, n=node.length(); i<n; i++) {
char c = node.charAt(i);
switch (c) {
case '"': buf.append("\\22"); break;
case '&': buf.append("\\26"); break;
case '\'': buf.append("\\27"); break;
case '/': buf.append("\\2f"); break;
case ':': buf.append("\\3a"); break;
case '<': buf.append("\\3c"); break;
case '>': buf.append("\\3e"); break;
case '@': buf.append("\\40"); break;
case '\\': buf.append("\\5c"); break;
default: {
if (Character.isWhitespace(c)) {
buf.append("\\20");
}
else {
buf.append(c);
}
}
}
}
return buf.toString();
}
/**
* Un-escapes the node portion of a JID according to "JID Escaping" (JEP-0106).<p>
* Escaping replaces characters prohibited by node-prep with escape sequences,
* as follows:<p>
*
* <table border="1">
* <tr><td><b>Unescaped Character</b></td><td><b>Encoded Sequence</b></td></tr>
* <tr><td><space></td><td>\20</td></tr>
* <tr><td>"</td><td>\22</td></tr>
* <tr><td>&</td><td>\26</td></tr>
* <tr><td>'</td><td>\27</td></tr>
* <tr><td>/</td><td>\2f</td></tr>
* <tr><td>:</td><td>\3a</td></tr>
* <tr><td><</td><td>\3c</td></tr>
* <tr><td>></td><td>\3e</td></tr>
* <tr><td>@</td><td>\40</td></tr>
* <tr><td>\</td><td>\5c</td></tr>
* </table><p>
*
* This process is useful when the node comes from an external source that doesn't
* conform to nodeprep. For example, a username in LDAP may be "Joe Smith". Because
* the <space> character isn't a valid part of a node, the username should
* be escaped to "Joe\20Smith" before being made into a JID (e.g. "joe\20smith@example.com"
* after case-folding, etc. has been applied).<p>
*
* All node escaping and un-escaping must be performed manually at the appropriate
* time; the JID class will not escape or un-escape automatically.
*
* @param node the escaped version of the node.
* @return the un-escaped version of the node.
*/
public static String unescapeNode(String node) {
if (node == null) {
return null;
}
char [] nodeChars = node.toCharArray();
StringBuilder buf = new StringBuilder(nodeChars.length);
for (int i=0, n=nodeChars.length; i<n; i++) {
compare: {
char c = node.charAt(i);
if (c == '\\' && i+2<n) {
char c2 = nodeChars[i+1];
char c3 = nodeChars[i+2];
if (c2 == '2') {
switch (c3) {
case '0': buf.append(' '); i+=2; break compare;
case '2': buf.append('"'); i+=2; break compare;
case '6': buf.append('&'); i+=2; break compare;
case '7': buf.append('\''); i+=2; break compare;
case 'f': buf.append('/'); i+=2; break compare;
}
}
else if (c2 == '3') {
switch (c3) {
case 'a': buf.append(':'); i+=2; break compare;
case 'c': buf.append('<'); i+=2; break compare;
case 'e': buf.append('>'); i+=2; break compare;
}
}
else if (c2 == '4') {
if (c3 == '0') {
buf.append("@");
i+=2;
break compare;
}
}
else if (c2 == '5') {
if (c3 == 'c') {
buf.append("\\");
i+=2;
break compare;
}
}
}
buf.append(c);
}
}
return buf.toString();
}
public static String resourceprep(String resource) throws StringprepException {
String answer = resource;
if (!stringprepCache.containsKey(resource)) {
answer = Stringprep.resourceprep(resource);
// Validate field is not greater than 1023 bytes. UTF-8 characters use two bytes.
if (answer != null && answer.length()*2 > 1023) {
return answer;
}
stringprepCache.put(answer, null);
}
return answer;
}
/**
* Constructor added for Externalizable. Do not use this constructor.
*/
public JID() {
}
/**
* Constructs a JID from it's String representation.
*
* @param jid a valid JID.
* @throws IllegalArgumentException if the JID is not valid.
*/
public JID(String jid) {
if (jid == null) {
throw new NullPointerException("JID cannot be null");
}
String[] parts = getParts(jid);
init(parts[0], parts[1], parts[2]);
}
/**
* Constructs a JID given a node, domain, and resource.
*
* @param node the node.
* @param domain the domain, which must not be <tt>null</tt>.
* @param resource the resource.
* @throws IllegalArgumentException if the JID is not valid.
*/
public JID(String node, String domain, String resource) {
if (domain == null) {
throw new NullPointerException("Domain cannot be null");
}
init(node, domain, resource);
}
/**
* Constructs a JID given a node, domain, and resource being able to specify if stringprep
* should be applied or not.
*
* @param node the node.
* @param domain the domain, which must not be <tt>null</tt>.
* @param resource the resource.
* @param skipStringprep true if stringprep should not be applied.
* @throws IllegalArgumentException if the JID is not valid.
*/
public JID(String node, String domain, String resource, boolean skipStringprep) {
if (domain == null) {
throw new NullPointerException("Domain cannot be null");
}
if (skipStringprep) {
this.node = node;
this.domain = domain;
this.resource = resource;
// Cache the bare and full JID String representation
updateCache();
}
else {
init(node, domain, resource);
}
}
/**
* Returns a String array with the parsed node, domain and resource.
* No Stringprep is performed while parsing the textual representation.
*
* @param jid the textual JID representation.
* @return a string array with the parsed node, domain and resource.
*/
static String[] getParts(String jid) {
String[] parts = new String[3];
String node = null , domain, resource;
if (jid == null) {
return parts;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -