fixed caps hash generation for empty form values
This commit is contained in:
		
							parent
							
								
									956f7c6812
								
							
						
					
					
						commit
						2a9413e64d
					
				| @ -1,5 +1,7 @@ | |||||||
| package eu.siacs.conversations.entities; | package eu.siacs.conversations.entities; | ||||||
| 
 | 
 | ||||||
|  | import android.support.annotation.NonNull; | ||||||
|  | 
 | ||||||
| import java.lang.Comparable; | import java.lang.Comparable; | ||||||
| import java.util.Locale; | import java.util.Locale; | ||||||
| 
 | 
 | ||||||
| @ -62,7 +64,7 @@ public class Presence implements Comparable { | |||||||
| 		return new Presence(Status.fromShowString(show), ver, hash, node, message); | 		return new Presence(Status.fromShowString(show), ver, hash, node, message); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	public int compareTo(Object other) { | 	public int compareTo(@NonNull Object other) { | ||||||
| 		return this.status.compareTo(((Presence)other).status); | 		return this.status.compareTo(((Presence)other).status); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,7 +2,10 @@ package eu.siacs.conversations.entities; | |||||||
| 
 | 
 | ||||||
| import android.content.ContentValues; | import android.content.ContentValues; | ||||||
| import android.database.Cursor; | import android.database.Cursor; | ||||||
|  | import android.support.annotation.NonNull; | ||||||
| import android.util.Base64; | import android.util.Base64; | ||||||
|  | import android.util.Log; | ||||||
|  | 
 | ||||||
| import java.io.UnsupportedEncodingException; | import java.io.UnsupportedEncodingException; | ||||||
| import java.lang.Comparable; | import java.lang.Comparable; | ||||||
| import java.security.MessageDigest; | import java.security.MessageDigest; | ||||||
| @ -16,6 +19,7 @@ import org.json.JSONArray; | |||||||
| import org.json.JSONException; | import org.json.JSONException; | ||||||
| import org.json.JSONObject; | import org.json.JSONObject; | ||||||
| 
 | 
 | ||||||
|  | import eu.siacs.conversations.Config; | ||||||
| import eu.siacs.conversations.xml.Element; | import eu.siacs.conversations.xml.Element; | ||||||
| import eu.siacs.conversations.xml.Namespace; | import eu.siacs.conversations.xml.Namespace; | ||||||
| import eu.siacs.conversations.xmpp.forms.Data; | import eu.siacs.conversations.xmpp.forms.Data; | ||||||
| @ -27,95 +31,11 @@ public class ServiceDiscoveryResult { | |||||||
| 	public static final String HASH = "hash"; | 	public static final String HASH = "hash"; | ||||||
| 	public static final String VER = "ver"; | 	public static final String VER = "ver"; | ||||||
| 	public static final String RESULT = "result"; | 	public static final String RESULT = "result"; | ||||||
| 
 |  | ||||||
| 	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 lang, final String name) { |  | ||||||
| 			this.category = category; |  | ||||||
| 			this.type = type; |  | ||||||
| 			this.lang = lang; |  | ||||||
| 			this.name = name; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		public Identity(final Element el) { |  | ||||||
| 			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() { |  | ||||||
| 			return this.category; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		public String getType() { |  | ||||||
| 			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; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		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 String hash; | 	protected final String hash; | ||||||
| 	protected final byte[] ver; | 	protected final byte[] ver; | ||||||
| 	protected final List<Identity> identities; |  | ||||||
| 	protected final List<String> features; | 	protected final List<String> features; | ||||||
| 	protected final List<Data> forms; | 	protected final List<Data> forms; | ||||||
| 
 | 	private final List<Identity> identities; | ||||||
| 	public ServiceDiscoveryResult(final IqPacket packet) { | 	public ServiceDiscoveryResult(final IqPacket packet) { | ||||||
| 		this.identities = new ArrayList<>(); | 		this.identities = new ArrayList<>(); | ||||||
| 		this.features = new ArrayList<>(); | 		this.features = new ArrayList<>(); | ||||||
| @ -140,8 +60,7 @@ public class ServiceDiscoveryResult { | |||||||
| 		} | 		} | ||||||
| 		this.ver = this.mkCapHash(); | 		this.ver = this.mkCapHash(); | ||||||
| 	} | 	} | ||||||
| 
 | 	private ServiceDiscoveryResult(String hash, byte[] ver, JSONObject o) throws JSONException { | ||||||
| 	public ServiceDiscoveryResult(String hash, byte[] ver, JSONObject o) throws JSONException { |  | ||||||
| 		this.identities = new ArrayList<>(); | 		this.identities = new ArrayList<>(); | ||||||
| 		this.features = new ArrayList<>(); | 		this.features = new ArrayList<>(); | ||||||
| 		this.forms = new ArrayList<>(); | 		this.forms = new ArrayList<>(); | ||||||
| @ -168,6 +87,22 @@ public class ServiceDiscoveryResult { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	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))) | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private static String clean(String s) { | ||||||
|  | 		return s.replace("<","<"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private static String blankNull(String s) { | ||||||
|  | 		return s == null ? "" : clean(s); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	private static Data createFormFromJSONObject(JSONObject o) { | 	private static Data createFormFromJSONObject(JSONObject o) { | ||||||
| 		Data data = new Data(); | 		Data data = new Data(); | ||||||
| 		JSONArray names = o.names(); | 		JSONArray names = o.names(); | ||||||
| @ -214,14 +149,6 @@ public class ServiceDiscoveryResult { | |||||||
| 		return new String(Base64.encode(this.ver, Base64.DEFAULT)).trim(); | 		return new String(Base64.encode(this.ver, Base64.DEFAULT)).trim(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	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<Identity> getIdentities() { | 	public List<Identity> getIdentities() { | ||||||
| 		return this.identities; | 		return this.identities; | ||||||
| 	} | 	} | ||||||
| @ -254,50 +181,42 @@ public class ServiceDiscoveryResult { | |||||||
| 		return null; | 		return null; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	protected byte[] mkCapHash() { | 	private byte[] mkCapHash() { | ||||||
| 		StringBuilder s = new StringBuilder(); | 		StringBuilder s = new StringBuilder(); | ||||||
| 
 | 
 | ||||||
| 		List<Identity> identities = this.getIdentities(); | 		List<Identity> identities = this.getIdentities(); | ||||||
| 		Collections.sort(identities); | 		Collections.sort(identities); | ||||||
| 
 | 
 | ||||||
| 		for (Identity id : identities) { | 		for (Identity id : identities) { | ||||||
| 			s.append( | 			s.append(blankNull(id.getCategory())) | ||||||
| 				blankNull(id.getCategory()) + "/" + | 					.append("/") | ||||||
| 				blankNull(id.getType()) + "/" + | 					.append(blankNull(id.getType())) | ||||||
| 				blankNull(id.getLang()) + "/" + | 					.append("/") | ||||||
| 				blankNull(id.getName()) + "<" | 					.append(blankNull(id.getLang())) | ||||||
| 			); | 					.append("/") | ||||||
|  | 					.append(blankNull(id.getName())) | ||||||
|  | 					.append("<"); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		List<String> features = this.getFeatures(); | 		List<String> features = this.getFeatures(); | ||||||
| 		Collections.sort(features); | 		Collections.sort(features); | ||||||
| 
 | 
 | ||||||
| 		for (String feature : features) { | 		for (String feature : features) { | ||||||
| 			s.append(feature + "<"); | 			s.append(clean(feature)).append("<"); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		Collections.sort(forms, new Comparator<Data>() { | 		Collections.sort(forms, (lhs, rhs) -> lhs.getFormType().compareTo(rhs.getFormType())); | ||||||
| 			@Override |  | ||||||
| 			public int compare(Data lhs, Data rhs) { |  | ||||||
| 				return lhs.getFormType().compareTo(rhs.getFormType()); |  | ||||||
| 			} |  | ||||||
| 		}); |  | ||||||
| 
 | 
 | ||||||
| 		for (Data form : forms) { | 		for (Data form : forms) { | ||||||
| 			s.append(form.getFormType() + "<"); | 			s.append(clean(form.getFormType())).append("<"); | ||||||
| 			List<Field> fields = form.getFields(); | 			List<Field> fields = form.getFields(); | ||||||
| 			Collections.sort(fields, new Comparator<Field>() { | 			Collections.sort(fields, (lhs, rhs) -> lhs.getFieldName().compareTo(rhs.getFieldName())); | ||||||
| 				@Override |  | ||||||
| 				public int compare(Field lhs, Field rhs) { |  | ||||||
| 					return lhs.getFieldName().compareTo(rhs.getFieldName()); |  | ||||||
| 				} |  | ||||||
| 			}); |  | ||||||
| 			for (Field field : fields) { | 			for (Field field : fields) { | ||||||
| 				s.append(field.getFieldName()+"<"); | 				s.append(clean(field.getFieldName())).append("<"); | ||||||
| 				List<String> values = field.getValues(); | 				List<String> values = field.getValues(); | ||||||
| 				Collections.sort(values); | 				Collections.sort(values); | ||||||
| 				for (String value : values) { | 				for (String value : values) { | ||||||
| 					s.append(value+"<"); | 					s.append(blankNull(value)).append("<"); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @ -316,7 +235,7 @@ public class ServiceDiscoveryResult { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	public JSONObject toJSON() { | 	private JSONObject toJSON() { | ||||||
| 		try { | 		try { | ||||||
| 			JSONObject o = new JSONObject(); | 			JSONObject o = new JSONObject(); | ||||||
| 
 | 
 | ||||||
| @ -344,7 +263,86 @@ public class ServiceDiscoveryResult { | |||||||
| 		final ContentValues values = new ContentValues(); | 		final ContentValues values = new ContentValues(); | ||||||
| 		values.put(HASH, this.hash); | 		values.put(HASH, this.hash); | ||||||
| 		values.put(VER, getVer()); | 		values.put(VER, getVer()); | ||||||
| 		values.put(RESULT, this.toJSON().toString()); | 		JSONObject jsonObject = toJSON(); | ||||||
|  | 		values.put(RESULT, jsonObject == null ? "" : jsonObject.toString()); | ||||||
| 		return values; | 		return values; | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	public static class Identity implements Comparable { | ||||||
|  | 		protected final String type; | ||||||
|  | 		protected final String lang; | ||||||
|  | 		protected final String name; | ||||||
|  | 		final String category; | ||||||
|  | 
 | ||||||
|  | 		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; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		Identity(final Element el) { | ||||||
|  | 			this( | ||||||
|  | 					el.getAttribute("category"), | ||||||
|  | 					el.getAttribute("type"), | ||||||
|  | 					el.getAttribute("xml:lang"), | ||||||
|  | 					el.getAttribute("name") | ||||||
|  | 			); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		Identity(final JSONObject o) { | ||||||
|  | 
 | ||||||
|  | 			this( | ||||||
|  | 					o.optString("category", null), | ||||||
|  | 					o.optString("type", null), | ||||||
|  | 					o.optString("lang", null), | ||||||
|  | 					o.optString("name", null) | ||||||
|  | 			); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public String getCategory() { | ||||||
|  | 			return this.category; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public String getType() { | ||||||
|  | 			return this.type; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public String getLang() { | ||||||
|  | 			return this.lang; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public String getName() { | ||||||
|  | 			return this.name; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public int compareTo(@NonNull 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; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		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; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -3649,20 +3649,21 @@ public class XmppConnectionService extends Service { | |||||||
| 				account.inProgressDiscoFetches.add(key); | 				account.inProgressDiscoFetches.add(key); | ||||||
| 				IqPacket request = new IqPacket(IqPacket.TYPE.GET); | 				IqPacket request = new IqPacket(IqPacket.TYPE.GET); | ||||||
| 				request.setTo(jid); | 				request.setTo(jid); | ||||||
| 				String node = presence.getNode(); | 				final String node = presence.getNode(); | ||||||
| 				Element query = request.query("http://jabber.org/protocol/disco#info"); | 				final String ver = presence.getVer(); | ||||||
| 				if (node != null) { | 				final Element query = request.query("http://jabber.org/protocol/disco#info"); | ||||||
| 					query.setAttribute("node",node); | 				if (node != null && ver != null) { | ||||||
|  | 					query.setAttribute("node",node+"#"+ver); | ||||||
| 				} | 				} | ||||||
| 				Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": making disco request for " + key.second + " to " + jid+ "node="+node); | 				Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": making disco request for " + key.second + " to " + jid); | ||||||
| 				sendIqPacket(account, request, (a, discoPacket) -> { | 				sendIqPacket(account, request, (a, response) -> { | ||||||
| 					if (discoPacket.getType() == IqPacket.TYPE.RESULT) { | 					if (response.getType() == IqPacket.TYPE.RESULT) { | ||||||
| 						ServiceDiscoveryResult disco1 = new ServiceDiscoveryResult(discoPacket); | 						ServiceDiscoveryResult discoveryResult = new ServiceDiscoveryResult(response); | ||||||
| 						if (presence.getVer().equals(disco1.getVer())) { | 						if (presence.getVer().equals(discoveryResult.getVer())) { | ||||||
| 							databaseBackend.insertDiscoveryResult(disco1); | 							databaseBackend.insertDiscoveryResult(discoveryResult); | ||||||
| 							injectServiceDiscorveryResult(a.getRoster(), presence.getHash(), presence.getVer(), disco1); | 							injectServiceDiscorveryResult(a.getRoster(), presence.getHash(), presence.getVer(), discoveryResult); | ||||||
| 						} else { | 						} else { | ||||||
| 							Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": mismatch in caps for contact " + jid + " " + presence.getVer() + " vs " + disco1.getVer()); | 							Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": mismatch in caps for contact " + jid + " " + presence.getVer() + " vs " + discoveryResult.getVer()); | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
| 					a.inProgressDiscoFetches.remove(key); | 					a.inProgressDiscoFetches.remove(key); | ||||||
|  | |||||||
| @ -58,10 +58,7 @@ public class Field extends Element { | |||||||
| 		List<String> values = new ArrayList<>(); | 		List<String> values = new ArrayList<>(); | ||||||
| 		for(Element child : getChildren()) { | 		for(Element child : getChildren()) { | ||||||
| 			if ("value".equals(child.getName())) { | 			if ("value".equals(child.getName())) { | ||||||
| 				String content = child.getContent(); | 				values.add(child.getContent()); | ||||||
| 				if (content != null) { |  | ||||||
| 					values.add(content); |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		return values; | 		return values; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Daniel Gultsch
						Daniel Gultsch