package org.mercury_im.messenger.core; import org.bouncycastle.openpgp.PGPException; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.chat2.ChatManager; import org.jivesoftware.smack.roster.Roster; import org.jivesoftware.smack.roster.RosterEntry; import org.jivesoftware.smackx.ox.OpenPgpManager; import org.jivesoftware.smackx.ox_im.OXInstantMessagingManager; import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.stringprep.XmppStringprepException; import org.mercury_im.messenger.core.connection.MercuryConnection; import org.mercury_im.messenger.core.connection.MercuryConnectionManager; import org.mercury_im.messenger.core.connection.message.MessageConsignor; import org.mercury_im.messenger.core.connection.message.OxMessageConsignor; import org.mercury_im.messenger.core.connection.message.PlaintextMessageConsignor; import org.mercury_im.messenger.core.data.repository.MessageRepository; import org.mercury_im.messenger.core.exception.ConnectionNotFoundException; import org.mercury_im.messenger.core.exception.ContactAlreadyAddedException; import org.mercury_im.messenger.entity.chat.Chat; import org.mercury_im.messenger.entity.contact.Peer; import java.io.IOException; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import javax.inject.Inject; import javax.inject.Singleton; import io.reactivex.Completable; @Singleton public class Messenger { public static final String TAG = "MercuryIM"; private static final Logger LOGGER = Logger.getLogger(Messenger.class.getName()); private final MercuryConnectionManager connectionManager; private final MessageRepository messageRepository; private final SchedulersFacade schedulers; @Inject public Messenger(MercuryConnectionManager connectionManager, MessageRepository messageRepository, SchedulersFacade schedulers) { this.connectionManager = connectionManager; this.messageRepository = messageRepository; this.schedulers = schedulers; } public MercuryConnectionManager getConnectionManager() { return connectionManager; } public Completable addContact(UUID accountId, String contactAddress) { return Completable.fromAction(() -> doAddContact(accountId, contactAddress)); } private void doAddContact(UUID accountId, String contactAddress) throws ConnectionNotFoundException, XmppStringprepException, ContactAlreadyAddedException, SmackException.NotLoggedInException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException { MercuryConnection connection = getConnection(accountId); EntityBareJid jid = JidCreate.entityBareFrom(contactAddress); Roster roster = Roster.getInstanceFor(connection.getConnection()); if (roster.getEntry(jid) != null) { throw new ContactAlreadyAddedException(jid); } if (roster.isSubscriptionPreApprovalSupported()) { LOGGER.log(Level.INFO, "Pre-Approval supported."); try { roster.preApproveAndCreateEntry(jid, null, null); } catch (SmackException.FeatureNotSupportedException e) { throw new AssertionError("pre-approval failed even though the feature is announced."); } } else { roster.createItemAndRequestSubscription(jid, null, null); } } public Completable deleteContact(Peer contact) { return Completable.fromAction(() -> doDeleteContact(contact)); } private void doDeleteContact(Peer contact) throws ConnectionNotFoundException, XmppStringprepException, SmackException.NotLoggedInException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException { MercuryConnection connection = getConnection(contact.getAccount().getId()); Roster roster = Roster.getInstanceFor(connection.getConnection()); EntityBareJid jid = JidCreate.entityBareFrom(contact.getAddress()); RosterEntry entry = roster.getEntry(jid); if (entry != null) { roster.removeEntry(entry); } else { throw new IllegalStateException("Contact " + jid.toString() + " not in roster!"); } } public Completable sendMessage(Peer peer, String body) { return Completable.fromAction(() -> doSendMessage(peer, body)); } private void doSendMessage(Peer contact, String body) throws ConnectionNotFoundException, SmackException.NotConnectedException, InterruptedException { MercuryConnection connection = getConnection(contact.getAccount().getId()); ChatManager chatManager = ChatManager.getInstanceFor(connection.getConnection()); chatManager.chatWith(JidCreate.entityBareFromOrThrowUnchecked(contact.getAddress())) .send(body); } public Completable sendEncryptedMessage(Peer contact, String body) { return Completable.fromAction(() -> doSendEncryptedMessage(contact, body)); } public void doSendEncryptedMessage(Peer contact, String body) throws ConnectionNotFoundException, InterruptedException, PGPException, SmackException.NotConnectedException, SmackException.NotLoggedInException, IOException { MercuryConnection connection = getConnection(contact.getAccount().getId()); OpenPgpManager oxManager = OpenPgpManager.getInstanceFor(connection.getConnection()); OXInstantMessagingManager oximManager = OXInstantMessagingManager.getInstanceFor(connection.getConnection()); oximManager.sendOxMessage(oxManager.getOpenPgpContact(contact.getJid()), body); } private MercuryConnection getConnection(UUID accountId) throws ConnectionNotFoundException { MercuryConnection connection = getConnectionManager().getConnection(accountId); if (connection == null) { throw new ConnectionNotFoundException(accountId); } return connection; } public MessageConsignor getMessageConsignor(Chat chat) { switch (chat.getChatPreferences().getEncryption()) { case plain: return new PlaintextMessageConsignor(connectionManager, messageRepository, chat); case ox_sign: case ox_crypt: case ox_signcrypt: return new OxMessageConsignor(connectionManager, messageRepository, chat); } throw new AssertionError("Illegal Encryption Type: " + chat.getChatPreferences().getEncryption()); } }