diff options
Diffstat (limited to 'src/eu/siacs/conversations/services/XmppConnectionService.java')
-rw-r--r-- | src/eu/siacs/conversations/services/XmppConnectionService.java | 804 |
1 files changed, 365 insertions, 439 deletions
diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java index 3db52753..0ac1adf7 100644 --- a/src/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/eu/siacs/conversations/services/XmppConnectionService.java @@ -2,6 +2,7 @@ package eu.siacs.conversations.services; import java.security.SecureRandom; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; @@ -9,6 +10,7 @@ import java.util.Hashtable; import java.util.List; import java.util.Locale; import java.util.TimeZone; +import java.util.concurrent.CopyOnWriteArrayList; import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpServiceConnection; @@ -18,6 +20,7 @@ import net.java.otr4j.session.Session; import net.java.otr4j.session.SessionStatus; import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.entities.Bookmark; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; @@ -25,12 +28,12 @@ import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.MucOptions.OnRenameListener; import eu.siacs.conversations.entities.Presences; import eu.siacs.conversations.generator.MessageGenerator; +import eu.siacs.conversations.generator.PresenceGenerator; +import eu.siacs.conversations.parser.IqParser; import eu.siacs.conversations.parser.MessageParser; import eu.siacs.conversations.parser.PresenceParser; import eu.siacs.conversations.persistance.DatabaseBackend; import eu.siacs.conversations.persistance.FileBackend; -import eu.siacs.conversations.ui.OnAccountListChangedListener; -import eu.siacs.conversations.ui.OnConversationListChangedListener; import eu.siacs.conversations.ui.UiCallback; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.ExceptionHelper; @@ -42,8 +45,6 @@ import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.OnBindListener; import eu.siacs.conversations.xmpp.OnContactStatusChanged; import eu.siacs.conversations.xmpp.OnIqPacketReceived; -import eu.siacs.conversations.xmpp.OnMessagePacketReceived; -import eu.siacs.conversations.xmpp.OnPresencePacketReceived; import eu.siacs.conversations.xmpp.OnStatusChanged; import eu.siacs.conversations.xmpp.OnTLSExceptionReceived; import eu.siacs.conversations.xmpp.XmppConnection; @@ -86,28 +87,31 @@ public class XmppConnectionService extends Service { private static final int PING_MIN_INTERVAL = 30; private static final int PING_TIMEOUT = 10; private static final int CONNECT_TIMEOUT = 90; - private static final long CARBON_GRACE_PERIOD = 60000L; + public static final long CARBON_GRACE_PERIOD = 60000L; private static String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts"; private MessageParser mMessageParser = new MessageParser(this); private PresenceParser mPresenceParser = new PresenceParser(this); + private IqParser mIqParser = new IqParser(this); private MessageGenerator mMessageGenerator = new MessageGenerator(); - + private PresenceGenerator mPresenceGenerator = new PresenceGenerator(); + private List<Account> accounts; - private List<Conversation> conversations = null; + private CopyOnWriteArrayList<Conversation> conversations = null; private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager( this); - private OnConversationListChangedListener convChangedListener = null; + private OnConversationUpdate mOnConversationUpdate = null; private int convChangedListenerCount = 0; - private OnAccountListChangedListener accountChangedListener = null; + private OnAccountUpdate mOnAccountUpdate = null; + private OnRosterUpdate mOnRosterUpdate = null; private OnTLSExceptionReceived tlsException = null; public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() { @Override public void onContactStatusChanged(Contact contact, boolean online) { - Conversation conversation = findActiveConversation(contact); + Conversation conversation = find(getConversations(),contact); if (conversation != null) { conversation.endOtrIfNeeded(); if (online && (contact.getPresences().size() == 1)) { @@ -124,8 +128,6 @@ public class XmppConnectionService extends Service { private SecureRandom mRandom; - private long lastCarbonMessageReceived = -CARBON_GRACE_PERIOD; - private ContentObserver contactObserver = new ContentObserver(null) { @Override public void onChange(boolean selfChange) { @@ -138,103 +140,20 @@ public class XmppConnectionService extends Service { }; private final IBinder mBinder = new XmppConnectionBinder(); - private OnMessagePacketReceived messageListener = new OnMessagePacketReceived() { - - @Override - public void onMessagePacketReceived(Account account, - MessagePacket packet) { - Message message = null; - boolean notify = true; - if (getPreferences().getBoolean( - "notification_grace_period_after_carbon_received", true)) { - notify = (SystemClock.elapsedRealtime() - lastCarbonMessageReceived) > CARBON_GRACE_PERIOD; - } - - if ((packet.getType() == MessagePacket.TYPE_CHAT)) { - if ((packet.getBody() != null) - && (packet.getBody().startsWith("?OTR"))) { - message = mMessageParser.parseOtrChat(packet, account); - if (message != null) { - message.markUnread(); - } - } else if (packet.hasChild("body")) { - message = mMessageParser.parseChat(packet, account); - message.markUnread(); - } else if (packet.hasChild("received") - || (packet.hasChild("sent"))) { - message = mMessageParser - .parseCarbonMessage(packet, account); - if (message != null) { - if (message.getStatus() == Message.STATUS_SEND) { - lastCarbonMessageReceived = SystemClock - .elapsedRealtime(); - notify = false; - message.getConversation().markRead(); - } else { - message.markUnread(); - } - } - } - - } else if (packet.getType() == MessagePacket.TYPE_GROUPCHAT) { - message = mMessageParser.parseGroupchat(packet, account); - if (message != null) { - if (message.getStatus() == Message.STATUS_RECIEVED) { - message.markUnread(); - } else { - message.getConversation().markRead(); - notify = false; - } - } - } else if (packet.getType() == MessagePacket.TYPE_ERROR) { - mMessageParser.parseError(packet, account); - return; - } else if (packet.getType() == MessagePacket.TYPE_NORMAL) { - mMessageParser.parseNormal(packet, account); - } - if ((message == null) || (message.getBody() == null)) { - return; - } - if ((confirmMessages()) && ((packet.getId() != null))) { - MessagePacket receivedPacket = new MessagePacket(); - receivedPacket.setType(MessagePacket.TYPE_NORMAL); - receivedPacket.setTo(message.getCounterpart()); - receivedPacket.setFrom(account.getFullJid()); - if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) { - Element received = receivedPacket.addChild("received", - "urn:xmpp:chat-markers:0"); - received.setAttribute("id", packet.getId()); - account.getXmppConnection().sendMessagePacket( - receivedPacket); - } else if (packet.hasChild("request", "urn:xmpp:receipts")) { - Element received = receivedPacket.addChild("received", - "urn:xmpp:receipts"); - received.setAttribute("id", packet.getId()); - account.getXmppConnection().sendMessagePacket( - receivedPacket); - } - } - Conversation conversation = message.getConversation(); - conversation.getMessages().add(message); - if (packet.getType() != MessagePacket.TYPE_ERROR) { - databaseBackend.createMessage(message); - } - if (convChangedListener != null) { - convChangedListener.onConversationListChanged(); - } else { - UIHelper.updateNotification(getApplicationContext(), - getConversations(), message.getConversation(), notify); - } - } - }; private OnStatusChanged statusListener = new OnStatusChanged() { @Override public void onStatusChanged(Account account) { - if (accountChangedListener != null) { - accountChangedListener.onAccountListChangedListener(); + if (mOnAccountUpdate != null) { + mOnAccountUpdate.onAccountUpdate();; } if (account.getStatus() == Account.STATUS_ONLINE) { + for(Conversation conversation : account.pendingConferenceLeaves) { + leaveMuc(conversation); + } + for(Conversation conversation : account.pendingConferenceJoins) { + joinMuc(conversation); + } mJingleConnectionManager.cancelInTransmission(); List<Conversation> conversations = getConversations(); for (int i = 0; i < conversations.size(); ++i) { @@ -269,73 +188,6 @@ public class XmppConnectionService extends Service { } }; - private OnPresencePacketReceived presenceListener = new OnPresencePacketReceived() { - - @Override - public void onPresencePacketReceived(final Account account, - PresencePacket packet) { - if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) { - mPresenceParser.parseConferencePresence(packet, account); - } else if (packet.hasChild("x", "http://jabber.org/protocol/muc")) { - mPresenceParser.parseConferencePresence(packet, account); - } else { - mPresenceParser.parseContactPresence(packet, account); - } - } - }; - - private OnIqPacketReceived unknownIqListener = new OnIqPacketReceived() { - - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.hasChild("query", "jabber:iq:roster")) { - String from = packet.getFrom(); - if ((from == null) || (from.equals(account.getJid()))) { - Element query = packet.findChild("query"); - processRosterItems(account, query); - } else { - Log.d(LOGTAG, "unauthorized roster push from: " + from); - } - } else if (packet - .hasChild("open", "http://jabber.org/protocol/ibb") - || packet - .hasChild("data", "http://jabber.org/protocol/ibb")) { - XmppConnectionService.this.mJingleConnectionManager - .deliverIbbPacket(account, packet); - } else if (packet.hasChild("query", - "http://jabber.org/protocol/disco#info")) { - IqPacket iqResponse = packet - .generateRespone(IqPacket.TYPE_RESULT); - Element query = iqResponse.addChild("query", - "http://jabber.org/protocol/disco#info"); - query.addChild("feature").setAttribute("var", - "urn:xmpp:jingle:1"); - query.addChild("feature").setAttribute("var", - "urn:xmpp:jingle:apps:file-transfer:3"); - query.addChild("feature").setAttribute("var", - "urn:xmpp:jingle:transports:s5b:1"); - query.addChild("feature").setAttribute("var", - "urn:xmpp:jingle:transports:ibb:1"); - if (confirmMessages()) { - query.addChild("feature").setAttribute("var", - "urn:xmpp:receipts"); - } - account.getXmppConnection().sendIqPacket(iqResponse, null); - } else { - if ((packet.getType() == IqPacket.TYPE_GET) - || (packet.getType() == IqPacket.TYPE_SET)) { - IqPacket response = packet - .generateRespone(IqPacket.TYPE_ERROR); - Element error = response.addChild("error"); - error.setAttribute("type", "cancel"); - error.addChild("feature-not-implemented", - "urn:ietf:params:xml:ns:xmpp-stanzas"); - account.getXmppConnection().sendIqPacket(response, null); - } - } - } - }; - private OnJinglePacketReceived jingleListener = new OnJinglePacketReceived() { @Override @@ -350,6 +202,21 @@ public class XmppConnectionService extends Service { private PendingIntent pendingPingIntent = null; private WakeLock wakeLock; private PowerManager pm; + private OnBindListener mOnBindListener = new OnBindListener() { + + @Override + public void onBind(final Account account) { + account.getRoster().clearPresences(); + account.clearPresences(); // self presences + account.pendingConferenceJoins.clear(); + account.pendingConferenceLeaves.clear(); + fetchRosterFromServer(account); + fetchBookmarks(account); + sendPresencePacket(account, mPresenceGenerator.sendPresence(account)); + connectMultiModeConversations(account); + updateConversationUi(); + } + }; public PgpEngine getPgpEngine() { if (pgpServiceConnection.isBound()) { @@ -401,39 +268,12 @@ public class XmppConnectionService extends Service { return message; } - public Conversation findMuc(String name, Account account) { - for (Conversation conversation : this.conversations) { - if (conversation.getContactJid().split("/")[0].equals(name) - && (conversation.getAccount() == account)) { - return conversation; - } - } - return null; + public Conversation find(Bookmark bookmark) { + return find(bookmark.getAccount(),bookmark.getJid()); } - - private void processRosterItems(Account account, Element elements) { - String version = elements.getAttribute("ver"); - if (version != null) { - account.getRoster().setVersion(version); - } - for (Element item : elements.getChildren()) { - if (item.getName().equals("item")) { - String jid = item.getAttribute("jid"); - String name = item.getAttribute("name"); - String subscription = item.getAttribute("subscription"); - Contact contact = account.getRoster().getContact(jid); - if (!contact.getOption(Contact.Options.DIRTY_PUSH)) { - contact.setServerName(name); - } - if (subscription.equals("remove")) { - contact.resetOption(Contact.Options.IN_ROSTER); - contact.resetOption(Contact.Options.DIRTY_DELETE); - } else { - contact.setOption(Contact.Options.IN_ROSTER); - contact.parseSubscriptionFromElement(item); - } - } - } + + public Conversation find(Account account, String jid) { + return find(getConversations(),account,jid); } public class XmppConnectionBinder extends Binder { @@ -508,13 +348,16 @@ public class XmppConnectionService extends Service { // in any case. reschedule wakup call this.scheduleWakeupCall(PING_MAX_INTERVAL, true); } - if (accountChangedListener != null) { - accountChangedListener.onAccountListChangedListener(); + if (mOnAccountUpdate != null) { + mOnAccountUpdate.onAccountUpdate(); } } } if (wakeLock.isHeld()) { - try { wakeLock.release();} catch (RuntimeException re) {} + try { + wakeLock.release(); + } catch (RuntimeException re) { + } } return START_STICKY; } @@ -618,11 +461,11 @@ public class XmppConnectionService extends Service { account.setResource(sharedPref.getString("resource", "mobile") .toLowerCase(Locale.getDefault())); XmppConnection connection = new XmppConnection(account, this); - connection.setOnMessagePacketReceivedListener(this.messageListener); + connection.setOnMessagePacketReceivedListener(this.mMessageParser); connection.setOnStatusChangedListener(this.statusListener); - connection.setOnPresencePacketReceivedListener(this.presenceListener); + connection.setOnPresencePacketReceivedListener(this.mPresenceParser); connection - .setOnUnregisteredIqPacketReceivedListener(this.unknownIqListener); + .setOnUnregisteredIqPacketReceivedListener(this.mIqParser); connection.setOnJinglePacketReceivedListener(this.jingleListener); connection .setOnTLSExceptionReceivedListener(new OnTLSExceptionReceived() { @@ -637,20 +480,7 @@ public class XmppConnectionService extends Service { } } }); - connection.setOnBindListener(new OnBindListener() { - - @Override - public void onBind(final Account account) { - account.getRoster().clearPresences(); - account.clearPresences(); // self presences - fetchRosterFromServer(account); - sendPresence(account); - connectMultiModeConversations(account); - if (convChangedListener != null) { - convChangedListener.onConversationListChanged(); - } - } - }); + connection.setOnBindListener(this.mOnBindListener); return connection; } @@ -691,25 +521,24 @@ public class XmppConnectionService extends Service { message.setStatus(Message.STATUS_WAITING); } else if (conv.hasValidOtrSession() && conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) { - message.setPresence(conv.getOtrSession().getSessionID().getUserID()); - try { - packet = mMessageGenerator.generateOtrChat(message); - send = true; - message.setStatus(Message.STATUS_SEND); - } catch (OtrException e) { - Log.e(LOGTAG,"error generating otr packet"); - packet = null; - } + message.setPresence(conv.getOtrSession().getSessionID() + .getUserID()); + packet = mMessageGenerator.generateOtrChat(message); + send = true; + message.setStatus(Message.STATUS_SEND); + } else if (message.getPresence() == null) { message.setStatus(Message.STATUS_WAITING); } } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { message.getConversation().endOtrIfNeeded(); + message.getConversation().failWaitingOtrMessages(); packet = mMessageGenerator.generatePgpChat(message); message.setStatus(Message.STATUS_SEND); send = true; } else { message.getConversation().endOtrIfNeeded(); + message.getConversation().failWaitingOtrMessages(); if (message.getConversation().getMode() == Conversation.MODE_SINGLE) { message.setStatus(Message.STATUS_SEND); } @@ -744,11 +573,9 @@ public class XmppConnectionService extends Service { databaseBackend.createMessage(message); } conv.getMessages().add(message); - if (convChangedListener != null) { - convChangedListener.onConversationListChanged(); - } + updateConversationUi(); if ((send) && (packet != null)) { - account.getXmppConnection().sendMessagePacket(packet); + sendMessagePacket(account, packet); } } @@ -782,9 +609,11 @@ public class XmppConnectionService extends Service { } } } else { - if (message.getConversation().getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) { + if (message.getConversation().getOtrSession() + .getSessionStatus() == SessionStatus.ENCRYPTED) { if (message.getType() == Message.TYPE_TEXT) { - packet = mMessageGenerator.generateOtrChat(message,true); + packet = mMessageGenerator.generateOtrChat(message, + true); } else if (message.getType() == Message.TYPE_IMAGE) { mJingleConnectionManager.createNewConnection(message); } @@ -792,9 +621,10 @@ public class XmppConnectionService extends Service { } } else if (message.getType() == Message.TYPE_TEXT) { if (message.getEncryption() == Message.ENCRYPTION_NONE) { - packet = mMessageGenerator.generateChat(message,true); - } else if ((message.getEncryption() == Message.ENCRYPTION_DECRYPTED)||(message.getEncryption() == Message.ENCRYPTION_PGP)) { - packet = mMessageGenerator.generatePgpChat(message,true); + packet = mMessageGenerator.generateChat(message, true); + } else if ((message.getEncryption() == Message.ENCRYPTION_DECRYPTED) + || (message.getEncryption() == Message.ENCRYPTION_PGP)) { + packet = mMessageGenerator.generatePgpChat(message, true); } } else if (message.getType() == Message.TYPE_IMAGE) { Presences presences = message.getConversation().getContact() @@ -813,7 +643,7 @@ public class XmppConnectionService extends Service { } } if (packet != null) { - account.getXmppConnection().sendMessagePacket(packet); + sendMessagePacket(account,packet); markMessage(message, Message.STATUS_SEND); } } @@ -834,14 +664,60 @@ public class XmppConnectionService extends Service { @Override public void onIqPacketReceived(final Account account, IqPacket packet) { - Element roster = packet.findChild("query"); - if (roster != null) { + Element query = packet.findChild("query"); + if (query != null) { account.getRoster().markAllAsNotInRoster(); - processRosterItems(account, roster); + mIqParser.rosterItems(account, query); } } }); } + + public void fetchBookmarks(Account account) { + IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET); + Element query = iqPacket.query("jabber:iq:private"); + query.addChild("storage", "storage:bookmarks"); + OnIqPacketReceived callback = new OnIqPacketReceived() { + + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + Element query = packet.query(); + List<Bookmark> bookmarks = new ArrayList<Bookmark>(); + Element storage = query.findChild("storage", "storage:bookmarks"); + if (storage!=null) { + for(Element item : storage.getChildren()) { + if (item.getName().equals("conference")) { + Bookmark bookmark = Bookmark.parse(item,account); + bookmarks.add(bookmark); + Conversation conversation = find(bookmark); + if (conversation!=null) { + conversation.setBookmark(bookmark); + } else { + if (bookmark.autojoin()) { + conversation = findOrCreateConversation(account, bookmark.getJid(), true); + conversation.setBookmark(bookmark); + joinMuc(conversation); + } + } + } + } + } + account.setBookmarks(bookmarks); + } + }; + sendIqPacket(account, iqPacket, callback); + + } + + public void pushBookmarks(Account account) { + IqPacket iqPacket = new IqPacket(IqPacket.TYPE_SET); + Element query = iqPacket.query("jabber:iq:private"); + Element storage = query.addChild("storage", "storage:bookmarks"); + for(Bookmark bookmark : account.getBookmarks()) { + storage.addChild(bookmark.toElement()); + } + sendIqPacket(account, iqPacket,null); + } private void mergePhoneContactsWithRoster() { PhoneHelper.loadPhoneContacts(getApplicationContext(), @@ -885,7 +761,14 @@ public class XmppConnectionService extends Service { conv.setMessages(databaseBackend.getMessages(conv, 50)); } } - Collections.sort(this.conversations, new Comparator<Conversation>() { + + return this.conversations; + } + + public void populateWithOrderedConversations(List<Conversation> list) { + list.clear(); + list.addAll(getConversations()); + Collections.sort(list, new Comparator<Conversation>() { @Override public int compare(Conversation lhs, Conversation rhs) { Message left = lhs.getLatestMessage(); @@ -899,7 +782,6 @@ public class XmppConnectionService extends Service { } } }); - return this.conversations; } public List<Message> getMoreMessages(Conversation conversation, @@ -916,8 +798,8 @@ public class XmppConnectionService extends Service { return this.accounts; } - public Conversation findActiveConversation(Contact contact) { - for (Conversation conversation : this.getConversations()) { + public Conversation find(List<Conversation> haystack, Contact contact) { + for (Conversation conversation : haystack) { if (conversation.getContact() == contact) { return conversation; } @@ -925,16 +807,24 @@ public class XmppConnectionService extends Service { return null; } + public Conversation find(List<Conversation> haystack, Account account, String jid) { + for (Conversation conversation : haystack) { + if ((conversation.getAccount().equals(account)) + && (conversation.getContactJid().split("/")[0].equals(jid))) { + return conversation; + } + } + return null; + } + + public Conversation findOrCreateConversation(Account account, String jid, boolean muc) { - for (Conversation conv : this.getConversations()) { - if ((conv.getAccount().equals(account)) - && (conv.getContactJid().split("/")[0].equals(jid))) { - return conv; - } + Conversation conversation = find(account, jid); + if (conversation != null) { + return conversation; } - Conversation conversation = databaseBackend.findConversation(account, - jid); + conversation = databaseBackend.findConversation(account,jid); if (conversation != null) { conversation.setStatus(Conversation.STATUS_AVAILABLE); conversation.setAccount(account); @@ -964,36 +854,31 @@ public class XmppConnectionService extends Service { this.databaseBackend.createConversation(conversation); } this.conversations.add(conversation); - if ((account.getStatus() == Account.STATUS_ONLINE) - && (conversation.getMode() == Conversation.MODE_MULTI)) { - joinMuc(conversation); - } - if (this.convChangedListener != null) { - this.convChangedListener.onConversationListChanged(); - } + updateConversationUi(); return conversation; } public void archiveConversation(Conversation conversation) { if (conversation.getMode() == Conversation.MODE_MULTI) { + Bookmark bookmark = conversation.getBookmark(); + if (bookmark!=null && bookmark.autojoin()) { + bookmark.setAutojoin(false); + pushBookmarks(bookmark.getAccount()); + } leaveMuc(conversation); } else { conversation.endOtrIfNeeded(); } this.databaseBackend.updateConversation(conversation); this.conversations.remove(conversation); - if (this.convChangedListener != null) { - this.convChangedListener.onConversationListChanged(); - } + updateConversationUi(); } public void clearConversationHistory(Conversation conversation) { this.databaseBackend.deleteMessagesInConversation(conversation); this.fileBackend.removeFiles(conversation); conversation.getMessages().clear(); - if (this.convChangedListener != null) { - this.convChangedListener.onConversationListChanged(); - } + updateConversationUi(); } public int getConversationCount() { @@ -1004,17 +889,14 @@ public class XmppConnectionService extends Service { databaseBackend.createAccount(account); this.accounts.add(account); this.reconnectAccount(account, false); - if (accountChangedListener != null) - accountChangedListener.onAccountListChangedListener(); + updateAccountUi(); } public void updateAccount(Account account) { this.statusListener.onStatusChanged(account); databaseBackend.updateAccount(account); reconnectAccount(account, false); - if (accountChangedListener != null) { - accountChangedListener.onAccountListChangedListener(); - } + updateAccountUi(); UIHelper.showErrorNotification(getApplicationContext(), getAccounts()); } @@ -1024,32 +906,37 @@ public class XmppConnectionService extends Service { } databaseBackend.deleteAccount(account); this.accounts.remove(account); - if (accountChangedListener != null) { - accountChangedListener.onAccountListChangedListener(); - } + updateAccountUi(); UIHelper.showErrorNotification(getApplicationContext(), getAccounts()); } public void setOnConversationListChangedListener( - OnConversationListChangedListener listener) { - this.convChangedListener = listener; + OnConversationUpdate listener) { + this.mOnConversationUpdate = listener; this.convChangedListenerCount++; } public void removeOnConversationListChangedListener() { this.convChangedListenerCount--; if (this.convChangedListenerCount == 0) { - this.convChangedListener = null; + this.mOnConversationUpdate = null; } } - public void setOnAccountListChangedListener( - OnAccountListChangedListener listener) { - this.accountChangedListener = listener; + public void setOnAccountListChangedListener(OnAccountUpdate listener) { + this.mOnAccountUpdate = listener; } public void removeOnAccountListChangedListener() { - this.accountChangedListener = null; + this.mOnAccountUpdate = null; + } + + public void setOnRosterUpdateListener(OnRosterUpdate listener) { + this.mOnRosterUpdate = listener; + } + + public void removeOnRosterUpdateListener() { + this.mOnRosterUpdate = null; } public void connectMultiModeConversations(Account account) { @@ -1065,33 +952,35 @@ public class XmppConnectionService extends Service { public void joinMuc(Conversation conversation) { Account account = conversation.getAccount(); - String[] mucParts = conversation.getContactJid().split("/"); - String muc; - String nick; - if (mucParts.length == 2) { - muc = mucParts[0]; - nick = mucParts[1]; + account.pendingConferenceJoins.remove(conversation); + account.pendingConferenceLeaves.remove(conversation); + if (account.getStatus() == Account.STATUS_ONLINE) { + Log.d(LOGTAG,"joining conversation "+conversation.getContactJid()); + String nick = conversation.getMucOptions().getProposedNick(); + conversation.getMucOptions().setJoinNick(nick); + PresencePacket packet = new PresencePacket(); + packet.setAttribute("to",conversation.getMucOptions().getJoinJid()); + Element x = new Element("x"); + x.setAttribute("xmlns", "http://jabber.org/protocol/muc"); + String sig = account.getPgpSignature(); + if (sig != null) { + packet.addChild("status").setContent("online"); + packet.addChild("x", "jabber:x:signed").setContent(sig); + } + if (conversation.getMessages().size() != 0) { + final SimpleDateFormat mDateFormat = new SimpleDateFormat( + "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US); + mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + Date date = new Date( + conversation.getLatestMessage().getTimeSent() + 1000); + x.addChild("history").setAttribute("since", + mDateFormat.format(date)); + } + packet.addChild(x); + sendPresencePacket(account, packet); } else { - muc = mucParts[0]; - nick = account.getUsername(); - } - PresencePacket packet = new PresencePacket(); - packet.setAttribute("to", muc + "/" + nick); - Element x = new Element("x"); - x.setAttribute("xmlns", "http://jabber.org/protocol/muc"); - String sig = account.getPgpSignature(); - if (sig != null) { - packet.addChild("status").setContent("online"); - packet.addChild("x", "jabber:x:signed").setContent(sig); - } - if (conversation.getMessages().size() != 0) { - final SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",Locale.US); - mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - Date date = new Date(conversation.getLatestMessage().getTimeSent() + 1000); - x.addChild("history").setAttribute("since",mDateFormat.format(date)); + account.pendingConferenceJoins.add(conversation); } - packet.addChild(x); - account.getXmppConnection().sendPresencePacket(packet); } private OnRenameListener renameListener = null; @@ -1102,6 +991,7 @@ public class XmppConnectionService extends Service { public void renameInMuc(final Conversation conversation, final String nick) { final MucOptions options = conversation.getMucOptions(); + options.setJoinNick(nick); if (options.online()) { Account account = conversation.getAccount(); options.setOnRenameListener(new OnRenameListener() { @@ -1112,17 +1002,19 @@ public class XmppConnectionService extends Service { renameListener.onRename(success); } if (success) { - String jid = conversation.getContactJid().split("/")[0] - + "/" + nick; - conversation.setContactJid(jid); + conversation.setContactJid(conversation.getMucOptions().getJoinJid()); databaseBackend.updateConversation(conversation); + Bookmark bookmark = conversation.getBookmark(); + if (bookmark!=null) { + bookmark.setNick(nick); + pushBookmarks(bookmark.getAccount()); + } } } }); options.flagAboutToRename(); PresencePacket packet = new PresencePacket(); - packet.setAttribute("to", - conversation.getContactJid().split("/")[0] + "/" + nick); + packet.setAttribute("to",options.getJoinJid()); packet.setAttribute("from", conversation.getAccount().getFullJid()); String sig = account.getPgpSignature(); @@ -1130,29 +1022,37 @@ public class XmppConnectionService extends Service { packet.addChild("status").setContent("online"); packet.addChild("x", "jabber:x:signed").setContent(sig); } - - account.getXmppConnection().sendPresencePacket(packet, null); + sendPresencePacket(account,packet); } else { - String jid = conversation.getContactJid().split("/")[0] + "/" - + nick; - conversation.setContactJid(jid); + conversation.setContactJid(options.getJoinJid()); databaseBackend.updateConversation(conversation); if (conversation.getAccount().getStatus() == Account.STATUS_ONLINE) { + Bookmark bookmark = conversation.getBookmark(); + if (bookmark!=null) { + bookmark.setNick(nick); + pushBookmarks(bookmark.getAccount()); + } joinMuc(conversation); } } } public void leaveMuc(Conversation conversation) { - PresencePacket packet = new PresencePacket(); - packet.setAttribute("to", conversation.getContactJid().split("/")[0] - + "/" + conversation.getMucOptions().getNick()); - packet.setAttribute("from", conversation.getAccount().getFullJid()); - packet.setAttribute("type", "unavailable"); - Log.d(LOGTAG, "send leaving muc " + packet); - conversation.getAccount().getXmppConnection() - .sendPresencePacket(packet); - conversation.getMucOptions().setOffline(); + Account account = conversation.getAccount(); + account.pendingConferenceJoins.remove(conversation); + account.pendingConferenceLeaves.remove(conversation); + if (account.getStatus() == Account.STATUS_ONLINE) { + PresencePacket packet = new PresencePacket(); + packet.setAttribute("to", conversation.getMucOptions().getJoinJid()); + packet.setAttribute("from", conversation.getAccount().getFullJid()); + packet.setAttribute("type", "unavailable"); + sendPresencePacket(conversation.getAccount(),packet); + conversation.getMucOptions().setOffline(); + conversation.deregisterWithBookmark(); + Log.d(LOGTAG,conversation.getAccount().getJid()+" leaving muc "+conversation.getContactJid()); + } else { + account.pendingConferenceLeaves.add(conversation); + } } public void disconnect(Account account, boolean force) { @@ -1219,18 +1119,19 @@ public class XmppConnectionService extends Service { && (msg.getEncryption() == Message.ENCRYPTION_OTR)) { msg.setPresence(otrSession.getSessionID().getUserID()); if (msg.getType() == Message.TYPE_TEXT) { - MessagePacket outPacket = mMessageGenerator.generateOtrChat(msg,true); - if (outPacket!=null) { + MessagePacket outPacket = mMessageGenerator + .generateOtrChat(msg, true); + if (outPacket != null) { msg.setStatus(Message.STATUS_SEND); databaseBackend.updateMessage(msg); - account.getXmppConnection().sendMessagePacket(outPacket); + sendMessagePacket(account,outPacket); } } else if (msg.getType() == Message.TYPE_IMAGE) { mJingleConnectionManager.createNewConnection(msg); } } } - updateUi(conversation, false); + notifyUi(conversation, false); } public boolean renewSymmetricKey(Conversation conversation) { @@ -1250,7 +1151,7 @@ public class XmppConnectionService extends Service { packet.setBody(otrSession .transformSending(CryptoHelper.FILETRANSFER + CryptoHelper.bytesToHex(symmetricKey))); - account.getXmppConnection().sendMessagePacket(packet); + sendMessagePacket(account,packet); conversation.setSymmetricKey(symmetricKey); return true; } catch (OtrException e) { @@ -1262,26 +1163,28 @@ public class XmppConnectionService extends Service { public void pushContactToServer(Contact contact) { contact.resetOption(Contact.Options.DIRTY_DELETE); + contact.setOption(Contact.Options.DIRTY_PUSH); Account account = contact.getAccount(); if (account.getStatus() == Account.STATUS_ONLINE) { + boolean ask = contact.getOption(Contact.Options.ASKING); + boolean sendUpdates = contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST) + && contact.getOption(Contact.Options.PREEMPTIVE_GRANT); IqPacket iq = new IqPacket(IqPacket.TYPE_SET); iq.query("jabber:iq:roster").addChild(contact.asElement()); account.getXmppConnection().sendIqPacket(iq, null); - if (contact.getOption(Contact.Options.ASKING)) { - requestPresenceUpdatesFrom(contact); + if (sendUpdates) { + sendPresencePacket(account, mPresenceGenerator.sendPresenceUpdatesTo(contact)); } - if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) { - Log.d("xmppService", "contact had pending subscription"); - sendPresenceUpdatesTo(contact); + if (ask) { + sendPresencePacket(account, mPresenceGenerator.requestPresenceUpdatesFrom(contact)); } - contact.resetOption(Contact.Options.DIRTY_PUSH); - } else { - contact.setOption(Contact.Options.DIRTY_PUSH); } } public void deleteContactOnServer(Contact contact) { + contact.resetOption(Contact.Options.PREEMPTIVE_GRANT); contact.resetOption(Contact.Options.DIRTY_PUSH); + contact.setOption(Contact.Options.DIRTY_DELETE); Account account = contact.getAccount(); if (account.getStatus() == Account.STATUS_ONLINE) { IqPacket iq = new IqPacket(IqPacket.TYPE_SET); @@ -1289,55 +1192,9 @@ public class XmppConnectionService extends Service { item.setAttribute("jid", contact.getJid()); item.setAttribute("subscription", "remove"); account.getXmppConnection().sendIqPacket(iq, null); - contact.resetOption(Contact.Options.DIRTY_DELETE); - } else { - contact.setOption(Contact.Options.DIRTY_DELETE); } } - public void requestPresenceUpdatesFrom(Contact contact) { - PresencePacket packet = new PresencePacket(); - packet.setAttribute("type", "subscribe"); - packet.setAttribute("to", contact.getJid()); - packet.setAttribute("from", contact.getAccount().getJid()); - contact.getAccount().getXmppConnection().sendPresencePacket(packet); - } - - public void stopPresenceUpdatesFrom(Contact contact) { - PresencePacket packet = new PresencePacket(); - packet.setAttribute("type", "unsubscribe"); - packet.setAttribute("to", contact.getJid()); - packet.setAttribute("from", contact.getAccount().getJid()); - contact.getAccount().getXmppConnection().sendPresencePacket(packet); - } - - public void stopPresenceUpdatesTo(Contact contact) { - PresencePacket packet = new PresencePacket(); - packet.setAttribute("type", "unsubscribed"); - packet.setAttribute("to", contact.getJid()); - packet.setAttribute("from", contact.getAccount().getJid()); - contact.getAccount().getXmppConnection().sendPresencePacket(packet); - } - - public void sendPresenceUpdatesTo(Contact contact) { - PresencePacket packet = new PresencePacket(); - packet.setAttribute("type", "subscribed"); - packet.setAttribute("to", contact.getJid()); - packet.setAttribute("from", contact.getAccount().getJid()); - contact.getAccount().getXmppConnection().sendPresencePacket(packet); - } - - public void sendPresence(Account account) { - PresencePacket packet = new PresencePacket(); - packet.setAttribute("from", account.getFullJid()); - String sig = account.getPgpSignature(); - if (sig != null) { - packet.addChild("status").setContent("online"); - packet.addChild("x", "jabber:x:signed").setContent(sig); - } - account.getXmppConnection().sendPresencePacket(packet); - } - public void updateConversation(Conversation conversation) { this.databaseBackend.updateConversation(conversation); } @@ -1366,38 +1223,19 @@ public class XmppConnectionService extends Service { }).start(); } - public void sendConversationSubject(Conversation conversation, - String subject) { + public void inviteToConference(Conversation conversation, String contactJid) { + Account account = conversation.getAccount(); MessagePacket packet = new MessagePacket(); - packet.setType(MessagePacket.TYPE_GROUPCHAT); packet.setTo(conversation.getContactJid().split("/")[0]); - Element subjectChild = new Element("subject"); - subjectChild.setContent(subject); - packet.addChild(subjectChild); - packet.setFrom(conversation.getAccount().getJid()); - Account account = conversation.getAccount(); - if (account.getStatus() == Account.STATUS_ONLINE) { - account.getXmppConnection().sendMessagePacket(packet); - } - } - - public void inviteToConference(Conversation conversation, - List<Contact> contacts) { - for (Contact contact : contacts) { - MessagePacket packet = new MessagePacket(); - packet.setTo(conversation.getContactJid().split("/")[0]); - packet.setFrom(conversation.getAccount().getFullJid()); - Element x = new Element("x"); - x.setAttribute("xmlns", "http://jabber.org/protocol/muc#user"); - Element invite = new Element("invite"); - invite.setAttribute("to", contact.getJid()); - x.addChild(invite); - packet.addChild(x); - Log.d(LOGTAG, packet.toString()); - conversation.getAccount().getXmppConnection() - .sendMessagePacket(packet); - } - + packet.setFrom(account.getFullJid()); + Element x = new Element("x"); + x.setAttribute("xmlns", "http://jabber.org/protocol/muc#user"); + Element invite = new Element("invite"); + invite.setAttribute("to", contactJid); + x.addChild(invite); + packet.addChild(x); + sendMessagePacket(account,packet); + Log.d(LOGTAG,packet.toString()); } public boolean markMessage(Account account, String recipient, String uuid, @@ -1425,9 +1263,7 @@ public class XmppConnectionService extends Service { public void markMessage(Message message, int status) { message.setStatus(status); databaseBackend.updateMessage(message); - if (convChangedListener != null) { - convChangedListener.onConversationListChanged(); - } + updateConversationUi(); } public SharedPreferences getPreferences() { @@ -1439,14 +1275,32 @@ public class XmppConnectionService extends Service { return getPreferences().getBoolean("confirm_messages", true); } - public void updateUi(Conversation conversation, boolean notify) { - if (convChangedListener != null) { - convChangedListener.onConversationListChanged(); + public void notifyUi(Conversation conversation, boolean notify) { + if (mOnConversationUpdate != null) { + mOnConversationUpdate.onConversationUpdate(); } else { UIHelper.updateNotification(getApplicationContext(), getConversations(), conversation, notify); } } + + public void updateConversationUi() { + if (mOnConversationUpdate != null) { + mOnConversationUpdate.onConversationUpdate(); + } + } + + public void updateAccountUi() { + if (mOnAccountUpdate != null) { + mOnAccountUpdate.onAccountUpdate(); + } + } + + public void updateRosterUi() { + if (mOnRosterUpdate != null) { + mOnRosterUpdate.onRosterUpdate(); + } + } public Account findAccountByJid(String accountJid) { for (Account account : this.accounts) { @@ -1456,20 +1310,24 @@ public class XmppConnectionService extends Service { } return null; } - - public void markRead(Conversation conversation) { - conversation.markRead(this); + + public Conversation findConversationByUuid(String uuid) { + for (Conversation conversation : getConversations()) { + if (conversation.getUuid().equals(uuid)) { + return conversation; + } + } + return null; } - public void sendConfirmMessage(Account account, String to, String id) { - MessagePacket receivedPacket = new MessagePacket(); - receivedPacket.setType(MessagePacket.TYPE_NORMAL); - receivedPacket.setTo(to); - receivedPacket.setFrom(account.getFullJid()); - Element received = receivedPacket.addChild("displayed", - "urn:xmpp:chat-markers:0"); - received.setAttribute("id", id); - account.getXmppConnection().sendMessagePacket(receivedPacket); + public void markRead(Conversation conversation) { + conversation.markRead(); + String id = conversation.popLatestMarkableMessageId(); + if (confirmMessages() && id != null) { + Account account = conversation.getAccount(); + String to = conversation.getContactJid(); + this.sendMessagePacket(conversation.getAccount(), mMessageGenerator.confirm(account, to, id)); + } } public SecureRandom getRNG() { @@ -1482,19 +1340,87 @@ public class XmppConnectionService extends Service { public void replyWithNotAcceptable(Account account, MessagePacket packet) { if (account.getStatus() == Account.STATUS_ONLINE) { - MessagePacket error = this.mMessageGenerator.generateNotAcceptable(packet); - account.getXmppConnection().sendMessagePacket(error); + MessagePacket error = this.mMessageGenerator + .generateNotAcceptable(packet); + sendMessagePacket(account,error); } } - + public void syncRosterToDisk(final Account account) { new Thread(new Runnable() { - + @Override public void run() { databaseBackend.writeRoster(account.getRoster()); } }).start(); - + + } + + public List<String> getKnownHosts() { + List<String> hosts = new ArrayList<String>(); + for (Account account : getAccounts()) { + if (!hosts.contains(account.getServer())) { + hosts.add(account.getServer()); + } + for (Contact contact : account.getRoster().getContacts()) { + if (contact.showInRoster()) { + String server = contact.getServer(); + if (server != null && !hosts.contains(server)) { + hosts.add(server); + } + } + } + } + return hosts; + } + + public List<String> getKnownConferenceHosts() { + ArrayList<String> mucServers = new ArrayList<String>(); + for (Account account : accounts) { + if (account.getXmppConnection() != null) { + String server = account.getXmppConnection().getMucServer(); + if (server != null) { + mucServers.add(server); + } + } + } + return mucServers; + } + + public void sendMessagePacket(Account account, MessagePacket packet) { + account.getXmppConnection().sendMessagePacket(packet); + } + + public void sendPresencePacket(Account account, PresencePacket packet) { + account.getXmppConnection().sendPresencePacket(packet); + } + + public void sendIqPacket(Account account, IqPacket packet, OnIqPacketReceived callback) { + account.getXmppConnection().sendIqPacket(packet, callback); + } + + public MessageGenerator getMessageGenerator() { + return this.mMessageGenerator; + } + + public PresenceGenerator getPresenceGenerator() { + return this.mPresenceGenerator; + } + + public JingleConnectionManager getJingleConnectionManager() { + return this.mJingleConnectionManager; + } + + public interface OnConversationUpdate { + public void onConversationUpdate(); + } + + public interface OnAccountUpdate { + public void onAccountUpdate(); + } + + public interface OnRosterUpdate { + public void onRosterUpdate(); } } |