From fccce229c6a75e3a0b4845199323a00cadf21ac7 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Sun, 10 Jan 2016 14:56:55 -0500 Subject: Factor out a representation of XEP-0030 results And the parser from Element to this representation. --- .../entities/ServiceDiscoveryResult.java | 84 ++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java (limited to 'src/main/java/eu/siacs/conversations/entities') diff --git a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java new file mode 100644 index 000000000..963464402 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java @@ -0,0 +1,84 @@ +package eu.siacs.conversations.entities; + +import java.util.List; +import java.util.ArrayList; + +import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.stanzas.IqPacket; + +public class ServiceDiscoveryResult { + public static class Identity { + protected final String category; + protected final String type; + protected final String name; + + public Identity(final String category, final String type, final String name) { + this.category = category; + this.type = type; + this.name = name; + } + + public Identity(final Element el) { + this.category = el.getAttribute("category"); + this.type = el.getAttribute("type"); + this.name = el.getAttribute("name"); + } + + public String getCategory() { + return this.category; + } + + public String getType() { + return this.type; + } + + public String getName() { + return this.name; + } + } + + protected final List identities; + protected final List features; + + public ServiceDiscoveryResult(final List identities, final List features) { + this.identities = identities; + this.features = features; + } + + public ServiceDiscoveryResult(final IqPacket packet) { + this.identities = new ArrayList<>(); + this.features = new ArrayList<>(); + + final List elements = packet.query().getChildren(); + + for (final Element element : elements) { + if (element.getName().equals("identity")) { + Identity id = new Identity(element); + if (id.getType() != null && id.getCategory() != null) { + identities.add(id); + } + } else if (element.getName().equals("feature")) { + features.add(element.getAttribute("var")); + } + } + } + + public List getIdentities() { + return this.identities; + } + + public List getFeatures() { + return this.features; + } + + public boolean hasIdentity(String category, String type) { + for(Identity id : this.getIdentities()) { + if((category == null || id.getCategory().equals(category)) && + (type == null || id.getType().equals(type))) { + return true; + } + } + + return false; + } +} -- cgit v1.2.3 From 1e335d527b888963aa953542cfae866ade0d5867 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Sun, 10 Jan 2016 16:25:26 -0500 Subject: Generate capHash from any discovery result --- .../entities/ServiceDiscoveryResult.java | 84 ++++++++++++++++++++-- 1 file changed, 80 insertions(+), 4 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/entities') diff --git a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java index 963464402..853e75aac 100644 --- a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java +++ b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java @@ -1,26 +1,41 @@ package eu.siacs.conversations.entities; -import java.util.List; +import android.content.ContentValues; +import android.util.Base64; +import java.io.UnsupportedEncodingException; +import java.lang.Comparable; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.stanzas.IqPacket; public class ServiceDiscoveryResult { - public static class Identity { + + protected static String blankNull(String s) { + return s == null ? "" : s; + } + + public static class Identity implements Comparable { protected final String category; protected final String type; + protected final String lang; protected final String name; - public Identity(final String category, final String type, final String name) { + public Identity(final String category, final String type, final String lang, final String name) { this.category = category; this.type = type; + this.lang = lang; this.name = name; } public Identity(final Element el) { this.category = el.getAttribute("category"); this.type = el.getAttribute("type"); + this.lang = el.getAttribute("xml:lang"); this.name = el.getAttribute("name"); } @@ -32,9 +47,29 @@ public class ServiceDiscoveryResult { return this.type; } + public String getLang() { + return this.lang; + } + public String getName() { return this.name; } + + public int compareTo(Object other) { + Identity o = (Identity)other; + int r = blankNull(this.getCategory()).compareTo(blankNull(o.getCategory())); + if(r == 0) { + r = blankNull(this.getType()).compareTo(blankNull(o.getType())); + } + if(r == 0) { + r = blankNull(this.getLang()).compareTo(blankNull(o.getLang())); + } + if(r == 0) { + r = blankNull(this.getName()).compareTo(blankNull(o.getName())); + } + + return r; + } } protected final List identities; @@ -58,7 +93,9 @@ public class ServiceDiscoveryResult { identities.add(id); } } else if (element.getName().equals("feature")) { - features.add(element.getAttribute("var")); + if (element.getAttribute("var") != null) { + features.add(element.getAttribute("var")); + } } } } @@ -81,4 +118,43 @@ public class ServiceDiscoveryResult { return false; } + + public byte[] getCapHash() { + StringBuilder s = new StringBuilder(); + + List identities = this.getIdentities(); + Collections.sort(identities); + + for(Identity id : identities) { + s.append( + blankNull(id.getCategory()) + "/" + + blankNull(id.getType()) + "/" + + blankNull(id.getLang()) + "/" + + blankNull(id.getName()) + "<" + ); + } + + List features = this.getFeatures(); + Collections.sort(features); + + for (String feature : features) { + s.append(feature + "<"); + } + + // TODO: data forms? + + MessageDigest md; + try { + md = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + return null; + } + + try { + return md.digest(s.toString().getBytes("UTF-8")); + } catch(UnsupportedEncodingException e) { + return null; + } + } + } -- cgit v1.2.3 From 56f8fff935baa4cec804807cfed3728d11c086ed Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Sun, 10 Jan 2016 16:43:48 -0500 Subject: Implement toJSON on ServiceDiscoveryResult --- .../entities/ServiceDiscoveryResult.java | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'src/main/java/eu/siacs/conversations/entities') diff --git a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java index 853e75aac..ac4e8c47e 100644 --- a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java +++ b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java @@ -9,6 +9,9 @@ import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.stanzas.IqPacket; @@ -70,6 +73,19 @@ public class ServiceDiscoveryResult { return r; } + + public JSONObject toJSON() { + try { + JSONObject o = new JSONObject(); + o.put("category", this.getCategory()); + o.put("type", this.getType()); + o.put("lang", this.getLang()); + o.put("name", this.getName()); + return o; + } catch(JSONException e) { + return null; + } + } } protected final List identities; @@ -157,4 +173,22 @@ public class ServiceDiscoveryResult { } } + public JSONObject toJSON() { + try { + JSONObject o = new JSONObject(); + + JSONArray ids = new JSONArray(); + for(Identity id : this.getIdentities()) { + ids.put(id.toJSON()); + } + o.put("identites", ids); + + o.put("features", new JSONArray(this.getFeatures())); + + return o; + } catch(JSONException e) { + return null; + } + } + } -- cgit v1.2.3 From ad36a4ba89f880a5522ffd5179dcaa98985e4164 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Tue, 12 Jan 2016 21:53:38 -0500 Subject: Persisitence and loading for ServiceDiscoveryResult --- .../entities/ServiceDiscoveryResult.java | 68 ++++++++++++++++++---- 1 file changed, 58 insertions(+), 10 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/entities') diff --git a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java index ac4e8c47e..9d77efab8 100644 --- a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java +++ b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java @@ -1,6 +1,7 @@ package eu.siacs.conversations.entities; import android.content.ContentValues; +import android.database.Cursor; import android.util.Base64; import java.io.UnsupportedEncodingException; import java.lang.Comparable; @@ -17,6 +18,10 @@ import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.stanzas.IqPacket; public class ServiceDiscoveryResult { + public static final String TABLENAME = "discovery_results"; + public static final String HASH = "hash"; + public static final String VER = "ver"; + public static final String RESULT = "result"; protected static String blankNull(String s) { return s == null ? "" : s; @@ -36,10 +41,21 @@ public class ServiceDiscoveryResult { } public Identity(final Element el) { - this.category = el.getAttribute("category"); - this.type = el.getAttribute("type"); - this.lang = el.getAttribute("xml:lang"); - this.name = el.getAttribute("name"); + this( + el.getAttribute("category"), + el.getAttribute("type"), + el.getAttribute("xml:lang"), + el.getAttribute("name") + ); + } + + public Identity(final JSONObject o) { + this( + o.optString("category", null), + o.optString("type", null), + o.optString("lang", null), + o.optString("name", null) + ); } public String getCategory() { @@ -88,17 +104,15 @@ public class ServiceDiscoveryResult { } } + protected final String hash; + protected final byte[] ver; protected final List identities; protected final List features; - public ServiceDiscoveryResult(final List identities, final List features) { - this.identities = identities; - this.features = features; - } - public ServiceDiscoveryResult(final IqPacket packet) { this.identities = new ArrayList<>(); this.features = new ArrayList<>(); + this.hash = "sha-1"; // We only support sha-1 for now final List elements = packet.query().getChildren(); @@ -114,6 +128,33 @@ public class ServiceDiscoveryResult { } } } + + this.ver = this.mkCapHash(); + } + + public ServiceDiscoveryResult(String hash, byte[] ver, JSONObject o) throws JSONException { + this.identities = new ArrayList<>(); + this.features = new ArrayList<>(); + this.hash = hash; + this.ver = ver; + + JSONArray identities = o.optJSONArray("identities"); + for(int i = 0; i < identities.length(); i++) { + this.identities.add(new Identity(identities.getJSONObject(i))); + } + + JSONArray features = o.optJSONArray("features"); + for(int i = 0; i < features.length(); i++) { + this.features.add(features.getString(i)); + } + } + + public ServiceDiscoveryResult(Cursor cursor) throws JSONException { + this( + cursor.getString(cursor.getColumnIndex(HASH)), + Base64.decode(cursor.getString(cursor.getColumnIndex(VER)), Base64.DEFAULT), + new JSONObject(cursor.getString(cursor.getColumnIndex(RESULT))) + ); } public List getIdentities() { @@ -135,7 +176,7 @@ public class ServiceDiscoveryResult { return false; } - public byte[] getCapHash() { + protected byte[] mkCapHash() { StringBuilder s = new StringBuilder(); List identities = this.getIdentities(); @@ -191,4 +232,11 @@ public class ServiceDiscoveryResult { } } + public ContentValues getContentValues() { + final ContentValues values = new ContentValues(); + values.put(HASH, this.hash); + values.put(VER, new String(Base64.encode(this.ver, Base64.DEFAULT)).trim()); + values.put(RESULT, this.toJSON().toString()); + return values; + } } -- cgit v1.2.3 From bf5b2f73f5a61f0a0179c9d4431579f87eecb001 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Sun, 17 Jan 2016 16:28:38 -0500 Subject: Use a Presence class for presence information Only has status for now, but doing it so I can add disco to it --- .../eu/siacs/conversations/entities/Contact.java | 23 ++++++---- .../eu/siacs/conversations/entities/Presence.java | 49 ++++++++++++++++++++++ .../eu/siacs/conversations/entities/Presences.java | 46 ++++---------------- 3 files changed, 71 insertions(+), 47 deletions(-) create mode 100644 src/main/java/eu/siacs/conversations/entities/Presence.java (limited to 'src/main/java/eu/siacs/conversations/entities') diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java index c2d8b278b..ed773b6e6 100644 --- a/src/main/java/eu/siacs/conversations/entities/Contact.java +++ b/src/main/java/eu/siacs/conversations/entities/Contact.java @@ -135,17 +135,17 @@ public class Contact implements ListItem, Blockable { tags.add(new Tag(group, UIHelper.getColorForName(group))); } switch (getMostAvailableStatus()) { - case Presences.CHAT: - case Presences.ONLINE: + case CHAT: + case ONLINE: tags.add(new Tag("online", 0xff259b24)); break; - case Presences.AWAY: + case AWAY: tags.add(new Tag("away", 0xffff9800)); break; - case Presences.XA: + case XA: tags.add(new Tag("not available", 0xfff44336)); break; - case Presences.DND: + case DND: tags.add(new Tag("dnd", 0xfff44336)); break; } @@ -225,8 +225,8 @@ public class Contact implements ListItem, Blockable { this.presences = pres; } - public void updatePresence(final String resource, final int status) { - this.presences.updatePresence(resource, status); + public void updatePresence(final String resource, final Presence presence) { + this.presences.updatePresence(resource, presence); } public void removePresence(final String resource) { @@ -238,8 +238,13 @@ public class Contact implements ListItem, Blockable { this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST); } - public int getMostAvailableStatus() { - return this.presences.getMostAvailableStatus(); + public Presence.Status getMostAvailableStatus() { + Presence p = this.presences.getMostAvailablePresence(); + if (p == null) { + return Presence.Status.OFFLINE; + } + + return p.getStatus(); } public boolean setPhotoUri(String uri) { diff --git a/src/main/java/eu/siacs/conversations/entities/Presence.java b/src/main/java/eu/siacs/conversations/entities/Presence.java new file mode 100644 index 000000000..adba74d12 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/entities/Presence.java @@ -0,0 +1,49 @@ +package eu.siacs.conversations.entities; + +import java.lang.Comparable; + +import eu.siacs.conversations.xml.Element; + +public class Presence implements Comparable { + + public enum Status { + CHAT, ONLINE, AWAY, XA, DND, OFFLINE; + + public String toShowString() { + switch(this) { + case CHAT: return "chat"; + case AWAY: return "away"; + case XA: return "xa"; + case DND: return "dnd"; + } + + return null; + } + } + + protected final Status status; + + public Presence(Element show) { + if ((show == null) || (show.getContent() == null)) { + this.status = Status.ONLINE; + } else if (show.getContent().equals("away")) { + this.status = Status.AWAY; + } else if (show.getContent().equals("xa")) { + this.status = Status.XA; + } else if (show.getContent().equals("chat")) { + this.status = Status.CHAT; + } else if (show.getContent().equals("dnd")) { + this.status = Status.DND; + } else { + this.status = Status.OFFLINE; + } + } + + public int compareTo(Object other) { + return this.status.compareTo(((Presence)other).status); + } + + public Status getStatus() { + return this.status; + } +} diff --git a/src/main/java/eu/siacs/conversations/entities/Presences.java b/src/main/java/eu/siacs/conversations/entities/Presences.java index 4729a11b9..813eda7a9 100644 --- a/src/main/java/eu/siacs/conversations/entities/Presences.java +++ b/src/main/java/eu/siacs/conversations/entities/Presences.java @@ -1,29 +1,21 @@ package eu.siacs.conversations.entities; +import java.util.Collections; import java.util.Hashtable; import java.util.Iterator; -import java.util.Map.Entry; import eu.siacs.conversations.xml.Element; public class Presences { + private final Hashtable presences = new Hashtable<>(); - public static final int CHAT = -1; - public static final int ONLINE = 0; - public static final int AWAY = 1; - public static final int XA = 2; - public static final int DND = 3; - public static final int OFFLINE = 4; - - private final Hashtable presences = new Hashtable<>(); - - public Hashtable getPresences() { + public Hashtable getPresences() { return this.presences; } - public void updatePresence(String resource, int status) { + public void updatePresence(String resource, Presence presence) { synchronized (this.presences) { - this.presences.put(resource, status); + this.presences.put(resource, presence); } } @@ -39,32 +31,10 @@ public class Presences { } } - public int getMostAvailableStatus() { - int status = OFFLINE; + public Presence getMostAvailablePresence() { synchronized (this.presences) { - Iterator> it = presences.entrySet().iterator(); - while (it.hasNext()) { - Entry entry = it.next(); - if (entry.getValue() < status) - status = entry.getValue(); - } - } - return status; - } - - public static int parseShow(Element show) { - if ((show == null) || (show.getContent() == null)) { - return Presences.ONLINE; - } else if (show.getContent().equals("away")) { - return Presences.AWAY; - } else if (show.getContent().equals("xa")) { - return Presences.XA; - } else if (show.getContent().equals("chat")) { - return Presences.CHAT; - } else if (show.getContent().equals("dnd")) { - return Presences.DND; - } else { - return Presences.OFFLINE; + if (presences.size() < 1) { return null; } + return Collections.min(presences.values()); } } -- cgit v1.2.3 From 000f59d614d07ac573c1159ea964ebfee6ecbef5 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Sun, 17 Jan 2016 16:46:32 -0500 Subject: Fetch cached caps result on new presence --- src/main/java/eu/siacs/conversations/entities/Presence.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/main/java/eu/siacs/conversations/entities') diff --git a/src/main/java/eu/siacs/conversations/entities/Presence.java b/src/main/java/eu/siacs/conversations/entities/Presence.java index adba74d12..d31bb69d5 100644 --- a/src/main/java/eu/siacs/conversations/entities/Presence.java +++ b/src/main/java/eu/siacs/conversations/entities/Presence.java @@ -22,8 +22,11 @@ public class Presence implements Comparable { } protected final Status status; + protected final ServiceDiscoveryResult disco; + + public Presence(Element show, ServiceDiscoveryResult disco) { + this.disco = disco; - public Presence(Element show) { if ((show == null) || (show.getContent() == null)) { this.status = Status.ONLINE; } else if (show.getContent().equals("away")) { -- cgit v1.2.3 From a3e11415ec06945bea88b49c99a5662773c8bf17 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 1 Feb 2016 12:11:40 +0100 Subject: refactored user handling in conferences. show try again button when conference has errors --- .../siacs/conversations/entities/MucOptions.java | 141 +++++++++------------ 1 file changed, 57 insertions(+), 84 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/entities') diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java index 014a4c985..7f4ded119 100644 --- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java +++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java @@ -3,7 +3,10 @@ package eu.siacs.conversations.entities; import android.annotation.SuppressLint; import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import eu.siacs.conversations.R; import eu.siacs.conversations.xmpp.forms.Data; @@ -64,7 +67,7 @@ public class MucOptions { PARTICIPANT("participant", R.string.participant,2), NONE("none", R.string.no_role,0); - private Role(String string, int resId, int rank) { + Role(String string, int resId, int rank) { this.string = string; this.resId = resId; this.rank = rank; @@ -94,6 +97,7 @@ public class MucOptions { public static final int ERROR_PASSWORD_REQUIRED = 3; public static final int ERROR_BANNED = 4; public static final int ERROR_MEMBERS_ONLY = 5; + public static final int ERROR_NO_RESPONSE = 6; public static final int KICKED_FROM_ROOM = 9; @@ -236,12 +240,12 @@ public class MucOptions { } private Account account; - private final List users = new ArrayList<>(); + private final Map users = Collections.synchronizedMap(new LinkedHashMap()); private List features = new ArrayList<>(); private Data form = new Data(); private Conversation conversation; private boolean isOnline = false; - private int error = ERROR_UNKNOWN; + private int error = ERROR_NO_RESPONSE; public OnRenameListener onRenameListener = null; private User self; private String subject = null; @@ -303,40 +307,15 @@ public class MucOptions { } public User deleteUser(String name) { - synchronized (this.users) { - for (int i = 0; i < users.size(); ++i) { - if (users.get(i).getName().equals(name)) { - return users.remove(i); - } - } - } - return null; + return this.users.remove(name); } public void addUser(User user) { - synchronized (this.users) { - for (int i = 0; i < users.size(); ++i) { - if (users.get(i).getName().equals(user.getName())) { - users.set(i, user); - return; - } - } - users.add(user); - } + this.users.put(user.getName(), user); } public User findUser(String name) { - if (name == null) { - return null; - } - synchronized (this.users) { - for (User user : users) { - if (user.getName().equals(name)) { - return user; - } - } - } - return null; + return this.users.get(name); } public boolean isUserInRoom(String name) { @@ -344,26 +323,34 @@ public class MucOptions { } public void setError(int error) { - this.isOnline = error == ERROR_NO_ERROR; + this.isOnline = isOnline && error == ERROR_NO_ERROR; this.error = error; } + public void setOnline() { + this.isOnline = true; + } + public ArrayList getUsers() { - synchronized (this.users) { - return new ArrayList(this.users); - } + return new ArrayList<>(users.values()); } public List getUsers(int max) { - synchronized (this.users) { - return new ArrayList<>(users.subList(0,Math.min(users.size(),5))); + ArrayList users = new ArrayList<>(); + int i = 1; + for(User user : this.users.values()) { + users.add(user); + if (i >= max) { + break; + } else { + ++i; + } } + return users; } public int getUserCount() { - synchronized (this.users) { - return this.users.size(); - } + return this.users.size(); } public String getProposedNick() { @@ -400,7 +387,7 @@ public class MucOptions { public void setOffline() { this.users.clear(); - this.error = 0; + this.error = ERROR_NO_RESPONSE; this.isOnline = false; } @@ -417,38 +404,34 @@ public class MucOptions { } public String createNameFromParticipants() { - synchronized (this.users) { - if (users.size() >= 2) { - List names = new ArrayList(); - for (User user : users) { - Contact contact = user.getContact(); - if (contact != null && !contact.getDisplayName().isEmpty()) { - names.add(contact.getDisplayName().split("\\s+")[0]); - } else { - names.add(user.getName()); - } + if (users.size() >= 2) { + List names = new ArrayList<>(); + for (User user : getUsers(5)) { + Contact contact = user.getContact(); + if (contact != null && !contact.getDisplayName().isEmpty()) { + names.add(contact.getDisplayName().split("\\s+")[0]); + } else { + names.add(user.getName()); } - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < names.size(); ++i) { - builder.append(names.get(i)); - if (i != names.size() - 1) { - builder.append(", "); - } + } + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < names.size(); ++i) { + builder.append(names.get(i)); + if (i != names.size() - 1) { + builder.append(", "); } - return builder.toString(); - } else { - return null; } + return builder.toString(); + } else { + return null; } } public long[] getPgpKeyIds() { List ids = new ArrayList<>(); - synchronized (this.users) { - for (User user : this.users) { - if (user.getPgpKeyId() != 0) { - ids.add(user.getPgpKeyId()); - } + for (User user : this.users.values()) { + if (user.getPgpKeyId() != 0) { + ids.add(user.getPgpKeyId()); } } ids.add(account.getPgpId()); @@ -460,22 +443,18 @@ public class MucOptions { } public boolean pgpKeysInUse() { - synchronized (this.users) { - for (User user : this.users) { - if (user.getPgpKeyId() != 0) { - return true; - } + for (User user : this.users.values()) { + if (user.getPgpKeyId() != 0) { + return true; } } return false; } public boolean everybodyHasKeys() { - synchronized (this.users) { - for (User user : this.users) { - if (user.getPgpKeyId() == 0) { - return false; - } + for (User user : this.users.values()) { + if (user.getPgpKeyId() == 0) { + return false; } } return true; @@ -489,15 +468,9 @@ public class MucOptions { } } - public Jid getTrueCounterpart(String counterpart) { - synchronized (this.users) { - for (User user : this.users) { - if (user.getName().equals(counterpart)) { - return user.getJid(); - } - } - } - return null; + public Jid getTrueCounterpart(String name) { + User user = findUser(name); + return user == null ? null : user.getJid(); } public String getPassword() { -- cgit v1.2.3 From fab0a45955e04fa3210da651ac74ea6541abadab Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 2 Feb 2016 13:43:20 +0100 Subject: re-read common name from certificates on startup --- src/main/java/eu/siacs/conversations/entities/Contact.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/entities') diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java index c2d8b278b..b338656ba 100644 --- a/src/main/java/eu/siacs/conversations/entities/Contact.java +++ b/src/main/java/eu/siacs/conversations/entities/Contact.java @@ -38,6 +38,7 @@ public class Contact implements ListItem, Blockable { protected String systemName; protected String serverName; protected String presenceName; + protected String commonName; protected Jid jid; protected int subscription = 0; protected String systemAccount; @@ -105,8 +106,8 @@ public class Contact implements ListItem, Blockable { } public String getDisplayName() { - if (this.presenceName != null && Config.X509_VERIFICATION) { - return this.presenceName; + if (this.commonName != null && Config.X509_VERIFICATION) { + return this.commonName; } else if (this.systemName != null) { return this.systemName; } else if (this.serverName != null) { @@ -510,6 +511,10 @@ public class Contact implements ListItem, Blockable { return account.getJid().toBareJid().equals(getJid().toBareJid()); } + public void setCommonName(String cn) { + this.commonName = cn; + } + public static class Lastseen { public long time; public String presence; -- cgit v1.2.3 From f0798216d568bca30051ba5392263da31e78eb98 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 3 Feb 2016 10:40:02 +0100 Subject: refactored disco cache. avoid making duplicate call. check hash --- .../eu/siacs/conversations/entities/Account.java | 3 ++ .../eu/siacs/conversations/entities/Presence.java | 42 +++++++++++++++++----- .../entities/ServiceDiscoveryResult.java | 20 +++++++---- 3 files changed, 49 insertions(+), 16 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/entities') diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java index 4abfc801a..356b34e57 100644 --- a/src/main/java/eu/siacs/conversations/entities/Account.java +++ b/src/main/java/eu/siacs/conversations/entities/Account.java @@ -3,6 +3,7 @@ package eu.siacs.conversations.entities; import android.content.ContentValues; import android.database.Cursor; import android.os.SystemClock; +import android.util.Pair; import eu.siacs.conversations.crypto.PgpDecryptionService; import net.java.otr4j.crypto.OtrCryptoEngineImpl; @@ -14,6 +15,7 @@ import org.json.JSONObject; import java.security.PublicKey; import java.security.interfaces.DSAPublicKey; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; @@ -48,6 +50,7 @@ public class Account extends AbstractEntity { public static final int OPTION_DISABLED = 1; public static final int OPTION_REGISTER = 2; public static final int OPTION_USECOMPRESSION = 3; + public final HashSet> inProgressDiscoFetches = new HashSet<>(); public boolean httpUploadAvailable() { return xmppConnection != null && xmppConnection.getFeatures().httpUpload(); diff --git a/src/main/java/eu/siacs/conversations/entities/Presence.java b/src/main/java/eu/siacs/conversations/entities/Presence.java index d31bb69d5..69cde8327 100644 --- a/src/main/java/eu/siacs/conversations/entities/Presence.java +++ b/src/main/java/eu/siacs/conversations/entities/Presence.java @@ -22,23 +22,31 @@ public class Presence implements Comparable { } protected final Status status; - protected final ServiceDiscoveryResult disco; + protected ServiceDiscoveryResult disco; + protected final String ver; + protected final String hash; - public Presence(Element show, ServiceDiscoveryResult disco) { - this.disco = disco; + private Presence(Status status, String ver, String hash) { + this.status = status; + this.ver = ver; + this.hash = hash; + } + public static Presence parse(Element show, Element caps) { + final String hash = caps == null ? null : caps.getAttribute("hash"); + final String ver = caps == null ? null : caps.getAttribute("ver"); if ((show == null) || (show.getContent() == null)) { - this.status = Status.ONLINE; + return new Presence(Status.ONLINE, ver, hash); } else if (show.getContent().equals("away")) { - this.status = Status.AWAY; + return new Presence(Status.AWAY, ver, hash); } else if (show.getContent().equals("xa")) { - this.status = Status.XA; + return new Presence(Status.XA, ver, hash); } else if (show.getContent().equals("chat")) { - this.status = Status.CHAT; + return new Presence(Status.CHAT, ver, hash); } else if (show.getContent().equals("dnd")) { - this.status = Status.DND; + return new Presence(Status.DND, ver, hash); } else { - this.status = Status.OFFLINE; + return new Presence(Status.OFFLINE, ver, hash); } } @@ -49,4 +57,20 @@ public class Presence implements Comparable { public Status getStatus() { return this.status; } + + public boolean hasCaps() { + return ver != null && hash != null; + } + + public String getVer() { + return this.ver; + } + + public String getHash() { + return this.hash; + } + + public void setServiceDiscoveryResult(ServiceDiscoveryResult disco) { + this.disco = disco; + } } diff --git a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java index 9d77efab8..c50640e1f 100644 --- a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java +++ b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java @@ -128,7 +128,6 @@ public class ServiceDiscoveryResult { } } } - this.ver = this.mkCapHash(); } @@ -139,16 +138,23 @@ public class ServiceDiscoveryResult { this.ver = ver; JSONArray identities = o.optJSONArray("identities"); - for(int i = 0; i < identities.length(); i++) { - this.identities.add(new Identity(identities.getJSONObject(i))); + if (identities != null) { + for (int i = 0; i < identities.length(); i++) { + this.identities.add(new Identity(identities.getJSONObject(i))); + } } - JSONArray features = o.optJSONArray("features"); - for(int i = 0; i < features.length(); i++) { - this.features.add(features.getString(i)); + if (features != null) { + for (int i = 0; i < features.length(); i++) { + this.features.add(features.getString(i)); + } } } + public String getVer() { + return new String(Base64.encode(this.ver, Base64.DEFAULT)).trim(); + } + public ServiceDiscoveryResult(Cursor cursor) throws JSONException { this( cursor.getString(cursor.getColumnIndex(HASH)), @@ -235,7 +241,7 @@ public class ServiceDiscoveryResult { public ContentValues getContentValues() { final ContentValues values = new ContentValues(); values.put(HASH, this.hash); - values.put(VER, new String(Base64.encode(this.ver, Base64.DEFAULT)).trim()); + values.put(VER, getVer()); values.put(RESULT, this.toJSON().toString()); return values; } -- cgit v1.2.3 From 1d572c61d0a55d5ac7a96ccaacd0f3243253757e Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 3 Feb 2016 17:19:05 +0100 Subject: cache server caps --- .../entities/ServiceDiscoveryResult.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'src/main/java/eu/siacs/conversations/entities') diff --git a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java index c50640e1f..42f2d8409 100644 --- a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java +++ b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java @@ -9,12 +9,14 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.forms.Data; import eu.siacs.conversations.xmpp.stanzas.IqPacket; public class ServiceDiscoveryResult { @@ -108,10 +110,12 @@ public class ServiceDiscoveryResult { protected final byte[] ver; protected final List identities; protected final List features; + protected final List forms; public ServiceDiscoveryResult(final IqPacket packet) { this.identities = new ArrayList<>(); this.features = new ArrayList<>(); + this.forms = new ArrayList<>(); this.hash = "sha-1"; // We only support sha-1 for now final List elements = packet.query().getChildren(); @@ -126,6 +130,8 @@ public class ServiceDiscoveryResult { if (element.getAttribute("var") != null) { features.add(element.getAttribute("var")); } + } else if (element.getName().equals("x") && "jabber:x:data".equals(element.getAttribute("xmlns"))) { + forms.add(Data.parse(element)); } } this.ver = this.mkCapHash(); @@ -134,6 +140,7 @@ public class ServiceDiscoveryResult { public ServiceDiscoveryResult(String hash, byte[] ver, JSONObject o) throws JSONException { this.identities = new ArrayList<>(); this.features = new ArrayList<>(); + this.forms = new ArrayList<>(); this.hash = hash; this.ver = ver; @@ -204,7 +211,17 @@ public class ServiceDiscoveryResult { s.append(feature + "<"); } - // TODO: data forms? + Collections.sort(forms, new Comparator() { + @Override + public int compare(Data lhs, Data rhs) { + return lhs.getFormType().compareTo(rhs.getFormType()); + } + }); + + for(Data form : forms) { + s.append(form.getFormType()+"<"); + //TODO append fields and values + } MessageDigest md; try { -- cgit v1.2.3 From f88b8c703eca10f6a0b96e01e5ba912b02488759 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 4 Feb 2016 11:55:42 +0100 Subject: add more fault tolerant checks for messages left on server --- src/main/java/eu/siacs/conversations/entities/Conversation.java | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/main/java/eu/siacs/conversations/entities') diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index 22607fc6e..f2c08b9a1 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -81,6 +81,7 @@ public class Conversation extends AbstractEntity implements Blockable { private ChatState mOutgoingChatState = Config.DEFAULT_CHATSTATE; private ChatState mIncomingChatState = Config.DEFAULT_CHATSTATE; private String mLastReceivedOtrMessageId = null; + private String mFirstMamReference = null; public boolean hasMessagesLeftOnServer() { return messagesLeftOnServer; @@ -277,6 +278,14 @@ public class Conversation extends AbstractEntity implements Blockable { } } + public void setFirstMamReference(String reference) { + this.mFirstMamReference = reference; + } + + public String getFirstMamReference() { + return this.mFirstMamReference; + } + public interface OnMessageFound { void onMessageFound(final Message message); } -- cgit v1.2.3 From 4fdb0d92fe854126ad5201d56752716b9ab581b2 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 4 Feb 2016 14:39:16 +0100 Subject: prevent previoulsly cleared messages from reloading. fixes #1110 --- .../java/eu/siacs/conversations/entities/Conversation.java | 12 ++++++++++++ src/main/java/eu/siacs/conversations/entities/Message.java | 8 ++++++++ 2 files changed, 20 insertions(+) (limited to 'src/main/java/eu/siacs/conversations/entities') diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index f2c08b9a1..cb472b87c 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -286,6 +286,14 @@ public class Conversation extends AbstractEntity implements Blockable { return this.mFirstMamReference; } + public void setLastClearHistory(long time) { + setAttribute("last_clear_history",String.valueOf(time)); + } + + public long getLastClearHistory() { + return getLongAttribute("last_clear_history", 0); + } + public interface OnMessageFound { void onMessageFound(final Message message); } @@ -720,6 +728,10 @@ public class Conversation extends AbstractEntity implements Blockable { } public long getLastMessageTransmitted() { + long last_clear = getLastClearHistory(); + if (last_clear != 0) { + return last_clear; + } synchronized (this.messages) { for(int i = this.messages.size() - 1; i >= 0; --i) { Message message = this.messages.get(i); diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index f3d891e86..f37ae4271 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -178,6 +178,14 @@ public class Message extends AbstractEntity { return message; } + public static Message createLoadMoreMessage(Conversation conversation) { + final Message message = new Message(); + message.setType(Message.TYPE_STATUS); + message.setConversation(conversation); + message.setBody("LOAD_MORE"); + return message; + } + @Override public ContentValues getContentValues() { ContentValues values = new ContentValues(); -- cgit v1.2.3 From 91ec4839acef3e395e83815f211dd9771af1287e Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 4 Feb 2016 16:40:18 +0100 Subject: prepend instead off append mam messages to conversations when going in reverse --- src/main/java/eu/siacs/conversations/entities/Conversation.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/main/java/eu/siacs/conversations/entities') diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index cb472b87c..53bd19a5f 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -752,7 +752,7 @@ public class Conversation extends AbstractEntity implements Blockable { } public boolean alwaysNotify() { - return mode == MODE_SINGLE || getBooleanAttribute(ATTRIBUTE_ALWAYS_NOTIFY,Config.ALWAYS_NOTIFY_BY_DEFAULT || isPnNA()); + return mode == MODE_SINGLE || getBooleanAttribute(ATTRIBUTE_ALWAYS_NOTIFY, Config.ALWAYS_NOTIFY_BY_DEFAULT || isPnNA()); } public boolean setAttribute(String key, String value) { @@ -814,6 +814,13 @@ public class Conversation extends AbstractEntity implements Blockable { } } + public void prepend(Message message) { + message.setConversation(this); + synchronized (this.messages) { + this.messages.add(0,message); + } + } + public void addAll(int index, List messages) { synchronized (this.messages) { this.messages.addAll(index, messages); -- cgit v1.2.3