diff options
author | Daniel Gultsch <daniel.gultsch@rwth-aachen.de> | 2014-02-28 18:46:01 +0100 |
---|---|---|
committer | Daniel Gultsch <daniel.gultsch@rwth-aachen.de> | 2014-02-28 18:46:01 +0100 |
commit | acf80bddd07d5579cdb20a888b3f9e99e53030a5 (patch) | |
tree | 034a8e364488c89c8e1997f56313d919019afa65 /src/eu/siacs/conversations/services/XmppConnectionService.java | |
parent | 03d96266f8bbb76e25610e903d74a0afa7dcd03b (diff) |
rebranding
Diffstat (limited to 'src/eu/siacs/conversations/services/XmppConnectionService.java')
-rw-r--r-- | src/eu/siacs/conversations/services/XmppConnectionService.java | 946 |
1 files changed, 946 insertions, 0 deletions
diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java new file mode 100644 index 000000000..218d5088b --- /dev/null +++ b/src/eu/siacs/conversations/services/XmppConnectionService.java @@ -0,0 +1,946 @@ +package eu.siacs.conversations.services; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Hashtable; +import java.util.List; + +import org.json.JSONException; +import org.openintents.openpgp.util.OpenPgpApi; +import org.openintents.openpgp.util.OpenPgpServiceConnection; + +import net.java.otr4j.OtrException; +import net.java.otr4j.session.Session; +import net.java.otr4j.session.SessionStatus; + +import eu.siacs.conversations.crypto.PgpEngine; +import eu.siacs.conversations.crypto.PgpEngine.OpenPgpException; +import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.entities.Contact; +import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.entities.Presences; +import eu.siacs.conversations.persistance.DatabaseBackend; +import eu.siacs.conversations.persistance.OnPhoneContactsMerged; +import eu.siacs.conversations.ui.OnAccountListChangedListener; +import eu.siacs.conversations.ui.OnConversationListChangedListener; +import eu.siacs.conversations.ui.OnRosterFetchedListener; +import eu.siacs.conversations.utils.MessageParser; +import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener; +import eu.siacs.conversations.utils.PhoneHelper; +import eu.siacs.conversations.utils.UIHelper; +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.OnMessagePacketReceived; +import eu.siacs.conversations.xmpp.OnPresencePacketReceived; +import eu.siacs.conversations.xmpp.OnStatusChanged; +import eu.siacs.conversations.xmpp.PresencePacket; +import eu.siacs.conversations.xmpp.XmppConnection; +import android.app.NotificationManager; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.database.ContentObserver; +import android.database.DatabaseUtils; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.PowerManager; +import android.preference.PreferenceManager; +import android.provider.ContactsContract; +import android.util.Log; + +public class XmppConnectionService extends Service { + + protected static final String LOGTAG = "xmppService"; + public DatabaseBackend databaseBackend; + + public long startDate; + + private List<Account> accounts; + private List<Conversation> conversations = null; + + public OnConversationListChangedListener convChangedListener = null; + private OnAccountListChangedListener accountChangedListener = null; + + private ContentObserver contactObserver = new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + Log.d(LOGTAG, "contact list has changed"); + mergePhoneContactsWithRoster(null); + } + }; + + private XmppConnectionService service = this; + + private final IBinder mBinder = new XmppConnectionBinder(); + private OnMessagePacketReceived messageListener = new OnMessagePacketReceived() { + + @Override + public void onMessagePacketReceived(Account account, + MessagePacket packet) { + Message message = null; + boolean notify = false; + if ((packet.getType() == MessagePacket.TYPE_CHAT)) { + String pgpBody = MessageParser.getPgpBody(packet); + if (pgpBody != null) { + message = MessageParser.parsePgpChat(pgpBody, packet, + account, service); + notify = false; + } else if (packet.hasChild("body") + && (packet.getBody().startsWith("?OTR"))) { + message = MessageParser.parseOtrChat(packet, account, + service); + notify = true; + } else if (packet.hasChild("body")) { + message = MessageParser.parsePlainTextChat(packet, account, + service); + notify = true; + } else if (packet.hasChild("received") + || (packet.hasChild("sent"))) { + message = MessageParser.parseCarbonMessage(packet, account, + service); + } + + } else if (packet.getType() == MessagePacket.TYPE_GROUPCHAT) { + message = MessageParser + .parseGroupchat(packet, account, service); + if (message != null) { + notify = (message.getStatus() == Message.STATUS_RECIEVED); + } + } else if (packet.getType() == MessagePacket.TYPE_ERROR) { + message = MessageParser.parseError(packet, account, service); + } else { + Log.d(LOGTAG, "unparsed message " + packet.toString()); + } + if (message == null) { + return; + } + if (packet.hasChild("delay")) { + try { + String stamp = packet.findChild("delay").getAttribute( + "stamp"); + stamp = stamp.replace("Z", "+0000"); + Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") + .parse(stamp); + message.setTime(date.getTime()); + } catch (ParseException e) { + Log.d(LOGTAG, "error trying to parse date" + e.getMessage()); + } + } + if (notify) { + message.markUnread(); + } + Conversation conversation = message.getConversation(); + conversation.getMessages().add(message); + if (packet.getType() != MessagePacket.TYPE_ERROR) { + databaseBackend.createMessage(message); + } + if (convChangedListener != null) { + convChangedListener.onConversationListChanged(); + } else { + if (notify) { + NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + mNotificationManager.notify(2342, UIHelper + .getUnreadMessageNotification( + getApplicationContext(), conversation)); + } + } + } + }; + private OnStatusChanged statusListener = new OnStatusChanged() { + + @Override + public void onStatusChanged(Account account) { + if (accountChangedListener != null) { + accountChangedListener.onAccountListChangedListener(); + } + if (account.getStatus() == Account.STATUS_ONLINE) { + databaseBackend.clearPresences(account); + connectMultiModeConversations(account); + List<Conversation> conversations = getConversations(); + for (int i = 0; i < conversations.size(); ++i) { + if (conversations.get(i).getAccount() == account) { + sendUnsendMessages(conversations.get(i)); + } + } + if (convChangedListener != null) { + convChangedListener.onConversationListChanged(); + } + if (account.getKeys().has("pgp_signature")) { + try { + sendPgpPresence(account, account.getKeys().getString("pgp_signature")); + } catch (JSONException e) { + // + } + } + } + } + }; + + private OnPresencePacketReceived presenceListener = new OnPresencePacketReceived() { + + @Override + public void onPresencePacketReceived(Account account, + PresencePacket packet) { + if (packet.hasChild("x")&&(packet.findChild("x").getAttribute("xmlns").startsWith("http://jabber.org/protocol/muc"))) { + Log.d(LOGTAG,"got muc presence "+packet.toString()); + } else { + String[] fromParts = packet.getAttribute("from").split("/"); + Contact contact = findContact(account, fromParts[0]); + if (contact == null) { + // most likely self or roster not synced + return; + } + String type = packet.getAttribute("type"); + if (type == null) { + Element show = packet.findChild("show"); + if (show == null) { + contact.updatePresence(fromParts[1], Presences.ONLINE); + } else if (show.getContent().equals("away")) { + contact.updatePresence(fromParts[1], Presences.AWAY); + } else if (show.getContent().equals("xa")) { + contact.updatePresence(fromParts[1], Presences.XA); + } else if (show.getContent().equals("chat")) { + contact.updatePresence(fromParts[1], Presences.CHAT); + } else if (show.getContent().equals("dnd")) { + contact.updatePresence(fromParts[1], Presences.DND); + } + PgpEngine pgp = getPgpEngine(); + if (pgp!=null) { + Element x = packet.findChild("x"); + if ((x != null) + && (x.getAttribute("xmlns").equals("jabber:x:signed"))) { + try { + Log.d(LOGTAG,"pgp signature for contact" +packet.getAttribute("from")); + contact.setPgpKeyId(pgp.fetchKeyId(packet.findChild("status") + .getContent(), x.getContent())); + } catch (OpenPgpException e) { + Log.d(LOGTAG,"faulty pgp. just ignore"); + } + } + } + databaseBackend.updateContact(contact); + } else if (type.equals("unavailable")) { + if (fromParts.length != 2) { + // Log.d(LOGTAG,"received presence with no resource "+packet.toString()); + } else { + contact.removePresence(fromParts[1]); + databaseBackend.updateContact(contact); + } + } else if (type.equals("subscribe")) { + if (contact + .getSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT)) { + sendPresenceUpdatesTo(contact); + contact.setSubscriptionOption(Contact.Subscription.FROM); + contact.resetSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT); + replaceContactInConversation(contact.getJid(), contact); + databaseBackend.updateContact(contact); + if ((contact + .getSubscriptionOption(Contact.Subscription.ASKING)) + && (!contact + .getSubscriptionOption(Contact.Subscription.TO))) { + requestPresenceUpdatesFrom(contact); + } + } else { + // TODO: ask user to handle it maybe + } + } else { + //Log.d(LOGTAG, packet.toString()); + } + replaceContactInConversation(contact.getJid(), contact); + } + } + }; + + private OnIqPacketReceived unknownIqListener = new OnIqPacketReceived() { + + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.hasChild("query")) { + Element query = packet.findChild("query"); + String xmlns = query.getAttribute("xmlns"); + if ((xmlns != null) && (xmlns.equals("jabber:iq:roster"))) { + processRosterItems(account, query); + mergePhoneContactsWithRoster(null); + } + } + } + }; + + private OpenPgpServiceConnection pgpServiceConnection; + private PgpEngine mPgpEngine = null; + + public PgpEngine getPgpEngine() { + if (pgpServiceConnection.isBound()) { + if (this.mPgpEngine == null) { + this.mPgpEngine = new PgpEngine(new OpenPgpApi( + getApplicationContext(), + pgpServiceConnection.getService())); + } + return mPgpEngine; + } else { + return null; + } + + } + + private void processRosterItems(Account account, Element elements) { + String version = elements.getAttribute("ver"); + if (version != null) { + account.setRosterVersion(version); + databaseBackend.updateAccount(account); + } + for (Element item : elements.getChildren()) { + if (item.getName().equals("item")) { + String jid = item.getAttribute("jid"); + String subscription = item.getAttribute("subscription"); + Contact contact = databaseBackend.findContact(account, jid); + if (contact == null) { + if (!subscription.equals("remove")) { + String name = item.getAttribute("name"); + if (name == null) { + name = jid.split("@")[0]; + } + contact = new Contact(account, name, jid, null); + contact.parseSubscriptionFromElement(item); + databaseBackend.createContact(contact); + } + } else { + if (subscription.equals("remove")) { + databaseBackend.deleteContact(contact); + replaceContactInConversation(contact.getJid(), null); + } else { + contact.parseSubscriptionFromElement(item); + databaseBackend.updateContact(contact); + replaceContactInConversation(contact.getJid(), contact); + } + } + } + } + } + + private void replaceContactInConversation(String jid, Contact contact) { + List<Conversation> conversations = getConversations(); + for (int i = 0; i < conversations.size(); ++i) { + if ((conversations.get(i).getContactJid().equals(jid))) { + conversations.get(i).setContact(contact); + break; + } + } + } + + public class XmppConnectionBinder extends Binder { + public XmppConnectionService getService() { + return XmppConnectionService.this; + } + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + for (Account account : accounts) { + if (account.getXmppConnection() == null) { + if (!account.isOptionSet(Account.OPTION_DISABLED)) { + account.setXmppConnection(this.createConnection(account)); + } + } + } + return START_STICKY; + } + + @Override + public void onCreate() { + databaseBackend = DatabaseBackend.getInstance(getApplicationContext()); + this.accounts = databaseBackend.getAccounts(); + + getContentResolver().registerContentObserver( + ContactsContract.Contacts.CONTENT_URI, true, contactObserver); + this.pgpServiceConnection = new OpenPgpServiceConnection( + getApplicationContext(), "org.sufficientlysecure.keychain"); + this.pgpServiceConnection.bindToService(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + for (Account account : accounts) { + if (account.getXmppConnection() != null) { + disconnect(account); + } + } + } + + public XmppConnection createConnection(Account account) { + PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + XmppConnection connection = new XmppConnection(account, pm); + connection.setOnMessagePacketReceivedListener(this.messageListener); + connection.setOnStatusChangedListener(this.statusListener); + connection.setOnPresencePacketReceivedListener(this.presenceListener); + connection + .setOnUnregisteredIqPacketReceivedListener(this.unknownIqListener); + Thread thread = new Thread(connection); + thread.start(); + return connection; + } + + public void sendMessage(Message message, String presence) { + Account account = message.getConversation().getAccount(); + Conversation conv = message.getConversation(); + boolean saveInDb = false; + boolean addToConversation = false; + if (account.getStatus() == Account.STATUS_ONLINE) { + MessagePacket packet; + if (message.getEncryption() == Message.ENCRYPTION_OTR) { + if (!conv.hasValidOtrSession()) { + // starting otr session. messages will be send later + conv.startOtrSession(getApplicationContext(), presence); + } else if (conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) { + // otr session aleary exists, creating message packet + // accordingly + packet = prepareMessagePacket(account, message, + conv.getOtrSession()); + account.getXmppConnection().sendMessagePacket(packet); + message.setStatus(Message.STATUS_SEND); + } + saveInDb = true; + addToConversation = true; + } else if (message.getEncryption() == Message.ENCRYPTION_PGP) { + long keyId = message.getConversation().getContact() + .getPgpKeyId(); + packet = new MessagePacket(); + packet.setType(MessagePacket.TYPE_CHAT); + packet.setFrom(message.getConversation().getAccount() + .getFullJid()); + packet.setTo(message.getCounterpart()); + packet.setBody("This is an XEP-0027 encryted message"); + Element x = new Element("x"); + x.setAttribute("xmlns", "jabber:x:encrypted"); + x.setContent(this.getPgpEngine().encrypt(keyId, + message.getBody())); + packet.addChild(x); + account.getXmppConnection().sendMessagePacket(packet); + message.setStatus(Message.STATUS_SEND); + message.setEncryption(Message.ENCRYPTION_DECRYPTED); + saveInDb = true; + addToConversation = true; + } else { + // don't encrypt + if (message.getConversation().getMode() == Conversation.MODE_SINGLE) { + message.setStatus(Message.STATUS_SEND); + saveInDb = true; + addToConversation = true; + } + + packet = prepareMessagePacket(account, message, null); + account.getXmppConnection().sendMessagePacket(packet); + } + } else { + // account is offline + saveInDb = true; + addToConversation = true; + + } + if (saveInDb) { + databaseBackend.createMessage(message); + } + if (addToConversation) { + conv.getMessages().add(message); + if (convChangedListener != null) { + convChangedListener.onConversationListChanged(); + } + } + + } + + private void sendUnsendMessages(Conversation conversation) { + for (int i = 0; i < conversation.getMessages().size(); ++i) { + if (conversation.getMessages().get(i).getStatus() == Message.STATUS_UNSEND) { + Message message = conversation.getMessages().get(i); + MessagePacket packet = prepareMessagePacket( + conversation.getAccount(), message, null); + conversation.getAccount().getXmppConnection() + .sendMessagePacket(packet); + message.setStatus(Message.STATUS_SEND); + if (conversation.getMode() == Conversation.MODE_SINGLE) { + databaseBackend.updateMessage(message); + } else { + databaseBackend.deleteMessage(message); + conversation.getMessages().remove(i); + i--; + } + } + } + } + + public MessagePacket prepareMessagePacket(Account account, Message message, + Session otrSession) { + MessagePacket packet = new MessagePacket(); + if (message.getConversation().getMode() == Conversation.MODE_SINGLE) { + packet.setType(MessagePacket.TYPE_CHAT); + if (otrSession != null) { + try { + packet.setBody(otrSession.transformSending(message + .getBody())); + } catch (OtrException e) { + Log.d(LOGTAG, + account.getJid() + + ": could not encrypt message to " + + message.getCounterpart()); + } + Element privateMarker = new Element("private"); + privateMarker.setAttribute("xmlns", "urn:xmpp:carbons:2"); + packet.addChild(privateMarker); + packet.setTo(otrSession.getSessionID().getAccountID() + "/" + + otrSession.getSessionID().getUserID()); + packet.setFrom(account.getFullJid()); + } else { + packet.setBody(message.getBody()); + packet.setTo(message.getCounterpart()); + packet.setFrom(account.getJid()); + } + } else if (message.getConversation().getMode() == Conversation.MODE_MULTI) { + packet.setType(MessagePacket.TYPE_GROUPCHAT); + packet.setBody(message.getBody()); + packet.setTo(message.getCounterpart()); + packet.setFrom(account.getJid()); + } + return packet; + } + + public void getRoster(Account account, + final OnRosterFetchedListener listener) { + List<Contact> contacts = databaseBackend.getContacts(account); + for (int i = 0; i < contacts.size(); ++i) { + contacts.get(i).setAccount(account); + } + if (listener != null) { + listener.onRosterFetched(contacts); + } + } + + public void updateRoster(final Account account, + final OnRosterFetchedListener listener) { + IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET); + Element query = new Element("query"); + query.setAttribute("xmlns", "jabber:iq:roster"); + query.setAttribute("ver", account.getRosterVersion()); + iqPacket.addChild(query); + account.getXmppConnection().sendIqPacket(iqPacket, + new OnIqPacketReceived() { + + @Override + public void onIqPacketReceived(final Account account, + IqPacket packet) { + Element roster = packet.findChild("query"); + if (roster != null) { + processRosterItems(account, roster); + StringBuilder mWhere = new StringBuilder(); + mWhere.append("jid NOT IN("); + List<Element> items = roster.getChildren(); + for (int i = 0; i < items.size(); ++i) { + mWhere.append(DatabaseUtils + .sqlEscapeString(items.get(i) + .getAttribute("jid"))); + if (i != items.size() - 1) { + mWhere.append(","); + } + } + mWhere.append(") and accountUuid = \""); + mWhere.append(account.getUuid()); + mWhere.append("\""); + List<Contact> contactsToDelete = databaseBackend + .getContats(mWhere.toString()); + for (Contact contact : contactsToDelete) { + databaseBackend.deleteContact(contact); + replaceContactInConversation(contact.getJid(), + null); + } + + } + mergePhoneContactsWithRoster(new OnPhoneContactsMerged() { + + @Override + public void phoneContactsMerged() { + if (listener != null) { + getRoster(account, listener); + } + } + }); + } + }); + } + + public void mergePhoneContactsWithRoster( + final OnPhoneContactsMerged listener) { + PhoneHelper.loadPhoneContacts(getApplicationContext(), + new OnPhoneContactsLoadedListener() { + @Override + public void onPhoneContactsLoaded( + Hashtable<String, Bundle> phoneContacts) { + List<Contact> contacts = databaseBackend + .getContacts(null); + for (int i = 0; i < contacts.size(); ++i) { + Contact contact = contacts.get(i); + if (phoneContacts.containsKey(contact.getJid())) { + Bundle phoneContact = phoneContacts.get(contact + .getJid()); + String systemAccount = phoneContact + .getInt("phoneid") + + "#" + + phoneContact.getString("lookup"); + contact.setSystemAccount(systemAccount); + contact.setPhotoUri(phoneContact + .getString("photouri")); + contact.setDisplayName(phoneContact + .getString("displayname")); + databaseBackend.updateContact(contact); + replaceContactInConversation(contact.getJid(), + contact); + } else { + if ((contact.getSystemAccount() != null) + || (contact.getProfilePhoto() != null)) { + contact.setSystemAccount(null); + contact.setPhotoUri(null); + databaseBackend.updateContact(contact); + replaceContactInConversation( + contact.getJid(), contact); + } + } + } + if (listener != null) { + listener.phoneContactsMerged(); + } + } + }); + } + + public List<Conversation> getConversations() { + if (this.conversations == null) { + Hashtable<String, Account> accountLookupTable = new Hashtable<String, Account>(); + for (Account account : this.accounts) { + accountLookupTable.put(account.getUuid(), account); + } + this.conversations = databaseBackend + .getConversations(Conversation.STATUS_AVAILABLE); + for (Conversation conv : this.conversations) { + Account account = accountLookupTable.get(conv.getAccountUuid()); + conv.setAccount(account); + conv.setContact(findContact(account, conv.getContactJid())); + conv.setMessages(databaseBackend.getMessages(conv, 50)); + } + } + return this.conversations; + } + + public List<Account> getAccounts() { + return this.accounts; + } + + public Contact findContact(Account account, String jid) { + Contact contact = databaseBackend.findContact(account, jid); + if (contact != null) { + contact.setAccount(account); + } + return contact; + } + + public Conversation findOrCreateConversation(Account account, String jid, + boolean muc) { + for (Conversation conv : this.getConversations()) { + if ((conv.getAccount().equals(account)) + && (conv.getContactJid().equals(jid))) { + return conv; + } + } + Conversation conversation = databaseBackend.findConversation(account, + jid); + if (conversation != null) { + conversation.setStatus(Conversation.STATUS_AVAILABLE); + conversation.setAccount(account); + if (muc) { + conversation.setMode(Conversation.MODE_MULTI); + if (account.getStatus() == Account.STATUS_ONLINE) { + joinMuc(conversation); + } + } else { + conversation.setMode(Conversation.MODE_SINGLE); + } + this.databaseBackend.updateConversation(conversation); + conversation.setContact(findContact(account, + conversation.getContactJid())); + } else { + String conversationName; + Contact contact = findContact(account, jid); + if (contact != null) { + conversationName = contact.getDisplayName(); + } else { + conversationName = jid.split("@")[0]; + } + if (muc) { + conversation = new Conversation(conversationName, account, jid, + Conversation.MODE_MULTI); + if (account.getStatus() == Account.STATUS_ONLINE) { + joinMuc(conversation); + } + } else { + conversation = new Conversation(conversationName, account, jid, + Conversation.MODE_SINGLE); + } + conversation.setContact(contact); + this.databaseBackend.createConversation(conversation); + } + this.conversations.add(conversation); + if (this.convChangedListener != null) { + this.convChangedListener.onConversationListChanged(); + } + return conversation; + } + + public void archiveConversation(Conversation conversation) { + if (conversation.getMode() == Conversation.MODE_MULTI) { + leaveMuc(conversation); + } else { + try { + conversation.endOtrIfNeeded(); + } catch (OtrException e) { + Log.d(LOGTAG, + "error ending otr session for " + + conversation.getName()); + } + } + this.databaseBackend.updateConversation(conversation); + this.conversations.remove(conversation); + if (this.convChangedListener != null) { + this.convChangedListener.onConversationListChanged(); + } + } + + public int getConversationCount() { + return this.databaseBackend.getConversationCount(); + } + + public void createAccount(Account account) { + databaseBackend.createAccount(account); + this.accounts.add(account); + account.setXmppConnection(this.createConnection(account)); + if (accountChangedListener != null) + accountChangedListener.onAccountListChangedListener(); + } + + public void deleteContact(Contact contact) { + IqPacket iq = new IqPacket(IqPacket.TYPE_SET); + Element query = new Element("query"); + query.setAttribute("xmlns", "jabber:iq:roster"); + Element item = new Element("item"); + item.setAttribute("jid", contact.getJid()); + item.setAttribute("subscription", "remove"); + query.addChild(item); + iq.addChild(query); + contact.getAccount().getXmppConnection().sendIqPacket(iq, null); + replaceContactInConversation(contact.getJid(), null); + databaseBackend.deleteContact(contact); + } + + public void updateAccount(Account account) { + databaseBackend.updateAccount(account); + if (account.getXmppConnection() != null) { + disconnect(account); + } + if (!account.isOptionSet(Account.OPTION_DISABLED)) { + account.setXmppConnection(this.createConnection(account)); + } + if (accountChangedListener != null) + accountChangedListener.onAccountListChangedListener(); + } + + public void deleteAccount(Account account) { + Log.d(LOGTAG, "called delete account"); + if (account.getXmppConnection() != null) { + this.disconnect(account); + } + databaseBackend.deleteAccount(account); + this.accounts.remove(account); + if (accountChangedListener != null) + accountChangedListener.onAccountListChangedListener(); + } + + public void setOnConversationListChangedListener( + OnConversationListChangedListener listener) { + this.convChangedListener = listener; + } + + public void removeOnConversationListChangedListener() { + this.convChangedListener = null; + } + + public void setOnAccountListChangedListener( + OnAccountListChangedListener listener) { + this.accountChangedListener = listener; + } + + public void removeOnAccountListChangedListener() { + this.accountChangedListener = null; + } + + public void connectMultiModeConversations(Account account) { + List<Conversation> conversations = getConversations(); + for (int i = 0; i < conversations.size(); i++) { + Conversation conversation = conversations.get(i); + if ((conversation.getMode() == Conversation.MODE_MULTI) + && (conversation.getAccount() == account)) { + joinMuc(conversation); + } + } + } + + public void joinMuc(Conversation conversation) { + String muc = conversation.getContactJid(); + PresencePacket packet = new PresencePacket(); + packet.setAttribute("to", muc + "/" + + conversation.getAccount().getUsername()); + Element x = new Element("x"); + x.setAttribute("xmlns", "http://jabber.org/protocol/muc"); + if (conversation.getMessages().size() != 0) { + Element history = new Element("history"); + long lastMsgTime = conversation.getLatestMessage().getTimeSent(); + long diff = (System.currentTimeMillis() - lastMsgTime) / 1000 - 1; + history.setAttribute("seconds", diff + ""); + x.addChild(history); + } + packet.addChild(x); + conversation.getAccount().getXmppConnection() + .sendPresencePacket(packet); + } + + public void leaveMuc(Conversation conversation) { + + } + + public void disconnect(Account account) { + List<Conversation> conversations = getConversations(); + for (int i = 0; i < conversations.size(); i++) { + Conversation conversation = conversations.get(i); + if (conversation.getAccount() == account) { + if (conversation.getMode() == Conversation.MODE_MULTI) { + leaveMuc(conversation); + } else { + try { + conversation.endOtrIfNeeded(); + } catch (OtrException e) { + Log.d(LOGTAG, "error ending otr session for " + + conversation.getName()); + } + } + } + } + account.getXmppConnection().disconnect(); + Log.d(LOGTAG, "disconnected account: " + account.getJid()); + account.setXmppConnection(null); + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + public void updateContact(Contact contact) { + databaseBackend.updateContact(contact); + } + + public void updateMessage(Message message) { + databaseBackend.updateMessage(message); + } + + public void createContact(Contact contact) { + SharedPreferences sharedPref = PreferenceManager + .getDefaultSharedPreferences(getApplicationContext()); + boolean autoGrant = sharedPref.getBoolean("grant_new_contacts", true); + if (autoGrant) { + contact.setSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT); + contact.setSubscriptionOption(Contact.Subscription.ASKING); + } + databaseBackend.createContact(contact); + IqPacket iq = new IqPacket(IqPacket.TYPE_SET); + Element query = new Element("query"); + query.setAttribute("xmlns", "jabber:iq:roster"); + Element item = new Element("item"); + item.setAttribute("jid", contact.getJid()); + item.setAttribute("name", contact.getJid()); + query.addChild(item); + iq.addChild(query); + Account account = contact.getAccount(); + account.getXmppConnection().sendIqPacket(iq, null); + if (autoGrant) { + requestPresenceUpdatesFrom(contact); + } + replaceContactInConversation(contact.getJid(), contact); + } + + public void requestPresenceUpdatesFrom(Contact contact) { + // Requesting a Subscription type=subscribe + PresencePacket packet = new PresencePacket(); + packet.setAttribute("type", "subscribe"); + packet.setAttribute("to", contact.getJid()); + packet.setAttribute("from", contact.getAccount().getJid()); + Log.d(LOGTAG, packet.toString()); + contact.getAccount().getXmppConnection().sendPresencePacket(packet); + } + + public void stopPresenceUpdatesFrom(Contact contact) { + // Unsubscribing type='unsubscribe' + PresencePacket packet = new PresencePacket(); + packet.setAttribute("type", "unsubscribe"); + packet.setAttribute("to", contact.getJid()); + packet.setAttribute("from", contact.getAccount().getJid()); + Log.d(LOGTAG, packet.toString()); + contact.getAccount().getXmppConnection().sendPresencePacket(packet); + } + + public void stopPresenceUpdatesTo(Contact contact) { + // Canceling a Subscription type=unsubscribed + PresencePacket packet = new PresencePacket(); + packet.setAttribute("type", "unsubscribed"); + packet.setAttribute("to", contact.getJid()); + packet.setAttribute("from", contact.getAccount().getJid()); + Log.d(LOGTAG, packet.toString()); + contact.getAccount().getXmppConnection().sendPresencePacket(packet); + } + + public void sendPresenceUpdatesTo(Contact contact) { + // type='subscribed' + PresencePacket packet = new PresencePacket(); + packet.setAttribute("type", "subscribed"); + packet.setAttribute("to", contact.getJid()); + packet.setAttribute("from", contact.getAccount().getJid()); + Log.d(LOGTAG, packet.toString()); + contact.getAccount().getXmppConnection().sendPresencePacket(packet); + } + + public void sendPgpPresence(Account account, String signature) { + PresencePacket packet = new PresencePacket(); + packet.setAttribute("from", account.getFullJid()); + 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); + account.getXmppConnection().sendPresencePacket(packet); + } + + public void generatePgpAnnouncement(Account account) + throws PgpEngine.UserInputRequiredException { + if (account.getStatus() == Account.STATUS_ONLINE) { + String signature = getPgpEngine().generateSignature("online"); + account.setKey("pgp_signature", signature); + databaseBackend.updateAccount(account); + sendPgpPresence(account, signature); + } + } +}
\ No newline at end of file |