/** * Helper method that creates an array of bytes for the specified <tt>Set</tt> of <tt>URN</tt> * instances. The bytes are written as specified in HUGE v 0.94. * * @param urns the <tt>Set</tt> of <tt>URN</tt> instances to use in constructing the byte array */ private static byte[] createExtBytes(Set urns, GGEPContainer ggep) { try { if (isEmpty(urns) && ggep.isEmpty()) return DataUtils.EMPTY_BYTE_ARRAY; ByteArrayOutputStream baos = new ByteArrayOutputStream(); if (!isEmpty(urns)) { // Add the extension for URNs, if any. Iterator iter = urns.iterator(); while (iter.hasNext()) { URN urn = (URN) iter.next(); Assert.that(urn != null, "Null URN"); baos.write(urn.toString().getBytes()); // If there's another URN, add the separator. if (iter.hasNext()) { baos.write(EXT_SEPARATOR); } } // If there's ggep data, write the separator. if (!ggep.isEmpty()) baos.write(EXT_SEPARATOR); } // It is imperitive that GGEP is added LAST. // That is because GGEP can contain 0x1c (EXT_SEPARATOR) // within it, which would cause parsing problems // otherwise. if (!ggep.isEmpty()) GGEPUtil.addGGEP(baos, ggep); return baos.toByteArray(); } catch (IOException impossible) { ErrorService.error(impossible); return DataUtils.EMPTY_BYTE_ARRAY; } }
/** * Factory method for instantiating individual responses from an <tt>InputStream</tt> instance. * * @param is the <tt>InputStream</tt> to read from * @throws <tt>IOException</tt> if there are any problems reading from or writing to the stream */ public static Response createFromStream(InputStream is) throws IOException { // extract file index & size long index = ByteOrder.uint2long(ByteOrder.leb2int(is)); long size = ByteOrder.uint2long(ByteOrder.leb2int(is)); if ((index & 0xFFFFFFFF00000000L) != 0) throw new IOException("invalid index: " + index); // must use Integer.MAX_VALUE instead of mask because // this value is later converted to an int, so we want // to ensure that when it's converted it doesn't become // negative. if (size > Integer.MAX_VALUE || size < 0) throw new IOException("invalid size: " + size); // The file name is terminated by a null terminator. // A second null indicates the end of this response. // Gnotella & others insert meta-information between // these null characters. So we have to handle this. // See http://gnutelladev.wego.com/go/ // wego.discussion.message?groupId=139406& // view=message&curMsgId=319258&discId=140845& // index=-1&action=view // Extract the filename ByteArrayOutputStream baos = new ByteArrayOutputStream(); int c; while ((c = is.read()) != 0) { if (c == -1) throw new IOException("EOF before null termination"); baos.write(c); } String name = new String(baos.toByteArray(), "UTF-8"); if (name.length() == 0) { throw new IOException("empty name in response"); } // Extract extra info, if any baos.reset(); while ((c = is.read()) != 0) { if (c == -1) throw new IOException("EOF before null termination"); baos.write(c); } byte[] rawMeta = baos.toByteArray(); if (rawMeta == null || rawMeta.length == 0) { if (is.available() < 16) { throw new IOException("not enough room for the GUID"); } return new Response(index, size, name); } else { // now handle between-the-nulls // \u001c is the HUGE v0.93 GEM delimiter HUGEExtension huge = new HUGEExtension(rawMeta); Set urns = huge.getURNS(); LimeXMLDocument doc = null; Iterator iter = huge.getMiscBlocks().iterator(); while (iter.hasNext() && doc == null) doc = createXmlDocument(name, (String) iter.next()); GGEPContainer ggep = GGEPUtil.getGGEP(huge.getGGEP()); return new Response(index, size, name, urns, doc, ggep, rawMeta); } }