package com.limegroup.gnutella.simpp; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.RandomAccessFile; import java.util.Arrays; import org.xml.sax.SAXException; import com.limegroup.gnutella.RouterService; import com.limegroup.gnutella.messages.vendor.CapabilitiesVM; import com.limegroup.gnutella.settings.SimppSettingsManager; import com.limegroup.gnutella.util.CommonUtils; import com.limegroup.gnutella.util.FileUtils; import com.limegroup.gnutella.util.ProcessingQueue; import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.Log; /** * Used for managing signed messages published by LimeWire, and chaning settings * as necessary. * <p> * Uses the singleton pattern */ public class SimppManager { private static final Log LOG = LogFactory.getLog(SimppManager.class); private static SimppManager INSTANCE; private int _latestVersion; private static final String SIMPP_FILE = "simpp.xml"; /** * The smallest version number of Simpp Messages. Any simpp message number * less than this will be rejected. It's set to 3 for testing purposes, the * first simpp message published by limwire will start at 4. */ private static int MIN_VERSION = 3; /** Cached Simpp bytes in case we need to sent it out on the wire */ private byte[] _simppBytes; private String _propsStream; private final ProcessingQueue _processingQueue; private SimppManager() { boolean problem = false; RandomAccessFile raf = null; _processingQueue = new ProcessingQueue("Simpp Handling Queue"); try { File file = new File(CommonUtils.getUserSettingsDir(), SIMPP_FILE); raf = new RandomAccessFile(file, "r"); byte[] content = new byte[(int)raf.length()]; raf.readFully(content); SimppDataVerifier verifier = new SimppDataVerifier(content); boolean verified = false; _latestVersion = 0; verified = verifier.verifySource(); if(!verified) { LOG.debug("Unable to verify simpp message."); problem = true; return; } SimppParser parser = null; try { parser = new SimppParser(verifier.getVerifiedData()); } catch(SAXException sx) { LOG.error("Unable to parse simpp data on disk", sx); problem = true; return; } catch (IOException iox) { LOG.error("IOX parsing simpp on disk", iox); problem = true; return; } if(parser.getVersion() <= MIN_VERSION) { LOG.error("Version below min on disk, aborting simpp."); problem = true; //set the values to default return; } this._latestVersion = parser.getVersion(); this._propsStream = parser.getPropsData(); this._simppBytes = content; } catch (IOException iox) { LOG.error("IOX reading simpp xml on disk", iox); problem = true; } finally { if(problem) { _latestVersion = MIN_VERSION; _propsStream = ""; _simppBytes = "".getBytes(); } if(raf!=null) { try { raf.close(); } catch(IOException iox) {} } } } public static synchronized SimppManager instance() { if(INSTANCE==null) INSTANCE = new SimppManager(); return INSTANCE; } public int getVersion() { return _latestVersion; } /** * @return the cached value of the simpp bytes. */ public byte[] getSimppBytes() { return _simppBytes; } public String getPropsString() { return _propsStream; } /** * Called when we receive a new SIMPPVendorMessage, */ public void checkAndUpdate(final byte[] simppPayload) { if(simppPayload == null) return; final int myVersion = _latestVersion; Runnable simppHandler = new Runnable() { public void run() { SimppDataVerifier verifier=new SimppDataVerifier(simppPayload); if (!verifier.verifySource()) return; SimppParser parser=null; try { parser = new SimppParser(verifier.getVerifiedData()); } catch(SAXException sx) { LOG.error("SAX error reading network simpp", sx); return; } catch(IOException iox) { LOG.error("IOX parsing network simpp", iox); return; } int version = parser.getVersion(); if(version <= myVersion) { LOG.error("Network simpp below current version, aborting."); return; } //OK. We have a new SimppMessage, take appropriate steps //1. Cache local values. SimppManager.this._latestVersion = version; SimppManager.this._simppBytes = simppPayload; SimppManager.this._propsStream = parser.getPropsData(); // 2. get the props we just read String props = parser.getPropsData(); // 3. Update the props in "updatable props manager" SimppSettingsManager.instance().updateSimppSettings(props); // 4. Save to disk, try 5 times for (int i =0;i < 5; i++) { if (save()) break; } // 5. Update the capabilities VM with the new version CapabilitiesVM.reconstructInstance(); // 5. Send the new CapabilityVM to all our connections. RouterService.getConnectionManager().sendUpdatedCapabilities(); } }; _processingQueue.add(simppHandler); } /** * Saves the simpp.xml file to the user settings directory. */ public boolean save(){ File tmp = new File(CommonUtils.getUserSettingsDir(),SIMPP_FILE+".tmp"); File simpp = new File(CommonUtils.getUserSettingsDir(),SIMPP_FILE); OutputStream simppWriter = null; try { simppWriter = new BufferedOutputStream(new FileOutputStream(tmp)); simppWriter.write(_simppBytes); simppWriter.flush(); }catch(IOException bad) { return false; } finally { if (simppWriter!=null) try{simppWriter.close();}catch(IOException ignored){} } //verify that we wrote everything correctly DataInputStream dis = null; byte [] data= new byte[_simppBytes.length]; try { dis = new DataInputStream(new BufferedInputStream(new FileInputStream(tmp))); dis.readFully(data); if (!Arrays.equals(data,_simppBytes)) return false; }catch(IOException bad) { return false; } finally { if (dis!=null) try{dis.close();}catch(IOException ignored){} } // if we couldn't rename the temp file, try again later. return FileUtils.forceRename(tmp,simpp); } }