package com.limegroup.gnutella.messages.vendor;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.DeflaterOutputStream;
import org.limewire.core.settings.ApplicationSettings;
import org.limewire.inspection.InspectionException;
import org.limewire.inspection.Inspector;
import org.limewire.io.GGEP;
import org.limewire.io.IOUtils;
import org.limewire.service.ErrorService;
import org.limewire.util.BEncoder;
import org.limewire.util.CommonUtils;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.limegroup.gnutella.util.DataUtils;
import com.limegroup.gnutella.util.FECUtils;
@Singleton
public class InspectionResponseFactoryImpl implements InspectionResponseFactory {
private static final String INSPECTION_FILE = "inspection.props";
private static final int OLD_VERSION = 1;
private static final int GGEP_VERSION = 2;
public static final float REDUNDANCY = 1.2f;
private final Inspector inspector;
private final FECUtils fecUtils;
@Inject
public InspectionResponseFactoryImpl(Inspector inspector, FECUtils fecUtils) {
this.inspector = inspector;
this.inspector.load(new File(CommonUtils.getCurrentDirectory(),INSPECTION_FILE));
this.fecUtils = fecUtils;
}
public InspectionResponse[] createResponses(InspectionRequest request) {
byte [] payload = derivePayload(request);
if (payload.length < InspectionResponse.PACKET_SIZE || !request.supportsEncoding())
return new InspectionResponse[]{new InspectionResponse(OLD_VERSION, request.getGUID(), payload)};
// package responses
List<byte []> chunks = fecUtils.encode(payload, InspectionResponse.PACKET_SIZE, REDUNDANCY);
List<InspectionResponse> ret = new ArrayList<InspectionResponse>(chunks.size());
for (int i = 0; i < chunks.size() ; i++ ) {
GGEP g = new GGEP();
g.put(InspectionResponse.DATA_KEY,chunks.get(i));
g.put(InspectionResponse.CHUNK_ID_KEY,i);
// need to be in every packet
g.put(InspectionResponse.TOTAL_CHUNKS_KEY,chunks.size());
g.put(InspectionResponse.LENGTH_KEY, payload.length);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
g.write(baos);
} catch (IOException impossible) {
continue; // don't disturb the user
}
ret.add(new InspectionResponse(GGEP_VERSION, request.getGUID(), baos.toByteArray()));
}
InspectionResponse [] rett = new InspectionResponse[ret.size()];
return ret.toArray(rett);
}
private byte[] derivePayload(InspectionRequest request) {
/*
* The format is a deflated bencoded mapping from
* the indices of the inspected fields to their values.
*/
String [] requested = request.getRequestedFields();
Map<Integer, Object> responses =
new HashMap<Integer, Object>(requested.length);
// if a timestamp was requested, it is put under the "-1" key.
if (request.requestsTimeStamp())
responses.put(-1,System.currentTimeMillis());
for (int i = 0; i < requested.length; i++) {
try {
responses.put(i, inspector.inspect(requested[i], ApplicationSettings.ALLOW_ANONYMOUS_STATISTICS_GATHERING.get()));
} catch (InspectionException skip){}
}
if (responses.isEmpty())
return DataUtils.EMPTY_BYTE_ARRAY;
// since the inspected values may contain any character, bencoding is safest
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DeflaterOutputStream dos = new DeflaterOutputStream(baos);
try {
try {
BEncoder.getEncoder(dos, false, true,"UTF-8").encodeDict(responses);
} catch (Throwable bencoding) {
// a BEInspectable returned invalid object - report the error.
String msg = bencoding.toString();
String ret = "d5:error"+msg.length()+":"+msg+"e";
dos.write(ret.getBytes("UTF-8"));
}
dos.flush();
} catch (IOException impossible) {
ErrorService.error(impossible);
} finally {
IOUtils.close(dos);
}
return baos.toByteArray();
}
}