some code cleanup

This commit is contained in:
iNPUTmice 2014-08-31 16:28:21 +02:00
parent 8d456085e5
commit 1ac5be4855
75 changed files with 1483 additions and 1264 deletions

View file

@ -0,0 +1,21 @@
package eu.siacs.conversations;
import android.graphics.Bitmap;
public final class Config {
public static final String LOGTAG = "conversations";
public static final int PING_MAX_INTERVAL = 300;
public static final int PING_MIN_INTERVAL = 30;
public static final int PING_TIMEOUT = 10;
public static final int CONNECT_TIMEOUT = 90;
public static final int CARBON_GRACE_PERIOD = 60;
public static final int AVATAR_SIZE = 192;
public static final Bitmap.CompressFormat AVATAR_FORMAT = Bitmap.CompressFormat.WEBP;
private Config() {
}
}

View file

@ -17,6 +17,7 @@ import org.json.JSONObject;
import android.content.Context; import android.content.Context;
import android.util.Log; import android.util.Log;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.persistance.DatabaseBackend; import eu.siacs.conversations.persistance.DatabaseBackend;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket; import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
@ -29,9 +30,7 @@ import net.java.otr4j.session.InstanceTag;
import net.java.otr4j.session.SessionID; import net.java.otr4j.session.SessionID;
public class OtrEngine implements OtrEngineHost { public class OtrEngine implements OtrEngineHost {
private static final String LOGTAG = "xmppService";
private Account account; private Account account;
private OtrPolicy otrPolicy; private OtrPolicy otrPolicy;
private KeyPair keyPair; private KeyPair keyPair;
@ -45,17 +44,17 @@ public class OtrEngine implements OtrEngineHost {
this.otrPolicy.setAllowV3(true); this.otrPolicy.setAllowV3(true);
this.keyPair = loadKey(account.getKeys()); this.keyPair = loadKey(account.getKeys());
} }
private KeyPair loadKey(JSONObject keys) { private KeyPair loadKey(JSONObject keys) {
if (keys == null) { if (keys == null) {
return null; return null;
} }
try { try {
BigInteger x = new BigInteger(keys.getString("otr_x"),16); BigInteger x = new BigInteger(keys.getString("otr_x"), 16);
BigInteger y = new BigInteger(keys.getString("otr_y"),16); BigInteger y = new BigInteger(keys.getString("otr_y"), 16);
BigInteger p = new BigInteger(keys.getString("otr_p"),16); BigInteger p = new BigInteger(keys.getString("otr_p"), 16);
BigInteger q = new BigInteger(keys.getString("otr_q"),16); BigInteger q = new BigInteger(keys.getString("otr_q"), 16);
BigInteger g = new BigInteger(keys.getString("otr_g"),16); BigInteger g = new BigInteger(keys.getString("otr_g"), 16);
KeyFactory keyFactory = KeyFactory.getInstance("DSA"); KeyFactory keyFactory = KeyFactory.getInstance("DSA");
DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(y, p, q, g); DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(y, p, q, g);
DSAPrivateKeySpec privateKeySpec = new DSAPrivateKeySpec(x, p, q, g); DSAPrivateKeySpec privateKeySpec = new DSAPrivateKeySpec(x, p, q, g);
@ -70,26 +69,28 @@ public class OtrEngine implements OtrEngineHost {
return null; return null;
} }
} }
private void saveKey() { private void saveKey() {
PublicKey publicKey = keyPair.getPublic(); PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate(); PrivateKey privateKey = keyPair.getPrivate();
KeyFactory keyFactory; KeyFactory keyFactory;
try { try {
keyFactory = KeyFactory.getInstance("DSA"); keyFactory = KeyFactory.getInstance("DSA");
DSAPrivateKeySpec privateKeySpec = keyFactory.getKeySpec(privateKey, DSAPrivateKeySpec.class); DSAPrivateKeySpec privateKeySpec = keyFactory.getKeySpec(
DSAPublicKeySpec publicKeySpec = keyFactory.getKeySpec(publicKey, DSAPublicKeySpec.class); privateKey, DSAPrivateKeySpec.class);
this.account.setKey("otr_x",privateKeySpec.getX().toString(16)); DSAPublicKeySpec publicKeySpec = keyFactory.getKeySpec(publicKey,
this.account.setKey("otr_g",privateKeySpec.getG().toString(16)); DSAPublicKeySpec.class);
this.account.setKey("otr_p",privateKeySpec.getP().toString(16)); this.account.setKey("otr_x", privateKeySpec.getX().toString(16));
this.account.setKey("otr_q",privateKeySpec.getQ().toString(16)); this.account.setKey("otr_g", privateKeySpec.getG().toString(16));
this.account.setKey("otr_y",publicKeySpec.getY().toString(16)); this.account.setKey("otr_p", privateKeySpec.getP().toString(16));
this.account.setKey("otr_q", privateKeySpec.getQ().toString(16));
this.account.setKey("otr_y", publicKeySpec.getY().toString(16));
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
e.printStackTrace(); e.printStackTrace();
} catch (InvalidKeySpecException e) { } catch (InvalidKeySpecException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
@Override @Override
@ -123,18 +124,19 @@ public class OtrEngine implements OtrEngineHost {
} }
return this.keyPair.getPublic(); return this.keyPair.getPublic();
} }
@Override @Override
public KeyPair getLocalKeyPair(SessionID arg0) throws OtrException { public KeyPair getLocalKeyPair(SessionID arg0) throws OtrException {
if (this.keyPair==null) { if (this.keyPair == null) {
KeyPairGenerator kg; KeyPairGenerator kg;
try { try {
kg = KeyPairGenerator.getInstance("DSA"); kg = KeyPairGenerator.getInstance("DSA");
this.keyPair = kg.genKeyPair(); this.keyPair = kg.genKeyPair();
this.saveKey(); this.saveKey();
DatabaseBackend.getInstance(context).updateAccount(account); DatabaseBackend.getInstance(context).updateAccount(account);
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
Log.d(LOGTAG,"error generating key pair "+e.getMessage()); Log.d(Config.LOGTAG,
"error generating key pair " + e.getMessage());
} }
} }
return this.keyPair; return this.keyPair;
@ -152,17 +154,18 @@ public class OtrEngine implements OtrEngineHost {
} }
@Override @Override
public void injectMessage(SessionID session, String body) throws OtrException { public void injectMessage(SessionID session, String body)
throws OtrException {
MessagePacket packet = new MessagePacket(); MessagePacket packet = new MessagePacket();
packet.setFrom(account.getFullJid()); packet.setFrom(account.getFullJid());
if (session.getUserID().isEmpty()) { if (session.getUserID().isEmpty()) {
packet.setTo(session.getAccountID()); packet.setTo(session.getAccountID());
} else { } else {
packet.setTo(session.getAccountID()+"/"+session.getUserID()); packet.setTo(session.getAccountID() + "/" + session.getUserID());
} }
packet.setBody(body); packet.setBody(body);
packet.addChild("private","urn:xmpp:carbons:2"); packet.addChild("private", "urn:xmpp:carbons:2");
packet.addChild("no-copy","urn:xmpp:hints"); packet.addChild("no-copy", "urn:xmpp:hints");
packet.setType(MessagePacket.TYPE_CHAT); packet.setType(MessagePacket.TYPE_CHAT);
account.getXmppConnection().sendMessagePacket(packet); account.getXmppConnection().sendMessagePacket(packet);
} }

View file

@ -14,6 +14,7 @@ import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback; import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
@ -38,7 +39,7 @@ public class PgpEngine {
public void decrypt(final Message message, public void decrypt(final Message message,
final UiCallback<Message> callback) { final UiCallback<Message> callback) {
Log.d("xmppService", "decrypting message " + message.getUuid()); Log.d(Config.LOGTAG, "decrypting message " + message.getUuid());
Intent params = new Intent(); Intent params = new Intent();
params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY); params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, message params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, message
@ -65,7 +66,7 @@ public class PgpEngine {
callback.error(R.string.openpgp_error, message); callback.error(R.string.openpgp_error, message);
return; return;
} }
return; return;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
callback.userInputRequried((PendingIntent) result callback.userInputRequried((PendingIntent) result
@ -73,8 +74,9 @@ public class PgpEngine {
message); message);
return; return;
case OpenPgpApi.RESULT_CODE_ERROR: case OpenPgpApi.RESULT_CODE_ERROR:
OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR); OpenPgpError error = result
Log.d("xmppService",error.getMessage()); .getParcelableExtra(OpenPgpApi.RESULT_ERROR);
Log.d(Config.LOGTAG, error.getMessage());
callback.error(R.string.openpgp_error, message); callback.error(R.string.openpgp_error, message);
return; return;
default: default:
@ -109,7 +111,8 @@ public class PgpEngine {
message.setEncryption(Message.ENCRYPTION_DECRYPTED); message.setEncryption(Message.ENCRYPTION_DECRYPTED);
PgpEngine.this.mXmppConnectionService PgpEngine.this.mXmppConnectionService
.updateMessage(message); .updateMessage(message);
PgpEngine.this.mXmppConnectionService.updateConversationUi(); PgpEngine.this.mXmppConnectionService
.updateConversationUi();
callback.success(message); callback.success(message);
return; return;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
@ -177,7 +180,7 @@ public class PgpEngine {
} catch (IOException e) { } catch (IOException e) {
callback.error(R.string.openpgp_error, message); callback.error(R.string.openpgp_error, message);
} }
break; break;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
callback.userInputRequried((PendingIntent) result callback.userInputRequried((PendingIntent) result
@ -221,9 +224,9 @@ public class PgpEngine {
} }
}); });
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
Log.d("xmppService", "file not found: " + e.getMessage()); Log.d(Config.LOGTAG, "file not found: " + e.getMessage());
} catch (IOException e) { } catch (IOException e) {
Log.d("xmppService", "io exception during file encrypt"); Log.d(Config.LOGTAG, "io exception during file encrypt");
} }
} }
} }
@ -267,7 +270,7 @@ public class PgpEngine {
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
return 0; return 0;
case OpenPgpApi.RESULT_CODE_ERROR: case OpenPgpApi.RESULT_CODE_ERROR:
Log.d("xmppService", Log.d(Config.LOGTAG,
"openpgp error: " "openpgp error: "
+ ((OpenPgpError) result + ((OpenPgpError) result
.getParcelableExtra(OpenPgpApi.RESULT_ERROR)) .getParcelableExtra(OpenPgpApi.RESULT_ERROR))

View file

@ -4,19 +4,18 @@ import android.content.ContentValues;
public abstract class AbstractEntity { public abstract class AbstractEntity {
public static final String UUID = "uuid"; public static final String UUID = "uuid";
protected String uuid; protected String uuid;
public String getUuid() { public String getUuid() {
return this.uuid; return this.uuid;
} }
public abstract ContentValues getContentValues(); public abstract ContentValues getContentValues();
public boolean equals(AbstractEntity entity) { public boolean equals(AbstractEntity entity) {
return this.getUuid().equals(entity.getUuid()); return this.getUuid().equals(entity.getUuid());
} }
} }

View file

@ -82,11 +82,12 @@ public class Account extends AbstractEntity {
public Account(String username, String server, String password) { public Account(String username, String server, String password) {
this(java.util.UUID.randomUUID().toString(), username, server, this(java.util.UUID.randomUUID().toString(), username, server,
password, 0, null, "",null); password, 0, null, "", null);
} }
public Account(String uuid, String username, String server, public Account(String uuid, String username, String server,
String password, int options, String rosterVersion, String keys, String avatar) { String password, int options, String rosterVersion, String keys,
String avatar) {
this.uuid = uuid; this.uuid = uuid;
this.username = username; this.username = username;
this.server = server; this.server = server;
@ -151,7 +152,10 @@ public class Account extends AbstractEntity {
public boolean errorStatus() { public boolean errorStatus() {
int s = getStatus(); int s = getStatus();
return (s == STATUS_REGISTRATION_FAILED || s == STATUS_REGISTRATION_CONFLICT || s == STATUS_REGISTRATION_NOT_SUPPORTED || s == STATUS_SERVER_NOT_FOUND || s == STATUS_UNAUTHORIZED); return (s == STATUS_REGISTRATION_FAILED
|| s == STATUS_REGISTRATION_CONFLICT
|| s == STATUS_REGISTRATION_NOT_SUPPORTED
|| s == STATUS_SERVER_NOT_FOUND || s == STATUS_UNAUTHORIZED);
} }
public boolean hasErrorStatus() { public boolean hasErrorStatus() {

View file

@ -8,30 +8,31 @@ import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
public class Bookmark implements ListItem { public class Bookmark implements ListItem {
private Account account; private Account account;
private String jid; private String jid;
private String nick; private String nick;
private String name; private String name;
private boolean autojoin; private boolean autojoin;
private Conversation mJoinedConversation; private Conversation mJoinedConversation;
public Bookmark(Account account, String jid) { public Bookmark(Account account, String jid) {
this.account = account; this.account = account;
this.jid = jid; this.jid = jid;
} }
public static Bookmark parse(Element element, Account account) { public static Bookmark parse(Element element, Account account) {
Bookmark bookmark = new Bookmark(account,element.getAttribute("jid")); Bookmark bookmark = new Bookmark(account, element.getAttribute("jid"));
bookmark.setName(element.getAttribute("name")); bookmark.setName(element.getAttribute("name"));
String autojoin = element.getAttribute("autojoin"); String autojoin = element.getAttribute("autojoin");
if (autojoin!=null && (autojoin.equals("true")||autojoin.equals("1"))) { if (autojoin != null
&& (autojoin.equals("true") || autojoin.equals("1"))) {
bookmark.setAutojoin(true); bookmark.setAutojoin(true);
} else { } else {
bookmark.setAutojoin(false); bookmark.setAutojoin(false);
} }
Element nick = element.findChild("nick"); Element nick = element.findChild("nick");
if (nick!=null) { if (nick != null) {
bookmark.setNick(nick.getContent()); bookmark.setNick(nick.getContent());
} }
return bookmark; return bookmark;
@ -40,25 +41,27 @@ public class Bookmark implements ListItem {
public void setAutojoin(boolean autojoin) { public void setAutojoin(boolean autojoin) {
this.autojoin = autojoin; this.autojoin = autojoin;
} }
public void setName(String name) { public void setName(String name) {
this.name = name; this.name = name;
} }
public void setNick(String nick) { public void setNick(String nick) {
this.nick = nick; this.nick = nick;
} }
@Override @Override
public int compareTo(ListItem another) { public int compareTo(ListItem another) {
return this.getDisplayName().compareToIgnoreCase(another.getDisplayName()); return this.getDisplayName().compareToIgnoreCase(
another.getDisplayName());
} }
@Override @Override
public String getDisplayName() { public String getDisplayName() {
if (this.mJoinedConversation!=null && (this.mJoinedConversation.getMucOptions().getSubject() != null)) { if (this.mJoinedConversation != null
&& (this.mJoinedConversation.getMucOptions().getSubject() != null)) {
return this.mJoinedConversation.getMucOptions().getSubject(); return this.mJoinedConversation.getMucOptions().getSubject();
} else if (name!=null) { } else if (name != null) {
return name; return name;
} else { } else {
return this.jid.split("@")[0]; return this.jid.split("@")[0];
@ -69,11 +72,11 @@ public class Bookmark implements ListItem {
public String getJid() { public String getJid() {
return this.jid.toLowerCase(Locale.US); return this.jid.toLowerCase(Locale.US);
} }
public String getNick() { public String getNick() {
return this.nick; return this.nick;
} }
public boolean autojoin() { public boolean autojoin() {
return autojoin; return autojoin;
} }
@ -81,8 +84,8 @@ public class Bookmark implements ListItem {
public boolean match(String needle) { public boolean match(String needle) {
return needle == null return needle == null
|| getJid().contains(needle.toLowerCase(Locale.US)) || getJid().contains(needle.toLowerCase(Locale.US))
|| getDisplayName().toLowerCase(Locale.US) || getDisplayName().toLowerCase(Locale.US).contains(
.contains(needle.toLowerCase(Locale.US)); needle.toLowerCase(Locale.US));
} }
public Account getAccount() { public Account getAccount() {
@ -91,10 +94,12 @@ public class Bookmark implements ListItem {
@Override @Override
public Bitmap getImage(int dpSize, Context context) { public Bitmap getImage(int dpSize, Context context) {
if (this.mJoinedConversation==null) { if (this.mJoinedConversation == null) {
return UIHelper.getContactPicture(getDisplayName(), dpSize, context, false); return UIHelper.getContactPicture(getDisplayName(), dpSize,
context, false);
} else { } else {
return UIHelper.getContactPicture(this.mJoinedConversation, dpSize, context, false); return UIHelper.getContactPicture(this.mJoinedConversation, dpSize,
context, false);
} }
} }
@ -105,7 +110,7 @@ public class Bookmark implements ListItem {
public String getName() { public String getName() {
return name; return name;
} }
public Element toElement() { public Element toElement() {
Element element = new Element("conference"); Element element = new Element("conference");
element.setAttribute("jid", this.getJid()); element.setAttribute("jid", this.getJid());

View file

@ -106,7 +106,7 @@ public class Contact implements ListItem {
values.put(SYSTEMACCOUNT, systemAccount); values.put(SYSTEMACCOUNT, systemAccount);
values.put(PHOTOURI, photoUri); values.put(PHOTOURI, photoUri);
values.put(KEYS, keys.toString()); values.put(KEYS, keys.toString());
values.put(AVATAR,avatar); values.put(AVATAR, avatar);
return values; return values;
} }
@ -138,7 +138,7 @@ public class Contact implements ListItem {
public Account getAccount() { public Account getAccount() {
return this.account; return this.account;
} }
public Presences getPresences() { public Presences getPresences() {
return this.presences; return this.presences;
} }
@ -309,7 +309,8 @@ public class Contact implements ListItem {
@Override @Override
public int compareTo(ListItem another) { public int compareTo(ListItem another) {
return this.getDisplayName().compareToIgnoreCase(another.getDisplayName()); return this.getDisplayName().compareToIgnoreCase(
another.getDisplayName());
} }
public String getServer() { public String getServer() {
@ -323,9 +324,9 @@ public class Contact implements ListItem {
@Override @Override
public Bitmap getImage(int size, Context context) { public Bitmap getImage(int size, Context context) {
if (this.avatar!=null) { if (this.avatar != null) {
Bitmap bm = FileBackend.getAvatar(avatar, size, context); Bitmap bm = FileBackend.getAvatar(avatar, size, context);
if (bm==null) { if (bm == null) {
return UIHelper.getContactPicture(this, size, context, false); return UIHelper.getContactPicture(this, size, context, false);
} else { } else {
return bm; return bm;

View file

@ -88,7 +88,8 @@ public class Conversation extends AbstractEntity {
public List<Message> getMessages() { public List<Message> getMessages() {
if (messages == null) { if (messages == null) {
this.messages = new CopyOnWriteArrayList<Message>(); // prevent null pointer this.messages = new CopyOnWriteArrayList<Message>(); // prevent null
// pointer
} }
// populate with Conversation (this) // populate with Conversation (this)
@ -287,7 +288,7 @@ public class Conversation extends AbstractEntity {
public String getOtrFingerprint() { public String getOtrFingerprint() {
if (this.otrFingerprint == null) { if (this.otrFingerprint == null) {
try { try {
if (getOtrSession()== null) { if (getOtrSession() == null) {
return ""; return "";
} }
DSAPublicKey remotePubKey = (DSAPublicKey) getOtrSession() DSAPublicKey remotePubKey = (DSAPublicKey) getOtrSession()
@ -403,15 +404,15 @@ public class Conversation extends AbstractEntity {
} }
public Bitmap getImage(Context context, int size) { public Bitmap getImage(Context context, int size) {
if (mode==MODE_SINGLE) { if (mode == MODE_SINGLE) {
return getContact().getImage(size, context); return getContact().getImage(size, context);
} else { } else {
return UIHelper.getContactPicture(this, size, context, false); return UIHelper.getContactPicture(this, size, context, false);
} }
} }
public boolean hasDuplicateMessage(Message message) { public boolean hasDuplicateMessage(Message message) {
for(int i = this.getMessages().size() -1; i >= 0; --i) { for (int i = this.getMessages().size() - 1; i >= 0; --i) {
if (this.messages.get(i).equals(message)) { if (this.messages.get(i).equals(message)) {
return true; return true;
} }

View file

@ -5,6 +5,8 @@ import android.graphics.Bitmap;
public interface ListItem extends Comparable<ListItem> { public interface ListItem extends Comparable<ListItem> {
public String getDisplayName(); public String getDisplayName();
public String getJid(); public String getJid();
public Bitmap getImage(int dpSize, Context context); public Bitmap getImage(int dpSize, Context context);
} }

View file

@ -142,7 +142,7 @@ public class Message extends AbstractEntity {
} }
} }
} }
public String getBody() { public String getBody() {
return body; return body;
} }

View file

@ -299,9 +299,9 @@ public class MucOptions {
return this.conversation.getContactJid().split("/")[0] + "/" return this.conversation.getContactJid().split("/")[0] + "/"
+ this.joinnick; + this.joinnick;
} }
public String getTrueCounterpart(String counterpart) { public String getTrueCounterpart(String counterpart) {
for(User user : this.getUsers()) { for (User user : this.getUsers()) {
if (user.getName().equals(counterpart)) { if (user.getName().equals(counterpart)) {
return user.getJid(); return user.getJid();
} }

View file

@ -14,7 +14,7 @@ public class Presences {
public static final int XA = 2; public static final int XA = 2;
public static final int DND = 3; public static final int DND = 3;
public static final int OFFLINE = 4; public static final int OFFLINE = 4;
private Hashtable<String, Integer> presences = new Hashtable<String, Integer>(); private Hashtable<String, Integer> presences = new Hashtable<String, Integer>();
public Hashtable<String, Integer> getPresences() { public Hashtable<String, Integer> getPresences() {
@ -28,23 +28,24 @@ public class Presences {
public void removePresence(String resource) { public void removePresence(String resource) {
this.presences.remove(resource); this.presences.remove(resource);
} }
public void clearPresences() { public void clearPresences() {
this.presences.clear(); this.presences.clear();
} }
public int getMostAvailableStatus() { public int getMostAvailableStatus() {
int status = OFFLINE; int status = OFFLINE;
Iterator<Entry<String, Integer>> it = presences.entrySet().iterator(); Iterator<Entry<String, Integer>> it = presences.entrySet().iterator();
while (it.hasNext()) { while (it.hasNext()) {
Entry<String, Integer> entry = it.next(); Entry<String, Integer> entry = it.next();
if (entry.getValue()<status) status = entry.getValue(); if (entry.getValue() < status)
status = entry.getValue();
} }
return status; return status;
} }
public static int parseShow(Element show) { public static int parseShow(Element show) {
if ((show == null)||(show.getContent() == null)) { if ((show == null) || (show.getContent() == null)) {
return Presences.ONLINE; return Presences.ONLINE;
} else if (show.getContent().equals("away")) { } else if (show.getContent().equals("away")) {
return Presences.AWAY; return Presences.AWAY;
@ -53,16 +54,16 @@ public class Presences {
} else if (show.getContent().equals("chat")) { } else if (show.getContent().equals("chat")) {
return Presences.CHAT; return Presences.CHAT;
} else if (show.getContent().equals("dnd")) { } else if (show.getContent().equals("dnd")) {
return Presences.DND; return Presences.DND;
} else { } else {
return Presences.OFFLINE; return Presences.OFFLINE;
} }
} }
public int size() { public int size() {
return presences.size(); return presences.size();
} }
public String[] asStringArray() { public String[] asStringArray() {
final String[] presencesArray = new String[presences.size()]; final String[] presencesArray = new String[presences.size()];
presences.keySet().toArray(presencesArray); presences.keySet().toArray(presencesArray);

View file

@ -9,16 +9,16 @@ public class Roster {
Account account; Account account;
ConcurrentHashMap<String, Contact> contacts = new ConcurrentHashMap<String, Contact>(); ConcurrentHashMap<String, Contact> contacts = new ConcurrentHashMap<String, Contact>();
private String version = null; private String version = null;
public Roster(Account account) { public Roster(Account account) {
this.account = account; this.account = account;
} }
public boolean hasContact(String jid) { public boolean hasContact(String jid) {
String cleanJid = jid.split("/")[0]; String cleanJid = jid.split("/")[0];
return contacts.containsKey(cleanJid); return contacts.containsKey(cleanJid);
} }
public Contact getContact(String jid) { public Contact getContact(String jid) {
String cleanJid = jid.split("/")[0].toLowerCase(Locale.getDefault()); String cleanJid = jid.split("/")[0].toLowerCase(Locale.getDefault());
if (contacts.containsKey(cleanJid)) { if (contacts.containsKey(cleanJid)) {
@ -32,19 +32,19 @@ public class Roster {
} }
public void clearPresences() { public void clearPresences() {
for(Contact contact : getContacts()) { for (Contact contact : getContacts()) {
contact.clearPresences(); contact.clearPresences();
} }
} }
public void markAllAsNotInRoster() { public void markAllAsNotInRoster() {
for(Contact contact : getContacts()) { for (Contact contact : getContacts()) {
contact.resetOption(Contact.Options.IN_ROSTER); contact.resetOption(Contact.Options.IN_ROSTER);
} }
} }
public void clearSystemAccounts() { public void clearSystemAccounts() {
for(Contact contact : getContacts()) { for (Contact contact : getContacts()) {
contact.setPhotoUri(null); contact.setPhotoUri(null);
contact.setSystemName(null); contact.setSystemName(null);
contact.setSystemAccount(null); contact.setSystemAccount(null);
@ -58,13 +58,13 @@ public class Roster {
public void initContact(Contact contact) { public void initContact(Contact contact) {
contact.setAccount(account); contact.setAccount(account);
contact.setOption(Contact.Options.IN_ROSTER); contact.setOption(Contact.Options.IN_ROSTER);
contacts.put(contact.getJid(),contact); contacts.put(contact.getJid(), contact);
} }
public void setVersion(String version) { public void setVersion(String version) {
this.version = version; this.version = version;
} }
public String getVersion() { public String getVersion() {
return this.version; return this.version;
} }

View file

@ -10,67 +10,69 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class IqGenerator extends AbstractGenerator { public class IqGenerator extends AbstractGenerator {
public IqPacket discoResponse(IqPacket request) { public IqPacket discoResponse(IqPacket request) {
IqPacket packet = new IqPacket(IqPacket.TYPE_RESULT); IqPacket packet = new IqPacket(IqPacket.TYPE_RESULT);
packet.setId(request.getId()); packet.setId(request.getId());
packet.setTo(request.getFrom()); packet.setTo(request.getFrom());
Element query = packet.addChild("query","http://jabber.org/protocol/disco#info"); Element query = packet.addChild("query",
"http://jabber.org/protocol/disco#info");
query.setAttribute("node", request.query().getAttribute("node")); query.setAttribute("node", request.query().getAttribute("node"));
Element identity = query.addChild("identity"); Element identity = query.addChild("identity");
identity.setAttribute("category","client"); identity.setAttribute("category", "client");
identity.setAttribute("type", this.IDENTITY_TYPE); identity.setAttribute("type", this.IDENTITY_TYPE);
identity.setAttribute("name", IDENTITY_NAME); identity.setAttribute("name", IDENTITY_NAME);
List<String> features = Arrays.asList(FEATURES); List<String> features = Arrays.asList(FEATURES);
Collections.sort(features); Collections.sort(features);
for(String feature : features) { for (String feature : features) {
query.addChild("feature").setAttribute("var",feature); query.addChild("feature").setAttribute("var", feature);
} }
return packet; return packet;
} }
protected IqPacket publish(String node, Element item) { protected IqPacket publish(String node, Element item) {
IqPacket packet = new IqPacket(IqPacket.TYPE_SET); IqPacket packet = new IqPacket(IqPacket.TYPE_SET);
Element pubsub = packet.addChild("pubsub", "http://jabber.org/protocol/pubsub"); Element pubsub = packet.addChild("pubsub",
"http://jabber.org/protocol/pubsub");
Element publish = pubsub.addChild("publish"); Element publish = pubsub.addChild("publish");
publish.setAttribute("node", node); publish.setAttribute("node", node);
publish.addChild(item); publish.addChild(item);
return packet; return packet;
} }
protected IqPacket retrieve(String node, Element item) { protected IqPacket retrieve(String node, Element item) {
IqPacket packet = new IqPacket(IqPacket.TYPE_GET); IqPacket packet = new IqPacket(IqPacket.TYPE_GET);
Element pubsub = packet.addChild("pubsub", "http://jabber.org/protocol/pubsub"); Element pubsub = packet.addChild("pubsub",
Element items = pubsub.addChild("items"); "http://jabber.org/protocol/pubsub");
items.setAttribute("node", node); Element items = pubsub.addChild("items");
if (item!=null) { items.setAttribute("node", node);
if (item != null) {
items.addChild(item); items.addChild(item);
} }
return packet; return packet;
} }
public IqPacket publishAvatar(Avatar avatar) { public IqPacket publishAvatar(Avatar avatar) {
Element item = new Element("item"); Element item = new Element("item");
item.setAttribute("id", avatar.sha1sum); item.setAttribute("id", avatar.sha1sum);
Element data = item.addChild("data","urn:xmpp:avatar:data"); Element data = item.addChild("data", "urn:xmpp:avatar:data");
data.setContent(avatar.image); data.setContent(avatar.image);
return publish("urn:xmpp:avatar:data", item); return publish("urn:xmpp:avatar:data", item);
} }
public IqPacket publishAvatarMetadata(Avatar avatar) { public IqPacket publishAvatarMetadata(Avatar avatar) {
Element item = new Element("item"); Element item = new Element("item");
item.setAttribute("id", avatar.sha1sum); item.setAttribute("id", avatar.sha1sum);
Element metadata = item.addChild("metadata","urn:xmpp:avatar:metadata"); Element metadata = item
.addChild("metadata", "urn:xmpp:avatar:metadata");
Element info = metadata.addChild("info"); Element info = metadata.addChild("info");
info.setAttribute("bytes",avatar.size); info.setAttribute("bytes", avatar.size);
info.setAttribute("id",avatar.sha1sum); info.setAttribute("id", avatar.sha1sum);
info.setAttribute("height",avatar.height); info.setAttribute("height", avatar.height);
info.setAttribute("width",avatar.height); info.setAttribute("width", avatar.height);
info.setAttribute("type", avatar.type); info.setAttribute("type", avatar.type);
return publish("urn:xmpp:avatar:metadata",item); return publish("urn:xmpp:avatar:metadata", item);
} }
public IqPacket retrieveAvatar(Avatar avatar) { public IqPacket retrieveAvatar(Avatar avatar) {
Element item = new Element("item"); Element item = new Element("item");
item.setAttribute("id", avatar.sha1sum); item.setAttribute("id", avatar.sha1sum);
@ -81,7 +83,7 @@ public class IqGenerator extends AbstractGenerator {
public IqPacket retrieveAvatarMetaData(String to) { public IqPacket retrieveAvatarMetaData(String to) {
IqPacket packet = retrieve("urn:xmpp:avatar:metadata", null); IqPacket packet = retrieve("urn:xmpp:avatar:metadata", null);
if (to!=null) { if (to != null) {
packet.setTo(to); packet.setTo(to);
} }
return packet; return packet;

View file

@ -32,56 +32,56 @@ public class MessageGenerator {
packet.setFrom(account.getFullJid()); packet.setFrom(account.getFullJid());
packet.setId(message.getUuid()); packet.setId(message.getUuid());
if (addDelay) { if (addDelay) {
addDelay(packet,message.getTimeSent()); addDelay(packet, message.getTimeSent());
} }
return packet; return packet;
} }
private void addDelay(MessagePacket packet, long timestamp) { private void addDelay(MessagePacket packet, long timestamp) {
final SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",Locale.US); final SimpleDateFormat mDateFormat = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
Element delay = packet.addChild("delay", "urn:xmpp:delay"); Element delay = packet.addChild("delay", "urn:xmpp:delay");
Date date = new Date(timestamp); Date date = new Date(timestamp);
delay.setAttribute("stamp", mDateFormat.format(date)); delay.setAttribute("stamp", mDateFormat.format(date));
} }
public MessagePacket generateOtrChat(Message message) { public MessagePacket generateOtrChat(Message message) {
return generateOtrChat(message, false); return generateOtrChat(message, false);
} }
public MessagePacket generateOtrChat(Message message, boolean addDelay) { public MessagePacket generateOtrChat(Message message, boolean addDelay) {
Session otrSession = message.getConversation().getOtrSession(); Session otrSession = message.getConversation().getOtrSession();
if (otrSession==null) { if (otrSession == null) {
return null; return null;
} }
MessagePacket packet = preparePacket(message,addDelay); MessagePacket packet = preparePacket(message, addDelay);
packet.addChild("private", "urn:xmpp:carbons:2"); packet.addChild("private", "urn:xmpp:carbons:2");
packet.addChild("no-copy", "urn:xmpp:hints"); packet.addChild("no-copy", "urn:xmpp:hints");
try { try {
packet.setBody(otrSession.transformSending(message packet.setBody(otrSession.transformSending(message.getBody()));
.getBody()));
return packet; return packet;
} catch (OtrException e) { } catch (OtrException e) {
return null; return null;
} }
} }
public MessagePacket generateChat(Message message) { public MessagePacket generateChat(Message message) {
return generateChat(message, false); return generateChat(message, false);
} }
public MessagePacket generateChat(Message message, boolean addDelay) { public MessagePacket generateChat(Message message, boolean addDelay) {
MessagePacket packet = preparePacket(message,addDelay); MessagePacket packet = preparePacket(message, addDelay);
packet.setBody(message.getBody()); packet.setBody(message.getBody());
return packet; return packet;
} }
public MessagePacket generatePgpChat(Message message) { public MessagePacket generatePgpChat(Message message) {
return generatePgpChat(message, false); return generatePgpChat(message, false);
} }
public MessagePacket generatePgpChat(Message message, boolean addDelay) { public MessagePacket generatePgpChat(Message message, boolean addDelay) {
MessagePacket packet = preparePacket(message,addDelay); MessagePacket packet = preparePacket(message, addDelay);
packet.setBody("This is an XEP-0027 encryted message"); packet.setBody("This is an XEP-0027 encryted message");
if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
packet.addChild("x", "jabber:x:encrypted").setContent( packet.addChild("x", "jabber:x:encrypted").setContent(
@ -100,7 +100,7 @@ public class MessageGenerator {
error.addChild("not-acceptable"); error.addChild("not-acceptable");
return packet; return packet;
} }
private MessagePacket generateError(MessagePacket origin) { private MessagePacket generateError(MessagePacket origin) {
MessagePacket packet = new MessagePacket(); MessagePacket packet = new MessagePacket();
packet.setId(origin.getId()); packet.setId(origin.getId());
@ -109,7 +109,7 @@ public class MessageGenerator {
packet.setType(MessagePacket.TYPE_ERROR); packet.setType(MessagePacket.TYPE_ERROR);
return packet; return packet;
} }
public MessagePacket confirm(Account account, String to, String id) { public MessagePacket confirm(Account account, String to, String id) {
MessagePacket packet = new MessagePacket(); MessagePacket packet = new MessagePacket();
packet.setType(MessagePacket.TYPE_NORMAL); packet.setType(MessagePacket.TYPE_NORMAL);
@ -120,8 +120,9 @@ public class MessageGenerator {
received.setAttribute("id", id); received.setAttribute("id", id);
return packet; return packet;
} }
public MessagePacket conferenceSubject(Conversation conversation,String subject) { public MessagePacket conferenceSubject(Conversation conversation,
String subject) {
MessagePacket packet = new MessagePacket(); MessagePacket packet = new MessagePacket();
packet.setType(MessagePacket.TYPE_GROUPCHAT); packet.setType(MessagePacket.TYPE_GROUPCHAT);
packet.setTo(conversation.getContactJid().split("/")[0]); packet.setTo(conversation.getContactJid().split("/")[0]);
@ -131,7 +132,7 @@ public class MessageGenerator {
packet.setFrom(conversation.getAccount().getJid()); packet.setFrom(conversation.getAccount().getJid());
return packet; return packet;
} }
public MessagePacket directInvite(Conversation conversation, String contact) { public MessagePacket directInvite(Conversation conversation, String contact) {
MessagePacket packet = new MessagePacket(); MessagePacket packet = new MessagePacket();
packet.setType(MessagePacket.TYPE_NORMAL); packet.setType(MessagePacket.TYPE_NORMAL);
@ -141,7 +142,7 @@ public class MessageGenerator {
x.setAttribute("jid", conversation.getContactJid().split("/")[0]); x.setAttribute("jid", conversation.getContactJid().split("/")[0]);
return packet; return packet;
} }
public MessagePacket invite(Conversation conversation, String contact) { public MessagePacket invite(Conversation conversation, String contact) {
MessagePacket packet = new MessagePacket(); MessagePacket packet = new MessagePacket();
packet.setTo(conversation.getContactJid().split("/")[0]); packet.setTo(conversation.getContactJid().split("/")[0]);
@ -154,13 +155,14 @@ public class MessageGenerator {
packet.addChild(x); packet.addChild(x);
return packet; return packet;
} }
public MessagePacket received(Account account, MessagePacket originalMessage, String namespace) { public MessagePacket received(Account account,
MessagePacket originalMessage, String namespace) {
MessagePacket receivedPacket = new MessagePacket(); MessagePacket receivedPacket = new MessagePacket();
receivedPacket.setType(MessagePacket.TYPE_NORMAL); receivedPacket.setType(MessagePacket.TYPE_NORMAL);
receivedPacket.setTo(originalMessage.getFrom()); receivedPacket.setTo(originalMessage.getFrom());
receivedPacket.setFrom(account.getFullJid()); receivedPacket.setFrom(account.getFullJid());
Element received = receivedPacket.addChild("received",namespace); Element received = receivedPacket.addChild("received", namespace);
received.setAttribute("id", originalMessage.getId()); received.setAttribute("id", originalMessage.getId());
return receivedPacket; return receivedPacket;
} }

View file

@ -14,7 +14,7 @@ public class PresenceGenerator extends AbstractGenerator {
packet.setAttribute("from", contact.getAccount().getJid()); packet.setAttribute("from", contact.getAccount().getJid());
return packet; return packet;
} }
public PresencePacket requestPresenceUpdatesFrom(Contact contact) { public PresencePacket requestPresenceUpdatesFrom(Contact contact) {
return subscription("subscribe", contact); return subscription("subscribe", contact);
} }
@ -41,9 +41,10 @@ public class PresenceGenerator extends AbstractGenerator {
} }
String capHash = getCapHash(); String capHash = getCapHash();
if (capHash != null) { if (capHash != null) {
Element cap = packet.addChild("c","http://jabber.org/protocol/caps"); Element cap = packet.addChild("c",
"http://jabber.org/protocol/caps");
cap.setAttribute("hash", "sha-1"); cap.setAttribute("hash", "sha-1");
cap.setAttribute("node","http://conversions.siacs.eu"); cap.setAttribute("node", "http://conversions.siacs.eu");
cap.setAttribute("ver", capHash); cap.setAttribute("ver", capHash);
} }
return packet; return packet;

View file

@ -13,17 +13,17 @@ import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
public abstract class AbstractParser { public abstract class AbstractParser {
protected XmppConnectionService mXmppConnectionService; protected XmppConnectionService mXmppConnectionService;
protected AbstractParser(XmppConnectionService service) { protected AbstractParser(XmppConnectionService service) {
this.mXmppConnectionService = service; this.mXmppConnectionService = service;
} }
protected long getTimestamp(Element packet) { protected long getTimestamp(Element packet) {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
ArrayList<String> stamps = new ArrayList<String>(); ArrayList<String> stamps = new ArrayList<String>();
for(Element child : packet.getChildren()) { for (Element child : packet.getChildren()) {
if (child.getName().equals("delay")) { if (child.getName().equals("delay")) {
stamps.add(child.getAttribute("stamp").replace("Z", "+0000")); stamps.add(child.getAttribute("stamp").replace("Z", "+0000"));
} }
@ -33,17 +33,18 @@ public abstract class AbstractParser {
try { try {
String stamp = stamps.get(stamps.size() - 1); String stamp = stamps.get(stamps.size() - 1);
if (stamp.contains(".")) { if (stamp.contains(".")) {
Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ",Locale.US) Date date = new SimpleDateFormat(
.parse(stamp); "yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US)
if (now<date.getTime()) { .parse(stamp);
if (now < date.getTime()) {
return now; return now;
} else { } else {
return date.getTime(); return date.getTime();
} }
} else { } else {
Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ",Locale.US) Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ",
.parse(stamp); Locale.US).parse(stamp);
if (now<date.getTime()) { if (now < date.getTime()) {
return now; return now;
} else { } else {
return date.getTime(); return date.getTime();
@ -56,8 +57,9 @@ public abstract class AbstractParser {
return now; return now;
} }
} }
protected void updateLastseen(Element packet, Account account, boolean presenceOverwrite) { protected void updateLastseen(Element packet, Account account,
boolean presenceOverwrite) {
String[] fromParts = packet.getAttribute("from").split("/"); String[] fromParts = packet.getAttribute("from").split("/");
String from = fromParts[0]; String from = fromParts[0];
String presence = null; String presence = null;
@ -70,19 +72,19 @@ public abstract class AbstractParser {
long timestamp = getTimestamp(packet); long timestamp = getTimestamp(packet);
if (timestamp >= contact.lastseen.time) { if (timestamp >= contact.lastseen.time) {
contact.lastseen.time = timestamp; contact.lastseen.time = timestamp;
if ((presence!=null)&&(presenceOverwrite)) { if ((presence != null) && (presenceOverwrite)) {
contact.lastseen.presence = presence; contact.lastseen.presence = presence;
} }
} }
} }
protected String avatarData(Element items) { protected String avatarData(Element items) {
Element item = items.findChild("item"); Element item = items.findChild("item");
if (item==null) { if (item == null) {
return null; return null;
} }
Element data = item.findChild("data","urn:xmpp:avatar:data"); Element data = item.findChild("data", "urn:xmpp:avatar:data");
if (data==null) { if (data == null) {
return null; return null;
} }
return data.getContent(); return data.getContent();

View file

@ -12,7 +12,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
public IqParser(XmppConnectionService service) { public IqParser(XmppConnectionService service) {
super(service); super(service);
} }
public void rosterItems(Account account, Element query) { public void rosterItems(Account account, Element query) {
String version = query.getAttribute("ver"); String version = query.getAttribute("ver");
if (version != null) { if (version != null) {
@ -27,7 +27,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
if (!contact.getOption(Contact.Options.DIRTY_PUSH)) { if (!contact.getOption(Contact.Options.DIRTY_PUSH)) {
contact.setServerName(name); contact.setServerName(name);
} }
if (subscription!=null) { if (subscription != null) {
if (subscription.equals("remove")) { if (subscription.equals("remove")) {
contact.resetOption(Contact.Options.IN_ROSTER); contact.resetOption(Contact.Options.IN_ROSTER);
contact.resetOption(Contact.Options.DIRTY_DELETE); contact.resetOption(Contact.Options.DIRTY_DELETE);
@ -42,14 +42,15 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
} }
mXmppConnectionService.updateRosterUi(); mXmppConnectionService.updateRosterUi();
} }
public String avatarData(IqPacket packet) { public String avatarData(IqPacket packet) {
Element pubsub = packet.findChild("pubsub", "http://jabber.org/protocol/pubsub"); Element pubsub = packet.findChild("pubsub",
if (pubsub==null) { "http://jabber.org/protocol/pubsub");
if (pubsub == null) {
return null; return null;
} }
Element items = pubsub.findChild("items"); Element items = pubsub.findChild("items");
if (items==null) { if (items == null) {
return null; return null;
} }
return super.avatarData(items); return super.avatarData(items);
@ -63,20 +64,19 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
Element query = packet.findChild("query"); Element query = packet.findChild("query");
this.rosterItems(account, query); this.rosterItems(account, query);
} }
} else if (packet } else if (packet.hasChild("open", "http://jabber.org/protocol/ibb")
.hasChild("open", "http://jabber.org/protocol/ibb") || packet.hasChild("data", "http://jabber.org/protocol/ibb")) {
|| packet mXmppConnectionService.getJingleConnectionManager()
.hasChild("data", "http://jabber.org/protocol/ibb")) { .deliverIbbPacket(account, packet);
mXmppConnectionService.getJingleConnectionManager().deliverIbbPacket(account, packet);
} else if (packet.hasChild("query", } else if (packet.hasChild("query",
"http://jabber.org/protocol/disco#info")) { "http://jabber.org/protocol/disco#info")) {
IqPacket response = mXmppConnectionService.getIqGenerator().discoResponse(packet); IqPacket response = mXmppConnectionService.getIqGenerator()
.discoResponse(packet);
account.getXmppConnection().sendIqPacket(response, null); account.getXmppConnection().sendIqPacket(response, null);
} else { } else {
if ((packet.getType() == IqPacket.TYPE_GET) if ((packet.getType() == IqPacket.TYPE_GET)
|| (packet.getType() == IqPacket.TYPE_SET)) { || (packet.getType() == IqPacket.TYPE_SET)) {
IqPacket response = packet IqPacket response = packet.generateRespone(IqPacket.TYPE_ERROR);
.generateRespone(IqPacket.TYPE_ERROR);
Element error = response.addChild("error"); Element error = response.addChild("error");
error.setAttribute("type", "cancel"); error.setAttribute("type", "cancel");
error.addChild("feature-not-implemented", error.addChild("feature-not-implemented",

View file

@ -4,6 +4,7 @@ import android.os.SystemClock;
import android.util.Log; import android.util.Log;
import net.java.otr4j.session.Session; import net.java.otr4j.session.Session;
import net.java.otr4j.session.SessionStatus; import net.java.otr4j.session.SessionStatus;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
@ -18,7 +19,7 @@ import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
public class MessageParser extends AbstractParser implements public class MessageParser extends AbstractParser implements
OnMessagePacketReceived { OnMessagePacketReceived {
private long lastCarbonMessageReceived = -XmppConnectionService.CARBON_GRACE_PERIOD; private long lastCarbonMessageReceived = -(Config.CARBON_GRACE_PERIOD * 1000);
public MessageParser(XmppConnectionService service) { public MessageParser(XmppConnectionService service) {
super(service); super(service);
@ -173,7 +174,8 @@ public class MessageParser extends AbstractParser implements
finishedMessage.setTrueCounterpart(conversation.getMucOptions() finishedMessage.setTrueCounterpart(conversation.getMucOptions()
.getTrueCounterpart(counterPart)); .getTrueCounterpart(counterPart));
} }
if (packet.hasChild("delay") && conversation.hasDuplicateMessage(finishedMessage)) { if (packet.hasChild("delay")
&& conversation.hasDuplicateMessage(finishedMessage)) {
return null; return null;
} }
finishedMessage.setTime(getTimestamp(packet)); finishedMessage.setTime(getTimestamp(packet));
@ -198,7 +200,8 @@ public class MessageParser extends AbstractParser implements
} }
Element message = forwarded.findChild("message"); Element message = forwarded.findChild("message");
if ((message == null) || (!message.hasChild("body"))) { if ((message == null) || (!message.hasChild("body"))) {
if (status == Message.STATUS_RECEIVED && message.getAttribute("from")!=null) { if (status == Message.STATUS_RECEIVED
&& message.getAttribute("from") != null) {
parseNormal(message, account); parseNormal(message, account);
} }
return null; return null;
@ -220,7 +223,7 @@ public class MessageParser extends AbstractParser implements
Conversation conversation = mXmppConnectionService Conversation conversation = mXmppConnectionService
.findOrCreateConversation(account, parts[0], false); .findOrCreateConversation(account, parts[0], false);
conversation.setLatestMarkableMessageId(getMarkableMessageId(packet)); conversation.setLatestMarkableMessageId(getMarkableMessageId(packet));
String pgpBody = getPgpBody(message); String pgpBody = getPgpBody(message);
Message finishedMessage; Message finishedMessage;
if (pgpBody != null) { if (pgpBody != null) {
@ -243,7 +246,7 @@ public class MessageParser extends AbstractParser implements
return null; return null;
} }
} }
return finishedMessage; return finishedMessage;
} }
@ -307,16 +310,18 @@ public class MessageParser extends AbstractParser implements
if (node != null) { if (node != null) {
if (node.equals("urn:xmpp:avatar:metadata")) { if (node.equals("urn:xmpp:avatar:metadata")) {
Avatar avatar = Avatar.parseMetadata(items); Avatar avatar = Avatar.parseMetadata(items);
if (avatar!=null) { if (avatar != null) {
avatar.owner = from; avatar.owner = from;
if (mXmppConnectionService.getFileBackend().isAvatarCached( if (mXmppConnectionService.getFileBackend().isAvatarCached(
avatar)) { avatar)) {
if (account.getJid().equals(from)) { if (account.getJid().equals(from)) {
if (account.setAvatar(avatar.getFilename())) { if (account.setAvatar(avatar.getFilename())) {
mXmppConnectionService.databaseBackend.updateAccount(account); mXmppConnectionService.databaseBackend
.updateAccount(account);
} }
} else { } else {
Contact contact = account.getRoster().getContact(from); Contact contact = account.getRoster().getContact(
from);
contact.setAvatar(avatar.getFilename()); contact.setAvatar(avatar.getFilename());
} }
} else { } else {
@ -324,11 +329,11 @@ public class MessageParser extends AbstractParser implements
} }
} }
} else { } else {
Log.d("xmppService", account.getJid() + ": " + node + " from " Log.d(Config.LOGTAG, account.getJid() + ": " + node + " from "
+ from); + from);
} }
} else { } else {
Log.d("xmppService", event.toString()); Log.d(Config.LOGTAG, event.toString());
} }
} }
@ -355,7 +360,7 @@ public class MessageParser extends AbstractParser implements
boolean notify = true; boolean notify = true;
if (mXmppConnectionService.getPreferences().getBoolean( if (mXmppConnectionService.getPreferences().getBoolean(
"notification_grace_period_after_carbon_received", true)) { "notification_grace_period_after_carbon_received", true)) {
notify = (SystemClock.elapsedRealtime() - lastCarbonMessageReceived) > XmppConnectionService.CARBON_GRACE_PERIOD; notify = (SystemClock.elapsedRealtime() - lastCarbonMessageReceived) > (Config.CARBON_GRACE_PERIOD * 1000);
} }
if ((packet.getType() == MessagePacket.TYPE_CHAT)) { if ((packet.getType() == MessagePacket.TYPE_CHAT)) {

View file

@ -26,7 +26,7 @@ public class PresenceParser extends AbstractParser implements
if (muc != null) { if (muc != null) {
boolean before = muc.getMucOptions().online(); boolean before = muc.getMucOptions().online();
muc.getMucOptions().processPacket(packet, mPgpEngine); muc.getMucOptions().processPacket(packet, mPgpEngine);
if (before!=muc.getMucOptions().online()) { if (before != muc.getMucOptions().online()) {
mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateConversationUi();
} }
} }
@ -36,7 +36,7 @@ public class PresenceParser extends AbstractParser implements
if (muc != null) { if (muc != null) {
boolean before = muc.getMucOptions().online(); boolean before = muc.getMucOptions().online();
muc.getMucOptions().processPacket(packet, mPgpEngine); muc.getMucOptions().processPacket(packet, mPgpEngine);
if (before!=muc.getMucOptions().online()) { if (before != muc.getMucOptions().online()) {
mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateConversationUi();
} }
} }

View file

@ -29,6 +29,7 @@ import android.util.Base64;
import android.util.Base64OutputStream; import android.util.Base64OutputStream;
import android.util.Log; import android.util.Log;
import android.util.LruCache; import android.util.LruCache;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
@ -44,8 +45,9 @@ public class FileBackend {
private Context context; private Context context;
private LruCache<String, Bitmap> thumbnailCache; private LruCache<String, Bitmap> thumbnailCache;
private SimpleDateFormat imageDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS",Locale.US); private SimpleDateFormat imageDateFormat = new SimpleDateFormat(
"yyyyMMdd_HHmmssSSS", Locale.US);
public FileBackend(Context context) { public FileBackend(Context context) {
this.context = context; this.context = context;
@ -85,14 +87,15 @@ public class FileBackend {
} }
return new JingleFile(path + "/" + filename); return new JingleFile(path + "/" + filename);
} }
public JingleFile getJingleFile(Message message) { public JingleFile getJingleFile(Message message) {
return getJingleFile(message, true); return getJingleFile(message, true);
} }
public JingleFile getJingleFile(Message message, boolean decrypted) { public JingleFile getJingleFile(Message message, boolean decrypted) {
StringBuilder filename = new StringBuilder(); StringBuilder filename = new StringBuilder();
filename.append(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath()); filename.append(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES).getAbsolutePath());
filename.append("/Conversations/"); filename.append("/Conversations/");
filename.append(message.getUuid()); filename.append(message.getUuid());
if ((decrypted) || (message.getEncryption() == Message.ENCRYPTION_NONE)) { if ((decrypted) || (message.getEncryption() == Message.ENCRYPTION_NONE)) {
@ -106,7 +109,7 @@ public class FileBackend {
} }
return new JingleFile(filename.toString()); return new JingleFile(filename.toString());
} }
public Bitmap resize(Bitmap originalBitmap, int size) { public Bitmap resize(Bitmap originalBitmap, int size) {
int w = originalBitmap.getWidth(); int w = originalBitmap.getWidth();
int h = originalBitmap.getHeight(); int h = originalBitmap.getHeight();
@ -144,14 +147,15 @@ public class FileBackend {
private JingleFile copyImageToPrivateStorage(Message message, Uri image, private JingleFile copyImageToPrivateStorage(Message message, Uri image,
int sampleSize) throws ImageCopyException { int sampleSize) throws ImageCopyException {
try { try {
InputStream is = context.getContentResolver().openInputStream(image); InputStream is = context.getContentResolver()
.openInputStream(image);
JingleFile file = getJingleFile(message); JingleFile file = getJingleFile(message);
file.getParentFile().mkdirs(); file.getParentFile().mkdirs();
file.createNewFile(); file.createNewFile();
Bitmap originalBitmap; Bitmap originalBitmap;
BitmapFactory.Options options = new BitmapFactory.Options(); BitmapFactory.Options options = new BitmapFactory.Options();
int inSampleSize = (int) Math.pow(2, sampleSize); int inSampleSize = (int) Math.pow(2, sampleSize);
Log.d("xmppService", "reading bitmap with sample size " Log.d(Config.LOGTAG, "reading bitmap with sample size "
+ inSampleSize); + inSampleSize);
options.inSampleSize = inSampleSize; options.inSampleSize = inSampleSize;
originalBitmap = BitmapFactory.decodeStream(is, null, options); originalBitmap = BitmapFactory.decodeStream(is, null, options);
@ -194,17 +198,20 @@ public class FileBackend {
} }
} }
} }
private int getRotation(Uri image) { private int getRotation(Uri image) {
if ("content".equals(image.getScheme())) { if ("content".equals(image.getScheme())) {
Cursor cursor = context.getContentResolver().query(image, Cursor cursor = context
new String[] { MediaStore.Images.ImageColumns.ORIENTATION }, null, null, null); .getContentResolver()
.query(image,
new String[] { MediaStore.Images.ImageColumns.ORIENTATION },
null, null, null);
if (cursor.getCount() != 1) { if (cursor.getCount() != 1) {
return -1; return -1;
} }
cursor.moveToFirst(); cursor.moveToFirst();
return cursor.getInt(0); return cursor.getInt(0);
} else { } else {
ExifInterface exif; ExifInterface exif;
try { try {
@ -258,7 +265,7 @@ public class FileBackend {
try { try {
this.deleteFile(file); this.deleteFile(file);
} catch (IOException e) { } catch (IOException e) {
Log.d("xmppService", Log.d(Config.LOGTAG,
"error deleting file: " + file.getAbsolutePath()); "error deleting file: " + file.getAbsolutePath());
} }
} }
@ -273,28 +280,32 @@ public class FileBackend {
public Uri getTakePhotoUri() { public Uri getTakePhotoUri() {
StringBuilder pathBuilder = new StringBuilder(); StringBuilder pathBuilder = new StringBuilder();
pathBuilder.append(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)); pathBuilder.append(Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM));
pathBuilder.append('/'); pathBuilder.append('/');
pathBuilder.append("Camera"); pathBuilder.append("Camera");
pathBuilder.append('/'); pathBuilder.append('/');
pathBuilder.append("IMG_"+this.imageDateFormat.format(new Date())+".jpg"); pathBuilder.append("IMG_" + this.imageDateFormat.format(new Date())
Uri uri = Uri.parse("file://"+pathBuilder.toString()); + ".jpg");
Uri uri = Uri.parse("file://" + pathBuilder.toString());
File file = new File(uri.toString()); File file = new File(uri.toString());
file.getParentFile().mkdirs(); file.getParentFile().mkdirs();
return uri; return uri;
} }
public Avatar getPepAvatar(Uri image, int size, Bitmap.CompressFormat format) { public Avatar getPepAvatar(Uri image, int size, Bitmap.CompressFormat format) {
try { try {
Avatar avatar = new Avatar(); Avatar avatar = new Avatar();
Bitmap bm = cropCenterSquare(image, size); Bitmap bm = cropCenterSquare(image, size);
if (bm==null) { if (bm == null) {
return null; return null;
} }
ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream(); ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream();
Base64OutputStream mBase64OutputSttream = new Base64OutputStream(mByteArrayOutputStream, Base64.DEFAULT); Base64OutputStream mBase64OutputSttream = new Base64OutputStream(
mByteArrayOutputStream, Base64.DEFAULT);
MessageDigest digest = MessageDigest.getInstance("SHA-1"); MessageDigest digest = MessageDigest.getInstance("SHA-1");
DigestOutputStream mDigestOutputStream = new DigestOutputStream(mBase64OutputSttream, digest); DigestOutputStream mDigestOutputStream = new DigestOutputStream(
mBase64OutputSttream, digest);
if (!bm.compress(format, 75, mDigestOutputStream)) { if (!bm.compress(format, 75, mDigestOutputStream)) {
return null; return null;
} }
@ -309,25 +320,26 @@ public class FileBackend {
return null; return null;
} }
} }
public boolean isAvatarCached(Avatar avatar) { public boolean isAvatarCached(Avatar avatar) {
File file = new File(getAvatarPath(context, avatar.getFilename())); File file = new File(getAvatarPath(context, avatar.getFilename()));
return file.exists(); return file.exists();
} }
public boolean save(Avatar avatar) { public boolean save(Avatar avatar) {
if (isAvatarCached(avatar)) { if (isAvatarCached(avatar)) {
return true; return true;
} }
String filename = getAvatarPath(context, avatar.getFilename()); String filename = getAvatarPath(context, avatar.getFilename());
File file = new File(filename+".tmp"); File file = new File(filename + ".tmp");
file.getParentFile().mkdirs(); file.getParentFile().mkdirs();
try { try {
file.createNewFile(); file.createNewFile();
FileOutputStream mFileOutputStream = new FileOutputStream(file); FileOutputStream mFileOutputStream = new FileOutputStream(file);
MessageDigest digest = MessageDigest.getInstance("SHA-1"); MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.reset(); digest.reset();
DigestOutputStream mDigestOutputStream = new DigestOutputStream(mFileOutputStream, digest); DigestOutputStream mDigestOutputStream = new DigestOutputStream(
mFileOutputStream, digest);
mDigestOutputStream.write(avatar.getImageAsBytes()); mDigestOutputStream.write(avatar.getImageAsBytes());
mDigestOutputStream.flush(); mDigestOutputStream.flush();
mDigestOutputStream.close(); mDigestOutputStream.close();
@ -337,7 +349,7 @@ public class FileBackend {
file.renameTo(new File(filename)); file.renameTo(new File(filename));
return true; return true;
} else { } else {
Log.d("xmppService","sha1sum mismatch for "+avatar.owner); Log.d(Config.LOGTAG, "sha1sum mismatch for " + avatar.owner);
file.delete(); file.delete();
return false; return false;
} }
@ -349,9 +361,9 @@ public class FileBackend {
return false; return false;
} }
} }
public static String getAvatarPath(Context context, String avatar) { public static String getAvatarPath(Context context, String avatar) {
return context.getFilesDir().getAbsolutePath() + "/avatars/"+avatar; return context.getFilesDir().getAbsolutePath() + "/avatars/" + avatar;
} }
public Bitmap cropCenterSquare(Uri image, int size) { public Bitmap cropCenterSquare(Uri image, int size) {
@ -361,7 +373,7 @@ public class FileBackend {
InputStream is = context.getContentResolver() InputStream is = context.getContentResolver()
.openInputStream(image); .openInputStream(image);
Bitmap input = BitmapFactory.decodeStream(is, null, options); Bitmap input = BitmapFactory.decodeStream(is, null, options);
if (input==null) { if (input == null) {
return null; return null;
} else { } else {
return cropCenterSquare(input, size); return cropCenterSquare(input, size);
@ -370,7 +382,7 @@ public class FileBackend {
return null; return null;
} }
} }
public static Bitmap cropCenterSquare(Bitmap input, int size) { public static Bitmap cropCenterSquare(Bitmap input, int size) {
int w = input.getWidth(); int w = input.getWidth();
int h = input.getHeight(); int h = input.getHeight();
@ -381,8 +393,7 @@ public class FileBackend {
float outHeight = scale * h; float outHeight = scale * h;
float left = (size - outWidth) / 2; float left = (size - outWidth) / 2;
float top = (size - outHeight) / 2; float top = (size - outHeight) / 2;
RectF target = new RectF(left, top, left + outWidth, top RectF target = new RectF(left, top, left + outWidth, top + outHeight);
+ outHeight);
Bitmap output = Bitmap.createBitmap(size, size, input.getConfig()); Bitmap output = Bitmap.createBitmap(size, size, input.getConfig());
Canvas canvas = new Canvas(output); Canvas canvas = new Canvas(output);
@ -412,11 +423,11 @@ public class FileBackend {
return inSampleSize; return inSampleSize;
} }
public Uri getJingleFileUri(Message message) { public Uri getJingleFileUri(Message message) {
File file = getJingleFile(message); File file = getJingleFile(message);
if (file.exists()) { if (file.exists()) {
return Uri.parse("file://"+file.getAbsolutePath()); return Uri.parse("file://" + file.getAbsolutePath());
} else { } else {
return ImageProvider.getProviderUri(message); return ImageProvider.getProviderUri(message);
} }
@ -436,8 +447,9 @@ public class FileBackend {
} }
public static Bitmap getAvatar(String avatar, int size, Context context) { public static Bitmap getAvatar(String avatar, int size, Context context) {
Bitmap bm = BitmapFactory.decodeFile(FileBackend.getAvatarPath(context, avatar)); Bitmap bm = BitmapFactory.decodeFile(FileBackend.getAvatarPath(context,
if (bm==null) { avatar));
if (bm == null) {
return null; return null;
} }
return cropCenterSquare(bm, UIHelper.getRealPx(size, context)); return cropCenterSquare(bm, UIHelper.getRealPx(size, context));

View file

@ -1,11 +0,0 @@
package eu.siacs.conversations.services;
import android.graphics.Bitmap;
public final class Defaults {
public static final int AVATAR_SIZE = 192;
public static final Bitmap.CompressFormat AVATAR_FORMAT = Bitmap.CompressFormat.WEBP;
private Defaults() {
}
}

View file

@ -3,6 +3,7 @@ package eu.siacs.conversations.services;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
public class EventReceiver extends BroadcastReceiver { public class EventReceiver extends BroadcastReceiver {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {

View file

@ -2,8 +2,8 @@ package eu.siacs.conversations.services;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
@ -27,7 +27,7 @@ public class ImageProvider extends ContentProvider {
DatabaseBackend databaseBackend = DatabaseBackend DatabaseBackend databaseBackend = DatabaseBackend
.getInstance(getContext()); .getInstance(getContext());
String uuids = uri.getPath(); String uuids = uri.getPath();
Log.d("xmppService", "uuids = " + uuids+" mode="+mode); Log.d(Config.LOGTAG, "uuids = " + uuids + " mode=" + mode);
if (uuids == null) { if (uuids == null) {
throw new FileNotFoundException(); throw new FileNotFoundException();
} }
@ -37,21 +37,21 @@ public class ImageProvider extends ContentProvider {
} }
String conversationUuid = uuidsSplited[1]; String conversationUuid = uuidsSplited[1];
String messageUuid = uuidsSplited[2].split("\\.")[0]; String messageUuid = uuidsSplited[2].split("\\.")[0];
Log.d("xmppService","messageUuid="+messageUuid); Log.d(Config.LOGTAG, "messageUuid=" + messageUuid);
Conversation conversation = databaseBackend Conversation conversation = databaseBackend
.findConversationByUuid(conversationUuid); .findConversationByUuid(conversationUuid);
if (conversation == null) { if (conversation == null) {
throw new FileNotFoundException("conversation " + conversationUuid throw new FileNotFoundException("conversation "
+ " could not be found"); + conversationUuid + " could not be found");
} }
Message message = databaseBackend.findMessageByUuid(messageUuid); Message message = databaseBackend.findMessageByUuid(messageUuid);
if (message == null) { if (message == null) {
throw new FileNotFoundException("message " + messageUuid throw new FileNotFoundException("message " + messageUuid
+ " could not be found"); + " could not be found");
} }
Account account = databaseBackend.findAccountByUuid(conversation Account account = databaseBackend.findAccountByUuid(conversation
.getAccountUuid()); .getAccountUuid());
if (account == null) { if (account == null) {
@ -60,7 +60,7 @@ public class ImageProvider extends ContentProvider {
} }
message.setConversation(conversation); message.setConversation(conversation);
conversation.setAccount(account); conversation.setAccount(account);
File file = fileBackend.getJingleFileLegacy(message); File file = fileBackend.getJingleFileLegacy(message);
pfd = ParcelFileDescriptor.open(file, pfd = ParcelFileDescriptor.open(file,
ParcelFileDescriptor.MODE_READ_ONLY); ParcelFileDescriptor.MODE_READ_ONLY);
@ -100,13 +100,10 @@ public class ImageProvider extends ContentProvider {
public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) { public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
return 0; return 0;
} }
public static Uri getProviderUri(Message message) { public static Uri getProviderUri(Message message) {
return Uri return Uri.parse("content://eu.siacs.conversations.images/"
.parse("content://eu.siacs.conversations.images/" + message.getConversationUuid() + "/" + message.getUuid()
+ message.getConversationUuid() + ".webp");
+ "/"
+ message.getUuid()
+ ".webp");
} }
} }

View file

@ -20,6 +20,7 @@ import de.duenndns.ssl.MemorizingTrustManager;
import net.java.otr4j.OtrException; import net.java.otr4j.OtrException;
import net.java.otr4j.session.Session; import net.java.otr4j.session.Session;
import net.java.otr4j.session.SessionStatus; import net.java.otr4j.session.SessionStatus;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
@ -83,18 +84,11 @@ import android.util.Log;
public class XmppConnectionService extends Service { public class XmppConnectionService extends Service {
protected static final String LOGTAG = "xmppService";
public DatabaseBackend databaseBackend; public DatabaseBackend databaseBackend;
private FileBackend fileBackend; private FileBackend fileBackend;
public long startDate; public long startDate;
private static final int PING_MAX_INTERVAL = 300;
private static final int PING_MIN_INTERVAL = 30;
private static final int PING_TIMEOUT = 10;
private static final int CONNECT_TIMEOUT = 90;
public static final long CARBON_GRACE_PERIOD = 60000L;
private static String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts"; private static String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
private MemorizingTrustManager mMemorizingTrustManager; private MemorizingTrustManager mMemorizingTrustManager;
@ -169,16 +163,17 @@ public class XmppConnectionService extends Service {
} }
if (connection != null && connection.getFeatures().csi()) { if (connection != null && connection.getFeatures().csi()) {
if (checkListeners()) { if (checkListeners()) {
Log.d(LOGTAG, account.getJid() Log.d(Config.LOGTAG, account.getJid()
+ " sending csi//inactive"); + " sending csi//inactive");
connection.sendInactive(); connection.sendInactive();
} else { } else {
Log.d(LOGTAG, account.getJid() + " sending csi//active"); Log.d(Config.LOGTAG, account.getJid()
+ " sending csi//active");
connection.sendActive(); connection.sendActive();
} }
} }
syncDirtyContacts(account); syncDirtyContacts(account);
scheduleWakeupCall(PING_MAX_INTERVAL, true); scheduleWakeupCall(Config.PING_MAX_INTERVAL, true);
} else if (account.getStatus() == Account.STATUS_OFFLINE) { } else if (account.getStatus() == Account.STATUS_OFFLINE) {
resetSendingToWaiting(account); resetSendingToWaiting(account);
if (!account.isOptionSet(Account.OPTION_DISABLED)) { if (!account.isOptionSet(Account.OPTION_DISABLED)) {
@ -192,7 +187,7 @@ public class XmppConnectionService extends Service {
&& (account.getStatus() != Account.STATUS_NO_INTERNET)) { && (account.getStatus() != Account.STATUS_NO_INTERNET)) {
if (connection != null) { if (connection != null) {
int next = connection.getTimeToNextAttempt(); int next = connection.getTimeToNextAttempt();
Log.d(LOGTAG, account.getJid() Log.d(Config.LOGTAG, account.getJid()
+ ": error connecting account. try again in " + ": error connecting account. try again in "
+ next + "s for the " + next + "s for the "
+ (connection.getAttempt() + 1) + " time"); + (connection.getAttempt() + 1) + " time");
@ -355,10 +350,11 @@ public class XmppConnectionService extends Service {
.getLastPacketReceived(); .getLastPacketReceived();
long lastSent = account.getXmppConnection() long lastSent = account.getXmppConnection()
.getLastPingSent(); .getLastPingSent();
if (lastSent - lastReceived >= PING_TIMEOUT * 1000) { if (lastSent - lastReceived >= Config.PING_TIMEOUT * 1000) {
Log.d(LOGTAG, account.getJid() + ": ping timeout"); Log.d(Config.LOGTAG, account.getJid()
+ ": ping timeout");
this.reconnectAccount(account, true); this.reconnectAccount(account, true);
} else if (SystemClock.elapsedRealtime() - lastReceived >= PING_MIN_INTERVAL * 1000) { } else if (SystemClock.elapsedRealtime() - lastReceived >= Config.PING_MIN_INTERVAL * 1000) {
account.getXmppConnection().sendPing(); account.getXmppConnection().sendPing();
this.scheduleWakeupCall(2, false); this.scheduleWakeupCall(2, false);
} }
@ -370,8 +366,8 @@ public class XmppConnectionService extends Service {
new Thread(account.getXmppConnection()).start(); new Thread(account.getXmppConnection()).start();
} else if ((account.getStatus() == Account.STATUS_CONNECTING) } else if ((account.getStatus() == Account.STATUS_CONNECTING)
&& ((SystemClock.elapsedRealtime() - account && ((SystemClock.elapsedRealtime() - account
.getXmppConnection().getLastConnect()) / 1000 >= CONNECT_TIMEOUT)) { .getXmppConnection().getLastConnect()) / 1000 >= Config.CONNECT_TIMEOUT)) {
Log.d(LOGTAG, account.getJid() Log.d(Config.LOGTAG, account.getJid()
+ ": time out during connect reconnecting"); + ": time out during connect reconnecting");
reconnectAccount(account, true); reconnectAccount(account, true);
} else { } else {
@ -380,7 +376,7 @@ public class XmppConnectionService extends Service {
} }
} }
// in any case. reschedule wakup call // in any case. reschedule wakup call
this.scheduleWakeupCall(PING_MAX_INTERVAL, true); this.scheduleWakeupCall(Config.PING_MAX_INTERVAL, true);
} }
if (mOnAccountUpdate != null) { if (mOnAccountUpdate != null) {
mOnAccountUpdate.onAccountUpdate(); mOnAccountUpdate.onAccountUpdate();
@ -450,7 +446,7 @@ public class XmppConnectionService extends Service {
.getSystemService(Context.ALARM_SERVICE); .getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, EventReceiver.class); Intent intent = new Intent(context, EventReceiver.class);
alarmManager.cancel(PendingIntent.getBroadcast(context, 0, intent, 0)); alarmManager.cancel(PendingIntent.getBroadcast(context, 0, intent, 0));
Log.d(LOGTAG, "good bye"); Log.d(Config.LOGTAG, "good bye");
stopSelf(); stopSelf();
} }
@ -673,10 +669,10 @@ public class XmppConnectionService extends Service {
public void fetchRosterFromServer(Account account) { public void fetchRosterFromServer(Account account) {
IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET); IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
if (!"".equals(account.getRosterVersion())) { if (!"".equals(account.getRosterVersion())) {
Log.d(LOGTAG, account.getJid() + ": fetching roster version " Log.d(Config.LOGTAG, account.getJid()
+ account.getRosterVersion()); + ": fetching roster version " + account.getRosterVersion());
} else { } else {
Log.d(LOGTAG, account.getJid() + ": fetching roster"); Log.d(Config.LOGTAG, account.getJid() + ": fetching roster");
} }
iqPacket.query("jabber:iq:roster").setAttribute("ver", iqPacket.query("jabber:iq:roster").setAttribute("ver",
account.getRosterVersion()); account.getRosterVersion());
@ -1006,7 +1002,8 @@ public class XmppConnectionService extends Service {
XmppConnection connection = account.getXmppConnection(); XmppConnection connection = account.getXmppConnection();
if (connection != null && connection.getFeatures().csi()) { if (connection != null && connection.getFeatures().csi()) {
connection.sendActive(); connection.sendActive();
Log.d(LOGTAG, account.getJid() + " sending csi//active"); Log.d(Config.LOGTAG, account.getJid()
+ " sending csi//active");
} }
} }
} }
@ -1018,7 +1015,8 @@ public class XmppConnectionService extends Service {
XmppConnection connection = account.getXmppConnection(); XmppConnection connection = account.getXmppConnection();
if (connection != null && connection.getFeatures().csi()) { if (connection != null && connection.getFeatures().csi()) {
connection.sendInactive(); connection.sendInactive();
Log.d(LOGTAG, account.getJid() + " sending csi//inactive"); Log.d(Config.LOGTAG, account.getJid()
+ " sending csi//inactive");
} }
} }
} }
@ -1040,7 +1038,7 @@ public class XmppConnectionService extends Service {
account.pendingConferenceJoins.remove(conversation); account.pendingConferenceJoins.remove(conversation);
account.pendingConferenceLeaves.remove(conversation); account.pendingConferenceLeaves.remove(conversation);
if (account.getStatus() == Account.STATUS_ONLINE) { if (account.getStatus() == Account.STATUS_ONLINE) {
Log.d(LOGTAG, Log.d(Config.LOGTAG,
"joining conversation " + conversation.getContactJid()); "joining conversation " + conversation.getContactJid());
String nick = conversation.getMucOptions().getProposedNick(); String nick = conversation.getMucOptions().getProposedNick();
conversation.getMucOptions().setJoinNick(nick); conversation.getMucOptions().setJoinNick(nick);
@ -1142,8 +1140,8 @@ public class XmppConnectionService extends Service {
sendPresencePacket(conversation.getAccount(), packet); sendPresencePacket(conversation.getAccount(), packet);
conversation.getMucOptions().setOffline(); conversation.getMucOptions().setOffline();
conversation.deregisterWithBookmark(); conversation.deregisterWithBookmark();
Log.d(LOGTAG, conversation.getAccount().getJid() + " leaving muc " Log.d(Config.LOGTAG, conversation.getAccount().getJid()
+ conversation.getContactJid()); + " leaving muc " + conversation.getContactJid());
} else { } else {
account.pendingConferenceLeaves.add(conversation); account.pendingConferenceLeaves.add(conversation);
} }
@ -1184,7 +1182,6 @@ public class XmppConnectionService extends Service {
pushContactToServer(contact); pushContactToServer(contact);
} }
if (contact.getOption(Contact.Options.DIRTY_DELETE)) { if (contact.getOption(Contact.Options.DIRTY_DELETE)) {
Log.d(LOGTAG, "dirty delete");
deleteContactOnServer(contact); deleteContactOnServer(contact);
} }
} }
@ -1204,9 +1201,10 @@ public class XmppConnectionService extends Service {
Account account = conversation.getAccount(); Account account = conversation.getAccount();
List<Message> messages = conversation.getMessages(); List<Message> messages = conversation.getMessages();
Session otrSession = conversation.getOtrSession(); Session otrSession = conversation.getOtrSession();
Log.d(LOGTAG, account.getJid() + " otr session established with " Log.d(Config.LOGTAG,
+ conversation.getContactJid() + "/" account.getJid() + " otr session established with "
+ otrSession.getSessionID().getUserID()); + conversation.getContactJid() + "/"
+ otrSession.getSessionID().getUserID());
for (int i = 0; i < messages.size(); ++i) { for (int i = 0; i < messages.size(); ++i) {
Message msg = messages.get(i); Message msg = messages.get(i);
if ((msg.getStatus() == Message.STATUS_UNSEND || msg.getStatus() == Message.STATUS_WAITING) if ((msg.getStatus() == Message.STATUS_UNSEND || msg.getStatus() == Message.STATUS_WAITING)
@ -1280,8 +1278,8 @@ public class XmppConnectionService extends Service {
public void publishAvatar(Account account, Uri image, public void publishAvatar(Account account, Uri image,
final UiCallback<Avatar> callback) { final UiCallback<Avatar> callback) {
final Bitmap.CompressFormat format = Defaults.AVATAR_FORMAT; final Bitmap.CompressFormat format = Config.AVATAR_FORMAT;
final int size = Defaults.AVATAR_SIZE; final int size = Config.AVATAR_SIZE;
final Avatar avatar = getFileBackend() final Avatar avatar = getFileBackend()
.getPepAvatar(image, size, format); .getPepAvatar(image, size, format);
if (avatar != null) { if (avatar != null) {
@ -1341,7 +1339,7 @@ public class XmppConnectionService extends Service {
public void fetchAvatar(Account account, final Avatar avatar, public void fetchAvatar(Account account, final Avatar avatar,
final UiCallback<Avatar> callback) { final UiCallback<Avatar> callback) {
Log.d(LOGTAG, account.getJid() + ": retrieving avatar for " Log.d(Config.LOGTAG, account.getJid() + ": retrieving avatar for "
+ avatar.owner); + avatar.owner);
IqPacket packet = this.mIqGenerator.retrieveAvatar(avatar); IqPacket packet = this.mIqGenerator.retrieveAvatar(avatar);
sendIqPacket(account, packet, new OnIqPacketReceived() { sendIqPacket(account, packet, new OnIqPacketReceived() {
@ -1439,7 +1437,8 @@ public class XmppConnectionService extends Service {
} }
Thread thread = new Thread(account.getXmppConnection()); Thread thread = new Thread(account.getXmppConnection());
thread.start(); thread.start();
scheduleWakeupCall((int) (CONNECT_TIMEOUT * 1.2), false); scheduleWakeupCall((int) (Config.CONNECT_TIMEOUT * 1.2),
false);
} }
} }
}).start(); }).start();

View file

@ -23,13 +23,13 @@ import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.ui.adapter.ListItemAdapter; import eu.siacs.conversations.ui.adapter.ListItemAdapter;
public class ChooseContactActivity extends XmppActivity { public class ChooseContactActivity extends XmppActivity {
private ListView mListView; private ListView mListView;
private ArrayList<ListItem> contacts = new ArrayList<ListItem>(); private ArrayList<ListItem> contacts = new ArrayList<ListItem>();
private ArrayAdapter<ListItem> mContactsAdapter; private ArrayAdapter<ListItem> mContactsAdapter;
private EditText mSearchEditText; private EditText mSearchEditText;
private TextWatcher mSearchTextWatcher = new TextWatcher() { private TextWatcher mSearchTextWatcher = new TextWatcher() {
@Override @Override
@ -47,7 +47,7 @@ public class ChooseContactActivity extends XmppActivity {
int count) { int count) {
} }
}; };
private MenuItem.OnActionExpandListener mOnActionExpandListener = new MenuItem.OnActionExpandListener() { private MenuItem.OnActionExpandListener mOnActionExpandListener = new MenuItem.OnActionExpandListener() {
@Override @Override
@ -76,35 +76,35 @@ public class ChooseContactActivity extends XmppActivity {
return true; return true;
} }
}; };
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_choose_contact); setContentView(R.layout.activity_choose_contact);
mListView = (ListView) findViewById(R.id.choose_contact_list); mListView = (ListView) findViewById(R.id.choose_contact_list);
mContactsAdapter = new ListItemAdapter(getApplicationContext(), contacts); mContactsAdapter = new ListItemAdapter(getApplicationContext(),
contacts);
mListView.setAdapter(mContactsAdapter); mListView.setAdapter(mContactsAdapter);
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override @Override
public void onItemClick(AdapterView<?> arg0, View arg1, int position, public void onItemClick(AdapterView<?> arg0, View arg1,
long arg3) { int position, long arg3) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(), imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(),
InputMethodManager.HIDE_IMPLICIT_ONLY); InputMethodManager.HIDE_IMPLICIT_ONLY);
Intent request = getIntent(); Intent request = getIntent();
Intent data = new Intent(); Intent data = new Intent();
data.putExtra("contact",contacts.get(position).getJid()); data.putExtra("contact", contacts.get(position).getJid());
data.putExtra("account",request.getStringExtra("account")); data.putExtra("account", request.getStringExtra("account"));
data.putExtra("conversation",request.getStringExtra("conversation")); data.putExtra("conversation",
request.getStringExtra("conversation"));
setResult(RESULT_OK, data); setResult(RESULT_OK, data);
finish(); finish();
} }
}); });
} }
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.choose_contact, menu); getMenuInflater().inflate(R.menu.choose_contact, menu);
@ -121,7 +121,7 @@ public class ChooseContactActivity extends XmppActivity {
void onBackendConnected() { void onBackendConnected() {
filterContacts(null); filterContacts(null);
} }
protected void filterContacts(String needle) { protected void filterContacts(String needle) {
this.contacts.clear(); this.contacts.clear();
for (Account account : xmppConnectionService.getAccounts()) { for (Account account : xmppConnectionService.getAccounts()) {

View file

@ -250,7 +250,8 @@ public class ConferenceDetailsActivity extends XmppActivity {
if (contact.showInRoster()) { if (contact.showInRoster()) {
bm = contact.getImage(48, this); bm = contact.getImage(48, this);
name.setText(contact.getDisplayName()); name.setText(contact.getDisplayName());
role.setText(user.getName() + " \u2022 " + getReadableRole(user.getRole())); role.setText(user.getName() + " \u2022 "
+ getReadableRole(user.getRole()));
} else { } else {
bm = UIHelper.getContactPicture(user.getName(), 48, this, bm = UIHelper.getContactPicture(user.getName(), 48, this,
false); false);

View file

@ -111,7 +111,8 @@ public class ContactDetailsActivity extends XmppActivity {
public void onCheckedChanged(CompoundButton buttonView, public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) { boolean isChecked) {
if (isChecked) { if (isChecked) {
if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) { if (contact
.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
xmppConnectionService.sendPresencePacket(contact xmppConnectionService.sendPresencePacket(contact
.getAccount(), .getAccount(),
xmppConnectionService.getPresenceGenerator() xmppConnectionService.getPresenceGenerator()
@ -146,7 +147,7 @@ public class ContactDetailsActivity extends XmppActivity {
}; };
private OnAccountUpdate accountUpdate = new OnAccountUpdate() { private OnAccountUpdate accountUpdate = new OnAccountUpdate() {
@Override @Override
public void onAccountUpdate() { public void onAccountUpdate() {
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
@ -269,7 +270,7 @@ public class ContactDetailsActivity extends XmppActivity {
send.setOnCheckedChangeListener(this.mOnSendCheckedChange); send.setOnCheckedChangeListener(this.mOnSendCheckedChange);
receive.setOnCheckedChangeListener(this.mOnReceiveCheckedChange); receive.setOnCheckedChangeListener(this.mOnReceiveCheckedChange);
lastseen.setText(UIHelper.lastseen(getApplicationContext(), lastseen.setText(UIHelper.lastseen(getApplicationContext(),
contact.lastseen.time)); contact.lastseen.time));
@ -363,7 +364,8 @@ public class ContactDetailsActivity extends XmppActivity {
@Override @Override
public void onBackendConnected() { public void onBackendConnected() {
xmppConnectionService.setOnRosterUpdateListener(this.rosterUpdate); xmppConnectionService.setOnRosterUpdateListener(this.rosterUpdate);
xmppConnectionService.setOnAccountListChangedListener(this.accountUpdate ); xmppConnectionService
.setOnAccountListChangedListener(this.accountUpdate);
if ((accountJid != null) && (contactJid != null)) { if ((accountJid != null) && (contactJid != null)) {
Account account = xmppConnectionService Account account = xmppConnectionService
.findAccountByJid(accountJid); .findAccountByJid(accountJid);

View file

@ -35,7 +35,6 @@ import android.graphics.drawable.Drawable;
import android.support.v4.widget.SlidingPaneLayout; import android.support.v4.widget.SlidingPaneLayout;
import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener; import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
@ -81,7 +80,7 @@ public class ConversationActivity extends XmppActivity {
private ArrayAdapter<Conversation> listAdapter; private ArrayAdapter<Conversation> listAdapter;
private OnConversationUpdate onConvChanged = new OnConversationUpdate() { private OnConversationUpdate onConvChanged = new OnConversationUpdate() {
@Override @Override
public void onConversationUpdate() { public void onConversationUpdate() {
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
@ -111,7 +110,7 @@ public class ConversationActivity extends XmppActivity {
protected ConversationActivity activity = this; protected ConversationActivity activity = this;
private DisplayMetrics metrics; private DisplayMetrics metrics;
private Toast prepareImageToast; private Toast prepareImageToast;
private Uri pendingImageUri = null; private Uri pendingImageUri = null;
public List<Conversation> getConversationList() { public List<Conversation> getConversationList() {
@ -148,7 +147,7 @@ public class ConversationActivity extends XmppActivity {
setContentView(R.layout.fragment_conversations_overview); setContentView(R.layout.fragment_conversations_overview);
listView = (ListView) findViewById(R.id.list); listView = (ListView) findViewById(R.id.list);
getActionBar().setDisplayHomeAsUpEnabled(false); getActionBar().setDisplayHomeAsUpEnabled(false);
getActionBar().setHomeButtonEnabled(false); getActionBar().setHomeButtonEnabled(false);
@ -179,7 +178,7 @@ public class ConversationActivity extends XmppActivity {
public void onPanelOpened(View arg0) { public void onPanelOpened(View arg0) {
paneShouldBeOpen = true; paneShouldBeOpen = true;
ActionBar ab = getActionBar(); ActionBar ab = getActionBar();
if (ab!=null) { if (ab != null) {
ab.setDisplayHomeAsUpEnabled(false); ab.setDisplayHomeAsUpEnabled(false);
ab.setHomeButtonEnabled(false); ab.setHomeButtonEnabled(false);
ab.setTitle(R.string.app_name); ab.setTitle(R.string.app_name);
@ -194,11 +193,11 @@ public class ConversationActivity extends XmppActivity {
if ((conversationList.size() > 0) if ((conversationList.size() > 0)
&& (getSelectedConversation() != null)) { && (getSelectedConversation() != null)) {
ActionBar ab = getActionBar(); ActionBar ab = getActionBar();
if (ab!=null) { if (ab != null) {
ab.setDisplayHomeAsUpEnabled(true); ab.setDisplayHomeAsUpEnabled(true);
ab.setHomeButtonEnabled(true); ab.setHomeButtonEnabled(true);
ab.setTitle( ab.setTitle(getSelectedConversation().getName(
getSelectedConversation().getName(useSubject)); useSubject));
} }
invalidateOptionsMenu(); invalidateOptionsMenu();
if (!getSelectedConversation().isRead()) { if (!getSelectedConversation().isRead()) {
@ -232,7 +231,8 @@ public class ConversationActivity extends XmppActivity {
MenuItem menuClearHistory = (MenuItem) menu MenuItem menuClearHistory = (MenuItem) menu
.findItem(R.id.action_clear_history); .findItem(R.id.action_clear_history);
MenuItem menuAdd = (MenuItem) menu.findItem(R.id.action_add); MenuItem menuAdd = (MenuItem) menu.findItem(R.id.action_add);
MenuItem menuInviteContact = (MenuItem) menu.findItem(R.id.action_invite); MenuItem menuInviteContact = (MenuItem) menu
.findItem(R.id.action_invite);
if ((spl.isOpen() && (spl.isSlideable()))) { if ((spl.isOpen() && (spl.isSlideable()))) {
menuArchive.setVisible(false); menuArchive.setVisible(false);
@ -267,11 +267,12 @@ public class ConversationActivity extends XmppActivity {
@Override @Override
public void onPresenceSelected() { public void onPresenceSelected() {
if (attachmentChoice == ATTACHMENT_CHOICE_TAKE_PHOTO) { if (attachmentChoice == ATTACHMENT_CHOICE_TAKE_PHOTO) {
pendingImageUri = xmppConnectionService.getFileBackend().getTakePhotoUri(); pendingImageUri = xmppConnectionService.getFileBackend()
Log.d("xmppService",pendingImageUri.toString()); .getTakePhotoUri();
Intent takePictureIntent = new Intent( Intent takePictureIntent = new Intent(
MediaStore.ACTION_IMAGE_CAPTURE); MediaStore.ACTION_IMAGE_CAPTURE);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,pendingImageUri); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
pendingImageUri);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) { if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent, startActivityForResult(takePictureIntent,
REQUEST_IMAGE_CAPTURE); REQUEST_IMAGE_CAPTURE);
@ -353,7 +354,7 @@ public class ConversationActivity extends XmppActivity {
return true; return true;
case R.id.action_attach_file: case R.id.action_attach_file:
View menuAttachFile = findViewById(R.id.action_attach_file); View menuAttachFile = findViewById(R.id.action_attach_file);
if (menuAttachFile==null) { if (menuAttachFile == null) {
break; break;
} }
PopupMenu attachFilePopup = new PopupMenu(this, menuAttachFile); PopupMenu attachFilePopup = new PopupMenu(this, menuAttachFile);
@ -405,7 +406,7 @@ public class ConversationActivity extends XmppActivity {
case R.id.action_security: case R.id.action_security:
final Conversation conversation = getSelectedConversation(); final Conversation conversation = getSelectedConversation();
View menuItemView = findViewById(R.id.action_security); View menuItemView = findViewById(R.id.action_security);
if (menuItemView==null) { if (menuItemView == null) {
break; break;
} }
PopupMenu popup = new PopupMenu(this, menuItemView); PopupMenu popup = new PopupMenu(this, menuItemView);
@ -527,11 +528,11 @@ public class ConversationActivity extends XmppActivity {
ConversationFragment selectedFragment = new ConversationFragment(); ConversationFragment selectedFragment = new ConversationFragment();
if (!isFinishing()) { if (!isFinishing()) {
FragmentTransaction transaction = getFragmentManager() FragmentTransaction transaction = getFragmentManager()
.beginTransaction(); .beginTransaction();
transaction.replace(R.id.selected_conversation, selectedFragment, transaction.replace(R.id.selected_conversation, selectedFragment,
"conversation"); "conversation");
transaction.commitAllowingStateLoss(); transaction.commitAllowingStateLoss();
} }
return selectedFragment; return selectedFragment;
@ -553,7 +554,8 @@ public class ConversationActivity extends XmppActivity {
if (xmppConnectionServiceBound) { if (xmppConnectionServiceBound) {
if ((Intent.ACTION_VIEW.equals(intent.getAction()) && (VIEW_CONVERSATION if ((Intent.ACTION_VIEW.equals(intent.getAction()) && (VIEW_CONVERSATION
.equals(intent.getType())))) { .equals(intent.getType())))) {
String convToView = (String) intent.getExtras().get(CONVERSATION); String convToView = (String) intent.getExtras().get(
CONVERSATION);
updateConversationList(); updateConversationList();
for (int i = 0; i < conversationList.size(); ++i) { for (int i = 0; i < conversationList.size(); ++i) {
if (conversationList.get(i).getUuid().equals(convToView)) { if (conversationList.get(i).getUuid().equals(convToView)) {
@ -600,9 +602,10 @@ public class ConversationActivity extends XmppActivity {
if (conversationList.size() == 0) { if (conversationList.size() == 0) {
updateConversationList(); updateConversationList();
} }
if (getSelectedConversation()!=null && pendingImageUri !=null) { if (getSelectedConversation() != null && pendingImageUri != null) {
attachImageToConversation(getSelectedConversation(), pendingImageUri); attachImageToConversation(getSelectedConversation(),
pendingImageUri);
pendingImageUri = null; pendingImageUri = null;
} else { } else {
pendingImageUri = null; pendingImageUri = null;
@ -670,7 +673,8 @@ public class ConversationActivity extends XmppActivity {
} else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) { } else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) {
pendingImageUri = data.getData(); pendingImageUri = data.getData();
if (xmppConnectionServiceBound) { if (xmppConnectionServiceBound) {
attachImageToConversation(getSelectedConversation(),pendingImageUri); attachImageToConversation(getSelectedConversation(),
pendingImageUri);
pendingImageUri = null; pendingImageUri = null;
} }
} else if (requestCode == REQUEST_SEND_PGP_IMAGE) { } else if (requestCode == REQUEST_SEND_PGP_IMAGE) {
@ -686,10 +690,12 @@ public class ConversationActivity extends XmppActivity {
// encryptTextMessage(); // encryptTextMessage();
} else if (requestCode == REQUEST_IMAGE_CAPTURE) { } else if (requestCode == REQUEST_IMAGE_CAPTURE) {
if (xmppConnectionServiceBound) { if (xmppConnectionServiceBound) {
attachImageToConversation(getSelectedConversation(), pendingImageUri); attachImageToConversation(getSelectedConversation(),
pendingImageUri);
pendingImageUri = null; pendingImageUri = null;
} }
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); Intent intent = new Intent(
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(pendingImageUri); intent.setData(pendingImageUri);
sendBroadcast(intent); sendBroadcast(intent);
} else if (requestCode == REQUEST_RECORD_AUDIO) { } else if (requestCode == REQUEST_RECORD_AUDIO) {
@ -700,7 +706,7 @@ public class ConversationActivity extends XmppActivity {
} }
private void attachAudioToConversation(Conversation conversation, Uri uri) { private void attachAudioToConversation(Conversation conversation, Uri uri) {
} }
private void attachImageToConversation(Conversation conversation, Uri uri) { private void attachImageToConversation(Conversation conversation, Uri uri) {
@ -744,7 +750,8 @@ public class ConversationActivity extends XmppActivity {
} }
public void updateConversationList() { public void updateConversationList() {
xmppConnectionService.populateWithOrderedConversations(conversationList); xmppConnectionService
.populateWithOrderedConversations(conversationList);
listView.invalidateViews(); listView.invalidateViews();
} }
@ -761,7 +768,8 @@ public class ConversationActivity extends XmppActivity {
try { try {
this.startIntentSenderForResult(pi.getIntentSender(), requestCode, this.startIntentSenderForResult(pi.getIntentSender(), requestCode,
null, 0, 0, 0); null, 0, 0, 0);
} catch (SendIntentException e1) {} } catch (SendIntentException e1) {
}
} }
class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> { class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {

View file

@ -201,7 +201,8 @@ public class EditAccountActivity extends XmppActivity {
this.mSaveButton.setEnabled(true); this.mSaveButton.setEnabled(true);
this.mSaveButton.setTextColor(getPrimaryTextColor()); this.mSaveButton.setTextColor(getPrimaryTextColor());
if (jidToEdit != null) { if (jidToEdit != null) {
if (mAccount!= null && mAccount.getStatus() == Account.STATUS_ONLINE) { if (mAccount != null
&& mAccount.getStatus() == Account.STATUS_ONLINE) {
this.mSaveButton.setText(R.string.save); this.mSaveButton.setText(R.string.save);
} else { } else {
this.mSaveButton.setText(R.string.connect); this.mSaveButton.setText(R.string.connect);

View file

@ -46,7 +46,7 @@ public class PublishProfilePictureActivity extends XmppActivity {
public void run() { public void run() {
if (mInitialAccountSetup) { if (mInitialAccountSetup) {
startActivity(new Intent(getApplicationContext(), startActivity(new Intent(getApplicationContext(),
StartConversationActivity.class)); StartConversationActivity.class));
} }
finish(); finish();
} }
@ -111,7 +111,7 @@ public class PublishProfilePictureActivity extends XmppActivity {
public void onClick(View v) { public void onClick(View v) {
if (mInitialAccountSetup) { if (mInitialAccountSetup) {
startActivity(new Intent(getApplicationContext(), startActivity(new Intent(getApplicationContext(),
StartConversationActivity.class)); StartConversationActivity.class));
} }
finish(); finish();
} }

View file

@ -14,7 +14,7 @@ public class SettingsActivity extends XmppActivity {
@Override @Override
void onBackendConnected() { void onBackendConnected() {
} }
} }

View file

@ -340,7 +340,7 @@ public class StartConversationActivity extends XmppActivity {
String contactJid = jid.getText().toString(); String contactJid = jid.getText().toString();
Account account = xmppConnectionService Account account = xmppConnectionService
.findAccountByJid(accountJid); .findAccountByJid(accountJid);
if (account==null) { if (account == null) {
dialog.dismiss(); dialog.dismiss();
return; return;
} }

View file

@ -4,6 +4,8 @@ import android.app.PendingIntent;
public interface UiCallback<T> { public interface UiCallback<T> {
public void success(T object); public void success(T object);
public void error(int errorCode, T object); public void error(int errorCode, T object);
public void userInputRequried(PendingIntent pi, T object); public void userInputRequried(PendingIntent pi, T object);
} }

View file

@ -1,5 +1,6 @@
package eu.siacs.conversations.ui; package eu.siacs.conversations.ui;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
@ -34,8 +35,6 @@ public abstract class XmppActivity extends Activity {
protected static final int REQUEST_ANNOUNCE_PGP = 0x0101; protected static final int REQUEST_ANNOUNCE_PGP = 0x0101;
protected static final int REQUEST_INVITE_TO_CONVERSATION = 0x0102; protected static final int REQUEST_INVITE_TO_CONVERSATION = 0x0102;
protected final static String LOGTAG = "xmppService";
public XmppConnectionService xmppConnectionService; public XmppConnectionService xmppConnectionService;
public boolean xmppConnectionServiceBound = false; public boolean xmppConnectionServiceBound = false;
protected boolean handledViewIntent = false; protected boolean handledViewIntent = false;
@ -370,7 +369,7 @@ public abstract class XmppActivity extends Activity {
if (conversation.getMode() == Conversation.MODE_MULTI) { if (conversation.getMode() == Conversation.MODE_MULTI) {
xmppConnectionService.invite(conversation, contactJid); xmppConnectionService.invite(conversation, contactJid);
} }
Log.d("xmppService", "inviting " + contactJid + " to " Log.d(Config.LOGTAG, "inviting " + contactJid + " to "
+ conversation.getName(true)); + conversation.getName(true));
} }
} }

View file

@ -14,9 +14,9 @@ import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
public class AccountAdapter extends ArrayAdapter<Account> { public class AccountAdapter extends ArrayAdapter<Account> {
private XmppActivity activity; private XmppActivity activity;
public AccountAdapter(XmppActivity activity, List<Account> objects) { public AccountAdapter(XmppActivity activity, List<Account> objects) {
super(activity, 0, objects); super(activity, 0, objects);
this.activity = activity; this.activity = activity;
@ -34,7 +34,7 @@ public class AccountAdapter extends ArrayAdapter<Account> {
jid.setText(account.getJid()); jid.setText(account.getJid());
TextView statusView = (TextView) view.findViewById(R.id.account_status); TextView statusView = (TextView) view.findViewById(R.id.account_status);
ImageView imageView = (ImageView) view.findViewById(R.id.account_image); ImageView imageView = (ImageView) view.findViewById(R.id.account_image);
imageView.setImageBitmap(account.getImage(activity,48)); imageView.setImageBitmap(account.getImage(activity, 48));
switch (account.getStatus()) { switch (account.getStatus()) {
case Account.STATUS_DISABLED: case Account.STATUS_DISABLED:
statusView.setText(getContext().getString( statusView.setText(getContext().getString(

View file

@ -19,7 +19,8 @@ public class KnownHostsAdapter extends ArrayAdapter<String> {
final String[] split = constraint.toString().split("@"); final String[] split = constraint.toString().split("@");
if (split.length == 1) { if (split.length == 1) {
for (String domain : domains) { for (String domain : domains) {
suggestions.add(split[0].toLowerCase(Locale.getDefault()) + "@" + domain); suggestions.add(split[0].toLowerCase(Locale
.getDefault()) + "@" + domain);
} }
} else if (split.length == 2) { } else if (split.length == 2) {
for (String domain : domains) { for (String domain : domains) {
@ -27,7 +28,8 @@ public class KnownHostsAdapter extends ArrayAdapter<String> {
suggestions.clear(); suggestions.clear();
break; break;
} else if (domain.contains(split[1])) { } else if (domain.contains(split[1])) {
suggestions.add(split[0].toLowerCase(Locale.getDefault()) + "@" + domain); suggestions.add(split[0].toLowerCase(Locale
.getDefault()) + "@" + domain);
} }
} }
} else { } else {

View file

@ -14,7 +14,6 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.text.Html;
import android.text.Spannable; import android.text.Spannable;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.style.ForegroundColorSpan; import android.text.style.ForegroundColorSpan;
@ -68,8 +67,9 @@ public class MessageAdapter extends ArrayAdapter<Message> {
public void setOnContactPictureClicked(OnContactPictureClicked listener) { public void setOnContactPictureClicked(OnContactPictureClicked listener) {
this.mOnContactPictureClickedListener = listener; this.mOnContactPictureClickedListener = listener;
} }
public void setOnContactPictureLongClicked(OnContactPictureLongClicked listener) { public void setOnContactPictureLongClicked(
OnContactPictureLongClicked listener) {
this.mOnContactPictureLongClickedListener = listener; this.mOnContactPictureLongClickedListener = listener;
} }
@ -219,7 +219,8 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} else { } else {
String privateMarker; String privateMarker;
if (message.getStatus() <= Message.STATUS_RECEIVED) { if (message.getStatus() <= Message.STATUS_RECEIVED) {
privateMarker = activity.getString(R.string.private_message); privateMarker = activity
.getString(R.string.private_message);
} else { } else {
String to; String to;
if (message.getPresence() != null) { if (message.getPresence() != null) {
@ -227,11 +228,18 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} else { } else {
to = message.getCounterpart(); to = message.getCounterpart();
} }
privateMarker = activity.getString(R.string.private_message_to, to); privateMarker = activity.getString(
R.string.private_message_to, to);
} }
SpannableString span = new SpannableString(privateMarker+" "+message.getBody()); SpannableString span = new SpannableString(privateMarker + " "
span.setSpan(new ForegroundColorSpan(activity.getSecondaryTextColor()), 0, privateMarker.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + message.getBody());
span.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, privateMarker.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); span.setSpan(
new ForegroundColorSpan(activity
.getSecondaryTextColor()), 0, privateMarker
.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
span.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0,
privateMarker.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
viewHolder.messageBody.setText(span); viewHolder.messageBody.setText(span);
} }
} else { } else {
@ -401,10 +409,11 @@ public class MessageAdapter extends ArrayAdapter<Message> {
contact, getContext())); contact, getContext()));
} else { } else {
String name = item.getPresence(); String name = item.getPresence();
if (name==null) { if (name == null) {
name = item.getCounterpart(); name = item.getCounterpart();
} }
viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(name, getContext())); viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
name, getContext()));
} }
viewHolder.contact_picture viewHolder.contact_picture
.setOnClickListener(new OnClickListener() { .setOnClickListener(new OnClickListener() {
@ -419,18 +428,20 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} }
}); });
viewHolder.contact_picture.setOnLongClickListener(new OnLongClickListener() { viewHolder.contact_picture
.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) { @Override
if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) { public boolean onLongClick(View v) {
MessageAdapter.this.mOnContactPictureLongClickedListener.onContactPictureLongClicked(item); if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
return true; MessageAdapter.this.mOnContactPictureLongClickedListener
} else { .onContactPictureLongClicked(item);
return false; return true;
} } else {
} return false;
}); }
}
});
} }
} }
@ -529,7 +540,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
public interface OnContactPictureClicked { public interface OnContactPictureClicked {
public void onContactPictureClicked(Message message); public void onContactPictureClicked(Message message);
} }
public interface OnContactPictureLongClicked { public interface OnContactPictureLongClicked {
public void onContactPictureLongClicked(Message message); public void onContactPictureLongClicked(Message message);
} }

View file

@ -11,7 +11,7 @@ import eu.siacs.conversations.entities.Account;
import android.util.Base64; import android.util.Base64;
public class CryptoHelper { public class CryptoHelper {
public static final String FILETRANSFER = "?FILETRANSFERv1:"; public static final String FILETRANSFER = "?FILETRANSFERv1:";
final protected static char[] hexArray = "0123456789abcdef".toCharArray(); final protected static char[] hexArray = "0123456789abcdef".toCharArray();
final protected static char[] vowels = "aeiou".toCharArray(); final protected static char[] vowels = "aeiou".toCharArray();
final protected static char[] consonants = "bcdfghjklmnpqrstvwxyz" final protected static char[] consonants = "bcdfghjklmnpqrstvwxyz"
@ -26,7 +26,7 @@ public class CryptoHelper {
} }
return new String(hexChars); return new String(hexChars);
} }
public static byte[] hexToBytes(String hexString) { public static byte[] hexToBytes(String hexString) {
byte[] array = new BigInteger(hexString, 16).toByteArray(); byte[] array = new BigInteger(hexString, 16).toByteArray();
if (array[0] == 0) { if (array[0] == 0) {
@ -42,13 +42,14 @@ public class CryptoHelper {
} }
private static byte[] concatenateByteArrays(byte[] a, byte[] b) { private static byte[] concatenateByteArrays(byte[] a, byte[] b) {
byte[] result = new byte[a.length + b.length]; byte[] result = new byte[a.length + b.length];
System.arraycopy(a, 0, result, 0, a.length); System.arraycopy(a, 0, result, 0, a.length);
System.arraycopy(b, 0, result, a.length, b.length); System.arraycopy(b, 0, result, a.length, b.length);
return result; return result;
} }
public static String saslDigestMd5(Account account, String challenge, SecureRandom random) { public static String saslDigestMd5(Account account, String challenge,
SecureRandom random) {
try { try {
String[] challengeParts = new String(Base64.decode(challenge, String[] challengeParts = new String(Base64.decode(challenge,
Base64.DEFAULT)).split(","); Base64.DEFAULT)).split(",");
@ -61,28 +62,29 @@ public class CryptoHelper {
return null; return null;
} }
} }
String digestUri = "xmpp/"+account.getServer(); String digestUri = "xmpp/" + account.getServer();
String nonceCount = "00000001"; String nonceCount = "00000001";
String x = account.getUsername() + ":" + account.getServer() + ":" String x = account.getUsername() + ":" + account.getServer() + ":"
+ account.getPassword(); + account.getPassword();
MessageDigest md = MessageDigest.getInstance("MD5"); MessageDigest md = MessageDigest.getInstance("MD5");
byte[] y = md byte[] y = md.digest(x.getBytes(Charset.defaultCharset()));
.digest(x.getBytes(Charset.defaultCharset()));
String cNonce = new BigInteger(100, random).toString(32); String cNonce = new BigInteger(100, random).toString(32);
byte[] a1 = concatenateByteArrays(y,(":"+nonce+":"+cNonce).getBytes(Charset.defaultCharset())); byte[] a1 = concatenateByteArrays(y,
String a2 = "AUTHENTICATE:"+digestUri; (":" + nonce + ":" + cNonce).getBytes(Charset
.defaultCharset()));
String a2 = "AUTHENTICATE:" + digestUri;
String ha1 = bytesToHex(md.digest(a1)); String ha1 = bytesToHex(md.digest(a1));
String ha2 = bytesToHex(md.digest(a2.getBytes(Charset String ha2 = bytesToHex(md.digest(a2.getBytes(Charset
.defaultCharset()))); .defaultCharset())));
String kd = ha1 + ":" + nonce + ":"+nonceCount+":" + cNonce + ":auth:" String kd = ha1 + ":" + nonce + ":" + nonceCount + ":" + cNonce
+ ha2; + ":auth:" + ha2;
String response = bytesToHex(md.digest(kd.getBytes(Charset String response = bytesToHex(md.digest(kd.getBytes(Charset
.defaultCharset()))); .defaultCharset())));
String saslString = "username=\"" + account.getUsername() String saslString = "username=\"" + account.getUsername()
+ "\",realm=\"" + account.getServer() + "\",nonce=\"" + "\",realm=\"" + account.getServer() + "\",nonce=\""
+ nonce + "\",cnonce=\"" + cNonce + nonce + "\",cnonce=\"" + cNonce + "\",nc=" + nonceCount
+ "\",nc="+nonceCount+",qop=auth,digest-uri=\""+digestUri+"\",response=" + response + ",qop=auth,digest-uri=\"" + digestUri + "\",response="
+ ",charset=utf-8"; + response + ",charset=utf-8";
return Base64.encodeToString( return Base64.encodeToString(
saslString.getBytes(Charset.defaultCharset()), saslString.getBytes(Charset.defaultCharset()),
Base64.NO_WRAP); Base64.NO_WRAP);

View file

@ -10,6 +10,7 @@ import de.measite.minidns.record.A;
import de.measite.minidns.record.AAAA; import de.measite.minidns.record.AAAA;
import de.measite.minidns.record.Data; import de.measite.minidns.record.Data;
import de.measite.minidns.util.NameUtil; import de.measite.minidns.util.NameUtil;
import eu.siacs.conversations.Config;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
@ -47,14 +48,11 @@ public class DNSHelper {
Bundle namePort = new Bundle(); Bundle namePort = new Bundle();
try { try {
String qname = "_xmpp-client._tcp." + host; String qname = "_xmpp-client._tcp." + host;
Log.d("xmppService", "using dns server: " + dnsServer.getHostAddress() Log.d(Config.LOGTAG,
+ " to look up " + host); "using dns server: " + dnsServer.getHostAddress()
DNSMessage message = + " to look up " + host);
client.query( DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN,
qname, dnsServer.getHostAddress());
TYPE.SRV,
CLASS.IN,
dnsServer.getHostAddress());
// How should we handle priorities and weight? // How should we handle priorities and weight?
// Wikipedia has a nice article about priorities vs. weights: // Wikipedia has a nice article about priorities vs. weights:
@ -64,21 +62,20 @@ public class DNSHelper {
// a random order respecting the weight, and dump that priority by // a random order respecting the weight, and dump that priority by
// priority // priority
TreeMap<Integer, ArrayList<SRV>> priorities = TreeMap<Integer, ArrayList<SRV>> priorities = new TreeMap<Integer, ArrayList<SRV>>();
new TreeMap<Integer, ArrayList<SRV>>(); TreeMap<String, ArrayList<String>> ips4 = new TreeMap<String, ArrayList<String>>();
TreeMap<String, ArrayList<String>> ips4 = TreeMap<String, ArrayList<String>> ips6 = new TreeMap<String, ArrayList<String>>();
new TreeMap<String, ArrayList<String>>();
TreeMap<String, ArrayList<String>> ips6 =
new TreeMap<String, ArrayList<String>>();
for (Record[] rrset : new Record[][]{ message.getAnswers(), for (Record[] rrset : new Record[][] { message.getAnswers(),
message.getAdditionalResourceRecords()}) { message.getAdditionalResourceRecords() }) {
for (Record rr : rrset) { for (Record rr : rrset) {
Data d = rr.getPayload(); Data d = rr.getPayload();
if (d instanceof SRV && NameUtil.idnEquals(qname,rr.getName())) { if (d instanceof SRV
&& NameUtil.idnEquals(qname, rr.getName())) {
SRV srv = (SRV) d; SRV srv = (SRV) d;
if (!priorities.containsKey(srv.getPriority())) { if (!priorities.containsKey(srv.getPriority())) {
priorities.put(srv.getPriority(), new ArrayList<SRV>(2)); priorities.put(srv.getPriority(),
new ArrayList<SRV>(2));
} }
priorities.get(srv.getPriority()).add(srv); priorities.get(srv.getPriority()).add(srv);
} }
@ -100,8 +97,9 @@ public class DNSHelper {
} }
Random rnd = new Random(); Random rnd = new Random();
ArrayList<SRV> result = new ArrayList<SRV>(priorities.size() * 2 + 1); ArrayList<SRV> result = new ArrayList<SRV>(
for (ArrayList<SRV> s: priorities.values()) { priorities.size() * 2 + 1);
for (ArrayList<SRV> s : priorities.values()) {
// trivial case // trivial case
if (s.size() <= 1) { if (s.size() <= 1) {
@ -110,18 +108,20 @@ public class DNSHelper {
} }
long totalweight = 0l; long totalweight = 0l;
for (SRV srv: s) { for (SRV srv : s) {
totalweight += srv.getWeight(); totalweight += srv.getWeight();
} }
while (totalweight > 0l && s.size() > 0) { while (totalweight > 0l && s.size() > 0) {
long p = (rnd.nextLong() & 0x7fffffffffffffffl) % totalweight; long p = (rnd.nextLong() & 0x7fffffffffffffffl)
% totalweight;
int i = 0; int i = 0;
while (p > 0) { while (p > 0) {
p -= s.get(i++).getPriority(); p -= s.get(i++).getPriority();
} }
i--; i--;
// remove is expensive, but we have only a few entries anyway // remove is expensive, but we have only a few entries
// anyway
SRV srv = s.remove(i); SRV srv = s.remove(i);
totalweight -= srv.getWeight(); totalweight -= srv.getWeight();
result.add(srv); result.add(srv);
@ -164,10 +164,10 @@ public class DNSHelper {
} }
} catch (SocketTimeoutException e) { } catch (SocketTimeoutException e) {
Log.d("xmppService", "timeout during dns"); Log.d(Config.LOGTAG, "timeout during dns");
namePort.putString("error", "timeout"); namePort.putString("error", "timeout");
} catch (Exception e) { } catch (Exception e) {
Log.d("xmppService","unhandled exception in sub project"); Log.d(Config.LOGTAG, "unhandled exception in sub project");
namePort.putString("error", "unhandled"); namePort.putString("error", "unhandled");
} }
return namePort; return namePort;

View file

@ -11,22 +11,25 @@ import java.lang.Thread.UncaughtExceptionHandler;
import android.content.Context; import android.content.Context;
public class ExceptionHandler implements UncaughtExceptionHandler { public class ExceptionHandler implements UncaughtExceptionHandler {
private UncaughtExceptionHandler defaultHandler; private UncaughtExceptionHandler defaultHandler;
private Context context; private Context context;
public ExceptionHandler(Context context) { public ExceptionHandler(Context context) {
this.context = context; this.context = context;
this.defaultHandler = Thread.getDefaultUncaughtExceptionHandler(); this.defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
} }
@Override @Override
public void uncaughtException(Thread thread, Throwable ex) { public void uncaughtException(Thread thread, Throwable ex) {
Writer result = new StringWriter(); Writer result = new StringWriter();
PrintWriter printWriter = new PrintWriter(result); PrintWriter printWriter = new PrintWriter(result);
ex.printStackTrace(printWriter); ex.printStackTrace(printWriter);
String stacktrace = result.toString(); String stacktrace = result.toString();
printWriter.close(); printWriter.close();
try { try {
OutputStream os = context.openFileOutput("stacktrace.txt",Context.MODE_PRIVATE); OutputStream os = context.openFileOutput("stacktrace.txt",
Context.MODE_PRIVATE);
os.write(stacktrace.getBytes()); os.write(stacktrace.getBytes());
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block

View file

@ -7,6 +7,7 @@ import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.List; import java.util.List;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
@ -26,77 +27,91 @@ import android.util.Log;
public class ExceptionHelper { public class ExceptionHelper {
public static void init(Context context) { public static void init(Context context) {
if(!(Thread.getDefaultUncaughtExceptionHandler() instanceof ExceptionHandler)) { if (!(Thread.getDefaultUncaughtExceptionHandler() instanceof ExceptionHandler)) {
Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(context)); Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(
context));
} }
} }
public static void checkForCrash(Context context, final XmppConnectionService service) { public static void checkForCrash(Context context,
final XmppConnectionService service) {
try { try {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); final SharedPreferences preferences = PreferenceManager
boolean neverSend = preferences.getBoolean("never_send",false); .getDefaultSharedPreferences(context);
boolean neverSend = preferences.getBoolean("never_send", false);
if (neverSend) { if (neverSend) {
return; return;
} }
List<Account> accounts = service.getAccounts(); List<Account> accounts = service.getAccounts();
Account account = null; Account account = null;
for(int i = 0; i < accounts.size(); ++i) { for (int i = 0; i < accounts.size(); ++i) {
if (!accounts.get(i).isOptionSet(Account.OPTION_DISABLED)) { if (!accounts.get(i).isOptionSet(Account.OPTION_DISABLED)) {
account = accounts.get(i); account = accounts.get(i);
break; break;
} }
} }
if (account==null) { if (account == null) {
return; return;
} }
final Account finalAccount = account; final Account finalAccount = account;
FileInputStream file = context.openFileInput("stacktrace.txt"); FileInputStream file = context.openFileInput("stacktrace.txt");
InputStreamReader inputStreamReader = new InputStreamReader( InputStreamReader inputStreamReader = new InputStreamReader(file);
file); BufferedReader stacktrace = new BufferedReader(inputStreamReader);
BufferedReader stacktrace = new BufferedReader( final StringBuilder report = new StringBuilder();
inputStreamReader); PackageManager pm = context.getPackageManager();
final StringBuilder report = new StringBuilder(); PackageInfo packageInfo = null;
PackageManager pm = context.getPackageManager(); try {
PackageInfo packageInfo = null; packageInfo = pm.getPackageInfo(context.getPackageName(), 0);
try { report.append("Version: " + packageInfo.versionName + '\n');
packageInfo = pm.getPackageInfo(context.getPackageName(), 0); report.append("Last Update: "
report.append("Version: "+packageInfo.versionName+'\n'); + DateUtils.formatDateTime(context,
report.append("Last Update: "+DateUtils.formatDateTime(context, packageInfo.lastUpdateTime, DateUtils.FORMAT_SHOW_TIME|DateUtils.FORMAT_SHOW_DATE)+'\n'); packageInfo.lastUpdateTime,
} catch (NameNotFoundException e) {} DateUtils.FORMAT_SHOW_TIME
String line; | DateUtils.FORMAT_SHOW_DATE) + '\n');
while((line = stacktrace.readLine()) != null) { } catch (NameNotFoundException e) {
report.append(line); }
report.append('\n'); String line;
} while ((line = stacktrace.readLine()) != null) {
file.close(); report.append(line);
context.deleteFile("stacktrace.txt"); report.append('\n');
}
file.close();
context.deleteFile("stacktrace.txt");
AlertDialog.Builder builder = new AlertDialog.Builder(context); AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(context.getString(R.string.crash_report_title)); builder.setTitle(context.getString(R.string.crash_report_title));
builder.setMessage(context.getText(R.string.crash_report_message)); builder.setMessage(context.getText(R.string.crash_report_message));
builder.setPositiveButton(context.getText(R.string.send_now), new OnClickListener() { builder.setPositiveButton(context.getText(R.string.send_now),
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) { @Override
public void onClick(DialogInterface dialog, int which) {
Log.d("xmppService","using account="+finalAccount.getJid()+" to send in stack trace");
Conversation conversation = service.findOrCreateConversation(finalAccount, "bugs@siacs.eu", false); Log.d(Config.LOGTAG, "using account="
Message message = new Message(conversation, report.toString(), Message.ENCRYPTION_NONE); + finalAccount.getJid()
service.sendMessage(message); + " to send in stack trace");
} Conversation conversation = service
}); .findOrCreateConversation(finalAccount,
builder.setNegativeButton(context.getText(R.string.send_never),new OnClickListener() { "bugs@siacs.eu", false);
Message message = new Message(conversation, report
@Override .toString(), Message.ENCRYPTION_NONE);
public void onClick(DialogInterface dialog, int which) { service.sendMessage(message);
preferences.edit().putBoolean("never_send", true).commit(); }
} });
}); builder.setNegativeButton(context.getText(R.string.send_never),
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
preferences.edit().putBoolean("never_send", true)
.commit();
}
});
builder.create().show(); builder.create().show();
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
return; return;
} catch (IOException e) { } catch (IOException e) {
return; return;
} }
} }
} }

View file

@ -21,306 +21,307 @@ import java.security.Security;
/** /**
* Fixes for the output of the default PRNG having low entropy. * Fixes for the output of the default PRNG having low entropy.
* *
* The fixes need to be applied via {@link #apply()} before any use of Java * The fixes need to be applied via {@link #apply()} before any use of Java
* Cryptography Architecture primitives. A good place to invoke them is in the * Cryptography Architecture primitives. A good place to invoke them is in the
* application's {@code onCreate}. * application's {@code onCreate}.
*/ */
public final class PRNGFixes { public final class PRNGFixes {
private static final int VERSION_CODE_JELLY_BEAN = 16; private static final int VERSION_CODE_JELLY_BEAN = 16;
private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18; private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18;
private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL = private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL = getBuildFingerprintAndDeviceSerial();
getBuildFingerprintAndDeviceSerial();
/** Hidden constructor to prevent instantiation. */ /** Hidden constructor to prevent instantiation. */
private PRNGFixes() {} private PRNGFixes() {
}
/** /**
* Applies all fixes. * Applies all fixes.
* *
* @throws SecurityException if a fix is needed but could not be applied. * @throws SecurityException
*/ * if a fix is needed but could not be applied.
public static void apply() { */
applyOpenSSLFix(); public static void apply() {
installLinuxPRNGSecureRandom(); applyOpenSSLFix();
} installLinuxPRNGSecureRandom();
}
/** /**
* Applies the fix for OpenSSL PRNG having low entropy. Does nothing if the * Applies the fix for OpenSSL PRNG having low entropy. Does nothing if the
* fix is not needed. * fix is not needed.
* *
* @throws SecurityException if the fix is needed but could not be applied. * @throws SecurityException
*/ * if the fix is needed but could not be applied.
private static void applyOpenSSLFix() throws SecurityException { */
if ((Build.VERSION.SDK_INT < VERSION_CODE_JELLY_BEAN) private static void applyOpenSSLFix() throws SecurityException {
|| (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2)) { if ((Build.VERSION.SDK_INT < VERSION_CODE_JELLY_BEAN)
// No need to apply the fix || (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2)) {
return; // No need to apply the fix
} return;
}
try { try {
// Mix in the device- and invocation-specific seed. // Mix in the device- and invocation-specific seed.
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto") Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
.getMethod("RAND_seed", byte[].class) .getMethod("RAND_seed", byte[].class)
.invoke(null, generateSeed()); .invoke(null, generateSeed());
// Mix output of Linux PRNG into OpenSSL's PRNG // Mix output of Linux PRNG into OpenSSL's PRNG
int bytesRead = (Integer) Class.forName( int bytesRead = (Integer) Class
"org.apache.harmony.xnet.provider.jsse.NativeCrypto") .forName(
.getMethod("RAND_load_file", String.class, long.class) "org.apache.harmony.xnet.provider.jsse.NativeCrypto")
.invoke(null, "/dev/urandom", 1024); .getMethod("RAND_load_file", String.class, long.class)
if (bytesRead != 1024) { .invoke(null, "/dev/urandom", 1024);
throw new IOException( if (bytesRead != 1024) {
"Unexpected number of bytes read from Linux PRNG: " throw new IOException(
+ bytesRead); "Unexpected number of bytes read from Linux PRNG: "
} + bytesRead);
} catch (Exception e) { }
throw new SecurityException("Failed to seed OpenSSL PRNG", e); } catch (Exception e) {
} throw new SecurityException("Failed to seed OpenSSL PRNG", e);
} }
}
/** /**
* Installs a Linux PRNG-backed {@code SecureRandom} implementation as the * Installs a Linux PRNG-backed {@code SecureRandom} implementation as the
* default. Does nothing if the implementation is already the default or if * default. Does nothing if the implementation is already the default or if
* there is not need to install the implementation. * there is not need to install the implementation.
* *
* @throws SecurityException if the fix is needed but could not be applied. * @throws SecurityException
*/ * if the fix is needed but could not be applied.
private static void installLinuxPRNGSecureRandom() */
throws SecurityException { private static void installLinuxPRNGSecureRandom() throws SecurityException {
if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) { if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) {
// No need to apply the fix // No need to apply the fix
return; return;
} }
// Install a Linux PRNG-based SecureRandom implementation as the // Install a Linux PRNG-based SecureRandom implementation as the
// default, if not yet installed. // default, if not yet installed.
Provider[] secureRandomProviders = Provider[] secureRandomProviders = Security
Security.getProviders("SecureRandom.SHA1PRNG"); .getProviders("SecureRandom.SHA1PRNG");
if ((secureRandomProviders == null) if ((secureRandomProviders == null)
|| (secureRandomProviders.length < 1) || (secureRandomProviders.length < 1)
|| (!LinuxPRNGSecureRandomProvider.class.equals( || (!LinuxPRNGSecureRandomProvider.class
secureRandomProviders[0].getClass()))) { .equals(secureRandomProviders[0].getClass()))) {
Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1); Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1);
} }
// Assert that new SecureRandom() and // Assert that new SecureRandom() and
// SecureRandom.getInstance("SHA1PRNG") return a SecureRandom backed // SecureRandom.getInstance("SHA1PRNG") return a SecureRandom backed
// by the Linux PRNG-based SecureRandom implementation. // by the Linux PRNG-based SecureRandom implementation.
SecureRandom rng1 = new SecureRandom(); SecureRandom rng1 = new SecureRandom();
if (!LinuxPRNGSecureRandomProvider.class.equals( if (!LinuxPRNGSecureRandomProvider.class.equals(rng1.getProvider()
rng1.getProvider().getClass())) { .getClass())) {
throw new SecurityException( throw new SecurityException(
"new SecureRandom() backed by wrong Provider: " "new SecureRandom() backed by wrong Provider: "
+ rng1.getProvider().getClass()); + rng1.getProvider().getClass());
} }
SecureRandom rng2; SecureRandom rng2;
try { try {
rng2 = SecureRandom.getInstance("SHA1PRNG"); rng2 = SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
throw new SecurityException("SHA1PRNG not available", e); throw new SecurityException("SHA1PRNG not available", e);
} }
if (!LinuxPRNGSecureRandomProvider.class.equals( if (!LinuxPRNGSecureRandomProvider.class.equals(rng2.getProvider()
rng2.getProvider().getClass())) { .getClass())) {
throw new SecurityException( throw new SecurityException(
"SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong" "SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong"
+ " Provider: " + rng2.getProvider().getClass()); + " Provider: " + rng2.getProvider().getClass());
} }
} }
/** /**
* {@code Provider} of {@code SecureRandom} engines which pass through * {@code Provider} of {@code SecureRandom} engines which pass through all
* all requests to the Linux PRNG. * requests to the Linux PRNG.
*/ */
private static class LinuxPRNGSecureRandomProvider extends Provider { private static class LinuxPRNGSecureRandomProvider extends Provider {
public LinuxPRNGSecureRandomProvider() { public LinuxPRNGSecureRandomProvider() {
super("LinuxPRNG", super("LinuxPRNG", 1.0,
1.0, "A Linux-specific random number provider that uses"
"A Linux-specific random number provider that uses" + " /dev/urandom");
+ " /dev/urandom"); // Although /dev/urandom is not a SHA-1 PRNG, some apps
// Although /dev/urandom is not a SHA-1 PRNG, some apps // explicitly request a SHA1PRNG SecureRandom and we thus need to
// explicitly request a SHA1PRNG SecureRandom and we thus need to // prevent them from getting the default implementation whose output
// prevent them from getting the default implementation whose output // may have low entropy.
// may have low entropy. put("SecureRandom.SHA1PRNG", LinuxPRNGSecureRandom.class.getName());
put("SecureRandom.SHA1PRNG", LinuxPRNGSecureRandom.class.getName()); put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
put("SecureRandom.SHA1PRNG ImplementedIn", "Software"); }
} }
}
/** /**
* {@link SecureRandomSpi} which passes all requests to the Linux PRNG * {@link SecureRandomSpi} which passes all requests to the Linux PRNG (
* ({@code /dev/urandom}). * {@code /dev/urandom}).
*/ */
public static class LinuxPRNGSecureRandom extends SecureRandomSpi { public static class LinuxPRNGSecureRandom extends SecureRandomSpi {
/* /*
* IMPLEMENTATION NOTE: Requests to generate bytes and to mix in a seed * IMPLEMENTATION NOTE: Requests to generate bytes and to mix in a seed
* are passed through to the Linux PRNG (/dev/urandom). Instances of * are passed through to the Linux PRNG (/dev/urandom). Instances of
* this class seed themselves by mixing in the current time, PID, UID, * this class seed themselves by mixing in the current time, PID, UID,
* build fingerprint, and hardware serial number (where available) into * build fingerprint, and hardware serial number (where available) into
* Linux PRNG. * Linux PRNG.
* *
* Concurrency: Read requests to the underlying Linux PRNG are * Concurrency: Read requests to the underlying Linux PRNG are
* serialized (on sLock) to ensure that multiple threads do not get * serialized (on sLock) to ensure that multiple threads do not get
* duplicated PRNG output. * duplicated PRNG output.
*/ */
private static final File URANDOM_FILE = new File("/dev/urandom"); private static final File URANDOM_FILE = new File("/dev/urandom");
private static final Object sLock = new Object(); private static final Object sLock = new Object();
/** /**
* Input stream for reading from Linux PRNG or {@code null} if not yet * Input stream for reading from Linux PRNG or {@code null} if not yet
* opened. * opened.
* *
* @GuardedBy("sLock") * @GuardedBy("sLock")
*/ */
private static DataInputStream sUrandomIn; private static DataInputStream sUrandomIn;
/** /**
* Output stream for writing to Linux PRNG or {@code null} if not yet * Output stream for writing to Linux PRNG or {@code null} if not yet
* opened. * opened.
* *
* @GuardedBy("sLock") * @GuardedBy("sLock")
*/ */
private static OutputStream sUrandomOut; private static OutputStream sUrandomOut;
/** /**
* Whether this engine instance has been seeded. This is needed because * Whether this engine instance has been seeded. This is needed because
* each instance needs to seed itself if the client does not explicitly * each instance needs to seed itself if the client does not explicitly
* seed it. * seed it.
*/ */
private boolean mSeeded; private boolean mSeeded;
@Override @Override
protected void engineSetSeed(byte[] bytes) { protected void engineSetSeed(byte[] bytes) {
try { try {
OutputStream out; OutputStream out;
synchronized (sLock) { synchronized (sLock) {
out = getUrandomOutputStream(); out = getUrandomOutputStream();
} }
out.write(bytes); out.write(bytes);
out.flush(); out.flush();
} catch (IOException e) { } catch (IOException e) {
// On a small fraction of devices /dev/urandom is not writable. // On a small fraction of devices /dev/urandom is not writable.
// Log and ignore. // Log and ignore.
Log.w(PRNGFixes.class.getSimpleName(), Log.w(PRNGFixes.class.getSimpleName(),
"Failed to mix seed into " + URANDOM_FILE); "Failed to mix seed into " + URANDOM_FILE);
} finally { } finally {
mSeeded = true; mSeeded = true;
} }
} }
@Override @Override
protected void engineNextBytes(byte[] bytes) { protected void engineNextBytes(byte[] bytes) {
if (!mSeeded) { if (!mSeeded) {
// Mix in the device- and invocation-specific seed. // Mix in the device- and invocation-specific seed.
engineSetSeed(generateSeed()); engineSetSeed(generateSeed());
} }
try { try {
DataInputStream in; DataInputStream in;
synchronized (sLock) { synchronized (sLock) {
in = getUrandomInputStream(); in = getUrandomInputStream();
} }
synchronized (in) { synchronized (in) {
in.readFully(bytes); in.readFully(bytes);
} }
} catch (IOException e) { } catch (IOException e) {
throw new SecurityException( throw new SecurityException("Failed to read from "
"Failed to read from " + URANDOM_FILE, e); + URANDOM_FILE, e);
} }
} }
@Override @Override
protected byte[] engineGenerateSeed(int size) { protected byte[] engineGenerateSeed(int size) {
byte[] seed = new byte[size]; byte[] seed = new byte[size];
engineNextBytes(seed); engineNextBytes(seed);
return seed; return seed;
} }
private DataInputStream getUrandomInputStream() { private DataInputStream getUrandomInputStream() {
synchronized (sLock) { synchronized (sLock) {
if (sUrandomIn == null) { if (sUrandomIn == null) {
// NOTE: Consider inserting a BufferedInputStream between // NOTE: Consider inserting a BufferedInputStream between
// DataInputStream and FileInputStream if you need higher // DataInputStream and FileInputStream if you need higher
// PRNG output performance and can live with future PRNG // PRNG output performance and can live with future PRNG
// output being pulled into this process prematurely. // output being pulled into this process prematurely.
try { try {
sUrandomIn = new DataInputStream( sUrandomIn = new DataInputStream(new FileInputStream(
new FileInputStream(URANDOM_FILE)); URANDOM_FILE));
} catch (IOException e) { } catch (IOException e) {
throw new SecurityException("Failed to open " throw new SecurityException("Failed to open "
+ URANDOM_FILE + " for reading", e); + URANDOM_FILE + " for reading", e);
} }
} }
return sUrandomIn; return sUrandomIn;
} }
} }
private OutputStream getUrandomOutputStream() throws IOException { private OutputStream getUrandomOutputStream() throws IOException {
synchronized (sLock) { synchronized (sLock) {
if (sUrandomOut == null) { if (sUrandomOut == null) {
sUrandomOut = new FileOutputStream(URANDOM_FILE); sUrandomOut = new FileOutputStream(URANDOM_FILE);
} }
return sUrandomOut; return sUrandomOut;
} }
} }
} }
/** /**
* Generates a device- and invocation-specific seed to be mixed into the * Generates a device- and invocation-specific seed to be mixed into the
* Linux PRNG. * Linux PRNG.
*/ */
private static byte[] generateSeed() { private static byte[] generateSeed() {
try { try {
ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream(); ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream();
DataOutputStream seedBufferOut = DataOutputStream seedBufferOut = new DataOutputStream(seedBuffer);
new DataOutputStream(seedBuffer); seedBufferOut.writeLong(System.currentTimeMillis());
seedBufferOut.writeLong(System.currentTimeMillis()); seedBufferOut.writeLong(System.nanoTime());
seedBufferOut.writeLong(System.nanoTime()); seedBufferOut.writeInt(Process.myPid());
seedBufferOut.writeInt(Process.myPid()); seedBufferOut.writeInt(Process.myUid());
seedBufferOut.writeInt(Process.myUid()); seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL);
seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL); seedBufferOut.close();
seedBufferOut.close(); return seedBuffer.toByteArray();
return seedBuffer.toByteArray(); } catch (IOException e) {
} catch (IOException e) { throw new SecurityException("Failed to generate seed", e);
throw new SecurityException("Failed to generate seed", e); }
} }
}
/** /**
* Gets the hardware serial number of this device. * Gets the hardware serial number of this device.
* *
* @return serial number or {@code null} if not available. * @return serial number or {@code null} if not available.
*/ */
private static String getDeviceSerialNumber() { private static String getDeviceSerialNumber() {
// We're using the Reflection API because Build.SERIAL is only available // We're using the Reflection API because Build.SERIAL is only available
// since API Level 9 (Gingerbread, Android 2.3). // since API Level 9 (Gingerbread, Android 2.3).
try { try {
return (String) Build.class.getField("SERIAL").get(null); return (String) Build.class.getField("SERIAL").get(null);
} catch (Exception ignored) { } catch (Exception ignored) {
return null; return null;
} }
} }
private static byte[] getBuildFingerprintAndDeviceSerial() { private static byte[] getBuildFingerprintAndDeviceSerial() {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
String fingerprint = Build.FINGERPRINT; String fingerprint = Build.FINGERPRINT;
if (fingerprint != null) { if (fingerprint != null) {
result.append(fingerprint); result.append(fingerprint);
} }
String serial = getDeviceSerialNumber(); String serial = getDeviceSerialNumber();
if (serial != null) { if (serial != null) {
result.append(serial); result.append(serial);
} }
try { try {
return result.toString().getBytes("UTF-8"); return result.toString().getBytes("UTF-8");
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
throw new RuntimeException("UTF-8 encoding not supported"); throw new RuntimeException("UTF-8 encoding not supported");
} }
} }
} }

View file

@ -18,7 +18,7 @@ public class PhoneHelper {
public static void loadPhoneContacts(Context context, public static void loadPhoneContacts(Context context,
final OnPhoneContactsLoadedListener listener) { final OnPhoneContactsLoadedListener listener) {
final List<Bundle> phoneContacts = new ArrayList<Bundle>(); final List<Bundle> phoneContacts = new ArrayList<Bundle>();
final String[] PROJECTION = new String[] { ContactsContract.Data._ID, final String[] PROJECTION = new String[] { ContactsContract.Data._ID,
ContactsContract.Data.DISPLAY_NAME, ContactsContract.Data.DISPLAY_NAME,
ContactsContract.Data.PHOTO_THUMBNAIL_URI, ContactsContract.Data.PHOTO_THUMBNAIL_URI,
@ -38,7 +38,7 @@ public class PhoneHelper {
@Override @Override
public void onLoadComplete(Loader<Cursor> arg0, Cursor cursor) { public void onLoadComplete(Loader<Cursor> arg0, Cursor cursor) {
if (cursor==null) { if (cursor == null) {
return; return;
} }
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
@ -55,8 +55,10 @@ public class PhoneHelper {
.getColumnIndex(ContactsContract.Data.PHOTO_THUMBNAIL_URI))); .getColumnIndex(ContactsContract.Data.PHOTO_THUMBNAIL_URI)));
contact.putString("lookup", cursor.getString(cursor contact.putString("lookup", cursor.getString(cursor
.getColumnIndex(ContactsContract.Data.LOOKUP_KEY))); .getColumnIndex(ContactsContract.Data.LOOKUP_KEY)));
contact.putString("jid",cursor.getString(cursor contact.putString(
"jid",
cursor.getString(cursor
.getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA))); .getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA)));
phoneContacts.add(contact); phoneContacts.add(contact);
} }
@ -69,8 +71,7 @@ public class PhoneHelper {
} }
public static Uri getSefliUri(Context context) { public static Uri getSefliUri(Context context) {
String[] mProjection = new String[] { Profile._ID, String[] mProjection = new String[] { Profile._ID, Profile.PHOTO_URI };
Profile.PHOTO_URI };
Cursor mProfileCursor = context.getContentResolver().query( Cursor mProfileCursor = context.getContentResolver().query(
Profile.CONTENT_URI, mProjection, null, null, null); Profile.CONTENT_URI, mProjection, null, null, null);

View file

@ -118,9 +118,13 @@ public class UIHelper {
} }
private static int getNameColor(String name) { private static int getNameColor(String name) {
/*int holoColors[] = { 0xFF1da9da, 0xFFb368d9, 0xFF83b600, 0xFFffa713, /*
0xFFe92727 };*/ * int holoColors[] = { 0xFF1da9da, 0xFFb368d9, 0xFF83b600, 0xFFffa713,
int holoColors[] = {0xFFe91e63, 0xFF9c27b0, 0xFF673ab7, 0xFF3f51b5, 0xFF5677fc, 0xFF03a9f4, 0xFF00bcd4, 0xFF009688, 0xFFff5722, 0xFF795548, 0xFF607d8b}; * 0xFFe92727 };
*/
int holoColors[] = { 0xFFe91e63, 0xFF9c27b0, 0xFF673ab7, 0xFF3f51b5,
0xFF5677fc, 0xFF03a9f4, 0xFF00bcd4, 0xFF009688, 0xFFff5722,
0xFF795548, 0xFF607d8b };
return holoColors[(int) ((name.hashCode() & 0xffffffffl) % holoColors.length)]; return holoColors[(int) ((name.hashCode() & 0xffffffffl) % holoColors.length)];
} }
@ -216,9 +220,9 @@ public class UIHelper {
} }
ArrayList<String> names = new ArrayList<String>(); ArrayList<String> names = new ArrayList<String>();
names.add(conversation.getMucOptions().getActualNick()); names.add(conversation.getMucOptions().getActualNick());
for(User user : members) { for (User user : members) {
names.add(user.getName()); names.add(user.getName());
if (names.size() > 4 ) { if (names.size() > 4) {
break; break;
} }
} }

View file

@ -4,9 +4,9 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class Validator { public class Validator {
public static final Pattern VALID_JID = public static final Pattern VALID_JID = Pattern.compile(
Pattern.compile("^[^@/<>'\"\\s]+@[^@/<>'\"\\s]+$", Pattern.CASE_INSENSITIVE); "^[^@/<>'\"\\s]+@[^@/<>'\"\\s]+$", Pattern.CASE_INSENSITIVE);
public static boolean isValidJid(String jid) { public static boolean isValidJid(String jid) {
Matcher matcher = VALID_JID.matcher(jid); Matcher matcher = VALID_JID.matcher(jid);
return matcher.find(); return matcher.find();

View file

@ -12,41 +12,43 @@ import java.util.zip.InflaterInputStream;
*/ */
public class ZLibInputStream extends InflaterInputStream { public class ZLibInputStream extends InflaterInputStream {
/** /**
* Construct a ZLibInputStream, reading data from the underlying stream. * Construct a ZLibInputStream, reading data from the underlying stream.
* *
* @param is The {@code InputStream} to read data from. * @param is
* @throws IOException If an {@code IOException} occurs. * The {@code InputStream} to read data from.
*/ * @throws IOException
public ZLibInputStream(InputStream is) throws IOException { * If an {@code IOException} occurs.
super(is, new Inflater(), 512); */
} public ZLibInputStream(InputStream is) throws IOException {
super(is, new Inflater(), 512);
}
/** /**
* Provide a more InputStream compatible version of available. * Provide a more InputStream compatible version of available. A return
* A return value of 1 means that it is likly to read one byte without * value of 1 means that it is likly to read one byte without blocking, 0
* blocking, 0 means that the system is known to block for more input. * means that the system is known to block for more input.
* *
* @return 0 if no data is available, 1 otherwise * @return 0 if no data is available, 1 otherwise
* @throws IOException * @throws IOException
*/ */
@Override @Override
public int available() throws IOException { public int available() throws IOException {
/* This is one of the funny code blocks. /*
* InflaterInputStream.available violates the contract of * This is one of the funny code blocks. InflaterInputStream.available
* InputStream.available, which breaks kXML2. * violates the contract of InputStream.available, which breaks kXML2.
* *
* I'm not sure who's to blame, oracle/sun for a broken api or the * I'm not sure who's to blame, oracle/sun for a broken api or the
* google guys for mixing a sun bug with a xml reader that can't handle * google guys for mixing a sun bug with a xml reader that can't handle
* it.... * it....
* *
* Anyway, this simple if breaks suns distorted reality, but helps * Anyway, this simple if breaks suns distorted reality, but helps to
* to use the api as intended. * use the api as intended.
*/ */
if (inf.needsInput()) { if (inf.needsInput()) {
return 0; return 0;
} }
return super.available(); return super.available();
} }
} }

View file

@ -9,76 +9,87 @@ import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream; import java.util.zip.DeflaterOutputStream;
/** /**
* <p>Android 2.2 includes Java7 FLUSH_SYNC option, which will be used by this * <p>
* Android 2.2 includes Java7 FLUSH_SYNC option, which will be used by this
* Implementation, preferable via reflection. The @hide was remove in API level * Implementation, preferable via reflection. The @hide was remove in API level
* 19. This class might thus go away in the future.</p> * 19. This class might thus go away in the future.
* <p>Please use {@link ZLibOutputStream#SUPPORTED} to check for flush * </p>
* compatibility.</p> * <p>
* Please use {@link ZLibOutputStream#SUPPORTED} to check for flush
* compatibility.
* </p>
*/ */
public class ZLibOutputStream extends DeflaterOutputStream { public class ZLibOutputStream extends DeflaterOutputStream {
/** /**
* The reflection based flush method. * The reflection based flush method.
*/ */
private final static Method method; private final static Method method;
/** /**
* SUPPORTED is true if a flush compatible method exists. * SUPPORTED is true if a flush compatible method exists.
*/ */
public final static boolean SUPPORTED; public final static boolean SUPPORTED;
/** /**
* Static block to initialize {@link #SUPPORTED} and {@link #method}. * Static block to initialize {@link #SUPPORTED} and {@link #method}.
*/ */
static { static {
Method m = null; Method m = null;
try { try {
m = Deflater.class.getMethod("deflate", byte[].class, int.class, int.class, int.class); m = Deflater.class.getMethod("deflate", byte[].class, int.class,
} catch (SecurityException e) { int.class, int.class);
} catch (NoSuchMethodException e) { } catch (SecurityException e) {
} } catch (NoSuchMethodException e) {
method = m;
SUPPORTED = (method != null);
}
/**
* Create a new ZLib compatible output stream wrapping the given low level
* stream. ZLib compatiblity means we will send a zlib header.
* @param os OutputStream The underlying stream.
* @throws IOException In case of a lowlevel transfer problem.
* @throws NoSuchAlgorithmException In case of a {@link Deflater} error.
*/
public ZLibOutputStream(OutputStream os) throws IOException,
NoSuchAlgorithmException {
super(os, new Deflater(Deflater.BEST_COMPRESSION));
}
/**
* Flush the given stream, preferring Java7 FLUSH_SYNC if available.
* @throws IOException In case of a lowlevel exception.
*/
@Override
public void flush() throws IOException {
if (!SUPPORTED) {
super.flush();
return;
}
try {
int count = 0;
do {
count = (Integer) method.invoke(def, buf, 0, buf.length, 3);
if (count > 0) {
out.write(buf, 0, count);
} }
} while (count > 0); method = m;
} catch (IllegalArgumentException e) { SUPPORTED = (method != null);
throw new IOException("Can't flush"); }
} catch (IllegalAccessException e) {
throw new IOException("Can't flush"); /**
} catch (InvocationTargetException e) { * Create a new ZLib compatible output stream wrapping the given low level
throw new IOException("Can't flush"); * stream. ZLib compatiblity means we will send a zlib header.
} *
super.flush(); * @param os
} * OutputStream The underlying stream.
* @throws IOException
* In case of a lowlevel transfer problem.
* @throws NoSuchAlgorithmException
* In case of a {@link Deflater} error.
*/
public ZLibOutputStream(OutputStream os) throws IOException,
NoSuchAlgorithmException {
super(os, new Deflater(Deflater.BEST_COMPRESSION));
}
/**
* Flush the given stream, preferring Java7 FLUSH_SYNC if available.
*
* @throws IOException
* In case of a lowlevel exception.
*/
@Override
public void flush() throws IOException {
if (!SUPPORTED) {
super.flush();
return;
}
try {
int count = 0;
do {
count = (Integer) method.invoke(def, buf, 0, buf.length, 3);
if (count > 0) {
out.write(buf, 0, count);
}
} while (count > 0);
} catch (IllegalArgumentException e) {
throw new IOException("Can't flush");
} catch (IllegalAccessException e) {
throw new IOException("Can't flush");
} catch (InvocationTargetException e) {
throw new IOException("Can't flush");
}
super.flush();
}
} }

View file

@ -146,10 +146,10 @@ public class Element {
} }
public void setAttribute(String name, long value) { public void setAttribute(String name, long value) {
this.setAttribute(name, ""+value); this.setAttribute(name, "" + value);
} }
public void setAttribute(String name, int value) { public void setAttribute(String name, int value) {
this.setAttribute(name, ""+value); this.setAttribute(name, "" + value);
} }
} }

View file

@ -10,77 +10,78 @@ public class Tag {
public static final int START = 0; public static final int START = 0;
public static final int END = 1; public static final int END = 1;
public static final int EMPTY = 2; public static final int EMPTY = 2;
protected int type; protected int type;
protected String name; protected String name;
protected Hashtable<String, String> attributes = new Hashtable<String, String>(); protected Hashtable<String, String> attributes = new Hashtable<String, String>();
protected Tag(int type, String name) { protected Tag(int type, String name) {
this.type = type; this.type = type;
this.name = name; this.name = name;
} }
public static Tag no(String text) { public static Tag no(String text) {
return new Tag(NO,text); return new Tag(NO, text);
} }
public static Tag start(String name) { public static Tag start(String name) {
return new Tag(START,name); return new Tag(START, name);
} }
public static Tag end(String name) { public static Tag end(String name) {
return new Tag(END,name); return new Tag(END, name);
} }
public static Tag empty(String name) { public static Tag empty(String name) {
return new Tag(EMPTY,name); return new Tag(EMPTY, name);
} }
public String getName() { public String getName() {
return name; return name;
} }
public String getAttribute(String attrName) { public String getAttribute(String attrName) {
return this.attributes.get(attrName); return this.attributes.get(attrName);
} }
public Tag setAttribute(String attrName, String attrValue) { public Tag setAttribute(String attrName, String attrValue) {
this.attributes.put(attrName, attrValue); this.attributes.put(attrName, attrValue);
return this; return this;
} }
public Tag setAtttributes(Hashtable<String, String> attributes) { public Tag setAtttributes(Hashtable<String, String> attributes) {
this.attributes = attributes; this.attributes = attributes;
return this; return this;
} }
public boolean isStart(String needle) { public boolean isStart(String needle) {
if (needle==null) return false; if (needle == null)
return false;
return (this.type == START) && (needle.equals(this.name)); return (this.type == START) && (needle.equals(this.name));
} }
public boolean isEnd(String needle) { public boolean isEnd(String needle) {
if (needle==null) return false; if (needle == null)
return false;
return (this.type == END) && (needle.equals(this.name)); return (this.type == END) && (needle.equals(this.name));
} }
public boolean isNo() { public boolean isNo() {
return (this.type == NO); return (this.type == NO);
} }
public String toString() { public String toString() {
StringBuilder tagOutput = new StringBuilder(); StringBuilder tagOutput = new StringBuilder();
tagOutput.append('<'); tagOutput.append('<');
if (type==END) { if (type == END) {
tagOutput.append('/'); tagOutput.append('/');
} }
tagOutput.append(name); tagOutput.append(name);
if(type!=END) { if (type != END) {
Set<Entry<String, String>> attributeSet = attributes.entrySet(); Set<Entry<String, String>> attributeSet = attributes.entrySet();
Iterator<Entry<String, String>> it = attributeSet.iterator(); Iterator<Entry<String, String>> it = attributeSet.iterator();
while(it.hasNext()) { while (it.hasNext()) {
Entry<String,String> entry = it.next(); Entry<String, String> entry = it.next();
tagOutput.append(' '); tagOutput.append(' ');
tagOutput.append(entry.getKey()); tagOutput.append(entry.getKey());
tagOutput.append("=\""); tagOutput.append("=\"");
@ -88,7 +89,7 @@ public class Tag {
tagOutput.append('"'); tagOutput.append('"');
} }
} }
if (type==EMPTY) { if (type == EMPTY) {
tagOutput.append('/'); tagOutput.append('/');
} }
tagOutput.append('>'); tagOutput.append('>');

View file

@ -8,22 +8,23 @@ import java.util.concurrent.LinkedBlockingQueue;
import eu.siacs.conversations.xmpp.stanzas.AbstractStanza; import eu.siacs.conversations.xmpp.stanzas.AbstractStanza;
public class TagWriter { public class TagWriter {
private OutputStream plainOutputStream; private OutputStream plainOutputStream;
private OutputStreamWriter outputStream; private OutputStreamWriter outputStream;
private boolean finshed = false; private boolean finshed = false;
private LinkedBlockingQueue<AbstractStanza> writeQueue = new LinkedBlockingQueue<AbstractStanza>(); private LinkedBlockingQueue<AbstractStanza> writeQueue = new LinkedBlockingQueue<AbstractStanza>();
private Thread asyncStanzaWriter = new Thread() { private Thread asyncStanzaWriter = new Thread() {
private boolean shouldStop = false; private boolean shouldStop = false;
@Override @Override
public void run() { public void run() {
while(!shouldStop) { while (!shouldStop) {
if ((finshed)&&(writeQueue.size() == 0)) { if ((finshed) && (writeQueue.size() == 0)) {
return; return;
} }
try { try {
AbstractStanza output = writeQueue.take(); AbstractStanza output = writeQueue.take();
if (outputStream==null) { if (outputStream == null) {
shouldStop = true; shouldStop = true;
} else { } else {
outputStream.write(output.toString()); outputStream.write(output.toString());
@ -37,12 +38,12 @@ public class TagWriter {
} }
} }
}; };
public TagWriter() { public TagWriter() {
} }
public void setOutputStream(OutputStream out) throws IOException { public void setOutputStream(OutputStream out) throws IOException {
if (out==null) { if (out == null) {
throw new IOException(); throw new IOException();
} }
this.plainOutputStream = out; this.plainOutputStream = out;
@ -50,23 +51,23 @@ public class TagWriter {
} }
public OutputStream getOutputStream() throws IOException { public OutputStream getOutputStream() throws IOException {
if (this.plainOutputStream==null) { if (this.plainOutputStream == null) {
throw new IOException(); throw new IOException();
} }
return this.plainOutputStream; return this.plainOutputStream;
} }
public TagWriter beginDocument() throws IOException { public TagWriter beginDocument() throws IOException {
if (outputStream==null) { if (outputStream == null) {
throw new IOException("output stream was null"); throw new IOException("output stream was null");
} }
outputStream.write("<?xml version='1.0'?>"); outputStream.write("<?xml version='1.0'?>");
outputStream.flush(); outputStream.flush();
return this; return this;
} }
public TagWriter writeTag(Tag tag) throws IOException { public TagWriter writeTag(Tag tag) throws IOException {
if (outputStream==null) { if (outputStream == null) {
throw new IOException("output stream was null"); throw new IOException("output stream was null");
} }
outputStream.write(tag.toString()); outputStream.write(tag.toString());
@ -75,34 +76,34 @@ public class TagWriter {
} }
public TagWriter writeElement(Element element) throws IOException { public TagWriter writeElement(Element element) throws IOException {
if (outputStream==null) { if (outputStream == null) {
throw new IOException("output stream was null"); throw new IOException("output stream was null");
} }
outputStream.write(element.toString()); outputStream.write(element.toString());
outputStream.flush(); outputStream.flush();
return this; return this;
} }
public TagWriter writeStanzaAsync(AbstractStanza stanza) { public TagWriter writeStanzaAsync(AbstractStanza stanza) {
if (finshed) { if (finshed) {
return this; return this;
} else { } else {
if (!asyncStanzaWriter.isAlive()) { if (!asyncStanzaWriter.isAlive()) {
try { try {
asyncStanzaWriter.start(); asyncStanzaWriter.start();
} catch (IllegalThreadStateException e) { } catch (IllegalThreadStateException e) {
//already started // already started
}
} }
writeQueue.add(stanza);
return this;
} }
writeQueue.add(stanza);
return this;
}
} }
public void finish() { public void finish() {
this.finshed = true; this.finshed = true;
} }
public boolean finished() { public boolean finished() {
return (this.writeQueue.size() == 0); return (this.writeQueue.size() == 0);
} }

View file

@ -7,13 +7,14 @@ import java.io.InputStreamReader;
import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import eu.siacs.conversations.Config;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.PowerManager.WakeLock; import android.os.PowerManager.WakeLock;
import android.util.Log; import android.util.Log;
import android.util.Xml; import android.util.Xml;
public class XmlReader { public class XmlReader {
private static final String LOGTAG = "xmppService";
private XmlPullParser parser; private XmlPullParser parser;
private PowerManager.WakeLock wakeLock; private PowerManager.WakeLock wakeLock;
private InputStream is; private InputStream is;
@ -21,15 +22,16 @@ public class XmlReader {
public XmlReader(WakeLock wakeLock) { public XmlReader(WakeLock wakeLock) {
this.parser = Xml.newPullParser(); this.parser = Xml.newPullParser();
try { try {
this.parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES,true); this.parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES,
true);
} catch (XmlPullParserException e) { } catch (XmlPullParserException e) {
Log.d(LOGTAG,"error setting namespace feature on parser"); Log.d(Config.LOGTAG, "error setting namespace feature on parser");
} }
this.wakeLock = wakeLock; this.wakeLock = wakeLock;
} }
public void setInputStream(InputStream inputStream) throws IOException { public void setInputStream(InputStream inputStream) throws IOException {
if (inputStream==null) { if (inputStream == null) {
throw new IOException(); throw new IOException();
} }
this.is = inputStream; this.is = inputStream;
@ -41,14 +43,14 @@ public class XmlReader {
} }
public InputStream getInputStream() throws IOException { public InputStream getInputStream() throws IOException {
if (this.is==null) { if (this.is == null) {
throw new IOException(); throw new IOException();
} }
return is; return is;
} }
public void reset() throws IOException { public void reset() throws IOException {
if (this.is==null) { if (this.is == null) {
throw new IOException(); throw new IOException();
} }
try { try {
@ -57,62 +59,74 @@ public class XmlReader {
throw new IOException("error resetting parser"); throw new IOException("error resetting parser");
} }
} }
public Tag readTag() throws XmlPullParserException, IOException { public Tag readTag() throws XmlPullParserException, IOException {
if (wakeLock.isHeld()) { if (wakeLock.isHeld()) {
try { wakeLock.release();} catch (RuntimeException re) {} try {
wakeLock.release();
} catch (RuntimeException re) {
}
} }
try { try {
while(this.is != null && parser.next() != XmlPullParser.END_DOCUMENT) { while (this.is != null
wakeLock.acquire(); && parser.next() != XmlPullParser.END_DOCUMENT) {
if (parser.getEventType() == XmlPullParser.START_TAG) { wakeLock.acquire();
Tag tag = Tag.start(parser.getName()); if (parser.getEventType() == XmlPullParser.START_TAG) {
for(int i = 0; i < parser.getAttributeCount(); ++i) { Tag tag = Tag.start(parser.getName());
tag.setAttribute(parser.getAttributeName(i), parser.getAttributeValue(i)); for (int i = 0; i < parser.getAttributeCount(); ++i) {
} tag.setAttribute(parser.getAttributeName(i),
String xmlns = parser.getNamespace(); parser.getAttributeValue(i));
if (xmlns!=null) {
tag.setAttribute("xmlns",xmlns);
}
return tag;
} else if (parser.getEventType() == XmlPullParser.END_TAG) {
Tag tag = Tag.end(parser.getName());
return tag;
} else if (parser.getEventType() == XmlPullParser.TEXT) {
Tag tag = Tag.no(parser.getText());
return tag;
} }
String xmlns = parser.getNamespace();
if (xmlns != null) {
tag.setAttribute("xmlns", xmlns);
}
return tag;
} else if (parser.getEventType() == XmlPullParser.END_TAG) {
Tag tag = Tag.end(parser.getName());
return tag;
} else if (parser.getEventType() == XmlPullParser.TEXT) {
Tag tag = Tag.no(parser.getText());
return tag;
} }
}
if (wakeLock.isHeld()) { if (wakeLock.isHeld()) {
try { wakeLock.release();} catch (RuntimeException re) {} try {
wakeLock.release();
} catch (RuntimeException re) {
}
} }
} catch (ArrayIndexOutOfBoundsException e) { } catch (ArrayIndexOutOfBoundsException e) {
throw new IOException("xml parser mishandled ArrayIndexOufOfBounds", e); throw new IOException(
"xml parser mishandled ArrayIndexOufOfBounds", e);
} catch (StringIndexOutOfBoundsException e) { } catch (StringIndexOutOfBoundsException e) {
throw new IOException("xml parser mishandled StringIndexOufOfBounds", e); throw new IOException(
"xml parser mishandled StringIndexOufOfBounds", e);
} catch (NullPointerException e) { } catch (NullPointerException e) {
throw new IOException("xml parser mishandled NullPointerException", e); throw new IOException("xml parser mishandled NullPointerException",
e);
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
throw new IOException("xml parser mishandled IndexOutOfBound", e); throw new IOException("xml parser mishandled IndexOutOfBound", e);
} }
return null; return null;
} }
public Element readElement(Tag currentTag) throws XmlPullParserException, IOException { public Element readElement(Tag currentTag) throws XmlPullParserException,
IOException {
Element element = new Element(currentTag.getName()); Element element = new Element(currentTag.getName());
element.setAttributes(currentTag.getAttributes()); element.setAttributes(currentTag.getAttributes());
Tag nextTag = this.readTag(); Tag nextTag = this.readTag();
if (nextTag == null) { if (nextTag == null) {
throw new IOException("unterupted mid tag"); throw new IOException("unterupted mid tag");
} }
if(nextTag.isNo()) { if (nextTag.isNo()) {
element.setContent(nextTag.getName()); element.setContent(nextTag.getName());
nextTag = this.readTag(); nextTag = this.readTag();
if (nextTag == null) { if (nextTag == null) {
throw new IOException("unterupted mid tag"); throw new IOException("unterupted mid tag");
} }
} }
while(!nextTag.isEnd(element.getName())) { while (!nextTag.isEnd(element.getName())) {
if (!nextTag.isNo()) { if (!nextTag.isNo()) {
Element child = this.readElement(nextTag); Element child = this.readElement(nextTag);
element.addChild(child); element.addChild(child);

View file

@ -32,6 +32,7 @@ import android.os.PowerManager.WakeLock;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Log; import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
@ -58,7 +59,6 @@ import eu.siacs.conversations.xmpp.stanzas.streammgmt.ResumePacket;
public class XmppConnection implements Runnable { public class XmppConnection implements Runnable {
protected Account account; protected Account account;
private static final String LOGTAG = "xmppService";
private WakeLock wakeLock; private WakeLock wakeLock;
@ -67,7 +67,7 @@ public class XmppConnection implements Runnable {
private Socket socket; private Socket socket;
private XmlReader tagReader; private XmlReader tagReader;
private TagWriter tagWriter; private TagWriter tagWriter;
private Features features = new Features(this); private Features features = new Features(this);
private boolean shouldBind = true; private boolean shouldBind = true;
@ -78,7 +78,7 @@ public class XmppConnection implements Runnable {
private String streamId = null; private String streamId = null;
private int smVersion = 3; private int smVersion = 3;
private SparseArray<String> messageReceipts = new SparseArray<String>(); private SparseArray<String> messageReceipts = new SparseArray<String>();
private boolean usingCompression = false; private boolean usingCompression = false;
private int stanzasReceived = 0; private int stanzasReceived = 0;
@ -109,8 +109,8 @@ public class XmppConnection implements Runnable {
this.mRandom = service.getRNG(); this.mRandom = service.getRNG();
this.mMemorizingTrustManager = service.getMemorizingTrustManager(); this.mMemorizingTrustManager = service.getMemorizingTrustManager();
this.account = account; this.account = account;
this.wakeLock = service.getPowerManager().newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.wakeLock = service.getPowerManager().newWakeLock(
account.getJid()); PowerManager.PARTIAL_WAKE_LOCK, account.getJid());
tagWriter = new TagWriter(); tagWriter = new TagWriter();
} }
@ -133,7 +133,7 @@ public class XmppConnection implements Runnable {
} }
protected void connect() { protected void connect() {
Log.d(LOGTAG, account.getJid() + ": connecting"); Log.d(Config.LOGTAG, account.getJid() + ": connecting");
usingCompression = false; usingCompression = false;
lastConnect = SystemClock.elapsedRealtime(); lastConnect = SystemClock.elapsedRealtime();
lastPingSent = SystemClock.elapsedRealtime(); lastPingSent = SystemClock.elapsedRealtime();
@ -147,7 +147,7 @@ public class XmppConnection implements Runnable {
this.changeStatus(Account.STATUS_CONNECTING); this.changeStatus(Account.STATUS_CONNECTING);
Bundle namePort = DNSHelper.getSRVRecord(account.getServer()); Bundle namePort = DNSHelper.getSRVRecord(account.getServer());
if ("timeout".equals(namePort.getString("error"))) { if ("timeout".equals(namePort.getString("error"))) {
Log.d(LOGTAG, account.getJid() + ": dns timeout"); Log.d(Config.LOGTAG, account.getJid() + ": dns timeout");
this.changeStatus(Account.STATUS_OFFLINE); this.changeStatus(Account.STATUS_OFFLINE);
return; return;
} }
@ -156,13 +156,14 @@ public class XmppConnection implements Runnable {
int srvRecordPort = namePort.getInt("port"); int srvRecordPort = namePort.getInt("port");
if (srvRecordServer != null) { if (srvRecordServer != null) {
if (srvIpServer != null) { if (srvIpServer != null) {
Log.d(LOGTAG, account.getJid() + ": using values from dns " Log.d(Config.LOGTAG, account.getJid()
+ srvRecordServer + "[" + srvIpServer + "]:" + ": using values from dns " + srvRecordServer
+ srvRecordPort); + "[" + srvIpServer + "]:" + srvRecordPort);
socket = new Socket(srvIpServer, srvRecordPort); socket = new Socket(srvIpServer, srvRecordPort);
} else { } else {
Log.d(LOGTAG, account.getJid() + ": using values from dns " Log.d(Config.LOGTAG, account.getJid()
+ srvRecordServer + ":" + srvRecordPort); + ": using values from dns " + srvRecordServer
+ ":" + srvRecordPort);
socket = new Socket(srvRecordServer, srvRecordPort); socket = new Socket(srvRecordServer, srvRecordPort);
} }
} else { } else {
@ -180,7 +181,8 @@ public class XmppConnection implements Runnable {
processStream(nextTag); processStream(nextTag);
break; break;
} else { } else {
Log.d(LOGTAG, "found unexpected tag: " + nextTag.getName()); Log.d(Config.LOGTAG,
"found unexpected tag: " + nextTag.getName());
return; return;
} }
} }
@ -190,27 +192,39 @@ public class XmppConnection implements Runnable {
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
this.changeStatus(Account.STATUS_SERVER_NOT_FOUND); this.changeStatus(Account.STATUS_SERVER_NOT_FOUND);
if (wakeLock.isHeld()) { if (wakeLock.isHeld()) {
try { wakeLock.release();} catch (RuntimeException re) {} try {
wakeLock.release();
} catch (RuntimeException re) {
}
} }
return; return;
} catch (IOException e) { } catch (IOException e) {
this.changeStatus(Account.STATUS_OFFLINE); this.changeStatus(Account.STATUS_OFFLINE);
if (wakeLock.isHeld()) { if (wakeLock.isHeld()) {
try { wakeLock.release();} catch (RuntimeException re) {} try {
wakeLock.release();
} catch (RuntimeException re) {
}
} }
return; return;
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
this.changeStatus(Account.STATUS_OFFLINE); this.changeStatus(Account.STATUS_OFFLINE);
Log.d(LOGTAG, "compression exception " + e.getMessage()); Log.d(Config.LOGTAG, "compression exception " + e.getMessage());
if (wakeLock.isHeld()) { if (wakeLock.isHeld()) {
try { wakeLock.release();} catch (RuntimeException re) {} try {
wakeLock.release();
} catch (RuntimeException re) {
}
} }
return; return;
} catch (XmlPullParserException e) { } catch (XmlPullParserException e) {
this.changeStatus(Account.STATUS_OFFLINE); this.changeStatus(Account.STATUS_OFFLINE);
Log.d(LOGTAG, "xml exception " + e.getMessage()); Log.d(Config.LOGTAG, "xml exception " + e.getMessage());
if (wakeLock.isHeld()) { if (wakeLock.isHeld()) {
try { wakeLock.release();} catch (RuntimeException re) {} try {
wakeLock.release();
} catch (RuntimeException re) {
}
} }
return; return;
} }
@ -235,7 +249,7 @@ public class XmppConnection implements Runnable {
} else if (nextTag.isStart("compressed")) { } else if (nextTag.isStart("compressed")) {
switchOverToZLib(nextTag); switchOverToZLib(nextTag);
} else if (nextTag.isStart("success")) { } else if (nextTag.isStart("success")) {
Log.d(LOGTAG, account.getJid() + ": logged in"); Log.d(Config.LOGTAG, account.getJid() + ": logged in");
tagReader.readTag(); tagReader.readTag();
tagReader.reset(); tagReader.reset();
sendStartStream(); sendStartStream();
@ -250,17 +264,18 @@ public class XmppConnection implements Runnable {
response.setAttribute("xmlns", response.setAttribute("xmlns",
"urn:ietf:params:xml:ns:xmpp-sasl"); "urn:ietf:params:xml:ns:xmpp-sasl");
response.setContent(CryptoHelper.saslDigestMd5(account, response.setContent(CryptoHelper.saslDigestMd5(account,
challange,mRandom)); challange, mRandom));
tagWriter.writeElement(response); tagWriter.writeElement(response);
} else if (nextTag.isStart("enabled")) { } else if (nextTag.isStart("enabled")) {
Element enabled = tagReader.readElement(nextTag); Element enabled = tagReader.readElement(nextTag);
if ("true".equals(enabled.getAttribute("resume"))) { if ("true".equals(enabled.getAttribute("resume"))) {
this.streamId = enabled.getAttribute("id"); this.streamId = enabled.getAttribute("id");
Log.d(LOGTAG, account.getJid() + ": stream managment(" Log.d(Config.LOGTAG, account.getJid()
+ smVersion + ") enabled (resumable)"); + ": stream managment(" + smVersion
+ ") enabled (resumable)");
} else { } else {
Log.d(LOGTAG, account.getJid() + ": stream managment(" Log.d(Config.LOGTAG, account.getJid()
+ smVersion + ") enabled"); + ": stream managment(" + smVersion + ") enabled");
} }
this.lastSessionStarted = SystemClock.elapsedRealtime(); this.lastSessionStarted = SystemClock.elapsedRealtime();
this.stanzasReceived = 0; this.stanzasReceived = 0;
@ -272,22 +287,25 @@ public class XmppConnection implements Runnable {
String h = resumed.getAttribute("h"); String h = resumed.getAttribute("h");
try { try {
int serverCount = Integer.parseInt(h); int serverCount = Integer.parseInt(h);
if (serverCount!=stanzasSent) { if (serverCount != stanzasSent) {
Log.d(LOGTAG,account.getJid() + ": session resumed with lost packages"); Log.d(Config.LOGTAG, account.getJid()
+ ": session resumed with lost packages");
stanzasSent = serverCount; stanzasSent = serverCount;
} else { } else {
Log.d(LOGTAG, account.getJid() + ": session resumed"); Log.d(Config.LOGTAG, account.getJid()
+ ": session resumed");
} }
if (acknowledgedListener!=null) { if (acknowledgedListener != null) {
for(int i = 0; i < messageReceipts.size(); ++i) { for (int i = 0; i < messageReceipts.size(); ++i) {
if (serverCount>=messageReceipts.keyAt(i)) { if (serverCount >= messageReceipts.keyAt(i)) {
acknowledgedListener.onMessageAcknowledged(account, messageReceipts.valueAt(i)); acknowledgedListener.onMessageAcknowledged(
account, messageReceipts.valueAt(i));
} }
} }
} }
messageReceipts.clear(); messageReceipts.clear();
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
} }
changeStatus(Account.STATUS_ONLINE); changeStatus(Account.STATUS_ONLINE);
} else if (nextTag.isStart("r")) { } else if (nextTag.isStart("r")) {
@ -301,13 +319,14 @@ public class XmppConnection implements Runnable {
String msgId = this.messageReceipts.get(serverSequence); String msgId = this.messageReceipts.get(serverSequence);
if (msgId != null) { if (msgId != null) {
if (this.acknowledgedListener != null) { if (this.acknowledgedListener != null) {
this.acknowledgedListener.onMessageAcknowledged(account, msgId); this.acknowledgedListener.onMessageAcknowledged(
account, msgId);
} }
this.messageReceipts.remove(serverSequence); this.messageReceipts.remove(serverSequence);
} }
} else if (nextTag.isStart("failed")) { } else if (nextTag.isStart("failed")) {
tagReader.readElement(nextTag); tagReader.readElement(nextTag);
Log.d(LOGTAG, account.getJid() + ": resumption failed"); Log.d(Config.LOGTAG, account.getJid() + ": resumption failed");
streamId = null; streamId = null;
if (account.getStatus() != Account.STATUS_ONLINE) { if (account.getStatus() != Account.STATUS_ONLINE) {
sendBindRequest(); sendBindRequest();
@ -347,7 +366,7 @@ public class XmppConnection implements Runnable {
} }
element.setAttributes(currentTag.getAttributes()); element.setAttributes(currentTag.getAttributes());
Tag nextTag = tagReader.readTag(); Tag nextTag = tagReader.readTag();
if (nextTag==null) { if (nextTag == null) {
throw new IOException("interrupted mid tag"); throw new IOException("interrupted mid tag");
} }
while (!nextTag.isEnd(element.getName())) { while (!nextTag.isEnd(element.getName())) {
@ -361,7 +380,7 @@ public class XmppConnection implements Runnable {
element.addChild(child); element.addChild(child);
} }
nextTag = tagReader.readTag(); nextTag = tagReader.readTag();
if (nextTag==null) { if (nextTag == null) {
throw new IOException("interrupted mid tag"); throw new IOException("interrupted mid tag");
} }
} }
@ -446,7 +465,7 @@ public class XmppConnection implements Runnable {
.setInputStream(new ZLibInputStream(tagReader.getInputStream())); .setInputStream(new ZLibInputStream(tagReader.getInputStream()));
sendStartStream(); sendStartStream();
Log.d(LOGTAG, account.getJid() + ": compression enabled"); Log.d(Config.LOGTAG, account.getJid() + ": compression enabled");
usingCompression = true; usingCompression = true;
processStream(tagReader.readTag()); processStream(tagReader.readTag());
} }
@ -462,23 +481,30 @@ public class XmppConnection implements Runnable {
tagReader.readTag(); tagReader.readTag();
try { try {
SSLContext sc = SSLContext.getInstance("TLS"); SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, new X509TrustManager[] { this.mMemorizingTrustManager }, mRandom); sc.init(null,
new X509TrustManager[] { this.mMemorizingTrustManager },
mRandom);
SSLSocketFactory factory = sc.getSocketFactory(); SSLSocketFactory factory = sc.getSocketFactory();
HostnameVerifier verifier = this.mMemorizingTrustManager.wrapHostnameVerifier(new org.apache.http.conn.ssl.StrictHostnameVerifier()); HostnameVerifier verifier = this.mMemorizingTrustManager
.wrapHostnameVerifier(new org.apache.http.conn.ssl.StrictHostnameVerifier());
SSLSocket sslSocket = (SSLSocket) factory.createSocket(socket, SSLSocket sslSocket = (SSLSocket) factory.createSocket(socket,
socket.getInetAddress().getHostAddress(), socket.getPort(), socket.getInetAddress().getHostAddress(), socket.getPort(),
true); true);
if (verifier != null && !verifier.verify(account.getServer(), sslSocket.getSession())) { if (verifier != null
Log.d(LOGTAG, account.getJid() + ": host mismatch in TLS connection"); && !verifier.verify(account.getServer(),
sslSocket.getSession())) {
Log.d(Config.LOGTAG, account.getJid()
+ ": host mismatch in TLS connection");
sslSocket.close(); sslSocket.close();
throw new IOException(); throw new IOException();
} }
tagReader.setInputStream(sslSocket.getInputStream()); tagReader.setInputStream(sslSocket.getInputStream());
tagWriter.setOutputStream(sslSocket.getOutputStream()); tagWriter.setOutputStream(sslSocket.getOutputStream());
sendStartStream(); sendStartStream();
Log.d(LOGTAG, account.getJid() + ": TLS connection established"); Log.d(Config.LOGTAG, account.getJid()
+ ": TLS connection established");
processStream(tagReader.readTag()); processStream(tagReader.readTag());
sslSocket.close(); sslSocket.close();
} catch (NoSuchAlgorithmException e1) { } catch (NoSuchAlgorithmException e1) {
@ -604,7 +630,7 @@ public class XmppConnection implements Runnable {
changeStatus(Account.STATUS_REGISTRATION_CONFLICT); changeStatus(Account.STATUS_REGISTRATION_CONFLICT);
} else { } else {
changeStatus(Account.STATUS_REGISTRATION_FAILED); changeStatus(Account.STATUS_REGISTRATION_FAILED);
Log.d(LOGTAG, packet.toString()); Log.d(Config.LOGTAG, packet.toString());
} }
disconnect(true); disconnect(true);
} }
@ -612,7 +638,7 @@ public class XmppConnection implements Runnable {
} else { } else {
changeStatus(Account.STATUS_REGISTRATION_FAILED); changeStatus(Account.STATUS_REGISTRATION_FAILED);
disconnect(true); disconnect(true);
Log.d(LOGTAG, account.getJid() Log.d(Config.LOGTAG, account.getJid()
+ ": could not register. instructions are" + ": could not register. instructions are"
+ instructions.getContent()); + instructions.getContent());
} }
@ -628,9 +654,9 @@ public class XmppConnection implements Runnable {
@Override @Override
public void onIqPacketReceived(Account account, IqPacket packet) { public void onIqPacketReceived(Account account, IqPacket packet) {
Element bind = packet.findChild("bind"); Element bind = packet.findChild("bind");
if (bind!=null) { if (bind != null) {
Element jid = bind.findChild("jid"); Element jid = bind.findChild("jid");
if (jid!=null) { if (jid != null) {
account.setResource(jid.getContent().split("/")[1]); account.setResource(jid.getContent().split("/")[1]);
if (streamFeatures.hasChild("sm", "urn:xmpp:sm:3")) { if (streamFeatures.hasChild("sm", "urn:xmpp:sm:3")) {
smVersion = 3; smVersion = 3;
@ -638,7 +664,8 @@ public class XmppConnection implements Runnable {
tagWriter.writeStanzaAsync(enable); tagWriter.writeStanzaAsync(enable);
stanzasSent = 0; stanzasSent = 0;
messageReceipts.clear(); messageReceipts.clear();
} else if (streamFeatures.hasChild("sm", "urn:xmpp:sm:2")) { } else if (streamFeatures.hasChild("sm",
"urn:xmpp:sm:2")) {
smVersion = 2; smVersion = 2;
EnablePacket enable = new EnablePacket(smVersion); EnablePacket enable = new EnablePacket(smVersion);
tagWriter.writeStanzaAsync(enable); tagWriter.writeStanzaAsync(enable);
@ -660,7 +687,8 @@ public class XmppConnection implements Runnable {
} }
}); });
if (this.streamFeatures.hasChild("session")) { if (this.streamFeatures.hasChild("session")) {
Log.d(LOGTAG, account.getJid() + ": sending deprecated session"); Log.d(Config.LOGTAG, account.getJid()
+ ": sending deprecated session");
IqPacket startSession = new IqPacket(IqPacket.TYPE_SET); IqPacket startSession = new IqPacket(IqPacket.TYPE_SET);
startSession.addChild("session", startSession.addChild("session",
"urn:ietf:params:xml:ns:xmpp-session"); "urn:ietf:params:xml:ns:xmpp-session");
@ -725,10 +753,10 @@ public class XmppConnection implements Runnable {
@Override @Override
public void onIqPacketReceived(Account account, IqPacket packet) { public void onIqPacketReceived(Account account, IqPacket packet) {
if (!packet.hasChild("error")) { if (!packet.hasChild("error")) {
Log.d(LOGTAG, account.getJid() Log.d(Config.LOGTAG, account.getJid()
+ ": successfully enabled carbons"); + ": successfully enabled carbons");
} else { } else {
Log.d(LOGTAG, account.getJid() Log.d(Config.LOGTAG, account.getJid()
+ ": error enableing carbons " + packet.toString()); + ": error enableing carbons " + packet.toString());
} }
} }
@ -736,7 +764,7 @@ public class XmppConnection implements Runnable {
} }
private void processStreamError(Tag currentTag) { private void processStreamError(Tag currentTag) {
Log.d(LOGTAG, "processStreamError"); Log.d(Config.LOGTAG, "processStreamError");
} }
private void sendStartStream() throws IOException { private void sendStartStream() throws IOException {
@ -778,15 +806,18 @@ public class XmppConnection implements Runnable {
public void sendPresencePacket(PresencePacket packet) { public void sendPresencePacket(PresencePacket packet) {
this.sendPacket(packet, null); this.sendPacket(packet, null);
} }
private synchronized void sendPacket(final AbstractStanza packet, private synchronized void sendPacket(final AbstractStanza packet,
PacketReceived callback) { PacketReceived callback) {
if (packet.getName().equals("iq") || packet.getName().equals("message") || packet.getName().equals("presence")) { if (packet.getName().equals("iq") || packet.getName().equals("message")
|| packet.getName().equals("presence")) {
++stanzasSent; ++stanzasSent;
} }
tagWriter.writeStanzaAsync(packet); tagWriter.writeStanzaAsync(packet);
if (packet instanceof MessagePacket && packet.getId() != null && this.streamId != null) { if (packet instanceof MessagePacket && packet.getId() != null
Log.d(LOGTAG,"request delivery report for stanza "+stanzasSent); && this.streamId != null) {
Log.d(Config.LOGTAG, "request delivery report for stanza "
+ stanzasSent);
this.messageReceipts.put(stanzasSent, packet.getId()); this.messageReceipts.put(stanzasSent, packet.getId());
tagWriter.writeStanzaAsync(new RequestPacket(this.smVersion)); tagWriter.writeStanzaAsync(new RequestPacket(this.smVersion));
} }
@ -837,14 +868,14 @@ public class XmppConnection implements Runnable {
public void setOnBindListener(OnBindListener listener) { public void setOnBindListener(OnBindListener listener) {
this.bindListener = listener; this.bindListener = listener;
} }
public void setOnMessageAcknowledgeListener(OnMessageAcknowledged listener) { public void setOnMessageAcknowledgeListener(OnMessageAcknowledged listener) {
this.acknowledgedListener = listener; this.acknowledgedListener = listener;
} }
public void disconnect(boolean force) { public void disconnect(boolean force) {
changeStatus(Account.STATUS_OFFLINE); changeStatus(Account.STATUS_OFFLINE);
Log.d(LOGTAG, "disconnecting"); Log.d(Config.LOGTAG, "disconnecting");
try { try {
if (force) { if (force) {
socket.close(); socket.close();
@ -858,20 +889,21 @@ public class XmppConnection implements Runnable {
tagWriter.finish(); tagWriter.finish();
try { try {
while (!tagWriter.finished()) { while (!tagWriter.finished()) {
Log.d(LOGTAG, "not yet finished"); Log.d(Config.LOGTAG, "not yet finished");
Thread.sleep(100); Thread.sleep(100);
} }
tagWriter.writeTag(Tag.end("stream:stream")); tagWriter.writeTag(Tag.end("stream:stream"));
} catch (IOException e) { } catch (IOException e) {
Log.d(LOGTAG, "io exception during disconnect"); Log.d(Config.LOGTAG,
"io exception during disconnect");
} catch (InterruptedException e) { } catch (InterruptedException e) {
Log.d(LOGTAG, "interrupted"); Log.d(Config.LOGTAG, "interrupted");
} }
} }
} }
}).start(); }).start();
} catch (IOException e) { } catch (IOException e) {
Log.d(LOGTAG, "io exception during disconnect"); Log.d(Config.LOGTAG, "io exception during disconnect");
} }
} }
@ -884,10 +916,10 @@ public class XmppConnection implements Runnable {
} }
return items; return items;
} }
public String findDiscoItemByFeature(String feature) { public String findDiscoItemByFeature(String feature) {
List<String> items = findDiscoItemsByFeature(feature); List<String> items = findDiscoItemsByFeature(feature);
if (items.size()>=1) { if (items.size() >= 1) {
return items.get(0); return items.get(0);
} }
return null; return null;
@ -896,7 +928,7 @@ public class XmppConnection implements Runnable {
public void r() { public void r() {
this.tagWriter.writeStanzaAsync(new RequestPacket(smVersion)); this.tagWriter.writeStanzaAsync(new RequestPacket(smVersion));
} }
public String getMucServer() { public String getMucServer() {
return findDiscoItemByFeature("http://jabber.org/protocol/muc"); return findDiscoItemByFeature("http://jabber.org/protocol/muc");
} }
@ -910,28 +942,29 @@ public class XmppConnection implements Runnable {
public int getAttempt() { public int getAttempt() {
return this.attempt; return this.attempt;
} }
public Features getFeatures() { public Features getFeatures() {
return this.features; return this.features;
} }
public class Features { public class Features {
XmppConnection connection; XmppConnection connection;
public Features(XmppConnection connection) { public Features(XmppConnection connection) {
this.connection = connection; this.connection = connection;
} }
private boolean hasDiscoFeature(String server, String feature) { private boolean hasDiscoFeature(String server, String feature) {
if (!connection.disco.containsKey(server)) { if (!connection.disco.containsKey(server)) {
return false; return false;
} }
return connection.disco.get(server).contains(feature); return connection.disco.get(server).contains(feature);
} }
public boolean carbons() { public boolean carbons() {
return hasDiscoFeature(account.getServer(), "urn:xmpp:carbons:2"); return hasDiscoFeature(account.getServer(), "urn:xmpp:carbons:2");
} }
public boolean sm() { public boolean sm() {
if (connection.streamFeatures == null) { if (connection.streamFeatures == null) {
return false; return false;
@ -939,19 +972,21 @@ public class XmppConnection implements Runnable {
return connection.streamFeatures.hasChild("sm"); return connection.streamFeatures.hasChild("sm");
} }
} }
public boolean csi() { public boolean csi() {
if (connection.streamFeatures == null) { if (connection.streamFeatures == null) {
return false; return false;
} else { } else {
return connection.streamFeatures.hasChild("csi","urn:xmpp:csi:0"); return connection.streamFeatures.hasChild("csi",
"urn:xmpp:csi:0");
} }
} }
public boolean pubsub() { public boolean pubsub() {
return hasDiscoFeature(account.getServer(), "http://jabber.org/protocol/pubsub#publish"); return hasDiscoFeature(account.getServer(),
"http://jabber.org/protocol/pubsub#publish");
} }
public boolean rosterVersioning() { public boolean rosterVersioning() {
if (connection.streamFeatures == null) { if (connection.streamFeatures == null) {
return false; return false;
@ -959,11 +994,12 @@ public class XmppConnection implements Runnable {
return connection.streamFeatures.hasChild("ver"); return connection.streamFeatures.hasChild("ver");
} }
} }
public boolean streamhost() { public boolean streamhost() {
return connection.findDiscoItemByFeature("http://jabber.org/protocol/bytestreams") != null; return connection
.findDiscoItemByFeature("http://jabber.org/protocol/bytestreams") != null;
} }
public boolean compression() { public boolean compression() {
return connection.usingCompression; return connection.usingCompression;
} }
@ -978,15 +1014,15 @@ public class XmppConnection implements Runnable {
} }
return System.currentTimeMillis() - diff; return System.currentTimeMillis() - diff;
} }
public long getLastConnect() { public long getLastConnect() {
return this.lastConnect; return this.lastConnect;
} }
public long getLastPingSent() { public long getLastPingSent() {
return this.lastPingSent; return this.lastPingSent;
} }
public long getLastPacketReceived() { public long getLastPacketReceived() {
return this.lastPaketReceived; return this.lastPaketReceived;
} }
@ -994,7 +1030,7 @@ public class XmppConnection implements Runnable {
public void sendActive() { public void sendActive() {
this.sendPacket(new ActivePacket(), null); this.sendPacket(new ActivePacket(), null);
} }
public void sendInactive() { public void sendInactive() {
this.sendPacket(new InactivePacket(), null); this.sendPacket(new InactivePacket(), null);
} }

View file

@ -6,11 +6,11 @@ import java.util.List;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
public class JingleCandidate { public class JingleCandidate {
public static int TYPE_UNKNOWN; public static int TYPE_UNKNOWN;
public static int TYPE_DIRECT = 0; public static int TYPE_DIRECT = 0;
public static int TYPE_PROXY = 1; public static int TYPE_PROXY = 1;
private boolean ours; private boolean ours;
private boolean usedByCounterpart = false; private boolean usedByCounterpart = false;
private String cid; private String cid;
@ -19,12 +19,12 @@ public class JingleCandidate {
private int type; private int type;
private String jid; private String jid;
private int priority; private int priority;
public JingleCandidate(String cid,boolean ours) { public JingleCandidate(String cid, boolean ours) {
this.ours = ours; this.ours = ours;
this.cid = cid; this.cid = cid;
} }
public String getCid() { public String getCid() {
return cid; return cid;
} }
@ -32,15 +32,15 @@ public class JingleCandidate {
public void setHost(String host) { public void setHost(String host) {
this.host = host; this.host = host;
} }
public String getHost() { public String getHost() {
return this.host; return this.host;
} }
public void setJid(String jid) { public void setJid(String jid) {
this.jid = jid; this.jid = jid;
} }
public String getJid() { public String getJid() {
return this.jid; return this.jid;
} }
@ -48,15 +48,15 @@ public class JingleCandidate {
public void setPort(int port) { public void setPort(int port) {
this.port = port; this.port = port;
} }
public int getPort() { public int getPort() {
return this.port; return this.port;
} }
public void setType(int type) { public void setType(int type) {
this.type = type; this.type = type;
} }
public void setType(String type) { public void setType(String type) {
if ("proxy".equals(type)) { if ("proxy".equals(type)) {
this.type = TYPE_PROXY; this.type = TYPE_PROXY;
@ -70,42 +70,46 @@ public class JingleCandidate {
public void setPriority(int i) { public void setPriority(int i) {
this.priority = i; this.priority = i;
} }
public int getPriority() { public int getPriority() {
return this.priority; return this.priority;
} }
public boolean equals(JingleCandidate other) { public boolean equals(JingleCandidate other) {
return this.getCid().equals(other.getCid()); return this.getCid().equals(other.getCid());
} }
public boolean equalValues(JingleCandidate other) { public boolean equalValues(JingleCandidate other) {
return other.getHost().equals(this.getHost())&&(other.getPort()==this.getPort()); return other.getHost().equals(this.getHost())
&& (other.getPort() == this.getPort());
} }
public boolean isOurs() { public boolean isOurs() {
return ours; return ours;
} }
public int getType() { public int getType() {
return this.type; return this.type;
} }
public static List<JingleCandidate> parse(List<Element> canditates) { public static List<JingleCandidate> parse(List<Element> canditates) {
List<JingleCandidate> parsedCandidates = new ArrayList<JingleCandidate>(); List<JingleCandidate> parsedCandidates = new ArrayList<JingleCandidate>();
for(Element c : canditates) { for (Element c : canditates) {
parsedCandidates.add(JingleCandidate.parse(c)); parsedCandidates.add(JingleCandidate.parse(c));
} }
return parsedCandidates; return parsedCandidates;
} }
public static JingleCandidate parse(Element candidate) { public static JingleCandidate parse(Element candidate) {
JingleCandidate parsedCandidate = new JingleCandidate(candidate.getAttribute("cid"), false); JingleCandidate parsedCandidate = new JingleCandidate(
candidate.getAttribute("cid"), false);
parsedCandidate.setHost(candidate.getAttribute("host")); parsedCandidate.setHost(candidate.getAttribute("host"));
parsedCandidate.setJid(candidate.getAttribute("jid")); parsedCandidate.setJid(candidate.getAttribute("jid"));
parsedCandidate.setType(candidate.getAttribute("type")); parsedCandidate.setType(candidate.getAttribute("type"));
parsedCandidate.setPriority(Integer.parseInt(candidate.getAttribute("priority"))); parsedCandidate.setPriority(Integer.parseInt(candidate
parsedCandidate.setPort(Integer.parseInt(candidate.getAttribute("port"))); .getAttribute("priority")));
parsedCandidate
.setPort(Integer.parseInt(candidate.getAttribute("port")));
return parsedCandidate; return parsedCandidate;
} }
@ -113,26 +117,27 @@ public class JingleCandidate {
Element element = new Element("candidate"); Element element = new Element("candidate");
element.setAttribute("cid", this.getCid()); element.setAttribute("cid", this.getCid());
element.setAttribute("host", this.getHost()); element.setAttribute("host", this.getHost());
element.setAttribute("port", ""+this.getPort()); element.setAttribute("port", "" + this.getPort());
element.setAttribute("jid", this.getJid()); element.setAttribute("jid", this.getJid());
element.setAttribute("priority",""+this.getPriority()); element.setAttribute("priority", "" + this.getPriority());
if (this.getType()==TYPE_DIRECT) { if (this.getType() == TYPE_DIRECT) {
element.setAttribute("type","direct"); element.setAttribute("type", "direct");
} else if (this.getType()==TYPE_PROXY) { } else if (this.getType() == TYPE_PROXY) {
element.setAttribute("type","proxy"); element.setAttribute("type", "proxy");
} }
return element; return element;
} }
public void flagAsUsedByCounterpart() { public void flagAsUsedByCounterpart() {
this.usedByCounterpart = true; this.usedByCounterpart = true;
} }
public boolean isUsedByCounterpart() { public boolean isUsedByCounterpart() {
return this.usedByCounterpart; return this.usedByCounterpart;
} }
public String toString() { public String toString() {
return this.getHost()+":"+this.getPort()+" (prio="+this.getPriority()+")"; return this.getHost() + ":" + this.getPort() + " (prio="
+ this.getPriority() + ")";
} }
} }

View file

@ -12,6 +12,7 @@ import android.content.Intent;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.net.Uri; import android.net.Uri;
import android.util.Log; import android.util.Log;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
@ -100,10 +101,11 @@ public class JingleConnection {
mXmppConnectionService.markMessage(message, mXmppConnectionService.markMessage(message,
Message.STATUS_RECEIVED); Message.STATUS_RECEIVED);
} }
Log.d("xmppService", Log.d(Config.LOGTAG,
"sucessfully transmitted file:" + file.getAbsolutePath()); "sucessfully transmitted file:" + file.getAbsolutePath());
if (message.getEncryption()!=Message.ENCRYPTION_PGP) { if (message.getEncryption() != Message.ENCRYPTION_PGP) {
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); Intent intent = new Intent(
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(file)); intent.setData(Uri.fromFile(file));
mXmppConnectionService.sendBroadcast(intent); mXmppConnectionService.sendBroadcast(intent);
} }
@ -121,17 +123,17 @@ public class JingleConnection {
@Override @Override
public void success() { public void success() {
if (initiator.equals(account.getFullJid())) { if (initiator.equals(account.getFullJid())) {
Log.d("xmppService", "we were initiating. sending file"); Log.d(Config.LOGTAG, "we were initiating. sending file");
transport.send(file, onFileTransmissionSatusChanged); transport.send(file, onFileTransmissionSatusChanged);
} else { } else {
transport.receive(file, onFileTransmissionSatusChanged); transport.receive(file, onFileTransmissionSatusChanged);
Log.d("xmppService", "we were responding. receiving file"); Log.d(Config.LOGTAG, "we were responding. receiving file");
} }
} }
@Override @Override
public void failed() { public void failed() {
Log.d("xmppService", "proxy activation failed"); Log.d(Config.LOGTAG, "proxy activation failed");
} }
}; };
@ -177,13 +179,13 @@ public class JingleConnection {
returnResult = this.receiveFallbackToIbb(packet); returnResult = this.receiveFallbackToIbb(packet);
} else { } else {
returnResult = false; returnResult = false;
Log.d("xmppService", "trying to fallback to something unknown" Log.d(Config.LOGTAG, "trying to fallback to something unknown"
+ packet.toString()); + packet.toString());
} }
} else if (packet.isAction("transport-accept")) { } else if (packet.isAction("transport-accept")) {
returnResult = this.receiveTransportAccept(packet); returnResult = this.receiveTransportAccept(packet);
} else { } else {
Log.d("xmppService", "packet arrived in connection. action was " Log.d(Config.LOGTAG, "packet arrived in connection. action was "
+ packet.getAction()); + packet.getAction());
returnResult = false; returnResult = false;
} }
@ -224,14 +226,14 @@ public class JingleConnection {
@Override @Override
public void failed() { public void failed() {
Log.d("xmppService", Log.d(Config.LOGTAG,
"connection to our own primary candidete failed"); "connection to our own primary candidete failed");
sendInitRequest(); sendInitRequest();
} }
@Override @Override
public void established() { public void established() {
Log.d("xmppService", Log.d(Config.LOGTAG,
"succesfully connected to our own primary candidate"); "succesfully connected to our own primary candidate");
mergeCandidate(candidate); mergeCandidate(candidate);
sendInitRequest(); sendInitRequest();
@ -239,7 +241,7 @@ public class JingleConnection {
}); });
mergeCandidate(candidate); mergeCandidate(candidate);
} else { } else {
Log.d("xmppService", Log.d(Config.LOGTAG,
"no primary candidate of our own was found"); "no primary candidate of our own was found");
sendInitRequest(); sendInitRequest();
} }
@ -288,7 +290,7 @@ public class JingleConnection {
filename[filename.length - 2])) { filename[filename.length - 2])) {
supportedFile = true; supportedFile = true;
if (filename[filename.length - 1].equals("otr")) { if (filename[filename.length - 1].equals("otr")) {
Log.d("xmppService", "receiving otr file"); Log.d(Config.LOGTAG, "receiving otr file");
this.message this.message
.setEncryption(Message.ENCRYPTION_OTR); .setEncryption(Message.ENCRYPTION_OTR);
} else { } else {
@ -304,13 +306,13 @@ public class JingleConnection {
conversation.getMessages().add(message); conversation.getMessages().add(message);
if (size <= this.mJingleConnectionManager if (size <= this.mJingleConnectionManager
.getAutoAcceptFileSize()) { .getAutoAcceptFileSize()) {
Log.d("xmppService", "auto accepting file from " Log.d(Config.LOGTAG, "auto accepting file from "
+ packet.getFrom()); + packet.getFrom());
this.acceptedAutomatically = true; this.acceptedAutomatically = true;
this.sendAccept(); this.sendAccept();
} else { } else {
message.markUnread(); message.markUnread();
Log.d("xmppService", Log.d(Config.LOGTAG,
"not auto accepting new file offer with size: " "not auto accepting new file offer with size: "
+ size + size
+ " allowed size:" + " allowed size:"
@ -400,7 +402,7 @@ public class JingleConnection {
@Override @Override
public void failed() { public void failed() {
Log.d("xmppService", Log.d(Config.LOGTAG,
"connection to our own primary candidate failed"); "connection to our own primary candidate failed");
content.socks5transport().setChildren( content.socks5transport().setChildren(
getCandidatesAsElements()); getCandidatesAsElements());
@ -411,7 +413,7 @@ public class JingleConnection {
@Override @Override
public void established() { public void established() {
Log.d("xmppService", Log.d(Config.LOGTAG,
"connected to primary candidate"); "connected to primary candidate");
mergeCandidate(candidate); mergeCandidate(candidate);
content.socks5transport().setChildren( content.socks5transport().setChildren(
@ -422,7 +424,7 @@ public class JingleConnection {
} }
}); });
} else { } else {
Log.d("xmppService", Log.d(Config.LOGTAG,
"did not find a primary candidate for ourself"); "did not find a primary candidate for ourself");
content.socks5transport().setChildren( content.socks5transport().setChildren(
getCandidatesAsElements()); getCandidatesAsElements());
@ -446,7 +448,7 @@ public class JingleConnection {
} }
private void sendJinglePacket(JinglePacket packet) { private void sendJinglePacket(JinglePacket packet) {
// Log.d("xmppService",packet.toString()); // Log.d(Config.LOGTAG,packet.toString());
account.getXmppConnection().sendIqPacket(packet, responseListener); account.getXmppConnection().sendIqPacket(packet, responseListener);
} }
@ -470,14 +472,14 @@ public class JingleConnection {
} else { } else {
String cid = content.socks5transport() String cid = content.socks5transport()
.findChild("activated").getAttribute("cid"); .findChild("activated").getAttribute("cid");
Log.d("xmppService", "received proxy activated (" + cid Log.d(Config.LOGTAG, "received proxy activated (" + cid
+ ")prior to choosing our own transport"); + ")prior to choosing our own transport");
JingleSocks5Transport connection = this.connections JingleSocks5Transport connection = this.connections
.get(cid); .get(cid);
if (connection != null) { if (connection != null) {
connection.setActivated(true); connection.setActivated(true);
} else { } else {
Log.d("xmppService", "activated connection not found"); Log.d(Config.LOGTAG, "activated connection not found");
this.sendCancel(); this.sendCancel();
this.cancel(); this.cancel();
} }
@ -487,7 +489,7 @@ public class JingleConnection {
onProxyActivated.failed(); onProxyActivated.failed();
return true; return true;
} else if (content.socks5transport().hasChild("candidate-error")) { } else if (content.socks5transport().hasChild("candidate-error")) {
Log.d("xmppService", "received candidate error"); Log.d(Config.LOGTAG, "received candidate error");
this.receivedCandidate = true; this.receivedCandidate = true;
if ((status == STATUS_ACCEPTED) && (this.sentCandidate)) { if ((status == STATUS_ACCEPTED) && (this.sentCandidate)) {
this.connect(); this.connect();
@ -497,14 +499,14 @@ public class JingleConnection {
String cid = content.socks5transport() String cid = content.socks5transport()
.findChild("candidate-used").getAttribute("cid"); .findChild("candidate-used").getAttribute("cid");
if (cid != null) { if (cid != null) {
Log.d("xmppService", "candidate used by counterpart:" + cid); Log.d(Config.LOGTAG, "candidate used by counterpart:" + cid);
JingleCandidate candidate = getCandidate(cid); JingleCandidate candidate = getCandidate(cid);
candidate.flagAsUsedByCounterpart(); candidate.flagAsUsedByCounterpart();
this.receivedCandidate = true; this.receivedCandidate = true;
if ((status == STATUS_ACCEPTED) && (this.sentCandidate)) { if ((status == STATUS_ACCEPTED) && (this.sentCandidate)) {
this.connect(); this.connect();
} else { } else {
Log.d("xmppService", Log.d(Config.LOGTAG,
"ignoring because file is already in transmission or we havent sent our candidate yet"); "ignoring because file is already in transmission or we havent sent our candidate yet");
} }
return true; return true;
@ -523,7 +525,7 @@ public class JingleConnection {
final JingleSocks5Transport connection = chooseConnection(); final JingleSocks5Transport connection = chooseConnection();
this.transport = connection; this.transport = connection;
if (connection == null) { if (connection == null) {
Log.d("xmppService", "could not find suitable candidate"); Log.d(Config.LOGTAG, "could not find suitable candidate");
this.disconnect(); this.disconnect();
if (this.initiator.equals(account.getFullJid())) { if (this.initiator.equals(account.getFullJid())) {
this.sendFallbackToIbb(); this.sendFallbackToIbb();
@ -532,7 +534,7 @@ public class JingleConnection {
this.status = STATUS_TRANSMITTING; this.status = STATUS_TRANSMITTING;
if (connection.needsActivation()) { if (connection.needsActivation()) {
if (connection.getCandidate().isOurs()) { if (connection.getCandidate().isOurs()) {
Log.d("xmppService", "candidate " Log.d(Config.LOGTAG, "candidate "
+ connection.getCandidate().getCid() + connection.getCandidate().getCid()
+ " was our proxy. going to activate"); + " was our proxy. going to activate");
IqPacket activation = new IqPacket(IqPacket.TYPE_SET); IqPacket activation = new IqPacket(IqPacket.TYPE_SET);
@ -557,17 +559,17 @@ public class JingleConnection {
} }
}); });
} else { } else {
Log.d("xmppService", Log.d(Config.LOGTAG,
"candidate " "candidate "
+ connection.getCandidate().getCid() + connection.getCandidate().getCid()
+ " was a proxy. waiting for other party to activate"); + " was a proxy. waiting for other party to activate");
} }
} else { } else {
if (initiator.equals(account.getFullJid())) { if (initiator.equals(account.getFullJid())) {
Log.d("xmppService", "we were initiating. sending file"); Log.d(Config.LOGTAG, "we were initiating. sending file");
connection.send(file, onFileTransmissionSatusChanged); connection.send(file, onFileTransmissionSatusChanged);
} else { } else {
Log.d("xmppService", "we were responding. receiving file"); Log.d(Config.LOGTAG, "we were responding. receiving file");
connection.receive(file, onFileTransmissionSatusChanged); connection.receive(file, onFileTransmissionSatusChanged);
} }
} }
@ -579,11 +581,11 @@ public class JingleConnection {
for (Entry<String, JingleSocks5Transport> cursor : connections for (Entry<String, JingleSocks5Transport> cursor : connections
.entrySet()) { .entrySet()) {
JingleSocks5Transport currentConnection = cursor.getValue(); JingleSocks5Transport currentConnection = cursor.getValue();
// Log.d("xmppService","comparing candidate: "+currentConnection.getCandidate().toString()); // Log.d(Config.LOGTAG,"comparing candidate: "+currentConnection.getCandidate().toString());
if (currentConnection.isEstablished() if (currentConnection.isEstablished()
&& (currentConnection.getCandidate().isUsedByCounterpart() || (!currentConnection && (currentConnection.getCandidate().isUsedByCounterpart() || (!currentConnection
.getCandidate().isOurs()))) { .getCandidate().isOurs()))) {
// Log.d("xmppService","is usable"); // Log.d(Config.LOGTAG,"is usable");
if (connection == null) { if (connection == null) {
connection = currentConnection; connection = currentConnection;
} else { } else {
@ -592,7 +594,7 @@ public class JingleConnection {
connection = currentConnection; connection = currentConnection;
} else if (connection.getCandidate().getPriority() == currentConnection } else if (connection.getCandidate().getPriority() == currentConnection
.getCandidate().getPriority()) { .getCandidate().getPriority()) {
// Log.d("xmppService","found two candidates with same priority"); // Log.d(Config.LOGTAG,"found two candidates with same priority");
if (initiator.equals(account.getFullJid())) { if (initiator.equals(account.getFullJid())) {
if (currentConnection.getCandidate().isOurs()) { if (currentConnection.getCandidate().isOurs()) {
connection = currentConnection; connection = currentConnection;
@ -672,7 +674,7 @@ public class JingleConnection {
@Override @Override
public void failed() { public void failed() {
Log.d("xmppService", "ibb open failed"); Log.d(Config.LOGTAG, "ibb open failed");
} }
@Override @Override
@ -742,7 +744,7 @@ public class JingleConnection {
@Override @Override
public void failed() { public void failed() {
Log.d("xmppService", Log.d(Config.LOGTAG,
"connection failed with " + candidate.getHost() + ":" "connection failed with " + candidate.getHost() + ":"
+ candidate.getPort()); + candidate.getPort());
connectNextCandidate(); connectNextCandidate();
@ -750,7 +752,7 @@ public class JingleConnection {
@Override @Override
public void established() { public void established() {
Log.d("xmppService", Log.d(Config.LOGTAG,
"established connection with " + candidate.getHost() "established connection with " + candidate.getHost()
+ ":" + candidate.getPort()); + ":" + candidate.getPort());
sendCandidateUsed(candidate.getCid()); sendCandidateUsed(candidate.getCid());
@ -874,7 +876,7 @@ public class JingleConnection {
} }
}).start(); }).start();
} else { } else {
Log.d("xmppService", "status (" + status + ") was not ok"); Log.d(Config.LOGTAG, "status (" + status + ") was not ok");
} }
} }
} }

View file

@ -7,6 +7,7 @@ import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.util.Log; import android.util.Log;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
@ -33,18 +34,20 @@ public class JingleConnectionManager {
public void deliverPacket(Account account, JinglePacket packet) { public void deliverPacket(Account account, JinglePacket packet) {
if (packet.isAction("session-initiate")) { if (packet.isAction("session-initiate")) {
JingleConnection connection = new JingleConnection(this); JingleConnection connection = new JingleConnection(this);
connection.init(account,packet); connection.init(account, packet);
connections.add(connection); connections.add(connection);
} else { } else {
for (JingleConnection connection : connections) { for (JingleConnection connection : connections) {
if (connection.getAccountJid().equals(account.getFullJid()) && connection if (connection.getAccountJid().equals(account.getFullJid())
.getSessionId().equals(packet.getSessionId()) && connection && connection.getSessionId().equals(
.getCounterPart().equals(packet.getFrom())) { packet.getSessionId())
&& connection.getCounterPart().equals(packet.getFrom())) {
connection.deliverPacket(packet); connection.deliverPacket(packet);
return; return;
} }
} }
account.getXmppConnection().sendIqPacket(packet.generateRespone(IqPacket.TYPE_ERROR), null); account.getXmppConnection().sendIqPacket(
packet.generateRespone(IqPacket.TYPE_ERROR), null);
} }
} }
@ -60,7 +63,7 @@ public class JingleConnectionManager {
this.connections.add(connection); this.connections.add(connection);
return connection; return connection;
} }
public void finishConnection(JingleConnection connection) { public void finishConnection(JingleConnection connection) {
this.connections.remove(connection); this.connections.remove(connection);
} }
@ -90,12 +93,17 @@ public class JingleConnectionManager {
.findChild("streamhost", .findChild("streamhost",
"http://jabber.org/protocol/bytestreams"); "http://jabber.org/protocol/bytestreams");
if (streamhost != null) { if (streamhost != null) {
JingleCandidate candidate = new JingleCandidate(nextRandomId(),true); JingleCandidate candidate = new JingleCandidate(
candidate.setHost(streamhost.getAttribute("host")); nextRandomId(), true);
candidate.setPort(Integer.parseInt(streamhost.getAttribute("port"))); candidate.setHost(streamhost
candidate.setType(JingleCandidate.TYPE_PROXY); .getAttribute("host"));
candidate.setPort(Integer
.parseInt(streamhost
.getAttribute("port")));
candidate
.setType(JingleCandidate.TYPE_PROXY);
candidate.setJid(proxy); candidate.setJid(proxy);
candidate.setPriority(655360+65535); candidate.setPriority(655360 + 65535);
primaryCandidates.put(account.getJid(), primaryCandidates.put(account.getJid(),
candidate); candidate);
listener.onPrimaryCandidateFound(true, listener.onPrimaryCandidateFound(true,
@ -119,9 +127,10 @@ public class JingleConnectionManager {
public String nextRandomId() { public String nextRandomId() {
return new BigInteger(50, random).toString(32); return new BigInteger(50, random).toString(32);
} }
public long getAutoAcceptFileSize() { public long getAutoAcceptFileSize() {
String config = this.xmppConnectionService.getPreferences().getString("auto_accept_file_size", "524288"); String config = this.xmppConnectionService.getPreferences().getString(
"auto_accept_file_size", "524288");
try { try {
return Long.parseLong(config); return Long.parseLong(config);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
@ -132,32 +141,35 @@ public class JingleConnectionManager {
public void deliverIbbPacket(Account account, IqPacket packet) { public void deliverIbbPacket(Account account, IqPacket packet) {
String sid = null; String sid = null;
Element payload = null; Element payload = null;
if (packet.hasChild("open","http://jabber.org/protocol/ibb")) { if (packet.hasChild("open", "http://jabber.org/protocol/ibb")) {
payload = packet.findChild("open","http://jabber.org/protocol/ibb"); payload = packet
.findChild("open", "http://jabber.org/protocol/ibb");
sid = payload.getAttribute("sid"); sid = payload.getAttribute("sid");
} else if (packet.hasChild("data","http://jabber.org/protocol/ibb")) { } else if (packet.hasChild("data", "http://jabber.org/protocol/ibb")) {
payload = packet.findChild("data","http://jabber.org/protocol/ibb"); payload = packet
.findChild("data", "http://jabber.org/protocol/ibb");
sid = payload.getAttribute("sid"); sid = payload.getAttribute("sid");
} }
if (sid!=null) { if (sid != null) {
for (JingleConnection connection : connections) { for (JingleConnection connection : connections) {
if (connection.hasTransportId(sid)) { if (connection.hasTransportId(sid)) {
JingleTransport transport = connection.getTransport(); JingleTransport transport = connection.getTransport();
if (transport instanceof JingleInbandTransport) { if (transport instanceof JingleInbandTransport) {
JingleInbandTransport inbandTransport = (JingleInbandTransport) transport; JingleInbandTransport inbandTransport = (JingleInbandTransport) transport;
inbandTransport.deliverPayload(packet,payload); inbandTransport.deliverPayload(packet, payload);
return; return;
} }
} }
} }
Log.d("xmppService","couldnt deliver payload: "+payload.toString()); Log.d(Config.LOGTAG,
"couldnt deliver payload: " + payload.toString());
} else { } else {
Log.d("xmppService","no sid found in incomming ibb packet"); Log.d(Config.LOGTAG, "no sid found in incomming ibb packet");
} }
} }
public void cancelInTransmission() { public void cancelInTransmission() {
for(JingleConnection connection : this.connections) { for (JingleConnection connection : this.connections) {
if (connection.getStatus() == JingleConnection.STATUS_TRANSMITTING) { if (connection.getStatus() == JingleConnection.STATUS_TRANSMITTING) {
connection.cancel(); connection.cancel();
} }

View file

@ -5,60 +5,63 @@ import java.security.Key;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
import android.util.Log; import android.util.Log;
public class JingleFile extends File { public class JingleFile extends File {
private static final long serialVersionUID = 2247012619505115863L; private static final long serialVersionUID = 2247012619505115863L;
private long expectedSize = 0; private long expectedSize = 0;
private String sha1sum; private String sha1sum;
private Key aeskey; private Key aeskey;
public JingleFile(String path) { public JingleFile(String path) {
super(path); super(path);
} }
public long getSize() { public long getSize() {
return super.length(); return super.length();
} }
public long getExpectedSize() { public long getExpectedSize() {
if (this.aeskey!=null) { if (this.aeskey != null) {
return (this.expectedSize/16 + 1) * 16; return (this.expectedSize / 16 + 1) * 16;
} else { } else {
return this.expectedSize; return this.expectedSize;
} }
} }
public void setExpectedSize(long size) { public void setExpectedSize(long size) {
this.expectedSize = size; this.expectedSize = size;
} }
public String getSha1Sum() { public String getSha1Sum() {
return this.sha1sum; return this.sha1sum;
} }
public void setSha1Sum(String sum) { public void setSha1Sum(String sum) {
this.sha1sum = sum; this.sha1sum = sum;
} }
public void setKey(byte[] key) { public void setKey(byte[] key) {
if (key.length>=32) { if (key.length >= 32) {
byte[] secretKey = new byte[32]; byte[] secretKey = new byte[32];
System.arraycopy(key, 0, secretKey, 0, 32); System.arraycopy(key, 0, secretKey, 0, 32);
this.aeskey = new SecretKeySpec(secretKey, "AES"); this.aeskey = new SecretKeySpec(secretKey, "AES");
} else if (key.length>=16) { } else if (key.length >= 16) {
byte[] secretKey = new byte[16]; byte[] secretKey = new byte[16];
System.arraycopy(key, 0, secretKey, 0, 16); System.arraycopy(key, 0, secretKey, 0, 16);
this.aeskey = new SecretKeySpec(secretKey, "AES"); this.aeskey = new SecretKeySpec(secretKey, "AES");
} else { } else {
Log.d("xmppService","weird key"); Log.d(Config.LOGTAG, "weird key");
} }
Log.d("xmppService","using aes key "+CryptoHelper.bytesToHex(this.aeskey.getEncoded())); Log.d(Config.LOGTAG,
"using aes key "
+ CryptoHelper.bytesToHex(this.aeskey.getEncoded()));
} }
public Key getKey() { public Key getKey() {
return this.aeskey; return this.aeskey;
} }

View file

@ -32,7 +32,7 @@ public class JingleInbandTransport extends JingleTransport {
private OutputStream fileOutputStream; private OutputStream fileOutputStream;
private long remainingSize; private long remainingSize;
private MessageDigest digest; private MessageDigest digest;
private OnFileTransmissionStatusChanged onFileTransmissionStatusChanged; private OnFileTransmissionStatusChanged onFileTransmissionStatusChanged;
private OnIqPacketReceived onAckReceived = new OnIqPacketReceived() { private OnIqPacketReceived onAckReceived = new OnIqPacketReceived() {
@ -77,7 +77,8 @@ public class JingleInbandTransport extends JingleTransport {
} }
@Override @Override
public void receive(JingleFile file, OnFileTransmissionStatusChanged callback) { public void receive(JingleFile file,
OnFileTransmissionStatusChanged callback) {
this.onFileTransmissionStatusChanged = callback; this.onFileTransmissionStatusChanged = callback;
this.file = file; this.file = file;
try { try {
@ -86,7 +87,7 @@ public class JingleInbandTransport extends JingleTransport {
file.getParentFile().mkdirs(); file.getParentFile().mkdirs();
file.createNewFile(); file.createNewFile();
this.fileOutputStream = getOutputStream(file); this.fileOutputStream = getOutputStream(file);
if (this.fileOutputStream==null) { if (this.fileOutputStream == null) {
callback.onFileTransferAborted(); callback.onFileTransferAborted();
return; return;
} }
@ -106,7 +107,7 @@ public class JingleInbandTransport extends JingleTransport {
this.digest = MessageDigest.getInstance("SHA-1"); this.digest = MessageDigest.getInstance("SHA-1");
this.digest.reset(); this.digest.reset();
fileInputStream = this.getInputStream(file); fileInputStream = this.getInputStream(file);
if (fileInputStream==null) { if (fileInputStream == null) {
callback.onFileTransferAborted(); callback.onFileTransferAborted();
return; return;
} }
@ -150,7 +151,8 @@ public class JingleInbandTransport extends JingleTransport {
try { try {
byte[] buffer = Base64.decode(data, Base64.NO_WRAP); byte[] buffer = Base64.decode(data, Base64.NO_WRAP);
if (this.remainingSize < buffer.length) { 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.remainingSize -= buffer.length;

View file

@ -21,7 +21,8 @@ public class JingleSocks5Transport extends JingleTransport {
private boolean activated = false; private boolean activated = false;
protected Socket socket; protected Socket socket;
public JingleSocks5Transport(JingleConnection jingleConnection, JingleCandidate candidate) { public JingleSocks5Transport(JingleConnection jingleConnection,
JingleCandidate candidate) {
this.candidate = candidate; this.candidate = candidate;
try { try {
MessageDigest mDigest = MessageDigest.getInstance("SHA-1"); MessageDigest mDigest = MessageDigest.getInstance("SHA-1");
@ -44,11 +45,12 @@ public class JingleSocks5Transport extends JingleTransport {
public void connect(final OnTransportConnected callback) { public void connect(final OnTransportConnected callback) {
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
try { try {
socket = new Socket(candidate.getHost(), candidate.getPort()); socket = new Socket(candidate.getHost(),
candidate.getPort());
inputStream = socket.getInputStream(); inputStream = socket.getInputStream();
outputStream = socket.getOutputStream(); outputStream = socket.getOutputStream();
byte[] login = { 0x05, 0x01, 0x00 }; byte[] login = { 0x05, 0x01, 0x00 };
@ -57,8 +59,9 @@ public class JingleSocks5Transport extends JingleTransport {
outputStream.write(login); outputStream.write(login);
inputStream.read(reply); inputStream.read(reply);
if (Arrays.equals(reply, expectedReply)) { if (Arrays.equals(reply, expectedReply)) {
String connect = "" + '\u0005' + '\u0001' + '\u0000' + '\u0003' String connect = "" + '\u0005' + '\u0001' + '\u0000'
+ '\u0028' + destination + '\u0000' + '\u0000'; + '\u0003' + '\u0028' + destination + '\u0000'
+ '\u0000';
outputStream.write(connect.getBytes()); outputStream.write(connect.getBytes());
byte[] result = new byte[2]; byte[] result = new byte[2];
inputStream.read(result); inputStream.read(result);
@ -80,12 +83,13 @@ public class JingleSocks5Transport extends JingleTransport {
} }
} }
}).start(); }).start();
} }
public void send(final JingleFile file, final OnFileTransmissionStatusChanged callback) { public void send(final JingleFile file,
final OnFileTransmissionStatusChanged callback) {
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
InputStream fileInputStream = null; InputStream fileInputStream = null;
@ -93,7 +97,7 @@ public class JingleSocks5Transport extends JingleTransport {
MessageDigest digest = MessageDigest.getInstance("SHA-1"); MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.reset(); digest.reset();
fileInputStream = getInputStream(file); fileInputStream = getInputStream(file);
if (fileInputStream==null) { if (fileInputStream == null) {
callback.onFileTransferAborted(); callback.onFileTransferAborted();
return; return;
} }
@ -105,7 +109,7 @@ public class JingleSocks5Transport extends JingleTransport {
} }
outputStream.flush(); outputStream.flush();
file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest())); file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
if (callback!=null) { if (callback != null) {
callback.onFileTransmitted(file); callback.onFileTransmitted(file);
} }
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
@ -125,12 +129,13 @@ public class JingleSocks5Transport extends JingleTransport {
} }
} }
}).start(); }).start();
} }
public void receive(final JingleFile file, final OnFileTransmissionStatusChanged callback) { public void receive(final JingleFile file,
final OnFileTransmissionStatusChanged callback) {
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
try { try {
@ -141,22 +146,22 @@ public class JingleSocks5Transport extends JingleTransport {
file.getParentFile().mkdirs(); file.getParentFile().mkdirs();
file.createNewFile(); file.createNewFile();
OutputStream fileOutputStream = getOutputStream(file); OutputStream fileOutputStream = getOutputStream(file);
if (fileOutputStream==null) { if (fileOutputStream == null) {
callback.onFileTransferAborted(); callback.onFileTransferAborted();
return; return;
} }
long remainingSize = file.getExpectedSize(); long remainingSize = file.getExpectedSize();
byte[] buffer = new byte[8192]; byte[] buffer = new byte[8192];
int count = buffer.length; int count = buffer.length;
while(remainingSize > 0) { while (remainingSize > 0) {
count = inputStream.read(buffer); count = inputStream.read(buffer);
if (count==-1) { if (count == -1) {
callback.onFileTransferAborted(); callback.onFileTransferAborted();
return; return;
} else { } else {
fileOutputStream.write(buffer, 0, count); fileOutputStream.write(buffer, 0, count);
digest.update(buffer, 0, count); digest.update(buffer, 0, count);
remainingSize-=count; remainingSize -= count;
} }
} }
fileOutputStream.flush(); fileOutputStream.flush();
@ -177,25 +182,25 @@ public class JingleSocks5Transport extends JingleTransport {
public boolean isProxy() { public boolean isProxy() {
return this.candidate.getType() == JingleCandidate.TYPE_PROXY; return this.candidate.getType() == JingleCandidate.TYPE_PROXY;
} }
public boolean needsActivation() { public boolean needsActivation() {
return (this.isProxy() && !this.activated); return (this.isProxy() && !this.activated);
} }
public void disconnect() { public void disconnect() {
if (this.socket!=null) { if (this.socket != null) {
try { try {
this.socket.close(); this.socket.close();
} catch (IOException e) { } catch (IOException e) {
} }
} }
} }
public boolean isEstablished() { public boolean isEstablished() {
return this.isEstablished; return this.isEstablished;
} }
public JingleCandidate getCandidate() { public JingleCandidate getCandidate() {
return this.candidate; return this.candidate;
} }

View file

@ -15,61 +15,72 @@ import javax.crypto.CipherInputStream;
import javax.crypto.NoSuchPaddingException; import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.IvParameterSpec;
import eu.siacs.conversations.Config;
import android.util.Log; import android.util.Log;
public abstract class JingleTransport { public abstract class JingleTransport {
public abstract void connect(final OnTransportConnected callback); public abstract void connect(final OnTransportConnected callback);
public abstract void receive(final JingleFile file, final OnFileTransmissionStatusChanged callback);
public abstract void send(final JingleFile file, final OnFileTransmissionStatusChanged callback); public abstract void receive(final JingleFile file,
private byte[] iv = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0xf}; final OnFileTransmissionStatusChanged callback);
protected InputStream getInputStream(JingleFile file) throws FileNotFoundException { public abstract void send(final JingleFile file,
final OnFileTransmissionStatusChanged callback);
private byte[] iv = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf };
protected InputStream getInputStream(JingleFile file)
throws FileNotFoundException {
if (file.getKey() == null) { if (file.getKey() == null) {
return new FileInputStream(file); return new FileInputStream(file);
} else { } else {
try { try {
IvParameterSpec ips = new IvParameterSpec(iv); IvParameterSpec ips = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, file.getKey(),ips); cipher.init(Cipher.ENCRYPT_MODE, file.getKey(), ips);
Log.d("xmppService","opening encrypted input stream"); Log.d(Config.LOGTAG, "opening encrypted input stream");
return new CipherInputStream(new FileInputStream(file), cipher); return new CipherInputStream(new FileInputStream(file), cipher);
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
Log.d("xmppService","no such algo: "+e.getMessage()); Log.d(Config.LOGTAG, "no such algo: " + e.getMessage());
return null; return null;
} catch (NoSuchPaddingException e) { } catch (NoSuchPaddingException e) {
Log.d("xmppService","no such padding: "+e.getMessage()); Log.d(Config.LOGTAG, "no such padding: " + e.getMessage());
return null; return null;
} catch (InvalidKeyException e) { } catch (InvalidKeyException e) {
Log.d("xmppService","invalid key: "+e.getMessage()); Log.d(Config.LOGTAG, "invalid key: " + e.getMessage());
return null; return null;
} catch (InvalidAlgorithmParameterException e) { } catch (InvalidAlgorithmParameterException e) {
Log.d("xmppService","invavid iv:"+e.getMessage()); Log.d(Config.LOGTAG, "invavid iv:" + e.getMessage());
return null; return null;
} }
} }
} }
protected OutputStream getOutputStream(JingleFile file) throws FileNotFoundException { protected OutputStream getOutputStream(JingleFile file)
throws FileNotFoundException {
if (file.getKey() == null) { if (file.getKey() == null) {
return new FileOutputStream(file); return new FileOutputStream(file);
} else { } else {
try { try {
IvParameterSpec ips = new IvParameterSpec(iv); IvParameterSpec ips = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, file.getKey(),ips); cipher.init(Cipher.DECRYPT_MODE, file.getKey(), ips);
Log.d("xmppService","opening encrypted output stream"); Log.d(Config.LOGTAG, "opening encrypted output stream");
return new CipherOutputStream(new FileOutputStream(file), cipher); return new CipherOutputStream(new FileOutputStream(file),
cipher);
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
Log.d("xmppService","no such algo: "+e.getMessage()); Log.d(Config.LOGTAG, "no such algo: " + e.getMessage());
return null; return null;
} catch (NoSuchPaddingException e) { } catch (NoSuchPaddingException e) {
Log.d("xmppService","no such padding: "+e.getMessage()); Log.d(Config.LOGTAG, "no such padding: " + e.getMessage());
return null; return null;
} catch (InvalidKeyException e) { } catch (InvalidKeyException e) {
Log.d("xmppService","invalid key: "+e.getMessage()); Log.d(Config.LOGTAG, "invalid key: " + e.getMessage());
return null; return null;
} catch (InvalidAlgorithmParameterException e) { } catch (InvalidAlgorithmParameterException e) {
Log.d("xmppService","invavid iv:"+e.getMessage()); Log.d(Config.LOGTAG, "invavid iv:" + e.getMessage());
return null; return null;
} }
} }

View file

@ -2,5 +2,6 @@ package eu.siacs.conversations.xmpp.jingle;
public interface OnFileTransmissionStatusChanged { public interface OnFileTransmissionStatusChanged {
public void onFileTransmitted(JingleFile file); public void onFileTransmitted(JingleFile file);
public void onFileTransferAborted(); public void onFileTransferAborted();
} }

View file

@ -1,5 +1,6 @@
package eu.siacs.conversations.xmpp.jingle; package eu.siacs.conversations.xmpp.jingle;
public interface OnPrimaryCandidateFound { public interface OnPrimaryCandidateFound {
public void onPrimaryCandidateFound(boolean success, JingleCandidate canditate); public void onPrimaryCandidateFound(boolean success,
JingleCandidate canditate);
} }

View file

@ -2,5 +2,6 @@ package eu.siacs.conversations.xmpp.jingle;
public interface OnTransportConnected { public interface OnTransportConnected {
public void failed(); public void failed();
public void established(); public void established();
} }

View file

@ -4,17 +4,17 @@ import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.jingle.JingleFile; import eu.siacs.conversations.xmpp.jingle.JingleFile;
public class Content extends Element { public class Content extends Element {
private String transportId; private String transportId;
private Content(String name) { private Content(String name) {
super(name); super(name);
} }
public Content() { public Content() {
super("content"); super("content");
} }
public Content(String creator, String name) { public Content(String creator, String name) {
super("content"); super("content");
this.setAttribute("creator", creator); this.setAttribute("creator", creator);
@ -24,39 +24,43 @@ public class Content extends Element {
public void setTransportId(String sid) { public void setTransportId(String sid) {
this.transportId = sid; this.transportId = sid;
} }
public void setFileOffer(JingleFile actualFile, boolean otr) { public void setFileOffer(JingleFile actualFile, boolean otr) {
Element description = this.addChild("description", "urn:xmpp:jingle:apps:file-transfer:3"); Element description = this.addChild("description",
"urn:xmpp:jingle:apps:file-transfer:3");
Element offer = description.addChild("offer"); Element offer = description.addChild("offer");
Element file = offer.addChild("file"); Element file = offer.addChild("file");
file.addChild("size").setContent(""+actualFile.getSize()); file.addChild("size").setContent("" + actualFile.getSize());
if (otr) { if (otr) {
file.addChild("name").setContent(actualFile.getName()+".otr"); file.addChild("name").setContent(actualFile.getName() + ".otr");
} else { } else {
file.addChild("name").setContent(actualFile.getName()); file.addChild("name").setContent(actualFile.getName());
} }
} }
public Element getFileOffer() { public Element getFileOffer() {
Element description = this.findChild("description", "urn:xmpp:jingle:apps:file-transfer:3"); Element description = this.findChild("description",
if (description==null) { "urn:xmpp:jingle:apps:file-transfer:3");
if (description == null) {
return null; return null;
} }
Element offer = description.findChild("offer"); Element offer = description.findChild("offer");
if (offer==null) { if (offer == null) {
return null; return null;
} }
return offer.findChild("file"); return offer.findChild("file");
} }
public void setFileOffer(Element fileOffer) { public void setFileOffer(Element fileOffer) {
Element description = this.findChild("description", "urn:xmpp:jingle:apps:file-transfer:3"); Element description = this.findChild("description",
if (description==null) { "urn:xmpp:jingle:apps:file-transfer:3");
description = this.addChild("description", "urn:xmpp:jingle:apps:file-transfer:3"); if (description == null) {
description = this.addChild("description",
"urn:xmpp:jingle:apps:file-transfer:3");
} }
description.addChild(fileOffer); description.addChild(fileOffer);
} }
public String getTransportId() { public String getTransportId() {
if (hasSocks5Transport()) { if (hasSocks5Transport()) {
this.transportId = socks5transport().getAttribute("sid"); this.transportId = socks5transport().getAttribute("sid");
@ -65,30 +69,34 @@ public class Content extends Element {
} }
return this.transportId; return this.transportId;
} }
public Element socks5transport() { public Element socks5transport() {
Element transport = this.findChild("transport", "urn:xmpp:jingle:transports:s5b:1"); Element transport = this.findChild("transport",
if (transport==null) { "urn:xmpp:jingle:transports:s5b:1");
transport = this.addChild("transport", "urn:xmpp:jingle:transports:s5b:1"); if (transport == null) {
transport = this.addChild("transport",
"urn:xmpp:jingle:transports:s5b:1");
transport.setAttribute("sid", this.transportId); transport.setAttribute("sid", this.transportId);
} }
return transport; return transport;
} }
public Element ibbTransport() { public Element ibbTransport() {
Element transport = this.findChild("transport", "urn:xmpp:jingle:transports:ibb:1"); Element transport = this.findChild("transport",
if (transport==null) { "urn:xmpp:jingle:transports:ibb:1");
transport = this.addChild("transport", "urn:xmpp:jingle:transports:ibb:1"); if (transport == null) {
transport = this.addChild("transport",
"urn:xmpp:jingle:transports:ibb:1");
transport.setAttribute("sid", this.transportId); transport.setAttribute("sid", this.transportId);
} }
return transport; return transport;
} }
public boolean hasSocks5Transport() { public boolean hasSocks5Transport() {
return this.hasChild("transport", "urn:xmpp:jingle:transports:s5b:1"); return this.hasChild("transport", "urn:xmpp:jingle:transports:s5b:1");
} }
public boolean hasIbbTransport() { public boolean hasIbbTransport() {
return this.hasChild("transport","urn:xmpp:jingle:transports:ibb:1"); return this.hasChild("transport", "urn:xmpp:jingle:transports:ibb:1");
} }
} }

View file

@ -7,18 +7,18 @@ public class JinglePacket extends IqPacket {
Content content = null; Content content = null;
Reason reason = null; Reason reason = null;
Element jingle = new Element("jingle"); Element jingle = new Element("jingle");
@Override @Override
public Element addChild(Element child) { public Element addChild(Element child) {
if ("jingle".equals(child.getName())) { if ("jingle".equals(child.getName())) {
Element contentElement = child.findChild("content"); Element contentElement = child.findChild("content");
if (contentElement!=null) { if (contentElement != null) {
this.content = new Content(); this.content = new Content();
this.content.setChildren(contentElement.getChildren()); this.content.setChildren(contentElement.getChildren());
this.content.setAttributes(contentElement.getAttributes()); this.content.setAttributes(contentElement.getAttributes());
} }
Element reasonElement = child.findChild("reason"); Element reasonElement = child.findChild("reason");
if (reasonElement!=null) { if (reasonElement != null) {
this.reason = new Reason(); this.reason = new Reason();
this.reason.setChildren(reasonElement.getChildren()); this.reason.setChildren(reasonElement.getChildren());
this.reason.setAttributes(reasonElement.getAttributes()); this.reason.setAttributes(reasonElement.getAttributes());
@ -27,33 +27,33 @@ public class JinglePacket extends IqPacket {
} }
return child; return child;
} }
public JinglePacket setContent(Content content) { public JinglePacket setContent(Content content) {
this.content = content; this.content = content;
return this; return this;
} }
public Content getJingleContent() { public Content getJingleContent() {
if (this.content==null) { if (this.content == null) {
this.content = new Content(); this.content = new Content();
} }
return this.content; return this.content;
} }
public JinglePacket setReason(Reason reason) { public JinglePacket setReason(Reason reason) {
this.reason = reason; this.reason = reason;
return this; return this;
} }
public Reason getReason() { public Reason getReason() {
return this.reason; return this.reason;
} }
private void build() { private void build() {
this.children.clear(); this.children.clear();
this.jingle.clearChildren(); this.jingle.clearChildren();
this.jingle.setAttribute("xmlns", "urn:xmpp:jingle:1"); this.jingle.setAttribute("xmlns", "urn:xmpp:jingle:1");
if (this.content!=null) { if (this.content != null) {
jingle.addChild(this.content); jingle.addChild(this.content);
} }
if (this.reason != null) { if (this.reason != null) {
@ -66,11 +66,11 @@ public class JinglePacket extends IqPacket {
public String getSessionId() { public String getSessionId() {
return this.jingle.getAttribute("sid"); return this.jingle.getAttribute("sid");
} }
public void setSessionId(String sid) { public void setSessionId(String sid) {
this.jingle.setAttribute("sid", sid); this.jingle.setAttribute("sid", sid);
} }
@Override @Override
public String toString() { public String toString() {
this.build(); this.build();
@ -80,11 +80,11 @@ public class JinglePacket extends IqPacket {
public void setAction(String action) { public void setAction(String action) {
this.jingle.setAttribute("action", action); this.jingle.setAttribute("action", action);
} }
public String getAction() { public String getAction() {
return this.jingle.getAttribute("action"); return this.jingle.getAttribute("action");
} }
public void setInitiator(String initiator) { public void setInitiator(String initiator) {
this.jingle.setAttribute("initiator", initiator); this.jingle.setAttribute("initiator", initiator);
} }

View file

@ -6,7 +6,7 @@ public class Reason extends Element {
private Reason(String name) { private Reason(String name) {
super(name); super(name);
} }
public Reason() { public Reason() {
super("reason"); super("reason");
} }

View file

@ -11,48 +11,51 @@ public class Avatar {
public int width; public int width;
public long size; public long size;
public String owner; public String owner;
public byte[] getImageAsBytes() { public byte[] getImageAsBytes() {
return Base64.decode(image, Base64.DEFAULT); return Base64.decode(image, Base64.DEFAULT);
} }
public String getFilename() { public String getFilename() {
if (type==null) { if (type == null) {
return sha1sum; return sha1sum;
} else if (type.equalsIgnoreCase("image/webp")) { } else if (type.equalsIgnoreCase("image/webp")) {
return sha1sum+".webp"; return sha1sum + ".webp";
} else if (type.equalsIgnoreCase("image/png")) { } else if (type.equalsIgnoreCase("image/png")) {
return sha1sum+".png"; return sha1sum + ".png";
} else { } else {
return sha1sum; return sha1sum;
} }
} }
public static Avatar parseMetadata(Element items) { public static Avatar parseMetadata(Element items) {
Element item = items.findChild("item"); Element item = items.findChild("item");
if (item==null) { if (item == null) {
return null; return null;
} }
Element metadata = item.findChild("metadata"); Element metadata = item.findChild("metadata");
if (metadata==null) { if (metadata == null) {
return null; return null;
} }
String primaryId = item.getAttribute("id"); String primaryId = item.getAttribute("id");
if (primaryId==null) { if (primaryId == null) {
return null; return null;
} }
for(Element child : metadata.getChildren()) { for (Element child : metadata.getChildren()) {
if (child.getName().equals("info") && primaryId.equals(child.getAttribute("id"))) { if (child.getName().equals("info")
&& primaryId.equals(child.getAttribute("id"))) {
Avatar avatar = new Avatar(); Avatar avatar = new Avatar();
String height = child.getAttribute("height"); String height = child.getAttribute("height");
String width = child.getAttribute("width"); String width = child.getAttribute("width");
String size = child.getAttribute("bytes"); String size = child.getAttribute("bytes");
try { try {
if (height!=null) { if (height != null) {
avatar.height = Integer.parseInt(height); avatar.height = Integer.parseInt(height);
} }
if (width!=null) { if (width != null) {
avatar.width = Integer.parseInt(width); avatar.width = Integer.parseInt(width);
} }
if (size!=null) { if (size != null) {
avatar.size = Long.parseLong(size); avatar.size = Long.parseLong(size);
} }
} catch (NumberFormatException e) { } catch (NumberFormatException e) {

View file

@ -15,20 +15,20 @@ public class AbstractStanza extends Element {
public String getFrom() { public String getFrom() {
return getAttribute("from"); return getAttribute("from");
} }
public String getId() { public String getId() {
return this.getAttribute("id"); return this.getAttribute("id");
} }
public void setTo(String to) { public void setTo(String to) {
setAttribute("to", to); setAttribute("to", to);
} }
public void setFrom(String from) { public void setFrom(String from) {
setAttribute("from",from); setAttribute("from", from);
} }
public void setId(String id) { public void setId(String id) {
setAttribute("id",id); setAttribute("id", id);
} }
} }

View file

@ -2,9 +2,8 @@ package eu.siacs.conversations.xmpp.stanzas;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
public class IqPacket extends AbstractStanza { public class IqPacket extends AbstractStanza {
public static final int TYPE_ERROR = -1; public static final int TYPE_ERROR = -1;
public static final int TYPE_SET = 0; public static final int TYPE_SET = 0;
public static final int TYPE_RESULT = 1; public static final int TYPE_RESULT = 1;
@ -33,25 +32,25 @@ public class IqPacket extends AbstractStanza {
break; break;
} }
} }
public IqPacket() { public IqPacket() {
super("iq"); super("iq");
} }
public Element query() { public Element query() {
Element query = findChild("query"); Element query = findChild("query");
if (query==null) { if (query == null) {
query = addChild("query"); query = addChild("query");
} }
return query; return query;
} }
public Element query(String xmlns) { public Element query(String xmlns) {
Element query = query(); Element query = query();
query.setAttribute("xmlns", xmlns); query.setAttribute("xmlns", xmlns);
return query(); return query();
} }
public int getType() { public int getType() {
String type = getAttribute("type"); String type = getAttribute("type");
if ("error".equals(type)) { if ("error".equals(type)) {
@ -66,7 +65,7 @@ public class IqPacket extends AbstractStanza {
return 1000; return 1000;
} }
} }
public IqPacket generateRespone(int type) { public IqPacket generateRespone(int type) {
IqPacket packet = new IqPacket(type); IqPacket packet = new IqPacket(type);
packet.setTo(this.getFrom()); packet.setTo(this.getFrom());

View file

@ -9,20 +9,20 @@ public class MessagePacket extends AbstractStanza {
public static final int TYPE_GROUPCHAT = 3; public static final int TYPE_GROUPCHAT = 3;
public static final int TYPE_ERROR = 4; public static final int TYPE_ERROR = 4;
public static final int TYPE_HEADLINE = 5; public static final int TYPE_HEADLINE = 5;
public MessagePacket() { public MessagePacket() {
super("message"); super("message");
} }
public String getBody() { public String getBody() {
Element body = this.findChild("body"); Element body = this.findChild("body");
if (body!=null) { if (body != null) {
return body.getContent(); return body.getContent();
} else { } else {
return null; return null;
} }
} }
public void setBody(String text) { public void setBody(String text) {
this.children.remove(findChild("body")); this.children.remove(findChild("body"));
Element body = new Element("body"); Element body = new Element("body");
@ -33,7 +33,7 @@ public class MessagePacket extends AbstractStanza {
public void setType(int type) { public void setType(int type) {
switch (type) { switch (type) {
case TYPE_CHAT: case TYPE_CHAT:
this.setAttribute("type","chat"); this.setAttribute("type", "chat");
break; break;
case TYPE_GROUPCHAT: case TYPE_GROUPCHAT:
this.setAttribute("type", "groupchat"); this.setAttribute("type", "groupchat");
@ -43,14 +43,14 @@ public class MessagePacket extends AbstractStanza {
case TYPE_NORMAL: case TYPE_NORMAL:
break; break;
default: default:
this.setAttribute("type","chat"); this.setAttribute("type", "chat");
break; break;
} }
} }
public int getType() { public int getType() {
String type = getAttribute("type"); String type = getAttribute("type");
if (type==null) { if (type == null) {
return TYPE_NORMAL; return TYPE_NORMAL;
} else if (type.equals("normal")) { } else if (type.equals("normal")) {
return TYPE_NORMAL; return TYPE_NORMAL;

View file

@ -1,8 +1,7 @@
package eu.siacs.conversations.xmpp.stanzas; package eu.siacs.conversations.xmpp.stanzas;
public class PresencePacket extends AbstractStanza { public class PresencePacket extends AbstractStanza {
public PresencePacket() { public PresencePacket() {
super("presence"); super("presence");
} }

View file

@ -6,8 +6,8 @@ public class AckPacket extends AbstractStanza {
public AckPacket(int sequence, int smVersion) { public AckPacket(int sequence, int smVersion) {
super("a"); super("a");
this.setAttribute("xmlns","urn:xmpp:sm:"+smVersion); this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
this.setAttribute("h", ""+sequence); this.setAttribute("h", "" + sequence);
} }
} }

View file

@ -6,7 +6,7 @@ public class EnablePacket extends AbstractStanza {
public EnablePacket(int smVersion) { public EnablePacket(int smVersion) {
super("enable"); super("enable");
this.setAttribute("xmlns","urn:xmpp:sm:"+smVersion); this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
this.setAttribute("resume", "true"); this.setAttribute("resume", "true");
} }

View file

@ -6,7 +6,7 @@ public class RequestPacket extends AbstractStanza {
public RequestPacket(int smVersion) { public RequestPacket(int smVersion) {
super("r"); super("r");
this.setAttribute("xmlns","urn:xmpp:sm:"+smVersion); this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
} }
} }

View file

@ -6,9 +6,9 @@ public class ResumePacket extends AbstractStanza {
public ResumePacket(String id, int sequence, int smVersion) { public ResumePacket(String id, int sequence, int smVersion) {
super("resume"); super("resume");
this.setAttribute("xmlns","urn:xmpp:sm:"+smVersion); this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion);
this.setAttribute("previd", id); this.setAttribute("previd", id);
this.setAttribute("h", ""+sequence); this.setAttribute("h", "" + sequence);
} }
} }