Merge tag '2.8.4' into develop

This commit is contained in:
genofire 2020-05-21 15:19:54 +02:00
commit c3f392445f
170 changed files with 1529 additions and 2545 deletions

View File

@ -1,5 +1,10 @@
# Changelog
### Version 2.8.4
* Rework Login with certificate UI
* Add ability to pin chats on top (add to favorites)
### Version 2.8.3
* Move call icon to the left in order to keep other toolbar icons in a consistent place

View File

@ -67,7 +67,7 @@ dependencies {
implementation 'com.makeramen:roundedimageview:2.3.0'
implementation "com.wefika:flowlayout:0.4.1"
implementation 'net.ypresto.androidtranscoder:android-transcoder:0.3.0'
implementation project(':libs:xmpp-addr')
implementation 'org.jxmpp:jxmpp-jid:0.6.4'
implementation 'org.osmdroid:osmdroid-android:6.1.5'
implementation 'org.hsluv:hsluv:0.2'
implementation 'org.conscrypt:conscrypt-android:2.2.1'
@ -96,8 +96,8 @@ android {
defaultConfig {
minSdkVersion 16
targetSdkVersion 25
versionCode 383
versionName "2.8.3"
versionCode 387
versionName "2.8.4"
archivesBaseName += "-$versionName"
applicationId "eu.sum7.conversations"
resValue "string", "applicationId", applicationId

View File

@ -1 +0,0 @@
/build

View File

@ -1,14 +0,0 @@
apply plugin: 'java-library'
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
implementation 'rocks.xmpp:precis:1.0.0'
}
sourceCompatibility = "8"
targetCompatibility = "8"

View File

@ -1,179 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2017 Christian Schudt
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package rocks.xmpp.addr;
import java.text.Collator;
import java.util.Arrays;
/**
* Abstract Jid implementation for both full and bare JIDs.
*
* @author Christian Schudt
*/
abstract class AbstractJid implements Jid {
/**
* Checks if the JID is a full JID.
* <blockquote>
* <p>The term "full JID" refers to an XMPP address of the form &lt;localpart@domainpart/resourcepart&gt; (for a particular authorized client or device associated with an account) or of the form &lt;domainpart/resourcepart&gt; (for a particular resource or script associated with a server).</p>
* </blockquote>
*
* @return True, if the JID is a full JID; otherwise false.
*/
@Override
public final boolean isFullJid() {
return getResource() != null;
}
/**
* Checks if the JID is a bare JID.
* <blockquote>
* <p>The term "bare JID" refers to an XMPP address of the form &lt;localpart@domainpart&gt; (for an account at a server) or of the form &lt;domainpart&gt; (for a server).</p>
* </blockquote>
*
* @return True, if the JID is a bare JID; otherwise false.
*/
@Override
public final boolean isBareJid() {
return getResource() == null;
}
@Override
public final boolean isDomainJid() {
return getLocal() == null;
}
@Override
public final boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof Jid)) {
return false;
}
Jid other = (Jid) o;
return (getLocal() == other.getLocal() || getLocal() != null && getLocal().equals(other.getLocal()))
&& (getDomain() == other.getDomain() || getDomain() != null && getDomain().equals(other.getDomain()))
&& (getResource() == other.getResource() || getResource() != null && getResource().equals(other.getResource()));
}
@Override
public final int hashCode() {
return Arrays.hashCode(new String[]{getLocal(), getDomain(), getResource()});
}
/**
* Compares this JID with another JID. First domain parts are compared. If these are equal, local parts are compared
* and if these are equal, too, resource parts are compared.
*
* @param o The other JID.
* @return The comparison result.
*/
@Override
public final int compareTo(Jid o) {
if (this == o) {
return 0;
}
if (o != null) {
final Collator collator = Collator.getInstance();
int result;
// First compare domain parts.
if (getDomain() != null) {
result = o.getDomain() != null ? collator.compare(getDomain(), o.getDomain()) : -1;
} else {
result = o.getDomain() != null ? 1 : 0;
}
// If the domains are equal, compare local parts.
if (result == 0) {
if (getLocal() != null) {
// If this local part is not null, but the other is null, move this down (1).
result = o.getLocal() != null ? collator.compare(getLocal(), o.getLocal()) : 1;
} else {
// If this local part is null, but the other is not, move this up (-1).
result = o.getLocal() != null ? -1 : 0;
}
}
// If the local parts are equal, compare resource parts.
if (result == 0) {
if (getResource() != null) {
// If this resource part is not null, but the other is null, move this down (1).
return o.getResource() != null ? collator.compare(getResource(), o.getResource()) : 1;
} else {
// If this resource part is null, but the other is not, move this up (-1).
return o.getResource() != null ? -1 : 0;
}
}
return result;
} else {
return -1;
}
}
@Override
public final int length() {
return toString().length();
}
@Override
public final char charAt(int index) {
return toString().charAt(index);
}
@Override
public final CharSequence subSequence(int start, int end) {
return toString().subSequence(start, end);
}
/**
* Returns the JID in its string representation, i.e. [ localpart "@" ] domainpart [ "/" resourcepart ].
*
* @return The JID.
* @see #toEscapedString()
*/
@Override
public final String toString() {
return toString(getLocal(), getDomain(), getResource());
}
@Override
public final String toEscapedString() {
return toString(getEscapedLocal(), getDomain(), getResource());
}
static String toString(String local, String domain, String resource) {
StringBuilder sb = new StringBuilder();
if (local != null) {
sb.append(local).append('@');
}
sb.append(domain);
if (resource != null) {
sb.append('/').append(resource);
}
return sb.toString();
}
}

View File

@ -1,498 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2017 Christian Schudt
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package rocks.xmpp.addr;
import rocks.xmpp.precis.PrecisProfile;
import rocks.xmpp.precis.PrecisProfiles;
import rocks.xmpp.util.cache.LruCache;
import java.net.IDN;
import java.nio.charset.Charset;
import java.text.Normalizer;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* The implementation of the JID as described in <a href="https://tools.ietf.org/html/rfc7622">Extensible Messaging and Presence Protocol (XMPP): Address Format</a>.
* <p>
* This class is thread-safe and immutable.
*
* @author Christian Schudt
* @see <a href="https://tools.ietf.org/html/rfc7622">RFC 7622 - Extensible Messaging and Presence Protocol (XMPP): Address Format</a>
*/
final class FullJid extends AbstractJid {
/**
* Escapes all disallowed characters and also backslash, when followed by a defined hex code for escaping. See 4. Business Rules.
*/
private static final Pattern ESCAPE_PATTERN = Pattern.compile("[ \"&'/:<>@]|\\\\(?=20|22|26|27|2f|3a|3c|3e|40|5c)");
private static final Pattern UNESCAPE_PATTERN = Pattern.compile("\\\\(20|22|26|27|2f|3a|3c|3e|40|5c)");
private static final Pattern JID = Pattern.compile("^((.*?)@)?([^/@]+)(/(.*))?$");
private static final IDNProfile IDN_PROFILE = new IDNProfile();
/**
* Whenever dots are used as label separators, the following characters MUST be recognized as dots: U+002E (full stop), U+3002 (ideographic full stop), U+FF0E (fullwidth full stop), U+FF61 (halfwidth ideographic full stop).
*/
private static final String DOTS = "[.\u3002\uFF0E\uFF61]";
/**
* Label separators for domain labels, which should be mapped to "." (dot): IDEOGRAPHIC FULL STOP character (U+3002)
*/
private static final Pattern LABEL_SEPARATOR = Pattern.compile(DOTS);
private static final Pattern LABEL_SEPARATOR_FINAL = Pattern.compile(DOTS + "$");
/**
* Caches the escaped JIDs.
*/
private static final Map<CharSequence, Jid> ESCAPED_CACHE = new LruCache<>(5000);
/**
* Caches the unescaped JIDs.
*/
private static final Map<CharSequence, Jid> UNESCAPED_CACHE = new LruCache<>(5000);
private static final long serialVersionUID = -3824234106101731424L;
private final String escapedLocal;
private final String local;
private final String domain;
private final String resource;
private final Jid bareJid;
/**
* Creates a full JID with local, domain and resource part.
*
* @param local The local part.
* @param domain The domain part.
* @param resource The resource part.
*/
FullJid(CharSequence local, CharSequence domain, CharSequence resource) {
this(local, domain, resource, false, null);
}
private FullJid(final CharSequence local, final CharSequence domain, final CharSequence resource, final boolean doUnescape, Jid bareJid) {
final String enforcedLocalPart;
final String enforcedDomainPart;
final String enforcedResource;
final String unescapedLocalPart;
if (domain == null) {
throw new NullPointerException();
}
if (doUnescape) {
unescapedLocalPart = unescape(local);
} else {
unescapedLocalPart = local != null ? local.toString() : null;
}
// Escape the local part, so that disallowed characters like the space characters pass the UsernameCaseMapped profile.
final String escapedLocalPart = escape(unescapedLocalPart);
// If the domainpart includes a final character considered to be a label
// separator (dot) by [RFC1034], this character MUST be stripped from
// the domainpart before the JID of which it is a part is used for the
// purpose of routing an XML stanza, comparing against another JID, or
// constructing an XMPP URI or IRI [RFC5122]. In particular, such a
// character MUST be stripped before any other canonicalization steps
// are taken.
// Also validate, that the domain name can be converted to ASCII, i.e. validate the domain name (e.g. must not start with "_").
final String strDomain = IDN.toASCII(LABEL_SEPARATOR_FINAL.matcher(domain).replaceAll(""), IDN.USE_STD3_ASCII_RULES);
enforcedLocalPart = escapedLocalPart != null ? PrecisProfiles.USERNAME_CASE_MAPPED.enforce(escapedLocalPart) : null;
enforcedResource = resource != null ? PrecisProfiles.OPAQUE_STRING.enforce(resource) : null;
// See https://tools.ietf.org/html/rfc5895#section-2
enforcedDomainPart = IDN_PROFILE.enforce(strDomain);
validateLength(enforcedLocalPart, "local");
validateLength(enforcedResource, "resource");
validateDomain(strDomain);
this.local = unescape(enforcedLocalPart);
this.escapedLocal = enforcedLocalPart;
this.domain = enforcedDomainPart;
this.resource = enforcedResource;
if (bareJid != null) {
this.bareJid = bareJid;
} else {
this.bareJid = isBareJid() ? this : new AbstractJid() {
@Override
public Jid asBareJid() {
return this;
}
@Override
public Jid withLocal(CharSequence local) {
if (local == this.getLocal() || local != null && local.equals(this.getLocal())) {
return this;
}
return new FullJid(local, getDomain(), getResource(), false, null);
}
@Override
public Jid withResource(CharSequence resource) {
if (resource == this.getResource() || resource != null && resource.equals(this.getResource())) {
return this;
}
return new FullJid(getLocal(), getDomain(), resource, false, asBareJid());
}
@Override
public Jid atSubdomain(CharSequence subdomain) {
if (subdomain == null) {
throw new NullPointerException();
}
return new FullJid(getLocal(), subdomain + "." + getDomain(), getResource(), false, null);
}
@Override
public String getLocal() {
return FullJid.this.getLocal();
}
@Override
public String getEscapedLocal() {
return FullJid.this.getEscapedLocal();
}
@Override
public String getDomain() {
return FullJid.this.getDomain();
}
@Override
public String getResource() {
return null;
}
};
}
}
/**
* Creates a JID from a string. The format must be
* <blockquote><p>[ localpart "@" ] domainpart [ "/" resourcepart ]</p></blockquote>
*
* @param jid The JID.
* @param doUnescape If the jid parameter will be unescaped.
* @return The JID.
* @throws NullPointerException If the jid is null.
* @throws IllegalArgumentException If the jid could not be parsed or is not valid.
* @see <a href="https://xmpp.org/extensions/xep-0106.html">XEP-0106: JID Escaping</a>
*/
static Jid of(String jid, final boolean doUnescape) {
if (jid == null) {
throw new NullPointerException("jid must not be null.");
}
jid = jid.trim();
if (jid.isEmpty()) {
throw new IllegalArgumentException("jid must not be empty.");
}
Jid result;
if (doUnescape) {
result = UNESCAPED_CACHE.get(jid);
} else {
result = ESCAPED_CACHE.get(jid);
}
if (result != null) {
return result;
}
Matcher matcher = JID.matcher(jid);
if (matcher.matches()) {
Jid jidValue = new FullJid(matcher.group(2), matcher.group(3), matcher.group(5), doUnescape, null);
if (doUnescape) {
UNESCAPED_CACHE.put(jid, jidValue);
} else {
ESCAPED_CACHE.put(jid, jidValue);
}
return jidValue;
} else {
throw new IllegalArgumentException("Could not parse JID: " + jid);
}
}
/**
* Escapes a local part. The characters {@code "&'/:<>@} (+ whitespace) are replaced with their respective escape characters.
*
* @param localPart The local part.
* @return The escaped local part or null.
* @see <a href="https://xmpp.org/extensions/xep-0106.html">XEP-0106: JID Escaping</a>
*/
private static String escape(final CharSequence localPart) {
if (localPart != null) {
final Matcher matcher = ESCAPE_PATTERN.matcher(localPart);
final StringBuffer sb = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(sb, "\\\\" + Integer.toHexString(matcher.group().charAt(0)));
}
matcher.appendTail(sb);
return sb.toString();
}
return null;
}
private static String unescape(final CharSequence localPart) {
if (localPart != null) {
final Matcher matcher = UNESCAPE_PATTERN.matcher(localPart);
final StringBuffer sb = new StringBuffer();
while (matcher.find()) {
final char c = (char) Integer.parseInt(matcher.group(1), 16);
if (c == '\\') {
matcher.appendReplacement(sb, "\\\\");
} else {
matcher.appendReplacement(sb, String.valueOf(c));
}
}
matcher.appendTail(sb);
return sb.toString();
}
return null;
}
private static void validateDomain(String domain) {
if (domain == null) {
throw new NullPointerException("domain must not be null.");
}
if (domain.contains("@")) {
// Prevent misuse of API.
throw new IllegalArgumentException("domain must not contain a '@' sign");
}
validateLength(domain, "domain");
}
/**
* Validates that the length of a local, domain or resource part is not longer than 1023 characters.
*
* @param value The value.
* @param part The part, only used to produce an exception message.
*/
private static void validateLength(CharSequence value, CharSequence part) {
if (value != null) {
if (value.length() == 0) {
throw new IllegalArgumentException(part + " must not be empty.");
}
if (value.toString().getBytes(Charset.forName("UTF-8")).length > 1023) {
throw new IllegalArgumentException(part + " must not be greater than 1023 bytes.");
}
}
}
/**
* Converts this JID into a bare JID, i.e. removes the resource part.
* <blockquote>
* <p>The term "bare JID" refers to an XMPP address of the form &lt;localpart@domainpart&gt; (for an account at a server) or of the form &lt;domainpart&gt; (for a server).</p>
* </blockquote>
*
* @return The bare JID.
* @see #withResource(CharSequence)
*/
@Override
public final Jid asBareJid() {
return bareJid;
}
/**
* Gets the local part of the JID, also known as the name or node.
* <blockquote>
* <p><cite><a href="https://tools.ietf.org/html/rfc7622#section-3.3">3.3. Localpart</a></cite></p>
* <p>The localpart of a JID is an optional identifier placed before the
* domainpart and separated from the latter by the '@' character.
* Typically, a localpart uniquely identifies the entity requesting and
* using network access provided by a server (i.e., a local account),
* although it can also represent other kinds of entities (e.g., a
* chatroom associated with a multi-user chat service [XEP-0045]). The
* entity represented by an XMPP localpart is addressed within the
* context of a specific domain (i.e., &lt;localpart@domainpart&gt;).</p>
* </blockquote>
*
* @return The local part or null.
*/
@Override
public final String getLocal() {
return local;
}
@Override
public final String getEscapedLocal() {
return escapedLocal;
}
/**
* Gets the domain part.
* <blockquote>
* <p><cite><a href="https://tools.ietf.org/html/rfc7622#section-3.2">3.2. Domainpart</a></cite></p>
* <p>The domainpart is the primary identifier and is the only REQUIRED
* element of a JID (a mere domainpart is a valid JID). Typically,
* a domainpart identifies the "home" server to which clients connect
* for XML routing and data management functionality.</p>
* </blockquote>
*
* @return The domain part.
*/
@Override
public final String getDomain() {
return domain;
}
/**
* Gets the resource part.
* <blockquote>
* <p><cite><a href="https://tools.ietf.org/html/rfc7622#section-3.4">3.4. Resourcepart</a></cite></p>
* <p>The resourcepart of a JID is an optional identifier placed after the
* domainpart and separated from the latter by the '/' character. A
* resourcepart can modify either a &lt;localpart@domainpart&gt; address or a
* mere &lt;domainpart&gt; address. Typically, a resourcepart uniquely
* identifies a specific connection (e.g., a device or location) or
* object (e.g., an occupant in a multi-user chatroom [XEP-0045])
* belonging to the entity associated with an XMPP localpart at a domain
* (i.e., &lt;localpart@domainpart/resourcepart&gt;).</p>
* </blockquote>
*
* @return The resource part or null.
*/
@Override
public final String getResource() {
return resource;
}
/**
* Creates a new JID with a new local part and the same domain and resource part of the current JID.
*
* @param local The local part.
* @return The JID with a new local part.
* @throws IllegalArgumentException If the local is not a valid local part.
* @see #withResource(CharSequence)
*/
@Override
public final Jid withLocal(CharSequence local) {
if (local == this.getLocal() || local != null && local.equals(this.getLocal())) {
return this;
}
return new FullJid(local, getDomain(), getResource(), false, null);
}
/**
* Creates a new full JID with a resource and the same local and domain part of the current JID.
*
* @param resource The resource.
* @return The full JID with a resource.
* @throws IllegalArgumentException If the resource is not a valid resource part.
* @see #asBareJid()
* @see #withLocal(CharSequence)
*/
@Override
public final Jid withResource(CharSequence resource) {
if (resource == this.getResource() || resource != null && resource.equals(this.getResource())) {
return this;
}
return new FullJid(getLocal(), getDomain(), resource, false, asBareJid());
}
/**
* Creates a new JID at a subdomain and at the same domain as this JID.
*
* @param subdomain The subdomain.
* @return The JID at a subdomain.
* @throws NullPointerException If subdomain is null.
* @throws IllegalArgumentException If subdomain is not a valid subdomain name.
*/
@Override
public final Jid atSubdomain(CharSequence subdomain) {
if (subdomain != null) {
throw new NullPointerException();
}
return new FullJid(getLocal(), subdomain + "." + getDomain(), getResource(), false, null);
}
/**
* A profile for applying the rules for IDN as in RFC 5895. Although IDN doesn't use Precis, it's still very similar so that we can use the base class.
*
* @see <a href="https://tools.ietf.org/html/rfc5895#section-2">RFC 5895</a>
*/
private static final class IDNProfile extends PrecisProfile {
private IDNProfile() {
super(false);
}
@Override
public String prepare(CharSequence input) {
return IDN.toUnicode(input.toString(), IDN.USE_STD3_ASCII_RULES);
}
@Override
public String enforce(CharSequence input) {
// 4. Map IDEOGRAPHIC FULL STOP character (U+3002) to dot.
return applyAdditionalMappingRule(
// 3. All characters are mapped using Unicode Normalization Form C (NFC).
applyNormalizationRule(
// 2. Fullwidth and halfwidth characters (those defined with
// Decomposition Types <wide> and <narrow>) are mapped to their
// decomposition mappings
applyWidthMappingRule(
// 1. Uppercase characters are mapped to their lowercase equivalents
applyCaseMappingRule(prepare(input))))).toString();
}
@Override
protected CharSequence applyWidthMappingRule(CharSequence charSequence) {
return widthMap(charSequence);
}
@Override
protected CharSequence applyAdditionalMappingRule(CharSequence charSequence) {
return LABEL_SEPARATOR.matcher(charSequence).replaceAll(".");
}
@Override
protected CharSequence applyCaseMappingRule(CharSequence charSequence) {
return charSequence.toString().toLowerCase();
}
@Override
protected CharSequence applyNormalizationRule(CharSequence charSequence) {
return Normalizer.normalize(charSequence, Normalizer.Form.NFC);
}
@Override
protected CharSequence applyDirectionalityRule(CharSequence charSequence) {
return charSequence;
}
}
}

