2018-05-28 00:58:13 +02:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* Copyright 2018 Paul Schaub.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
package org.jivesoftware.smackx.ox;
|
|
|
|
|
2018-06-22 15:28:14 +02:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.util.ArrayList;
|
2018-06-01 13:11:43 +02:00
|
|
|
import java.util.HashSet;
|
2018-06-22 15:28:14 +02:00
|
|
|
import java.util.List;
|
2018-05-28 00:58:13 +02:00
|
|
|
import java.util.Map;
|
2018-06-01 13:11:43 +02:00
|
|
|
import java.util.Set;
|
2018-05-28 00:58:13 +02:00
|
|
|
import java.util.WeakHashMap;
|
|
|
|
|
|
|
|
import org.jivesoftware.smack.Manager;
|
2018-05-30 22:06:09 +02:00
|
|
|
import org.jivesoftware.smack.SmackException;
|
2018-05-28 00:58:13 +02:00
|
|
|
import org.jivesoftware.smack.XMPPConnection;
|
2018-05-30 22:06:09 +02:00
|
|
|
import org.jivesoftware.smack.XMPPException;
|
2018-07-04 16:02:03 +02:00
|
|
|
import org.jivesoftware.smack.chat2.ChatManager;
|
2018-06-22 15:28:14 +02:00
|
|
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
2018-06-01 13:11:43 +02:00
|
|
|
import org.jivesoftware.smack.packet.Message;
|
2018-05-30 22:06:09 +02:00
|
|
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
2018-06-22 15:28:14 +02:00
|
|
|
import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement;
|
|
|
|
import org.jivesoftware.smackx.hints.element.StoreHint;
|
2018-06-01 13:11:43 +02:00
|
|
|
import org.jivesoftware.smackx.ox.element.SigncryptElement;
|
2018-06-22 15:28:14 +02:00
|
|
|
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
|
|
|
|
import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
|
2018-06-20 11:02:30 +02:00
|
|
|
import org.jivesoftware.smackx.ox.listener.OxMessageListener;
|
|
|
|
import org.jivesoftware.smackx.ox.listener.internal.SigncryptElementReceivedListener;
|
2018-07-05 13:44:40 +02:00
|
|
|
|
2018-05-28 00:58:13 +02:00
|
|
|
import org.jxmpp.jid.BareJid;
|
|
|
|
|
2018-05-30 22:06:09 +02:00
|
|
|
/**
|
|
|
|
* Entry point of Smacks API for XEP-0374: OpenPGP for XMPP: Instant Messaging.
|
|
|
|
*
|
|
|
|
* @see <a href="https://xmpp.org/extensions/xep-0374.html">
|
|
|
|
* XEP-0374: OpenPGP for XMPP: Instant Messaging</a>
|
|
|
|
*/
|
2018-06-20 11:02:30 +02:00
|
|
|
public final class OXInstantMessagingManager extends Manager implements SigncryptElementReceivedListener {
|
2018-05-30 22:06:09 +02:00
|
|
|
|
|
|
|
public static final String NAMESPACE_0 = "urn:xmpp:openpgp:im:0";
|
|
|
|
|
2018-05-28 00:58:13 +02:00
|
|
|
private static final Map<XMPPConnection, OXInstantMessagingManager> INSTANCES = new WeakHashMap<>();
|
|
|
|
|
2018-06-20 11:02:30 +02:00
|
|
|
private final Set<OxMessageListener> oxMessageListeners = new HashSet<>();
|
2018-06-01 13:11:43 +02:00
|
|
|
|
|
|
|
private OXInstantMessagingManager(final XMPPConnection connection) {
|
2018-05-28 00:58:13 +02:00
|
|
|
super(connection);
|
2018-06-27 11:56:08 +02:00
|
|
|
OpenPgpManager.getInstanceFor(connection).registerSigncryptReceivedListener(this);
|
2018-06-20 11:02:30 +02:00
|
|
|
announceSupportForOxInstantMessaging();
|
2018-05-28 00:58:13 +02:00
|
|
|
}
|
|
|
|
|
2018-07-05 14:25:29 +02:00
|
|
|
/**
|
|
|
|
* Return an instance of the {@link OXInstantMessagingManager} that belongs to the given {@code connection}.
|
|
|
|
*
|
|
|
|
* @param connection XMPP connection
|
|
|
|
*
|
|
|
|
* @return manager instance
|
|
|
|
*/
|
2018-05-28 00:58:13 +02:00
|
|
|
public static OXInstantMessagingManager getInstanceFor(XMPPConnection connection) {
|
|
|
|
OXInstantMessagingManager manager = INSTANCES.get(connection);
|
2018-06-19 16:41:02 +02:00
|
|
|
|
2018-05-28 00:58:13 +02:00
|
|
|
if (manager == null) {
|
|
|
|
manager = new OXInstantMessagingManager(connection);
|
|
|
|
INSTANCES.put(connection, manager);
|
|
|
|
}
|
2018-06-19 16:41:02 +02:00
|
|
|
|
2018-05-28 00:58:13 +02:00
|
|
|
return manager;
|
|
|
|
}
|
|
|
|
|
2018-07-05 14:25:29 +02:00
|
|
|
/**
|
|
|
|
* Add the OX:IM namespace as a feature to our disco features.
|
|
|
|
*/
|
2018-06-01 15:36:50 +02:00
|
|
|
public void announceSupportForOxInstantMessaging() {
|
|
|
|
ServiceDiscoveryManager.getInstanceFor(connection())
|
|
|
|
.addFeature(NAMESPACE_0);
|
|
|
|
}
|
|
|
|
|
2018-05-30 22:06:09 +02:00
|
|
|
/**
|
|
|
|
* Determine, whether a contact announces support for XEP-0374: OpenPGP for XMPP: Instant Messaging.
|
|
|
|
*
|
|
|
|
* @param jid {@link BareJid} of the contact in question.
|
|
|
|
* @return true if contact announces support, otherwise false.
|
2018-07-05 14:25:29 +02:00
|
|
|
* @throws XMPPException.XMPPErrorException in case of an XMPP protocol error
|
|
|
|
* @throws SmackException.NotConnectedException if we are not connected
|
2018-07-06 12:21:02 +02:00
|
|
|
* @throws InterruptedException if the thread gets interrupted
|
2018-07-05 14:25:29 +02:00
|
|
|
* @throws SmackException.NoResponseException if the server doesn't respond
|
2018-05-30 22:06:09 +02:00
|
|
|
*/
|
|
|
|
public boolean contactSupportsOxInstantMessaging(BareJid jid)
|
|
|
|
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
|
|
|
|
SmackException.NoResponseException {
|
|
|
|
return ServiceDiscoveryManager.getInstanceFor(connection()).supportsFeature(jid, NAMESPACE_0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-06-20 11:02:30 +02:00
|
|
|
* Determine, whether a contact announces support for XEP-0374: OpenPGP for XMPP: Instant Messaging.
|
2018-05-30 22:06:09 +02:00
|
|
|
*
|
2018-06-20 11:02:30 +02:00
|
|
|
* @param contact {@link OpenPgpContact} in question.
|
|
|
|
* @return true if contact announces support, otherwise false.
|
2018-07-05 14:25:29 +02:00
|
|
|
* @throws XMPPException.XMPPErrorException in case of an XMPP protocol error
|
|
|
|
* @throws SmackException.NotConnectedException if we are not connected
|
2018-07-06 12:21:02 +02:00
|
|
|
* @throws InterruptedException if the thread is interrupted
|
2018-07-05 14:25:29 +02:00
|
|
|
* @throws SmackException.NoResponseException if the server doesn't respond
|
2018-05-30 22:06:09 +02:00
|
|
|
*/
|
2018-06-20 11:02:30 +02:00
|
|
|
public boolean contactSupportsOxInstantMessaging(OpenPgpContact contact)
|
|
|
|
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
|
|
|
|
SmackException.NoResponseException {
|
2018-06-20 12:45:05 +02:00
|
|
|
return contactSupportsOxInstantMessaging(contact.getJid());
|
2018-05-30 22:06:09 +02:00
|
|
|
}
|
|
|
|
|
2018-07-05 14:25:29 +02:00
|
|
|
/**
|
|
|
|
* Add an {@link OxMessageListener}. The listener gets notified about incoming {@link OpenPgpMessage}s which
|
|
|
|
* contained an OX-IM message.
|
|
|
|
* @param listener listener
|
|
|
|
* @return true if the listener gets added, otherwise false.
|
|
|
|
*/
|
2018-06-20 11:02:30 +02:00
|
|
|
public boolean addOxMessageListener(OxMessageListener listener) {
|
|
|
|
return oxMessageListeners.add(listener);
|
2018-06-01 13:11:43 +02:00
|
|
|
}
|
2018-05-28 00:58:13 +02:00
|
|
|
|
2018-07-05 14:25:29 +02:00
|
|
|
/**
|
|
|
|
* Remove an {@link OxMessageListener}. The listener will no longer be notified about OX-IM messages.
|
|
|
|
* @param listener listener
|
|
|
|
* @return true, if the listener gets removed, otherwise false
|
|
|
|
*/
|
2018-06-20 11:02:30 +02:00
|
|
|
public boolean removeOxMessageListener(OxMessageListener listener) {
|
|
|
|
return oxMessageListeners.remove(listener);
|
2018-05-28 00:58:13 +02:00
|
|
|
}
|
2018-06-01 13:11:43 +02:00
|
|
|
|
2018-06-20 11:02:30 +02:00
|
|
|
@Override
|
|
|
|
public void signcryptElementReceived(OpenPgpContact contact, Message originalMessage, SigncryptElement signcryptElement) {
|
|
|
|
for (OxMessageListener listener : oxMessageListeners) {
|
|
|
|
listener.newIncomingOxMessage(contact, originalMessage, signcryptElement);
|
|
|
|
}
|
|
|
|
}
|
2018-06-22 15:28:14 +02:00
|
|
|
|
2018-07-05 14:25:29 +02:00
|
|
|
/**
|
|
|
|
* Send an OX message to a {@link OpenPgpContact}. The message will be encrypted to all active keys of the contact,
|
|
|
|
* as well as all of our active keys. The message is also signed with our key.
|
|
|
|
*
|
|
|
|
* @param contact contact capable of OpenPGP for XMPP: Instant Messaging.
|
|
|
|
* @param body message body.
|
2018-07-06 12:21:02 +02:00
|
|
|
* @throws InterruptedException if the thread is interrupted
|
2018-07-05 14:25:29 +02:00
|
|
|
* @throws MissingOpenPgpKeyPairException if we cannot access our signing key
|
|
|
|
* @throws IOException IO is dangerous
|
|
|
|
* @throws SmackException.NotConnectedException if we are not connected
|
|
|
|
* @throws SmackOpenPgpException in case of an OpenPGP error
|
|
|
|
* @throws SmackException.NotLoggedInException if we are not logged in
|
|
|
|
*/
|
2018-06-22 15:28:14 +02:00
|
|
|
public void sendOxMessage(OpenPgpContact contact, CharSequence body)
|
|
|
|
throws InterruptedException, MissingOpenPgpKeyPairException, IOException,
|
2018-07-03 18:07:54 +02:00
|
|
|
SmackException.NotConnectedException, SmackOpenPgpException, SmackException.NotLoggedInException {
|
2018-06-22 15:28:14 +02:00
|
|
|
Message message = new Message(contact.getJid());
|
|
|
|
List<ExtensionElement> payload = new ArrayList<>();
|
|
|
|
payload.add(new Message.Body(null, body.toString()));
|
|
|
|
|
|
|
|
// Add additional information to the message
|
|
|
|
message.addExtension(new ExplicitMessageEncryptionElement(
|
|
|
|
ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.openpgpV0));
|
|
|
|
StoreHint.set(message);
|
|
|
|
message.setBody("This message is encrypted using XEP-0374: OpenPGP for XMPP: Instant Messaging.");
|
|
|
|
|
2018-07-08 19:36:44 +02:00
|
|
|
//contact.addSignedEncryptedPayloadTo(message, payload);
|
2018-07-04 16:02:03 +02:00
|
|
|
|
|
|
|
ChatManager.getInstanceFor(connection()).chatWith(contact.getJid().asEntityBareJidIfPossible()).send(message);
|
2018-06-22 15:28:14 +02:00
|
|
|
}
|
2018-05-28 00:58:13 +02:00
|
|
|
}
|