aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/eu/siacs/conversations/xmpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/eu/siacs/conversations/xmpp')
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java65
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java13
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java12
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java6
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java61
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java19
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java50
7 files changed, 164 insertions, 62 deletions
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index a2b58a14..9bda3c09 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -167,7 +167,7 @@ public class XmppConnection implements Runnable {
try {
String srvRecordServer;
try {
- srvRecordServer=IDN.toASCII(namePort.getString("name"));
+ srvRecordServer = IDN.toASCII(namePort.getString("name"));
} catch (final IllegalArgumentException e) {
// TODO: Handle me?`
srvRecordServer = "";
@@ -187,7 +187,7 @@ public class XmppConnection implements Runnable {
+ srvRecordServer + ":" + srvRecordPort);
}
socket = new Socket();
- socket.connect(addr, 20000);
+ socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
socketError = false;
} catch (final UnknownHostException e) {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
@@ -224,6 +224,12 @@ public class XmppConnection implements Runnable {
if (socket.isConnected()) {
socket.close();
}
+ } catch (final IncompatibleServerException e) {
+ this.changeStatus(Account.State.INCOMPATIBLE_SERVER);
+ } catch (final SecurityException e) {
+ this.changeStatus(Account.State.SECURITY_ERROR);
+ } catch (final UnauthorizedException e) {
+ this.changeStatus(Account.State.UNAUTHORIZED);
} catch (final UnknownHostException | ConnectException e) {
this.changeStatus(Account.State.SERVER_NOT_FOUND);
} catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) {
@@ -231,6 +237,13 @@ public class XmppConnection implements Runnable {
this.changeStatus(Account.State.OFFLINE);
this.attempt--; //don't count attempt when reconnecting instantly anyway
} finally {
+ if (socket != null) {
+ try {
+ socket.close();
+ } catch (IOException e) {
+
+ }
+ }
if (wakeLock.isHeld()) {
try {
wakeLock.release();
@@ -279,8 +292,7 @@ public class XmppConnection implements Runnable {
processStream(tagReader.readTag());
break;
} else if (nextTag.isStart("failure")) {
- tagReader.readElement(nextTag);
- changeStatus(Account.State.UNAUTHORIZED);
+ throw new UnauthorizedException();
} else if (nextTag.isStart("challenge")) {
final String challenge = tagReader.readElement(nextTag).getContent();
final Element response = new Element("response");
@@ -437,6 +449,10 @@ public class XmppConnection implements Runnable {
throw new IOException("interrupted mid tag");
}
}
+ if (stanzasReceived == Integer.MAX_VALUE) {
+ resetStreamId();
+ throw new IOException("time to restart the session. cant handle >2 billion pcks");
+ }
++stanzasReceived;
lastPacketReceived = SystemClock.elapsedRealtime();
return element;
@@ -538,8 +554,7 @@ public class XmppConnection implements Runnable {
if (!verifier.verify(account.getServer().getDomainpart(),sslSocket.getSession())) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
- disconnect(true);
- changeStatus(Account.State.SECURITY_ERROR);
+ throw new SecurityException();
}
tagReader.setInputStream(sslSocket.getInputStream());
tagWriter.setOutputStream(sslSocket.getOutputStream());
@@ -550,8 +565,7 @@ public class XmppConnection implements Runnable {
sslSocket.close();
} catch (final NoSuchAlgorithmException | KeyManagementException e1) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
- disconnect(true);
- changeStatus(Account.State.SECURITY_ERROR);
+ throw new SecurityException();
}
}
@@ -590,8 +604,7 @@ public class XmppConnection implements Runnable {
" has lower priority (" + String.valueOf(saslMechanism.getPriority()) +
") than pinned priority (" + keys.getInt(Account.PINNED_MECHANISM_KEY) +
"). Possible downgrade attack?");
- disconnect(true);
- changeStatus(Account.State.SECURITY_ERROR);
+ throw new SecurityException();
}
} catch (final JSONException e) {
Log.d(Config.LOGTAG, "Parse error while checking pinned auth mechanism");
@@ -603,8 +616,7 @@ public class XmppConnection implements Runnable {
}
tagWriter.writeElement(auth);
} else {
- disconnect(true);
- changeStatus(Account.State.INCOMPATIBLE_SERVER);
+ throw new IncompatibleServerException();
}
} else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:"
+ smVersion)
@@ -643,10 +655,8 @@ public class XmppConnection implements Runnable {
if (packet.query().hasChild("username")
&& (packet.query().hasChild("password"))) {
final IqPacket register = new IqPacket(IqPacket.TYPE.SET);
- final Element username = new Element("username")
- .setContent(account.getUsername());
- final Element password = new Element("password")
- .setContent(account.getPassword());
+ final Element username = new Element("username").setContent(account.getUsername());
+ final Element password = new Element("password").setContent(account.getPassword());
register.query("jabber:iq:register").addChild(username);
register.query().addChild(password);
sendIqPacket(register, new OnIqPacketReceived() {
@@ -659,7 +669,7 @@ public class XmppConnection implements Runnable {
changeStatus(Account.State.REGISTRATION_SUCCESSFUL);
} else if (packet.hasChild("error")
&& (packet.findChild("error")
- .hasChild("conflict"))) {
+ .hasChild("conflict"))) {
changeStatus(Account.State.REGISTRATION_CONFLICT);
} else {
changeStatus(Account.State.REGISTRATION_FAILED);
@@ -673,7 +683,7 @@ public class XmppConnection implements Runnable {
disconnect(true);
Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ ": could not register. instructions are"
- + instructions.getContent());
+ + (instructions != null ? instructions.getContent() : ""));
}
}
});
@@ -688,7 +698,7 @@ public class XmppConnection implements Runnable {
}
final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
iq.addChild("bind", "urn:ietf:params:xml:ns:xmpp-bind")
- .addChild("resource").setContent(account.getResource());
+ .addChild("resource").setContent(account.getResource());
this.sendUnmodifiedIqPacket(iq, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(final Account account, final IqPacket packet) {
@@ -901,6 +911,11 @@ public class XmppConnection implements Runnable {
}
private synchronized void sendPacket(final AbstractStanza packet) {
+ if (stanzasSent == Integer.MAX_VALUE) {
+ resetStreamId();
+ disconnect(true);
+ return;
+ }
final String name = packet.getName();
if (name.equals("iq") || name.equals("message") || name.equals("presence")) {
++stanzasSent;
@@ -1091,6 +1106,18 @@ public class XmppConnection implements Runnable {
public final ArrayList<Pair<String,String>> identities = new ArrayList<>();
}
+ private class UnauthorizedException extends IOException {
+
+ }
+
+ private class SecurityException extends IOException {
+
+ }
+
+ private class IncompatibleServerException extends IOException {
+
+ }
+
public class Features {
XmppConnection connection;
private boolean carbonsEnabled = false;
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java
index 295e067a..f989c0c2 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java
@@ -130,12 +130,19 @@ public final class Jid {
if (resourcepart.isEmpty() || resourcepart.length() > 1023) {
throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH);
}
- dp = IDN.toUnicode(jid.substring(domainpartStart, slashLoc), IDN.USE_STD3_ASCII_RULES);
+ try {
+ dp = IDN.toUnicode(Stringprep.nameprep(jid.substring(domainpartStart, slashLoc)), IDN.USE_STD3_ASCII_RULES);
+ } catch (final StringprepException e) {
+ throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
+ }
finaljid = finaljid + dp + "/" + rp;
} else {
resourcepart = "";
- dp = IDN.toUnicode(jid.substring(domainpartStart, jid.length()),
- IDN.USE_STD3_ASCII_RULES);
+ try{
+ dp = IDN.toUnicode(Stringprep.nameprep(jid.substring(domainpartStart, jid.length())), IDN.USE_STD3_ASCII_RULES);
+ } catch (final StringprepException e) {
+ throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
+ }
finaljid = finaljid + dp;
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
index e448f947..c9bb9c93 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
@@ -99,7 +99,7 @@ public class JingleConnection implements Downloadable {
file.delete();
}
}
- Log.d(Config.LOGTAG,"sucessfully transmitted file:" + file.getAbsolutePath());
+ Log.d(Config.LOGTAG,"successfully transmitted file:" + file.getAbsolutePath()+" ("+file.getSha1Sum()+")");
if (message.getEncryption() != Message.ENCRYPTION_PGP) {
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(file));
@@ -213,7 +213,7 @@ public class JingleConnection implements Downloadable {
@Override
public void onPrimaryCandidateFound(boolean success,
- final JingleCandidate candidate) {
+ final JingleCandidate candidate) {
if (success) {
final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
JingleConnection.this, candidate);
@@ -271,6 +271,9 @@ public class JingleConnection implements Downloadable {
this.mergeCandidates(JingleCandidate.parse(content.socks5transport()
.getChildren()));
this.fileOffer = packet.getJingleContent().getFileOffer();
+
+ mXmppConnectionService.sendIqPacket(account,packet.generateResponse(IqPacket.TYPE.RESULT),null);
+
if (fileOffer != null) {
Element fileSize = fileOffer.findChild("size");
Element fileNameElement = fileOffer.findChild("name");
@@ -381,6 +384,7 @@ public class JingleConnection implements Downloadable {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() != IqPacket.TYPE.ERROR) {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": other party received offer");
mJingleStatus = JINGLE_STATUS_INITIATED;
mXmppConnectionService.markMessage(message, Message.STATUS_OFFERED);
} else {
@@ -395,7 +399,9 @@ public class JingleConnection implements Downloadable {
private List<Element> getCandidatesAsElements() {
List<Element> elements = new ArrayList<>();
for (JingleCandidate c : this.candidates) {
- elements.add(c.toElement());
+ if (c.isOurs()) {
+ elements.add(c.toElement());
+ }
}
return elements;
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
index 5dfa3ff4..c19dd04c 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
@@ -9,6 +9,7 @@ import android.annotation.SuppressLint;
import android.util.Log;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Downloadable;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.AbstractConnectionManager;
import eu.siacs.conversations.services.XmppConnectionService;
@@ -58,7 +59,12 @@ public class JingleConnectionManager extends AbstractConnectionManager {
}
public JingleConnection createNewConnection(Message message) {
+ Downloadable old = message.getDownloadable();
+ if (old != null) {
+ old.cancel();
+ }
JingleConnection connection = new JingleConnection(this);
+ mXmppConnectionService.markMessage(message,Message.STATUS_WAITING);
connection.init(message);
this.connections.add(connection);
return connection;
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
index 9866af03..29efcf8f 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
@@ -8,7 +8,9 @@ import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import android.util.Base64;
+import android.util.Log;
+import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.persistance.FileBackend;
@@ -95,11 +97,13 @@ public class JingleInbandTransport extends JingleTransport {
file.createNewFile();
this.fileOutputStream = file.createOutputStream();
if (this.fileOutputStream == null) {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not create output stream");
callback.onFileTransferAborted();
return;
}
this.remainingSize = this.fileSize = file.getExpectedSize();
} catch (final NoSuchAlgorithmException | IOException e) {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+" "+e.getMessage());
callback.onFileTransferAborted();
}
}
@@ -110,12 +114,17 @@ public class JingleInbandTransport extends JingleTransport {
this.onFileTransmissionStatusChanged = callback;
this.file = file;
try {
- this.remainingSize = this.file.getSize();
+ if (this.file.getKey() != null) {
+ this.remainingSize = (this.file.getSize() / 16 + 1) * 16;
+ } else {
+ this.remainingSize = this.file.getSize();
+ }
this.fileSize = this.remainingSize;
this.digest = MessageDigest.getInstance("SHA-1");
this.digest.reset();
fileInputStream = this.file.createInputStream();
if (fileInputStream == null) {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could no create input stream");
callback.onFileTransferAborted();
return;
}
@@ -124,6 +133,7 @@ public class JingleInbandTransport extends JingleTransport {
}
} catch (NoSuchAlgorithmException e) {
callback.onFileTransferAborted();
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
}
}
@@ -150,29 +160,33 @@ public class JingleInbandTransport extends JingleTransport {
byte[] buffer = new byte[this.bufferSize];
try {
int count = fileInputStream.read(buffer);
- if (count == -1) {
+ this.remainingSize -= count;
+ if (count != buffer.length && count != -1) {
+ int rem = fileInputStream.read(buffer,count,buffer.length-count);
+ if (rem > 0) {
+ count += rem;
+ }
+ }
+ this.digest.update(buffer,0,count);
+ String base64 = Base64.encodeToString(buffer,0,count, Base64.NO_WRAP);
+ IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
+ iq.setTo(this.counterpart);
+ Element data = iq.addChild("data", "http://jabber.org/protocol/ibb");
+ data.setAttribute("seq", Integer.toString(this.seq));
+ data.setAttribute("block-size", Integer.toString(this.blockSize));
+ data.setAttribute("sid", this.sessionId);
+ data.setContent(base64);
+ this.account.getXmppConnection().sendIqPacket(iq, this.onAckReceived);
+ this.seq++;
+ if (this.remainingSize > 0) {
+ connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
+ } else {
file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
- fileInputStream.close();
this.onFileTransmissionStatusChanged.onFileTransmitted(file);
- } else {
- this.remainingSize -= count;
- this.digest.update(buffer);
- String base64 = Base64.encodeToString(buffer, Base64.NO_WRAP);
- IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
- iq.setTo(this.counterpart);
- Element data = iq.addChild("data",
- "http://jabber.org/protocol/ibb");
- data.setAttribute("seq", Integer.toString(this.seq));
- data.setAttribute("block-size",
- Integer.toString(this.blockSize));
- data.setAttribute("sid", this.sessionId);
- data.setContent(base64);
- this.account.getXmppConnection().sendIqPacket(iq,
- this.onAckReceived);
- this.seq++;
- connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
+ fileInputStream.close();
}
} catch (IOException e) {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
FileBackend.close(fileInputStream);
this.onFileTransmissionStatusChanged.onFileTransferAborted();
}
@@ -182,14 +196,10 @@ public class JingleInbandTransport extends JingleTransport {
try {
byte[] buffer = Base64.decode(data, Base64.NO_WRAP);
if (this.remainingSize < buffer.length) {
- buffer = Arrays
- .copyOfRange(buffer, 0, (int) this.remainingSize);
+ buffer = Arrays.copyOfRange(buffer, 0, (int) this.remainingSize);
}
this.remainingSize -= buffer.length;
-
-
this.fileOutputStream.write(buffer);
-
this.digest.update(buffer);
if (this.remainingSize <= 0) {
file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
@@ -200,6 +210,7 @@ public class JingleInbandTransport extends JingleTransport {
connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
}
} catch (IOException e) {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
FileBackend.close(fileOutputStream);
this.onFileTransmissionStatusChanged.onFileTransferAborted();
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
index 72015a05..8d74f44e 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
@@ -1,15 +1,20 @@
package eu.siacs.conversations.xmpp.jingle;
+import android.util.Log;
+
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.InetSocketAddress;
import java.net.Socket;
+import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
+import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.utils.CryptoHelper;
@@ -53,8 +58,9 @@ public class JingleSocks5Transport extends JingleTransport {
@Override
public void run() {
try {
- socket = new Socket(candidate.getHost(),
- candidate.getPort());
+ socket = new Socket();
+ SocketAddress address = new InetSocketAddress(candidate.getHost(),candidate.getPort());
+ socket.connect(address,Config.SOCKET_TIMEOUT * 1000);
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
byte[] login = { 0x05, 0x01, 0x00 };
@@ -102,6 +108,7 @@ public class JingleSocks5Transport extends JingleTransport {
digest.reset();
fileInputStream = file.createInputStream();
if (fileInputStream == null) {
+ Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create input stream");
callback.onFileTransferAborted();
return;
}
@@ -121,10 +128,13 @@ public class JingleSocks5Transport extends JingleTransport {
callback.onFileTransmitted(file);
}
} catch (FileNotFoundException e) {
+ Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
callback.onFileTransferAborted();
} catch (IOException e) {
+ Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
callback.onFileTransferAborted();
} catch (NoSuchAlgorithmException e) {
+ Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
callback.onFileTransferAborted();
} finally {
FileBackend.close(fileInputStream);
@@ -150,6 +160,7 @@ public class JingleSocks5Transport extends JingleTransport {
fileOutputStream = file.createOutputStream();
if (fileOutputStream == null) {
callback.onFileTransferAborted();
+ Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create output stream");
return;
}
double size = file.getExpectedSize();
@@ -160,6 +171,7 @@ public class JingleSocks5Transport extends JingleTransport {
count = inputStream.read(buffer);
if (count == -1) {
callback.onFileTransferAborted();
+ Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": file ended prematurely with "+remainingSize+" bytes remaining");
return;
} else {
fileOutputStream.write(buffer, 0, count);
@@ -173,10 +185,13 @@ public class JingleSocks5Transport extends JingleTransport {
file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
callback.onFileTransmitted(file);
} catch (FileNotFoundException e) {
+ Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
callback.onFileTransferAborted();
} catch (IOException e) {
+ Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
callback.onFileTransferAborted();
} catch (NoSuchAlgorithmException e) {
+ Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
callback.onFileTransferAborted();
} finally {
FileBackend.close(fileOutputStream);
diff --git a/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java b/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java
index 9f5ac988..04d55bbe 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java
@@ -6,6 +6,9 @@ import eu.siacs.conversations.xmpp.jid.Jid;
import android.util.Base64;
public class Avatar {
+
+ public enum Origin { PEP, VCARD };
+
public String type;
public String sha1sum;
public String image;
@@ -13,21 +16,14 @@ public class Avatar {
public int width;
public long size;
public Jid owner;
+ public Origin origin = Origin.PEP; //default to maintain compat
public byte[] getImageAsBytes() {
return Base64.decode(image, Base64.DEFAULT);
}
public String getFilename() {
- if (type == null) {
- return sha1sum;
- } else if (type.equalsIgnoreCase("image/webp")) {
- return sha1sum + ".webp";
- } else if (type.equalsIgnoreCase("image/png")) {
- return sha1sum + ".png";
- } else {
- return sha1sum;
- }
+ return sha1sum;
}
public static Avatar parseMetadata(Element items) {
@@ -64,10 +60,44 @@ public class Avatar {
return null;
}
avatar.type = child.getAttribute("type");
- avatar.sha1sum = child.getAttribute("id");
+ String hash = child.getAttribute("id");
+ if (!isValidSHA1(hash)) {
+ return null;
+ }
+ avatar.sha1sum = hash;
+ avatar.origin = Origin.PEP;
return avatar;
}
}
return null;
}
+
+ @Override
+ public boolean equals(Object object) {
+ if (object != null && object instanceof Avatar) {
+ Avatar other = (Avatar) object;
+ return other.getFilename().equals(this.getFilename());
+ } else {
+ return false;
+ }
+ }
+
+ public static Avatar parsePresence(Element x) {
+ Element photo = x != null ? x.findChild("photo") : null;
+ String hash = photo != null ? photo.getContent() : null;
+ if (hash == null) {
+ return null;
+ }
+ if (!isValidSHA1(hash)) {
+ return null;
+ }
+ Avatar avatar = new Avatar();
+ avatar.sha1sum = hash;
+ avatar.origin = Origin.VCARD;
+ return avatar;
+ }
+
+ private static boolean isValidSHA1(String s) {
+ return s != null && s.matches("[a-fA-F0-9]{40}");
+ }
}