Source Code Cross Referenced for GuardDB.java in  » Development » RetroGuard » COM » rl » obf » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Development » RetroGuard » COM.rl.obf 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /* ===========================================================================
002:         * $RCSfile: GuardDB.java,v $
003:         * ===========================================================================
004:         *
005:         * RetroGuard -- an obfuscation package for Java classfiles.
006:         *
007:         * Copyright (c) 1998-2006 Mark Welsh (markw@retrologic.com)
008:         *
009:         * This program can be redistributed and/or modified under the terms of the 
010:         * Version 2 of the GNU General Public License as published by the Free 
011:         * Software Foundation.
012:         *
013:         * This program is distributed in the hope that it will be useful,
014:         * but WITHOUT ANY WARRANTY; without even the implied warranty of
015:         * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
016:         * GNU General Public License for more details.
017:         *
018:         */
019:
020:        package COM.rl.obf;
021:
022:        import java.io.*;
023:        import java.util.*;
024:        import java.util.zip.*;
025:        import java.security.*;
026:        import COM.rl.obf.classfile.*;
027:        import COM.rl.util.*;
028:        import COM.rl.util.rfc822.*;
029:
030:        /**
031:         * Classfile database for obfuscation.
032:         *
033:         * @author      Mark Welsh
034:         */
035:        public class GuardDB implements  ClassConstants {
036:            // Constants -------------------------------------------------------------
037:            private static final String STREAM_NAME_MANIFEST = "META-INF/MANIFEST.MF";
038:            private static final String MANIFEST_VERSION_TAG = "Manifest-Version";
039:            private static final String MANIFEST_VERSION_VALUE = "1.0";
040:            private static final String MANIFEST_NAME_TAG = "Name";
041:            private static final String MANIFEST_DIGESTALG_TAG = "Digest-Algorithms";
042:            private static final String CLASS_EXT = ".class";
043:            private static final String SIGNATURE_PREFIX = "META-INF/";
044:            private static final String SIGNATURE_EXT = ".SF";
045:            private static final String LOG_MEMORY_USED = "# Memory in use after class data structure built: ";
046:            private static final String LOG_MEMORY_TOTAL = "# Total memory available                        : ";
047:            private static final String LOG_MEMORY_BYTES = " bytes";
048:            private static final String WARNING_SCRIPT_ENTRY_ABSENT = "# WARNING - identifier from script file not found in JAR: ";
049:            private static final String ERROR_CORRUPT_CLASS = "# ERROR - corrupt class file: ";
050:            private static final String WARNING_INCOMPATIBLE_VERSION_1 = "# WARNING - class file format has incompatible major-version number: v";
051:            private static final String WARNING_INCOMPATIBLE_VERSION_2 = "# WARNING - this version of RetroGuard supports up to class format:  v";
052:
053:            // Fields ----------------------------------------------------------------
054:            private ZipFile inJar; // JAR file for obfuscation
055:            private SectionList oldManifest; // MANIFEST.MF RFC822-style data from old Jar
056:            private SectionList newManifest; // MANIFEST.MF RFC822-style data for new Jar
057:            private ClassTree classTree; // Tree of packages, classes. methods, fields
058:            private boolean hasMap = false; // Has the mapping been generated already?
059:            private boolean enableMapClassString = false; // Remap strings in reflection?
060:            private boolean enableTrim = false; // Trim unused method, field, classes?
061:            private boolean enableRepackage = false; // Repackage classes for size?
062:            private boolean enableDummySourceFile = false; // Remap SourceFile attribute to text "SourceFile"?
063:            private boolean enableDigestSHA = false; // Produce SHA-1 manifest digests?
064:            private boolean enableDigestMD5 = false; // Produce MD5 manifest digests?
065:
066:            // Class Methods ---------------------------------------------------------
067:
068:            // Instance Methods ------------------------------------------------------
069:            /** A classfile database for obfuscation. */
070:            public GuardDB(File inFile, boolean enableTrim) throws Exception {
071:                inJar = new ZipFile(inFile);
072:                this .enableTrim = enableTrim;
073:                parseManifest();
074:            }
075:
076:            /** Close input JAR file and log-file at GC-time. */
077:            protected void finalize() throws Exception {
078:                close();
079:            }
080:
081:            /** Create a classfile database. */
082:            public void buildClassTree(PrintWriter log) throws Exception {
083:                // Go through the input Jar, adding each class file to the database
084:                int incompatibleVersion = 0;
085:                classTree = new ClassTree();
086:                Enumeration entries = inJar.entries();
087:                while (entries.hasMoreElements()) {
088:                    // Get the next entry from the input Jar
089:                    ZipEntry inEntry = (ZipEntry) entries.nextElement();
090:                    String name = inEntry.getName();
091:                    if (name.length() > CLASS_EXT.length()
092:                            && name.substring(
093:                                    name.length() - CLASS_EXT.length(),
094:                                    name.length()).equals(CLASS_EXT)) {
095:                        // Create a full internal representation of the class file
096:                        DataInputStream inStream = new DataInputStream(
097:                                new BufferedInputStream(inJar
098:                                        .getInputStream(inEntry)));
099:                        ClassFile cf = null;
100:                        try {
101:                            cf = ClassFile.create(inStream);
102:                        } catch (Exception e) {
103:                            log.println(ERROR_CORRUPT_CLASS + name + " ("
104:                                    + e.getMessage() + ")");
105:                        } finally {
106:                            inStream.close();
107:                        }
108:                        if (cf.hasIncompatibleVersion()) {
109:                            incompatibleVersion = cf.getMajorVersion();
110:                        }
111:                        classTree.addClassFile(cf, enableTrim);
112:                    }
113:                }
114:                // Warn if classes are incompatible version of class file format
115:                if (incompatibleVersion != 0) {
116:                    log.println(WARNING_INCOMPATIBLE_VERSION_1
117:                            + incompatibleVersion);
118:                    log.println(WARNING_INCOMPATIBLE_VERSION_2 + MAJOR_VERSION);
119:                }
120:            }
121:
122:            /**
123:             * Go through database marking certain entities for retention, while
124:             * maintaining polymorphic integrity.
125:             */
126:            public void retain(RgsEnum rgsEnum, PrintWriter log)
127:                    throws Exception {
128:
129:                // Build database if not already done, or if a mapping has already been generated
130:                if (classTree == null || hasMap) {
131:                    hasMap = false;
132:                    buildClassTree(log);
133:                }
134:
135:                // Always retain native methods and their classes, using script entry:
136:                // .method;native ** * and_class
137:                classTree.retainMethod("**", "*", true, null, false, false,
138:                        ClassConstants.ACC_NATIVE, ClassConstants.ACC_NATIVE);
139:
140:                // Always retain the auto-generated values() and valueOf(...) 
141:                // methods in Enums, using script entries:
142:                // .method;public;static;final **/values * extends java/lang/Enum
143:                // .method;public;static **/valueOf * extends java/lang/Enum
144:                classTree.retainMethod("**/values", "*", false,
145:                        "java/lang/Enum", false, false,
146:                        ClassConstants.ACC_PUBLIC | ClassConstants.ACC_STATIC
147:                                | ClassConstants.ACC_FINAL,
148:                        ClassConstants.ACC_PUBLIC | ClassConstants.ACC_STATIC
149:                                | ClassConstants.ACC_FINAL);
150:                classTree.retainMethod("**/valueOf", "*", false,
151:                        "java/lang/Enum", false, false,
152:                        ClassConstants.ACC_PUBLIC | ClassConstants.ACC_STATIC,
153:                        ClassConstants.ACC_PUBLIC | ClassConstants.ACC_STATIC);
154:
155:                // Enumerate the entries in the RGS script
156:                while (rgsEnum.hasMoreEntries()) {
157:                    RgsEntry entry = rgsEnum.nextEntry();
158:                    try {
159:                        switch (entry.type) {
160:                        case RgsEntry.TYPE_OPTION:
161:                            if (ClassConstants.OPTION_DigestSHA
162:                                    .equals(entry.name)) {
163:                                enableDigestSHA = true;
164:                            } else if (ClassConstants.OPTION_DigestMD5
165:                                    .equals(entry.name)) {
166:                                enableDigestMD5 = true;
167:                            } else if (ClassConstants.OPTION_MapClassString
168:                                    .equals(entry.name)) {
169:                                enableMapClassString = true;
170:                            } else if (ClassConstants.OPTION_Trim
171:                                    .equals(entry.name)) {
172:                                // NOTE - already set in special RGS pass
173:                                //enableTrim = true;
174:                            } else if (ClassConstants.OPTION_Repackage
175:                                    .equals(entry.name)) {
176:                                enableRepackage = true;
177:                            } else if (ClassConstants.OPTION_Generic
178:                                    .equals(entry.name)) {
179:                                classTree
180:                                        .retainAttribute(ClassConstants.ATTR_Signature);
181:                            } else if (ClassConstants.OPTION_LineNumberDebug
182:                                    .equals(entry.name)) {
183:                                classTree
184:                                        .retainAttribute(ClassConstants.ATTR_LineNumberTable);
185:                                classTree
186:                                        .retainAttribute(ClassConstants.ATTR_SourceFile);
187:                                enableDummySourceFile = true;
188:                            } else if (ClassConstants.OPTION_RuntimeAnnotations
189:                                    .equals(entry.name)) {
190:                                classTree
191:                                        .retainAttribute(ClassConstants.ATTR_RuntimeVisibleAnnotations);
192:                                classTree
193:                                        .retainAttribute(ClassConstants.ATTR_RuntimeVisibleParameterAnnotations);
194:                                classTree
195:                                        .retainAttribute(ClassConstants.ATTR_AnnotationDefault);
196:                            } else if (ClassConstants.OPTION_Annotations
197:                                    .equals(entry.name)) {
198:                                classTree
199:                                        .retainAttribute(ClassConstants.ATTR_RuntimeVisibleAnnotations);
200:                                classTree
201:                                        .retainAttribute(ClassConstants.ATTR_RuntimeInvisibleAnnotations);
202:                                classTree
203:                                        .retainAttribute(ClassConstants.ATTR_RuntimeVisibleParameterAnnotations);
204:                                classTree
205:                                        .retainAttribute(ClassConstants.ATTR_RuntimeInvisibleParameterAnnotations);
206:                                classTree
207:                                        .retainAttribute(ClassConstants.ATTR_AnnotationDefault);
208:                            } else if (ClassConstants.OPTION_Enumeration
209:                                    .equals(entry.name)) {
210:                                // .option Enumeration - translates into
211:                                // .class ** public extends java/lang/Enum
212:                                classTree.retainClass("**", true, false, false,
213:                                        false, false, "java/lang/Enum", false,
214:                                        false, 0, 0);
215:                            } else if (ClassConstants.OPTION_Application
216:                                    .equals(entry.name)) {
217:                                // .option Application - translates into
218:                                // .method **/main ([Ljava/lang/String;)V and_class
219:                                classTree.retainMethod("**/main",
220:                                        "([Ljava/lang/String;)V", true, null,
221:                                        false, false, 0, 0);
222:                            } else if (ClassConstants.OPTION_Applet
223:                                    .equals(entry.name)) {
224:                                // .option Applet - translates into
225:                                // .class ** extends java/applet/Applet
226:                                classTree.retainClass("**", false, false,
227:                                        false, false, false,
228:                                        "java/applet/Applet", false, false, 0,
229:                                        0);
230:                            } else if (ClassConstants.OPTION_RMI
231:                                    .equals(entry.name)) {
232:                                // .option RMI - translates into
233:                                // .option Serializable (see below for details)
234:                                // .class ** protected extends java/rmi/Remote
235:                                // .class **_Stub
236:                                // .class **_Skel
237:                                classTree.retainClass("**", false,
238:                                        true, // protected
239:                                        false, false, false, "java/rmi/Remote",
240:                                        false, false, 0, 0);
241:                                classTree.retainClass("**_Stub", false, false,
242:                                        false, false, false, null, false,
243:                                        false, 0, 0);
244:                                classTree.retainClass("**_Skel", false, false,
245:                                        false, false, false, null, false,
246:                                        false, 0, 0);
247:                            }
248:                            if (ClassConstants.OPTION_Serializable
249:                                    .equals(entry.name)
250:                                    || ClassConstants.OPTION_RMI
251:                                            .equals(entry.name)) {
252:                                // .option Serializable - translates into
253:                                // .method;private **/writeObject (Ljava/io/ObjectOutputStream;)V extends java/io/Serializable
254:                                // .method;private **/readObject (Ljava/io/ObjectInputStream;)V extends java/io/Serializable
255:                                // .method **/writeReplace ()Ljava/lang/Object; extends java/io/Serializable
256:                                // .method **/readResolve ()Ljava/lang/Object; extends java/io/Serializable
257:                                // .field;static;final **/serialVersionUID J extends java/io/Serializable
258:                                // .field;static;final **/serialPersistentFields [Ljava/io/ObjectStreamField; extends java/io/Serializable
259:                                // .class ** extends java/io/Serializable
260:                                // .field;!transient;!static ** * extends java/io/Serializable
261:                                classTree.retainMethod("**/writeObject",
262:                                        "(Ljava/io/ObjectOutputStream;)V",
263:                                        false, "java/io/Serializable", false,
264:                                        false, ClassConstants.ACC_PRIVATE,
265:                                        ClassConstants.ACC_PRIVATE);
266:                                classTree.retainMethod("**/readObject",
267:                                        "(Ljava/io/ObjectInputStream;)V",
268:                                        false, "java/io/Serializable", false,
269:                                        false, ClassConstants.ACC_PRIVATE,
270:                                        ClassConstants.ACC_PRIVATE);
271:                                classTree.retainMethod("**/writeReplace",
272:                                        "()Ljava/lang/Object;", false,
273:                                        "java/io/Serializable", false, false,
274:                                        0, 0);
275:                                classTree.retainMethod("**/readResolve",
276:                                        "()Ljava/lang/Object;", false,
277:                                        "java/io/Serializable", false, false,
278:                                        0, 0);
279:                                classTree.retainField("**/serialVersionUID",
280:                                        "J", false, "java/io/Serializable",
281:                                        false, false, ClassConstants.ACC_STATIC
282:                                                | ClassConstants.ACC_FINAL,
283:                                        ClassConstants.ACC_STATIC
284:                                                | ClassConstants.ACC_FINAL);
285:                                classTree.retainField(
286:                                        "**/serialPersistentFields",
287:                                        "[Ljava/io/ObjectStreamField;", false,
288:                                        "java/io/Serializable", false, false,
289:                                        ClassConstants.ACC_PRIVATE
290:                                                | ClassConstants.ACC_STATIC
291:                                                | ClassConstants.ACC_FINAL,
292:                                        ClassConstants.ACC_PRIVATE
293:                                                | ClassConstants.ACC_STATIC
294:                                                | ClassConstants.ACC_FINAL);
295:                                classTree.retainClass("**", false, false,
296:                                        false, false, false,
297:                                        "java/io/Serializable", false, false,
298:                                        0, 0);
299:                                classTree.retainField("**", "*", false,
300:                                        "java/io/Serializable", false, false,
301:                                        ClassConstants.ACC_TRANSIENT
302:                                                | ClassConstants.ACC_STATIC, 0);
303:                            }
304:                            break;
305:
306:                        case RgsEntry.TYPE_ATTR:
307:                            classTree.retainAttribute(entry.name);
308:                            break;
309:
310:                        case RgsEntry.TYPE_NOWARN:
311:                            classTree.noWarnClass(entry.name);
312:                            break;
313:
314:                        case RgsEntry.TYPE_CLASS:
315:                        case RgsEntry.TYPE_NOTRIM_CLASS:
316:                        case RgsEntry.TYPE_NOT_CLASS:
317:                            classTree.retainClass(entry.name,
318:                                    entry.retainToPublic,
319:                                    entry.retainToProtected,
320:                                    entry.retainPubProtOnly,
321:                                    entry.retainFieldsOnly,
322:                                    entry.retainMethodsOnly, entry.extendsName,
323:                                    entry.type == entry.TYPE_NOT_CLASS,
324:                                    entry.type == entry.TYPE_NOTRIM_CLASS,
325:                                    entry.accessMask, entry.accessSetting);
326:                            break;
327:
328:                        case RgsEntry.TYPE_METHOD:
329:                        case RgsEntry.TYPE_NOTRIM_METHOD:
330:                        case RgsEntry.TYPE_NOT_METHOD:
331:                            classTree.retainMethod(entry.name,
332:                                    entry.descriptor, entry.retainAndClass,
333:                                    entry.extendsName,
334:                                    entry.type == entry.TYPE_NOT_METHOD,
335:                                    entry.type == entry.TYPE_NOTRIM_METHOD,
336:                                    entry.accessMask, entry.accessSetting);
337:                            break;
338:
339:                        case RgsEntry.TYPE_FIELD:
340:                        case RgsEntry.TYPE_NOT_FIELD:
341:                            classTree.retainField(entry.name, entry.descriptor,
342:                                    entry.retainAndClass, entry.extendsName,
343:                                    entry.type == entry.TYPE_NOT_FIELD,
344:                                    entry.type == entry.TYPE_NOTRIM_FIELD,
345:                                    entry.accessMask, entry.accessSetting);
346:                            break;
347:
348:                        case RgsEntry.TYPE_PACKAGE_MAP:
349:                            classTree.retainPackageMap(entry.name,
350:                                    entry.obfName);
351:                            break;
352:
353:                        case RgsEntry.TYPE_REPACKAGE_MAP:
354:                            classTree.retainRepackageMap(entry.name,
355:                                    entry.obfName);
356:                            break;
357:
358:                        case RgsEntry.TYPE_CLASS_MAP:
359:                            classTree.retainClassMap(entry.name, entry.obfName);
360:                            break;
361:
362:                        case RgsEntry.TYPE_METHOD_MAP:
363:                            classTree.retainMethodMap(entry.name,
364:                                    entry.descriptor, entry.obfName);
365:                            break;
366:
367:                        case RgsEntry.TYPE_FIELD_MAP:
368:                            classTree.retainFieldMap(entry.name, entry.obfName);
369:                            break;
370:
371:                        default:
372:                            throw new Exception(
373:                                    "Illegal type received from the .rgs script");
374:                        }
375:                    } catch (Exception e) {
376:                        log.println(WARNING_SCRIPT_ENTRY_ABSENT + entry.name);
377:                    }
378:                }
379:            }
380:
381:            /** Write any non-suppressed warnings to the log. */
382:            public void logWarnings(PrintWriter log) throws Exception {
383:                if (classTree != null) {
384:                    classTree.logWarnings(log);
385:                }
386:            }
387:
388:            /** Generate a mapping table for obfuscation. */
389:            public void createMap(PrintWriter log) throws Exception {
390:                // Build database if not already done
391:                if (classTree == null) {
392:                    buildClassTree(log);
393:                }
394:
395:                // Traverse the class tree, generating obfuscated names within
396:                // package and class namespaces
397:                classTree.generateNames(enableRepackage);
398:
399:                // Resolve the polymorphic dependencies of each class, generating
400:                // non-private method and field names for each namespace
401:                classTree.resolveClasses();
402:
403:                // Signal that the namespace maps have been created
404:                hasMap = true;
405:
406:                // Write the memory usage at this point to the log file
407:                Runtime rt = Runtime.getRuntime();
408:                rt.gc();
409:                log.println("#");
410:                log.println(LOG_MEMORY_USED
411:                        + Long.toString(rt.totalMemory() - rt.freeMemory())
412:                        + LOG_MEMORY_BYTES);
413:                log.println(LOG_MEMORY_TOTAL + Long.toString(rt.totalMemory())
414:                        + LOG_MEMORY_BYTES);
415:                log.println("#");
416:            }
417:
418:            /**
419:             * If trim requested, mark all classes, methods, fields for trimming,
420:             * then traverse method calls from isFixed methods, untrimming methods,
421:             * fields, and classes touched.
422:             */
423:            public void trim(PrintWriter log) throws Exception {
424:                // Generate map table if not already done
425:                if (!hasMap) {
426:                    createMap(log);
427:                }
428:
429:                if (enableTrim) {
430:                    // Mark all for trimming
431:                    classTree.walkTree(new TreeAction() {
432:                        public void classAction(Cl cl) {
433:                            cl.setTrimCheck(false);
434:                            cl.setTrimmed(true);
435:                        }
436:
437:                        public void methodAction(Md md) {
438:                            md.setTrimCheck(false);
439:                            md.setTrimmed(true);
440:                        }
441:
442:                        public void fieldAction(Fd fd) {
443:                            fd.setTrimCheck(false);
444:                            fd.setTrimmed(true);
445:                        }
446:
447:                        public void packageAction(Pk pk) {
448:                        }
449:                    });
450:                    // Add script-fixed and inheritance-fixed items to stack
451:                    final TIStack stack = new TIStack();
452:                    classTree.walkTree(new TreeAction() {
453:                        public void classAction(Cl cl) {
454:                            if (cl.isFromScript())
455:                                stack.push(cl);
456:                        }
457:
458:                        public void methodAction(Md md) {
459:                            if (md.isFromScript())
460:                                stack.push(md);
461:                            // NOTE - Push all methods which are overrides indirectly
462:                            // onto the stack via their parent class (only doing the 
463:                            // actual push later, if their class is referenced). This
464:                            // is still over-conservative, since the override method
465:                            // could be safely trimmed if none of its super 
466:                            // implementations is called. 
467:                            if (md.isOverride()) {
468:                                md.getParent().addRef(md);
469:                            }
470:                        }
471:
472:                        public void fieldAction(Fd fd) {
473:                            if (fd.isFromScript())
474:                                stack.push(fd);
475:                            // NOTE - See above
476:                            if (fd.isOverride()) {
477:                                fd.getParent().addRef(fd);
478:                            }
479:                        }
480:
481:                        public void packageAction(Pk pk) {
482:                        }
483:                    });
484:                    // Process each, which will involve the addition and processing
485:                    // of dependent references
486:                    while (!stack.empty()) {
487:                        TreeItem ti = (TreeItem) stack.pop();
488:                        ti.pushRefs(stack);
489:                    }
490:                }
491:            }
492:
493:            /** Remap each class based on the remap database, and remove attributes. */
494:            public void remapTo(File out, PrintWriter log) throws Exception {
495:                // Generate map table if not already done
496:                if (!hasMap) {
497:                    createMap(log);
498:                }
499:
500:                // Write the name frequency and name mapping table to the log file
501:                classTree.dump(log);
502:
503:                // Go through the input Jar, removing attributes and remapping the Constant Pool
504:                // for each class file. Other files are copied through unchanged, except for manifest
505:                // and any signature files - these are deleted and the manifest is regenerated.
506:                Enumeration entries = inJar.entries();
507:                ZipOutputStream outJar = null;
508:                try {
509:                    outJar = new ZipOutputStream(new BufferedOutputStream(
510:                            new FileOutputStream(out)));
511:                    // No comment in Pro, to reduce output jar size
512:                    if (Version.isLite) {
513:                        outJar.setComment(Version.getJarComment());
514:                    }
515:                    while (entries.hasMoreElements()) {
516:                        // Get the next entry from the input Jar
517:                        ZipEntry inEntry = (ZipEntry) entries.nextElement();
518:
519:                        // Ignore directories
520:                        if (inEntry.isDirectory()) {
521:                            continue;
522:                        }
523:
524:                        // Open the entry and prepare to process it
525:                        DataInputStream inStream = null;
526:                        try {
527:                            inStream = new DataInputStream(
528:                                    new BufferedInputStream(inJar
529:                                            .getInputStream(inEntry)));
530:                            String inName = inEntry.getName();
531:                            if (inName.length() > CLASS_EXT.length()
532:                                    && inName.substring(
533:                                            inName.length()
534:                                                    - CLASS_EXT.length(),
535:                                            inName.length()).equals(CLASS_EXT)) {
536:                                // Write obfuscated class to the output Jar
537:                                ClassFile cf = ClassFile.create(inStream);
538:                                // To reduce output jar size in Pro, no class ID string
539:                                if (Version.isLite) {
540:                                    cf.setIdString(Version.getClassIdString());
541:                                }
542:                                Cl cl = classTree.getCl(cf.getName());
543:                                // Trim entire class if requested
544:                                if (cl != null && !cl.isTrimmed()) {
545:                                    cf.trimAttrs(classTree);
546:                                    cl.trimClassFile(cf);
547:                                    cf.updateRefCount();
548:                                    cf.remap(classTree, log,
549:                                            enableMapClassString,
550:                                            enableDummySourceFile);
551:                                    ZipEntry outEntry = new ZipEntry(cf
552:                                            .getName()
553:                                            + CLASS_EXT);
554:                                    outJar.putNextEntry(outEntry);
555:
556:                                    // Create an OutputStream piped through a number of digest generators for the manifest
557:                                    MessageDigest shaDigest = null;
558:                                    MessageDigest md5Digest = null;
559:                                    OutputStream outputStream = outJar;
560:                                    if (enableDigestSHA) {
561:                                        shaDigest = MessageDigest
562:                                                .getInstance("SHA-1");
563:                                        outputStream = new DigestOutputStream(
564:                                                outputStream, shaDigest);
565:                                    }
566:                                    if (enableDigestMD5) {
567:                                        md5Digest = MessageDigest
568:                                                .getInstance("MD5");
569:                                        outputStream = new DigestOutputStream(
570:                                                outputStream, md5Digest);
571:                                    }
572:                                    DataOutputStream dataOutputStream = new DataOutputStream(
573:                                            outputStream);
574:
575:                                    // Dump the classfile, while creating the digests
576:                                    cf.write(dataOutputStream);
577:                                    dataOutputStream.flush();
578:                                    outJar.closeEntry();
579:
580:                                    // Now update the manifest entry for the class with new name and new digests
581:                                    MessageDigest[] digests = { shaDigest,
582:                                            md5Digest };
583:                                    updateManifest(inName, cf.getName()
584:                                            + CLASS_EXT, digests);
585:                                }
586:                            } else if (STREAM_NAME_MANIFEST.equals(inName
587:                                    .toUpperCase())
588:                                    || (inName.length() > (SIGNATURE_PREFIX
589:                                            .length() + 1 + SIGNATURE_EXT
590:                                            .length())
591:                                            && inName.indexOf(SIGNATURE_PREFIX) != -1 && inName
592:                                            .substring(
593:                                                    inName.length()
594:                                                            - SIGNATURE_EXT
595:                                                                    .length(),
596:                                                    inName.length()).equals(
597:                                                    SIGNATURE_EXT))) {
598:                                // Don't pass through the manifest or signature files
599:                                continue;
600:                            } else {
601:                                // Copy the non-class entry through unchanged
602:                                long size = inEntry.getSize();
603:                                if (size != -1) {
604:                                    byte[] bytes = new byte[(int) size];
605:                                    inStream.readFully(bytes);
606:                                    String outName = classTree
607:                                            .getOutName(inName);
608:                                    ZipEntry outEntry = new ZipEntry(outName);
609:                                    outJar.putNextEntry(outEntry);
610:
611:                                    // Create an OutputStream piped through a number of digest generators for the manifest
612:                                    MessageDigest shaDigest = null;
613:                                    MessageDigest md5Digest = null;
614:                                    OutputStream outputStream = outJar;
615:                                    if (enableDigestSHA) {
616:                                        shaDigest = MessageDigest
617:                                                .getInstance("SHA-1");
618:                                        outputStream = new DigestOutputStream(
619:                                                outputStream, shaDigest);
620:                                    }
621:                                    if (enableDigestMD5) {
622:                                        md5Digest = MessageDigest
623:                                                .getInstance("MD5");
624:                                        outputStream = new DigestOutputStream(
625:                                                outputStream, md5Digest);
626:                                    }
627:                                    DataOutputStream dataOutputStream = new DataOutputStream(
628:                                            outputStream);
629:
630:                                    // Dump the data, while creating the digests
631:                                    dataOutputStream.write(bytes, 0,
632:                                            bytes.length);
633:                                    dataOutputStream.flush();
634:                                    outJar.closeEntry();
635:
636:                                    // Now update the manifest entry for the entry with new name and new digests
637:                                    MessageDigest[] digests = { shaDigest,
638:                                            md5Digest };
639:                                    updateManifest(inName, outName, digests);
640:                                }
641:                            }
642:                        } finally {
643:                            if (inStream != null) {
644:                                inStream.close();
645:                            }
646:                        }
647:                    }
648:
649:                    // Finally, write the new manifest file
650:                    ZipEntry outEntry = new ZipEntry(STREAM_NAME_MANIFEST);
651:                    outJar.putNextEntry(outEntry);
652:                    PrintWriter writer = new PrintWriter(new BufferedWriter(
653:                            new OutputStreamWriter(outJar)));
654:                    newManifest.writeString(writer);
655:                    writer.flush();
656:                    outJar.closeEntry();
657:                } finally {
658:                    if (outJar != null) {
659:                        outJar.close();
660:                    }
661:                }
662:            }
663:
664:            /** Close input JAR file. */
665:            public void close() throws Exception {
666:                if (inJar != null) {
667:                    inJar.close();
668:                    inJar = null;
669:                }
670:            }
671:
672:            // Parse the RFC822-style MANIFEST.MF file
673:            private void parseManifest() throws Exception {
674:                // The manifest file is the first in the jar and is called
675:                // (case insensitively) 'MANIFEST.MF'
676:                oldManifest = new SectionList();
677:                Enumeration entries = inJar.entries();
678:                while (entries.hasMoreElements()) {
679:                    // Get the first entry only from the input Jar
680:                    ZipEntry inEntry = (ZipEntry) entries.nextElement();
681:                    String name = inEntry.getName();
682:                    if (STREAM_NAME_MANIFEST.equals(name.toUpperCase())) {
683:                        oldManifest.parse(inJar.getInputStream(inEntry));
684:                        break;
685:                    }
686:                }
687:
688:                // Create a fresh manifest, with a version header
689:                newManifest = new SectionList();
690:                Section version = oldManifest.find(MANIFEST_VERSION_TAG,
691:                        MANIFEST_VERSION_VALUE);
692:                if (version == null) {
693:                    version = new Section();
694:                    version.add(MANIFEST_VERSION_TAG, MANIFEST_VERSION_VALUE);
695:                }
696:                newManifest.add(version);
697:
698:                // copy through all the none-filename sections, apart from the version
699:                for (Enumeration enm = oldManifest.elements(); enm
700:                        .hasMoreElements();) {
701:                    Section section = (Section) enm.nextElement();
702:                    if (section != null && section != version) {
703:                        Header name = section.findTag(MANIFEST_NAME_TAG);
704:                        if (name == null) {
705:                            newManifest.add(section);
706:                        } else {
707:                            String value = name.getValue();
708:                            if (value.length() > 0
709:                                    && value.charAt(value.length() - 1) == '/') {
710:                                newManifest.add(section);
711:                            }
712:                        }
713:                    }
714:                }
715:            }
716:
717:            // Update an entry in the manifest file
718:            private void updateManifest(String inName, String outName,
719:                    MessageDigest[] digests) {
720:                // Check for section in old manifest
721:                Section oldSection = oldManifest
722:                        .find(MANIFEST_NAME_TAG, inName);
723:                if (oldSection != null) {
724:                    // Create fresh section for entry, and enter "Name" header
725:                    Section newSection = new Section();
726:                    newSection.add(MANIFEST_NAME_TAG, outName);
727:
728:                    // Copy over non-"Name", non-digest entries
729:                    for (Enumeration enm = oldSection.elements(); enm
730:                            .hasMoreElements();) {
731:                        Header header = (Header) enm.nextElement();
732:                        if (!header.getTag().equals(MANIFEST_NAME_TAG)
733:                                && header.getTag().indexOf("Digest") == -1) {
734:                            newSection.add(header);
735:                        }
736:                    }
737:
738:                    // Create fresh digest entries in the new section
739:                    if (digests != null && digests.length > 0) {
740:                        // Digest-Algorithms header
741:                        StringBuffer sb = new StringBuffer();
742:                        for (int i = 0; i < digests.length; i++) {
743:                            if (digests[i] != null) {
744:                                sb.append(digests[i].getAlgorithm());
745:                                sb.append(" ");
746:                            }
747:                        }
748:                        if (sb.length() > 0) {
749:                            newSection.add(MANIFEST_DIGESTALG_TAG, sb
750:                                    .toString());
751:                        }
752:
753:                        // *-Digest headers
754:                        for (int i = 0; i < digests.length; i++) {
755:                            if (digests[i] != null) {
756:                                newSection.add(digests[i].getAlgorithm()
757:                                        + "-Digest", Tools.toBase64(digests[i]
758:                                        .digest()));
759:                            }
760:                        }
761:                    }
762:
763:                    // Append the new section to the new manifest
764:                    newManifest.add(newSection);
765:                }
766:            }
767:        }
768:
769:        // Stack used for marking TreeItems that must not be trimmed
770:        class TIStack extends Stack {
771:            public Object push(Object o) {
772:                try {
773:                    if (o != null) {
774:                        TreeItem ti = (TreeItem) o;
775:                        if (ti instanceof  Cl) {
776:                            pushClTree((Cl) ti);
777:                        } else if (ti instanceof  MdFd) {
778:                            MdFd mdfd = (MdFd) ti;
779:                            // Preserve class if method or field is static
780:                            Cl cl = (Cl) mdfd.getParent();
781:                            if (mdfd.isStatic()) {
782:                                pushClTree(cl);
783:                            }
784:                            if (mdfd instanceof  Fd) {
785:                                pushFdGroup(cl, mdfd.getInName());
786:                            } else // method
787:                            {
788:                                Md md = (Md) mdfd;
789:                                String mdName = md.getInName();
790:                                // Treat special methods (<init> <clinit>) differently
791:                                if (mdName.charAt(0) == '<') {
792:                                    pushItem(md);
793:                                } else {
794:                                    pushMdGroup(cl, mdName, md.getDescriptor());
795:                                }
796:                            }
797:                        }
798:                    }
799:                } catch (Exception e) { /* ignore */
800:                }
801:                return o;
802:            }
803:
804:            // Push class and all supers onto trim-preserve stack
805:            private Object pushClTree(Cl cl) throws Exception {
806:                if (cl != null) {
807:                    pushItem(cl);
808:                    // Propagate up supers in jar
809:                    pushClTree(cl.getSuperCl());
810:                    for (Enumeration enm = cl.getSuperInterfaces(); enm
811:                            .hasMoreElements(); pushClTree((Cl) enm
812:                            .nextElement()))
813:                        ;
814:                }
815:                return cl;
816:            }
817:
818:            // Push item onto trim-preserve stack
819:            private void pushItem(TreeItem ti) {
820:                if (ti != null && !ti.isTrimCheck()) {
821:                    ti.setTrimCheck(true);
822:                    ti.setTrimmed(false);
823:                    super .push(ti);
824:                }
825:            }
826:
827:            // Push method across inheritance group onto trim-preserve stack
828:            private void pushMdGroup(Cl cl, String mdName, String mdDesc)
829:                    throws Exception {
830:                pushMdFdGroup(cl, mdName, mdDesc);
831:            }
832:
833:            // Push field across inheritance group onto trim-preserve stack
834:            private void pushFdGroup(Cl cl, String fdName) throws Exception {
835:                pushMdFdGroup(cl, fdName, null);
836:            }
837:
838:            private void pushMdFdGroup(Cl cl, String name, String desc)
839:                    throws Exception {
840:                if (cl != null) {
841:                    final String fname = name;
842:                    final String fdesc = desc;
843:                    cl.walkGroup(new TreeAction() {
844:                        public void classAction(Cl cl) {
845:                            try {
846:                                MdFd mdfd = (fdesc == null ? (MdFd) cl
847:                                        .getField(fname) : (MdFd) cl.getMethod(
848:                                        fname, fdesc));
849:                                pushItem(mdfd);
850:                            } catch (Exception e) { /* ignore */
851:                            }
852:                        }
853:                    });
854:                }
855:            }
856:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.