protected void rejectIncomingFileTransfer(FileTransferRequest request) { StreamInitiation initiation = request.getStreamInitiation(); IQ rejection = FileTransferNegotiator.createIQ( initiation.getPacketID(), initiation.getFrom(), initiation.getTo(), IQ.Type.ERROR); rejection.setError(new XMPPError(XMPPError.Condition.no_acceptable)); connection.sendPacket(rejection); }
public InputStream createIncomingStream(StreamInitiation initiation) throws XMPPException { PacketCollector collector = connection.createPacketCollector( getInitiationPacketFilter(initiation.getFrom(), initiation.getSessionID())); connection.sendPacket(super.createInitiationAccept(initiation, getNamespaces())); ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(2); CompletionService<InputStream> service = new ExecutorCompletionService<InputStream>(threadPoolExecutor); List<Future<InputStream>> futures = new ArrayList<Future<InputStream>>(); InputStream stream = null; XMPPException exception = null; try { futures.add(service.submit(new NegotiatorService(collector))); futures.add(service.submit(new NegotiatorService(collector))); int i = 0; while (stream == null && i < futures.size()) { Future<InputStream> future; try { i++; future = service.poll(10, TimeUnit.SECONDS); } catch (InterruptedException e) { continue; } if (future == null) { continue; } try { stream = future.get(); } catch (InterruptedException e) { /* Do Nothing */ } catch (ExecutionException e) { exception = new XMPPException(e.getCause()); } } } finally { for (Future<InputStream> future : futures) { future.cancel(true); } collector.cancel(); threadPoolExecutor.shutdownNow(); } if (stream == null) { if (exception != null) { throw exception; } else { throw new XMPPException("File transfer negotiation failed."); } } return stream; }
/** * Selects an appropriate stream negotiator after examining the incoming file transfer request. * * @param request The related file transfer request. * @return The file transfer object that handles the transfer * @throws XMPPException If there are either no stream methods contained in the packet, or there * is not an appropriate stream method. */ public StreamNegotiator selectStreamNegotiator(FileTransferRequest request) throws XMPPException { final StreamInitiation si = request.getStreamInitiation(); final FormField streamMethodField = getStreamMethodField(si.getFeatureNegotiationForm()); if (streamMethodField == null) { final String errorMessage = "No stream methods contained in packet."; final XMPPError error = new XMPPError(XMPPError.Condition.bad_request, errorMessage); final IQ iqPacket = createIQ(si.getPacketID(), si.getFrom(), si.getTo(), IQ.Type.ERROR); iqPacket.setError(error); connection.sendPacket(iqPacket); throw new XMPPException(errorMessage, error); } // select the appropriate protocol StreamNegotiator selectedStreamNegotiator; try { selectedStreamNegotiator = getNegotiator(streamMethodField); } catch (final XMPPException e) { final IQ iqPacket = createIQ(si.getPacketID(), si.getFrom(), si.getTo(), IQ.Type.ERROR); iqPacket.setError(e.getXMPPError()); connection.sendPacket(iqPacket); throw e; } // return the appropriate negotiator return selectedStreamNegotiator; }
/** * Listens for file transfer packets. * * @param packet packet to be processed */ public void processPacket(Packet packet) { if (!(packet instanceof StreamInitiation)) return; if (logger.isDebugEnabled()) logger.debug("Incoming Jabber file transfer request."); StreamInitiation streamInitiation = (StreamInitiation) packet; FileTransferRequest jabberRequest = new FileTransferRequest(manager, streamInitiation); // Create a global incoming file transfer request. IncomingFileTransferRequestJabberImpl incomingFileTransferRequest = new IncomingFileTransferRequestJabberImpl( jabberProvider, OperationSetFileTransferJabberImpl.this, jabberRequest); // Send a thumbnail request if a thumbnail is advertised in the // streamInitiation packet. org.jivesoftware.smackx.packet.StreamInitiation.File file = streamInitiation.getFile(); boolean isThumbnailedFile = false; if (file instanceof FileElement) { ThumbnailElement thumbnailElement = ((FileElement) file).getThumbnailElement(); if (thumbnailElement != null) { isThumbnailedFile = true; incomingFileTransferRequest.createThumbnailListeners(thumbnailElement.getCid()); ThumbnailIQ thumbnailRequest = new ThumbnailIQ( streamInitiation.getTo(), streamInitiation.getFrom(), thumbnailElement.getCid(), IQ.Type.GET); if (logger.isDebugEnabled()) logger.debug("Sending thumbnail request:" + thumbnailRequest.toXML()); jabberProvider.getConnection().sendPacket(thumbnailRequest); } } if (!isThumbnailedFile) { // Create an event associated to this global request. FileTransferRequestEvent fileTransferRequestEvent = new FileTransferRequestEvent( OperationSetFileTransferJabberImpl.this, incomingFileTransferRequest, new Date()); // Notify the global listener that a request has arrived. fireFileTransferRequest(fileTransferRequestEvent); } }
/** * Reject a stream initiation request from a remote user. * * @param si The Stream Initiation request to reject. */ public void rejectStream(final StreamInitiation si) { final XMPPError error = new XMPPError(XMPPError.Condition.forbidden, "Offer Declined"); final IQ iqPacket = createIQ(si.getPacketID(), si.getFrom(), si.getTo(), IQ.Type.ERROR); iqPacket.setError(error); connection.sendPacket(iqPacket); }
/** * Send a request to another user to send them a file. The other user has the option of, * accepting, rejecting, or not responding to a received file transfer request. * * <p>If they accept, the packet will contain the other user's chosen stream type to send the file * across. The two choices this implementation provides to the other user for file transfer are <a * href="http://www.jabber.org/jeps/jep-0065.html">SOCKS5 Bytestreams</a>, which is the preferred * method of transfer, and <a href="http://www.jabber.org/jeps/jep-0047.html">In-Band * Bytestreams</a>, which is the fallback mechanism. * * <p>The other user may choose to decline the file request if they do not desire the file, their * client does not support JEP-0096, or if there are no acceptable means to transfer the file. * * <p>Finally, if the other user does not respond this method will return null after the specified * timeout. * * @param userID The userID of the user to whom the file will be sent. * @param streamID The unique identifier for this file transfer. * @param fileName The name of this file. Preferably it should include an extension as it is used * to determine what type of file it is. * @param size The size, in bytes, of the file. * @param desc A description of the file. * @param responseTimeout The amount of time, in milliseconds, to wait for the remote user to * respond. If they do not respond in time, this * @return Returns the stream negotiator selected by the peer. * @throws XMPPException Thrown if there is an error negotiating the file transfer. */ public StreamNegotiator negotiateOutgoingTransfer( final String userID, final String streamID, final String fileName, final long size, final String desc, int responseTimeout) throws XMPPException { final StreamInitiation si = new StreamInitiation(); si.setSesssionID(streamID); si.setMimeType(URLConnection.guessContentTypeFromName(fileName)); final StreamInitiation.File siFile = new StreamInitiation.File(fileName, size); siFile.setDesc(desc); si.setFile(siFile); si.setFeatureNegotiationForm(createDefaultInitiationForm()); si.setFrom(connection.getUser()); si.setTo(userID); si.setType(IQ.Type.SET); final PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(si.getPacketID())); connection.sendPacket(si); final Packet siResponse = collector.nextResult(responseTimeout); collector.cancel(); if (siResponse instanceof IQ) { final IQ iqResponse = (IQ) siResponse; if (iqResponse.getType().equals(IQ.Type.RESULT)) { final StreamInitiation response = (StreamInitiation) siResponse; return getOutgoingNegotiator(getStreamMethodField(response.getFeatureNegotiationForm())); } else if (iqResponse.getType().equals(IQ.Type.ERROR)) { throw new XMPPException(iqResponse.getError()); } else { throw new XMPPException("File transfer response unreadable"); } } else { return null; } }
/** * Parses the given <tt>parser</tt> in order to create a <tt>FileElement</tt> from it. * * @param parser the parser to parse * @see IQProvider#parseIQ(XmlPullParser) */ public IQ parseIQ(final XmlPullParser parser) throws Exception { boolean done = false; // si String id = parser.getAttributeValue("", "id"); String mimeType = parser.getAttributeValue("", "mime-type"); StreamInitiation initiation = new StreamInitiation(); // file String name = null; String size = null; String hash = null; String date = null; String desc = null; ThumbnailElement thumbnail = null; boolean isRanged = false; // feature DataForm form = null; DataFormProvider dataFormProvider = new DataFormProvider(); int eventType; String elementName; String namespace; while (!done) { eventType = parser.next(); elementName = parser.getName(); namespace = parser.getNamespace(); if (eventType == XmlPullParser.START_TAG) { if (elementName.equals("file")) { name = parser.getAttributeValue("", "name"); size = parser.getAttributeValue("", "size"); hash = parser.getAttributeValue("", "hash"); date = parser.getAttributeValue("", "date"); } else if (elementName.equals("desc")) { desc = parser.nextText(); } else if (elementName.equals("range")) { isRanged = true; } else if (elementName.equals("x") && namespace.equals("jabber:x:data")) { form = (DataForm) dataFormProvider.parseExtension(parser); } else if (elementName.equals("thumbnail")) { thumbnail = new ThumbnailElement(parser.getText()); } } else if (eventType == XmlPullParser.END_TAG) { if (elementName.equals("si")) { done = true; } // The name-attribute is required per XEP-0096, so ignore the // IQ if the name is not set to avoid exceptions. Particularly, // the SI response of Empathy contains an invalid, empty // file-tag. else if (elementName.equals("file") && name != null) { long fileSize = 0; if (size != null && size.trim().length() != 0) { try { fileSize = Long.parseLong(size); } catch (NumberFormatException e) { logger.warn( "Received an invalid file size," + " continuing with fileSize set to 0", e); } } FileElement file = new FileElement(name, fileSize); file.setHash(hash); if (date != null) { // try all known date formats boolean found = false; if (date.matches(".*?T\\d+:\\d+:\\d+(\\.\\d+)?(\\+|-)\\d+:\\d+")) { int timeZoneColon = date.lastIndexOf(":"); date = date.substring(0, timeZoneColon) + date.substring(timeZoneColon + 1, date.length()); } for (DateFormat fmt : DATE_FORMATS) { try { file.setDate(fmt.parse(date)); found = true; break; } catch (ParseException ex) { } } if (!found) { logger.warn("Unknown dateformat on incoming file transfer: " + date); } } if (thumbnail != null) file.setThumbnailElement(thumbnail); file.setDesc(desc); file.setRanged(isRanged); initiation.setFile(file); } } } initiation.setSesssionID(id); initiation.setMimeType(mimeType); initiation.setFeatureNegotiationForm(form); return initiation; }