/**
* $RCSfile$
* $Revision: 2495 $
* $Date: 2005-05-30 10:14:25 -0500 (Mon, 30 May 2005) $
*
* Copyright 2003-2004 Jive Software.
*
* All rights reserved. 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.filetransfer;
import java.io.*;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.*;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.packet.*;
import org.jivesoftware.smack.packet.*;
import java.util.*;
/**
* Provides a method to send IBB data
*
*@author Adam Olsen
*/
public class IBBSend extends FileTransfer {
private FileSend send = null;
private InputStream stream = null;
private XMPPConnection connection;
private String sid = null;
private boolean ft = false;
private IBBOpen request = null;
private long total = 0;
private long totalBytes = 0;
private ArrayList progress = new ArrayList();
private boolean cancelled = false;
/**
* Constructer used when using IBB to send a file
*
*@param send The FileSend wrapper of this stream
*@param stream The stream to read from
*/
public IBBSend(FileSend send, InputStream stream) {
this.send = send;
this.stream = stream;
this.total = send.getSize();
this.connection = send.getManager().getConnection();
this.sid = send.getSI().getSid();
ft = true;
}
/**
* Constructer used when you want to make a standalone IBB stream
*
*@param connection The connection to use for this stream
*@param req The request to use for this stream
*/
public IBBSend(XMPPConnection connection, IBBOpen req) {
this.connection = connection;
this.request = req;
this.sid = req.getSid();
}
/**
* Cancels the transfer
*/
public void cancel() { cancelled = true; }
/**
* Sets the progress listeners interested in the progress of this stream
*
*@param list The listeners interested in this stream
*/
void setProgressListeners(ArrayList list) {
this.progress = list;
}
/**
* Reads a chunk of data from the input stream and base64 encodes it
*
*@return Description of the Return Value
*@throws IOException Description of the Exception
*/
private IBBChunk readChunk() throws IOException {
byte buf[] = new byte[4096];
int read = 0;
if ((read = stream.read(buf)) == -1) {
return null;
}
totalBytes += read;
if (ft) {
FileTransferManager.update(FileTransferManager.Event.TRANSFERRING,
progress, (float) totalBytes / (float) total, totalBytes);
}
if (read != 4096) {
byte n[] = new byte[read];
for (int i = 0; i < read; i++) {
n[i] = buf[i];
}
buf = n;
}
IBBChunk chunk = new IBBChunk();
chunk.setData(new String(StringUtils.encodeBase64(buf)));
return chunk;
}
/**
* Use this method to send the stream when you are using standalone IBB. It
* will be sent to the person in the "to" field of the IBB request
*
*@throws IOException When an error reading the InputStream occurs
*@throws XMPPException When an error sending occurs
*/
public void send() throws IOException, XMPPException {
send(request.getTo());
}
/**
* Sends the file to the specified recipient
*
*@param to The JID of the person you want to send the file
*@throws IOException When an error reading the InputStream occurs
*@throws XMPPException When an error sending occurs
*/
public void send(String to) throws IOException, XMPPException {
if (request == null) {
request = new IBBOpen();
request.setType(IQ.Type.SET);
request.setSid(sid);
request.setFrom(connection.getUser());
}
int timeout = SmackConfiguration.getPacketReplyTimeout();
if (ft) {
timeout = send.getManager().getTimeout();
}
request.setTo(to);
PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(request.getPacketID()));
connection.sendPacket(request);
IQ result = (IQ) collector.nextResult(timeout);
collector.cancel();
if (result == null || result.getType() == IQ.Type.ERROR) {
if(result != null && result.getError() != null) {
throw new XMPPException(result.getError());
}
throw new XMPPException("File transfer could not be initiated.");
}
IBBChunk chunk = null;
int seq = 0;
while ((chunk = readChunk()) != null) {
if (seq > 65535) {
seq = 0;
}
Message m = new Message(to);
m.setFrom(connection.getUser());
if(cancelled)
{
if(ft)
{
FileTransferManager.update(FileTransferManager.Event.CANCELLED, progress,
(float) totalBytes / (float) total, totalBytes);
}
m.setError(new XMPPError(403, "Cancelled"));
stream.close();
connection.sendPacket(m);
return;
}
m.addExtension(chunk);
chunk.setSid(sid);
chunk.setSeq(seq);
connection.sendPacket(m);
seq++;
}
IBBClose close = new IBBClose();
close.setTo(to);
close.setType(IQ.Type.SET);
close.setFrom(connection.getUser());
close.setSid(sid);
connection.sendPacket(close);
if (ft) {
FileTransferManager.update(FileTransferManager.Event.DONE, progress, 1, totalBytes);
}
}
}