001 package org.shiftone.jrat.inject.process; 002 003 004 import org.shiftone.jrat.core.JRatException; 005 import org.shiftone.jrat.inject.InjectorOptions; 006 import org.shiftone.jrat.inject.bytecode.Transformer; 007 import org.shiftone.jrat.util.Assert; 008 import org.shiftone.jrat.util.VersionUtil; 009 import org.shiftone.jrat.util.io.IOUtil; 010 import org.shiftone.jrat.util.io.OpenInputStream; 011 import org.shiftone.jrat.util.log.Logger; 012 import org.shiftone.jrat.util.regex.CompositeMatcher; 013 import org.shiftone.jrat.util.regex.Matcher; 014 015 import java.io.File; 016 import java.io.IOException; 017 import java.io.InputStream; 018 import java.io.PrintStream; 019 import java.util.zip.Deflater; 020 import java.util.zip.ZipEntry; 021 import java.util.zip.ZipInputStream; 022 import java.util.zip.ZipOutputStream; 023 024 025 /** 026 * @author jeff@shiftone.org (Jeff Drost) 027 */ 028 public class ArchiveFileProcessor extends AbstractFileProcessor { 029 030 private static final Logger LOG = Logger.getLogger(ArchiveFileProcessor.class); 031 private static final Matcher EXTENTION_MATCHER = 032 CompositeMatcher.buildCompositeGlobMatcher("zip,jar,ear,war,sar,har"); 033 private static final int BUFFER_SIZE = 1024 * 64; 034 035 protected void processFile(Transformer transformer, InjectorOptions options, File source, File target) { 036 037 LOG.debug("processFile " + source.getAbsolutePath() + " => " + target.getAbsolutePath()); 038 039 ZipInputStream sourceStream = new ZipInputStream(IOUtil.openInputStream(source, BUFFER_SIZE)); 040 ZipOutputStream targetStream = new ZipOutputStream(IOUtil.openOutputStream(target, BUFFER_SIZE)); 041 042 targetStream.setLevel(Deflater.BEST_SPEED); 043 044 try { 045 processStreams(transformer, options, sourceStream, targetStream); 046 } 047 catch (Exception e) { 048 throw new JRatException("error injecting " + source.getAbsoluteFile() + " => " 049 + target.getAbsolutePath(), e); 050 } 051 finally { 052 IOUtil.close(sourceStream); 053 IOUtil.close(targetStream); 054 } 055 } 056 057 058 protected boolean processStreams( 059 Transformer transformer, InjectorOptions options, ZipInputStream sourceStream, ZipOutputStream targetStream) 060 throws Exception { 061 062 Assert.assertNotNull("transformer", transformer); 063 064 ZipEntry inEntry = null; 065 ZipEntry outEntry = null; 066 InputStream entryInputStream; 067 068 addReadmeCommentFile(targetStream); 069 070 while ((inEntry = sourceStream.getNextEntry()) != null) { 071 outEntry = new ZipEntry(inEntry.getName()); 072 entryInputStream = new OpenInputStream(sourceStream); 073 074 targetStream.putNextEntry(outEntry); 075 076 String ext = getNormalizedExtention(inEntry); 077 078 if (isArchiveExtention(ext)) { 079 LOG.info("Entering nested archive : " + outEntry.getName()); 080 081 ZipInputStream nestedSourceStream = new ZipInputStream(entryInputStream); 082 ZipOutputStream nestedTargetStream = new ZipOutputStream(targetStream); 083 084 processStreams(transformer, options, nestedSourceStream, nestedTargetStream); 085 nestedTargetStream.finish(); // this line seems important... 086 087 // :) 088 } else if (isClassExtention(ext)) { 089 LOG.debug("injecting " + inEntry.getName()); 090 091 byte[] transformedClass = transformer.inject(entryInputStream, inEntry.getName(), options); 092 093 targetStream.write(transformedClass); 094 } else { 095 LOG.debug("copying " + inEntry.getName()); 096 IOUtil.copy(entryInputStream, targetStream); 097 } 098 099 targetStream.closeEntry(); 100 } 101 102 return true; 103 } 104 105 106 private void addReadmeCommentFile(ZipOutputStream zipOutputStream) { 107 108 ZipEntry entry = new ZipEntry("_READ_ME.JRAT"); 109 110 try { 111 zipOutputStream.putNextEntry(entry); 112 113 PrintStream printStream = new PrintStream(zipOutputStream); 114 115 printStream.println("# This Archive file was injected by"); 116 printStream.println("# Shiftone JRat the Java Runtime Analysis Toolkit"); 117 printStream.println("# For more information, visit http://jrat.sourceforge.net"); 118 printStream.println("#\tVersion : " + VersionUtil.getVersion()); 119 printStream.println("#\tBuilt On : " + VersionUtil.getBuiltOn()); 120 printStream.println("#\tBuilt By : " + VersionUtil.getBuiltBy()); 121 printStream.println(); 122 printStream.println(); 123 printStream.println(); 124 printStream.println("# the following system properties were present during injection"); 125 printStream.flush(); 126 System.getProperties().store(zipOutputStream, null); 127 zipOutputStream.closeEntry(); 128 } 129 catch (IOException e) { 130 throw new JRatException("unable to add comment file to archive", e); 131 } 132 } 133 134 135 private String getNormalizedExtention(ZipEntry entry) { 136 return IOUtil.getExtention(entry.getName()); 137 } 138 139 140 public static boolean isArchiveExtention(String ext) { 141 return EXTENTION_MATCHER.isMatch(ext); 142 } 143 144 145 public boolean isClassExtention(String ext) { 146 return (ext != null) && (ext.equals("class")); 147 } 148 }