Pass events down to contents

This commit is contained in:
vanitasvitae 2017-07-29 19:31:16 +02:00
parent 185d569b89
commit af069ffc49
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
9 changed files with 423 additions and 251 deletions

View File

@ -166,7 +166,7 @@ public final class JingleFileTransferManager extends Manager implements JingleDe
} }
@Override @Override
public void notifyContentAdd(JingleContent content) { public void notifyContentAdd(JingleSession session, JingleContent content) {
notifyTransfer((JingleFileTransfer) content.getDescription()); notifyTransfer((JingleFileTransfer) content.getDescription());
} }
} }

View File

@ -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.jft.adapter;

View File

@ -19,9 +19,13 @@ package org.jivesoftware.smackx.jft.controller;
import java.io.File; import java.io.File;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
/** /**
* Created by vanitas on 27.07.17. * Created by vanitas on 27.07.17.
*/ */
public interface IncomingFileOfferController extends JingleFileTransferController { public interface IncomingFileOfferController extends JingleFileTransferController {
Future<Void> accept(File target); Future<Void> accept(XMPPConnection connection, File target) throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException;
} }

View File

@ -23,6 +23,9 @@ import java.util.concurrent.Future;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.bytestreams.BytestreamSession; import org.jivesoftware.smackx.bytestreams.BytestreamSession;
import org.jivesoftware.smackx.jft.controller.IncomingFileOfferController; import org.jivesoftware.smackx.jft.controller.IncomingFileOfferController;
import org.jivesoftware.smackx.jft.element.JingleFileTransferChildElement; import org.jivesoftware.smackx.jft.element.JingleFileTransferChildElement;
@ -69,10 +72,12 @@ public class JingleIncomingFileOffer extends AbstractJingleFileOffer<RemoteFile>
} }
@Override @Override
public Future<Void> accept(File target) { public Future<Void> accept(XMPPConnection connection, File target)
throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException,
SmackException.NoResponseException {
JingleSession session = getParent().getParent(); JingleSession session = getParent().getParent();
if (session.getSessionState() == JingleSession.SessionState.pending) { if (session.getSessionState() == JingleSession.SessionState.pending) {
//session.accept(); session.accept(connection);
} }
return null; return null;

View File

@ -28,5 +28,5 @@ public interface JingleDescriptionManager {
void notifySessionInitiate(JingleSession session); void notifySessionInitiate(JingleSession session);
void notifyContentAdd(JingleContent content); void notifyContentAdd(JingleSession session, JingleContent content);
} }

View File

@ -27,6 +27,7 @@ import java.util.logging.Logger;
import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.bytestreams.BytestreamSession; import org.jivesoftware.smackx.bytestreams.BytestreamSession;
import org.jivesoftware.smackx.jingle.Callback; import org.jivesoftware.smackx.jingle.Callback;
@ -62,6 +63,69 @@ public class JingleContent implements JingleTransportCallback {
private final List<Callback> callbacks = Collections.synchronizedList(new ArrayList<Callback>()); private final List<Callback> callbacks = Collections.synchronizedList(new ArrayList<Callback>());
private final Set<String> transportBlacklist = Collections.synchronizedSet(new HashSet<String>()); private final Set<String> transportBlacklist = Collections.synchronizedSet(new HashSet<String>());
public IQ handleJingleRequest(JingleElement request, XMPPConnection connection) {
switch (request.getAction()) {
case content_modify:
return handleContentModify(request, connection);
case description_info:
return handleDescriptionInfo(request, connection);
case security_info:
return handleSecurityInfo(request, connection);
case session_info:
return handleSessionInfo(request, connection);
case transport_accept:
return handleTransportAccept(request, connection);
case transport_info:
return handleTransportInfo(request, connection);
case transport_reject:
return handleTransportReject(request, connection);
case transport_replace:
return handleTransportReplace(request, connection);
default:
throw new AssertionError("Illegal jingle action: " + request.getAction() + " is not allowed here.");
}
}
public IQ handleSessionAccept(JingleElement request, XMPPConnection connection) {
return IQ.createResultIQ(request);
}
public IQ handleContentModify(JingleElement request, XMPPConnection connection) {
return IQ.createResultIQ(request);
}
public IQ handleDescriptionInfo(JingleElement request, XMPPConnection connection) {
return IQ.createResultIQ(request);
}
public void handleContentRemove(JingleSession session, XMPPConnection connection) {
}
public IQ handleSecurityInfo(JingleElement request, XMPPConnection connection) {
return IQ.createResultIQ(request);
}
public IQ handleSessionInfo(JingleElement request, XMPPConnection connection) {
return IQ.createResultIQ(request);
}
public IQ handleTransportAccept(JingleElement request, XMPPConnection connection) {
return IQ.createResultIQ(request);
}
public IQ handleTransportInfo(JingleElement request, XMPPConnection connection) {
return IQ.createResultIQ(request);
}
public IQ handleTransportReject(JingleElement request, XMPPConnection connection) {
return IQ.createResultIQ(request);
}
public IQ handleTransportReplace(JingleElement request, XMPPConnection connection) {
return IQ.createResultIQ(request);
}
public enum STATE { public enum STATE {
pending_accept, pending_accept,
pending_transmission_start, pending_transmission_start,
@ -77,9 +141,9 @@ public class JingleContent implements JingleTransportCallback {
} }
public JingleContent(JingleDescription<?> description, JingleTransport<?> transport, JingleSecurity<?> security, String name, String disposition, JingleContentElement.Creator creator, JingleContentElement.Senders senders) { public JingleContent(JingleDescription<?> description, JingleTransport<?> transport, JingleSecurity<?> security, String name, String disposition, JingleContentElement.Creator creator, JingleContentElement.Senders senders) {
this.description = description; setDescription(description);
this.transport = transport; setTransport(transport);
this.security = security; setSecurity(security);
this.name = name; this.name = name;
this.disposition = disposition; this.disposition = disposition;
this.creator = creator; this.creator = creator;
@ -178,7 +242,7 @@ public class JingleContent implements JingleTransportCallback {
} }
public void setDescription(JingleDescription<?> description) { public void setDescription(JingleDescription<?> description) {
if (this.description != description) { if (description != null && this.description != description) {
this.description = description; this.description = description;
description.setParent(this); description.setParent(this);
} }
@ -189,7 +253,7 @@ public class JingleContent implements JingleTransportCallback {
} }
public void setTransport(JingleTransport<?> transport) { public void setTransport(JingleTransport<?> transport) {
if (this.transport != transport) { if (transport != null && this.transport != transport) {
this.transport = transport; this.transport = transport;
transport.setParent(this); transport.setParent(this);
} }
@ -200,7 +264,7 @@ public class JingleContent implements JingleTransportCallback {
} }
public void setSecurity(JingleSecurity<?> security) { public void setSecurity(JingleSecurity<?> security) {
if (this.security != security) { if (security != null && this.security != security) {
this.security = security; this.security = security;
security.setParent(this); security.setParent(this);
} }
@ -266,13 +330,16 @@ public class JingleContent implements JingleTransportCallback {
connection.createStanzaCollectorAndSend(transportReplace).nextResultOrThrow(); connection.createStanzaCollectorAndSend(transportReplace).nextResultOrThrow();
} }
public void onContentAccept(XMPPConnection connection) public void handleContentAccept(JingleElement request, XMPPConnection connection) {
throws SmackException.NotConnectedException, InterruptedException {
//Establish transport //Establish transport
if (isReceiving()) { try {
getTransport().establishIncomingBytestreamSession(connection, this, getParent()); if (isReceiving()) {
} else if (isSending()) { getTransport().establishIncomingBytestreamSession(connection, this, getParent());
getTransport().establishOutgoingBytestreamSession(connection, this, getParent()); } else if (isSending()) {
getTransport().establishOutgoingBytestreamSession(connection, this, getParent());
}
} catch (SmackException.NotConnectedException | InterruptedException e) {
LOGGER.log(Level.SEVERE, "Error establishing connection: " + e, e);
} }
} }

View File

@ -21,7 +21,6 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -33,12 +32,8 @@ import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.util.Async; import org.jivesoftware.smack.util.Async;
import org.jivesoftware.smackx.jingle.JingleDescriptionManager; import org.jivesoftware.smackx.jingle.JingleDescriptionManager;
import org.jivesoftware.smackx.jingle.JingleManager; import org.jivesoftware.smackx.jingle.JingleManager;
import org.jivesoftware.smackx.jingle.adapter.JingleTransportAdapter;
import org.jivesoftware.smackx.jingle.element.JingleAction; import org.jivesoftware.smackx.jingle.element.JingleAction;
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionElement;
import org.jivesoftware.smackx.jingle.element.JingleContentElement; import org.jivesoftware.smackx.jingle.element.JingleContentElement;
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
import org.jivesoftware.smackx.jingle.element.JingleContentTransportInfoElement;
import org.jivesoftware.smackx.jingle.element.JingleElement; import org.jivesoftware.smackx.jingle.element.JingleElement;
import org.jivesoftware.smackx.jingle.element.JingleReasonElement; import org.jivesoftware.smackx.jingle.element.JingleReasonElement;
import org.jivesoftware.smackx.jingle.exception.UnsupportedDescriptionException; import org.jivesoftware.smackx.jingle.exception.UnsupportedDescriptionException;
@ -55,6 +50,7 @@ public class JingleSession {
private static final Logger LOGGER = Logger.getLogger(JingleSession.class.getName()); private static final Logger LOGGER = Logger.getLogger(JingleSession.class.getName());
private final ConcurrentHashMap<String, JingleContent> contents = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, JingleContent> contents = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, JingleContent> proposedContents = new ConcurrentHashMap<>();
private final JingleManager jingleManager; private final JingleManager jingleManager;
private final FullJid initiator, responder; private final FullJid initiator, responder;
@ -82,36 +78,6 @@ public class JingleSession {
this.sessionState = SessionState.fresh; this.sessionState = SessionState.fresh;
} }
public void addContent(JingleContent content) {
contents.put(content.getName(), content);
content.setParent(this);
}
public void addContent(JingleContentElement content)
throws UnsupportedSecurityException, UnsupportedTransportException, UnsupportedDescriptionException {
addContent(JingleContent.fromElement(content));
}
public ConcurrentHashMap<String, JingleContent> getContents() {
return contents;
}
public JingleContent getContent(String name) {
return contents.get(name);
}
public JingleContent getSoleContentOrThrow() {
if (contents.isEmpty()) {
return null;
}
if (contents.size() > 1) {
throw new IllegalStateException();
}
return contents.values().iterator().next();
}
public static JingleSession fromSessionInitiate(JingleManager manager, JingleElement initiate) public static JingleSession fromSessionInitiate(JingleManager manager, JingleElement initiate)
throws UnsupportedSecurityException, UnsupportedDescriptionException, UnsupportedTransportException { throws UnsupportedSecurityException, UnsupportedDescriptionException, UnsupportedTransportException {
if (initiate.getAction() != JingleAction.session_initiate) { if (initiate.getAction() != JingleAction.session_initiate) {
@ -131,10 +97,23 @@ public class JingleSession {
} }
public void initiate(XMPPConnection connection) throws SmackException.NotConnectedException, InterruptedException, XMPPException.XMPPErrorException, SmackException.NoResponseException { public void initiate(XMPPConnection connection) throws SmackException.NotConnectedException, InterruptedException, XMPPException.XMPPErrorException, SmackException.NoResponseException {
if (this.sessionState != SessionState.fresh) {
throw new IllegalStateException("Session is not in fresh state.");
}
connection.createStanzaCollectorAndSend(createSessionInitiate()).nextResultOrThrow(); connection.createStanzaCollectorAndSend(createSessionInitiate()).nextResultOrThrow();
this.sessionState = SessionState.pending; this.sessionState = SessionState.pending;
} }
public void accept(XMPPConnection connection) throws SmackException.NotConnectedException, InterruptedException, XMPPException.XMPPErrorException, SmackException.NoResponseException {
if (this.sessionState != SessionState.pending) {
throw new IllegalStateException("Session is not in pending state.");
}
connection.createStanzaCollectorAndSend(createSessionAccept()).nextResultOrThrow();
this.sessionState = SessionState.active;
}
public JingleElement createSessionInitiate() { public JingleElement createSessionInitiate() {
if (role != Role.initiator) { if (role != Role.initiator) {
throw new IllegalStateException("Sessions role is not initiator."); throw new IllegalStateException("Sessions role is not initiator.");
@ -148,87 +127,81 @@ public class JingleSession {
return JingleElement.createSessionInitiate(getInitiator(), getResponder(), getSessionId(), contentElements); return JingleElement.createSessionInitiate(getInitiator(), getResponder(), getSessionId(), contentElements);
} }
public JingleElement createSessionAccept() {
if (role != Role.responder) {
throw new IllegalStateException("Sessions role is not responder.");
}
List<JingleContentElement> contentElements = new ArrayList<>();
for (JingleContent c : contents.values()) {
contentElements.add(c.getElement());
}
return JingleElement.createSessionAccept(getInitiator(), getResponder(), getSessionId(), contentElements);
}
public IQ handleJingleRequest(JingleElement request) { public IQ handleJingleRequest(JingleElement request) {
switch (request.getAction()) { switch (request.getAction()) {
case content_modify:
case description_info:
case security_info:
case session_info:
case transport_accept:
case transport_info:
case transport_reject:
case transport_replace:
return getSoleAffectedContentOrThrow(request).handleJingleRequest(request, jingleManager.getConnection());
case content_accept: case content_accept:
return handleContentAccept(request); return handleContentAccept(request);
case content_add: case content_add:
return handleContentAdd(request); return handleContentAdd(request);
case content_modify:
return handleContentModify(request);
case content_reject: case content_reject:
return handleContentReject(request); return handleContentReject(request);
case content_remove: case content_remove:
return handleContentRemove(request); return handleContentRemove(request);
case description_info:
return handleDescriptionInfo(request);
case session_info:
return handleSessionInfo(request);
case security_info:
return handleSecurityInfo(request);
case session_accept: case session_accept:
return handleSessionAccept(request); return handleSessionAccept(request);
case transport_accept:
return handleTransportAccept(request);
case transport_info:
return handleTransportInfo(request);
case session_initiate: case session_initiate:
return handleSessionInitiate(request); return handleSessionInitiate(request);
case transport_reject:
return handleTransportReject(request);
case session_terminate: case session_terminate:
return handleSessionTerminate(request); return handleSessionTerminate(request);
case transport_replace:
return handleTransportReplace(request);
default: default:
throw new AssertionError("Unknown Jingle Action enum! " + request.getAction()); throw new AssertionError("Illegal jingle action: " + request.getAction());
} }
} }
private IQ handleTransportReplace(final JingleElement request) { /* ############## Processed in this class ############## */
Async.go(new Runnable() {
@Override
public void run() {
List<JingleContentElement> affectedContents = request.getContents();
List<JingleElement> responses = new ArrayList<>();
for (JingleContentElement affected : affectedContents) { /**
JingleContent content = contents.get(affected.getName()); * Handle incoming session-accept stanza.
JingleContentTransportElement newTransport = affected.getTransport(); * @param request session-accept stanza.
Set<String> blacklist = content.getTransportBlacklist(); * @return result.
*/
private IQ handleSessionAccept(final JingleElement request) {
this.sessionState = SessionState.active;
// Proposed transport method might already be on the blacklist (eg. because of previous failures) for (final JingleContent content : contents.values()) {
if (blacklist.contains(newTransport.getNamespace())) { Async.go(new Runnable() {
responses.add(JingleElement.createTransportReject(getInitiator(), getPeer(), getSessionId(), @Override
content.getCreator(), content.getName(), newTransport)); public void run() {
continue; content.handleSessionAccept(request, jingleManager.getConnection());
}
JingleTransportAdapter<?> transportAdapter = JingleManager.getJingleTransportAdapter(
newTransport.getNamespace());
// This might be an unknown transport.
if (transportAdapter == null) {
responses.add(JingleElement.createTransportReject(getInitiator(), getPeer(), getSessionId(),
content.getCreator(), content.getName(), newTransport));
continue;
}
//Otherwise, when all went well so far, accept the transport-replace
content.setTransport(JingleManager.getJingleTransportAdapter(newTransport.getNamespace())
.transportFromElement(newTransport));
responses.add(JingleElement.createTransportAccept(getInitiator(), getPeer(), getSessionId(),
content.getCreator(), content.getName(), newTransport));
} }
});
}
for (JingleElement response : responses) { return IQ.createResultIQ(request);
try { }
jingleManager.getConnection().createStanzaCollectorAndSend(response).nextResultOrThrow();
} catch (SmackException.NoResponseException | XMPPException.XMPPErrorException | InterruptedException | SmackException.NotConnectedException e) { private IQ handleSessionInitiate(JingleElement request) {
LOGGER.log(Level.SEVERE, "Could not send response to transport-replace: " + e, e); JingleDescription<?> description = getSoleContentOrThrow().getDescription();
} JingleDescriptionManager descriptionManager = jingleManager.getDescriptionManager(description.getNamespace());
}
} if (descriptionManager == null) {
}); LOGGER.log(Level.WARNING, "Unsupported description type: " + description.getNamespace());
return JingleElement.createSessionTerminate(getPeer(), getSessionId(), JingleReasonElement.Reason.unsupported_applications);
}
descriptionManager.notifySessionInitiate(this);
return IQ.createResultIQ(request); return IQ.createResultIQ(request);
} }
@ -247,168 +220,193 @@ public class JingleSession {
return IQ.createResultIQ(request); return IQ.createResultIQ(request);
} }
private IQ handleTransportReject(JingleElement request) { private IQ handleContentAccept(final JingleElement request) {
HashMap<JingleContentElement, JingleContent> affectedContents = getAffectedContents(request); for (JingleContentElement a : request.getContents()) {
for (JingleContent c : affectedContents.values()) { final JingleContent accepted = proposedContents.get(a.getName());
} if (accepted == null) {
return null; throw new AssertionError("Illegal content name!");
}
private IQ handleSessionInitiate(JingleElement request) {
JingleDescription<?> description = getSoleContentOrThrow().getDescription();
JingleDescriptionManager descriptionManager = jingleManager.getDescriptionManager(description.getNamespace());
if (descriptionManager == null) {
LOGGER.log(Level.WARNING, "Unsupported description type: " + description.getNamespace());
return JingleElement.createSessionTerminate(getPeer(), getSessionId(), JingleReasonElement.Reason.unsupported_applications);
}
descriptionManager.notifySessionInitiate(this);
return IQ.createResultIQ(request);
}
private IQ handleTransportInfo(JingleElement request) {
HashMap<JingleContentElement, JingleContent> affectedContents = getAffectedContents(request);
for (Map.Entry<JingleContentElement, JingleContent> entry : affectedContents.entrySet()) {
JingleTransport<?> transport = entry.getValue().getTransport();
JingleContentTransportInfoElement info = entry.getKey().getTransport().getInfo();
transport.handleTransportInfo(info, request);
}
return IQ.createResultIQ(request);
}
private IQ handleTransportAccept(JingleElement request) {
HashMap<JingleContentElement, JingleContent> affectedContents = getAffectedContents(request);
for (Map.Entry<JingleContentElement, JingleContent> entry : affectedContents.entrySet()) {
PendingJingleAction pending = pendingJingleActions.get(entry.getValue());
if (pending == null) {
continue;
} }
if (pending.getAction() != JingleAction.transport_replace) { proposedContents.remove(accepted.getName());
//TODO: Are multiple contents even possible here? contents.put(accepted.getName(), accepted);
//TODO: How to react to partially illegal requests?
return JingleElement.createJingleErrorOutOfOrder(request);
}
entry.getValue().setTransport(((PendingJingleAction.TransportReplace) pending).getNewTransport()); Async.go(new Runnable() {
@Override
public void run() {
accepted.handleContentAccept(request, jingleManager.getConnection());
}
});
} }
return IQ.createResultIQ(request); return IQ.createResultIQ(request);
} }
private IQ handleSessionAccept(JingleElement request) {
this.sessionState = SessionState.active;
return null;
}
private IQ handleSecurityInfo(JingleElement request) {
HashMap<JingleContentElement, JingleContent> affectedContents = getAffectedContents(request);
List<JingleElement> responses = new ArrayList<>();
for (Map.Entry<JingleContentElement, JingleContent> entry : affectedContents.entrySet()) {
responses.add(entry.getValue().getSecurity().handleSecurityInfo(entry.getKey().getSecurity().getSecurityInfo(), request));
}
for (JingleElement response : responses) {
try {
getJingleManager().getConnection().createStanzaCollectorAndSend(response).nextResultOrThrow();
} catch (SmackException.NoResponseException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | InterruptedException e) {
LOGGER.log(Level.SEVERE, "Could not send response to security-info: " + e, e);
}
}
return IQ.createResultIQ(request);
}
private IQ handleSessionInfo(JingleElement request) {
return null;
}
private IQ handleDescriptionInfo(JingleElement request) {
HashMap<JingleContentElement, JingleContent> affectedContents = getAffectedContents(request);
List<JingleElement> responses = new ArrayList<>();
for (Map.Entry<JingleContentElement, JingleContent> entry : affectedContents.entrySet()) {
responses.add(entry.getValue().getDescription().handleDescriptionInfo(entry.getKey().getDescription().getDescriptionInfo()));
}
for (JingleElement response : responses) {
try {
getJingleManager().getConnection().createStanzaCollectorAndSend(response).nextResultOrThrow();
} catch (SmackException.NoResponseException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | InterruptedException e) {
LOGGER.log(Level.SEVERE, "Could not send response to description-info: " + e, e);
}
}
return IQ.createResultIQ(request);
}
private IQ handleContentRemove(JingleElement request) {
return null;
}
private IQ handleContentReject(JingleElement request) {
return null;
}
private IQ handleContentModify(JingleElement request) {
return null;
}
private IQ handleContentAdd(JingleElement request) { private IQ handleContentAdd(JingleElement request) {
final List<JingleContentElement> proposedContents = request.getContents(); final JingleContent proposed = getSoleProposedContentOrThrow(request);
final List<JingleContentElement> acceptedContents = new ArrayList<>();
final HashMap<String, List<JingleContent>> contentsByDescription = new HashMap<>(); final JingleDescriptionManager descriptionManager = jingleManager.getDescriptionManager(proposed.getDescription().getNamespace());
for (JingleContentElement p : proposedContents) { if (descriptionManager == null) {
JingleContentDescriptionElement description = p.getDescription(); throw new AssertionError("DescriptionManager is null: " + proposed.getDescription().getNamespace());
List<JingleContent> list = contentsByDescription.get(description.getNamespace());
if (list == null) {
list = new ArrayList<>();
contentsByDescription.put(description.getNamespace(), list);
}
list.add(JingleContent.fromElement(p));
} }
for (Map.Entry<String, List<JingleContent>> descriptionCategory : contentsByDescription.entrySet()) { Async.go(new Runnable() {
JingleDescriptionManager descriptionManager = JingleManager.getInstanceFor(getJingleManager().getConnection()).getDescriptionManager(descriptionCategory.getKey()); @Override
public void run() {
if (descriptionManager == null) { descriptionManager.notifyContentAdd(JingleSession.this, proposed);
//blabla
continue;
} }
});
for (final JingleContent content : descriptionCategory.getValue()) {
descriptionManager.notifyContentAdd(content);
}
}
if (acceptedContents.size() > 0) {
JingleElement accept = JingleElement.createContentAccept(getPeer(), getSessionId(), acceptedContents);
try {
getJingleManager().getConnection().createStanzaCollectorAndSend(accept).nextResultOrThrow();
} catch (SmackException.NoResponseException | XMPPException.XMPPErrorException | InterruptedException | SmackException.NotConnectedException e) {
LOGGER.log(Level.SEVERE, "Could not send response to content-add: " + e, e);
}
}
//TODO: send content-reject for rejected contents!
return IQ.createResultIQ(request); return IQ.createResultIQ(request);
} }
private IQ handleContentAccept(JingleElement request) { private IQ handleContentReject(JingleElement request) {
return null; for (JingleContentElement r : request.getContents()) {
final JingleContent rejected = proposedContents.get(r.getName());
if (rejected == null) {
throw new AssertionError("Illegal content name!");
}
proposedContents.remove(rejected.getName());
/*
Async.go(new Runnable() {
@Override
public void run() {
rejected.handleContentReject(request, jingleManager.getConnection());
}
});
*/
}
return IQ.createResultIQ(request);
} }
private IQ handleContentRemove(final JingleElement request) {
for (JingleContentElement r : request.getContents()) {
final JingleContent removed = contents.get(r.getName());
if (removed == null) {
throw new AssertionError("Illegal content name!");
}
contents.remove(removed.getName());
Async.go(new Runnable() {
@Override
public void run() {
removed.handleContentRemove(JingleSession.this, jingleManager.getConnection());
}
});
}
return IQ.createResultIQ(request);
}
/* ############## Processed further down ############## */
private IQ handleContentModify(final JingleElement request) {
final JingleContent content = getSoleAffectedContentOrThrow(request);
Async.go(new Runnable() {
@Override
public void run() {
content.handleContentModify(request, jingleManager.getConnection());
}
});
return IQ.createResultIQ(request);
}
private IQ handleDescriptionInfo(final JingleElement request) {
final JingleContent content = getSoleAffectedContentOrThrow(request);
Async.go(new Runnable() {
@Override
public void run() {
content.handleDescriptionInfo(request, jingleManager.getConnection());
}
});
return IQ.createResultIQ(request);
}
private IQ handleSecurityInfo(final JingleElement request) {
final JingleContent content = getSoleAffectedContentOrThrow(request);
Async.go(new Runnable() {
@Override
public void run() {
content.handleSecurityInfo(request, jingleManager.getConnection());
}
});
return IQ.createResultIQ(request);
}
private IQ handleSessionInfo(final JingleElement request) {
final JingleContent content = getSoleAffectedContentOrThrow(request);
Async.go(new Runnable() {
@Override
public void run() {
content.handleSessionInfo(request, jingleManager.getConnection());
}
});
return IQ.createResultIQ(request);
}
private IQ handleTransportAccept(final JingleElement request) {
final JingleContent content = getSoleAffectedContentOrThrow(request);
Async.go(new Runnable() {
@Override
public void run() {
content.handleTransportAccept(request, jingleManager.getConnection());
}
});
return IQ.createResultIQ(request);
}
private IQ handleTransportInfo(final JingleElement request) {
final JingleContent content = getSoleAffectedContentOrThrow(request);
Async.go(new Runnable() {
@Override
public void run() {
content.handleTransportInfo(request, jingleManager.getConnection());
}
});
return IQ.createResultIQ(request);
}
private IQ handleTransportReject(final JingleElement request) {
final JingleContent content = getSoleAffectedContentOrThrow(request);
Async.go(new Runnable() {
@Override
public void run() {
content.handleTransportReject(request, jingleManager.getConnection());
}
});
return IQ.createResultIQ(request);
}
private IQ handleTransportReplace(final JingleElement request) {
final JingleContent content = getSoleAffectedContentOrThrow(request);
Async.go(new Runnable() {
@Override
public void run() {
content.handleTransportReplace(request, jingleManager.getConnection());
}
});
return IQ.createResultIQ(request);
}
/* ################ Other getters and setters ############### */
public FullJid getInitiator() { public FullJid getInitiator() {
return initiator; return initiator;
} }
@ -453,6 +451,57 @@ public class JingleSession {
return map; return map;
} }
private JingleContent getSoleAffectedContentOrThrow(JingleElement request) {
if (request.getContents().size() != 1) {
throw new AssertionError("More/less than 1 content in request!");
}
JingleContent content = contents.get(request.getContents().get(0).getName());
if (content == null) {
throw new AssertionError("Illegal content name!");
}
return content;
}
private JingleContent getSoleProposedContentOrThrow(JingleElement request) {
if (request.getContents().size() != 1) {
throw new AssertionError("More/less than 1 content in request!");
}
return JingleContent.fromElement(request.getContents().get(0));
}
public void addContent(JingleContent content) {
contents.put(content.getName(), content);
content.setParent(this);
}
public void addContent(JingleContentElement content)
throws UnsupportedSecurityException, UnsupportedTransportException, UnsupportedDescriptionException {
addContent(JingleContent.fromElement(content));
}
public ConcurrentHashMap<String, JingleContent> getContents() {
return contents;
}
public JingleContent getContent(String name) {
return contents.get(name);
}
public JingleContent getSoleContentOrThrow() {
if (contents.isEmpty()) {
return null;
}
if (contents.size() > 1) {
throw new IllegalStateException();
}
return contents.values().iterator().next();
}
public SessionState getSessionState() { public SessionState getSessionState() {
return sessionState; return sessionState;
} }

View File

@ -1,3 +1,19 @@
/**
*
* 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.element; package org.jivesoftware.smackx.jingle.element;
import org.jivesoftware.smack.packet.NamedElement; import org.jivesoftware.smack.packet.NamedElement;

View File

@ -27,6 +27,8 @@ import java.util.ArrayList;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.logging.Level; import java.util.logging.Level;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy; import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy;
import org.jivesoftware.smackx.jft.controller.IncomingFileOfferController; import org.jivesoftware.smackx.jft.controller.IncomingFileOfferController;
import org.jivesoftware.smackx.jft.controller.OutgoingFileOfferController; import org.jivesoftware.smackx.jft.controller.OutgoingFileOfferController;
@ -86,6 +88,7 @@ public class JingleFileTransferTest extends AbstractSmackIntegrationTest {
@Override @Override
public void onIncomingFileOffer(IncomingFileOfferController offer) { public void onIncomingFileOffer(IncomingFileOfferController offer) {
LOGGER.log(Level.INFO, "INCOMING FILE TRANSFER!"); LOGGER.log(Level.INFO, "INCOMING FILE TRANSFER!");
offer.addProgressListener(new ProgressListener() { offer.addProgressListener(new ProgressListener() {
@Override @Override
public void started() { public void started() {
@ -102,11 +105,17 @@ public class JingleFileTransferTest extends AbstractSmackIntegrationTest {
resultSyncPoint2.signal(); resultSyncPoint2.signal();
} }
}); });
receiveFuture.add(offer.accept(target));
try {
receiveFuture.add(offer.accept(conTwo, target));
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
fail(e.toString());
}
} }
}); });
OutgoingFileOfferController sending = aftm.sendFile(source, bob); OutgoingFileOfferController sending = aftm.sendFile(source, bob);
sending.addProgressListener(new ProgressListener() { sending.addProgressListener(new ProgressListener() {
@Override @Override
public void started() { public void started() {