/*
* XNap
*
* A pure java file sharing client.
*
* See AUTHORS for copyright information.
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
package xnap.plugin.nap.net;
import xnap.net.*;
import xnap.plugin.nap.Plugin;
import xnap.plugin.nap.net.msg.MessageHandler;
import xnap.plugin.nap.net.msg.client.DirectBrowseRequestMessage;
import xnap.plugin.nap.net.msg.server.DirectBrowseAckMessage;
import xnap.plugin.nap.net.msg.server.DirectBrowseErrorMessage;
import xnap.plugin.nap.net.msg.server.MessageStream;
import xnap.plugin.nap.net.msg.server.ServerMessage;
import xnap.util.*;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.*;
import org.apache.log4j.Logger;
public class DirectBrowse extends AbstractBrowse {
//--- Constant(s) ---
public static final int SERVER_TIMEOUT = 2 * 60 * 1000;
//--- Data field(s) ---
protected static Logger logger = Logger.getLogger(DirectBrowse.class);
protected Server server;
protected Socket socket;
protected InputStream in;
protected OutputStream out;
protected StringBuffer readBuffer = new StringBuffer();
//--- Constructor(s) ---
public DirectBrowse(User user)
{
super(user);
server = user.getServer();
}
//--- Method(s) ---
public synchronized int available() throws IOException
{
int i = in.available();
if (i > 0) {
byte b[] = new byte[i];
int c = in.read(b, 0, i);
String s = new String(b);
readBuffer.append(s);
parse();
}
else if (i == -1) {
throw new IOException("Socket closed");
}
return super.available();
}
public void close()
{
try {
if (in != null)
in.close();
if (out != null)
out.close();
if (socket != null)
socket.close();
}
catch (IOException e) {
}
}
public void connect() throws IOException
{
try {
establishConnection();
logger.debug("connected to " + getUser().getName());
}
catch (IOException e) {
close();
throw(e);
}
}
private void establishConnection() throws IOException
{
String ip = "";
int port = 0;
BrowseSocket bs = null;
MessageStream ms = new MessageStream(server);
MessageHandler.subscribe(DirectBrowseAckMessage.TYPE, ms);
MessageHandler.subscribe(DirectBrowseErrorMessage.TYPE, ms);
server.send(new DirectBrowseRequestMessage(getUser().getName()));
// wait for ack
IOException e = null;
long startTime = System.currentTimeMillis();
while (true) {
long timeLeft = SERVER_TIMEOUT - (System.currentTimeMillis()
- startTime);
if (timeLeft <= 0) {
e = new IOException("server timeout");
break;
}
if (ms.hasNext(100)) {
ServerMessage msg = ms.next();
if (msg instanceof DirectBrowseErrorMessage) {
DirectBrowseErrorMessage m = (DirectBrowseErrorMessage)msg;
if (m.nick.equals(getUser().getName())) {
e = new IOException(m.message);
break;
}
}
else if (msg instanceof DirectBrowseAckMessage) {
DirectBrowseAckMessage m = (DirectBrowseAckMessage)msg;
if (m.nick.equals(getUser().getName())) {
ip = m.ip;
port = m.port;
break;
}
}
}
else if (server.getListener() != null) {
bs = (BrowseSocket)server.getListener().waitForSocket
(new BrowseSocket(getUser().getName()), 100);
if (bs != null) {
break;
}
}
}
MessageHandler.unsubscribe(DirectBrowseAckMessage.TYPE, ms);
MessageHandler.unsubscribe(DirectBrowseErrorMessage.TYPE, ms);
if (e != null) {
throw(e);
}
if (bs != null) {
handle(bs);
}
else if (port == 0) {
establishReverseStream();
}
else {
establishStream(ip, port);
}
}
/**
* Opens socket and request file.
*/
private void establishStream(String ip, int port) throws IOException
{
logger.debug("opening socket " + ip + ":" + port);
socket = new Socket(ip, port);
try {
socket.setSoTimeout(SOCKET_TIMEOUT);
}
catch (SocketException s) {
}
out = socket.getOutputStream();
in = new BufferedInputStream(socket.getInputStream());
// read magic number '1'
logger.debug("reading magic number");
char c = (char)in.read();
if (c != '1') {
throw new IOException(Plugin.tr("Invalid request"));
}
write("GETLIST");
byte data[] = new byte[2048];
in.mark(2048);
int i = in.read(data);
if (i > 0) {
String nick = new String(data, 0, i);
int j = nick.indexOf("\n");
if (j == -1) {
// read '\n'
in.read();
}
else {
nick = nick.substring(0, j).trim();
in.reset();
in.skip(j + 1);
}
if (!nick.equals(getUser().getName())) {
throw new IOException("Invalid user: " + nick);
}
}
else {
throw new IOException("Socket error");
}
}
/**
* Waits for push.
*/
private void establishReverseStream() throws IOException
{
if (server.getListener() == null) {
throw new IOException("Both parties firewalled");
}
BrowseSocket bs = (BrowseSocket)server.getListener().waitForSocket
(new BrowseSocket(getUser().getName()), SOCKET_TIMEOUT);
if (bs == null) {
throw new IOException("Listener timeout");
}
handle(bs);
}
private void handle(BrowseSocket bs) throws IOException
{
socket = bs.socket;
try {
socket.setSoTimeout(SOCKET_TIMEOUT);
}
catch (SocketException e) {
}
out = socket.getOutputStream();
in = bs.in;
}
protected void parse()
{
int i;
while ((i = readBuffer.toString().indexOf("\n")) != -1) {
String s = readBuffer.substring(0, i);
readBuffer.delete(0, i + 1);
logger.debug("parse [" + s + "]");
if (s.equals("")) {
finished = true;
logger.debug("browse finished");
return;
}
try {
QuotedStringTokenizer t = new QuotedStringTokenizer(s);
if (t.countTokens() < 6) {
continue;
}
String filename = t.nextToken();
String md5 = t.nextToken();
long size = Long.parseLong(t.nextToken());
int bitrate = Integer.parseInt(t.nextToken());
int frequency = Integer.parseInt(t.nextToken());
int length = Integer.parseInt(t.nextToken());
add(new SearchResult(size, bitrate, frequency, length,
(User)getUser(), filename, md5));
}
catch (Exception e) {
logger.warn("parse " + s, e);
}
}
}
protected void write(String message) throws IOException
{
logger.debug("> " + message);
out.write(message.getBytes());
out.flush();
}
}