View File

@ -1,313 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2017 Christian Schudt
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package rocks.xmpp.addr;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.io.Serializable;
/**
* Represents the JID as described in <a href="https://tools.ietf.org/html/rfc7622">Extensible Messaging and Presence Protocol (XMPP): Address Format</a>.
* <p>
* A JID consists of three parts:
* <p>
* [ localpart "@" ] domainpart [ "/" resourcepart ]
* </p>
* The easiest way to create a JID is to use the {@link #of(CharSequence)} method:
* ```java
* Jid jid = Jid.of("juliet@capulet.lit/balcony");
* ```
* You can then get the parts from it via the respective methods:
* ```java
* String local = jid.getLocal(); // juliet
* String domain = jid.getDomain(); // capulet.lit
* String resource = jid.getResource(); // balcony
* ```
* Implementations of this interface should override <code>equals()</code> and <code>hashCode()</code>, so that different instances with the same value are equal:
* ```java
* Jid.of("romeo@capulet.lit/balcony").equals(Jid.of("romeo@capulet.lit/balcony")); // true
* ```
* The default implementation of this class also supports <a href="https://xmpp.org/extensions/xep-0106.html">XEP-0106: JID Escaping</a>, i.e.
* ```java
* Jid.of("d'artagnan@musketeers.lit")
* ```
* is escaped as <code>d\\27artagnan@musketeers.lit</code>.
* <p>
* Implementations of this interface should be thread-safe and immutable.
*
* @author Christian Schudt
* @see <a href="https://tools.ietf.org/html/rfc7622">RFC 7622 - Extensible Messaging and Presence Protocol (XMPP): Address Format</a>
*/
public interface Jid extends Comparable<Jid>, Serializable, CharSequence {
/**
* The maximal length of a full JID, which is 3071.
* <blockquote>
* <p><cite><a href="https://tools.ietf.org/html/rfc7622#section-3.1">3.1. Fundamentals</a></cite></p>
* <p>Each allowable portion of a JID (localpart, domainpart, and
* resourcepart) is 1 to 1023 octets in length, resulting in a maximum
* total size (including the '@' and '/' separators) of 3071 octets.
* </p>
* </blockquote>
* Note that the length is based on bytes, not characters.
*
* @see #MAX_BARE_JID_LENGTH
*/
int MAX_FULL_JID_LENGTH = 3071;
/**
* The maximal length of a bare JID, which is 2047 (1023 + 1 + 1023).
* Note that the length is based on bytes, not characters.
*
* @see #MAX_FULL_JID_LENGTH
*/
int MAX_BARE_JID_LENGTH = 2047;
/**
* The service discovery feature used for determining support of JID escaping (<code>jid\20escaping</code>).
*/
String ESCAPING_FEATURE = "jid\\20escaping";
/**
* Returns a full JID with a domain and resource part, e.g. <code>capulet.com/balcony</code>
*
* @param local The local part.
* @param domain The domain.
* @param resource The resource part.
* @return The JID.
* @throws NullPointerException If the domain is null.
* @throws IllegalArgumentException If the domain, local or resource part are not valid.
*/
static Jid of(CharSequence local, CharSequence domain, CharSequence resource) {
return new FullJid(local, domain, resource);
}
/**
* Creates a bare JID with only the domain part, e.g. <code>capulet.com</code>
*
* @param domain The domain.
* @return The JID.
* @throws NullPointerException If the domain is null.
* @throws IllegalArgumentException If the domain or local part are not valid.
*/
static Jid ofDomain(CharSequence domain) {
return new FullJid(null, domain, null);
}
/**
* Creates a bare JID with a local and domain part, e.g. <code>juliet@capulet.com</code>
*
* @param local The local part.
* @param domain The domain.
* @return The JID.
* @throws NullPointerException If the domain is null.
* @throws IllegalArgumentException If the domain or local part are not valid.
*/
static Jid ofLocalAndDomain(CharSequence local, CharSequence domain) {
return new FullJid(local, domain, null);
}
/**
* Creates a full JID with a domain and resource part, e.g. <code>capulet.com/balcony</code>
*
* @param domain The domain.
* @param resource The resource part.
* @return The JID.
* @throws NullPointerException If the domain is null.
* @throws IllegalArgumentException If the domain or resource are not valid.
*/
static Jid ofDomainAndResource(CharSequence domain, CharSequence resource) {
return new FullJid(null, domain, resource);
}
/**
* Creates a JID from an unescaped string. The format must be
* <blockquote><p>[ localpart "@" ] domainpart [ "/" resourcepart ]</p></blockquote>
* The input string will be escaped.
*
* @param jid The JID.
* @return The JID.
* @throws NullPointerException If the jid is null.
* @throws IllegalArgumentException If the jid could not be parsed or is not valid.
* @see <a href="https://xmpp.org/extensions/xep-0106.html">XEP-0106: JID Escaping</a>
*/
static Jid of(CharSequence jid) {
if (jid instanceof Jid) {
return (Jid) jid;
}
return FullJid.of(jid.toString(), false);
}
/**
* Creates a JID from a escaped JID string. The format must be
* <blockquote><p>[ localpart "@" ] domainpart [ "/" resourcepart ]</p></blockquote>
* This method should be used, when parsing JIDs from the XMPP stream.
*
* @param jid The JID.
* @return The JID.
* @throws NullPointerException If the jid is null.
* @throws IllegalArgumentException If the jid could not be parsed or is not valid.
* @see <a href="https://xmpp.org/extensions/xep-0106.html">XEP-0106: JID Escaping</a>
*/
static Jid ofEscaped(CharSequence jid) {
return FullJid.of(jid.toString(), true);
}
/**
* Checks if the JID is a full JID.
* <blockquote>
* <p>The term "full JID" refers to an XMPP address of the form &lt;localpart@domainpart/resourcepart&gt; (for a particular authorized client or device associated with an account) or of the form &lt;domainpart/resourcepart&gt; (for a particular resource or script associated with a server).</p>
* </blockquote>
*
* @return True, if the JID is a full JID; otherwise false.
*/
boolean isFullJid();
/**
* Checks if the JID is a bare JID.
* <blockquote>
* <p>The term "bare JID" refers to an XMPP address of the form &lt;localpart@domainpart&gt; (for an account at a server) or of the form &lt;domainpart&gt; (for a server).</p>
* </blockquote>
*
* @return True, if the JID is a bare JID; otherwise false.
*/
boolean isBareJid();
/**
* Checks if the JID is a domain JID, i.e. if it has no local part.
*
* @return True, if the JID is a domain JID, i.e. if it has no local part.
*/
boolean isDomainJid();
/**
* Gets the bare JID representation of this JID, i.e. removes the resource part.
* <blockquote>
* <p>The term "bare JID" refers to an XMPP address of the form &lt;localpart@domainpart&gt; (for an account at a server) or of the form &lt;domainpart&gt; (for a server).</p>
* </blockquote>
*
* @return The bare JID.
* @see #withResource(CharSequence)
*/
Jid asBareJid();
/**
* Creates a new JID with a new local part and the same domain and resource part of the current JID.
*
* @param local The local part.
* @return The JID with a new local part.
* @throws IllegalArgumentException If the local is not a valid local part.
* @see #withResource(CharSequence)
*/
Jid withLocal(CharSequence local);
/**
* Creates a new full JID with a resource and the same local and domain part of the current JID.
*
* @param resource The resource.
* @return The full JID with a resource.
* @throws IllegalArgumentException If the resource is not a valid resource part.
* @see #asBareJid()
* @see #withLocal(CharSequence)
*/
Jid withResource(CharSequence resource);
/**
* Creates a new JID at a subdomain and at the same domain as this JID.
*
* @param subdomain The subdomain.
* @return The JID at a subdomain.
* @throws NullPointerException If subdomain is null.
* @throws IllegalArgumentException If subdomain is not a valid subdomain name.
*/
Jid atSubdomain(CharSequence subdomain);
/**
* Gets the local part of the JID, also known as the name or node.
* <blockquote>
* <p><cite><a href="https://tools.ietf.org/html/rfc7622#section-3.3">3.3. Localpart</a></cite></p>
* <p>The localpart of a JID is an optional identifier placed before the
* domainpart and separated from the latter by the '@' character.
* Typically, a localpart uniquely identifies the entity requesting and
* using network access provided by a server (i.e., a local account),
* although it can also represent other kinds of entities (e.g., a
* chatroom associated with a multi-user chat service [XEP-0045]). The
* entity represented by an XMPP localpart is addressed within the
* context of a specific domain (i.e., &lt;localpart@domainpart&gt;).</p>
* </blockquote>
*
* @return The local part or null.
* @see #getEscapedLocal()
*/
String getLocal();
/**
* Gets the escaped local part of the JID.
*
* @return The escaped local part or null.
* @see #getLocal()
* @since 0.8.0
*/
String getEscapedLocal();
/**
* Gets the domain part.
* <blockquote>
* <p><cite><a href="https://tools.ietf.org/html/rfc7622#section-3.2">3.2. Domainpart</a></cite></p>
* <p>The domainpart is the primary identifier and is the only REQUIRED
* element of a JID (a mere domainpart is a valid JID). Typically,
* a domainpart identifies the "home" server to which clients connect
* for XML routing and data management functionality.</p>
* </blockquote>
*
* @return The domain part.
*/
String getDomain();
/**
* Gets the resource part.
* <blockquote>
* <p><cite><a href="https://tools.ietf.org/html/rfc7622#section-3.4">3.4. Resourcepart</a></cite></p>
* <p>The resourcepart of a JID is an optional identifier placed after the
* domainpart and separated from the latter by the '/' character. A
* resourcepart can modify either a &lt;localpart@domainpart&gt; address or a
* mere &lt;domainpart&gt; address. Typically, a resourcepart uniquely
* identifies a specific connection (e.g., a device or location) or
* object (e.g., an occupant in a multi-user chatroom [XEP-0045])
* belonging to the entity associated with an XMPP localpart at a domain
* (i.e., &lt;localpart@domainpart/resourcepart&gt;).</p>
* </blockquote>
*
* @return The resource part or null.
*/
String getResource();
/**
* Returns the JID in escaped form as described in <a href="https://xmpp.org/extensions/xep-0106.html">XEP-0106: JID Escaping</a>.
*
* @return The escaped JID.
* @see #toString()
*/
String toEscapedString();
}

