/*
This file is part of Project MAXS.
MAXS and its modules is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
MAXS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with MAXS. If not, see <http://www.gnu.org/licenses/>.
*/
package org.projectmaxs.transport.xmpp.xmppservice;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.ArrayList;
import java.util.List;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager;
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy;
import org.jivesoftware.smackx.filetransfer.FileTransferListener;
import org.jivesoftware.smackx.filetransfer.FileTransferManager;
import org.jivesoftware.smackx.filetransfer.FileTransferRequest;
import org.jivesoftware.smackx.filetransfer.OutgoingFileTransfer;
import org.jxmpp.jid.EntityFullJid;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
import org.projectmaxs.shared.global.GlobalConstants;
import org.projectmaxs.shared.global.aidl.IMAXSIncomingFileTransferService;
import org.projectmaxs.shared.global.aidl.IMAXSOutgoingFileTransferService;
import org.projectmaxs.shared.global.util.AsyncServiceTask;
import org.projectmaxs.shared.global.util.Log;
import org.projectmaxs.shared.global.util.ParcelFileDescriptorUtil;
import org.projectmaxs.shared.global.util.SharedStringUtil;
import org.projectmaxs.transport.xmpp.Settings;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
public class XMPPFileTransfer extends StateChangeListener implements FileTransferListener {
private static final Log LOG = Log.getLog();
private static final Intent INCOMING_FILETRANSFER_BIND_INTENT;
static {
INCOMING_FILETRANSFER_BIND_INTENT = new Intent(
GlobalConstants.ACTION_INCOMING_FILETRANSFER);
INCOMING_FILETRANSFER_BIND_INTENT.setClassName(GlobalConstants.FILEWRITE_MODULE_PACKAGE,
GlobalConstants.FILEWRITE_MODULE_IFT_SERVICE);
}
private static FileTransferManager sFileTransferManager;
private final Settings mSettings;
private final Context mContext;
private final WifiManager mWifiManager;
private final Socks5Proxy mProxy;
private final BroadcastReceiver mWifiBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)
&& intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false)) {
onWifiConnected();
}
}
};
protected XMPPFileTransfer(Context context) {
mSettings = Settings.getInstance(context);
mContext = context;
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
mProxy = Socks5Proxy.getSocks5Proxy();
}
@Override
public void fileTransferRequest(FileTransferRequest request) {
final Jid requestor = request.getRequestor();
if (!mSettings.isMasterJID(requestor)) {
LOG.w("File transfer from non master jid " + requestor);
try {
request.reject();
} catch (NotConnectedException | InterruptedException e) {
LOG.w("reject threw exception", e);
}
return;
}
final String filename = request.getFileName();
final String description = request.getDescription();
final long size = request.getFileSize();
InputStream isTmp;
try {
isTmp = request.accept().recieveFile();
} catch (Exception e2) {
LOG.e("fileTransferRequest", e2);
return;
}
final InputStream is = isTmp;
new AsyncServiceTask<IMAXSIncomingFileTransferService>(INCOMING_FILETRANSFER_BIND_INTENT,
mContext) {
@Override
public IMAXSIncomingFileTransferService asInterface(IBinder iBinder) {
return IMAXSIncomingFileTransferService.Stub.asInterface(iBinder);
}
@Override
public void performTask(IMAXSIncomingFileTransferService iinterface) {
try {
ParcelFileDescriptor pfd = iinterface.incomingFileTransfer(filename, size,
description);
if (pfd == null) {
LOG.e("fileTranferRequest: PFD from incomingFileTransfer is null");
is.close();
return;
}
OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(pfd);
int len;
byte[] buf = new byte[1024];
try {
while ((len = is.read(buf)) > 0) {
os.write(buf, 0, len);
}
} catch (IOException e) {
LOG.e("fleTransferRequest", e);
} finally {
try {
is.close();
} catch (IOException e1) {
LOG.e("fleTransferRequest", e1);
}
try {
os.close();
} catch (IOException e1) {
LOG.e("fleTransferRequest", e1);
}
}
} catch (RemoteException e) {
LOG.e("fileTransferRequest", e);
} catch (IOException e) {
LOG.e("fileTransferRequest", e);
}
}
}.go();
}
@Override
public void newConnection(XMPPConnection connection) {
// disable streamhost prioritization
Socks5BytestreamManager s5bsm = Socks5BytestreamManager.getBytestreamManager(connection);
s5bsm.setProxyPrioritizationEnabled(false);
sFileTransferManager = FileTransferManager.getInstanceFor(connection);
sFileTransferManager.addFileTransferListener(this);
}
@Override
public void connected(XMPPConnection connection) {
mContext.registerReceiver(mWifiBroadcastReceiver,
new IntentFilter(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION));
}
@Override
public void disconnected(XMPPConnection connection) {
mContext.unregisterReceiver(mWifiBroadcastReceiver);
}
private void onWifiConnected() {
WifiInfo info = mWifiManager.getConnectionInfo();
List<String> addresses = new ArrayList<String>();
if (info != null) {
// There is an active Wifi connection
String ip = SharedStringUtil.ipIntToString(info.getIpAddress());
// Sometimes "0.0.0.0" gets returned
if (!ip.equals("0.0.0.0")) addresses.add(ip);
}
// set an ip in case there is a Wifi Connection
// otherwise addresses will be empty and local S5B proxy
// will not be used
mProxy.replaceLocalAddresses(addresses);
}
public static class MAXSOutgoingFileTransferService extends Service {
@Override
public IBinder onBind(Intent arg0) {
return mBinder;
}
private final IMAXSOutgoingFileTransferService.Stub mBinder = new IMAXSOutgoingFileTransferService.Stub() {
@Override
public ParcelFileDescriptor outgoingFileTransfer(String filename, long size,
String description, String toJIDString) throws RemoteException {
if (sFileTransferManager == null) {
LOG.e("outgoingFileTransfer: no connection");
return null;
}
final EntityFullJid toJID;
try {
toJID = JidCreate.entityFullFrom(toJIDString);
} catch (XmppStringprepException e) {
LOG.e("outgoingFileTransfer: Invalid JID", e);
return null;
}
PipedInputStream is = new PipedInputStream();
OutputStream os;
ParcelFileDescriptor pfd;
try {
os = new PipedOutputStream(is);
pfd = ParcelFileDescriptorUtil.pipeTo(os);
} catch (IOException e) {
LOG.e("outgoingFileTransfer: no connection");
return null;
}
OutgoingFileTransfer transfer = sFileTransferManager
.createOutgoingFileTransfer(toJID);
transfer.sendStream(is, filename, size, description);
return pfd;
}
};
}
}