use of org.jivesoftware.smackx.disco.packet.DiscoverInfo.Feature in project Smack by igniterealtime.
the class EntityCapsManager method generateVerificationString.
/**
* Generates a XEP-115 Verification String
*
* @see <a href="http://xmpp.org/extensions/xep-0115.html#ver">XEP-115
* Verification String</a>
*
* @param discoverInfo
* @param hash
* the used hash function, if null, default hash will be used
* @return The generated verification String or null if the hash is not
* supported
*/
protected static CapsVersionAndHash generateVerificationString(DiscoverInfo discoverInfo, String hash) {
if (hash == null) {
hash = DEFAULT_HASH;
}
// SUPPORTED_HASHES uses the format of MessageDigest, which is uppercase, e.g. "SHA-1" instead of "sha-1"
MessageDigest md = SUPPORTED_HASHES.get(hash.toUpperCase(Locale.US));
if (md == null)
return null;
// Then transform the hash to lowercase, as this value will be put on the wire within the caps element's hash
// attribute. I'm not sure if the standard is case insensitive here, but let's assume that even it is, there could
// be "broken" implementation in the wild, so we *always* transform to lowercase.
hash = hash.toLowerCase(Locale.US);
DataForm extendedInfo = DataForm.from(discoverInfo);
// 1. Initialize an empty string S ('sb' in this method).
// Use StringBuilder as we don't
StringBuilder sb = new StringBuilder();
// need thread-safe StringBuffer
// 2. Sort the service discovery identities by category and then by
// type and then by xml:lang
// (if it exists), formatted as CATEGORY '/' [TYPE] '/' [LANG] '/'
// [NAME]. Note that each slash is included even if the LANG or
// NAME is not included (in accordance with XEP-0030, the category and
// type MUST be included.
SortedSet<DiscoverInfo.Identity> sortedIdentities = new TreeSet<DiscoverInfo.Identity>();
for (DiscoverInfo.Identity i : discoverInfo.getIdentities()) sortedIdentities.add(i);
// followed by the '<' character.
for (DiscoverInfo.Identity identity : sortedIdentities) {
sb.append(identity.getCategory());
sb.append('/');
sb.append(identity.getType());
sb.append('/');
sb.append(identity.getLanguage() == null ? "" : identity.getLanguage());
sb.append('/');
sb.append(identity.getName() == null ? "" : identity.getName());
sb.append('<');
}
// 4. Sort the supported service discovery features.
SortedSet<String> features = new TreeSet<String>();
for (Feature f : discoverInfo.getFeatures()) features.add(f.getVar());
// character
for (String f : features) {
sb.append(f);
sb.append('<');
}
// see XEP-0115 5.4 step 3.6
if (extendedInfo != null && extendedInfo.hasHiddenFormTypeField()) {
synchronized (extendedInfo) {
// 6. If the service discovery information response includes
// XEP-0128 data forms, sort the forms by the FORM_TYPE (i.e.,
// by the XML character data of the <value/> element).
SortedSet<FormField> fs = new TreeSet<FormField>(new Comparator<FormField>() {
@Override
public int compare(FormField f1, FormField f2) {
return f1.getVariable().compareTo(f2.getVariable());
}
});
FormField ft = null;
for (FormField f : extendedInfo.getFields()) {
if (!f.getVariable().equals("FORM_TYPE")) {
fs.add(f);
} else {
ft = f;
}
}
// Add FORM_TYPE values
if (ft != null) {
formFieldValuesToCaps(ft.getValues(), sb);
}
// followed by the '<' character.
for (FormField f : fs) {
sb.append(f.getVariable());
sb.append('<');
formFieldValuesToCaps(f.getValues(), sb);
}
}
}
// 8. Ensure that S is encoded according to the UTF-8 encoding (RFC
// 3269).
// 9. Compute the verification string by hashing S using the algorithm
// specified in the 'hash' attribute (e.g., SHA-1 as defined in RFC
// 3174).
// The hashed data MUST be generated with binary output and
// encoded using Base64 as specified in Section 4 of RFC 4648
// (note: the Base64 output MUST NOT include whitespace and MUST set
// padding bits to zero).
byte[] bytes;
try {
bytes = sb.toString().getBytes(StringUtils.UTF8);
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
byte[] digest;
synchronized (md) {
digest = md.digest(bytes);
}
String version = Base64.encodeToString(digest);
return new CapsVersionAndHash(version, hash);
}
Aggregations