View File

@ -1,131 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2017 Christian Schudt
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package rocks.xmpp.addr;
/**
* Represents a malformed JID in order to handle the <code>jid-malformed</code> error.
* <p>
* This class is not intended to be publicly instantiable, but is used for malformed JIDs during parsing automatically.
*
* @author Christian Schudt
* @see <a href="https://xmpp.org/rfcs/rfc6120.html#stanzas-error-conditions-jid-malformed">RFC 6120, 8.3.3.8. jid-malformed</a>
*/
public final class MalformedJid extends AbstractJid {
private static final long serialVersionUID = -2896737611021417985L;
private final String localPart;
private final String domainPart;
private final String resourcePart;
private final Throwable cause;
static MalformedJid of(final String jid, final Throwable cause) {
// Do some basic parsing without any further checks or validation.
final StringBuilder sb = new StringBuilder(jid);
// 1. Remove any portion from the first '/' character to the end of the
// string (if there is a '/' character present).
final int indexOfResourceDelimiter = jid.indexOf('/');
final String resourcePart;
if (indexOfResourceDelimiter > -1) {
resourcePart = sb.substring(indexOfResourceDelimiter + 1);
sb.delete(indexOfResourceDelimiter, sb.length());
} else {
resourcePart = null;
}
// 2. Remove any portion from the beginning of the string to the first
// '@' character (if there is an '@' character present).
final int indexOfAt = jid.indexOf('@');
final String localPart;
if (indexOfAt > -1) {
localPart = sb.substring(0, indexOfAt);
sb.delete(0, indexOfAt + 1);
} else {
localPart = null;
}
return new MalformedJid(localPart, sb.toString(), resourcePart, cause);
}
private MalformedJid(final String localPart, final String domainPart, final String resourcePart, final Throwable cause) {
this.localPart = localPart;
this.domainPart = domainPart;
this.resourcePart = resourcePart;
this.cause = cause;
}
@Override
public final Jid asBareJid() {
return new MalformedJid(localPart, domainPart, null, cause);
}
@Override
public Jid withLocal(CharSequence local) {
return new MalformedJid(local.toString(), domainPart, resourcePart, cause);
}
@Override
public Jid withResource(CharSequence resource) {
return new MalformedJid(localPart, domainPart, resource.toString(), cause);
}
@Override
public Jid atSubdomain(CharSequence subdomain) {
if (subdomain == null) {
throw new NullPointerException();
}
return new MalformedJid(localPart, subdomain + "." + domainPart, resourcePart, cause);
}
@Override
public final String getLocal() {
return localPart;
}
@Override
public final String getEscapedLocal() {
return localPart;
}
@Override
public final String getDomain() {
return domainPart;
}
@Override
public final String getResource() {
return resourcePart;
}
/**
* Gets the cause why the JID is malformed.
*
* @return The cause.
*/
public final Throwable getCause() {
return cause;
}
}

View File

@ -1,31 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2016 Christian Schudt
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Provides classes for the <a href="https://tools.ietf.org/html/rfc7622">XMPP Address Format</a> (JID).
*
* @see <a href="https://tools.ietf.org/html/rfc7622">Extensible Messaging and Presence Protocol (XMPP): Address Format</a>
*/
package rocks.xmpp.addr;

View File

