private BlockItem getBlockItem(ObjectContainer container, ClientContext context) { try { synchronized (this) { if (finished) return null; } if (persistent) { if (sourceData == null) { Logger.error( this, "getBlockItem(): sourceData = null but active = " + container.ext().isActive(this)); return null; } } boolean deactivateBucket = false; if (persistent) { container.activate(uri, 1); deactivateBucket = !container.ext().isActive(sourceData); if (deactivateBucket) container.activate(sourceData, 1); } Bucket data = sourceData.createShadow(); FreenetURI u = uri; if (u.getKeyType().equals("CHK") && !persistent) u = FreenetURI.EMPTY_CHK_URI; else u = u.clone(); if (data == null) { data = context.tempBucketFactory.makeBucket(sourceData.size()); BucketTools.copy(sourceData, data); } if (persistent) { if (deactivateBucket) container.deactivate(sourceData, 1); container.deactivate(uri, 1); } return new BlockItem( this, data, isMetadata, compressionCodec, sourceLength, u, hashCode(), persistent); } catch (IOException e) { fail(new InsertException(InsertException.BUCKET_ERROR, e, null), container, context); return null; } }
private void makeMainPage( ToadletContext ctx, List<String> errors, String key, int hexWidth, boolean automf, boolean deep, boolean ml) throws ToadletContextClosedException, IOException, RedirectException, URISyntaxException { PageNode page = pluginContext.pageMaker.getPageNode(i18n("KeyExplorer.PageTitle"), ctx); HTMLNode pageNode = page.outer; HTMLNode contentNode = page.content; byte[] data = null; GetResult getresult = null; String extraParams = "&hexwidth=" + hexWidth; if (automf) { extraParams += "&automf=checked"; } if (deep) { extraParams += "&deep=checked"; } if (ml) { extraParams += "&ml=checked"; } FreenetURI furi = null; FreenetURI retryUri = null; try { if (key != null && (key.trim().length() > 0)) { furi = URISanitizer.sanitizeURI( errors, key, false, URISanitizer.Options.NOMETASTRINGS, URISanitizer.Options.SSKFORUSK); retryUri = furi; if (ml) { // multilevel is requestet Metadata tempMD = KeyExplorerUtils.simpleManifestGet(pluginContext.pluginRespirator, furi); FetchResult tempResult = KeyExplorerUtils.splitGet(pluginContext.pluginRespirator, tempMD); getresult = new GetResult(tempResult.asBucket(), true); data = tempResult.asByteArray(); } else { // normal get getresult = KeyExplorerUtils.simpleGet(pluginContext.pluginRespirator, furi); data = BucketTools.toByteArray(getresult.getData()); } } } catch (MalformedURLException e) { errors.add("MalformedURL: " + key); } catch (IOException e) { Logger.error(this, "500", e); errors.add("IO Error: " + e.getMessage()); } catch (MetadataParseException e) { errors.add("Metadata Parse Error: " + e.getMessage()); } catch (FetchException e) { errors.add("Get failed (" + e.mode + "): " + e.getMessage()); } catch (KeyListenerConstructionException e) { Logger.error(this, "Hu?", e); errors.add("Internal Error: " + e.getMessage()); } finally { if (getresult != null) getresult.free(); } HTMLNode uriBox = createUriBox( pluginContext, ((furi == null) ? null : furi.toString(false, false)), hexWidth, automf, deep, errors); if (errors.size() > 0) { contentNode.addChild(createErrorBox(errors, path(), retryUri, extraParams)); errors.clear(); } contentNode.addChild(uriBox); if (data != null) { Metadata md = null; if (getresult.isMetaData()) { try { md = Metadata.construct(data); } catch (MetadataParseException e) { errors.add("Metadata parse error: " + e.getMessage()); } if (md != null) { if (automf && md.isArchiveManifest()) { if (md.getArchiveType() == ARCHIVE_TYPE.TAR) { writeTemporaryRedirect( ctx, "", KeyUtilsPlugin.PLUGIN_URI + "/Site/?mftype=TARmanifest&key=" + furi + extraParams); return; } else if (md.getArchiveType() == ARCHIVE_TYPE.ZIP) { writeTemporaryRedirect( ctx, "", KeyUtilsPlugin.PLUGIN_URI + "/Site/?mftype=ZIPmanifest&key=" + furi + extraParams); return; } else { errors.add("Unknown Archive Type: " + md.getArchiveType().name()); } } if (automf && md.isSimpleManifest()) { writeTemporaryRedirect( ctx, "", KeyUtilsPlugin.PLUGIN_URI + "/Site/?mftype=simplemanifest&key=" + furi + extraParams); return; } } } String title = "Key: " + furi.toString(false, false); if (getresult.isMetaData()) title = title + "\u00a0(MetaData)"; HTMLNode dataBox2 = pluginContext.pageMaker.getInfobox("#", title, contentNode); dataBox2.addChild("%", "<pre lang=\"en\" style=\"font-family: monospace;\">\n"); dataBox2.addChild("#", hexDump(data, hexWidth)); dataBox2.addChild("%", "\n</pre>"); if (getresult.isMetaData()) { if (md != null) { HTMLNode metaBox = pluginContext.pageMaker.getInfobox("#", "Decomposed metadata", contentNode); metaBox.addChild("#", "Metadata version " + Short.toString(md.getParsedVersion())); metaBox.addChild("br"); metaBox.addChild("#", "Document type:\u00a0"); if (md.isSimpleRedirect()) { metaBox.addChild("#", "SimpleRedirect"); } else if (md.isSimpleManifest()) { metaBox.addChild("#", "SimpleManifest"); } else if (md.isArchiveInternalRedirect()) { metaBox.addChild("#", "ArchiveInternalRedirect"); } else if (md.isArchiveMetadataRedirect()) { metaBox.addChild("#", "ArchiveMetadataRedirect"); } else if (md.isArchiveManifest()) { metaBox.addChild("#", "ArchiveManifest"); } else if (md.isMultiLevelMetadata()) { metaBox.addChild("#", "MultiLevelMetadata"); } else if (md.isSymbolicShortlink()) { metaBox.addChild("#", "SymbolicShortlink"); } else { metaBox.addChild("#", "<Unknown document type>"); } metaBox.addChild("br"); final String MIMEType = md.getMIMEType(); if (MIMEType != null) { metaBox.addChild("#", "MIME Type: " + MIMEType); metaBox.addChild("br"); } if (md.haveFlags()) { metaBox.addChild("#", "Flags:\u00a0"); boolean isFirst = true; if (md.isSplitfile()) { metaBox.addChild("#", "SplitFile"); isFirst = false; } if (md.isCompressed()) { if (isFirst) isFirst = false; else metaBox.addChild("#", "\u00a0"); metaBox.addChild("#", "Compressed (" + md.getCompressionCodec().name + ")"); } if (md.hasTopData()) { if (isFirst) isFirst = false; else metaBox.addChild("#", "\u00a0"); metaBox.addChild("#", "HasTopData"); } if (isFirst) metaBox.addChild("#", "<No flag set>"); } metaBox.addChild("br"); if (md.isCompressed()) { metaBox.addChild("#", "Decompressed size: " + md.uncompressedDataLength() + " bytes."); } else { metaBox.addChild("#", "Uncompressed"); } metaBox.addChild("br"); if (md.topCompatibilityMode != 0) { metaBox.addChild("#", "Compatibility mode: " + md.getTopCompatibilityMode().toString()); metaBox.addChild("br"); } if (md.hasTopData()) { metaBox.addChild("#", "Top Block Data:"); metaBox.addChild("br"); metaBox.addChild( "#", "\u00a0\u00a0DontCompress: " + Boolean.toString(md.topDontCompress)); metaBox.addChild("br"); metaBox.addChild( "#", "\u00a0\u00a0Compressed size: " + Long.toString(md.topCompressedSize) + " bytes."); metaBox.addChild("br"); metaBox.addChild( "#", "\u00a0\u00a0Decompressed Size: " + Long.toString(md.topSize) + " bytes."); metaBox.addChild("br"); metaBox.addChild( "#", "\u00a0\u00a0Blocks: " + Integer.toString(md.topBlocksRequired) + " required, " + Integer.toString(md.topBlocksTotal) + " total."); metaBox.addChild("br"); } final HashResult[] hashes = md.getHashes(); if (hashes != null && hashes.length > 0) { metaBox.addChild("#", "Hashes:"); metaBox.addChild("br"); for (final HashResult hash : hashes) { metaBox.addChild( "#", "\u00a0\u00a0" + hash.type.name() + ": " + HexUtil.bytesToHex(hash.result)); metaBox.addChild("br"); } } if (md.isSplitfile()) { metaBox.addChild("#", "Splitfile size\u00a0=\u00a0" + md.dataLength() + " bytes."); metaBox.addChild("br"); byte[] splitfileCryptoKey = md.getCustomSplitfileKey(); if (splitfileCryptoKey != null) { metaBox.addChild( "#", "Splitfile CryptoKey\u00a0=\u00a0" + HexUtil.bytesToHex(splitfileCryptoKey)); metaBox.addChild("br"); } } metaBox.addChild("#", "Options:"); metaBox.addChild("br"); if (md.isSimpleManifest()) { metaBox.addChild( new HTMLNode( "a", "href", KeyUtilsPlugin.PLUGIN_URI + "/Site/?mftype=simplemanifest&key=" + furi + extraParams, "reopen as manifest")); metaBox.addChild("br"); } if (md.isArchiveManifest()) { metaBox.addChild( new HTMLNode( "a", "href", KeyUtilsPlugin.PLUGIN_URI + "/Site/?mftype=" + md.getArchiveType().name() + "manifest&key=" + furi + extraParams, "reopen as manifest")); metaBox.addChild("br"); } if (md.isMultiLevelMetadata()) { if (ml) metaBox.addChild( new HTMLNode( "a", "href", KeyUtilsPlugin.PLUGIN_URI + "/?key=" + furi + extraParams, "explore multilevel")); else metaBox.addChild( new HTMLNode( "a", "href", KeyUtilsPlugin.PLUGIN_URI + "/?ml=checked&key=" + furi + extraParams, "explore multilevel")); metaBox.addChild("br"); } FreenetURI uri = md.getSingleTarget(); if (uri != null) { String sfrUri = uri.toString(false, false); metaBox.addChild("#", sfrUri); metaBox.addChild("#", "\u00a0"); metaBox.addChild(new HTMLNode("a", "href", "/?key=" + sfrUri, "open")); metaBox.addChild("#", "\u00a0"); metaBox.addChild( new HTMLNode( "a", "href", KeyUtilsPlugin.PLUGIN_URI + "/?key=" + sfrUri + extraParams, "explore")); } else { metaBox.addChild(new HTMLNode("a", "href", "/?key=" + furi, "reopen normal")); } metaBox.addChild("br"); if ((uri == null) && md.isSplitfile()) { metaBox.addChild( new HTMLNode( "a", "href", KeyUtilsPlugin.PLUGIN_URI + "/Split?key=" + furi.toString(false, false), "reopen as splitfile")); metaBox.addChild("br"); metaBox.addChild( new HTMLNode( "a", "href", KeyUtilsPlugin.PLUGIN_URI + "/Download?action=splitdownload&key=" + furi.toString(false, false), "split-download")); metaBox.addChild("br"); } } } if (errors.size() > 0) contentNode.addChild(createErrorBox(errors)); } contentNode.addChild(Utils.makeDonateFooter(_intl)); writeHTMLReply(ctx, 200, "OK", pageNode.generate()); }
/** * @param data The Bucket which contains the reply data. This function does not free() the Bucket! * <p>FIXME: For all references to this function, check whether they free() the Bucket. */ public void writeData(Bucket data) throws ToadletContextClosedException, IOException { if (closed) throw new ToadletContextClosedException(); BucketTools.copyTo(data, sockOutputStream, Long.MAX_VALUE); }
/** Handle an incoming connection. Blocking, obviously. */ public static void handle(Socket sock, ToadletContainer container, PageMaker pageMaker) { try { InputStream is = new BufferedInputStream(sock.getInputStream(), 4096); LineReadingInputStream lis = new LineReadingInputStream(is); while (true) { String firstLine = lis.readLine(32768, 128, false); // ISO-8859-1 or US-ASCII, _not_ UTF-8 if (firstLine == null) { sock.close(); return; } else if (firstLine.equals("")) { continue; } boolean logMINOR = Logger.shouldLog(Logger.MINOR, ToadletContextImpl.class); if (logMINOR) Logger.minor(ToadletContextImpl.class, "first line: " + firstLine); String[] split = firstLine.split(" "); if (split.length != 3) throw new ParseException( "Could not parse request line (split.length=" + split.length + "): " + firstLine); if (!split[2].startsWith("HTTP/1.")) throw new ParseException("Unrecognized protocol " + split[2]); URI uri; try { uri = URIPreEncoder.encodeURI(split[1]).normalize(); if (logMINOR) Logger.minor( ToadletContextImpl.class, "URI: " + uri + " path " + uri.getPath() + " host " + uri.getHost() + " frag " + uri.getFragment() + " port " + uri.getPort() + " query " + uri.getQuery() + " scheme " + uri.getScheme()); } catch (URISyntaxException e) { sendURIParseError(sock.getOutputStream(), true, e); return; } String method = split[0]; MultiValueTable<String, String> headers = new MultiValueTable<String, String>(); while (true) { String line = lis.readLine(32768, 128, false); // ISO-8859 or US-ASCII, not UTF-8 if (line == null) { sock.close(); return; } // System.out.println("Length="+line.length()+": "+line); if (line.length() == 0) break; int index = line.indexOf(':'); if (index < 0) { throw new ParseException("Missing ':' in request header field"); } String before = line.substring(0, index).toLowerCase(); String after = line.substring(index + 1); after = after.trim(); headers.put(before, after); } boolean disconnect = shouldDisconnectAfterHandled(split[2].equals("HTTP/1.0"), headers) || !container.enablePersistentConnections(); boolean allowPost = container.allowPosts(); BucketFactory bf = container.getBucketFactory(); ToadletContextImpl ctx = new ToadletContextImpl(sock, headers, bf, pageMaker, container); ctx.shouldDisconnect = disconnect; /* * copy the data into a bucket now, * before we go into the redirect loop */ Bucket data; boolean methodIsConfigurable = true; String slen = headers.get("content-length"); if (METHODS_MUST_HAVE_DATA.contains(method)) { // <method> must have data methodIsConfigurable = false; if (slen == null) { ctx.shouldDisconnect = true; ctx.sendReplyHeaders(400, "Bad Request", null, null, -1); return; } } else if (METHODS_CANNOT_HAVE_DATA.contains(method)) { // <method> can not have data methodIsConfigurable = false; if (slen != null) { ctx.shouldDisconnect = true; ctx.sendReplyHeaders(400, "Bad Request", null, null, -1); return; } } if (slen != null) { long len; try { len = Integer.parseInt(slen); if (len < 0) throw new NumberFormatException("content-length less than 0"); } catch (NumberFormatException e) { ctx.shouldDisconnect = true; ctx.sendReplyHeaders(400, "Bad Request", null, null, -1); return; } if (allowPost && ((!container.publicGatewayMode()) || ctx.isAllowedFullAccess())) { data = bf.makeBucket(len); BucketTools.copyFrom(data, is, len); } else { FileUtil.skipFully(is, len); if (method.equals("POST")) { ctx.sendMethodNotAllowed("POST", true); } else { sendError( sock.getOutputStream(), 403, "Forbidden", "Content not allowed in this configuration", true, null); } ctx.close(); return; } } else { // we're not doing to use it, but we have to keep // the compiler happy data = null; } if (!container.enableExtendedMethodHandling()) { if (!METHODS_RESTRICTED_MODE.contains(method)) { sendError( sock.getOutputStream(), 403, "Forbidden", "Method not allowed in this configuration", true, null); return; } } // Handle it. try { boolean redirect = true; while (redirect) { // don't go around the loop unless set explicitly redirect = false; Toadlet t; try { t = container.findToadlet(uri); } catch (PermanentRedirectException e) { Toadlet.writePermanentRedirect(ctx, "Found elsewhere", e.newuri.toASCIIString()); break; } if (t == null) { ctx.sendNoToadletError(ctx.shouldDisconnect); break; } // if the Toadlet does not support the method, we don't need to parse the data // also due this pre check a 'NoSuchMethodException' should never appear if (!(t.findSupportedMethods().contains(method))) { ctx.sendMethodNotAllowed(method, ctx.shouldDisconnect); break; } HTTPRequestImpl req = new HTTPRequestImpl(uri, data, ctx, method); try { String methodName = "handleMethod" + method; try { Class<? extends Toadlet> c = t.getClass(); Method m = c.getMethod(methodName, HANDLE_PARAMETERS); if (methodIsConfigurable) { AllowData anno = m.getAnnotation(AllowData.class); if (anno == null) { if (data != null) { sendError( sock.getOutputStream(), 400, "Bad Request", "Content not allowed", true, null); ctx.close(); return; } } else if (anno.value()) { if (data == null) { sendError( sock.getOutputStream(), 400, "Bad Request", "Missing Content", true, null); ctx.close(); return; } } } ctx.setActiveToadlet(t); Object arglist[] = new Object[] {uri, req, ctx}; m.invoke(t, arglist); } catch (InvocationTargetException ite) { throw ite.getCause(); } } catch (RedirectException re) { uri = re.newuri; redirect = true; } finally { req.freeParts(); } } if (ctx.shouldDisconnect) { sock.close(); return; } } finally { if (data != null) data.free(); } } } catch (ParseException e) { try { sendError( sock.getOutputStream(), 400, "Bad Request", l10n("parseErrorWithError", "error", e.getMessage()), true, null); } catch (IOException e1) { // Ignore } } catch (TooLongException e) { try { sendError( sock.getOutputStream(), 400, "Bad Request", l10n("headersLineTooLong"), true, null); } catch (IOException e1) { // Ignore } } catch (IOException e) { // ignore and return } catch (ToadletContextClosedException e) { Logger.error( ToadletContextImpl.class, "ToadletContextClosedException while handling connection!"); } catch (Throwable t) { Logger.error(ToadletContextImpl.class, "Caught error: " + t + " handling socket", t); try { sendError(sock.getOutputStream(), 500, "Internal Error", t.toString(), true, null); } catch (IOException e1) { // ignore and return } } }
/** Handle an incoming connection. Blocking, obviously. */ public static void handle(Socket sock, ToadletContainer container, PageMaker pageMaker) { try { InputStream is = new BufferedInputStream(sock.getInputStream(), 4096); LineReadingInputStream lis = new LineReadingInputStream(is); while (true) { String firstLine = lis.readLine(32768, 128, false); // ISO-8859-1 or US-ASCII, _not_ UTF-8 if (firstLine == null) { sock.close(); return; } else if (firstLine.equals("")) { continue; } boolean logMINOR = Logger.shouldLog(Logger.MINOR, ToadletContextImpl.class); if (logMINOR) Logger.minor(ToadletContextImpl.class, "first line: " + firstLine); String[] split = firstLine.split(" "); if (split.length != 3) throw new ParseException( "Could not parse request line (split.length=" + split.length + "): " + firstLine); if (!split[2].startsWith("HTTP/1.")) throw new ParseException("Unrecognized protocol " + split[2]); URI uri; try { uri = URIPreEncoder.encodeURI(split[1]).normalize(); if (logMINOR) Logger.minor( ToadletContextImpl.class, "URI: " + uri + " path " + uri.getPath() + " host " + uri.getHost() + " frag " + uri.getFragment() + " port " + uri.getPort() + " query " + uri.getQuery() + " scheme " + uri.getScheme()); } catch (URISyntaxException e) { sendURIParseError(sock.getOutputStream(), true, e); return; } String method = split[0]; MultiValueTable<String, String> headers = new MultiValueTable<String, String>(); while (true) { String line = lis.readLine(32768, 128, false); // ISO-8859 or US-ASCII, not UTF-8 if (line == null) { sock.close(); return; } // System.out.println("Length="+line.length()+": "+line); if (line.length() == 0) break; int index = line.indexOf(':'); if (index < 0) { throw new ParseException("Missing ':' in request header field"); } String before = line.substring(0, index).toLowerCase(); String after = line.substring(index + 1); after = after.trim(); headers.put(before, after); } boolean disconnect = shouldDisconnectAfterHandled(split[2].equals("HTTP/1.0"), headers) || !container.enablePersistentConnections(); boolean allowPost = container.allowPosts(); BucketFactory bf = container.getBucketFactory(); ToadletContextImpl ctx = new ToadletContextImpl(sock, headers, bf, pageMaker, container); ctx.shouldDisconnect = disconnect; /* * if we're handling a POST, copy the data into a bucket now, * before we go into the redirect loop */ Bucket data; if (method.equals("POST")) { String slen = headers.get("content-length"); if (slen == null) { sendError( sock.getOutputStream(), 400, "Bad Request", l10n("noContentLengthInPOST"), true, null); return; } long len; try { len = Integer.parseInt(slen); if (len < 0) throw new NumberFormatException("content-length less than 0"); } catch (NumberFormatException e) { sendError( sock.getOutputStream(), 400, "Bad Request", l10n("cannotParseContentLengthWithError", "error", e.toString()), true, null); return; } if (allowPost && ((!container.publicGatewayMode()) || ctx.isAllowedFullAccess())) { data = bf.makeBucket(len); BucketTools.copyFrom(data, is, len); } else { FileUtil.skipFully(is, len); ctx.sendMethodNotAllowed("POST", true); ctx.close(); return; } } else { // we're not doing to use it, but we have to keep // the compiler happy data = null; } // Handle it. try { boolean redirect = true; while (redirect) { // don't go around the loop unless set explicitly redirect = false; Toadlet t; try { t = container.findToadlet(uri); } catch (PermanentRedirectException e) { Toadlet.writePermanentRedirect(ctx, "Found elsewhere", e.newuri.toASCIIString()); break; } if (t == null) { ctx.sendNoToadletError(ctx.shouldDisconnect); break; } HTTPRequestImpl req = new HTTPRequestImpl(uri, data, ctx, method); try { if (method.equals("GET")) { ctx.setActiveToadlet(t); t.handleGet(uri, req, ctx); ctx.close(); } else if (method.equals("POST")) { ctx.setActiveToadlet(t); t.handlePost(uri, req, ctx); } else { ctx.sendMethodNotAllowed(method, ctx.shouldDisconnect); ctx.close(); } } catch (RedirectException re) { uri = re.newuri; redirect = true; } finally { req.freeParts(); } } if (ctx.shouldDisconnect) { sock.close(); return; } } finally { if (data != null) data.free(); } } } catch (ParseException e) { try { sendError( sock.getOutputStream(), 400, "Bad Request", l10n("parseErrorWithError", "error", e.getMessage()), true, null); } catch (IOException e1) { // Ignore } } catch (TooLongException e) { try { sendError( sock.getOutputStream(), 400, "Bad Request", l10n("headersLineTooLong"), true, null); } catch (IOException e1) { // Ignore } } catch (IOException e) { // ignore and return } catch (ToadletContextClosedException e) { Logger.error( ToadletContextImpl.class, "ToadletContextClosedException while handling connection!"); } catch (Throwable t) { Logger.error(ToadletContextImpl.class, "Caught error: " + t + " handling socket", t); try { sendError(sock.getOutputStream(), 500, "Internal Error", t.toString(), true, null); } catch (IOException e1) { // ignore and return } } }
/** * Extract data to cache. Call synchronized on ctx. * * @param key The key the data was fetched from. * @param archiveType The archive type. Must be Metadata.ARCHIVE_ZIP | Metadata.ARCHIVE_TAR. * @param data The actual data fetched. * @param archiveContext The context for the whole fetch process. * @param ctx The ArchiveStoreContext for this key. * @param element A particular element that the caller is especially interested in, or null. * @param callback A callback to be called if we find that element, or if we don't. * @throws ArchiveFailureException If we could not extract the data, or it was too big, etc. * @throws ArchiveRestartException * @throws ArchiveRestartException If the request needs to be restarted because the archive * changed. */ public void extractToCache( FreenetURI key, ARCHIVE_TYPE archiveType, COMPRESSOR_TYPE ctype, final Bucket data, ArchiveContext archiveContext, ArchiveStoreContext ctx, String element, ArchiveExtractCallback callback, ClientContext context) throws ArchiveFailureException, ArchiveRestartException { logMINOR = Logger.shouldLog(LogLevel.MINOR, this); MutableBoolean gotElement = element != null ? new MutableBoolean() : null; if (logMINOR) Logger.minor(this, "Extracting " + key); ctx.removeAllCachedItems(this); // flush cache anyway final long expectedSize = ctx.getLastSize(); final long archiveSize = data.size(); /** * Set if we need to throw a RestartedException rather than returning success, after we have * unpacked everything. */ boolean throwAtExit = false; if ((expectedSize != -1) && (archiveSize != expectedSize)) { throwAtExit = true; ctx.setLastSize(archiveSize); } byte[] expectedHash = ctx.getLastHash(); if (expectedHash != null) { byte[] realHash; try { realHash = BucketTools.hash(data); } catch (IOException e) { throw new ArchiveFailureException("Error reading archive data: " + e, e); } if (!Arrays.equals(realHash, expectedHash)) throwAtExit = true; ctx.setLastHash(realHash); } if (archiveSize > archiveContext.maxArchiveSize) throw new ArchiveFailureException( "Archive too big (" + archiveSize + " > " + archiveContext.maxArchiveSize + ")!"); else if (archiveSize <= 0) throw new ArchiveFailureException("Archive too small! (" + archiveSize + ')'); else if (logMINOR) Logger.minor(this, "Container size (possibly compressed): " + archiveSize + " for " + data); InputStream is = null; try { final ExceptionWrapper wrapper; if ((ctype == null) || (ARCHIVE_TYPE.ZIP == archiveType)) { if (logMINOR) Logger.minor(this, "No compression"); is = data.getInputStream(); wrapper = null; } else if (ctype == COMPRESSOR_TYPE.BZIP2) { if (logMINOR) Logger.minor(this, "dealing with BZIP2"); is = new BZip2CompressorInputStream(data.getInputStream()); wrapper = null; } else if (ctype == COMPRESSOR_TYPE.GZIP) { if (logMINOR) Logger.minor(this, "dealing with GZIP"); is = new GZIPInputStream(data.getInputStream()); wrapper = null; } else if (ctype == COMPRESSOR_TYPE.LZMA_NEW) { // LZMA internally uses pipe streams, so we may as well do it here. // In fact we need to for LZMA_NEW, because of the properties bytes. PipedInputStream pis = new PipedInputStream(); PipedOutputStream pos = new PipedOutputStream(); pis.connect(pos); final OutputStream os = new BufferedOutputStream(pos); wrapper = new ExceptionWrapper(); context.mainExecutor.execute( new Runnable() { @Override public void run() { InputStream is = null; try { Compressor.COMPRESSOR_TYPE.LZMA_NEW.decompress( is = data.getInputStream(), os, data.size(), expectedSize); } catch (CompressionOutputSizeException e) { Logger.error(this, "Failed to decompress archive: " + e, e); wrapper.set(e); } catch (IOException e) { Logger.error(this, "Failed to decompress archive: " + e, e); wrapper.set(e); } finally { try { os.close(); } catch (IOException e) { Logger.error(this, "Failed to close PipedOutputStream: " + e, e); } Closer.close(is); } } }); is = pis; } else if (ctype == COMPRESSOR_TYPE.LZMA) { if (logMINOR) Logger.minor(this, "dealing with LZMA"); is = new LzmaInputStream(data.getInputStream()); wrapper = null; } else { wrapper = null; } if (ARCHIVE_TYPE.ZIP == archiveType) handleZIPArchive(ctx, key, is, element, callback, gotElement, throwAtExit, context); else if (ARCHIVE_TYPE.TAR == archiveType) handleTARArchive(ctx, key, is, element, callback, gotElement, throwAtExit, context); else throw new ArchiveFailureException( "Unknown or unsupported archive algorithm " + archiveType); if (wrapper != null) { Exception e = wrapper.get(); if (e != null) throw new ArchiveFailureException( "An exception occured decompressing: " + e.getMessage(), e); } } catch (IOException ioe) { throw new ArchiveFailureException("An IOE occured: " + ioe.getMessage(), ioe); } finally { Closer.close(is); } }
public boolean send( NodeClientCore core, RequestScheduler sched, final ClientContext context, ChosenBlock req) { // Ignore keyNum, key, since we're only sending one block. ClientKeyBlock b; ClientKey key = null; if (SingleBlockInserter.logMINOR) Logger.minor(this, "Starting request: " + SingleBlockInserter.this); BlockItem block = (BlockItem) req.token; try { try { b = innerEncode( context.random, block.uri, block.copyBucket, block.isMetadata, block.compressionCodec, block.sourceLength, compressorDescriptor); } catch (CHKEncodeException e) { throw new LowLevelPutException( LowLevelPutException.INTERNAL_ERROR, e.toString() + ":" + e.getMessage(), e); } catch (SSKEncodeException e) { throw new LowLevelPutException( LowLevelPutException.INTERNAL_ERROR, e.toString() + ":" + e.getMessage(), e); } catch (MalformedURLException e) { throw new LowLevelPutException( LowLevelPutException.INTERNAL_ERROR, e.toString() + ":" + e.getMessage(), e); } catch (InsertException e) { throw new LowLevelPutException( LowLevelPutException.INTERNAL_ERROR, e.toString() + ":" + e.getMessage(), e); } catch (IOException e) { throw new LowLevelPutException( LowLevelPutException.INTERNAL_ERROR, e.toString() + ":" + e.getMessage(), e); } catch (InvalidCompressionCodecException e) { throw new LowLevelPutException( LowLevelPutException.INTERNAL_ERROR, e.toString() + ":" + e.getMessage(), e); } if (b == null) { Logger.error( this, "Asked to send empty block on " + SingleBlockInserter.this, new Exception("error")); return false; } key = b.getClientKey(); final ClientKey k = key; if (block.persistent) { context.jobRunner.queue( new DBJob() { public boolean run(ObjectContainer container, ClientContext context) { if (!container.ext().isStored(SingleBlockInserter.this)) return false; container.activate(SingleBlockInserter.this, 1); onEncode(k, container, context); container.deactivate(SingleBlockInserter.this, 1); return false; } }, NativeThread.NORM_PRIORITY + 1, false); } else { context.mainExecutor.execute( new Runnable() { public void run() { onEncode(k, null, context); } }, "Got URI"); } if (req.localRequestOnly) try { core.node.store(b, false, req.canWriteClientCache, true, false); } catch (KeyCollisionException e) { throw new LowLevelPutException(LowLevelPutException.COLLISION); } else core.realPut(b, req.canWriteClientCache, req.forkOnCacheable); } catch (LowLevelPutException e) { if (e.code == LowLevelPutException.COLLISION) { // Collision try { ClientSSKBlock collided = (ClientSSKBlock) core.node.fetch((ClientSSK) key, true, true, req.canWriteClientCache); byte[] data = collided.memoryDecode(true); byte[] inserting = BucketTools.toByteArray(block.copyBucket); if (collided.isMetadata() == block.isMetadata && collided.getCompressionCodec() == block.compressionCodec && Arrays.equals(data, inserting)) { if (SingleBlockInserter.logMINOR) Logger.minor(this, "Collided with identical data: " + SingleBlockInserter.this); req.onInsertSuccess(context); return true; } } catch (KeyVerifyException e1) { Logger.error(this, "Caught " + e1 + " when checking collision!", e1); } catch (KeyDecodeException e1) { Logger.error(this, "Caught " + e1 + " when checking collision!", e1); } catch (IOException e1) { Logger.error(this, "Caught " + e1 + " when checking collision!", e1); } } req.onFailure(e, context); if (SingleBlockInserter.logMINOR) Logger.minor(this, "Request failed: " + SingleBlockInserter.this + " for " + e); return true; } catch (DatabaseDisabledException e) { // Impossible, and nothing to do. Logger.error(this, "Running persistent insert but database is disabled!"); } finally { block.copyBucket.free(); } if (SingleBlockInserter.logMINOR) Logger.minor(this, "Request succeeded: " + SingleBlockInserter.this); req.onInsertSuccess(context); return true; }