Merge branch 'streammanagment'

This commit is contained in:
Daniel Gultsch 2014-03-11 16:51:30 +01:00
commit 49efe81644
22 changed files with 527 additions and 289 deletions

View File

@ -20,7 +20,7 @@ import android.util.Log;
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.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.MessagePacket; import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import net.java.otr4j.OtrEngineHost; import net.java.otr4j.OtrEngineHost;
import net.java.otr4j.OtrException; import net.java.otr4j.OtrException;

View File

@ -5,7 +5,7 @@ import java.util.List;
import eu.siacs.conversations.entities.MucOptions.User; import eu.siacs.conversations.entities.MucOptions.User;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.PresencePacket; import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.util.Log; import android.util.Log;
@ -144,7 +144,11 @@ public class MucOptions {
if (split.length == 2) { if (split.length == 2) {
return split[1]; return split[1];
} else { } else {
return conversation.getAccount().getUsername(); if (conversation.getAccount()!=null) {
return conversation.getAccount().getUsername();
} else {
return null;
}
} }
} }

View File

@ -8,11 +8,10 @@ public class EventReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Intent mIntentForService = new Intent(context, Intent mIntentForService = new Intent(context,
XmppConnectionService.class); XmppConnectionService.class);
mIntentForService.putExtra("ping", intent.getBooleanExtra("ping",false)); if (intent.getAction() != null) {
if ((intent.getAction() != null) mIntentForService.setAction(intent.getAction());
&& (intent.getAction() } else {
.equals("android.intent.action.BOOT_COMPLETED"))) { mIntentForService.setAction("other");
} }
context.startService(mIntentForService); context.startService(mIntentForService);
} }

View File