@ -1,192 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2016 Christian Schudt
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package rocks.xmpp.util.cache;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* A simple directory based cache for caching of persistent items like avatars or entity capabilities.
*
* @author Christian Schudt
*/
public final class DirectoryCache implements Map<String, byte[]> {
private final Path cacheDirectory;
public DirectoryCache(Path cacheDirectory) {
this.cacheDirectory = cacheDirectory;
}
@Override
public final int size() {
try (final Stream<Path> files = cacheContent()) {
return (int) Math.min(files.count(), Integer.MAX_VALUE);
}
}
@Override
public final boolean isEmpty() {
try (final Stream<Path> files = cacheContent()) {
return files.findAny().map(file -> Boolean.FALSE).orElse(Boolean.TRUE);
}
}
@Override
public final boolean containsKey(Object key) {
return Files.exists(cacheDirectory.resolve(key.toString()));
}
@Override
public final boolean containsValue(Object value) {
throw new UnsupportedOperationException();
}
@Override
public final byte[] get(final Object key) {
return Optional.ofNullable(key).map(Object::toString).filter(((Predicate<String>) String::isEmpty).negate()).map(cacheDirectory::resolve).filter(Files::isReadable).map(file -> {
try {
return Files.readAllBytes(file);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}).orElse(null);
}
@Override
public final byte[] put(String key, byte[] value) {
// Make sure the directory exists.
byte[] data = get(key);
if (!Arrays.equals(data, value))
try {
if (Files.notExists(cacheDirectory)) {
Files.createDirectories(cacheDirectory);
}
Path file = cacheDirectory.resolve(key);
Files.write(file, value);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return data;
}
@Override
public final byte[] remove(Object key) {
byte[] data = get(key);
try {
Files.deleteIfExists(cacheDirectory.resolve(key.toString()));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return data;
}
@Override
public final void putAll(Map<? extends String, ? extends byte[]> m) {
m.forEach(this::put);
}
@Override
public final void clear() {
try {
Files.walkFileTree(cacheDirectory, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.deleteIfExists(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
// Don't delete the cache directory itself.
if (!Files.isSameFile(dir, cacheDirectory)) {
Files.deleteIfExists(dir);
}
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
public final Set<String> keySet() {
try (final Stream<Path> files = Files.list(cacheDirectory)) {
return Collections.unmodifiableSet(files.map(Path::getFileName).map(Path::toString).collect(Collectors.toSet()));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
public final Collection<byte[]> values() {
throw new UnsupportedOperationException();
}
@Override
public final Set<Entry<String, byte[]>> entrySet() {
throw new UnsupportedOperationException();
}
@Override
public final void forEach(final BiConsumer<? super String, ? super byte[]> action) {
if (Files.exists(cacheDirectory))
try (final Stream<Path> files = cacheContent().filter(Files::isReadable)) {
files.forEach(file -> {
try {
action.accept(file.getFileName().toString(), Files.readAllBytes(file));
} catch (final IOException e) {
throw new UncheckedIOException(e);
}
});
}
}
@SuppressWarnings("StreamResourceLeak")
private final Stream<Path> cacheContent() {
try {
return Files.walk(cacheDirectory).filter(Files::isRegularFile);
} catch (final IOException e) {
throw new UncheckedIOException(e);
}
}
}

View File

@ -1,228 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2016 Christian Schudt
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package rocks.xmpp.util.cache;
import java.util.Collection;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* A simple concurrent implementation of a least-recently-used cache.
* <p>
* This cache is keeps a maximal number of items in memory and removes the least-recently-used item, when new items are added.
*
* @param <K> The key.
* @param <V> The value.
* @author Christian Schudt
* @see <a href="http://javadecodedquestions.blogspot.de/2013/02/java-cache-static-data-loading.html">http://javadecodedquestions.blogspot.de/2013/02/java-cache-static-data-loading.html</a>
* @see <a href="http://stackoverflow.com/a/22891780">http://stackoverflow.com/a/22891780</a>
*/
public final class LruCache<K, V> implements Map<K, V> {
private final int maxEntries;
private final Map<K, V> map;
final Queue<K> queue;
public LruCache(final int maxEntries) {
this.maxEntries = maxEntries;
this.map = new ConcurrentHashMap<>(maxEntries);
// Don't use a ConcurrentLinkedQueue here.
// There's a JDK bug, leading to OutOfMemoryError and high CPU usage:
// https://bugs.openjdk.java.net/browse/JDK-8054446
this.queue = new ConcurrentLinkedDeque<>();
}
@Override
public final int size() {
return map.size();
}
@Override
public final boolean isEmpty() {
return map.isEmpty();
}
@Override
public final boolean containsKey(final Object key) {
return map.containsKey(key);
}
@Override
public final boolean containsValue(final Object value) {
return map.containsValue(value);
}
@SuppressWarnings("unchecked")
@Override
public final V get(final Object key) {
final V v = map.get(key);
if (v != null) {
// Remove the key from the queue and re-add it to the tail. It is now the most recently used key.
keyUsed((K) key);
}
return v;
}
@Override
public final V put(final K key, final V value) {
V v = map.put(key, value);
keyUsed(key);
limit();
return v;
}
@Override
public final V remove(final Object key) {
queue.remove(key);
return map.remove(key);
}
@Override
public final void putAll(final Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
@Override
public final void clear() {
queue.clear();
map.clear();
}
@Override
public final Set<K> keySet() {
return map.keySet();
}
@Override
public final Collection<V> values() {
return map.values();
}
@Override
public final Set<Entry<K, V>> entrySet() {
return map.entrySet();
}
// Default methods
@Override
public final V putIfAbsent(final K key, final V value) {
final V v = map.putIfAbsent(key, value);
if (v == null) {
keyUsed(key);
}
limit();
return v;
}
@Override
public final boolean remove(final Object key, final Object value) {
final boolean removed = map.remove(key, value);
if (removed) {
queue.remove(key);
}
return removed;
}
@Override
public final boolean replace(final K key, final V oldValue, final V newValue) {
final boolean replaced = map.replace(key, oldValue, newValue);
if (replaced) {
keyUsed(key);
}
return replaced;
}
@Override
public final V replace(final K key, final V value) {
final V v = map.replace(key, value);
if (v != null) {
keyUsed(key);
}
return v;
}
@Override
public final V computeIfAbsent(final K key, final Function<? super K, ? extends V> mappingFunction) {
return map.computeIfAbsent(key, mappingFunction.<V>andThen(v -> {
keyUsed(key);
limit();
return v;
}));
}
@Override
public final V computeIfPresent(final K key, final BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
return map.computeIfPresent(key, remappingFunction.<V>andThen(v -> {
keyUsed(key);
limit();
return v;
}));
}
@Override
public final V compute(final K key, final BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
return map.compute(key, remappingFunction.<V>andThen(v -> {
keyUsed(key);
limit();
return v;
}));
}
@Override
public final V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
return map.merge(key, value, remappingFunction.<V>andThen(v -> {
keyUsed(key);
limit();
return v;
}));
}
private void limit() {
while (queue.size() > maxEntries) {
final K oldestKey = queue.poll();
if (oldestKey != null) {
map.remove(oldestKey);
}
}
}
private void keyUsed(final K key) {
// remove it from the queue and re-add it, to make it the most recently used key.
queue.remove(key);
queue.offer(key);
}
}

View File

@ -1,28 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2016 Christian Schudt
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Provides simple cache implementations.
*/
package rocks.xmpp.util.cache;

View File

@ -0,0 +1,2 @@
• Rework Login with certificate UI
• Add ability to pin chats on top (add to favorites)

View File

@ -1,2 +1 @@
include ':libs:xmpp-addr'
rootProject.name = 'Conversations'

View File

@ -46,7 +46,7 @@ import eu.siacs.conversations.ui.ManageAccountActivity;
import eu.siacs.conversations.utils.BackupFileHeader;
import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
import static eu.siacs.conversations.services.ExportBackupService.CIPHERMODE;
import static eu.siacs.conversations.services.ExportBackupService.KEYTYPE;
@ -208,7 +208,7 @@ public class ImportBackupService extends Service {
}
}
final Jid jid = backupFileHeader.getJid();
Cursor countCursor = db.rawQuery("select count(messages.uuid) from messages join conversations on conversations.uuid=messages.conversationUuid join accounts on conversations.accountUuid=accounts.uuid where accounts.username=? and accounts.server=?", new String[]{jid.getEscapedLocal(), jid.getDomain()});
Cursor countCursor = db.rawQuery("select count(messages.uuid) from messages join conversations on conversations.uuid=messages.conversationUuid join accounts on conversations.accountUuid=accounts.uuid where accounts.username=? and accounts.server=?", new String[]{jid.getEscapedLocal(), jid.getDomain().toEscapedString()});
countCursor.moveToFirst();
int count = countCursor.getInt(0);
Log.d(Config.LOGTAG, "restored " + count + " messages");

View File

@ -18,7 +18,7 @@ import eu.siacs.conversations.databinding.MagicCreateBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.InstallReferrerUtils;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class MagicCreateActivity extends XmppActivity implements TextWatcher {
@ -79,13 +79,13 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher {
final boolean fixedUsername;
if (this.domain != null && this.username != null) {
fixedUsername = true;
jid = Jid.ofLocalAndDomain(this.username, this.domain);
jid = Jid.ofLocalAndDomainEscaped(this.username, this.domain);
} else if (this.domain != null) {
fixedUsername = false;
jid = Jid.ofLocalAndDomain(username, this.domain);
jid = Jid.ofLocalAndDomainEscaped(username, this.domain);
} else {
fixedUsername = false;
jid = Jid.ofLocalAndDomain(username, Config.MAGIC_CREATE_DOMAIN);
jid = Jid.ofLocalAndDomainEscaped(username, Config.MAGIC_CREATE_DOMAIN);
}
if (!jid.getEscapedLocal().equals(jid.getLocal()) || (this.username == null && username.length() < 3)) {
binding.username.setError(getString(R.string.invalid_username));
@ -149,9 +149,9 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher {
binding.fullJid.setVisibility(View.VISIBLE);
final Jid jid;
if (this.domain == null) {
jid = Jid.ofLocalAndDomain(username, Config.MAGIC_CREATE_DOMAIN);
jid = Jid.ofLocalAndDomainEscaped(username, Config.MAGIC_CREATE_DOMAIN);
} else {
jid = Jid.ofLocalAndDomain(username, this.domain);
jid = Jid.ofLocalAndDomainEscaped(username, this.domain);
}
binding.fullJid.setText(getString(R.string.your_full_jid_will_be, jid.toEscapedString()));
} catch (IllegalArgumentException e) {

View File

@ -14,9 +14,7 @@ import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.Toast;
@ -34,7 +32,7 @@ import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
import eu.siacs.conversations.ui.adapter.AccountAdapter;
import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
import eu.siacs.conversations.xmpp.XmppConnection;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
import static eu.siacs.conversations.utils.PermissionUtils.allGranted;
import static eu.siacs.conversations.utils.PermissionUtils.writeGranted;
@ -133,7 +131,7 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(false);
menu.findItem(R.id.mgmt_account_publish_avatar).setVisible(false);
}
menu.setHeaderTitle(this.selectedAccount.getJid().asBareJid().toString());
menu.setHeaderTitle(this.selectedAccount.getJid().asBareJid().toEscapedString());
}
@Override
@ -369,7 +367,7 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
}
private void deleteAccount(final Account account) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.mgmt_account_are_you_sure));
builder.setIconAttribute(android.R.attr.alertDialogIcon);
builder.setMessage(getString(R.string.mgmt_account_delete_confirm_text));
@ -408,15 +406,15 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
}
@Override
public void alias(String alias) {
public void alias(final String alias) {
if (alias != null) {
xmppConnectionService.createAccountFromKey(alias, this);
}
}
@Override
public void onAccountCreated(Account account) {
Intent intent = new Intent(this, EditAccountActivity.class);
public void onAccountCreated(final Account account) {
final Intent intent = new Intent(this, EditAccountActivity.class);
intent.putExtra("jid", account.getJid().asBareJid().toString());
intent.putExtra("init", true);
startActivity(intent);

View File

@ -1,10 +1,6 @@
package eu.siacs.conversations.ui;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import java.util.ArrayList;
@ -14,7 +10,7 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.ui.adapter.AccountAdapter;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class ShareViaAccountActivity extends XmppActivity {
public static final String EXTRA_CONTACT = "contact";

View File

@ -1,11 +1,14 @@
package eu.siacs.conversations.ui;
import android.Manifest;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.security.KeyChain;
import android.security.KeyChainAliasCallback;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
@ -21,15 +24,16 @@ import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityWelcomeBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.InstallReferrerUtils;
import eu.siacs.conversations.utils.SignupUtils;
import eu.siacs.conversations.utils.XmppUri;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
import static eu.siacs.conversations.utils.PermissionUtils.allGranted;
import static eu.siacs.conversations.utils.PermissionUtils.writeGranted;
public class WelcomeActivity extends XmppActivity {
public class WelcomeActivity extends XmppActivity implements XmppConnectionService.OnAccountCreated, KeyChainAliasCallback {
private static final int REQUEST_IMPORT_BACKUP = 0x63fb;
@ -154,10 +158,42 @@ public class WelcomeActivity extends XmppActivity {
case R.id.action_scan_qr_code:
UriHandlerActivity.scan(this);
break;
case R.id.action_add_account_with_cert:
addAccountFromKey();
break;
}
return super.onOptionsItemSelected(item);
}
private void addAccountFromKey() {
try {
KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, R.string.device_does_not_support_certificates, Toast.LENGTH_LONG).show();
}
}
@Override
public void alias(final String alias) {
if (alias != null) {
xmppConnectionService.createAccountFromKey(alias, this);
}
}
@Override
public void onAccountCreated(final Account account) {
final Intent intent = new Intent(this, EditAccountActivity.class);
intent.putExtra("jid", account.getJid().asBareJid().toString());
intent.putExtra("init", true);
addInviteUri(intent);
startActivity(intent);
}
@Override
public void informUser(final int r) {
runOnUiThread(() -> Toast.makeText(this, r, Toast.LENGTH_LONG).show());
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
UriHandlerActivity.onRequestPermissionResult(this, requestCode, grantResults);

View File

@ -26,7 +26,7 @@ import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.ImportBackupService;
import eu.siacs.conversations.utils.BackupFileHeader;
import eu.siacs.conversations.utils.UIHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class BackupFileAdapter extends RecyclerView.Adapter<BackupFileAdapter.BackupFileViewHolder> {

View File

@ -13,7 +13,7 @@ import eu.siacs.conversations.ui.ManageAccountActivity;
import eu.siacs.conversations.ui.PickServerActivity;
import eu.siacs.conversations.ui.StartConversationActivity;
import eu.siacs.conversations.ui.WelcomeActivity;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class SignupUtils {
@ -24,9 +24,9 @@ public class SignupUtils {
public static Intent getTokenRegistrationIntent(final Activity activity, Jid jid, String preAuth) {
final Intent intent = new Intent(activity, MagicCreateActivity.class);
if (jid.isDomainJid()) {
intent.putExtra(MagicCreateActivity.EXTRA_DOMAIN, jid.getDomain());
intent.putExtra(MagicCreateActivity.EXTRA_DOMAIN, jid.getDomain().toEscapedString());
} else {
intent.putExtra(MagicCreateActivity.EXTRA_DOMAIN, jid.getDomain());
intent.putExtra(MagicCreateActivity.EXTRA_DOMAIN, jid.getDomain().toEscapedString());
intent.putExtra(MagicCreateActivity.EXTRA_USERNAME, jid.getEscapedLocal());
}
intent.putExtra(MagicCreateActivity.EXTRA_PRE_AUTH, preAuth);

View File

@ -3,14 +3,20 @@
<item
android:id="@+id/action_scan_qr_code"
android:title="@string/scan_qr_code"
app:showAsAction="ifRoom"
android:icon="?attr/icon_scan_qr_code"
android:orderInCategory="10"
android:title="@string/scan_qr_code"
android:visible="@bool/show_qr_code_scan"
android:icon="?attr/icon_scan_qr_code"/>
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_add_account_with_cert"
android:title="@string/action_add_account_with_certificate"
android:visible="true"
app:showAsAction="never" />
<item
android:id="@+id/action_import_backup"
app:showAsAction="never"
android:title="@string/restore_backup"/>
android:title="@string/restore_backup"
app:showAsAction="never" />
</menu>

View File

@ -3,9 +3,9 @@
<string name="pick_a_server">Виберіть постачальника послуг обміну повідомленнями XMPP</string>
<string name="use_chat.sum7.eu">Скористатися chat.sum7.eu</string>
<string name="create_new_account">Створити новий обліковий запис</string>
<string name="do_you_have_an_account">Вже маєте обліковий запис XMPP? Можливо, користуєтеся іншою програмою XMPP або користувалися цією програмою раніше. Якщо ні, можете створити новий обліковий запис XMPP просто зараз.\nЗверніть увагу: Деякі постачальники електронної пошти водночас надають облікові записи XMPP.</string>
<string name="server_select_text">XMPP — це мережа обміну повідомленнями, незалежна від постачальників. Можете використовувати цю програму з будь-яким XMPP сервером, який оберете.\nПроте, для зручності, ми спростили створення облікового запису на chat.sum7.eu — в постачальника, який спеціально налаштований на роботу з цією програмою.</string>
<string name="magic_create_text_on_x">Вас запросили до %1$s. Ми проведемо Вас крок за кроком, щоб створити обліковий запис.\nОбираючи %1$s в якості свого постачальника, Ви зможете спілкуватися з користувачами інших постачальників, повідомивши їм свою повну адресу XMPP.</string>
<string name="magic_create_text_fixed">Вас запросили до %1$s. Для Вас уже обрали ім\'я користувача. Ми проведемо Вас крок за кроком, щоб створити обліковий запис.\nВи зможете спілкуватися з користувачами інших постачальників, повідомивши їм свою повну адресу XMPP.</string>
<string name="do_you_have_an_account">Вже маєте обліковий запис XMPP? Можливо, користуєтеся іншою програмою XMPP або користувалися цією програмою раніше. Якщо ні, можете створити новий обліковий запис XMPP просто зараз.\nЗверніть увагу, що деякі постачальники електронної пошти у той же час надають облікові записи XMPP.</string>
<string name="server_select_text">XMPP — це мережа обміну повідомленнями, незалежна від постачальників. Можете використовувати цю програму з будь-яким XMPP сервером, який оберете.\nПроте, для зручності, ми спростили створення облікового запису на chat.sum7.eu — у постачальника, який спеціально налаштований на роботу з цією програмою.</string>
<string name="magic_create_text_on_x">Вас запросили до %1$s. Ми проведемо вас крок за кроком, щоб створити обліковий запис.\nОбираючи %1$s в якості свого постачальника, ви зможете спілкуватися з користувачами інших постачальників, для цього повідомте їм свою повну адресу XMPP.</string>
<string name="magic_create_text_fixed">Вас запросили до %1$s. Для вас створено ім\'я користувача. Ми проведемо вас крок за кроком, щоб створити обліковий запис.\nВи зможете спілкуватися з користувачами інших постачальників, для цього повідомите їм свою повну адресу XMPP.</string>
<string name="your_server_invitation">Ваше запрошення до сервера</string>
</resources>

View File

@ -6,7 +6,7 @@ import java.util.Collections;
import java.util.List;
import eu.siacs.conversations.xmpp.chatstate.ChatState;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public final class Config {
private static final int UNENCRYPTED = 1;

View File

@ -13,7 +13,7 @@ import java.util.HashMap;
import java.util.Map;
import eu.siacs.conversations.Config;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class JabberIdContact extends AbstractPhoneContact {

View File

@ -53,7 +53,7 @@ import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.pep.PublishOptions;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {

View File

@ -2,7 +2,6 @@ package eu.siacs.conversations.crypto.axolotl;
import android.util.Base64;
import android.util.Log;
import android.util.SparseArray;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
@ -24,7 +23,7 @@ import javax.crypto.spec.SecretKeySpec;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.xml.Element;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class XmppAxolotlMessage {
public static final String CONTAINERTAG = "encrypted";

View File

@ -24,6 +24,6 @@ public class External extends SaslMechanism {
@Override
public String getClientFirstMessage() {
return Base64.encodeToString(account.getJid().asBareJid().toString().getBytes(),Base64.NO_WRAP);
return Base64.encodeToString(account.getJid().asBareJid().toEscapedString().getBytes(),Base64.NO_WRAP);
}
}

View File

@ -173,7 +173,7 @@ abstract class ScramMechanism extends SaslMechanism {
// Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations,SASL-Mechanism".
final KeyPair keys = CACHE.get(
CryptoHelper.bytesToHex(account.getJid().asBareJid().toString().getBytes()) + ","
CryptoHelper.bytesToHex(account.getJid().asBareJid().toEscapedString().getBytes()) + ","
+ CryptoHelper.bytesToHex(account.getPassword().getBytes()) + ","
+ CryptoHelper.bytesToHex(salt.getBytes()) + ","
+ String.valueOf(iterationCount) + ","

View File

@ -16,7 +16,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import eu.siacs.conversations.Config;
@ -30,7 +29,7 @@ import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.jingle.RtpCapability;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class Account extends AbstractEntity implements AvatarService.Avatarable {
@ -233,10 +232,14 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
return next != null && !next.equals(previousFull);
}
public String getServer() {
public Jid getDomain() {
return jid.getDomain();
}
public String getServer() {
return jid.getDomain().toEscapedString();
}
public String getPassword() {
return password;
}
@ -370,7 +373,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
final ContentValues values = new ContentValues();
values.put(UUID, uuid);
values.put(USERNAME, jid.getLocal());
values.put(SERVER, jid.getDomain());
values.put(SERVER, jid.getDomain().toEscapedString());
values.put(PASSWORD, password);
values.put(OPTIONS, options);
synchronized (this.keys) {
@ -588,7 +591,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
public boolean isBlocked(final ListItem contact) {
final Jid jid = contact.getJid();
return jid != null && (blocklist.contains(jid.asBareJid()) || blocklist.contains(Jid.ofDomain(jid.getDomain())));
return jid != null && (blocklist.contains(jid.asBareJid()) || blocklist.contains(jid.getDomain()));
}
public boolean isBlocked(final Jid jid) {

View File

@ -1,6 +1,6 @@
package eu.siacs.conversations.entities;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public interface Blockable {
boolean isBlocked();

View File

@ -17,7 +17,7 @@ import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.InvalidJid;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class Bookmark extends Element implements ListItem {
@ -28,7 +28,7 @@ public class Bookmark extends Element implements ListItem {
public Bookmark(final Account account, final Jid jid) {
super("conference");
this.jid = jid;
this.setAttribute("jid", jid.toString());
this.setAttribute("jid", jid);
this.account = account;
}

View File

@ -26,7 +26,7 @@ import eu.siacs.conversations.utils.JidHelper;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.pep.Avatar;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class Contact implements ListItem, Blockable {
public static final String TABLENAME = "contacts";
@ -134,12 +134,12 @@ public class Contact implements ListItem, Blockable {
return this.systemName;
} else if (!TextUtils.isEmpty(this.serverName)) {
return this.serverName;
} else if (!TextUtils.isEmpty(this.presenceName) && ((QuickConversationsService.isQuicksy() && Config.QUICKSY_DOMAIN.equals(jid.getDomain())) ||mutualPresenceSubscription())) {
} else if (!TextUtils.isEmpty(this.presenceName) && ((QuickConversationsService.isQuicksy() && Config.QUICKSY_DOMAIN.equals(jid.getDomain().toEscapedString())) ||mutualPresenceSubscription())) {
return this.presenceName;
} else if (jid.getLocal() != null) {
return JidHelper.localPartOrFallback(jid);
} else {
return jid.getDomain();
return jid.getDomain().toEscapedString();
}
}
@ -396,7 +396,7 @@ public class Contact implements ListItem, Blockable {
public Element asElement() {
final Element item = new Element("item");
item.setAttribute("jid", this.jid.toString());
item.setAttribute("jid", this.jid);
if (this.serverName != null) {
item.setAttribute("name", this.serverName);
}
@ -413,7 +413,7 @@ public class Contact implements ListItem, Blockable {
}
public String getServer() {
return getJid().getDomain();
return getJid().getDomain().toEscapedString();
}
public boolean setAvatar(Avatar avatar) {
@ -451,13 +451,13 @@ public class Contact implements ListItem, Blockable {
@Override
public boolean isDomainBlocked() {
return getAccount().isBlocked(Jid.ofDomain(this.getJid().getDomain()));
return getAccount().isBlocked(this.getJid().getDomain());
}
@Override
public Jid getBlockedJid() {
if (isDomainBlocked()) {
return Jid.ofDomain(getJid().getDomain());
return getJid().getDomain();
} else {
return getJid();
}

View File

@ -6,6 +6,8 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import com.google.common.collect.ComparisonChain;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@ -27,7 +29,7 @@ import eu.siacs.conversations.utils.JidHelper;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.chatstate.ChatState;
import eu.siacs.conversations.xmpp.mam.MamReference;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
import static eu.siacs.conversations.entities.Bookmark.printableValue;
@ -49,9 +51,9 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
public static final String ATTRIBUTE_MUTED_TILL = "muted_till";
public static final String ATTRIBUTE_ALWAYS_NOTIFY = "always_notify";
public static final String ATTRIBUTE_PUSH_NODE = "push_node";
public static final String ATTRIBUTE_LAST_CLEAR_HISTORY = "last_clear_history";
public static final String ATTRIBUTE_FORMERLY_PRIVATE_NON_ANONYMOUS = "formerly_private_non_anonymous";
public static final String ATTRIBUTE_PINNED_ON_TOP = "pinned_on_top";
static final String ATTRIBUTE_MUC_PASSWORD = "muc_password";
static final String ATTRIBUTE_MEMBERS_ONLY = "members_only";
static final String ATTRIBUTE_MODERATED = "moderated";
@ -137,7 +139,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
if (conversation.getContact().isOwnServer()) {
return false;
}
final String contact = conversation.getJid().getDomain();
final String contact = conversation.getJid().getDomain().toEscapedString();
final String account = conversation.getAccount().getServer();
if (Config.OMEMO_EXCEPTIONS.CONTACT_DOMAINS.contains(contact) || Config.OMEMO_EXCEPTIONS.ACCOUNT_DOMAINS.contains(account)) {
return false;
@ -479,7 +481,10 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
@Override
public int compareTo(@NonNull Conversation another) {
return Long.compare(another.getSortableTime(), getSortableTime());
return ComparisonChain.start()
.compareFalseFirst(another.getBooleanAttribute(ATTRIBUTE_PINNED_ON_TOP, false), getBooleanAttribute(ATTRIBUTE_PINNED_ON_TOP,false))
.compare(another.getSortableTime(), getSortableTime())
.result();
}
private long getSortableTime() {

View File

@ -29,7 +29,7 @@
package eu.siacs.conversations.entities;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public interface Conversational {

View File

@ -34,7 +34,7 @@ import android.database.Cursor;
import java.util.Set;
import eu.siacs.conversations.ui.adapter.MessageAdapter;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class IndividualMessage extends Message {

View File

@ -5,7 +5,7 @@ import android.content.Context;
import java.util.List;
import eu.siacs.conversations.services.AvatarService;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public interface ListItem extends Comparable<ListItem>, AvatarService.Avatarable {

View File

@ -27,7 +27,7 @@ import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.MessageUtils;
import eu.siacs.conversations.utils.MimeUtils;
import eu.siacs.conversations.utils.UIHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class Message extends AbstractEntity implements AvatarService.Avatarable {

View File

@ -17,12 +17,11 @@ import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.MessageArchiveService;
import eu.siacs.conversations.utils.JidHelper;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.chatstate.ChatState;
import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.forms.Field;
import eu.siacs.conversations.xmpp.pep.Avatar;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class MucOptions {

View File

@ -8,7 +8,7 @@ import java.util.List;
import java.util.Locale;
import eu.siacs.conversations.utils.UIHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class RawBlockable implements ListItem, Blockable {

View File

@ -6,10 +6,9 @@ import org.json.JSONObject;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class ReadByMarker {

View File

@ -29,7 +29,7 @@
package eu.siacs.conversations.entities;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class ReceiptRequest {

View File

@ -4,12 +4,10 @@ import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.common.collect.ComparisonChain;
import java.util.Comparator;
import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.utils.LanguageUtils;
import eu.siacs.conversations.utils.UIHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class Room implements AvatarService.Avatarable, Comparable<Room> {

View File

@ -6,7 +6,7 @@ import java.util.Iterator;
import java.util.List;
import eu.siacs.conversations.android.AbstractPhoneContact;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class Roster {

View File

@ -29,7 +29,7 @@
package eu.siacs.conversations.entities;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class StubConversation implements Conversational {

View File

@ -34,7 +34,7 @@ import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class IqGenerator extends AbstractGenerator {
@ -302,7 +302,7 @@ public class IqGenerator extends AbstractGenerator {
if (mam.muc()) {
packet.setTo(mam.getWith());
} else if (mam.getWith() != null) {
data.put("with", mam.getWith().toString());
data.put("with", mam.getWith().toEscapedString());
}
final long start = mam.getStart();
final long end = mam.getEnd();
@ -334,7 +334,7 @@ public class IqGenerator extends AbstractGenerator {
public IqPacket generateSetBlockRequest(final Jid jid, boolean reportSpam) {
final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
final Element block = iq.addChild("block", Namespace.BLOCKING);
final Element item = block.addChild("item").setAttribute("jid", jid.toEscapedString());
final Element item = block.addChild("item").setAttribute("jid", jid);
if (reportSpam) {
item.addChild("report", "urn:xmpp:reporting:0").addChild("spam");
}
@ -345,13 +345,13 @@ public class IqGenerator extends AbstractGenerator {
public IqPacket generateSetUnblockRequest(final Jid jid) {
final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
final Element block = iq.addChild("unblock", Namespace.BLOCKING);
block.addChild("item").setAttribute("jid", jid.toEscapedString());
block.addChild("item").setAttribute("jid", jid);
return iq;
}
public IqPacket generateSetPassword(final Account account, final String newPassword) {
final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
packet.setTo(Jid.of(account.getServer()));
packet.setTo(account.getDomain());
final Element query = packet.addChild("query", Namespace.REGISTER);
final Jid jid = account.getJid();
query.addChild("username").setContent(jid.getLocal());
@ -372,7 +372,7 @@ public class IqGenerator extends AbstractGenerator {
Element query = packet.query("http://jabber.org/protocol/muc#admin");
for (Jid jid : jids) {
Element item = query.addChild("item");
item.setAttribute("jid", jid.toEscapedString());
item.setAttribute("jid", jid);
item.setAttribute("affiliation", affiliation);
}
return packet;
@ -442,7 +442,7 @@ public class IqGenerator extends AbstractGenerator {
public IqPacket generateCreateAccountWithCaptcha(Account account, String id, Data data) {
final IqPacket register = new IqPacket(IqPacket.TYPE.SET);
register.setFrom(account.getJid().asBareJid());
register.setTo(Jid.of(account.getServer()));
register.setTo(account.getDomain());
register.setId(id);
Element query = register.query(Namespace.REGISTER);
if (data != null) {
@ -489,7 +489,7 @@ public class IqGenerator extends AbstractGenerator {
public IqPacket enablePush(final Jid jid, final String node, final String secret) {
IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
Element enable = packet.addChild("enable", Namespace.PUSH);
enable.setAttribute("jid", jid.toString());
enable.setAttribute("jid", jid);
enable.setAttribute("node", node);
if (secret != null) {
Data data = new Data();
@ -504,7 +504,7 @@ public class IqGenerator extends AbstractGenerator {
public IqPacket disablePush(final Jid jid, final String node) {
IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
Element disable = packet.addChild("disable", Namespace.PUSH);
disable.setAttribute("jid", jid.toEscapedString());
disable.setAttribute("jid", jid);
disable.setAttribute("node", node);
return packet;
}

View File

@ -23,7 +23,7 @@ import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import eu.siacs.conversations.xmpp.jingle.Media;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class MessageGenerator extends AbstractGenerator {
private static final String OMEMO_FALLBACK_MESSAGE = "I sent you an OMEMO encrypted message but your client doesnt seem to support that. Find more information on https://conversations.im/omemo";
@ -198,7 +198,7 @@ public class MessageGenerator extends AbstractGenerator {
packet.setTo(contact);
packet.setFrom(conversation.getAccount().getJid());
Element x = packet.addChild("x", "jabber:x:conference");
x.setAttribute("jid", conversation.getJid().asBareJid().toString());
x.setAttribute("jid", conversation.getJid().asBareJid());
String password = conversation.getMucOptions().getPassword();
if (password != null) {
x.setAttribute("password", password);

View File

@ -32,7 +32,7 @@ import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.FileWriterException;
import eu.siacs.conversations.utils.WakeLockHelper;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class HttpDownloadConnection implements Transferable {
@ -265,7 +265,7 @@ public class HttpDownloadConnection implements Transferable {
private void retrieveUrl() {
changeStatus(STATUS_CHECKING);
final Account account = message.getConversation().getAccount();
IqPacket request = mXmppConnectionService.getIqGenerator().requestP1S3Url(Jid.of(account.getJid().getDomain()), mUrl.getHost());
IqPacket request = mXmppConnectionService.getIqGenerator().requestP1S3Url(account.getDomain(), mUrl.getHost());
mXmppConnectionService.sendIqPacket(message.getConversation().getAccount(), request, (a, packet) -> {
if (packet.getType() == IqPacket.TYPE.RESULT) {
String download = packet.query().getAttribute("download");

View File

@ -31,7 +31,6 @@ package eu.siacs.conversations.http;
import android.util.Log;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
@ -44,7 +43,7 @@ import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class SlotRequester {
@ -62,7 +61,7 @@ public class SlotRequester {
Jid host = account.getXmppConnection().findDiscoItemByFeature(Namespace.HTTP_UPLOAD_LEGACY);
requestHttpUploadLegacy(account, host, file, mime, callback);
} else {
requestP1S3(account, Jid.of(account.getServer()), file.getName(), md5, callback);
requestP1S3(account, account.getDomain(), file.getName(), md5, callback);
}
}

View File

@ -16,7 +16,7 @@ import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.InvalidJid;
import eu.siacs.conversations.xmpp.stanzas.AbstractStanza;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public abstract class AbstractParser {
@ -106,7 +106,7 @@ public abstract class AbstractParser {
public static MucOptions.User parseItem(Conversation conference, Element item, Jid fullJid) {
final String local = conference.getJid().getLocal();
final String domain = conference.getJid().getDomain();
final String domain = conference.getJid().getDomain().toEscapedString();
String affiliation = item.getAttribute("affiliation");
String role = item.getAttribute("role");
String nick = item.getAttribute("nick");

View File

@ -39,7 +39,7 @@ import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class IqParser extends AbstractParser implements OnIqPacketReceived {

View File

@ -48,7 +48,7 @@ import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class MessageParser extends AbstractParser implements OnMessagePacketReceived {

View File

@ -25,7 +25,7 @@ import eu.siacs.conversations.xmpp.InvalidJid;
import eu.siacs.conversations.xmpp.OnPresencePacketReceived;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class PresenceParser extends AbstractParser implements
OnPresencePacketReceived {

View File

@ -58,7 +58,7 @@ import eu.siacs.conversations.utils.MimeUtils;
import eu.siacs.conversations.utils.Resolver;
import eu.siacs.conversations.xmpp.InvalidJid;
import eu.siacs.conversations.xmpp.mam.MamReference;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class DatabaseBackend extends SQLiteOpenHelper {
@ -621,7 +621,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
cursor.getString(cursor.getColumnIndex(Account.USERNAME)),
cursor.getString(cursor.getColumnIndex(Account.SERVER)),
null
).getDomain();
).getDomain().toEscapedString();
} catch (IllegalArgumentException ignored) {
Log.e(Config.LOGTAG, "Failed to migrate Account SERVER "
+ cursor.getString(cursor.getColumnIndex(Account.SERVER))
@ -858,7 +858,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
public List<FilePath> getRelativeFilePaths(String account, Jid jid, int limit) {
SQLiteDatabase db = this.getReadableDatabase();
final String SQL = "select uuid,relativeFilePath from messages where type in (1,2,5) and deleted=0 and "+Message.RELATIVE_FILE_PATH+" is not null and conversationUuid=(select uuid from conversations where accountUuid=? and (contactJid=? or contactJid like ?)) order by timeSent desc";
final String[] args = {account, jid.toEscapedString(), jid.toEscapedString() + "/%"};
final String[] args = {account, jid.toString(), jid.toString() + "/%"};
Cursor cursor = db.rawQuery(SQL + (limit > 0 ? " limit " + String.valueOf(limit) : ""), args);
List<FilePath> filesPaths = new ArrayList<>();
while (cursor.moveToNext()) {

View File

@ -39,11 +39,10 @@ import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.RawBlockable;
import eu.siacs.conversations.entities.Room;
import eu.siacs.conversations.http.services.MuclumbusService;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
import eu.siacs.conversations.xmpp.XmppConnection;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class AvatarService implements OnAdvancedStreamFeaturesLoaded {

View File

@ -14,7 +14,6 @@ import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import com.google.zxing.BarcodeFormat;
@ -32,7 +31,7 @@ import java.util.Hashtable;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.utils.CryptoHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class BarcodeProvider extends ContentProvider implements ServiceConnection {

View File

@ -10,7 +10,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
@ -23,8 +22,6 @@ import eu.siacs.conversations.entities.Room;
import eu.siacs.conversations.http.HttpConnectionManager;
import eu.siacs.conversations.http.services.MuclumbusService;
import eu.siacs.conversations.parser.IqParser;
import eu.siacs.conversations.utils.LanguageUtils;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
@ -35,7 +32,7 @@ import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class ChannelDiscoveryService {

View File

@ -15,13 +15,12 @@ import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Conversational;
import eu.siacs.conversations.entities.ReceiptRequest;
import eu.siacs.conversations.generator.AbstractGenerator;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
import eu.siacs.conversations.xmpp.mam.MamReference;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {

View File

@ -34,7 +34,6 @@ import android.os.SystemClock;
import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@ -49,7 +48,7 @@ import eu.siacs.conversations.ui.interfaces.OnSearchResultsAvailable;
import eu.siacs.conversations.utils.Cancellable;
import eu.siacs.conversations.utils.MessageUtils;
import eu.siacs.conversations.utils.ReplacingSerialSingleThreadExecutor;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class MessageSearchTask implements Runnable, Cancellable {

View File

@ -1109,7 +1109,7 @@ public class NotificationService {
return;
} else if (errors.size() == 1) {
mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.problem_connecting_to_account));
mBuilder.setContentText(errors.get(0).getJid().asBareJid().toString());
mBuilder.setContentText(errors.get(0).getJid().asBareJid().toEscapedString());
} else {
mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.problem_connecting_to_accounts));
mBuilder.setContentText(mXmppConnectionService.getString(R.string.touch_to_fix));

View File

@ -20,7 +20,7 @@ import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.ui.StartConversationActivity;
import eu.siacs.conversations.utils.ReplacingSerialSingleThreadExecutor;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class ShortcutService {
@ -116,13 +116,13 @@ public class ShortcutService {
}
private static String getShortcutId(Contact contact) {
return contact.getAccount().getJid().asBareJid().toString()+"#"+contact.getJid().asBareJid().toString();
return contact.getAccount().getJid().asBareJid().toEscapedString()+"#"+contact.getJid().asBareJid().toEscapedString();
}
private Intent getShortcutIntent(Contact contact) {
Intent intent = new Intent(xmppConnectionService, StartConversationActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("xmpp:"+contact.getJid().asBareJid().toString()));
intent.setData(Uri.parse("xmpp:"+contact.getJid().asBareJid().toEscapedString()));
intent.putExtra("account",contact.getAccount().getJid().asBareJid().toString());
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP| Intent.FLAG_ACTIVITY_SINGLE_TOP);
return intent;

View File

@ -155,7 +155,7 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
import me.leolin.shortcutbadger.ShortcutBadger;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class XmppConnectionService extends Service {
@ -2196,15 +2196,16 @@ public class XmppConnectionService extends Service {
return;
}
if (findAccountByJid(info.first) == null) {
Account account = new Account(info.first, "");
final Account account = new Account(info.first, "");
account.setPrivateKeyAlias(alias);
account.setOption(Account.OPTION_DISABLED, true);
account.setOption(Account.OPTION_FIXED_USERNAME, true);
account.setDisplayName(info.second);
createAccount(account);
callback.onAccountCreated(account);
if (Config.X509_VERIFICATION) {
try {
getMemorizingTrustManager().getNonInteractive(account.getJid().getDomain()).checkClientTrusted(chain, "RSA");
getMemorizingTrustManager().getNonInteractive(account.getServer()).checkClientTrusted(chain, "RSA");
} catch (CertificateException e) {
callback.informUser(R.string.certificate_chain_is_not_trusted);
}
@ -2213,7 +2214,6 @@ public class XmppConnectionService extends Service {
callback.informUser(R.string.account_already_exists);
}
} catch (Exception e) {
e.printStackTrace();
callback.informUser(R.string.unable_to_parse_certificate);
}
}).start();
@ -2821,7 +2821,7 @@ public class XmppConnectionService extends Service {
boolean changed = false;
for (ListIterator<Jid> iterator = cryptoTargets.listIterator(); iterator.hasNext(); ) {
Jid jid = iterator.next();
if (!members.contains(jid) && !members.contains(Jid.ofDomain(jid.getDomain()))) {
if (!members.contains(jid) && !members.contains(jid.getDomain())) {
iterator.remove();
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": removed " + jid + " from crypto targets of " + conversation.getName());
changed = true;
@ -3751,7 +3751,7 @@ public class XmppConnectionService extends Service {
if (account.getStatus() == Account.State.ONLINE) {
IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
Element item = iq.query(Namespace.ROSTER).addChild("item");
item.setAttribute("jid", contact.getJid().toString());
item.setAttribute("jid", contact.getJid());
item.setAttribute("subscription", "remove");
account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler);
}
@ -4160,7 +4160,7 @@ public class XmppConnectionService extends Service {
mucServers.addAll(account.getXmppConnection().getMucServers());
for (Bookmark bookmark : account.getBookmarks()) {
final Jid jid = bookmark.getJid();
final String s = jid == null ? null : jid.getDomain();
final String s = jid == null ? null : jid.getDomain().toEscapedString();
if (s != null) {
mucServers.add(s);
}

View File

@ -3,9 +3,6 @@ package eu.siacs.conversations.ui;
import android.databinding.DataBindingUtil;
import android.support.annotation.StringRes;
import android.support.v7.app.AlertDialog;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.TypefaceSpan;
import android.view.View;
import android.widget.Toast;
@ -14,7 +11,7 @@ import eu.siacs.conversations.databinding.DialogBlockContactBinding;
import eu.siacs.conversations.entities.Blockable;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.ui.util.JidDialog;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public final class BlockContactDialog {
public static void show(final XmppActivity xmppActivity, final Blockable blockable) {

View File

@ -11,12 +11,11 @@ import java.util.Collections;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Blockable;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.entities.RawBlockable;
import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class BlocklistActivity extends AbstractSearchableListItemActivity implements OnUpdateBlocklist {
@ -80,7 +79,7 @@ public class BlocklistActivity extends AbstractSearchableListItemActivity implem
getString(R.string.block_jabber_id),
getString(R.string.block),
null,
account.getJid().asBareJid().toString(),
account.getJid().asBareJid().toEscapedString(),
true,
false
);

View File

@ -38,7 +38,7 @@ import eu.siacs.conversations.ui.util.PendingItem;
import eu.siacs.conversations.ui.util.SoftKeyboardUtils;
import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.utils.AccountUtils;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.OnActionExpandListener, TextView.OnEditorActionListener, ChannelDiscoveryService.OnChannelSearchResultsFound, ChannelSearchResultAdapter.OnChannelSearchResultSelected {
@ -263,7 +263,7 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O
}
public void joinChannelSearchResult(String selectedAccount, Room result) {
final Jid jid = Config.DOMAIN_LOCK == null ? Jid.of(selectedAccount) : Jid.of(selectedAccount, Config.DOMAIN_LOCK, null);
final Jid jid = Config.DOMAIN_LOCK == null ? Jid.ofEscaped(selectedAccount) : Jid.ofEscaped(selectedAccount, Config.DOMAIN_LOCK, null);
final boolean syncAutoJoin = getBooleanPreference("autojoin", R.bool.autojoin);
final Account account = xmppConnectionService.findAccountByJid(jid);
final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, result.getRoom(), true, true, true);

View File

@ -3,7 +3,6 @@ package eu.siacs.conversations.ui;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.widget.ListView;
import android.widget.Toast;
@ -12,9 +11,7 @@ import java.util.List;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.ui.adapter.AccountAdapter;
import rocks.xmpp.addr.Jid;
public class ChooseAccountForProfilePictureActivity extends XmppActivity {

View File

@ -39,7 +39,7 @@ import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
import eu.siacs.conversations.ui.util.ActivityResult;
import eu.siacs.conversations.ui.util.PendingItem;
import eu.siacs.conversations.utils.XmppUri;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class ChooseContactActivity extends AbstractSearchableListItemActivity implements MultiChoiceModeListener, AdapterView.OnItemClickListener {
public static final String EXTRA_TITLE_RES_ID = "extra_title_res_id";
@ -361,9 +361,9 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
for (Account account : xmppConnectionService.getAccounts()) {
if (account.getStatus() != Account.State.DISABLED) {
if (Config.DOMAIN_LOCK != null) {
this.mActivatedAccounts.add(account.getJid().getLocal());
this.mActivatedAccounts.add(account.getJid().getEscapedLocal());
} else {
this.mActivatedAccounts.add(account.getJid().asBareJid().toString());
this.mActivatedAccounts.add(account.getJid().asBareJid().toEscapedString());
}
}
}

View File

@ -3,7 +3,6 @@ package eu.siacs.conversations.ui;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
@ -23,7 +22,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.databinding.ActivityMucDetailsBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Bookmark;
@ -51,7 +49,7 @@ import eu.siacs.conversations.utils.StringUtils;
import eu.siacs.conversations.utils.StylingHelper;
import eu.siacs.conversations.utils.XmppUri;
import me.drakeet.support.toast.ToastCompat;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
import static eu.siacs.conversations.entities.Bookmark.printableValue;
import static eu.siacs.conversations.utils.StringUtils.changed;
@ -447,9 +445,9 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
final User self = mucOptions.getSelf();
String account;
if (Config.DOMAIN_LOCK != null) {
account = mConversation.getAccount().getJid().getLocal();
account = mConversation.getAccount().getJid().getEscapedLocal();
} else {
account = mConversation.getAccount().getJid().asBareJid().toString();
account = mConversation.getAccount().getJid().asBareJid().toEscapedString();
}
setTitle(mucOptions.isPrivateAndNonAnonymous() ? R.string.action_muc_details : R.string.channel_details);
this.binding.editMucNameButton.setVisibility((self.getAffiliation().ranks(MucOptions.Affiliation.OWNER) || mucOptions.canChangeSubject()) ? View.VISIBLE : View.GONE);
@ -586,7 +584,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
@Override
public void onAffiliationChangeFailed(Jid jid, int resId) {
displayToast(getString(resId, jid.asBareJid().toString()));
displayToast(getString(resId, jid.asBareJid().toEscapedString()));
}
@Override

View File

@ -60,7 +60,7 @@ import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.XmppConnection;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class ContactDetailsActivity extends OmemoActivity implements OnAccountUpdate, OnRosterUpdate, OnUpdateBlocklist, OnKeyStatusUpdated, OnMediaLoaded {
public static final String ACTION_VIEW_CONTACT = "view_contact";
@ -136,7 +136,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
AlertDialog.Builder builder = new AlertDialog.Builder(
ContactDetailsActivity.this);
builder.setTitle(getString(R.string.action_add_phone_book));
builder.setMessage(getString(R.string.add_phone_book_text, contact.getJid().toString()));
builder.setMessage(getString(R.string.add_phone_book_text, contact.getJid().toEscapedString()));
builder.setNegativeButton(getString(R.string.cancel), null);
builder.setPositiveButton(getString(R.string.add), addToPhonebook);
builder.create().show();
@ -411,9 +411,9 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
binding.detailsContactjid.setText(IrregularUnicodeDetector.style(this, contact.getJid()));
String account;
if (Config.DOMAIN_LOCK != null) {
account = contact.getAccount().getJid().getLocal();
account = contact.getAccount().getJid().getEscapedLocal();
} else {
account = contact.getAccount().getJid().asBareJid().toString();
account = contact.getAccount().getJid().asBareJid().toEscapedString();
}
binding.detailsAccount.setText(getString(R.string.using_account, account));
AvatarWorkerTask.loadAvatar(contact, binding.detailsContactBadge, R.dimen.avatar_on_details_screen_size);

View File

@ -56,6 +56,7 @@ import com.google.common.base.Optional;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
@ -123,7 +124,7 @@ import eu.siacs.conversations.xmpp.jingle.JingleFileTransferConnection;
import eu.siacs.conversations.xmpp.jingle.Media;
import eu.siacs.conversations.xmpp.jingle.OngoingRtpSession;
import eu.siacs.conversations.xmpp.jingle.RtpCapability;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
import static eu.siacs.conversations.ui.XmppActivity.EXTRA_ACCOUNT;
import static eu.siacs.conversations.ui.XmppActivity.REQUEST_INVITE_TO_CONVERSATION;
@ -863,13 +864,13 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
private void commitAttachments() {
if (!hasPermissions(REQUEST_COMMIT_ATTACHMENTS, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
final List<Attachment> attachments = mediaPreviewAdapter.getAttachments();
if (anyNeedsExternalStoragePermission(attachments) && !hasPermissions(REQUEST_COMMIT_ATTACHMENTS, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
return;
}
if (conversation.getNextEncryption() == Message.ENCRYPTION_AXOLOTL && trustKeysIfNeeded(REQUEST_TRUST_KEYS_ATTACHMENTS)) {
return;
}
final List<Attachment> attachments = mediaPreviewAdapter.getAttachments();
final PresenceSelector.OnPresenceSelected callback = () -> {
for (Iterator<Attachment> i = attachments.iterator(); i.hasNext(); i.remove()) {
final Attachment attachment = i.next();
@ -896,6 +897,16 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
}
private static boolean anyNeedsExternalStoragePermission(final Collection<Attachment> attachments) {
for(final Attachment attachment : attachments) {
if (attachment.getType() != Attachment.Type.LOCATION) {
return true;
}
}
return false;
}
public void toggleInputMethod() {
boolean hasAttachments = mediaPreviewAdapter.hasAttachments();
binding.textinput.setVisibility(hasAttachments ? View.GONE : View.VISIBLE);
@ -962,6 +973,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
final MenuItem menuCall = menu.findItem(R.id.action_call);
final MenuItem menuOngoingCall = menu.findItem(R.id.action_ongoing_call);
final MenuItem menuVideoCall = menu.findItem(R.id.action_video_call);
final MenuItem menuTogglePinned = menu.findItem(R.id.action_toggle_pinned);
if (conversation != null) {
@ -994,6 +1006,11 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
ConversationMenuConfigurator.configureAttachmentMenu(conversation, menu);
ConversationMenuConfigurator.configureEncryptionMenu(conversation, menu);
if (conversation.getBooleanAttribute(Conversation.ATTRIBUTE_PINNED_ON_TOP, false)) {
menuTogglePinned.setTitle(R.string.remove_from_favorites);
} else {
menuTogglePinned.setTitle(R.string.add_to_favorites);
}
}
super.onCreateOptionsMenu(menu, menuInflater);
}
@ -1261,6 +1278,9 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
case R.id.action_ongoing_call:
returnToOngoingCall();
break;
case R.id.action_toggle_pinned:
togglePinned();
break;
default:
break;
}
@ -1289,6 +1309,12 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
private void togglePinned() {
final boolean pinned = conversation.getBooleanAttribute(Conversation.ATTRIBUTE_PINNED_ON_TOP, false);
conversation.setAttribute(Conversation.ATTRIBUTE_PINNED_ON_TOP, !pinned);
activity.xmppConnectionService.updateConversation(conversation);
}
private void checkPermissionAndTriggerAudioCall() {
if (activity.mUseTor || conversation.getAccount().isOnion()) {
Toast.makeText(activity, R.string.disable_tor_to_make_call, Toast.LENGTH_SHORT).show();

View File

@ -81,7 +81,7 @@ import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.utils.SignupUtils;
import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
import static eu.siacs.conversations.ui.ConversationFragment.REQUEST_DECRYPT_PGP;

View File

@ -15,7 +15,6 @@ import android.text.TextWatcher;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.Spinner;
import java.security.SecureRandom;
import java.util.ArrayList;
@ -23,7 +22,6 @@ import java.util.Collection;
import java.util.List;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.CreateConferenceDialogBinding;
import eu.siacs.conversations.databinding.CreatePublicChannelDialogBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.XmppConnectionService;
@ -32,7 +30,7 @@ import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
import eu.siacs.conversations.ui.util.DelayedHintHelper;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.xmpp.XmppConnection;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class CreatePublicChannelDialog extends DialogFragment implements OnBackendConnected {

View File

@ -75,7 +75,7 @@ import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.XmppConnection.Features;
import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.pep.Avatar;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class EditAccountActivity extends OmemoActivity implements OnAccountUpdate, OnUpdateBlocklist,
OnKeyStatusUpdated, OnCaptchaRequested, KeyChainAliasCallback, XmppConnectionService.OnShowErrorToast, XmppConnectionService.OnMamPreferencesFetched {
@ -201,9 +201,9 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
final Jid jid;
try {
if (mUsernameMode) {
jid = Jid.of(binding.accountJid.getText().toString(), getUserModeDomain(), null);
jid = Jid.ofEscaped(binding.accountJid.getText().toString(), getUserModeDomain(), null);
} else {
jid = Jid.of(binding.accountJid.getText().toString());
jid = Jid.ofEscaped(binding.accountJid.getText().toString());
}
} catch (final NullPointerException | IllegalArgumentException e) {
if (mUsernameMode) {
@ -577,9 +577,9 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
protected boolean jidEdited() {
final String unmodified;
if (mUsernameMode) {
unmodified = this.mAccount.getJid().getLocal();
unmodified = this.mAccount.getJid().getEscapedLocal();
} else {
unmodified = this.mAccount.getJid().asBareJid().toString();
unmodified = this.mAccount.getJid().asBareJid().toEscapedString();
}
return !unmodified.equals(this.binding.accountJid.getText().toString());
}
@ -784,12 +784,6 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
this.mInitMode |= this.mAccount.isOptionSet(Account.OPTION_REGISTER);
this.mUsernameMode |= mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) && mAccount.isOptionSet(Account.OPTION_REGISTER);
if (this.mAccount.getPrivateKeyAlias() != null) {
this.binding.accountPassword.setHint(R.string.authenticate_with_certificate);
if (this.mInitMode) {
this.binding.accountPassword.requestFocus();
}
}
if (mPendingFingerprintVerificationUri != null) {
processFingerprintVerification(mPendingFingerprintVerificationUri, false);
mPendingFingerprintVerificationUri = null;
@ -822,7 +816,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
private String getUserModeDomain() {
if (mAccount != null && mAccount.getJid().getDomain() != null) {
return mAccount.getJid().getDomain();
return mAccount.getServer();
} else {
return Config.DOMAIN_LOCK;
}
@ -965,9 +959,9 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
if (init) {
this.binding.accountJid.getEditableText().clear();
if (mUsernameMode) {
this.binding.accountJid.getEditableText().append(this.mAccount.getJid().getLocal());
this.binding.accountJid.getEditableText().append(this.mAccount.getJid().getEscapedLocal());
} else {
this.binding.accountJid.getEditableText().append(this.mAccount.getJid().asBareJid().toString());
this.binding.accountJid.getEditableText().append(this.mAccount.getJid().asBareJid().toEscapedString());
}
this.binding.accountPassword.getEditableText().clear();
this.binding.accountPassword.getEditableText().append(this.mAccount.getPassword());

View File

@ -24,7 +24,7 @@ import eu.siacs.conversations.databinding.EnterJidDialogBinding;
import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
import eu.siacs.conversations.ui.util.DelayedHintHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class EnterJidDialog extends DialogFragment implements OnBackendConnected, TextWatcher {
@ -146,16 +146,16 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
}
try {
if (Config.DOMAIN_LOCK != null) {
accountJid = Jid.of((String) binding.account.getSelectedItem(), Config.DOMAIN_LOCK, null);
accountJid = Jid.ofEscaped((String) binding.account.getSelectedItem(), Config.DOMAIN_LOCK, null);
} else {
accountJid = Jid.of((String) binding.account.getSelectedItem());
accountJid = Jid.ofEscaped((String) binding.account.getSelectedItem());
}
} catch (final IllegalArgumentException e) {
return;
}
final Jid contactJid;
try {
contactJid = Jid.of(binding.jid.getText().toString());
contactJid = Jid.ofEscaped(binding.jid.getText().toString());
} catch (final IllegalArgumentException e) {
binding.jidLayout.setError(getActivity().getString(R.string.invalid_jid));
return;
@ -168,7 +168,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
issuedWarning = true;
return;
}
if (suspiciousSubDomain(contactJid.getDomain())) {
if (suspiciousSubDomain(contactJid.getDomain().toEscapedString())) {
binding.jidLayout.setError(getActivity().getString(R.string.this_looks_like_channel));
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
issuedWarning = true;

View File

@ -18,7 +18,7 @@ import eu.siacs.conversations.ui.adapter.MediaAdapter;
import eu.siacs.conversations.ui.interfaces.OnMediaLoaded;
import eu.siacs.conversations.ui.util.Attachment;
import eu.siacs.conversations.ui.util.GridManager;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class MediaBrowserActivity extends XmppActivity implements OnMediaLoaded {
@ -49,7 +49,7 @@ public class MediaBrowserActivity extends XmppActivity implements OnMediaLoaded
String account = intent == null ? null : intent.getStringExtra("account");
String jid = intent == null ? null : intent.getStringExtra("jid");
if (account != null && jid != null) {
xmppConnectionService.getAttachments(account, Jid.of(jid), 0, this);
xmppConnectionService.getAttachments(account, Jid.ofEscaped(jid), 0, this);
}
}

View File

@ -27,7 +27,7 @@ import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.adapter.UserAdapter;
import eu.siacs.conversations.ui.util.MucDetailsContextMenuHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class MucUsersActivity extends XmppActivity implements XmppConnectionService.OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged, MenuItem.OnActionExpandListener, TextWatcher {

View File

@ -54,7 +54,7 @@ import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import eu.siacs.conversations.xmpp.jingle.Media;
import eu.siacs.conversations.xmpp.jingle.RtpEndUserState;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
import static eu.siacs.conversations.utils.PermissionUtils.getFirstDenied;
import static java.util.Arrays.asList;
@ -129,7 +129,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
final Intent intent = getIntent();
final String action = intent.getAction();
final Account account = extractAccount(intent);
final Jid with = Jid.of(intent.getStringExtra(EXTRA_WITH));
final Jid with = Jid.ofEscaped(intent.getStringExtra(EXTRA_WITH));
final String state = intent.getStringExtra(EXTRA_LAST_REPORTED_STATE);
if (!Intent.ACTION_VIEW.equals(action) || state == null || !END_CARD.contains(RtpEndUserState.valueOf(state))) {
resetIntent(account, with, RtpEndUserState.RETRACTED, actionToMedia(intent.getAction()));
@ -246,7 +246,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
return;
}
final Account account = extractAccount(intent);
final Jid with = Jid.of(intent.getStringExtra(EXTRA_WITH));
final Jid with = Jid.ofEscaped(intent.getStringExtra(EXTRA_WITH));
final String sessionId = intent.getStringExtra(EXTRA_SESSION_ID);
if (sessionId != null) {
Log.d(Config.LOGTAG, "reinitializing from onNewIntent()");
@ -268,7 +268,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
final Intent intent = getIntent();
final String action = intent.getAction();
final Account account = extractAccount(intent);
final Jid with = Jid.of(intent.getStringExtra(EXTRA_WITH));
final Jid with = Jid.ofEscaped(intent.getStringExtra(EXTRA_WITH));
final String sessionId = intent.getStringExtra(EXTRA_SESSION_ID);
if (sessionId != null) {
if (initializeActivityWithRunningRtpSession(account, with, sessionId)) {
@ -831,7 +831,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
Log.d(Config.LOGTAG, "attempting retry");
final Intent intent = getIntent();
final Account account = extractAccount(intent);
final Jid with = Jid.of(intent.getStringExtra(EXTRA_WITH));
final Jid with = Jid.ofEscaped(intent.getStringExtra(EXTRA_WITH));
final String lastAction = intent.getStringExtra(EXTRA_LAST_ACTION);
final String action = intent.getAction();
final Set<Media> media = actionToMedia(lastAction == null ? action : lastAction);

View File

@ -41,7 +41,7 @@ import eu.siacs.conversations.services.QuickConversationsService;
import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.TimeFrameUtils;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class SettingsActivity extends XmppActivity implements
OnSharedPreferenceChangeListener {

View File

@ -12,7 +12,6 @@ import android.view.MenuItem;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import eu.siacs.conversations.Config;
@ -21,9 +20,7 @@ import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.adapter.ConversationAdapter;
import eu.siacs.conversations.ui.service.EmojiService;
import eu.siacs.conversations.utils.GeoHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class ShareWithActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate {

View File

@ -79,7 +79,7 @@ import eu.siacs.conversations.utils.AccountUtils;
import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.XmppConnection;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class StartConversationActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist, CreatePrivateGroupChatDialog.CreateConferenceDialogListener, JoinConferenceDialog.JoinConferenceDialogListener, SwipeRefreshLayout.OnRefreshListener, CreatePublicChannelDialog.CreatePublicChannelDialogListener {
@ -577,9 +577,9 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
Jid jid;
try {
if (Config.DOMAIN_LOCK != null) {
jid = Jid.of((String) spinner.getSelectedItem(), Config.DOMAIN_LOCK, null);
jid = Jid.ofEscaped((String) spinner.getSelectedItem(), Config.DOMAIN_LOCK, null);
} else {
jid = Jid.of((String) spinner.getSelectedItem());
jid = Jid.ofEscaped((String) spinner.getSelectedItem());
}
} catch (final IllegalArgumentException e) {
return null;
@ -855,11 +855,11 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
switchToConversationDoNotAppend(muc, invite.getBody());
return true;
} else {
showJoinConferenceDialog(invite.getJid().asBareJid().toString());
showJoinConferenceDialog(invite.getJid().asBareJid().toEscapedString());
return false;
}
} else if (contacts.size() == 0) {
showCreateContactDialog(invite.getJid().toString(), invite);
showCreateContactDialog(invite.getJid().toEscapedString(), invite);
return false;
} else if (contacts.size() == 1) {
Contact contact = contacts.get(0);
@ -881,10 +881,10 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
if (mMenuSearchView != null) {
mMenuSearchView.expandActionView();
mSearchEditText.setText("");
mSearchEditText.append(invite.getJid().toString());
filter(invite.getJid().toString());
mSearchEditText.append(invite.getJid().toEscapedString());
filter(invite.getJid().toEscapedString());
} else {
mInitialSearchValue.push(invite.getJid().toString());
mInitialSearchValue.push(invite.getJid().toEscapedString());
}
return true;
}
@ -1017,7 +1017,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
final String input = jid.getText().toString();
Jid conferenceJid;
try {
conferenceJid = Jid.of(input);
conferenceJid = Jid.ofEscaped(input);
} catch (final IllegalArgumentException e) {
final XmppUri xmppUri = new XmppUri(input);
if (xmppUri.isValidJid() && xmppUri.isAction(XmppUri.ACTION_JOIN)) {

View File

@ -38,7 +38,7 @@ import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.IrregularUnicodeDetector;
import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdated {
@ -213,7 +213,7 @@ public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdat
showCameraToast();
}
binding.ownKeysTitle.setText(mAccount.getJid().asBareJid().toString());
binding.ownKeysTitle.setText(mAccount.getJid().asBareJid().toEscapedString());
binding.ownKeysCard.setVisibility(hasOwnKeys ? View.VISIBLE : View.GONE);
binding.foreignKeys.setVisibility(hasForeignKeys ? View.VISIBLE : View.GONE);
if (hasPendingKeyFetches()) {

View File

@ -19,7 +19,7 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.persistance.DatabaseBackend;
import eu.siacs.conversations.utils.SignupUtils;
import eu.siacs.conversations.utils.XmppUri;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class UriHandlerActivity extends AppCompatActivity {

View File

@ -79,7 +79,7 @@ import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.utils.ThemeHelper;
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public abstract class XmppActivity extends ActionBarActivity {
@ -870,7 +870,7 @@ public abstract class XmppActivity extends ActionBarActivity {
protected Account extractAccount(Intent intent) {
final String jid = intent != null ? intent.getStringExtra(EXTRA_ACCOUNT) : null;
try {
return jid != null ? xmppConnectionService.findAccountByJid(Jid.of(jid)) : null;
return jid != null ? xmppConnectionService.findAccountByJid(Jid.ofEscaped(jid)) : null;
} catch (IllegalArgumentException e) {
return null;
}

View File

@ -58,7 +58,7 @@ public class AccountAdapter extends ArrayAdapter<Account> {
if (Config.DOMAIN_LOCK != null) {
viewHolder.binding.accountJid.setText(account.getJid().getLocal());
} else {
viewHolder.binding.accountJid.setText(account.getJid().asBareJid().toString());
viewHolder.binding.accountJid.setText(account.getJid().asBareJid().toEscapedString());
}
AvatarWorkerTask.loadAvatar(account, viewHolder.binding.accountImage, R.dimen.avatar);
viewHolder.binding.accountStatus.setText(getContext().getString(account.getStatus().getReadableId()));

View File

@ -19,7 +19,7 @@ import eu.siacs.conversations.databinding.SearchResultItemBinding;
import eu.siacs.conversations.entities.Room;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class ChannelSearchResultAdapter extends ListAdapter<Room, ChannelSearchResultAdapter.ViewHolder> implements View.OnCreateContextMenuListener {

View File

@ -25,9 +25,8 @@ import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.utils.EmojiWrapper;
import eu.siacs.conversations.utils.IrregularUnicodeDetector;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
import eu.siacs.conversations.xmpp.jingle.OngoingRtpSession;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class ConversationAdapter extends RecyclerView.Adapter<ConversationAdapter.ConversationViewHolder> {
@ -202,6 +201,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationAdapte
} else {
timestamp = conversation.getLatestMessage().getTimeSent();
}
viewHolder.binding.pinnedOnTop.setVisibility(conversation.getBooleanAttribute(Conversation.ATTRIBUTE_PINNED_ON_TOP,false) ? View.VISIBLE : View.GONE);
viewHolder.binding.conversationLastupdate.setText(UIHelper.readableTimeDifference(activity, timestamp));
AvatarWorkerTask.loadAvatar(conversation, viewHolder.binding.conversationImage, R.dimen.avatar_on_conversation_overview);
viewHolder.itemView.setOnClickListener(v -> listener.onConversationClick(v, conversation));

View File

@ -1,12 +1,7 @@
package eu.siacs.conversations.ui.adapter;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.databinding.DataBindingUtil;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
@ -17,9 +12,7 @@ import android.widget.TextView;
import com.wefika.flowlayout.FlowLayout;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.concurrent.RejectedExecutionException;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ContactBinding;
@ -30,9 +23,7 @@ import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.utils.EmojiWrapper;
import eu.siacs.conversations.utils.IrregularUnicodeDetector;
import eu.siacs.conversations.utils.ThemeHelper;
import eu.siacs.conversations.utils.UIHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class ListItemAdapter extends ArrayAdapter<ListItem> {

View File

@ -76,7 +76,7 @@ import eu.siacs.conversations.utils.StylingHelper;
import eu.siacs.conversations.utils.TimeFrameUtils;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.mam.MamReference;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextView.CopyHandler {

View File

@ -24,7 +24,7 @@ import eu.siacs.conversations.ui.ConferenceDetailsActivity;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.ui.util.MucDetailsContextMenuHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class UserAdapter extends ListAdapter<MucOptions.User, UserAdapter.ViewHolder> implements View.OnCreateContextMenuListener {

View File

@ -7,7 +7,7 @@ import java.util.List;
import eu.siacs.conversations.R;
import eu.siacs.conversations.xmpp.forms.Field;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class FormJidSingleFieldWrapper extends FormTextFieldWrapper {

View File

@ -24,7 +24,7 @@ import eu.siacs.conversations.ui.ConversationFragment;
import eu.siacs.conversations.ui.ConversationsActivity;
import eu.siacs.conversations.ui.MucUsersActivity;
import eu.siacs.conversations.ui.XmppActivity;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public final class MucDetailsContextMenuHelper {

View File

@ -43,7 +43,7 @@ import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Presences;
import eu.siacs.conversations.utils.CryptoHelper;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class PresenceSelector {

View File

@ -43,7 +43,7 @@ import eu.siacs.conversations.ui.ConversationsActivity;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.utils.Patterns;
import eu.siacs.conversations.utils.XmppUri;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class ShareUtil {

View File

@ -39,9 +39,9 @@ public class AccountUtils {
for (Account account : service.getAccounts()) {
if (account.getStatus() != Account.State.DISABLED) {
if (Config.DOMAIN_LOCK != null) {
accounts.add(account.getJid().getLocal());
accounts.add(account.getJid().toEscapedString());
} else {
accounts.add(account.getJid().asBareJid().toString());
accounts.add(account.getJid().asBareJid().toEscapedString());
}
}
}

View File

@ -3,9 +3,8 @@ package eu.siacs.conversations.utils;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class BackupFileHeader {

View File

@ -24,7 +24,6 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;
import eu.siacs.conversations.Config;
@ -32,7 +31,7 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.http.AesGcmURLStreamHandler;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public final class CryptoHelper {

View File

@ -53,7 +53,7 @@ import java.util.regex.Pattern;
import eu.siacs.conversations.R;
import eu.siacs.conversations.ui.util.StyledAttributes;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class IrregularUnicodeDetector {
@ -82,14 +82,14 @@ public class IrregularUnicodeDetector {
private static Spannable style(Jid jid, @ColorInt int color) {
PatternTuple patternTuple = find(jid);
SpannableStringBuilder builder = new SpannableStringBuilder();
if (jid.getLocal() != null && patternTuple.local != null) {
SpannableString local = new SpannableString(jid.getLocal());
if (jid.getEscapedLocal() != null && patternTuple.local != null) {
SpannableString local = new SpannableString(jid.getEscapedLocal());
colorize(local, patternTuple.local, color);
builder.append(local);
builder.append('@');
}
if (jid.getDomain() != null) {
String[] labels = jid.getDomain().split("\\.");
String[] labels = jid.getDomain().toEscapedString().split("\\.");
for (int i = 0; i < labels.length; ++i) {
SpannableString spannableString = new SpannableString(labels[i]);
colorize(spannableString, patternTuple.domain.get(i), color);
@ -258,12 +258,12 @@ public class IrregularUnicodeDetector {
private static PatternTuple of(Jid jid) {
final Pattern localPattern;
if (jid.getLocal() != null) {
localPattern = create(findIrregularCodePoints(jid.getLocal()));
if (jid.getEscapedLocal() != null) {
localPattern = create(findIrregularCodePoints(jid.getEscapedLocal()));
} else {
localPattern = null;
}
String domain = jid.getDomain();
String domain = jid.getDomain().toEscapedString();
final List<Pattern> domainPatterns = new ArrayList<>();
if (domain != null) {
for (String label : domain.split("\\.")) {

View File

@ -35,7 +35,7 @@ import java.util.List;
import java.util.Locale;
import eu.siacs.conversations.xmpp.InvalidJid;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class JidHelper {
@ -43,7 +43,7 @@ public class JidHelper {
public static String localPartOrFallback(Jid jid) {
if (LOCAL_PART_BLACKLIST.contains(jid.getLocal().toLowerCase(Locale.ENGLISH))) {
final String domain = jid.getDomain();
final String domain = jid.getDomain().toEscapedString();
final int index = domain.indexOf('.');
return index > 1 ? domain.substring(0, index) : domain;
} else {

View File

@ -5,7 +5,7 @@ import java.util.List;
import java.util.Set;
import eu.siacs.conversations.entities.Conversation;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class NickValidityChecker {

View File

@ -2,19 +2,13 @@ package eu.siacs.conversations.utils;
import android.content.Context;
import android.support.annotation.ColorInt;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.Pair;
import android.widget.PopupMenu;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
@ -34,7 +28,7 @@ import eu.siacs.conversations.entities.Presence;
import eu.siacs.conversations.entities.RtpSessionStatus;
import eu.siacs.conversations.entities.Transferable;
import eu.siacs.conversations.services.ExportBackupService;
import rocks.xmpp.addr.Jid;
import eu.siacs.conversations.xmpp.Jid;
public class UIHelper {
@ -526,7 +520,7 @@ public class UIHelper {
return ((Conversation) conversation).getMucOptions().getSelf().getName();
} else {
final Jid jid = conversation.getAccount().getJid();
return jid.getLocal() != null ? jid.getLocal() : Jid.ofDomain(jid.getDomain()).toString();
return jid.getLocal() != null ? jid.getLocal() : jid.getDomain().toString();
}
}
}

Some files were not shown because too many files have changed in this diff Show More