Skip to content

Commit

Permalink
Add Jingle changes to smack-experimental
Browse files Browse the repository at this point in the history
  • Loading branch information
gkfabs committed Oct 28, 2021
1 parent d5045fd commit 3ce304e
Show file tree
Hide file tree
Showing 36 changed files with 2,207 additions and 116 deletions.
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ allprojects {
junit4Projects = [
':smack-core',
':smack-extensions',
':smack-experimental',
':smack-integration-test',
':smack-im',
':smack-omemo',
':smack-omemo-signal',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,201 @@
*/
package org.jivesoftware.smackx.jingle_filetransfer;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.jingle.JingleDescriptionManager;
import org.jivesoftware.smackx.jingle.JingleManager;
import org.jivesoftware.smackx.jingle.JingleTransportManager;
import org.jivesoftware.smackx.jingle.component.JingleContent;
import org.jivesoftware.smackx.jingle.component.JingleSession;
import org.jivesoftware.smackx.jingle.component.JingleTransport;
import org.jivesoftware.smackx.jingle.element.JingleContentElement;
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.JingleIBBTransport;
import org.jivesoftware.smackx.jingle.util.Role;
import org.jivesoftware.smackx.jingle_filetransfer.adapter.JingleFileTransferAdapter;
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleFile;
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleFileTransfer;
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleIncomingFileOffer;
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleIncomingFileRequest;
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleOutgoingFileOffer;
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleOutgoingFileRequest;
import org.jivesoftware.smackx.jingle_filetransfer.controller.OutgoingFileOfferController;
import org.jivesoftware.smackx.jingle_filetransfer.controller.OutgoingFileRequestController;
import org.jivesoftware.smackx.jingle_filetransfer.listener.IncomingFileOfferListener;
import org.jivesoftware.smackx.jingle_filetransfer.listener.IncomingFileRequestListener;
import org.jivesoftware.smackx.jingle_filetransfer.provider.JingleFileTransferProvider;

import org.jxmpp.jid.FullJid;

/**
* Manager for JingleFileTransfer (XEP-0234).
* Manager for XEP-0234 - JingleFileTransfers.
*/
public final class JingleFileTransferManager extends Manager {
public final class JingleFileTransferManager extends Manager implements JingleDescriptionManager {

private static final Logger LOGGER = Logger.getLogger(JingleFileTransferManager.class.getName());

private static final WeakHashMap<XMPPConnection, JingleFileTransferManager> INSTANCES = new WeakHashMap<>();

/**
* Reference to the JingleManager of the connection.
*/
private final JingleManager jingleManager;

/**
* Listeners for incoming file offers.
*/
private final List<IncomingFileOfferListener> offerListeners =
Collections.synchronizedList(new ArrayList<IncomingFileOfferListener>());

/**
* Listeners for incoming file requests.
*/
private final List<IncomingFileRequestListener> requestListeners =
Collections.synchronizedList(new ArrayList<IncomingFileRequestListener>());

static {
// Register adapters and providers.
JingleManager.addJingleDescriptionAdapter(new JingleFileTransferAdapter());
JingleManager.addJingleDescriptionProvider(new JingleFileTransferProvider());
}

private JingleFileTransferManager(XMPPConnection connection) {
super(connection);
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(getNamespace());
jingleManager = JingleManager.getInstanceFor(connection);
jingleManager.addJingleDescriptionManager(this);
}

public static synchronized JingleFileTransferManager getInstanceFor(XMPPConnection connection) {
JingleFileTransferManager manager = INSTANCES.get(connection);

if (manager == null) {
manager = new JingleFileTransferManager(connection);
INSTANCES.put(connection, manager);
}

return manager;
}

public OutgoingFileOfferController sendFile(File file, FullJid to)
throws SmackException.NotConnectedException, InterruptedException, XMPPException.XMPPErrorException,
SmackException.NoResponseException, SmackException.FeatureNotSupportedException, IOException, NoSuchAlgorithmException {
return sendFile(file, JingleFile.fromFile(file, null, null, null), to);
}

public OutgoingFileOfferController sendFile(File file, JingleFile metadata, FullJid to) throws SmackException.FeatureNotSupportedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, FileNotFoundException {
if (file == null || !file.exists()) {
throw new IllegalArgumentException("File MUST NOT be null and MUST exist.");
}

FileInputStream fi = new FileInputStream(file);

return sendStream(fi, metadata, to);
}

public OutgoingFileOfferController sendStream(final InputStream stream, JingleFile metadata, FullJid recipient) throws SmackException.FeatureNotSupportedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
if (!ServiceDiscoveryManager.getInstanceFor(connection()).supportsFeature(recipient, getNamespace())) {
throw new SmackException.FeatureNotSupportedException(getNamespace(), recipient);
}

JingleSession session = jingleManager.createSession(Role.initiator, recipient);

JingleContent content = new JingleContent(JingleContentElement.Creator.initiator, JingleContentElement.Senders.initiator);
session.addContent(content);

JingleOutgoingFileOffer outgoingFileOffer = new JingleOutgoingFileOffer(stream, metadata);

content.setDescription(outgoingFileOffer);

JingleTransportManager transportManager = jingleManager.getBestAvailableTransportManager(recipient);
if (transportManager == null) {
// At least Jingle IBB should be supported.
throw new SmackException.FeatureNotSupportedException(JingleIBBTransport.NAMESPACE, recipient);
}
JingleTransport<?> transport = transportManager.createTransportForInitiator(content);
content.setTransport(transport);

session.sendInitiate(connection());

return outgoingFileOffer;
}

public OutgoingFileRequestController requestFile(JingleFile metadata, FullJid from) {
JingleOutgoingFileRequest request = new JingleOutgoingFileRequest(metadata);

// TODO at some point.

return request;
}

public void addIncomingFileOfferListener(IncomingFileOfferListener listener) {
offerListeners.add(listener);
}

public void removeIncomingFileOfferListener(IncomingFileOfferListener listener) {
offerListeners.remove(listener);
}

public void notifyIncomingFileOfferListeners(JingleIncomingFileOffer offer) {
LOGGER.log(Level.FINE, "Incoming File transfer: [" + offer.getNamespace() + ", "
+ offer.getParent().getTransport().getNamespace() + ", "
+ (offer.getParent().getSecurity() != null ? offer.getParent().getSecurity().getNamespace() : "") + "]");
for (IncomingFileOfferListener l : offerListeners) {
l.onIncomingFileOffer(offer);
}
}

public void addIncomingFileRequestListener(IncomingFileRequestListener listener) {
requestListeners.add(listener);
}

public void removeIncomingFileRequestListener(IncomingFileRequestListener listener) {
requestListeners.remove(listener);
}

public void notifyIncomingFileRequestListeners(JingleIncomingFileRequest request) {
for (IncomingFileRequestListener l : requestListeners) {
l.onIncomingFileRequest(request);
}
}

@Override
public String getNamespace() {
return JingleFileTransfer.NAMESPACE;
}

private void notifyTransfer(JingleFileTransfer transfer) {
if (transfer.isOffer()) {
notifyIncomingFileOfferListeners((JingleIncomingFileOffer) transfer);
} else {
notifyIncomingFileRequestListeners((JingleIncomingFileRequest) transfer);
}
}

@Override
public void notifySessionInitiate(JingleSession session) {
JingleContent content = session.getSoleContentOrThrow();
notifyTransfer((JingleFileTransfer) content.getDescription());
}

@Override
public void notifyContentAdd(JingleSession session, JingleContent content) {
notifyTransfer((JingleFileTransfer) content.getDescription());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
*
* Copyright 2017 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.jingle_filetransfer.adapter;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smackx.jingle.adapter.JingleDescriptionAdapter;
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionElement;
import org.jivesoftware.smackx.jingle.element.JingleContentElement;
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleFileTransfer;
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleIncomingFileOffer;
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleIncomingFileRequest;
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChildElement;
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferElement;

/**
* Adapter to convert JingleFileTransferElements (element) to JingleFileTransfer objects (component).
*/
public class JingleFileTransferAdapter implements JingleDescriptionAdapter<JingleFileTransfer> {

private static final Logger LOGGER = Logger.getLogger(JingleFileTransferAdapter.class.getName());

@Override
public JingleFileTransfer descriptionFromElement(JingleContentElement.Creator creator, JingleContentElement.Senders senders,
String contentName, String contentDisposition, JingleContentDescriptionElement element) {
JingleFileTransferElement description = (JingleFileTransferElement) element;
List<NamedElement> children = description.getJingleContentDescriptionChildren();
assert children.size() == 1;
JingleFileTransferChildElement file = (JingleFileTransferChildElement) children.get(0);

if (senders == JingleContentElement.Senders.initiator) {
return new JingleIncomingFileOffer(file);
} else if (senders == JingleContentElement.Senders.responder) {
return new JingleIncomingFileRequest(file);
} else {
if (senders == null) {
LOGGER.log(Level.INFO, "Senders is null. Gajim workaround: assume 'initiator'.");
return new JingleIncomingFileOffer(file);
}
throw new AssertionError("Senders attribute MUST be either initiator or responder. Is: " + senders);
}
}

@Override
public String getNamespace() {
return JingleFileTransfer.NAMESPACE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
*
* Copyright 2017 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.
*/

/**
* Smack's API for <a href="https://xmpp.org/extensions/xep-0234.html">XEP-0234: Jingle File Transfer</a>.
* Adapters.
*/
package org.jivesoftware.smackx.jingle_filetransfer.adapter;
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
*
* Copyright 2017 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.jingle_filetransfer.component;

/**
* This class represents the base of a JingleFileOffer.
*/
public abstract class AbstractJingleFileOffer extends JingleFileTransfer {

AbstractJingleFileOffer(JingleFile fileTransferFile) {
super(fileTransferFile);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
*
* Copyright 2017 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.jingle_filetransfer.component;

/**
* Class representing the base of a JingleFileRequest.
* TODO: Implement.
*/
public abstract class AbstractJingleFileRequest extends JingleFileTransfer {

AbstractJingleFileRequest(JingleFile fileTransferFile) {
super(fileTransferFile);
}
}
Loading

0 comments on commit 3ce304e

Please sign in to comment.