@ -7,7 +7,6 @@ import java.util.Hashtable;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import org.json.JSONException;
import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection; import org.openintents.openpgp.util.OpenPgpServiceConnection;
@ -35,15 +34,15 @@ import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener;
import eu.siacs.conversations.utils.PhoneHelper; import eu.siacs.conversations.utils.PhoneHelper;
import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.IqPacket;
import eu.siacs.conversations.xmpp.MessagePacket;
import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.OnMessagePacketReceived; import eu.siacs.conversations.xmpp.OnMessagePacketReceived;
import eu.siacs.conversations.xmpp.OnPresencePacketReceived; import eu.siacs.conversations.xmpp.OnPresencePacketReceived;
import eu.siacs.conversations.xmpp.OnStatusChanged; import eu.siacs.conversations.xmpp.OnStatusChanged;
import eu.siacs.conversations.xmpp.OnTLSExceptionReceived; import eu.siacs.conversations.xmpp.OnTLSExceptionReceived;
import eu.siacs.conversations.xmpp.PresencePacket;
import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
@ -70,9 +69,10 @@ public class XmppConnectionService extends Service {
public long startDate; public long startDate;
private static final int PING_INTERVAL = 300; private static final int PING_MAX_INTERVAL = 300;
private static final int PING_MIN_INTERVAL = 10;
private static final int PING_TIMEOUT = 2; private static final int PING_TIMEOUT = 2;
private List<Account> accounts; private List<Account> accounts;
private List<Conversation> conversations = null; private List<Conversation> conversations = null;
@ -80,10 +80,11 @@ public class XmppConnectionService extends Service {
private OnAccountListChangedListener accountChangedListener = null; private OnAccountListChangedListener accountChangedListener = null;
private OnTLSExceptionReceived tlsException = null; private OnTLSExceptionReceived tlsException = null;
public void setOnTLSExceptionReceivedListener(OnTLSExceptionReceived listener) { public void setOnTLSExceptionReceivedListener(
OnTLSExceptionReceived listener) {
tlsException = listener; tlsException = listener;
} }
private Random mRandom = new Random(System.currentTimeMillis()); private Random mRandom = new Random(System.currentTimeMillis());
private ContentObserver contactObserver = new ContentObserver(null) { private ContentObserver contactObserver = new ContentObserver(null) {
@ -115,7 +116,7 @@ public class XmppConnectionService extends Service {
&& (packet.getBody().startsWith("?OTR"))) { && (packet.getBody().startsWith("?OTR"))) {
message = MessageParser.parseOtrChat(packet, account, message = MessageParser.parseOtrChat(packet, account,
service); service);
if (message!=null) { if (message != null) {
message.markUnread(); message.markUnread();
} }
} else if (packet.hasChild("body")) { } else if (packet.hasChild("body")) {
@ -126,7 +127,9 @@ public class XmppConnectionService extends Service {
|| (packet.hasChild("sent"))) { || (packet.hasChild("sent"))) {
message = MessageParser.parseCarbonMessage(packet, account, message = MessageParser.parseCarbonMessage(packet, account,
service); service);
message.getConversation().markRead(); if (message != null) {
message.getConversation().markRead();
}
notify = false; notify = false;
} }
@ -144,7 +147,7 @@ public class XmppConnectionService extends Service {
} else if (packet.getType() == MessagePacket.TYPE_ERROR) { } else if (packet.getType() == MessagePacket.TYPE_ERROR) {
message = MessageParser.parseError(packet, account, service); message = MessageParser.parseError(packet, account, service);
} else { } else {
//Log.d(LOGTAG, "unparsed message " + packet.toString()); // Log.d(LOGTAG, "unparsed message " + packet.toString());
} }
if (message == null) { if (message == null) {
return; return;
@ -169,7 +172,8 @@ public class XmppConnectionService extends Service {
if (convChangedListener != null) { if (convChangedListener != null) {
convChangedListener.onConversationListChanged(); convChangedListener.onConversationListChanged();
} else { } else {
UIHelper.updateNotification(getApplicationContext(), getConversations(), notify); UIHelper.updateNotification(getApplicationContext(),
getConversations(), notify);
} }
} }
}; };
@ -195,20 +199,12 @@ public class XmppConnectionService extends Service {
if (convChangedListener != null) { if (convChangedListener != null) {
convChangedListener.onConversationListChanged(); convChangedListener.onConversationListChanged();
} }
if (account.getKeys().has("pgp_signature")) { scheduleWakeupCall(PING_MAX_INTERVAL, true);
try {
sendPgpPresence(account, account.getKeys().getString("pgp_signature"));
} catch (JSONException e) {
//
}
}
scheduleWakeupCall(PING_INTERVAL, true);
} else if (account.getStatus() == Account.STATUS_OFFLINE) { } else if (account.getStatus() == Account.STATUS_OFFLINE) {
Log.d(LOGTAG,"onStatusChanged offline");
databaseBackend.clearPresences(account); databaseBackend.clearPresences(account);
if (!account.isOptionSet(Account.OPTION_DISABLED)) { if (!account.isOptionSet(Account.OPTION_DISABLED)) {
int timeToReconnect = mRandom.nextInt(50)+10; int timeToReconnect = mRandom.nextInt(50) + 10;
scheduleWakeupCall(timeToReconnect,false); scheduleWakeupCall(timeToReconnect, false);
} }
} }
@ -220,13 +216,17 @@ public class XmppConnectionService extends Service {
@Override @Override
public void onPresencePacketReceived(Account account, public void onPresencePacketReceived(Account account,
PresencePacket packet) { PresencePacket packet) {
if (packet.hasChild("x")&&(packet.findChild("x").getAttribute("xmlns").startsWith("http://jabber.org/protocol/muc"))) { if (packet.hasChild("x")
Conversation muc = findMuc(packet.getAttribute("from").split("/")[0]); && (packet.findChild("x").getAttribute("xmlns")
if (muc!=null) { .startsWith("http://jabber.org/protocol/muc"))) {
Conversation muc = findMuc(packet.getAttribute("from").split(
"/")[0]);
if (muc != null) {
int error = muc.getMucOptions().getError(); int error = muc.getMucOptions().getError();
muc.getMucOptions().processPacket(packet); muc.getMucOptions().processPacket(packet);
if ((muc.getMucOptions().getError()!=error)&&(convChangedListener!=null)) { if ((muc.getMucOptions().getError() != error)
Log.d(LOGTAG,"muc error status changed"); && (convChangedListener != null)) {
Log.d(LOGTAG, "muc error status changed");
convChangedListener.onConversationListChanged(); convChangedListener.onConversationListChanged();
} }
} }
@ -252,15 +252,17 @@ public class XmppConnectionService extends Service {
contact.updatePresence(fromParts[1], Presences.DND); contact.updatePresence(fromParts[1], Presences.DND);
} }
PgpEngine pgp = getPgpEngine(); PgpEngine pgp = getPgpEngine();
if (pgp!=null) { if (pgp != null) {
Element x = packet.findChild("x"); Element x = packet.findChild("x");
if ((x != null) if ((x != null)
&& (x.getAttribute("xmlns").equals("jabber:x:signed"))) { && (x.getAttribute("xmlns")
.equals("jabber:x:signed"))) {
try { try {
contact.setPgpKeyId(pgp.fetchKeyId(packet.findChild("status") contact.setPgpKeyId(pgp.fetchKeyId(packet
.getContent(), x.getContent())); .findChild("status").getContent(), x
.getContent()));
} catch (OpenPgpException e) { } catch (OpenPgpException e) {
Log.d(LOGTAG,"faulty pgp. just ignore"); Log.d(LOGTAG, "faulty pgp. just ignore");
} }
} }
} }
@ -290,7 +292,7 @@ public class XmppConnectionService extends Service {
// TODO: ask user to handle it maybe // TODO: ask user to handle it maybe
} }
} else { } else {
Log.d(LOGTAG, packet.toString()); //Log.d(LOGTAG, packet.toString());
} }
replaceContactInConversation(contact.getJid(), contact); replaceContactInConversation(contact.getJid(), contact);
} }
@ -314,6 +316,8 @@ public class XmppConnectionService extends Service {
private OpenPgpServiceConnection pgpServiceConnection; private OpenPgpServiceConnection pgpServiceConnection;
private PgpEngine mPgpEngine = null; private PgpEngine mPgpEngine = null;
private Intent pingIntent;
private PendingIntent pendingPingIntent = null;
public PgpEngine getPgpEngine() { public PgpEngine getPgpEngine() {
if (pgpServiceConnection.isBound()) { if (pgpServiceConnection.isBound()) {
@ -330,7 +334,7 @@ public class XmppConnectionService extends Service {
} }
protected Conversation findMuc(String name) { protected Conversation findMuc(String name) {
for(Conversation conversation : this.conversations) { for (Conversation conversation : this.conversations) {
if (conversation.getContactJid().split("/")[0].equals(name)) { if (conversation.getContactJid().split("/")[0].equals(name)) {
return conversation; return conversation;
} }
@ -391,34 +395,64 @@ public class XmppConnectionService extends Service {
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
ConnectivityManager cm = (ConnectivityManager) getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE); Log.d(LOGTAG,"calling start service. caller was:"+intent.getAction());
// internet and online last_received - list_ping >= max_ping : ping
// internet and online last_ping - last_received >= ping_timeout :
// reconnect
// internet and offline and enabled : connect (Threat start)
// no internet - set no internet
ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null boolean isConnected = activeNetwork != null
&& activeNetwork.isConnected(); && activeNetwork.isConnected();
for (Account account : accounts) { for (Account account : accounts) {
if (!isConnected) { if (!account.isOptionSet(Account.OPTION_DISABLED)) {
account.setStatus(Account.STATUS_NO_INTERNET); if (!isConnected) {
} else { account.setStatus(Account.STATUS_NO_INTERNET);
if (account.getStatus() == Account.STATUS_NO_INTERNET) { if (statusListener!=null) {
account.setStatus(Account.STATUS_OFFLINE); statusListener.onStatusChanged(account);
}
}
if (accountChangedListener!=null) {
accountChangedListener.onAccountListChangedListener();
}
if ((!account.isOptionSet(Account.OPTION_DISABLED))&&(isConnected)) {
if (account.getXmppConnection() == null) {
account.setXmppConnection(this.createConnection(account));
}
if (account.getStatus()==Account.STATUS_OFFLINE) {
Thread thread = new Thread(account.getXmppConnection());
thread.start();
} else {
if (intent.getBooleanExtra("ping", false)) {
Log.d(LOGTAG,"start service ping");
ping(account,PING_TIMEOUT);
} }
} else {
if (account.getStatus() == Account.STATUS_NO_INTERNET) {
account.setStatus(Account.STATUS_OFFLINE);
if (statusListener!=null) {
statusListener.onStatusChanged(account);
}
}
// TODO 3 remaining cases
if (account.getStatus() == Account.STATUS_ONLINE) {
long lastReceived = account.getXmppConnection().lastPaketReceived;
long lastSent = account.getXmppConnection().lastPingSent;
if (lastSent - lastReceived >= PING_TIMEOUT * 1000) {
Log.d(LOGTAG, account.getJid() + ": ping timeout");
this.reconnectAccount(account);
} else if (SystemClock.elapsedRealtime() - lastReceived >= PING_MIN_INTERVAL * 1000) {
account.getXmppConnection().sendPing();
account.getXmppConnection().lastPingSent = SystemClock.elapsedRealtime();
this.scheduleWakeupCall(2, false);
}
} else if (account.getStatus() == Account.STATUS_OFFLINE) {
if (account.getXmppConnection() == null) {
account.setXmppConnection(this
.createConnection(account));
}
account.getXmppConnection().lastPingSent = SystemClock.elapsedRealtime();
new Thread(account.getXmppConnection()).start();
} else {
Log.d(LOGTAG,account.getJid()+": status="+account.getStatus());
// TODO notify user of ssl cert problem or auth problem or what ever
}
//in any case. reschedule wakup call
this.scheduleWakeupCall(PING_MAX_INTERVAL, true);
}
if (accountChangedListener != null) {
accountChangedListener.onAccountListChangedListener();
} }
} }
} }
@ -436,8 +470,7 @@ public class XmppConnectionService extends Service {
this.pgpServiceConnection = new OpenPgpServiceConnection( this.pgpServiceConnection = new OpenPgpServiceConnection(
getApplicationContext(), "org.sufficientlysecure.keychain"); getApplicationContext(), "org.sufficientlysecure.keychain");
this.pgpServiceConnection.bindToService(); this.pgpServiceConnection.bindToService();
} }
@Override @Override
@ -445,26 +478,47 @@ public class XmppConnectionService extends Service {
super.onDestroy(); super.onDestroy();
for (Account account : accounts) { for (Account account : accounts) {
if (account.getXmppConnection() != null) { if (account.getXmppConnection() != null) {
disconnect(account); disconnect(account, true);
} }
} }
} }
protected void scheduleWakeupCall(int seconds,boolean ping) { protected void scheduleWakeupCall(int seconds, boolean ping) {
long timeToWake = SystemClock.elapsedRealtime() + seconds * 1000;
Context context = getApplicationContext(); Context context = getApplicationContext();
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); AlarmManager alarmManager = (AlarmManager) context
Intent intent = new Intent(context, EventReceiver.class); .getSystemService(Context.ALARM_SERVICE);
intent.putExtra("ping", ping);
PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() +
seconds * 1000, alarmIntent);
if (ping) {
Log.d(LOGTAG,"schedule ping in "+seconds+" seconds");
} else {
Log.d(LOGTAG,"schedule reconnect in "+seconds+" seconds");
}
if (ping) {
if (this.pingIntent==null) {
this.pingIntent = new Intent(context, EventReceiver.class);
this.pingIntent.setAction("ping");
this.pingIntent.putExtra("time", timeToWake);
this.pendingPingIntent = PendingIntent.getBroadcast(context, 0,
this.pingIntent, 0);
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,timeToWake, pendingPingIntent);
//Log.d(LOGTAG,"schedule ping in "+seconds+" seconds");
} else {
long scheduledTime = this.pingIntent.getLongExtra("time", 0);
if (scheduledTime<SystemClock.elapsedRealtime() || (scheduledTime > timeToWake)) {
this.pingIntent.putExtra("time", timeToWake);
alarmManager.cancel(this.pendingPingIntent);
this.pendingPingIntent = PendingIntent.getBroadcast(context, 0,
this.pingIntent, 0);
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,timeToWake, pendingPingIntent);
//Log.d(LOGTAG,"reschedule old ping to ping in "+seconds+" seconds");
}
}
} else {
Intent intent = new Intent(context, EventReceiver.class);
intent.setAction("ping_check");
PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 0,
intent, 0);
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,timeToWake, alarmIntent);
}
} }
public XmppConnection createConnection(Account account) { public XmppConnection createConnection(Account account) {
@ -475,16 +529,19 @@ public class XmppConnectionService extends Service {
connection.setOnPresencePacketReceivedListener(this.presenceListener); connection.setOnPresencePacketReceivedListener(this.presenceListener);
connection connection
.setOnUnregisteredIqPacketReceivedListener(this.unknownIqListener); .setOnUnregisteredIqPacketReceivedListener(this.unknownIqListener);
connection.setOnTLSExceptionReceivedListener(new OnTLSExceptionReceived() { connection
.setOnTLSExceptionReceivedListener(new OnTLSExceptionReceived() {
@Override
public void onTLSExceptionReceived(String fingerprint, Account account) { @Override
Log.d(LOGTAG,"tls exception arrived in service"); public void onTLSExceptionReceived(String fingerprint,
if (tlsException!=null) { Account account) {
tlsException.onTLSExceptionReceived(fingerprint,account); Log.d(LOGTAG, "tls exception arrived in service");
} if (tlsException != null) {
} tlsException.onTLSExceptionReceived(fingerprint,
}); account);
}
}
});
return connection; return connection;
} }
@ -629,9 +686,10 @@ public class XmppConnectionService extends Service {
Element query = new Element("query"); Element query = new Element("query");
query.setAttribute("xmlns", "jabber:iq:roster"); query.setAttribute("xmlns", "jabber:iq:roster");
if (!"".equals(account.getRosterVersion())) { if (!"".equals(account.getRosterVersion())) {
Log.d(LOGTAG,account.getJid()+ ": fetching roster version "+account.getRosterVersion()); Log.d(LOGTAG, account.getJid() + ": fetching roster version "
+ account.getRosterVersion());
} else { } else {
Log.d(LOGTAG,account.getJid()+": fetching roster"); Log.d(LOGTAG, account.getJid() + ": fetching roster");
} }
query.setAttribute("ver", account.getRosterVersion()); query.setAttribute("ver", account.getRosterVersion());
iqPacket.addChild(query); iqPacket.addChild(query);
@ -643,7 +701,8 @@ public class XmppConnectionService extends Service {
IqPacket packet) { IqPacket packet) {
Element roster = packet.findChild("query"); Element roster = packet.findChild("query");
if (roster != null) { if (roster != null) {
Log.d(LOGTAG,account.getJid()+": processing roster"); Log.d(LOGTAG, account.getJid()
+ ": processing roster");
processRosterItems(account, roster); processRosterItems(account, roster);
StringBuilder mWhere = new StringBuilder(); StringBuilder mWhere = new StringBuilder();
mWhere.append("jid NOT IN("); mWhere.append("jid NOT IN(");
@ -668,7 +727,8 @@ public class XmppConnectionService extends Service {
} }
} else { } else {
Log.d(LOGTAG,account.getJid()+": empty roster returend"); Log.d(LOGTAG, account.getJid()
+ ": empty roster returend");
} }
mergePhoneContactsWithRoster(new OnPhoneContactsMerged() { mergePhoneContactsWithRoster(new OnPhoneContactsMerged() {
@ -864,7 +924,7 @@ public class XmppConnectionService extends Service {
public void deleteAccount(Account account) { public void deleteAccount(Account account) {
Log.d(LOGTAG, "called delete account"); Log.d(LOGTAG, "called delete account");
if (account.getXmppConnection() != null) { if (account.getXmppConnection() != null) {
this.disconnect(account); this.disconnect(account, false);
} }
databaseBackend.deleteAccount(account); databaseBackend.deleteAccount(account);
this.accounts.remove(account); this.accounts.remove(account);
@ -913,8 +973,7 @@ public class XmppConnectionService extends Service {
nick = conversation.getAccount().getUsername(); nick = conversation.getAccount().getUsername();
} }
PresencePacket packet = new PresencePacket(); PresencePacket packet = new PresencePacket();
packet.setAttribute("to", muc + "/" packet.setAttribute("to", muc + "/" + nick);
+ nick);
Element x = new Element("x"); Element x = new Element("x");
x.setAttribute("xmlns", "http://jabber.org/protocol/muc"); x.setAttribute("xmlns", "http://jabber.org/protocol/muc");
if (conversation.getMessages().size() != 0) { if (conversation.getMessages().size() != 0) {
@ -928,21 +987,22 @@ public class XmppConnectionService extends Service {
conversation.getAccount().getXmppConnection() conversation.getAccount().getXmppConnection()
.sendPresencePacket(packet); .sendPresencePacket(packet);
} }
private OnRenameListener renameListener = null; private OnRenameListener renameListener = null;
private boolean pongReceived; private boolean pongReceived;
public void setOnRenameListener(OnRenameListener listener) { public void setOnRenameListener(OnRenameListener listener) {
this.renameListener = listener; this.renameListener = listener;
} }
public void renameInMuc(final Conversation conversation, final String nick) { public void renameInMuc(final Conversation conversation, final String nick) {
final MucOptions options = conversation.getMucOptions(); final MucOptions options = conversation.getMucOptions();
if (options.online()) { if (options.online()) {
options.setOnRenameListener(new OnRenameListener() { options.setOnRenameListener(new OnRenameListener() {
@Override @Override
public void onRename(boolean success) { public void onRename(boolean success) {
if (renameListener!=null) { if (renameListener != null) {
renameListener.onRename(success); renameListener.onRename(success);
} }
if (success) { if (success) {
@ -951,30 +1011,36 @@ public class XmppConnectionService extends Service {
} }
}); });
PresencePacket packet = new PresencePacket(); PresencePacket packet = new PresencePacket();
packet.setAttribute("to", conversation.getContactJid().split("/")[0]+"/"+nick); packet.setAttribute("to",
conversation.getContactJid().split("/")[0] + "/" + nick);
packet.setAttribute("from", conversation.getAccount().getFullJid()); packet.setAttribute("from", conversation.getAccount().getFullJid());
packet = conversation.getAccount().getXmppConnection().sendPresencePacket(packet, new OnPresencePacketReceived() { conversation.getAccount().getXmppConnection()
.sendPresencePacket(packet, new OnPresencePacketReceived() {
@Override
public void onPresencePacketReceived(Account account, PresencePacket packet) { @Override
final boolean changed; public void onPresencePacketReceived(Account account,
String type = packet.getAttribute("type"); PresencePacket packet) {
changed = (!"error".equals(type)); final boolean changed;
if (!changed) { String type = packet.getAttribute("type");
options.getOnRenameListener().onRename(false); changed = (!"error".equals(type));
} else { if (!changed) {
if (type==null) { options.getOnRenameListener().onRename(false);
options.getOnRenameListener().onRename(true); } else {
options.setNick(packet.getAttribute("from").split("/")[1]); if (type == null) {
} else { options.getOnRenameListener()
options.processPacket(packet); .onRename(true);
options.setNick(packet.getAttribute("from")
.split("/")[1]);
} else {
options.processPacket(packet);
}
}
} }
} });
}
});
} else { } else {
String jid = conversation.getContactJid().split("/")[0]+"/"+nick; String jid = conversation.getContactJid().split("/")[0] + "/"
+ nick;
conversation.setContactJid(jid); conversation.setContactJid(jid);
databaseBackend.updateConversation(conversation); databaseBackend.updateConversation(conversation);
if (conversation.getAccount().getStatus() == Account.STATUS_ONLINE) { if (conversation.getAccount().getStatus() == Account.STATUS_ONLINE) {
@ -987,31 +1053,46 @@ public class XmppConnectionService extends Service {
PresencePacket packet = new PresencePacket(); PresencePacket packet = new PresencePacket();
packet.setAttribute("to", conversation.getContactJid()); packet.setAttribute("to", conversation.getContactJid());
packet.setAttribute("from", conversation.getAccount().getFullJid()); packet.setAttribute("from", conversation.getAccount().getFullJid());
packet.setAttribute("type","unavailable"); packet.setAttribute("type", "unavailable");
conversation.getAccount().getXmppConnection().sendPresencePacket(packet); conversation.getAccount().getXmppConnection()
.sendPresencePacket(packet);
conversation.getMucOptions().setOffline(); conversation.getMucOptions().setOffline();
} }
public void disconnect(Account account) { public void disconnect(final Account account, boolean blocking) {
List<Conversation> conversations = getConversations(); if ((account.getStatus() == Account.STATUS_ONLINE)||(account.getStatus() == Account.STATUS_DISABLED)) {
for (int i = 0; i < conversations.size(); i++) { List<Conversation> conversations = getConversations();
Conversation conversation = conversations.get(i); for (int i = 0; i < conversations.size(); i++) {
if (conversation.getAccount() == account) { Conversation conversation = conversations.get(i);
if (conversation.getMode() == Conversation.MODE_MULTI) { if (conversation.getAccount() == account) {
leaveMuc(conversation); if (conversation.getMode() == Conversation.MODE_MULTI) {
} else { leaveMuc(conversation);
try { } else {
conversation.endOtrIfNeeded(); try {
} catch (OtrException e) { conversation.endOtrIfNeeded();
Log.d(LOGTAG, "error ending otr session for " } catch (OtrException e) {
+ conversation.getName()); Log.d(LOGTAG, "error ending otr session for "
+ conversation.getName());
}
} }
} }
} }
if (!blocking) {
new Thread(new Runnable() {
@Override
public void run() {
account.getXmppConnection().disconnect(false);
Log.d(LOGTAG, "disconnected account: " + account.getJid());
//account.setXmppConnection(null);
}
}).start();
} else {
account.getXmppConnection().disconnect(false);
Log.d(LOGTAG, "disconnected account: " + account.getJid());
//account.setXmppConnection(null);
}
} }
account.getXmppConnection().disconnect();
Log.d(LOGTAG, "disconnected account: " + account.getJid());
account.setXmppConnection(null);
} }
@Override @Override
@ -1092,7 +1173,7 @@ public class XmppConnectionService extends Service {
Log.d(LOGTAG, packet.toString()); Log.d(LOGTAG, packet.toString());
contact.getAccount().getXmppConnection().sendPresencePacket(packet); contact.getAccount().getXmppConnection().sendPresencePacket(packet);
} }
public void sendPgpPresence(Account account, String signature) { public void sendPgpPresence(Account account, String signature) {
PresencePacket packet = new PresencePacket(); PresencePacket packet = new PresencePacket();
packet.setAttribute("from", account.getFullJid()); packet.setAttribute("from", account.getFullJid());
@ -1122,7 +1203,7 @@ public class XmppConnectionService extends Service {
public Contact findContact(String uuid) { public Contact findContact(String uuid) {
Contact contact = this.databaseBackend.getContact(uuid); Contact contact = this.databaseBackend.getContact(uuid);
for(Account account : getAccounts()) { for (Account account : getAccounts()) {
if (contact.getAccountUuid().equals(account.getUuid())) { if (contact.getAccountUuid().equals(account.getUuid())) {
contact.setAccount(account); contact.setAccount(account);
} }
@ -1134,56 +1215,22 @@ public class XmppConnectionService extends Service {
this.tlsException = null; this.tlsException = null;
} }
public void reconnectAccount(Account account) { //TODO dont let thread sleep but schedule wake up
if (account.getXmppConnection() != null) { public void reconnectAccount(final Account account) {
disconnect(account);
}
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
if (account.getXmppConnection()==null) {
account.setXmppConnection(this.createConnection(account));
}
Thread thread = new Thread(account.getXmppConnection());
thread.start();
}
}
public void ping(final Account account,final int timeout) {
Log.d(LOGTAG,account.getJid()+": sending ping");
IqPacket iq = new IqPacket(IqPacket.TYPE_GET);
Element ping = new Element("ping");
iq.setAttribute("from",account.getFullJid());
ping.setAttribute("xmlns", "urn:xmpp:ping");
iq.addChild(ping);
pongReceived = false;
account.getXmppConnection().sendIqPacket(iq, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
pongReceived = true;
}
});
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
int i = 0; if (account.getXmppConnection() != null) {
while(i <= (5 * timeout)) { disconnect(account, true);
if (pongReceived) {
scheduleWakeupCall(PING_INTERVAL,true);
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
++i;
} }
if (!pongReceived) { if (!account.isOptionSet(Account.OPTION_DISABLED)) {
Log.d("xmppService",account.getJid()+" no pong after "+timeout+" seconds"); if (account.getXmppConnection() == null) {
reconnectAccount(account); account.setXmppConnection(createConnection(account));
}
Thread thread = new Thread(account.getXmppConnection());
thread.start();
} }
} }
}).start(); }).start();
} }

View File

@ -332,6 +332,9 @@ public class ConversationFragment extends Fragment {
final ConversationActivity activity = (ConversationActivity) getActivity(); final ConversationActivity activity = (ConversationActivity) getActivity();
activity.registerListener(); activity.registerListener();
this.conversation = activity.getSelectedConversation(); this.conversation = activity.getSelectedConversation();
if (this.conversation==null) {
return;
}
this.selfBitmap = findSelfPicture(); this.selfBitmap = findSelfPicture();
updateMessages(); updateMessages();
// rendering complete. now go tell activity to close pane // rendering complete. now go tell activity to close pane
@ -374,7 +377,6 @@ public class ConversationFragment extends Fragment {
public void updateMessages() { public void updateMessages() {
ConversationActivity activity = (ConversationActivity) getActivity(); ConversationActivity activity = (ConversationActivity) getActivity();
List<Message> encryptedMessages = new LinkedList<Message>(); List<Message> encryptedMessages = new LinkedList<Message>();
// TODO this.conversation could be null?!
for(Message message : this.conversation.getMessages()) { for(Message message : this.conversation.getMessages()) {
if (message.getEncryption() == Message.ENCRYPTION_PGP) { if (message.getEncryption() == Message.ENCRYPTION_PGP) {
encryptedMessages.add(message); encryptedMessages.add(message);

View File

@ -48,7 +48,6 @@ public class ManageAccountActivity extends XmppActivity {
@Override @Override
public void onAccountListChangedListener() { public void onAccountListChangedListener() {
Log.d("xmppService", "ui on account list changed listener");
accountList.clear(); accountList.clear();
accountList.addAll(xmppConnectionService.getAccounts()); accountList.addAll(xmppConnectionService.getAccounts());
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {

View File

@ -42,8 +42,9 @@ public abstract class XmppActivity extends Activity {
protected void onStart() { protected void onStart() {
super.onStart(); super.onStart();
if (!xmppConnectionServiceBound) { if (!xmppConnectionServiceBound) {
startService(new Intent(this, XmppConnectionService.class));
Intent intent = new Intent(this, XmppConnectionService.class); Intent intent = new Intent(this, XmppConnectionService.class);
intent.setAction("ui");
startService(intent);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE); bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
} }
} }

View File

@ -11,7 +11,7 @@ import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.MessagePacket; import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
public class MessageParser { public class MessageParser {

View File

@ -5,25 +5,29 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import android.util.Log; import eu.siacs.conversations.xmpp.stanzas.AbstractStanza;
public class TagWriter { public class TagWriter {
private OutputStreamWriter outputStream; private OutputStreamWriter outputStream;
private LinkedBlockingQueue<String> writeQueue = new LinkedBlockingQueue<String>(); private boolean finshed = false;
private Thread writer = new Thread() { private LinkedBlockingQueue<AbstractStanza> writeQueue = new LinkedBlockingQueue<AbstractStanza>();
public boolean shouldStop = false; private Thread asyncStanzaWriter = new Thread() {
private boolean shouldStop = false;
@Override @Override
public void run() { public void run() {
while(!shouldStop) { while(!shouldStop) {
if ((finshed)&&(writeQueue.size() == 0)) {
return;
}
try { try {
String output = writeQueue.take(); AbstractStanza output = writeQueue.take();
outputStream.write(output); outputStream.write(output.toString());
outputStream.flush(); outputStream.flush();
} catch (IOException e) { } catch (IOException e) {
Log.d("xmppService", "error writing to stream"); shouldStop = true;
} catch (InterruptedException e) { } catch (InterruptedException e) {
shouldStop = true;
} }
} }
} }
@ -31,34 +35,49 @@ public class TagWriter {
public TagWriter() { public TagWriter() {
} }
public TagWriter(OutputStream out) { public TagWriter(OutputStream out) {
this.setOutputStream(out); this.setOutputStream(out);
writer.start();
} }
public void setOutputStream(OutputStream out) { public void setOutputStream(OutputStream out) {
this.outputStream = new OutputStreamWriter(out); this.outputStream = new OutputStreamWriter(out);
if (!writer.isAlive()) writer.start();
} }
public TagWriter beginDocument() { public TagWriter beginDocument() throws IOException {
writeQueue.add("<?xml version='1.0'?>"); outputStream.write("<?xml version='1.0'?>");
outputStream.flush();
return this; return this;
} }
public TagWriter writeTag(Tag tag) { public TagWriter writeTag(Tag tag) throws IOException {
writeQueue.add(tag.toString()); outputStream.write(tag.toString());
outputStream.flush();
return this; return this;
} }
public void writeString(String string) { public TagWriter writeElement(Element element) throws IOException {
writeQueue.add(string); outputStream.write(element.toString());
outputStream.flush();
return this;
} }
public void writeElement(Element element) { public TagWriter writeStanzaAsync(AbstractStanza stanza) {
writeQueue.add(element.toString()); if (finshed) {
return this;
} else {
if (!asyncStanzaWriter.isAlive()) asyncStanzaWriter.start();
writeQueue.add(stanza);
return this;
}
}
public void finish() {
this.finshed = true;
}
public boolean finished() {
return (this.writeQueue.size() == 0);
} }
} }

View File

@ -1,6 +1,7 @@
package eu.siacs.conversations.xmpp; package eu.siacs.conversations.xmpp;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public interface OnIqPacketReceived extends PacketReceived { public interface OnIqPacketReceived extends PacketReceived {
public void onIqPacketReceived(Account account, IqPacket packet); public void onIqPacketReceived(Account account, IqPacket packet);

View File

@ -1,6 +1,7 @@
package eu.siacs.conversations.xmpp; package eu.siacs.conversations.xmpp;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
public interface OnMessagePacketReceived extends PacketReceived { public interface OnMessagePacketReceived extends PacketReceived {
public void onMessagePacketReceived(Account account, MessagePacket packet); public void onMessagePacketReceived(Account account, MessagePacket packet);

View File

@ -1,6 +1,7 @@
package eu.siacs.conversations.xmpp; package eu.siacs.conversations.xmpp;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
public interface OnPresencePacketReceived extends PacketReceived { public interface OnPresencePacketReceived extends PacketReceived {
public void onPresencePacketReceived(Account account, PresencePacket packet); public void onPresencePacketReceived(Account account, PresencePacket packet);

View File

@ -1,13 +0,0 @@
package eu.siacs.conversations.xmpp;
import eu.siacs.conversations.xml.Element;
public class PresencePacket extends Element {
private PresencePacket(String name) {
super("presence");
}
public PresencePacket() {
super("presence");
}
}

View File

@ -26,10 +26,12 @@ import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import org.json.JSONException;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import android.os.Bundle; import android.os.Bundle;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.SystemClock;
import android.util.Log; import android.util.Log;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
@ -38,6 +40,14 @@ import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Tag; import eu.siacs.conversations.xml.Tag;
import eu.siacs.conversations.xml.TagWriter; import eu.siacs.conversations.xml.TagWriter;
import eu.siacs.conversations.xml.XmlReader; import eu.siacs.conversations.xml.XmlReader;
import eu.siacs.conversations.xmpp.stanzas.AbstractStanza;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
import eu.siacs.conversations.xmpp.stanzas.streammgmt.AckPacket;
import eu.siacs.conversations.xmpp.stanzas.streammgmt.EnablePacket;
import eu.siacs.conversations.xmpp.stanzas.streammgmt.RequestPacket;
import eu.siacs.conversations.xmpp.stanzas.streammgmt.ResumePacket;
public class XmppConnection implements Runnable { public class XmppConnection implements Runnable {
@ -56,6 +66,14 @@ public class XmppConnection implements Runnable {
private boolean shouldAuthenticate = true; private boolean shouldAuthenticate = true;
private Element streamFeatures; private Element streamFeatures;
private HashSet<String> discoFeatures = new HashSet<String>(); private HashSet<String> discoFeatures = new HashSet<String>();
private String streamId = null;
private int stanzasReceived = 0;
private int stanzasSent = 0;
public long lastPaketReceived = 0;
public long lastPingSent = 0;
private static final int PACKET_IQ = 0; private static final int PACKET_IQ = 0;
private static final int PACKET_MESSAGE = 1; private static final int PACKET_MESSAGE = 1;
@ -77,14 +95,16 @@ public class XmppConnection implements Runnable {
} }
protected void changeStatus(int nextStatus) { protected void changeStatus(int nextStatus) {
account.setStatus(nextStatus); if (account.getStatus() != nextStatus) {
if (statusListener != null) { account.setStatus(nextStatus);
statusListener.onStatusChanged(account); if (statusListener != null) {
statusListener.onStatusChanged(account);
}
} }
} }
protected void connect() { protected void connect() {
Log.d(LOGTAG, "connecting"); Log.d(LOGTAG,account.getJid()+ ": connecting");
try { try {
tagReader = new XmlReader(wakeLock); tagReader = new XmlReader(wakeLock);
tagWriter = new TagWriter(); tagWriter = new TagWriter();
@ -147,7 +167,6 @@ public class XmppConnection implements Runnable {
@Override @Override
public void run() { public void run() {
connect(); connect();
Log.d(LOGTAG, "end run");
} }
private void processStream(Tag currentTag) throws XmlPullParserException, private void processStream(Tag currentTag) throws XmlPullParserException,
@ -176,6 +195,35 @@ public class XmppConnection implements Runnable {
} else if (nextTag.isStart("failure")) { } else if (nextTag.isStart("failure")) {
Element failure = tagReader.readElement(nextTag); Element failure = tagReader.readElement(nextTag);
changeStatus(Account.STATUS_UNAUTHORIZED); changeStatus(Account.STATUS_UNAUTHORIZED);
} else if (nextTag.isStart("enabled")) {
this.stanzasSent = 0;
Element enabled = tagReader.readElement(nextTag);
if ("true".equals(enabled.getAttribute("resume"))) {
this.streamId = enabled.getAttribute("id");
Log.d(LOGTAG,account.getJid()+": stream managment enabled (resumable)");
} else {
Log.d(LOGTAG,account.getJid()+": stream managment enabled");
}
this.stanzasReceived = 0;
RequestPacket r = new RequestPacket();
tagWriter.writeStanzaAsync(r);
} else if (nextTag.isStart("resumed")) {
tagReader.readElement(nextTag);
changeStatus(Account.STATUS_ONLINE);
Log.d(LOGTAG,account.getJid()+": session resumed");
} else if (nextTag.isStart("r")) {
tagReader.readElement(nextTag);
AckPacket ack = new AckPacket(this.stanzasReceived);
//Log.d(LOGTAG,ack.toString());
tagWriter.writeStanzaAsync(ack);
} else if (nextTag.isStart("a")) {
Element ack = tagReader.readElement(nextTag);
lastPaketReceived = SystemClock.elapsedRealtime();
int serverSequence = Integer.parseInt(ack.getAttribute("h"));
if (serverSequence>this.stanzasSent) {
this.stanzasSent = serverSequence;
}
//Log.d(LOGTAG,"server ack"+ack.toString()+" ("+this.stanzasSent+")");
} else if (nextTag.isStart("iq")) { } else if (nextTag.isStart("iq")) {
processIq(nextTag); processIq(nextTag);
} else if (nextTag.isStart("message")) { } else if (nextTag.isStart("message")) {
@ -221,6 +269,8 @@ public class XmppConnection implements Runnable {
} }
nextTag = tagReader.readTag(); nextTag = tagReader.readTag();
} }
++stanzasReceived;
lastPaketReceived = SystemClock.elapsedRealtime();
return element; return element;
} }
@ -271,7 +321,7 @@ public class XmppConnection implements Runnable {
} }
} }
private void sendStartTLS() { private void sendStartTLS() throws IOException {
Tag startTLS = Tag.empty("starttls"); Tag startTLS = Tag.empty("starttls");
startTLS.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-tls"); startTLS.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-tls");
tagWriter.writeTag(startTLS); tagWriter.writeTag(startTLS);
@ -378,25 +428,45 @@ public class XmppConnection implements Runnable {
} else if (this.streamFeatures.hasChild("mechanisms") } else if (this.streamFeatures.hasChild("mechanisms")
&& shouldAuthenticate) { && shouldAuthenticate) {
sendSaslAuth(); sendSaslAuth();
} } else if (this.streamFeatures.hasChild("sm") && streamId != null) {
if (this.streamFeatures.hasChild("bind") && shouldBind) { Log.d(LOGTAG,"found old stream id. trying to remuse");
ResumePacket resume = new ResumePacket(this.streamId,stanzasReceived);
this.tagWriter.writeStanzaAsync(resume);
} else if (this.streamFeatures.hasChild("bind") && shouldBind) {
sendBindRequest(); sendBindRequest();
if (this.streamFeatures.hasChild("session")) { if (this.streamFeatures.hasChild("session")) {
Log.d(LOGTAG,"sending session");
IqPacket startSession = new IqPacket(IqPacket.TYPE_SET); IqPacket startSession = new IqPacket(IqPacket.TYPE_SET);
Element session = new Element("session"); Element session = new Element("session");
session.setAttribute("xmlns", session.setAttribute("xmlns",
"urn:ietf:params:xml:ns:xmpp-session"); "urn:ietf:params:xml:ns:xmpp-session");
session.setContent(""); session.setContent("");
startSession.addChild(session); startSession.addChild(session);
sendIqPacket(startSession, null); this.sendIqPacket(startSession, null);
tagWriter.writeElement(startSession);
} }
Element presence = new Element("presence");
tagWriter.writeElement(presence);
} }
} }
private void sendInitialPresence() {
PresencePacket packet = new PresencePacket();
packet.setAttribute("from", account.getFullJid());
if (account.getKeys().has("pgp_signature")) {
try {
String signature = account.getKeys().getString("pgp_signature");
Element status = new Element("status");
status.setContent("online");
packet.addChild(status);
Element x = new Element("x");
x.setAttribute("xmlns", "jabber:x:signed");
x.setContent(signature);
packet.addChild(x);
} catch (JSONException e) {
//
}
}
this.sendPresencePacket(packet);
}
private void sendBindRequest() throws IOException { private void sendBindRequest() throws IOException {
IqPacket iq = new IqPacket(IqPacket.TYPE_SET); IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
Element bind = new Element("bind"); Element bind = new Element("bind");
@ -412,10 +482,15 @@ public class XmppConnection implements Runnable {
.getContent().split("/")[1]; .getContent().split("/")[1];
account.setResource(resource); account.setResource(resource);
account.setStatus(Account.STATUS_ONLINE); account.setStatus(Account.STATUS_ONLINE);
if (streamFeatures.hasChild("sm")) {
EnablePacket enable = new EnablePacket();
tagWriter.writeStanzaAsync(enable);
}
sendInitialPresence();
sendServiceDiscovery();
if (statusListener != null) { if (statusListener != null) {
statusListener.onStatusChanged(account); statusListener.onStatusChanged(account);
} }
sendServiceDiscovery();
} }
}); });
} }
@ -471,7 +546,7 @@ public class XmppConnection implements Runnable {
Log.d(LOGTAG, "processStreamError"); Log.d(LOGTAG, "processStreamError");
} }
private void sendStartStream() { private void sendStartStream() throws IOException {
Tag stream = Tag.start("stream:stream"); Tag stream = Tag.start("stream:stream");
stream.setAttribute("from", account.getJid()); stream.setAttribute("from", account.getJid());
stream.setAttribute("to", account.getServer()); stream.setAttribute("to", account.getServer());
@ -489,39 +564,52 @@ public class XmppConnection implements Runnable {
public void sendIqPacket(IqPacket packet, OnIqPacketReceived callback) { public void sendIqPacket(IqPacket packet, OnIqPacketReceived callback) {
String id = nextRandomId(); String id = nextRandomId();
packet.setAttribute("id", id); packet.setAttribute("id", id);
tagWriter.writeElement(packet); this.sendPacket(packet, callback);
if (callback != null) {
packetCallbacks.put(id, callback);
}
} }
public void sendMessagePacket(MessagePacket packet) { public void sendMessagePacket(MessagePacket packet) {
this.sendMessagePacket(packet, null); this.sendPacket(packet, null);
} }
public void sendMessagePacket(MessagePacket packet, public void sendMessagePacket(MessagePacket packet,
OnMessagePacketReceived callback) { OnMessagePacketReceived callback) {
String id = nextRandomId(); this.sendPacket(packet, callback);
packet.setAttribute("id", id);
tagWriter.writeElement(packet);
if (callback != null) {
packetCallbacks.put(id, callback);
}
} }
public void sendPresencePacket(PresencePacket packet) { public void sendPresencePacket(PresencePacket packet) {
this.sendPresencePacket(packet, null); this.sendPacket(packet, null);
} }
public PresencePacket sendPresencePacket(PresencePacket packet, public void sendPresencePacket(PresencePacket packet,
OnPresencePacketReceived callback) { OnPresencePacketReceived callback) {
String id = nextRandomId(); this.sendPacket(packet, callback);
packet.setAttribute("id", id); }
tagWriter.writeElement(packet);
private synchronized void sendPacket(final AbstractStanza packet, PacketReceived callback) {
// TODO dont increment stanza count if packet = request packet or ack;
++stanzasSent;
tagWriter.writeStanzaAsync(packet);
if (callback != null) { if (callback != null) {
packetCallbacks.put(id, callback); if (packet.getId()==null) {
packet.setId(nextRandomId());
}
packetCallbacks.put(packet.getId(), callback);
}
}
public void sendPing() {
if (streamFeatures.hasChild("sm")) {
Log.d(LOGTAG,account.getJid()+": sending r as ping");
tagWriter.writeStanzaAsync(new RequestPacket());
} else {
Log.d(LOGTAG,account.getJid()+": sending iq as ping");
IqPacket iq = new IqPacket(IqPacket.TYPE_GET);
Element ping = new Element("ping");
iq.setAttribute("from",account.getFullJid());
ping.setAttribute("xmlns", "urn:xmpp:ping");
iq.addChild(ping);
this.sendIqPacket(iq, null);
} }
return packet;
} }
public void setOnMessagePacketReceivedListener( public void setOnMessagePacketReceivedListener(
@ -547,8 +635,23 @@ public class XmppConnection implements Runnable {
this.tlsListener = listener; this.tlsListener = listener;
} }
public void disconnect() { public void disconnect(boolean force) {
Log.d(LOGTAG,"disconnecting");
try {
if (force) {
socket.close();
}
tagWriter.finish();
while(!tagWriter.finished()) {
Log.d(LOGTAG,"not yet finished");
Thread.sleep(100);
}
tagWriter.writeTag(Tag.end("stream:stream")); tagWriter.writeTag(Tag.end("stream:stream"));
} catch (IOException e) {
Log.d(LOGTAG,"io exception during disconnect");
} catch (InterruptedException e) {
Log.d(LOGTAG,"interupted while waiting for disconnect");
}
} }
public boolean hasFeatureRosterManagment() { public boolean hasFeatureRosterManagment() {
@ -558,4 +661,8 @@ public class XmppConnection implements Runnable {
return this.streamFeatures.hasChild("ver"); return this.streamFeatures.hasChild("ver");
} }
} }
public void r() {
this.tagWriter.writeStanzaAsync(new RequestPacket());
}
} }

View File

@ -0,0 +1,34 @@
package eu.siacs.conversations.xmpp.stanzas;
import eu.siacs.conversations.xml.Element;
public class AbstractStanza extends Element {
protected AbstractStanza(String name) {
super(name);
}
public String getTo() {
return getAttribute("to");
}
public String getFrom() {
return getAttribute("from");
}
public String getId() {
return this.getAttribute("id");
}
public void setTo(String to) {
setAttribute("to", to);
}
public void setFrom(String from) {
setAttribute("from",from);
}
public void setId(String id) {
setAttribute("id",id);
}
}

View File

@ -1,8 +1,7 @@
package eu.siacs.conversations.xmpp; package eu.siacs.conversations.xmpp.stanzas;
import eu.siacs.conversations.xml.Element;
public class IqPacket extends Element { public class IqPacket extends AbstractStanza {
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,8 +32,4 @@ public class IqPacket extends Element {
super("iq"); super("iq");
} }
public String getId() {
return this.getAttribute("id");
}
} }

View File

@ -1,30 +1,18 @@
package eu.siacs.conversations.xmpp; package eu.siacs.conversations.xmpp.stanzas;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
public class MessagePacket extends Element { public class MessagePacket extends AbstractStanza {
public static final int TYPE_CHAT = 0; public static final int TYPE_CHAT = 0;
public static final int TYPE_UNKNOWN = 1; public static final int TYPE_UNKNOWN = 1;
public static final int TYPE_NO = 2; public static final int TYPE_NO = 2;
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;
private MessagePacket(String name) {
super(name);
}
public MessagePacket() { public MessagePacket() {
super("message"); super("message");
} }
public String getTo() {
return getAttribute("to");
}
public String getFrom() {
return getAttribute("from");
}
public String getBody() { public String getBody() {
Element body = this.findChild("body"); Element body = this.findChild("body");
if (body!=null) { if (body!=null) {
@ -34,14 +22,6 @@ public class MessagePacket extends Element {
} }
} }
public void setTo(String to) {
setAttribute("to", to);
}
public void setFrom(String from) {
setAttribute("from",from);
}
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");

View File

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

View File

@ -0,0 +1,13 @@
package eu.siacs.conversations.xmpp.stanzas.streammgmt;
import eu.siacs.conversations.xmpp.stanzas.AbstractStanza;
public class AckPacket extends AbstractStanza {
public AckPacket(int sequence) {
super("a");
this.setAttribute("xmlns","urn:xmpp:sm:3");
this.setAttribute("h", ""+sequence);
}
}

View File

@ -0,0 +1,13 @@
package eu.siacs.conversations.xmpp.stanzas.streammgmt;
import eu.siacs.conversations.xmpp.stanzas.AbstractStanza;
public class EnablePacket extends AbstractStanza {
public EnablePacket() {
super("enable");
this.setAttribute("xmlns","urn:xmpp:sm:3");
this.setAttribute("resume", "true");
}
}

View File

@ -0,0 +1,12 @@
package eu.siacs.conversations.xmpp.stanzas.streammgmt;
import eu.siacs.conversations.xmpp.stanzas.AbstractStanza;
public class RequestPacket extends AbstractStanza {
public RequestPacket() {
super("r");
this.setAttribute("xmlns","urn:xmpp:sm:3");
}
}

View File

@ -0,0 +1,14 @@
package eu.siacs.conversations.xmpp.stanzas.streammgmt;
import eu.siacs.conversations.xmpp.stanzas.AbstractStanza;
public class ResumePacket extends AbstractStanza {
public ResumePacket(String id, int sequence) {
super("resume");
this.setAttribute("xmlns","urn:xmpp:sm:3");
this.setAttribute("previd", id);
this.setAttribute("h", ""+sequence);
}
}