From 33ffb03aab9a5d8142abc1e4e010f92233027051 Mon Sep 17 00:00:00 2001 From: nathan Date: Thu, 25 Dec 2014 19:53:27 +0100 Subject: [PATCH] Initial import of OAK java build management --- .classpath | 15 + .gitignore | 5 + .project | 17 + LICENSE | 90 + LICENSE.Apachev2 | 218 ++ LICENSE.BSD | 26 + LICENSE.GPLv2_CP | 347 ++++ LICENSE.LGPLv3 | 165 ++ LICENSE.MIT | 21 + LICENSE.Public | 121 ++ README.md | 22 + src/dorkbox/Build.java | 432 ++++ src/dorkbox/BuildOptions.java | 180 ++ src/dorkbox/build/ProjectBasics.java | 337 +++ src/dorkbox/build/ProjectGwt.java | 398 ++++ src/dorkbox/build/ProjectJava.java | 512 +++++ src/dorkbox/build/SimpleArgs.java | 77 + src/dorkbox/build/util/BuildLog.java | 119 ++ src/dorkbox/build/util/BuildParser.java | 124 ++ src/dorkbox/build/util/ByteClassloader.java | 90 + src/dorkbox/build/util/ClassByteIterator.java | 106 + .../build/util/JavaMemFileManager.java | 99 + src/dorkbox/build/util/PreJarAction.java | 20 + .../build/util/jar/EncryptInterface.java | 24 + .../build/util/jar/ExtraDataInterface.java | 23 + src/dorkbox/build/util/jar/JarOptions.java | 44 + .../build/util/jar/JarSignatureUtil.java | 319 +++ src/dorkbox/build/util/jar/JarSigner.java | 316 +++ src/dorkbox/build/util/jar/JarUtil.java | 1816 +++++++++++++++++ src/dorkbox/build/util/jar/Pack.java | 34 + src/dorkbox/build/util/jar/Pack200Util.java | 488 +++++ src/dorkbox/build/util/jar/PackAction.java | 104 + src/dorkbox/build/util/jar/PackTask.java | 50 + src/dorkbox/build/util/jar/Repack.java | 104 + .../build/util/jar/SafeJarInputStream.java | 76 + src/dorkbox/build/util/jar/SafeManifest.java | 42 + src/dorkbox/license/LICENSE.Apachev2 | 218 ++ src/dorkbox/license/LICENSE.BSD | 26 + src/dorkbox/license/LICENSE.CC0 | 121 ++ src/dorkbox/license/LICENSE.EPLv1 | 210 ++ src/dorkbox/license/LICENSE.GPLv2_CP | 347 ++++ src/dorkbox/license/LICENSE.LGPLv3 | 165 ++ src/dorkbox/license/LICENSE.MIT | 21 + src/dorkbox/license/LICENSE.MPLv1.1 | 469 +++++ src/dorkbox/license/LICENSE.OFLv1.1 | 86 + src/dorkbox/license/LICENSE.Public | 121 ++ src/dorkbox/license/License.java | 464 +++++ src/dorkbox/license/LicenseType.java | 47 + 48 files changed, 9276 insertions(+) create mode 100644 .classpath create mode 100644 .gitignore create mode 100644 .project create mode 100644 LICENSE create mode 100644 LICENSE.Apachev2 create mode 100644 LICENSE.BSD create mode 100644 LICENSE.GPLv2_CP create mode 100644 LICENSE.LGPLv3 create mode 100644 LICENSE.MIT create mode 100644 LICENSE.Public create mode 100644 README.md create mode 100644 src/dorkbox/Build.java create mode 100644 src/dorkbox/BuildOptions.java create mode 100644 src/dorkbox/build/ProjectBasics.java create mode 100644 src/dorkbox/build/ProjectGwt.java create mode 100644 src/dorkbox/build/ProjectJava.java create mode 100644 src/dorkbox/build/SimpleArgs.java create mode 100644 src/dorkbox/build/util/BuildLog.java create mode 100644 src/dorkbox/build/util/BuildParser.java create mode 100644 src/dorkbox/build/util/ByteClassloader.java create mode 100644 src/dorkbox/build/util/ClassByteIterator.java create mode 100644 src/dorkbox/build/util/JavaMemFileManager.java create mode 100644 src/dorkbox/build/util/PreJarAction.java create mode 100644 src/dorkbox/build/util/jar/EncryptInterface.java create mode 100644 src/dorkbox/build/util/jar/ExtraDataInterface.java create mode 100644 src/dorkbox/build/util/jar/JarOptions.java create mode 100644 src/dorkbox/build/util/jar/JarSignatureUtil.java create mode 100644 src/dorkbox/build/util/jar/JarSigner.java create mode 100644 src/dorkbox/build/util/jar/JarUtil.java create mode 100644 src/dorkbox/build/util/jar/Pack.java create mode 100644 src/dorkbox/build/util/jar/Pack200Util.java create mode 100644 src/dorkbox/build/util/jar/PackAction.java create mode 100644 src/dorkbox/build/util/jar/PackTask.java create mode 100644 src/dorkbox/build/util/jar/Repack.java create mode 100644 src/dorkbox/build/util/jar/SafeJarInputStream.java create mode 100644 src/dorkbox/build/util/jar/SafeManifest.java create mode 100644 src/dorkbox/license/LICENSE.Apachev2 create mode 100644 src/dorkbox/license/LICENSE.BSD create mode 100644 src/dorkbox/license/LICENSE.CC0 create mode 100644 src/dorkbox/license/LICENSE.EPLv1 create mode 100644 src/dorkbox/license/LICENSE.GPLv2_CP create mode 100644 src/dorkbox/license/LICENSE.LGPLv3 create mode 100644 src/dorkbox/license/LICENSE.MIT create mode 100644 src/dorkbox/license/LICENSE.MPLv1.1 create mode 100644 src/dorkbox/license/LICENSE.OFLv1.1 create mode 100644 src/dorkbox/license/LICENSE.Public create mode 100644 src/dorkbox/license/License.java create mode 100644 src/dorkbox/license/LicenseType.java diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..a9cfbd8 --- /dev/null +++ b/.classpath @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0b0ff27 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/classes/ +/staging/ +*.ini +*.key +temp.lzma diff --git a/.project b/.project new file mode 100644 index 0000000..d2df808 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + OAK + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c1df64a --- /dev/null +++ b/LICENSE @@ -0,0 +1,90 @@ + - Dorkbox OAK - Apache 2.0 License + https://github.com/dorkbox + Copyright 2012, dorkbox, llc + Java project management and build tool, using the Java language + + + - AnnotationDetector - Apache 2.0 License + https://github.com/rmuller/infomas-asl + Copyright 2011 - 2014, XIAM Solutions B.V. (http://www.xiam.nl) + + + - BouncyCastle - MIT License + http://www.bouncycastle.org + Copyright 2000-2009, The Legion Of The Bouncy Castle + + + - Dorkbox Utils - Apache 2.0 License + https://github.com/dorkbox + Copyright 2010, dorkbox, llc + + + - FastMD5 - LGPL v3 License + http://www.twmacinta.com/myjava/fast_md5.php + Copyright 1996, Santeri Paavolainen, Helsinki Finland + Many changes Copyright 2002 - 2010 Timothy W Macinta + Originally written by Santeri Paavolainen, Helsinki Finland 1996 + + + - FilenameUtils.java (normalize + dependencies) - Apache 2.0 License + http://commons.apache.org/proper/commons-io/ + Copyright 2013, ASF + Kevin A. Burton + Scott Sanders + Daniel Rall + Christoph.Reck + Peter Donald + Jeff Turner + Matthew Hawthorne + Martin Cooper + Jeremias Maerki + Stephen Colebourne + + + - Javatar - Public Domain + http://www.trustice.com/java/tar + Timothy Gerard Endres, time@gjt.org + + + - LZMA-Java - Apache 2.0 License + http://jponge.github.com/lzma-java + http://www.7-zip.org/sdk.html + Copyright 2014 Igor Pavlov + Julien Ponge (julien.ponge@gmail.com) + + + - MiG Base64 - BSD License + http://migbase64.sourceforge.net/ + Copyright 2004, Mikael Grev, MiG InfoCom AB. (base64@miginfocom.com) + High performance base64 encoder & decoder + + + - OpenJDK - GPL v2 License, with Classpath exception + http://openjdk.java.net + https://github.com/alexkasko/openjdk-unofficial-builds + Copyright 2007, Sun Microsystems, Inc + http://www.gnu.org/software/classpath/license.html + When GNU Classpath is used unmodified as the core class library for a virtual machine, + compiler for the java language, or for a program written in the java programming language + it does not affect the licensing for distributing those programs directly. + + + - Scar - BSD License + https://github.com/EsotericSoftware/scar + Copyright 2011, Nathan Sweet + + + - SLF4J - MIT License + http://www.slf4j.org/ + Copyright 2004-2008, QOS.ch + + + - Wildcard - BSD License + https://github.com/EsotericSoftware/wildcard + Copyright 2008, Nathan Sweet + + + - YamlBeans - BSD License + https://github.com/EsotericSoftware/yamlbeans + Copyright 2006, Ola Bini + Copyright 2008, Nathan Sweet diff --git a/LICENSE.Apachev2 b/LICENSE.Apachev2 new file mode 100644 index 0000000..79d1b97 --- /dev/null +++ b/LICENSE.Apachev2 @@ -0,0 +1,218 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/LICENSE.BSD b/LICENSE.BSD new file mode 100644 index 0000000..4ac1e8f --- /dev/null +++ b/LICENSE.BSD @@ -0,0 +1,26 @@ + BSD License + + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL TINKERPOP BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/LICENSE.GPLv2_CP b/LICENSE.GPLv2_CP new file mode 100644 index 0000000..b40a0f4 --- /dev/null +++ b/LICENSE.GPLv2_CP @@ -0,0 +1,347 @@ +The GNU General Public License (GPL) + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public License is intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. This General Public License applies to +most of the Free Software Foundation's software and to any other program whose +authors commit to using it. (Some other Free Software Foundation software is +covered by the GNU Library General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for this service if you wish), +that you receive source code or can get it if you want it, that you can change +the software or use pieces of it in new free programs; and that you know you +can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny +you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of the +software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must give the recipients all the rights that you have. You must +make sure that they, too, receive or can get the source code. And you must +show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If the +software is modified by someone else and passed on, we want its recipients to +know that what they have is not the original, so that any problems introduced +by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We +wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program proprietary. +To prevent this, we have made it clear that any patent must be licensed for +everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification +follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice +placed by the copyright holder saying it may be distributed under the terms of +this General Public License. The "Program", below, refers to any such program +or work, and a "work based on the Program" means either the Program or any +derivative work under copyright law: that is to say, a work containing the +Program or a portion of it, either verbatim or with modifications and/or +translated into another language. (Hereinafter, translation is included +without limitation in the term "modification".) Each licensee is addressed as +"you". + +Activities other than copying, distribution and modification are not covered by +this License; they are outside its scope. The act of running the Program is +not restricted, and the output from the Program is covered only if its contents +constitute a work based on the Program (independent of having been made by +running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as +you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this License +and to the absence of any warranty; and give any other recipients of the +Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may +at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus +forming a work based on the Program, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all of +these conditions: + + a) You must cause the modified files to carry prominent notices stating + that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole or + in part contains or is derived from the Program or any part thereof, to be + licensed as a whole at no charge to all third parties under the terms of + this License. + + c) If the modified program normally reads commands interactively when run, + you must cause it, when started running for such interactive use in the + most ordinary way, to print or display an announcement including an + appropriate copyright notice and a notice that there is no warranty (or + else, saying that you provide a warranty) and that users may redistribute + the program under these conditions, and telling the user how to view a copy + of this License. (Exception: if the Program itself is interactive but does + not normally print such an announcement, your work based on the Program is + not required to print an announcement.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Program, and can be reasonably +considered independent and separate works in themselves, then this License, and +its terms, do not apply to those sections when you distribute them as separate +works. But when you distribute the same sections as part of a whole which is a +work based on the Program, the distribution of the whole must be on the terms +of this License, whose permissions for other licensees extend to the entire +whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise the +right to control the distribution of derivative or collective works based on +the Program. + +In addition, mere aggregation of another work not based on the Program with the +Program (or with a work based on the Program) on a volume of a storage or +distribution medium does not bring the other work under the scope of this +License. + +3. You may copy and distribute the Program (or a work based on it, under +Section 2) in object code or executable form under the terms of Sections 1 and +2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source + code, which must be distributed under the terms of Sections 1 and 2 above + on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to + give any third party, for a charge no more than your cost of physically + performing source distribution, a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of Sections 1 + and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed only + for noncommercial distribution and only if you received the program in + object code or executable form with such an offer, in accord with + Subsection b above.) + +The source code for a work means the preferred form of the work for making +modifications to it. For an executable work, complete source code means all +the source code for all modules it contains, plus any associated interface +definition files, plus the scripts used to control compilation and installation +of the executable. However, as a special exception, the source code +distributed need not include anything that is normally distributed (in either +source or binary form) with the major components (compiler, kernel, and so on) +of the operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the source +code from the same place counts as distribution of the source code, even though +third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as +expressly provided under this License. Any attempt otherwise to copy, modify, +sublicense or distribute the Program is void, and will automatically terminate +your rights under this License. However, parties who have received copies, or +rights, from you under this License will not have their licenses terminated so +long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. +However, nothing else grants you permission to modify or distribute the Program +or its derivative works. These actions are prohibited by law if you do not +accept this License. Therefore, by modifying or distributing the Program (or +any work based on the Program), you indicate your acceptance of this License to +do so, and all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), +the recipient automatically receives a license from the original licensor to +copy, distribute or modify the Program subject to these terms and conditions. +You may not impose any further restrictions on the recipients' exercise of the +rights granted herein. You are not responsible for enforcing compliance by +third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), conditions +are imposed on you (whether by court order, agreement or otherwise) that +contradict the conditions of this License, they do not excuse you from the +conditions of this License. If you cannot distribute so as to satisfy +simultaneously your obligations under this License and any other pertinent +obligations, then as a consequence you may not distribute the Program at all. +For example, if a patent license would not permit royalty-free redistribution +of the Program by all those who receive copies directly or indirectly through +you, then the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply and +the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or +other property right claims or to contest validity of any such claims; this +section has the sole purpose of protecting the integrity of the free software +distribution system, which is implemented by public license practices. Many +people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose that +choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain +countries either by patents or by copyrighted interfaces, the original +copyright holder who places the Program under this License may add an explicit +geographical distribution limitation excluding those countries, so that +distribution is permitted only in or among countries not thus excluded. In +such case, this License incorporates the limitation as if written in the body +of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the +General Public License from time to time. Such new versions will be similar in +spirit to the present version, but may differ in detail to address new problems +or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any later +version", you have the option of following the terms and conditions either of +that version or of any later version published by the Free Software Foundation. +If the Program does not specify a version number of this License, you may +choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs +whose distribution conditions are different, write to the author to ask for +permission. For software which is copyrighted by the Free Software Foundation, +write to the Free Software Foundation; we sometimes make exceptions for this. +Our decision will be guided by the two goals of preserving the free status of +all derivatives of our free software and of promoting the sharing and reuse of +software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR +THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE +PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, +YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL +ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE +PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR +INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA +BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER +OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible +use to the public, the best way to achieve this is to make it free software +which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach +them to the start of each source file to most effectively convey the exclusion +of warranty; and each file should have at least the "copyright" line and a +pointer to where the full notice is found. + + One line to give the program's name and a brief idea of what it does. + + Copyright (C) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it +starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes + with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free + software, and you are welcome to redistribute it under certain conditions; + type 'show c' for details. + +The hypothetical commands 'show w' and 'show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may be +called something other than 'show w' and 'show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the program, if necessary. Here +is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + 'Gnomovision' (which makes passes at compilers) written by James Hacker. + + signature of Ty Coon, 1 April 1989 + + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General Public +License instead of this License. + + +"CLASSPATH" EXCEPTION TO THE GPL + +Certain source files distributed by Oracle America and/or its affiliates are +subject to the following clarification and special exception to the GPL, but +only where Oracle has expressly included in the particular source file's header +the words "Oracle designates this particular file as subject to the "Classpath" +exception as provided by Oracle in the LICENSE file that accompanied this code." + + Linking this library statically or dynamically with other modules is making + a combined work based on this library. Thus, the terms and conditions of + the GNU General Public License cover the whole combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent modules, + and to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent module, + the terms and conditions of the license of that module. An independent + module is a module which is not derived from or based on this library. If + you modify this library, you may extend this exception to your version of + the library, but you are not obligated to do so. If you do not wish to do + so, delete this exception statement from your version. diff --git a/LICENSE.LGPLv3 b/LICENSE.LGPLv3 new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/LICENSE.LGPLv3 @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/LICENSE.MIT b/LICENSE.MIT new file mode 100644 index 0000000..2e9f5aa --- /dev/null +++ b/LICENSE.MIT @@ -0,0 +1,21 @@ + MIT License + + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/LICENSE.Public b/LICENSE.Public new file mode 100644 index 0000000..1625c17 --- /dev/null +++ b/LICENSE.Public @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..68d6e94 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +OAK +=== + +Dorkbox OAK is a Java project management and build tool, using the Java language. + +The main distinction between this tool and others, such as Ant, Maven, Ivy, Gradle, etc. is that +*this* build tool lets you work directly in Java. Since you are building a Java project, it can +safely be assumed that you already know Java, why use XML, Groovy, or some other language to build +your project? + +- This is for cross-platform use, specifically - linux 32/64, mac 32/64, and windows 32/64. Java 6+ + + +For example: + + - See Build.java to see an example + +OR, for a "real world" example + + - See the example code in the wiki, which is what is used to build OAK + - Then, java -jar libs/OAK.jar build oak + diff --git a/src/dorkbox/Build.java b/src/dorkbox/Build.java new file mode 100644 index 0000000..2a29c57 --- /dev/null +++ b/src/dorkbox/Build.java @@ -0,0 +1,432 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Date; +import java.util.HashMap; +import java.util.List; + +import com.esotericsoftware.wildcard.Paths; + +import dorkbox.build.ProjectBasics; +import dorkbox.build.ProjectJava; +import dorkbox.build.SimpleArgs; +import dorkbox.build.util.BuildLog; +import dorkbox.build.util.BuildParser; +import dorkbox.build.util.ByteClassloader; +import dorkbox.build.util.ClassByteIterator; +import dorkbox.build.util.jar.Pack200Util; +import dorkbox.util.FileUtil; +import dorkbox.util.LZMA; +import dorkbox.util.LocationResolver; +import dorkbox.util.OS; +import dorkbox.util.Sys; +import dorkbox.util.annotation.AnnotationDefaults; +import dorkbox.util.annotation.AnnotationDetector; +import dorkbox.util.properties.PropertiesProvider; + +public class Build { + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.TYPE}) + public @interface Builder { + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.TYPE}) + public @interface Configure { + } + + public static final String BUILD_MODE = "build"; + + /** Location where settings are stored */ + public static PropertiesProvider settings = new PropertiesProvider(new File("settings.ini")); + + private ByteClassloader classloader; + + static { + Paths.setDefaultGlobExcludes("**/.svn/**, **/.git/**"); + } + + public static void main(String[] _args) { + for (int i=0;i<_args.length;i++) { + _args[i] = _args[i].toLowerCase(); + } + + if (_args.length < 2) { + System.err.println("You must specify an action, followed by what you want done."); + System.err.println("For example: build myProject , which will then find and build your project"); + return; + } + + SimpleArgs args = new SimpleArgs(_args); + + System.err.println("Dorkbox OAK: starting " + args); + + Build build = new Build(); + try { + build.prepareXcompile(); + build.loadBuildInfo(args); + build.start(args); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + // loads the build.oak file information + private void loadBuildInfo(SimpleArgs args) throws Exception { + HashMap data = BuildParser.parse(args); + + + Paths classPaths = BuildParser.getPathsFromMap(data, "classpath"); + Paths sourcePaths = BuildParser.getPathsFromMap(data, "source"); + + // always use these as the default. don't want the runtimes on our path + classPaths.glob("libs", "**/*.jar", "!jdkRuntimes"); + + + String projectName = "project"; + + if (data.containsKey("name")) { + Object object = data.get("name"); + projectName = (String) object; + } + + ByteClassloader bytesClassloader = new ByteClassloader(Thread.currentThread().getContextClassLoader()); + + ProjectJava project = ProjectJava.create(projectName) + .classPath(classPaths) + .compilerClassloader(bytesClassloader) + .sourcePath(sourcePaths); + + + try { + log().println("-= Compiling build instructions =-" + OS.LINE_SEPARATOR); + BuildLog.stop(); + project.forceBuild(new BuildOptions(), false, false); + ProjectBasics.reset(); + BuildLog.start(); + } catch (Exception e) { + BuildLog.start(); + throw e; + } + + this.classloader = bytesClassloader; + } + + private Build() { + } + + public static BuildLog log() { + return new BuildLog(); + } + + public static BuildLog log(PrintStream printer) { + return new BuildLog(printer); + } + + private void start(SimpleArgs args) throws IOException, IllegalAccessException, IllegalArgumentException, + InvocationTargetException, InstantiationException { + + BuildOptions buildOptions = new BuildOptions(); + List> controllers = AnnotationDetector.scan(this.classloader, new ClassByteIterator(this.classloader, null)) + .forAnnotations(Build.Configure.class) + .collect(AnnotationDefaults.getType); + + if (controllers != null) { + // do we have something to control the build process?? + // now we want to update/search for all project builders if we didn't already run our specific builder + for (Class c : controllers) { + Class[] params = new Class[] {BuildOptions.class, SimpleArgs.class}; + Method buildTargeted = null; + + // setup(BuildOptions, Args) + try { + buildTargeted = c.getMethod("setup", params); + } catch (Exception e) {} + + if (buildTargeted != null) { + Object newInstance = c.newInstance(); + // see if we can build a targeted build + buildTargeted.invoke(newInstance, buildOptions, args); + break; + } + } + } + + // now we want to update/search for all project builders. + if (args.getMode().equals(Build.BUILD_MODE)) { + boolean found = false; + List> builders = AnnotationDetector.scan(this.classloader, new ClassByteIterator(this.classloader, null)) + .forAnnotations(Build.Builder.class) + .collect(AnnotationDefaults.getType); + + if (builders != null) { + String projectToBuild = args.get(1); + for (Class c : builders) { + String simpleName = c.getSimpleName().toLowerCase(); + + if (projectToBuild.equals(simpleName)) { + Method build = null; + + // 4 different build methods supported. + // build() + try { + build = c.getMethod(Build.BUILD_MODE, new Class[] {}); + } catch (Exception e) {} + + if (build != null) { + build .invoke(c); + found = true; + break; + } + + // build(Args) + Class[] params = new Class[] {SimpleArgs.class}; + try { + build = c.getMethod(Build.BUILD_MODE, params); + } catch (Exception e) {} + + if (build != null) { + build .invoke(c, args); + found = true; + break; + } + + + // build(BuildOptions) + params = new Class[] {BuildOptions.class}; + try { + build = c.getMethod(Build.BUILD_MODE, params); + } catch (Exception e) {} + + if (build != null) { + build.invoke(c, buildOptions); + found = true; + break; + } + + + // build(BuildOptions, Args) + params = new Class[] {BuildOptions.class, SimpleArgs.class}; + try { + build = c.getMethod(Build.BUILD_MODE, params); + } catch (Exception e) {} + + if (build != null) { + build .invoke(c, buildOptions, args); + found = true; + break; + } + } + } + } + + if (!found) { + System.err.println("Unable to find a build for the target: " + args); + } + } + + if (controllers != null) { + // do we have something to control the build process?? + // now we want to update/search for all project builders if we didn't already run our specific builder + for (Class c : controllers) { + Class[] params = new Class[] {BuildOptions.class, SimpleArgs.class}; + Method buildTargeted = null; + + // finish(BuildOptions, Args) + try { + buildTargeted = c.getMethod("takedown", params); + } catch (Exception e) {} + + if (buildTargeted != null) { + Object newInstance = c.newInstance(); + // see if we can build a targeted build + buildTargeted.invoke(newInstance, buildOptions, args); + break; + } + } + } + } + + /** + * check to see if our jdk files have been decompressed (necessary for cross target builds) + */ + private void prepareXcompile() throws IOException { + String jdkDist = FileUtil.normalizeAsFile(Build.path("libs", "jdkRuntimes")); + List jdkFiles = FileUtil.parseDir(jdkDist); + boolean first = true; + for (File f : jdkFiles) { + // unLZMA + unpack200 + String name = f.getName(); + String suffix = ".pack.lzma"; + + if (name.endsWith(suffix)) { + int nameLength = f.getAbsolutePath().length(); + String fixedName = f.getAbsolutePath().substring(0, nameLength - suffix.length()); + File file = new File(fixedName); + + if (!file.canRead() || file.length() == 0) { + if (first) { + first = false; + System.err.println("******************************************"); + System.err.println("* Dorkbox OAK -- Preparing environment *"); + System.err.println("******************************************"); + } + + System.err.println("* Decompressing: " + f.getAbsolutePath()); + InputStream inputStream = new FileInputStream(f); + // now uncompress + ByteArrayOutputStream outputStream = LZMA.decode(inputStream); + + // now unpack + inputStream = new ByteArrayInputStream(outputStream.toByteArray()); + outputStream = Pack200Util.Java.unpack200((ByteArrayInputStream) inputStream); + + // now write to disk + inputStream = new ByteArrayInputStream(outputStream.toByteArray()); + + FileOutputStream fileOutputStream = new FileOutputStream(new File(fixedName)); + Sys.copyStream(inputStream, fileOutputStream); + Sys.close(fileOutputStream); + } + } + } + + if (!first) { + System.err.println("* Finished preparing environment"); + System.err.println("******************************************\n\n"); + } + } + + public static String path(String... paths) { + StringBuilder buffer = new StringBuilder(128); + + for (String p : paths) { + buffer.append(p).append(File.separator); + } + + int length = buffer.length(); + buffer.delete(length-1, length); + + return buffer.toString(); + } + + /** + * Converts a class to it's .java file. + */ + public static Paths getClassPath(Class clazz) throws IOException { + String rootPath = LocationResolver.get(clazz).getAbsolutePath(); + + String fileName = clazz.getCanonicalName(); + String convert = fileName.replace('.', File.separatorChar) + ".java"; + File rootFile = new File(rootPath); + + if (rootFile.isDirectory()) { + File location = rootFile.getParentFile(); + location = new File(location, "src"); + + Paths path = new Paths(location.getAbsolutePath(), convert); + return path; + } else { + throw new IOException("we can only support listing files that are not in a container!"); + } + } + + /** + * Gets all of the .java files accessible which belong to the + * package and subpackages of the given class + */ + public static Paths getClassPathPackage(Class clazz) throws IOException { + String rootPath = LocationResolver.get(clazz).getAbsolutePath(); + + String dirName = clazz.getPackage().getName(); + String convert = dirName.replace('.', File.separatorChar); + File rootFile = new File(rootPath); + + if (rootFile.isDirectory()) { + File location = rootFile.getParentFile(); + location = new File(location, "src"); + location = new File(location, convert); + + Paths paths = new Paths(location.getAbsolutePath(), "**.java"); + return paths; + } else { + throw new IOException("we can only support listing class path packages that are not in a container!"); + } + } + + public static final void finish(String text) { + System.err.println("\n\n"); + System.err.println("TIME: " + new Date()); + System.err.println("FINISHED: " + text); + } + + public static File moveFile(String source, String target) throws IOException { + source = FileUtil.normalizeAsFile(source); + target = FileUtil.normalizeAsFile(target); + + + log().title(" Moving file").message(" ╭─ " + source, + "╰─> " + target); + + return FileUtil.moveFile(source, target); + } + + public static File copyFile(File source, File target) throws IOException { + source = FileUtil.normalize(source); + target = FileUtil.normalize(target); + + + log().title(" Copying file").message(" ╭─ " + source.getAbsolutePath(), + "╰─> " + target.getAbsolutePath()); + + return FileUtil.copyFile(source, target); + } + + public static void copyFile(String source, String target) throws IOException { + source = FileUtil.normalizeAsFile(source); + target = FileUtil.normalizeAsFile(target); + + log().title(" Copying file").message(" ╭─ " + source, + "╰─> " + target); + + FileUtil.copyFile(source, target); + } + + public static void copyDirectory(String source, String target, String... dirNamesToIgnore) throws IOException { + source = FileUtil.normalizeAsFile(source); + target = FileUtil.normalizeAsFile(target); + + log().title(" Copying dir").message(" ╭─ " + source, + "╰─> " + target); + FileUtil.copyDirectory(source, target, dirNamesToIgnore); + } +} diff --git a/src/dorkbox/BuildOptions.java b/src/dorkbox/BuildOptions.java new file mode 100644 index 0000000..839dd2e --- /dev/null +++ b/src/dorkbox/BuildOptions.java @@ -0,0 +1,180 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox; + +public class BuildOptions { + + /** + * Options that affect the compilation of the project + */ + public static class Compiler { + /** + * Do we want to force a rebuild of the project? + */ + public boolean forceRebuild = false; + + /** + * enables PACK200+LZMA+GZIP+ENCRYPTION on jar contents. Since this is + * REALLY SLOW (creating and running), we don't always want to do this. + */ + public boolean release = false; + + /** + * we want debugging enabled (until release!) + */ + public boolean debugEnabled = true; + + /** + * if we are debug mode, do we want to disable certain actions to make compiling faster? + */ + public boolean enableDebugSpeedImprovement = false; + + + /** + * Suppress sun warnings during the compile stage. ONLY enable this is you know what you are doing in your project! + */ + public boolean suppressSunWarnings = false; + + /** + * what version do we want to compile java for? + * + * when compiling for java 1.6, you MUST specify the 1.6 rt.jar location + * Also, when compiling GWT, this has no effect + */ + public int targetJavaVersion = 7; + + /** + * this is only necessary when building for lesser versions of java than you are currently running + * (for example, compiling for 1.6, when compiling on 1.7). + *

+ * This is meant to be overridden for custom build locations + */ + public CrossCompilerLibrary crossCompileLibrary = new CrossCompilerLibrary(); + + + /** + * US export controls require that the JVM cannot perform AES-256 crypto. Here we are able to control the JCE policy files to + * to permit unlimited crypto if we want to (and are following US export controls) + */ + public boolean unlimitedJceCrpytoRuntime = true; + + /** + * Adds the "verbose" compile option. This is useful if you want to get a list (from the compiler) of EVERY CLASS compiled/used + */ + public boolean enableCompilerTrace = false; + + /** + * Provide the location of the rt.jar libraries for 'cross compiling' to a different java target. + *

+ * This is meant to be overridden for custom locations. + */ + public static class CrossCompilerLibrary { + public CrossCompilerLibrary() { + } + + /** Please note that the binary release is GLPv2 + Classpath Exception, giving us permission to use it to compile binaries */ + public String getCrossCompileLibraryLocation(int targetVersion) { + return Build.path("libs", "jdkRuntimes", "openJdk" + targetVersion + "_rt.jar"); + } + } + } + + /** + * Options that affect how the launcher is included, and how the jar is signed/encrypted + */ + public static class Launcher { + /** + * do we want to enable the launcher crypto signature verification? (runtime requires this) + */ + public boolean crypto = true; + + /** + * do we want to deploy the JAVA runtime as a part of our app? (depends on crypto to work) + */ + public boolean runtime = true; + + /** + * do we want to enable the key/mouse input monitor? + */ + public boolean monitor = true; + + /** + * do we want to enable the socket bind wrapper? (when you run launcher as root, it will drop root when runnning java) + */ + public boolean bindWrapper = true; + + /** + * do we want to enable LGPL parsing of the RESOURCES.BOX file? + */ + public boolean lpgl = false; + } + + /** + * Misc libraries to include (which are not easy to just link the library) + */ + public static class Misc { + /** + * Java7 (but not ARM) can have the optional JavaFX library included. + */ + public boolean includeJavaFx = false; + + } + + public Compiler compiler = new Compiler(); + public Launcher launcher = new Launcher(); + public Misc misc = new Misc(); + + + + + /** + * Gets the executable name based on what different build options are specified. + */ + public String getExecutableName(String exectuableBaseName) { + if (this.launcher.runtime && !this.launcher.crypto) { + throw new RuntimeException("Unable to deply runtime with crypto disabled! You must enable crypto to continue!"); + } + + String newName = exectuableBaseName; + if (this.compiler.debugEnabled) { + newName += "_debug"; + } + if (this.launcher.lpgl) { + newName += "_lgpl"; + } + if (this.launcher.crypto) { + newName += "_crypto"; + } + if (this.launcher.runtime) { + newName += "_runtime"; + } + if (this.launcher.monitor) { + newName += "_monitor"; + } + if (this.launcher.bindWrapper) { + newName += "_bind"; + } + + return newName; + } + + /** + * @return the target java version to compile, in full format. IE: "1.6", or "1.7" + */ + public String getTargetVersion() { + return "1."+this.compiler.targetJavaVersion; + } +} diff --git a/src/dorkbox/build/ProjectBasics.java b/src/dorkbox/build/ProjectBasics.java new file mode 100644 index 0000000..a9f2ffd --- /dev/null +++ b/src/dorkbox/build/ProjectBasics.java @@ -0,0 +1,337 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.bouncycastle.crypto.digests.MD5Digest; + +import com.esotericsoftware.wildcard.Paths; +import com.twmacinta.util.MD5; + +import dorkbox.Build; +import dorkbox.BuildOptions; +import dorkbox.util.Base64Fast; +import dorkbox.util.FileUtil; + +public abstract class ProjectBasics { + public static final String NO_PATH_TOKEN = "NO_PATH_TOKEN"; + + public static final String Java_Pattern = "**" + File.separator + "*.java"; + public static final String Jar_Pattern = "**" + File.separator + "*.jar"; + public static final String STAGING = "staging"; + + public static Map deps = new LinkedHashMap(); + private static Set buildList = new HashSet(); + + private static boolean forceRebuild = false; + private static boolean alreadyChecked = false; + + public static List builderFiles = new ArrayList(); + + { + // check to see if our deploy code has changed. if yes, then we have to rebuild everything since + // we don't know what might have changed. + Paths paths = new Paths(); + File file = new File(ProjectBasics.class.getSimpleName() + ".java").getAbsoluteFile().getParentFile(); + paths.glob(file.getAbsolutePath(), Java_Pattern); + + for (File f : builderFiles) { + paths.glob(f.getAbsolutePath(), Java_Pattern); + } + + try { + String oldHash = Build.settings.get("BUILD", String.class); + String hashedContents = generateChecksums(paths); + + if (oldHash != null) { + if (!oldHash.equals(hashedContents)) { + forceRebuild = true; + } + } else { + forceRebuild = true; + } + + if (forceRebuild) { + if (!alreadyChecked) { + alreadyChecked = true; + System.err.println("-= Build system changed. Rebuilding =-"); + } + Build.settings.save("BUILD", hashedContents); + } + } catch (IOException e) { + } + } + + + // removes all saved checksums as well as dependencies. Used to "reset everything", similar to if it was relaunched. + public static void reset() { + deps.clear(); + buildList.clear(); + } + + public String name; + + protected Paths extraFiles = new Paths(); + + public String outputFile; + public String outputDir; + + protected Set dependencies; + + private transient Paths checksumPaths = new Paths(); + + + public static ProjectBasics get(String projectName) { + if (deps.containsKey(projectName)) { + ProjectBasics project = deps.get(projectName); + // put swt lib into jar! + return project; + } else { + throw new IllegalArgumentException(projectName + " project must exist!"); + } + } + + public static void buildAll(BuildOptions properties) throws Exception { + for (ProjectBasics project : deps.values()) { + ProjectBasics.build(project, properties); + } + } + + public static void build(String projectName, BuildOptions properties) throws Exception { + ProjectBasics project = get(projectName); + + if (project != null) { + project.build(properties); + } else { + System.err.println("Project is NULL. Aborting build."); + } + } + + public static void build(ProjectBasics project, BuildOptions properties) throws Exception { + project.build(properties); + } + + public static void remove(String outputDir) { + deps.remove(outputDir); + } + + protected ProjectBasics(String projectName) { + this.name = projectName; + + String lowerCase_outputDir = projectName.toLowerCase(); + this.outputDir = FileUtil.normalizeAsFile(STAGING + File.separator + lowerCase_outputDir); + + String outputFile = lowerCase_outputDir.substring(lowerCase_outputDir.lastIndexOf("/") + 1, lowerCase_outputDir.length()); + this.outputFile = outputFile + getExtension(); + } + + public ProjectBasics depends(String dependsProjectName) { + if (dependsProjectName == null) { + throw new NullPointerException("Dependencies cannot be null!"); + } + + if (this.dependencies == null) { + this.dependencies = new HashSet(2); + } + this.dependencies.add(dependsProjectName); + + return this; + } + + public ProjectBasics output() { + String lowerCase_outputDir = this.outputDir.toLowerCase(); + this.outputDir = STAGING + File.separator + lowerCase_outputDir; + + String outputFile = lowerCase_outputDir.substring(lowerCase_outputDir.lastIndexOf("/") + 1, lowerCase_outputDir.length()); + this.outputFile = outputFile + getExtension(); + + return this; + } + + /** + * Checks to see if we already built this project. Also, will automatically build this projects + * dependencies (if they haven't already been built). + * + * @return true if we can skip building this project + */ + protected boolean checkAndBuildDependencies(BuildOptions properties) throws Exception { + // exit early if we already built this project + if (buildList.contains(this.outputDir)) { + Build.log().message("Skipped (built this run)"); + return true; + } + + buildList.add(this.outputDir); + + // ONLY build the dependencies as well + if (this.dependencies != null) { + for (String dep : this.dependencies) { + ProjectBasics project = deps.get(dep); + if (!buildList.contains(project.outputDir)) { + project.build(properties); + } + } + } + + return false; + } + + /** extra files to include when you jar the project */ + public ProjectBasics extraFiles(Paths filePaths) { + this.extraFiles.add(filePaths); + + return this; + } + + protected abstract ProjectBasics build(BuildOptions properties) throws Exception; + protected abstract String getExtension(); + + + + /** + * Add a path to be checksum'd. + */ + public final void checksum(Paths path) { + this.checksumPaths.add(path); + } + + /** + * @return true if the checksums for path match the saved checksums and the jar file exists + */ + boolean verifyChecksums(BuildOptions properties) throws IOException { + if (forceRebuild || properties.compiler.forceRebuild) { + return false; + } + + // check to see if our SOURCES *and check-summed files* have changed. + String hashedContents = generateChecksums(this.checksumPaths); + String checkContents = Build.settings.get(this.name, String.class); + + return hashedContents != null && hashedContents.equals(checkContents); + } + + /** + * Saves the checksums for a given path + */ + void saveChecksums() throws IOException { + // hash/save the sources *and check-summed files* files + String hashedContents = generateChecksums(this.checksumPaths); + Build.settings.save(this.name, hashedContents); + } + + /** + * Generates checksums for the given path + */ + public static final String generateChecksum(File file) throws IOException { + synchronized (ProjectBasics.class) { + // calculate the hash of file + boolean found = false; + if (file.isFile() && file.canRead()) { + found = true; + } + + if (!found) { + return null; + } + + byte[] hashBytes = MD5.getHash(file); + + String fileChecksums = Base64Fast.encodeToString(hashBytes, false); + return fileChecksums; + } + } + + /** + * Generates checksums for the given path + */ + public static final String generateChecksums(Paths... paths) throws IOException { + synchronized (ProjectBasics.class) { + // calculate the hash of all the files in the source path + Set names = new HashSet(64); + + for (Paths path : paths) { + names.addAll(path.getPaths()); + } + + // hash of hash of files. faster than using java to hash files + MD5Digest md5_digest = new MD5Digest(); + + boolean found = false; + for (String name : names) { + File file = new File(name); + if (file.isFile() && file.canRead()) { + found = true; + + byte[] hashBytes = MD5.getHash(file); + md5_digest.update(hashBytes, 0, hashBytes.length); + } + } + + if (!found) { + return null; + } + + byte[] hashBytes = new byte[md5_digest.getDigestSize()]; + md5_digest.doFinal(hashBytes, 0); + + String fileChecksums = Base64Fast.encodeToString(hashBytes, false); + return fileChecksums; + } + } + + @Override + public String toString() { + return this.name; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (this.name == null ? 0 : this.name.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ProjectBasics other = (ProjectBasics) obj; + if (this.name == null) { + if (other.name != null) { + return false; + } + } else if (!this.name.equals(other.name)) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/src/dorkbox/build/ProjectGwt.java b/src/dorkbox/build/ProjectGwt.java new file mode 100644 index 0000000..ca17eed --- /dev/null +++ b/src/dorkbox/build/ProjectGwt.java @@ -0,0 +1,398 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.Arrays; +import java.util.List; +import java.util.Map.Entry; + +import com.esotericsoftware.wildcard.Paths; + +import dorkbox.Build; +import dorkbox.BuildOptions; +import dorkbox.build.util.jar.JarUtil; +import dorkbox.util.FileUtil; +import dorkbox.util.gwt.GwtSymbolMapParser; +import dorkbox.util.process.JavaProcessBuilder; + +public class ProjectGwt extends ProjectBasics { + + private String[] options; + private String projectLocation; + protected Paths sourcePaths = new Paths(); + + public static ProjectGwt create(String projectName, String projectLocation) { + ProjectGwt project = new ProjectGwt(projectName, projectLocation); + deps.put(projectName, project); + + return project; + } + + + protected ProjectGwt(String projectName, String projectLocation) { + super(projectName); + + this.projectLocation = projectLocation; + checksum(this.sourcePaths); + } + + public ProjectGwt sourcePath(Paths sourcePaths) { + if (sourcePaths == null) { + throw new NullPointerException("Source paths cannot be null!"); + } + this.sourcePaths.add(sourcePaths); + + return this; + } + + public ProjectGwt sourcePath(String srcDir) { + if (srcDir.endsWith("src")) { + String parent = new File(srcDir).getAbsoluteFile().getParent(); + checksum(new Paths(parent)); + } + + return sourcePath(new Paths(srcDir, "./")); + } + + public ProjectGwt sourcePath(String dir, String... patterns) { + return sourcePath(new Paths(dir, patterns)); + } + + /** + * GWT only cares about the output dir (it doesn't make jars for compiling) + * @return true if the checksums for path match the saved checksums and the jar file exists + */ + boolean verifyChecksums(ProjectBasics project, BuildOptions properties) throws IOException { + boolean sourceHashesSame = super.verifyChecksums(properties); + if (!sourceHashesSame) { + return false; + } + + // if the sources are the same, check the output dir + String fileName = project.outputDir; + + File file = new File(fileName); + if (file.exists()) { + String dirChecksum = generateChecksum(file); + String checkContents = Build.settings.get(fileName, String.class); + + return dirChecksum != null && dirChecksum.equals(checkContents); + } + + return true; + } + + /** + * GWT only cares about the output dir (it doesn't make jars for compiling) + * Saves the checksums for a given path + */ + @Override + void saveChecksums() throws IOException { + super.saveChecksums(); + + // hash/save the output files (if there are any) + String fileName = this.outputDir; + File file = new File(fileName); + if (file.exists()) { + String fileChecksum = generateChecksum(file); + Build.settings.save(fileName, fileChecksum); + } + } + + + /** + * This uses the same gwt symbol parser as the web-server project. + */ + @Override + protected ProjectGwt build(BuildOptions properties) throws Exception { + // exit early if we already built this project + if (checkAndBuildDependencies(properties)) { + return this; + } + + boolean shouldBuild = false; + try { + // GWT checksum requirements are different than regular java. + shouldBuild = !verifyChecksums(properties); + + if (shouldBuild) { + // make sure our dependencies are on the classpath. + if (this.dependencies != null) { + for (String dep : this.dependencies) { + ProjectBasics project = deps.get(dep); + this.sourcePaths.glob(STAGING, project.outputFile); + } + } + + + FileUtil.delete(this.outputDir); + FileUtil.mkdir(this.outputDir); + + String clientString = "Client"; + + String stagingWar = Build.path(STAGING, "war"); + String stagingWarWebINF = Build.path(stagingWar, "WEB-INF"); + String stagingWarWebINFDeploy = Build.path(stagingWarWebINF, "deploy"); + + + String stagingJunk = Build.path(STAGING, "junk"); + String stagingUnitCache = Build.path(STAGING, "gwt-unitCache"); + String stagingSymbols = Build.path(STAGING, "symbolMaps"); + String clientTempLocation = Build.path(STAGING, clientString + "_javascript"); + + + String srcWarPath = Build.path("..", this.projectLocation, "war"); + + // make the output directory + FileUtil.delete(stagingWar); + FileUtil.delete(stagingSymbols); + FileUtil.delete(clientTempLocation); + + + FileUtil.mkdir(stagingWar); + FileUtil.mkdir(stagingUnitCache); + + if (properties.compiler.release) { + FileUtil.delete(stagingUnitCache); + } + + + + System.err.println("Compiling GWT modules. This can take a while...."); + System.err.println(" Working location: " + FileUtil.normalize(new File("test")).getParent()); + + JavaProcessBuilder builder = new JavaProcessBuilder(System.in, System.out, System.err); + builder.setMaximumHeapSizeInMegabytes(512); + + // we want to DEBUG this! (figure out wtf is going on) +// builder.addJvmOption("-Xdebug"); +// builder.addJvmOption("-Xrunjdwp:transport=dt_socket,server=y,address=1044"); + + builder.setMainClass("com.google.gwt.dev.Compiler"); + + // The Java classpath should include: + // - the Java source code of your application + // - gwt-dev.jar, gwt-user.jar, + // - any compiler-visible resources such as ui.xml and .png files, + // - and the COMPILED versions of any generators and linkers added or used by the build. + + // the GWT compiler needs the source/parent directory of the XML files + builder.addJvmClasspaths(this.sourcePaths.getPaths()); + + // prevent phone-home of google GWT compiler + builder.addArgument("-XdisableUpdateCheck"); + + // EXPERIMENTAL: Disables some java.lang.Class methods (e.g. getName()) + builder.addArgument("-XdisableClassMetadata"); + + //Logs output in a graphical tree view + builder.addArgument("-treeLogger"); + + // fail if there are any warnings + builder.addArgument("-strict"); + + // The directory into which deployable output files will be written (defaults to 'war') + builder.addArgument("-war " + stagingWar); + + // we want the compiler to save the symbols map (so we can correctly do the mapping for message-bus de-obfuscation) + builder.addArgument("-deploy " + stagingWarWebINFDeploy); + + // The directory into which extra files, not intended for deployment, will be written + builder.addArgument("-extra " + stagingJunk); + + // Additional arguments like -style PRETTY/OBFuscated/DETAILED or -logLevel INFO/WARN/ERROR/TRACE/DEBUG/SPAM/ALL + // logLevel is log level during compile. + builder.addArgument("-logLevel INFO"); +// builder.addArgument("-logLevel TRACE"); + + for (String option : this.options) { + builder.addArgument(option); + } + + // DETAILED, PRETTY, OBF[USCATED] + if (properties.compiler.debugEnabled) { +// builder.addArgument("-style PRETTY"); + builder.addArgument("-style DETAILED"); + } else { + builder.addArgument("-style OBF"); + } + + if (properties.compiler.release) { + // generate the gwt cache files EVERY time + builder.addArgument("-Dgwt.usearchives=false"); + } + + + // must be last + builder.addArgument("hive." + clientString); + + builder.start(); + + + // move the symbol maps to the correct spot + FileUtil.mkdir(stagingSymbols); + Paths symbolMapPaths = new Paths(Build.path(stagingWarWebINFDeploy, clientString, "symbolMaps"), "*.symbolMap"); + for (File file : symbolMapPaths.getFiles()) { + // clean up the symbolmaps first! + parseAndCopyGwtSymbols(file, new File(Build.path(stagingSymbols, file.getName()))); + } + + + // move the client generated javascript to the correct spot + FileUtil.moveDirectory(Build.path(stagingWar, clientString), clientTempLocation); + + + // remove directories that are not needed/wanted in the deployment + FileUtil.delete(stagingWar); + FileUtil.delete(stagingJunk); + + if (properties.compiler.release) { + FileUtil.delete(stagingUnitCache); + } + + + // create the NEW war directory + FileUtil.mkdir(stagingWar); + // todo: pass in the hive-webclient project name somehow? + Paths warFiles = new Paths(srcWarPath, "*.html", "*.ico"); + for (File file : warFiles.getFiles()) { + FileUtil.copyFile(file.getAbsolutePath(), Build.path(stagingWar, file.getName())); + } + + // copy over images + FileUtil.copyDirectory(Build.path(srcWarPath, "images"), Build.path(stagingWar, "images"), ".svn"); + + + // make the web-INF directory + FileUtil.mkdir(stagingWarWebINF); + + // move the symbolMaps into the correct spot. + FileUtil.moveDirectory(stagingSymbols, Build.path(stagingWarWebINF, "symbolMaps")); + + // add any extra files to the output war dir. + File warDir = new File(stagingWar); + + List paths = this.extraFiles.getPaths(); + List paths2 = this.extraFiles.getRelativePaths(); + for (int i=0;i fullPaths = warFiles.filesOnly().getPaths(); + List relativePaths = warFiles.filesOnly().getRelativePaths(); + + String warFilePath = Build.path(STAGING, this.outputFile); + + File warFile = new File(warFilePath); + if (warFile.exists()) { + warFile.delete(); + } + FileUtil.mkdir(warFile.getParent()); + + + // make a jar (really a war file) + JarUtil.war(warFilePath, fullPaths, relativePaths); + + // cleanup + FileUtil.delete(stagingWar); + + // calculate the hash of all the files in the source path + saveChecksums(); + } else { + Build.log().message("Skipped (nothing changed)"); + } + } finally { + if (shouldBuild) { + FileUtil.delete(this.outputDir); + } + } + + System.err.println(" Build success: " + this.outputDir); + return this; + } + + public ProjectGwt options(String... options) { + if (this.options != null) { + List origList = Arrays.asList(this.options); + List newList = Arrays.asList(options); + + origList.addAll(newList); + this.options = origList.toArray(new String[0]); + } else { + this.options = options; + } + + return this; + } + + /** + * Parses the relevant data from the symbol map and saves it in the specified file. + */ + public static void parseAndCopyGwtSymbols(File sourceFile, File destSymbolFile) throws IOException { + GwtSymbolMapParser parser = new GwtSymbolMapParser(); + + //FileReader always assumes default encoding is OK! + FileInputStream fileInputStream = new FileInputStream(sourceFile); + parser.parse(fileInputStream); + + destSymbolFile.getParentFile().mkdirs(); + Writer output = new BufferedWriter(new FileWriter(destSymbolFile)); + + try { + // FileWriter always assumes default encoding is OK + output.write("# BUILD-SCRIPT MODIFIED: only relevant symbols are present.\n"); + + StringBuilder stringBuilder = new StringBuilder(); + for (Entry entry : parser.getSymbolMap().entrySet()) { + stringBuilder.delete(0, stringBuilder.capacity()); + // create a "shrunk" version for deployment. This is marginally better than the original version + stringBuilder + .append(entry.getKey()) // jsName + .append(",") + .append(entry.getValue()) // java className + .append("\n"); + + + String line = stringBuilder.toString(); + + // FileWriter always assumes default encoding is OK + output.write(line); + } + } finally { + output.close(); + } + } + + @Override + protected String getExtension() { + return ".war"; + } +} \ No newline at end of file diff --git a/src/dorkbox/build/ProjectJava.java b/src/dorkbox/build/ProjectJava.java new file mode 100644 index 0000000..c9bddd8 --- /dev/null +++ b/src/dorkbox/build/ProjectJava.java @@ -0,0 +1,512 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import javax.tools.Diagnostic; +import javax.tools.DiagnosticCollector; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; + +import com.esotericsoftware.wildcard.Paths; +import com.esotericsoftware.yamlbeans.YamlConfig; +import com.esotericsoftware.yamlbeans.YamlException; +import com.esotericsoftware.yamlbeans.YamlWriter; +import com.esotericsoftware.yamlbeans.scalar.ScalarSerializer; + +import dorkbox.Build; +import dorkbox.BuildOptions; +import dorkbox.build.util.ByteClassloader; +import dorkbox.build.util.JavaMemFileManager; +import dorkbox.build.util.PreJarAction; +import dorkbox.build.util.jar.JarOptions; +import dorkbox.build.util.jar.JarSigner; +import dorkbox.build.util.jar.JarUtil; +import dorkbox.license.License; +import dorkbox.license.LicenseType; +import dorkbox.util.FileUtil; +import dorkbox.util.OS; + +public class ProjectJava extends ProjectBasics { + + protected ArrayList extraArgs; + + protected Paths sourcePaths = new Paths(); + public Paths classPaths = new Paths(); + private boolean includeSource; + public List licenses = new ArrayList(); + + private transient PreJarAction preJarAction; + + private Class mainClass; + + private ByteClassloader bytesClassloader = null; + + public static ProjectJava create(String projectName) { + ProjectJava project = new ProjectJava(projectName); + deps.put(projectName, project); + + return project; + } + + public ProjectJava(String projectName) { + super(projectName); + + checksum(this.sourcePaths); + checksum(this.classPaths); + } + + public ProjectJava sourcePath(Paths sourcePaths) { + if (sourcePaths == null) { + throw new NullPointerException("Source paths cannot be null!"); + } + this.sourcePaths.add(sourcePaths); + + // ALWAYS add the source paths to be checksumed! + checksum(sourcePaths); + + return this; + } + + public ProjectJava sourcePath(String srcDir) { + if (srcDir.endsWith("src")) { + String parent = new File(srcDir).getAbsoluteFile().getParent(); + checksum(new Paths(parent)); + } + + return sourcePath(new Paths(srcDir, "./")); + } + + public ProjectJava sourcePath(String dir, String... patterns) { + return sourcePath(new Paths(dir, patterns)); + } + + public ProjectJava classPath(Paths classPaths) { + if (classPaths == null) { + throw new NullPointerException("Class paths cannot be null!"); + } + + this.classPaths.add(classPaths); + + return this; + } + + public ProjectJava classPath(String dir, String... patterns) { + return classPath(new Paths(dir, patterns)); + } + + /** extra files to include when you jar the project */ + @Override + public ProjectJava extraFiles(Paths filePaths) { + super.extraFiles(filePaths); + return this; + } + + @Override + public final ProjectJava depends(String dependsProjectName) { + super.depends(dependsProjectName); + return this; + } + + @Override + public ProjectJava output() { + String lowerCase_outputDir = this.outputDir.toLowerCase(); + this.outputDir = STAGING + File.separator + lowerCase_outputDir; + + String outputFile = lowerCase_outputDir.substring(lowerCase_outputDir.lastIndexOf("/") + 1, lowerCase_outputDir.length()); + this.outputFile = outputFile + getExtension(); + + return this; + } + + public ProjectJava addArg(String arg) { + if (this.extraArgs == null) { + this.extraArgs = new ArrayList(); + } + this.extraArgs.add(arg); + + return this; + } + + @Override + protected ProjectJava build(BuildOptions properties) throws Exception { + return build(properties, true, true, false, null); + } + + /** always does a build, ignoring checksums */ + public void forceBuild(BuildOptions options) throws Exception { + forceBuild(options, false, false); + } + + /** always does a build, ignoring checksums */ + public void forceBuild(BuildOptions options, boolean deleteCompiledOnComplete, boolean buildJar) throws Exception { + File file = FileUtil.normalize(new File(STAGING + File.separator + this.outputFile)); + if (file.exists()) { + FileUtil.delete(file); + } + + File dir = new File(this.outputDir); + if (dir.exists()) { + FileUtil.delete(dir); + } + + Build.settings.remove(this.outputDir); + build(options, deleteCompiledOnComplete, buildJar, false, null); + } + + // (and add them to the classpath) + protected ProjectJava build(BuildOptions options, boolean deleteOnComplete, boolean buildJar, + boolean signJar, String signName) throws Exception { + + Build.log().message(); + Build.log().title(" Building").message(this.name, "Output - " + this.outputDir); + + // exit early if we already built this project + if (checkAndBuildDependencies(options)) { + return this; + } + + boolean shouldBuild = !verifyChecksums(this, options); + + if (shouldBuild) { + // barf if we don't have source files! + if (this.sourcePaths.getFiles().isEmpty()) { + throw new RuntimeException("No source files specified for project: " + this.name); + } + + // make sure our dependencies are on the classpath. + if (this.dependencies != null) { + for (String dep : this.dependencies) { + ProjectBasics project = deps.get(dep); + this.classPaths.glob(STAGING, project.outputFile); + } + } + + compile(this.sourcePaths, this.classPaths, this.outputDir, options); + Build.log().message("Compile success."); + + if (buildJar) { + if (this.preJarAction != null) { + Build.log().message("Running action before Jar is created..."); + this.preJarAction.executeBeforeJarHappens(this.outputDir); + } + + JarOptions jarOptions = new JarOptions(); + jarOptions.outputFile = STAGING + File.separator + this.outputFile; + jarOptions.inputPaths = new Paths(this.outputDir); + jarOptions.extraPaths = this.extraFiles; + if (this.mainClass != null) { + jarOptions.mainClass = this.mainClass.getCanonicalName(); + jarOptions.classpath = this.classPaths; + } + if (this.includeSource) { + jarOptions.sourcePaths = this.sourcePaths; + } + if (!this.licenses.isEmpty()) { + jarOptions.licenses = this.licenses; + } + jarOptions.createDebugVersion = options.compiler.debugEnabled; + + + + JarUtil.jar(jarOptions); + + if (signJar) { + JarSigner.sign(this.outputFile, signName); + } + + // calculate the hash of all the files in the source path + saveChecksums(); + } + } else { + Build.log().message("Skipped (nothing changed)"); + } + if (shouldBuild && deleteOnComplete) { + FileUtil.delete(this.outputDir); + } + + return this; + } + + /** + * @return true if the checksums for path match the saved checksums and the jar file exists + */ + boolean verifyChecksums(ProjectBasics project, BuildOptions properties) throws IOException { + boolean sourceHashesSame = super.verifyChecksums(properties); + if (!sourceHashesSame) { + return false; + } + + // if the sources are the same, check the jar file + String fileName = project.outputDir; + fileName += project.getExtension(); + + File file = new File(fileName); + if (file.exists()) { + String jarChecksum = generateChecksum(file); + String checkContents = Build.settings.get(fileName, String.class); + + return jarChecksum != null && jarChecksum.equals(checkContents); + } else { + // output dir was removed + return false; + } + } + + /** + * Saves the checksums for a given path + */ + @Override + void saveChecksums() throws IOException { + super.saveChecksums(); + + // hash/save the jar file (if there was one) + String fileName = this.outputDir; + fileName += getExtension(); + File file = new File(fileName); + if (file.exists()) { + String fileChecksum = generateChecksum(file); + Build.settings.save(fileName, fileChecksum); + } + } + + /** + * Compiles into class files. + */ + public void compile(Paths source, Paths classpath, String outputDir, BuildOptions buildOptions) throws IOException { + // if you get messages, such as + // warning: [path] bad path element "/x/y/z/lib/fubar-all.jar": no such file or directory + // That is because that file exists in a MANIFEST.MF somewhere on the classpath! Find the jar that has that, and rip out + // the offending manifest.mf file. + // see: http://stackoverflow.com/questions/1344202/bad-path-warning-where-is-it-coming-from + + if (source.isEmpty()) { + throw new IOException("No source files found."); + } + + FileUtil.delete(outputDir); + FileUtil.mkdir(outputDir); + + ArrayList args = new ArrayList(); + if (buildOptions.compiler.enableCompilerTrace) { + // TODO: Interesting to note, that when COMPILING this with verbose, we can get a list (from the compiler) of EVERY CLASS NEEDED + // to run our application! This would be useful in "trimming" the necessary files needed by the JVM. + args.add("-verbose"); + } + + if (buildOptions.compiler.debugEnabled) { + Build.log().message("Adding debug info."); + + args.add("-g"); // Generate all debugging information, including local variables. By default, only line number and source file information is generated. + } else { + args.add("-g:none"); + } + + args.add("-d"); + args.add(outputDir); + + args.add("-encoding"); + args.add("UTF-8"); + + if (OS.getJavaVersion() > buildOptions.compiler.targetJavaVersion) { + Build.log().message("Building cross-platform target!"); + // if our runtime env. is NOT equal to our target env. + args.add("-source"); + args.add(buildOptions.getTargetVersion()); + + args.add("-target"); + args.add(buildOptions.getTargetVersion()); + + args.add("-bootclasspath"); + args.add(buildOptions.compiler.crossCompileLibrary.getCrossCompileLibraryLocation(buildOptions.compiler.targetJavaVersion)); + } + + // suppress sun proprietary warnings + if (buildOptions.compiler.suppressSunWarnings) { + args.add("-XDignore.symbol.file"); + } + + if (this.extraArgs != null) { + boolean extraArgsHaveXlint = false; + for (String arg : this.extraArgs) { + if (arg.startsWith("-Xlint")) { + extraArgsHaveXlint = true; + break; + } + } + + if (!extraArgsHaveXlint) { + args.add("-Xlint:all"); + } + + // add any extra arguments + args.addAll(this.extraArgs); + } + + + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + if (compiler == null) { + throw new RuntimeException("No compiler available. Ensure you are running from a JDK, and not a JRE."); + } + + if (classpath != null && !classpath.isEmpty()) { + args.add("-classpath"); + args.add(classpath.toString(File.pathSeparator)); + } + + // now compile the code + DiagnosticCollector diagnostics = new DiagnosticCollector(); + JavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); + + try { + Iterable javaFileObjectsFromFiles; + if (this.bytesClassloader == null) { + javaFileObjectsFromFiles = ((StandardJavaFileManager)fileManager).getJavaFileObjectsFromFiles(source.getFiles()); + + } else { + fileManager = new JavaMemFileManager((StandardJavaFileManager)fileManager, this.bytesClassloader); + ((JavaMemFileManager)fileManager).setSource(source); + javaFileObjectsFromFiles = ((JavaMemFileManager)fileManager).getSourceFiles(); + } + + compiler.getTask(null, fileManager, diagnostics, args, null, + javaFileObjectsFromFiles).call(); + } finally { + fileManager.close(); + } + + + boolean hasError = false; + for (@SuppressWarnings("rawtypes") Diagnostic diagnostic : diagnostics.getDiagnostics()) { + if (diagnostic.getKind() == javax.tools.Diagnostic.Kind.ERROR) { + hasError = true; + break; + } + } + if (hasError) { + StringBuilder buffer = new StringBuilder(1024); + for (@SuppressWarnings("rawtypes") Diagnostic diagnostic : diagnostics.getDiagnostics()) { + if (buffer.length() > 0) { + buffer.append("\n"); + } + buffer.append("Line ").append(diagnostic.getLineNumber()).append(": "); + buffer.append(diagnostic.getMessage(null)); + } + throw new RuntimeException("Compilation errors:\n" + buffer); + } + + compiler = null; + System.gc(); + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + } + } + + @Override + protected String getExtension() { + return ".jar"; + } + + public static interface OnJarEntryAction { + boolean canHandle(String fileName); + int onEntry(String fileName, ByteArrayInputStream inputStream, OutputStream output) throws Exception; + } + + public ProjectJava includeSourceInJar() { + this.includeSource = true; + return this; + } + + public ProjectJava license(License license) { + this.licenses.add(license); + return this; + } + + public ProjectJava license(List licenses) { + this.licenses.addAll(licenses); + return this; + } + + /** Actions that might need to take place before the project is jar'd */ + public ProjectJava preJarAction(PreJarAction preJarAction) { + this.preJarAction = preJarAction; + return this; + } + + /** + * Specify the main class. + */ + public ProjectJava mainClass(Class clazz) { + this.mainClass = clazz; + return this; + } + + /** + * Take all of the parameters of this project, and convert it to a text file. + * @throws IOException + */ + public void toBuildFile() throws IOException { + YamlWriter writer = new YamlWriter(new FileWriter("build.oak")); + YamlConfig config = writer.getConfig(); + + config.writeConfig.setWriteRootTags(false); + + config.setPropertyElementType(ProjectJava.class, "licenses", License.class); + config.setPrivateFields(true); + + config.readConfig.setConstructorParameters(License.class, new Class[]{String.class, LicenseType.class}, new String[] {"licenseName", "licenseType"}); + config.readConfig.setConstructorParameters(ProjectJava.class, new Class[]{String.class}, new String[] {"projectName"}); + + config.setScalarSerializer(Paths.class, new ScalarSerializer() { + @Override + public Paths read (String value) throws YamlException { + String[] split = value.split(File.pathSeparator); + Paths paths = new Paths(); + for (String s : split) { + paths.addFile(s); + } + return paths; + } + + @Override + public String write (Paths paths) throws YamlException { + return paths.toString(File.pathSeparator); + } + }); + + writer.write(this); + writer.close(); + } + + /** + * The specified byte loading classloader to save the compiled class bytes into, + */ + public ProjectJava compilerClassloader(ByteClassloader bytesClassloader) { + this.bytesClassloader = bytesClassloader; + return this; + } +} \ No newline at end of file diff --git a/src/dorkbox/build/SimpleArgs.java b/src/dorkbox/build/SimpleArgs.java new file mode 100644 index 0000000..c4eb0b6 --- /dev/null +++ b/src/dorkbox/build/SimpleArgs.java @@ -0,0 +1,77 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; + +public class SimpleArgs { + + private final Set argsAsSet; + private final String[] args; + private int lastIndex = 0; + + public SimpleArgs(String[] args) { + this.args = args; + + this.argsAsSet = new HashSet(args.length); + for (int i = 0; i < args.length; i++) { + String arg = args[i]; + String lowerCase = arg.toLowerCase(Locale.US); + this.argsAsSet.add(lowerCase); + } + } + + public boolean has(String argToCheck) { + String argToCheck2 = argToCheck.toLowerCase(Locale.US); + + return this.argsAsSet.contains(argToCheck2); + } + + public String get(String argToCheck) { + String argToCheck2 = argToCheck.toLowerCase(Locale.US); + + String[] args2 = this.args; + for (int i = 0; i < args2.length; i++) { + String arg = args2[i]; + String lowerCase = arg.toLowerCase(Locale.US); + if (lowerCase.equals(argToCheck2)) { + this.lastIndex = i; + return arg; + } + } + return null; + } + + public String getNext() { + return this.args[this.lastIndex++]; + } + + @Override + public String toString() { + return Arrays.toString(this.args); + } + + public String getMode() { + return this.args[0]; + } + + public String get(int i) { + return this.args[i]; + } +} diff --git a/src/dorkbox/build/util/BuildLog.java b/src/dorkbox/build/util/BuildLog.java new file mode 100644 index 0000000..fa77af8 --- /dev/null +++ b/src/dorkbox/build/util/BuildLog.java @@ -0,0 +1,119 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build.util; + +import java.io.PrintStream; + +import dorkbox.util.OS; + +public class BuildLog { + private static boolean suppressOutput = false; + + public static void start() { + suppressOutput = false; + } + + public static void stop() { + suppressOutput = true; + } + + private PrintStream printer; + private String title; + + public BuildLog() { + this.printer = System.err; + } + + public BuildLog(PrintStream printer) { + this.printer = printer; + } + + public BuildLog title(String title) { + this.title = title; + return this; + } + + public void message() { + message((String) null); + } + + public void message(String... message) { + if (suppressOutput) { + return; + } + + String spacer = " "; + + if (this.title == null) { + this.title = spacer; + } else { + int length = this.title.length(); + int padding = 16 - length; + if (padding > 0) { + StringBuilder msg = new StringBuilder(16); + msg.append(this.title); + for (int i = 0; i < padding; i++) { + msg.append(" "); + } + this.title = msg.toString(); + } + } + + if (message != null && message.length > 0 && message[0] != null) { + StringBuilder msg = new StringBuilder(1024); + int start = 0; + if (message.length > 1) { + msg.append(this.title).append(": ").append(message[start++]); + } + + String newLineToken = OS.LINE_SEPARATOR; + for (int i = start; i < message.length; i++) { + String m = message[i]; + if (msg.length() > 0) { + msg.append(newLineToken); + } + msg.append(spacer).append(": ").append(" ").append(m); + } + + this.printer.println(msg.toString()); + } else { + StringBuilder msg = new StringBuilder(1024); + msg.append(this.title).append(":"); + this.printer.println(msg.toString()); + } + } + + public void print(String message) { + if (suppressOutput) { + return; + } + this.printer.print(message); + } + + public void println(String message) { + if (suppressOutput) { + return; + } + this.printer.println(message); + } + + public void write(int b) { + if (suppressOutput) { + return; + } + this.printer.write(b); + } +} diff --git a/src/dorkbox/build/util/BuildParser.java b/src/dorkbox/build/util/BuildParser.java new file mode 100644 index 0000000..7204842 --- /dev/null +++ b/src/dorkbox/build/util/BuildParser.java @@ -0,0 +1,124 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build.util; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; + +import com.esotericsoftware.wildcard.Paths; +import com.esotericsoftware.yamlbeans.YamlException; +import com.esotericsoftware.yamlbeans.YamlReader; +import com.esotericsoftware.yamlbeans.parser.Parser.ParserException; +import com.esotericsoftware.yamlbeans.tokenizer.Tokenizer.TokenizerException; + +import dorkbox.build.SimpleArgs; +import dorkbox.util.FileUtil; +import dorkbox.util.Sys; + +public class BuildParser { + public static String fileName = "build.oak"; + + @SuppressWarnings("unchecked") + public static HashMap parse(SimpleArgs args) { + String fileName = BuildParser.fileName; + if (args.has("-file") || args.has("-f")) { + fileName = args.getNext(); + } + + final File file = new File(FileUtil.normalizeAsFile(fileName)); + + + HashMap data = null; + + Reader fileReader = null; + try { + fileReader = new FileReader(file); + YamlReader yamlReader = new YamlReader(fileReader) { + @SuppressWarnings("rawtypes") + @Override + protected Object readValue (Class type, Class elementType, Class defaultType) throws YamlException, ParserException, + TokenizerException { + + Object value = super.readValue(type, elementType, defaultType); + // replace $dir$ with the parent dir, for use in parameters + if (value instanceof String) { + value = ((String)value).replaceAll("\\$dir\\$", file.getParent()); + } + + return value; + } + }; + + try { + data = yamlReader.read(HashMap.class); + yamlReader.close(); + if (data == null) { + return new HashMap(0); + } else { + return data; + } + } catch (YamlException ex) { + throw new IOException("Error reading YAML file: " + file, ex); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + Sys.close(fileReader); + } + + return new HashMap(0); + } + + public static Paths getPathsFromMap(HashMap map, String key) throws IOException { + Paths sourcePaths = new Paths(); + if (map.containsKey(key)) { + Object object = map.get(key); + if (object instanceof String) { + sourcePaths.glob(".", (String) object); + } else if (object instanceof Collection) { + @SuppressWarnings("rawtypes") + Collection col = (Collection) object; + for (Object c : col) { + // we use the full path info + Paths paths = new Paths(".", (String) c); + if (paths.isEmpty()) { + throw new IOException("Location does not exist: " + c); + } else { + Iterator iterator = paths.iterator(); + while (iterator.hasNext()) { + String next = FileUtil.normalizeAsFile(iterator.next()); + File file = new File(next); + if (!file.canRead()) { + throw new IOException("Location does not exist: " + next); + } + + sourcePaths.add(file.getParent(), file.getName()); + } + } + } + } else { + throw new IOException("Unknown source type: " + object.getClass().getSimpleName()); + } + } + + return sourcePaths; + } +} diff --git a/src/dorkbox/build/util/ByteClassloader.java b/src/dorkbox/build/util/ByteClassloader.java new file mode 100644 index 0000000..222285a --- /dev/null +++ b/src/dorkbox/build/util/ByteClassloader.java @@ -0,0 +1,90 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build.util; + +import java.security.ProtectionDomain; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +public class ByteClassloader extends java.lang.ClassLoader { + + private Map bytes = new ConcurrentHashMap(); + private Map> classes = new ConcurrentHashMap>(); + + private final ProtectionDomain domain; + + public ByteClassloader(ClassLoader classloader) { + super(classloader); + + this.domain = this.getClass().getProtectionDomain(); + } + + public final void saveBytes(String name, byte[] bytes) { + // this defines our class, and saves it in our cache -- so that findClass() will work + if (this.bytes != null) { + this.bytes.put(name, bytes); + } + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + return loadClass(name, false); + } + + // check OURSELVES first, then check our parent. + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (this.bytes != null) { + byte[] classBytes = this.bytes.get(name); + + if (classBytes != null) { + // have to make sure that the package is properly setup. + int i = -1; + String packageName = name; + while ((i = name.indexOf('.', i)) > 0) { + packageName = name.substring(0, i++); + if (getPackage(packageName) == null) { + definePackage(packageName, null, null, null, null, null, null, null); + } + } + + Class clazz = defineClass(name, classBytes, 0, classBytes.length, this.domain); + + if (resolve) { + resolveClass(clazz); + } + + // cache our classes that we create + this.classes.put(name, clazz); + + return clazz; + } + } + + Class c = this.classes.get(name); + if (c != null) { + return c; + } + + return getParent().loadClass(name); + } + + Iterator> getBytesIterator() { + return this.bytes.entrySet().iterator(); + } +} \ No newline at end of file diff --git a/src/dorkbox/build/util/ClassByteIterator.java b/src/dorkbox/build/util/ClassByteIterator.java new file mode 100644 index 0000000..4258a06 --- /dev/null +++ b/src/dorkbox/build/util/ClassByteIterator.java @@ -0,0 +1,106 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build.util; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; +import java.util.Map.Entry; + +import dorkbox.util.annotation.ClassFileIterator; + +/** + * {@code ClassBytesIterator} is used to iterate over all Java ClassFile files available within + * a specific context. + *

+ * For every Java ClassFile ({@code .class}) an {@link InputStream} is returned. + */ +public class ClassByteIterator extends ClassFileIterator { + + + private final Iterator> iterator; + private String fullPath; + + /** + * Create a new {@code ClassFileIterator} returning all Java ClassFile files available + * from the specified files and/or directories, including sub directories. + *

+ * If the (optional) package filter is defined, only class files staring with one of the + * defined package names are returned. + * NOTE: package names must be defined in the native format (using '/' instead of '.'). + */ + public ClassByteIterator(final ByteClassloader classloader, final String[] pkgNameFilter) { + super(pkgNameFilter); + this.iterator = classloader.getBytesIterator(); + } + + /** + * Return the name of the Java ClassFile returned from the last call to {@link #next()}. + * The name is the PACKAGE name of a file + */ + @Override + public String getName() { + return this.fullPath; + } + + /** + * Return {@code true} if the current {@link InputStream} is reading from a plain + * {@link File}. + * Return {@code false} if the current {@link InputStream} is reading from a + * ZIP File Entry. + */ + @Override + public boolean isFile() { + return true; + } + + /** + * Return the next Java ClassFile as an {@code InputStream}. + *

+ * NOTICE: Client code MUST close the returned {@code InputStream}! + */ + @Override + public InputStream next(final FilenameFilter filter) throws IOException { + while (this.iterator.hasNext()) { + Entry next = this.iterator.next(); + this.fullPath = next.getKey(); + + File dir = null; + String name = null; + + int lastIndexOf = this.fullPath.lastIndexOf("."); + if (filter != null) { + + if (lastIndexOf > 0) { + dir = new File(this.fullPath.substring(0, lastIndexOf)); + name = this.fullPath.substring(lastIndexOf+1); + } + } else { + name = this.fullPath; + } + + if (filter == null || filter.accept(dir, name)) { + return new ByteArrayInputStream(next.getValue()); + } + // else just ignore + } + + return null; + } +} diff --git a/src/dorkbox/build/util/JavaMemFileManager.java b/src/dorkbox/build/util/JavaMemFileManager.java new file mode 100644 index 0000000..119a5f4 --- /dev/null +++ b/src/dorkbox/build/util/JavaMemFileManager.java @@ -0,0 +1,99 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.util.HashMap; +import java.util.Map.Entry; + +import javax.tools.FileObject; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.StandardLocation; + +import com.esotericsoftware.wildcard.Paths; + +public class JavaMemFileManager extends ForwardingJavaFileManager { + + static class ClassMemFileObject extends SimpleJavaFileObject { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + ClassMemFileObject(String className) { + super(URI.create("mem:///" + className + Kind.CLASS.extension), Kind.CLASS); + } + + byte[] getBytes() { + return this.os.toByteArray(); + } + + @Override + public OutputStream openOutputStream() throws IOException { + return this.os; + } + } + + + + private HashMap classes = new HashMap(); + private Paths source; + private ByteClassloader bytesClassloader; + + public JavaMemFileManager(StandardJavaFileManager standardFileManager, ByteClassloader bytesClassloader) { + super(standardFileManager); + this.bytesClassloader = bytesClassloader; + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) + throws IOException { + + if (StandardLocation.CLASS_OUTPUT == location && JavaFileObject.Kind.CLASS == kind) { + ClassMemFileObject clazz = new ClassMemFileObject(className); + this.classes.put(className, clazz); + return clazz; + } else { + return super.getJavaFileForOutput(location, className, kind, sibling); + } + } + + public void setSource(Paths source) { + this.source = source; + } + + public Iterable getSourceFiles() { + return super.fileManager.getJavaFileObjectsFromFiles(this.source.getFiles()); + } + + @Override + public void close() throws IOException { + super.close(); + + // and save all of our bytes into our classloader + for (Entry entry : this.classes.entrySet()) { + String key = entry.getKey(); + ClassMemFileObject value = entry.getValue(); + + this.bytesClassloader.saveBytes(key, value.getBytes()); + } + + this.classes.clear(); + } +} diff --git a/src/dorkbox/build/util/PreJarAction.java b/src/dorkbox/build/util/PreJarAction.java new file mode 100644 index 0000000..0210f01 --- /dev/null +++ b/src/dorkbox/build/util/PreJarAction.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build.util; + +public interface PreJarAction { + public void executeBeforeJarHappens(String outputDir) throws Exception; +} diff --git a/src/dorkbox/build/util/jar/EncryptInterface.java b/src/dorkbox/build/util/jar/EncryptInterface.java new file mode 100644 index 0000000..8366e4a --- /dev/null +++ b/src/dorkbox/build/util/jar/EncryptInterface.java @@ -0,0 +1,24 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build.util.jar; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; + +public interface EncryptInterface { + /** Specify how data is to be encrypted, if specified */ + ByteArrayOutputStream encrypt(InputStream inputStream, int length); +} diff --git a/src/dorkbox/build/util/jar/ExtraDataInterface.java b/src/dorkbox/build/util/jar/ExtraDataInterface.java new file mode 100644 index 0000000..3871026 --- /dev/null +++ b/src/dorkbox/build/util/jar/ExtraDataInterface.java @@ -0,0 +1,23 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build.util.jar; + +import java.util.zip.ZipEntry; + +public interface ExtraDataInterface { + + void write(ZipEntry entry, PackTask task); +} diff --git a/src/dorkbox/build/util/jar/JarOptions.java b/src/dorkbox/build/util/jar/JarOptions.java new file mode 100644 index 0000000..9296c83 --- /dev/null +++ b/src/dorkbox/build/util/jar/JarOptions.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build.util.jar; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.esotericsoftware.wildcard.Paths; + +import dorkbox.license.License; + +public class JarOptions { + public String outputFile = null; + public Paths inputPaths = null; + + public String mainClass = null; + public Map otherManifestAttributes = new LinkedHashMap(); + + public Paths classpath = null; + public boolean createDebugVersion = false; + + /** target dir + paths for extra files **/ + public Paths extraPaths; + + /** Include the source code if requested **/ + public Paths sourcePaths; + + /** Include the various licenses if possible **/ + public List licenses; +} diff --git a/src/dorkbox/build/util/jar/JarSignatureUtil.java b/src/dorkbox/build/util/jar/JarSignatureUtil.java new file mode 100644 index 0000000..c442ee0 --- /dev/null +++ b/src/dorkbox/build/util/jar/JarSignatureUtil.java @@ -0,0 +1,319 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build.util.jar; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.MessageDigest; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Map; +import java.util.Map.Entry; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.cms.ContentInfo; +import org.bouncycastle.asn1.cms.SignedData; + +import dorkbox.util.Base64Fast; +import dorkbox.util.OS; +import dorkbox.util.Sys; + +public class JarSignatureUtil { + /** + * a small helper function that will convert a manifest into an array of + * bytes + */ + public static final byte[] serialiseManifest(Manifest manifest) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + manifest.write(baos); + baos.flush(); + baos.close(); + return baos.toByteArray(); + + } + + /** + * update the attributes in the manifest to have the appropriate message + * digests. we store the new entries into the entries Map and return it (we + * do not compute the digests for those entries in the META-INF directory) + */ + public static final Map updateManifestHashes(Manifest manifest, JarFile jarFile, MessageDigest messageDigest) throws IOException { + Map entries = manifest.getEntries(); + Enumeration jarElements = jarFile.entries(); + String digestName = messageDigest.getAlgorithm() + "-Digest"; + + while (jarElements.hasMoreElements()) { + JarEntry jarEntry = jarElements.nextElement(); + String name = jarEntry.getName(); + + if (name.startsWith(JarUtil.metaInfName)) { + continue; + } else if (!jarEntry.isDirectory()) { + // store away the digest into a new Attribute + // because we don't already have an attribute list + // for this entry. we do not store attributes for + // directories within the JAR + Attributes attributes = new Attributes(); + // attributes.putValue("Name", name); NOT NECESSARY! + InputStream inputStream = jarFile.getInputStream(jarEntry); + attributes.putValue(digestName, JarUtil.updateDigest(inputStream, messageDigest)); + Sys.close(inputStream); + + entries.put(name, attributes); + } + } + + return entries; + } + + /** + * @return null if there is a problem with the certificate loading process. + */ + public static final String extractSignatureHashFromSignatureBlock(byte[] signatureBlock) { + ASN1InputStream sigStream = null; + try { + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + + InputStream signatureIn = new ByteArrayInputStream(signatureBlock); + sigStream = new ASN1InputStream(signatureIn); + ASN1Primitive signatureASN = sigStream.readObject(); + ASN1Sequence seq = ASN1Sequence.getInstance(signatureASN); + ASN1TaggedObject tagged = (ASN1TaggedObject) seq.getObjectAt(1); + + // Extract certificates + SignedData newSignedData = SignedData.getInstance(tagged.getObject()); + + @SuppressWarnings("rawtypes") + Enumeration newSigOjects = newSignedData.getCertificates().getObjects(); + Object newSigElement = newSigOjects.nextElement(); + + if (newSigElement instanceof DERSequence) { + DERSequence newSigDERElement = (DERSequence) newSigElement; + InputStream newSigIn = new ByteArrayInputStream(newSigDERElement.getEncoded()); + Certificate newSigCertificate = certFactory.generateCertificate(newSigIn); + + // certificate bytes + byte[] newSigCertificateBytes = newSigCertificate.getEncoded(); + String encodeToString = Base64Fast.encodeToString(newSigCertificateBytes, false); + return encodeToString; + } + } catch (IOException e) {} catch (CertificateException e) {} + finally { + Sys.close(sigStream); + } + return null; + } + + /** + * Verify that the two certificates MATCH from within a signature block (ie, + * XXXXX.DSA in the META-INF directory). + * + * @return true if the two certificates are the same. false otherwise. + */ + public static final boolean compareCertificates(byte[] newSignatureContainerBytes, byte[] oldSignatureContainerBytes) { + ASN1InputStream newSigStream = null; + ASN1InputStream oldSigStream = null; + try { + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + + InputStream newSignatureIn = new ByteArrayInputStream(newSignatureContainerBytes); + newSigStream = new ASN1InputStream(newSignatureIn); + ASN1Primitive newSigASNPrim = newSigStream.readObject(); + ContentInfo newSigContent = ContentInfo.getInstance(newSigASNPrim); + + InputStream oldSignatureIn = new ByteArrayInputStream(oldSignatureContainerBytes); + oldSigStream = new ASN1InputStream(oldSignatureIn); + ASN1Primitive oldSigASNPrim = oldSigStream.readObject(); + ContentInfo oldSigContent = ContentInfo.getInstance(oldSigASNPrim); + + // Extract certificates + SignedData newSignedData = SignedData.getInstance(newSigContent.getContent()); + @SuppressWarnings("rawtypes") + Enumeration newSigOjects = newSignedData.getCertificates().getObjects(); + + SignedData oldSignedData = SignedData.getInstance(oldSigContent.getContent()); + @SuppressWarnings("rawtypes") + Enumeration oldSigOjects = oldSignedData.getCertificates().getObjects(); + + Object newSigElement = newSigOjects.nextElement(); + Object oldSigElement = oldSigOjects.nextElement(); + + if (newSigElement instanceof DERSequence && oldSigElement instanceof DERSequence) { + DERSequence newSigDERElement = (DERSequence) newSigElement; + InputStream newSigIn = new ByteArrayInputStream(newSigDERElement.getEncoded()); + Certificate newSigCertificate = certFactory.generateCertificate(newSigIn); + + DERSequence oldSigDERElement = (DERSequence) oldSigElement; + InputStream oldSigIn = new ByteArrayInputStream(oldSigDERElement.getEncoded()); + Certificate oldSigCertificate = certFactory.generateCertificate(oldSigIn); + + // certificate bytes + byte[] newSigCertificateBytes = newSigCertificate.getEncoded(); + byte[] oldSigCertificateBytes = oldSigCertificate.getEncoded(); + + return Arrays.equals(newSigCertificateBytes, oldSigCertificateBytes); + } + } catch (IOException e) {} catch (CertificateException e) {} + finally { + Sys.close(newSigStream); + Sys.close(oldSigStream); + } + + return false; + } + + /** + * Creates a NEW signature file manifest based on the supplied message + * digest and manifest. + */ + @SuppressWarnings("deprecation") + public static final Manifest createSignatureFileManifest(MessageDigest messageDigest, Manifest manifest, byte[] manifestBytes) throws IOException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { + + String messageDigestTitle = messageDigest.getAlgorithm() + "-Digest"; + + // create the new manifest signature (.SF) + Manifest signatureManifest = new Manifest(); + + Attributes signatureMainAttributes = signatureManifest.getMainAttributes(); + signatureMainAttributes.putValue(Attributes.Name.SIGNATURE_VERSION.toString(), "1.0"); + + String version = System.getProperty("java.version"); + String javaVendor = System.getProperty("java.vendor"); + signatureMainAttributes.putValue("Created-By", version + " (" + javaVendor + ")"); + + // SIGN THE WHOLE MANIFEST + messageDigest.reset(); + messageDigest.update(manifestBytes, 0, manifestBytes.length); + + /* + * Do not insert a default newline at the end of the output line, as + * java.util.jar does its own line management (see + * Manifest.make72Safe()). Inserting additional new lines will cause + * line-wrapping problems. + */ + String entireManifestHash = Base64Fast.encodeToString(messageDigest.digest(), false); + signatureMainAttributes.putValue(messageDigestTitle + "-Manifest", entireManifestHash); + // System.err.println("ENCODED ALL : " + entireManifestHash); + + // ////////////////////////// + // Instead of reverse engineering the BYTES, we'll just read the + // manifest again and encode on the fly. + // ////////////////////////// + ByteArrayOutputStream manifestStream = new ByteArrayOutputStream(); + + Method writeMainMethod = Attributes.class.getDeclaredMethod("writeMain", + new Class[] {DataOutputStream.class}); + writeMainMethod.setAccessible(true); + + // MAIN ATTRIBUTES + DataOutputStream dataOutputStream = new DataOutputStream(manifestStream); + // Write out the main attributes for the manifest + writeMainMethod.invoke(manifest.getMainAttributes(), dataOutputStream); + dataOutputStream.flush(); + manifestStream.flush(); + + // HASH the contents of the main attributes (WHICH ARE ALWAYS FIRST!) + byte[] mainAttributesByteArray = manifestStream.toByteArray(); + messageDigest.reset(); + messageDigest.update(mainAttributesByteArray, 0, mainAttributesByteArray.length); + + /* + * Do not insert a default newline at the end of the output line, as + * java.util.jar does its own line management (see + * Manifest.make72Safe()). Inserting additional new lines will cause + * line-wrapping problems. + */ + String mainAttribsManifestHash = Base64Fast.encodeToString(messageDigest.digest(), false); + if (mainAttribsManifestHash != null) { + signatureMainAttributes.putValue(messageDigestTitle + "-Manifest-Main-Attributes", mainAttribsManifestHash); + // System.err.println("ENCODED main: " + mainAttribsManifestHash); + } else { + throw new RuntimeException("Unable to create manifest-main-attribute signature"); + } + + // PER-ENTRY ATTRIBUTES + Method writeMethod = Attributes.class.getDeclaredMethod("write", new Class[] {DataOutputStream.class}); + writeMethod.setAccessible(true); + + Method make72Method = Manifest.class.getDeclaredMethod("make72Safe", new Class[] {StringBuffer.class}); + make72Method.setAccessible(true); + + Map entries = manifest.getEntries(); + Map signatureEntries = signatureManifest.getEntries(); + + for (Entry e : entries.entrySet()) { + manifestStream.reset(); + dataOutputStream = new DataOutputStream(manifestStream); + + // has to be string buffer. + StringBuffer buffer = new StringBuffer("Name: "); + String entryName = e.getKey(); + + if (entryName != null) { + byte[] vb = entryName.getBytes(OS.UTF_8); // by doing this, the following new string + // will be safe (UTF-8) despite warnings + entryName = new String(vb, 0, 0, vb.length); + } + buffer.append(entryName); + buffer.append("\r\n"); // must be this because of stupid windows... + make72Method.invoke(null, buffer); + dataOutputStream.writeBytes(buffer.toString()); + + // Write out the attributes for the manifest + writeMethod.invoke(e.getValue(), dataOutputStream); + dataOutputStream.flush(); + manifestStream.flush(); + + // HASH the contents of the attributes + byte[] attributesByteArray = manifestStream.toByteArray(); + messageDigest.reset(); + messageDigest.update(attributesByteArray, 0, attributesByteArray.length); + + /* + * Do not insert a default newline at the end of the output line, as + * java.util.jar does its own line management (see + * Manifest.make72Safe()). Inserting additional new lines will cause + * line-wrapping problems. + */ + String entryHash = Base64Fast.encodeToString(messageDigest.digest(), false); + + Attributes attribute = new Attributes(); + attribute.putValue(messageDigestTitle, entryHash); + signatureEntries.put(entryName, attribute); + + // System.err.println("ENCODED " + entryName + " : " + entryHash); + } + + return signatureManifest; + } + +} diff --git a/src/dorkbox/build/util/jar/JarSigner.java b/src/dorkbox/build/util/jar/JarSigner.java new file mode 100644 index 0000000..ab50bc9 --- /dev/null +++ b/src/dorkbox/build/util/jar/JarSigner.java @@ -0,0 +1,316 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build.util.jar; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.math.BigInteger; +import java.security.GeneralSecurityException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Calendar; +import java.util.Date; +import java.util.Map; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.DSAParameter; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.params.DSAKeyParameters; +import org.bouncycastle.crypto.params.DSAParameters; +import org.bouncycastle.crypto.params.DSAPrivateKeyParameters; +import org.bouncycastle.crypto.params.DSAPublicKeyParameters; +import org.bouncycastle.crypto.util.PrivateKeyFactory; +import org.bouncycastle.crypto.util.PublicKeyFactory; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import dorkbox.Build; +import dorkbox.util.Base64Fast; +import dorkbox.util.Sys; +import dorkbox.util.crypto.Crypto; +import dorkbox.util.crypto.CryptoX509; + +public class JarSigner { + + static { + BouncyCastleProvider provider = new BouncyCastleProvider(); + Security.addProvider(provider); + } + + public static File sign(String jarName, String name) { + + Build.log().message(); + Build.log().title("Signing JAR").message(jarName, name.toUpperCase()); + + if (jarName == null) { + throw new IllegalArgumentException("jarName cannot be null."); + } + + try { + File jarFile = new File(jarName); + ByteArrayOutputStream signJarFile; + + if (jarFile.isFile() && jarFile.canRead()) { + signJarFile = signJar(jarFile, name); + } else { + throw new RuntimeException("Unable to read file: " + jarFile.getCanonicalPath()); + } + + // write out the file + OutputStream outputStream = new FileOutputStream(jarFile); + signJarFile.writeTo(outputStream); + Sys.close(outputStream); + + return new File(jarName); + } + catch (Throwable ex) { + throw new RuntimeException("Unable to sign jar file! " + ex.getMessage()); + } + } + + /** + * the actual JAR signing method + * @param createDebugVersion + */ + private static ByteArrayOutputStream signJar(File jarFile, String name) + throws IOException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, GeneralSecurityException { + + // proper "jar signing" does not allow for ECC signatures to be used. RSA/DSA and that's it. + // so this "self signed" cert is just that. wimpy. + // the magic is in the uber-strong ECC key that is used internally, and also has AES keys mixed in. + DSAKeyParameters[] wimpyKeys = getWimpyKeys(); + DSAPublicKeyParameters wimpyPublicKey = (DSAPublicKeyParameters) wimpyKeys[0]; + DSAPrivateKeyParameters wimpyPrivateKey = (DSAPrivateKeyParameters) wimpyKeys[1]; + + + // create the certificate + Calendar expiry = Calendar.getInstance(); + expiry.add(Calendar.YEAR, 2); + + Date startDate = new Date(); // time from which certificate is valid + Date expiryDate = expiry.getTime(); // time after which certificate is not valid + BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis()); // serial number for certificate + + + + X509CertificateHolder wimpyX509CertificateHolder = CryptoX509.DSA.createCertHolder( + startDate, expiryDate, + new X500Name("ST=Lunar Base Alpha, O=Dorkbox, CN=Dorkbox Server, emailaddress=admin@dorkbox.com"), + new X500Name("ST=Earth, O=Dorkbox, CN=Dorkbox Client, emailaddress=admin@dorkbox.com"), + serialNumber, wimpyPrivateKey, wimpyPublicKey); + + JarFile jar = new JarFile(jarFile.getCanonicalPath()); + + // UNFORTUNATELY, with java6, we CANNOT do anything higher. As such, a CUSTOM signing tool will be developed, + // which the launcher will verify on it's own. + // FORTUNATELY, this is will produce the exact same output as if using the command line. + String digestName = CryptoX509.Util.getDigestNameFromCert(wimpyX509CertificateHolder); + MessageDigest messageDigest = MessageDigest.getInstance(digestName); + + // get the manifest out of the jar. + Manifest manifest = JarUtil.getManifestFile(jar); + + // it ONLY exists if it's an "executable" jar + if (manifest == null) { + manifest = new Manifest(); + + // have to add basic entries. + Attributes mainAttributes = manifest.getMainAttributes(); + mainAttributes.putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0"); + } else { + // clear out all entries in the manifest + Map entries = manifest.getEntries(); + if (entries.size() > 0) { + entries.clear(); + } + } + + + // create the message digest and start updating the + // the attributes in the manifest to contain the SHA digests + JarSignatureUtil.updateManifestHashes(manifest, jar, messageDigest); + + byte manifestBytes[] = JarSignatureUtil.serialiseManifest(manifest); + + + // create a NEW signature file manifest based on the supplied message digest and manifest. + Manifest signatureFileManifest = JarSignatureUtil.createSignatureFileManifest(messageDigest, manifest, manifestBytes); + byte signatureFileManifestBytes[] = JarSignatureUtil.serialiseManifest(signatureFileManifest); + + + byte signatureBlockBytes[] = CryptoX509.createSignature(signatureFileManifestBytes, + wimpyX509CertificateHolder, wimpyPrivateKey); + + ByteArrayOutputStream byteArrayOutputStream = JarUtil.createNewJar(jar, + name, + manifestBytes, + signatureFileManifestBytes, + signatureBlockBytes); + + // close the JAR file that we have been using + jar.close(); + return byteArrayOutputStream; + } + + + public static DSAKeyParameters[] getWimpyKeys() throws IOException, FileNotFoundException { + String wimpyKeyName = "wimpyCert.key"; + + DSAPrivateKeyParameters wimpyPrivateKey = null; + DSAPublicKeyParameters wimpyPublicKey = null; + + File wimpyKeyRawFile = new File(wimpyKeyName); + + // do we need to create the (wimpy) certificate keys? + if (!wimpyKeyRawFile.canRead()) { + // using DSA, since that is compatible with ALL java versions + @SuppressWarnings("deprecation") + AsymmetricCipherKeyPair generateKeyPair = Crypto.DSA.generateKeyPair(new SecureRandom(), 8192); + wimpyPrivateKey = (DSAPrivateKeyParameters) generateKeyPair.getPrivate(); + wimpyPublicKey = (DSAPublicKeyParameters) generateKeyPair.getPublic(); + + writeDsaKeysToFile(wimpyPrivateKey, wimpyPublicKey, wimpyKeyRawFile); + } else { + FileInputStream inputStream = new FileInputStream(wimpyKeyRawFile); + long fileSize = inputStream.getChannel().size(); + + // check file size. + if (fileSize > Integer.MAX_VALUE-1) { + System.err.println("Corrupt wimpyKeyFile! " + wimpyKeyRawFile.getAbsolutePath() + " Creating a new one."); + + // using DSA, since that is compatible with ALL java versions + @SuppressWarnings("deprecation") + AsymmetricCipherKeyPair generateKeyPair = Crypto.DSA.generateKeyPair(new SecureRandom(), 8192); + wimpyPrivateKey = (DSAPrivateKeyParameters) generateKeyPair.getPrivate(); + wimpyPublicKey = (DSAPublicKeyParameters) generateKeyPair.getPublic(); + + writeDsaKeysToFile(wimpyPrivateKey, wimpyPublicKey, wimpyKeyRawFile); + } else { + // read in the entire file as bytes. + int fileSizeAsInt = (int) fileSize; + + byte[] inputBytes = new byte[fileSizeAsInt]; + inputStream.read(inputBytes, 0, fileSizeAsInt); + Sys.close(inputStream); + + // read public key length + int wimpyPublicKeyLength = (inputBytes[fileSizeAsInt - 4] & 0xff) << 24 | + (inputBytes[fileSizeAsInt - 3] & 0xff) << 16 | + (inputBytes[fileSizeAsInt - 2] & 0xff) << 8 | + (inputBytes[fileSizeAsInt - 1] & 0xff) << 0; + + + byte[] publicKeyBytes = new byte[wimpyPublicKeyLength]; + byte[] privateKeyBytes = new byte[fileSizeAsInt - 4 - wimpyPublicKeyLength]; + + System.arraycopy(inputBytes, 0, publicKeyBytes, 0, publicKeyBytes.length); + System.arraycopy(inputBytes, publicKeyBytes.length, privateKeyBytes, 0, privateKeyBytes.length); + + displayByteHash(publicKeyBytes); + + wimpyPublicKey = (DSAPublicKeyParameters) PublicKeyFactory.createKey(publicKeyBytes); + wimpyPrivateKey = (DSAPrivateKeyParameters) PrivateKeyFactory.createKey(privateKeyBytes); + } + } + + return new DSAKeyParameters[] {wimpyPublicKey, wimpyPrivateKey}; + } + + private static void writeDsaKeysToFile(DSAPrivateKeyParameters wimpyPrivateKey, DSAPublicKeyParameters wimpyPublicKey, File wimpyKeyRawFile) + throws IOException, FileNotFoundException { + + DSAParameters parameters = wimpyPublicKey.getParameters(); // has to convert to DSAParameter so encoding works. + byte[] publicKeyBytes = new SubjectPublicKeyInfo( + new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, + new DSAParameter(parameters.getP(), parameters.getQ(), parameters.getG()).toASN1Primitive()), + new ASN1Integer(wimpyPublicKey.getY())).getEncoded(); + // SAME AS: + // Certificate[] certificates = Launcher.class.getProtectionDomain().getCodeSource().getCertificates(); + // if (certificates.length != 1) { + // // WHOOPS! + // Exit.FailedSecurity("Incorrect certificate length!"); + // } + // + // Certificate certificate = certificates[0]; + // PublicKey publicKey = certificate.getPublicKey(); + // byte[] publicKeyBytes = publicKey.getEncoded(); + // + // digest.reset(); + // digest.update(publicKeyBytes, 0, publicKeyBytes.length); + // hashPublicKeyBytes = digest.digest(); + + + parameters = wimpyPrivateKey.getParameters(); + byte[] privateKeyBytes = new PrivateKeyInfo( + new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, + new DSAParameter(parameters.getP(), parameters.getQ(), parameters.getG()).toASN1Primitive()), + new ASN1Integer(wimpyPrivateKey.getX())).getEncoded(); + + // write public length to bytes. + byte[] publicKeySize = new byte[] {(byte) (publicKeyBytes.length >>> 24), + (byte) (publicKeyBytes.length >>> 16), + (byte) (publicKeyBytes.length >>> 8), + (byte) (publicKeyBytes.length >>> 0)}; + + ByteArrayOutputStream keyOutputStream = new ByteArrayOutputStream(4 + publicKeyBytes.length + privateKeyBytes.length); + + keyOutputStream.write(publicKeyBytes, 0, publicKeyBytes.length); + keyOutputStream.write(privateKeyBytes, 0, privateKeyBytes.length); + keyOutputStream.write(publicKeySize, 0, publicKeySize.length); // mess with people staring at the keys (store length at the end). + + displayByteHash(publicKeyBytes); + + // write out the file + OutputStream outputStream = new FileOutputStream(wimpyKeyRawFile); + keyOutputStream.writeTo(outputStream); + Sys.close(outputStream); + } + + private static void displayByteHash(byte[] publicKeyBytes) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-512"); + digest.reset(); + digest.update(publicKeyBytes, 0, publicKeyBytes.length); + + String digestString = Base64Fast.encodeToString(digest.digest(), false); + + String origDigestHash = "9f5LkG90ITAMR37xxbXGXAGyaGkZL1dP7FzU8y/CL8gskIxegZTRbOn0g3ks/eCJ5jSKTX4eVZCPmA0TKj7zlw=="; + if (!digestString.equals(origDigestHash)) { + System.err.println("Wimpy public key bytes. Need to modify " + JarSigner.class.getSimpleName() + " and in the Launcher"); + System.err.println(digestString); + } + + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/dorkbox/build/util/jar/JarUtil.java b/src/dorkbox/build/util/jar/JarUtil.java new file mode 100644 index 0000000..9d42221 --- /dev/null +++ b/src/dorkbox/build/util/jar/JarUtil.java @@ -0,0 +1,1816 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build.util.jar; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.RandomAccessFile; +import java.io.Writer; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.zip.GZIPInputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +import org.bouncycastle.crypto.digests.SHA512Digest; + +import com.esotericsoftware.wildcard.Paths; +import com.ice.tar.TarEntry; +import com.ice.tar.TarInputStream; + +import dorkbox.Build; +import dorkbox.BuildOptions; +import dorkbox.license.License; +import dorkbox.util.Base64Fast; +import dorkbox.util.FileUtil; +import dorkbox.util.LZMA; +import dorkbox.util.OS; +import dorkbox.util.OsType; +import dorkbox.util.Sys; + +public class JarUtil { + public static int JAR_COMPRESSION_LEVEL = 9; + + public static byte[] ZIP_HEADER = { 80, 75, 3, 4 }; // PK34 + + public static final String metaInfName = "META-INF/"; + public static final String configFile = "config.ini"; + + /** + * @return true if the file is a zip/jar file + */ + public static boolean isZipFile(File file) { + boolean isZip = true; + byte[] buffer = new byte[ZIP_HEADER.length]; + + RandomAccessFile raf = null; + try { + raf = new RandomAccessFile(file, "r"); + raf.readFully(buffer); + for (int i = 0; i < ZIP_HEADER.length; i++) { + if (buffer[i] != ZIP_HEADER[i]) { + isZip = false; + break; + } + } + } catch (Exception e) { + isZip = false; + } finally { + if (raf != null) { + try { + raf.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return isZip; + } + + /** + * @return true if the file is a zip/jar stream + */ + public static boolean isZipStream(ByteArrayInputStream input) { + boolean isZip = true; + int length = ZIP_HEADER.length; + + try { + input.mark(length+1); + + for (int i = 0; i < length; i++) { + byte read = (byte) input.read(); + if (read != ZIP_HEADER[i]) { + isZip = false; + break; + } + } + input.reset(); + } catch (Exception e) { + isZip = false; + } + return isZip; + } + + /** + * retrieve the manifest from a jar file -- this will either load a + * pre-existing META-INF/MANIFEST.MF, or return null if none + */ + public static final Manifest getManifestFile(JarFile jarFile) throws IOException { + JarEntry je = jarFile.getJarEntry(JarFile.MANIFEST_NAME); + + // verify that it really exists. + if (je != null) { + Enumeration jarEntries = jarFile.entries(); + while (jarEntries.hasMoreElements()) { + je = jarEntries.nextElement(); + if (JarFile.MANIFEST_NAME.equals(je.getName())) { + break; + } else { + je = null; + } + } + + // create the manifest object + Manifest manifest = new Manifest(); + InputStream inputStream = jarFile.getInputStream(je); + manifest.read(inputStream); + Sys.close(inputStream); + + return manifest; + } else { + return null; + } + } + + /** + * a helper function that can take entries from one jar file and write it to + * another jar stream + * + * Will close the output stream automatically. + */ + public static final void writeZipEntry(ZipEntry entry, ZipFile zipInputFile, ZipOutputStream zipOutputStream) throws IOException { + // create a new entry to avoid ZipException: invalid entry compressed size + ZipEntry newEntry = new ZipEntry(entry.getName()); + newEntry.setTime(entry.getTime()); + newEntry.setComment(entry.getComment()); + newEntry.setExtra(entry.getExtra()); + + zipOutputStream.putNextEntry(newEntry); + if (!entry.isDirectory()) { + InputStream is = zipInputFile.getInputStream(entry); + + Sys.copyStream(is, zipOutputStream); + Sys.close(is); + zipOutputStream.flush(); + } + + zipOutputStream.closeEntry(); + } + + /** + * a helper function that can take entries from one jar file and write it to + * another jar stream + * + * Does NOT close any streams! + */ + public static void writeZipEntry(ZipEntry entry, ZipInputStream zipInputStream, ZipOutputStream zipOutputStream) throws IOException { + ZipEntry newEntry = new ZipEntry(entry.getName()); + newEntry.setTime(entry.getTime()); + newEntry.setComment(entry.getComment()); + newEntry.setExtra(entry.getExtra()); + + zipOutputStream.putNextEntry(newEntry); + + if (!entry.isDirectory()) { + Sys.copyStream(zipInputStream, zipOutputStream); + zipOutputStream.flush(); + } + + zipInputStream.closeEntry(); + zipOutputStream.closeEntry(); + } + + + public static final String updateDigest(InputStream inputStream, MessageDigest digest) throws IOException { + byte[] buffer = new byte[2048]; + int read = 0; + digest.reset(); + + while ((read = inputStream.read(buffer)) > 0) { + digest.update(buffer, 0, read); + } + Sys.close(inputStream); + + byte[] digestBytes = digest.digest(); + + /* + * Do not insert a default newline at the end of the output line, as + * java.util.jar does its own line management (see + * Manifest.make72Safe()). Inserting additional new lines will cause + * line-wrapping problems. + */ + return Base64Fast.encodeToString(digestBytes, false); + } + + public static final ByteArrayOutputStream createNewJar(JarFile jar, String name, byte[] manifestBytes, + byte[] signatureFileManifestBytes, byte[] signatureBlockBytes) throws IOException { + + name = name.toUpperCase(); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + + JarOutputStream jarOutputStream = new JarOutputStream(new BufferedOutputStream(byteArrayOutputStream)); + jarOutputStream.setLevel(JAR_COMPRESSION_LEVEL); + + // cannot use the jarInputStream technique here, since i'm reordering + // the contents of the jar. + + // MANIFEST ENTRIES MUST BE FIRST + // write out the manifest to the output jar stream + + JarEntry manifestFile = new JarEntry(JarFile.MANIFEST_NAME); + jarOutputStream.putNextEntry(manifestFile); + jarOutputStream.write(manifestBytes, 0, manifestBytes.length); + jarOutputStream.closeEntry(); + + String signatureAlias = metaInfName + name; + + // write out the signature file + String signatureFileName = signatureAlias + ".SF"; + JarEntry signatureFileEntry = new JarEntry(signatureFileName); + jarOutputStream.putNextEntry(signatureFileEntry); + jarOutputStream.write(signatureFileManifestBytes, 0, signatureFileManifestBytes.length); + jarOutputStream.closeEntry(); + + // write out the signature block file + String signatureBlockName = signatureAlias + ".DSA"; // forced DSA + JarEntry signatureBlockEntry = new JarEntry(signatureBlockName); + jarOutputStream.putNextEntry(signatureBlockEntry); + jarOutputStream.write(signatureBlockBytes, 0, signatureBlockBytes.length); + jarOutputStream.closeEntry(); + + // commit the rest of the original entries in the + // META-INF directory. if any of their names conflict + // with one that we created for the signed JAR file, then + // we simply ignore it + Enumeration metaEntries = jar.entries(); + while (metaEntries.hasMoreElements()) { + JarEntry metaEntry = metaEntries.nextElement(); + String entryName = metaEntry.getName(); + + if (entryName.startsWith(metaInfName) + && !(JarFile.MANIFEST_NAME.equalsIgnoreCase(entryName) || signatureFileName.equalsIgnoreCase(entryName) || signatureBlockName.equalsIgnoreCase(entryName))) { + + JarUtil.writeZipEntry(metaEntry, jar, jarOutputStream); + } + } + + // now write out the rest of the files to the stream + Enumeration allEntries = jar.entries(); + while (allEntries.hasMoreElements()) { + JarEntry entry = allEntries.nextElement(); + if (!entry.getName().startsWith(metaInfName)) { + JarUtil.writeZipEntry(entry, jar, jarOutputStream); + } + } + + // finish the stream that we have been writing to + jarOutputStream.finish(); + Sys.close(jarOutputStream); + + jar.close(); + + return byteArrayOutputStream; + } + + /** + * removes all of the (META-INF, OSGI-INF, etc) information (removes the entire directory), AND ALSO removes all comments from the files + */ + public static InputStream removeManifestCommentsAndFiles(String fileName, InputStream inputStream, + String[] pathToRemove, String[] pathToKeep) throws IOException { + // shortcut out -- nothing to do + if (pathToRemove == null || pathToRemove.length == 0) { + return inputStream; + } + + Set stripped = new HashSet(); + + // by default, this will not have access to the manifest! (not that we care...) + // we will ALSO lose entry comments! + JarInputStream jarInputStream = new JarInputStream(inputStream, false); + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + JarOutputStream jarOutputStream = new JarOutputStream(byteArrayOutputStream); + jarOutputStream.setLevel(JAR_COMPRESSION_LEVEL); + + JarEntry entry; + JAR_PROCESSING: + while ((entry = jarInputStream.getNextJarEntry()) != null) { + String name = entry.getName(); + boolean preserveEntry = false; + + if (pathToKeep != null) { + for (String dir : pathToKeep) { + if (name.startsWith(dir)) { + preserveEntry = true; + break; + } + } + } + + if (!preserveEntry) { + for (String dir : pathToRemove) { + if (name.startsWith(dir)) { + if (!stripped.contains(fileName)) { + System.err.println("Removing " + dir + " directory & signatures... (" + fileName + ")"); + stripped.add(fileName); + } + continue JAR_PROCESSING; + } + } + } + + // create a new entry to avoid ZipException: invalid entry compressed size + // we want to COPY this over. hashes should remain the same between builds! + writeZipEntry(entry, jarInputStream, jarOutputStream); + } + + + // finish the stream that we have been writing to + jarOutputStream.finish(); + Sys.close(jarOutputStream); + Sys.close(jarInputStream); + Sys.close(inputStream); + + // return the regular stream if we didn't strip anything! + // convert the output stream to an input stream + int length = byteArrayOutputStream.size(); + return new ByteArrayInputStream(byteArrayOutputStream.toByteArray(), 0, length); + } + + /** + * removes all of the (META-INF, OSGI-INF, etc) information (removes the entire directory), AND ALSO removes all comments from the files + */ + public static InputStream extractLibraries(File parentLocation, InputStream inputStream, String libraryNameOverride) throws IOException { + + // these are really the file extensions that we want to extract + // from the JAR to the DIR this file is in + String[] libraryExtensions = new String[] {".so", ".dll", ".dylib", ".jnilib"}; + + List pathsToRemove = new ArrayList(); + + // by default, this will not have access to the manifest! (not that we care...) + // we will ALSO lose entry comments! + JarInputStream jarInputStream = new JarInputStream(inputStream, false); + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + JarOutputStream jarOutputStream = new JarOutputStream(byteArrayOutputStream); + jarOutputStream.setLevel(JAR_COMPRESSION_LEVEL); + + JarEntry entry; + JAR_PROCESSING: + while ((entry = jarInputStream.getNextJarEntry()) != null) { + String name = entry.getName(); + boolean moveEntry = !entry.isDirectory(); + + if (moveEntry) { + moveEntry = false; + for (String extension : libraryExtensions) { + if (name.endsWith(extension)) { + moveEntry = true; + break; + } + } + } + + if (moveEntry) { + // extract file+path to parent name location + + int index = name.lastIndexOf('/'); + String nameOnly = name; + String directParent = ""; + String fullParentPath = ""; + if (index > 0) { + nameOnly = name.substring(index+1); + fullParentPath = name.substring(0, name.length()-nameOnly.length()-1); + directParent = fullParentPath; + + index = fullParentPath.lastIndexOf('/'); + if (index > 0) { + directParent = fullParentPath.substring(index+1); + } + + // have to put the '/' back on the path + fullParentPath = fullParentPath + '/'; + pathsToRemove.add(fullParentPath); + + // now we have to override the library name + index = name.lastIndexOf('.'); + String extension = name.substring(index); + nameOnly = libraryNameOverride + extension; + } + + // now, we have a parent file that needs to be TRANSLATED to our OS specific (so we can nicely load it later) + if (directParent == null) { + throw new RuntimeException("DirectParent"); + } + + + if (directParent.equals("darwin")) { + // JNA includes a multi-arch binary for x86, x86-64, and ppc. + directParent = OsType.MacOsX32.getName(); + + System.err.println("\t - Moving " + directParent + " library..."); + // now we copy the file to disk + File parentFile = new File(parentLocation, directParent); + parentFile.mkdir(); + + File file = new File(parentFile, nameOnly); + + FileOutputStream fileOutputStream = new FileOutputStream(file); + Sys.copyStream(jarInputStream, fileOutputStream); + fileOutputStream.flush(); + jarInputStream.closeEntry(); + + + // 64 bit is the same lib + directParent = OsType.MacOsX64.getName(); + + System.err.println("\t - Moving " + directParent + " library..."); + // now we copy the file to disk + parentFile = new File(parentLocation, directParent); + parentFile.mkdir(); + + file = new File(parentFile, nameOnly); + + fileOutputStream = new FileOutputStream(file); + Sys.copyStream(jarInputStream, fileOutputStream); + fileOutputStream.flush(); + jarInputStream.closeEntry(); + + continue JAR_PROCESSING; + } else if (directParent.startsWith("freebsd")) { + directParent = null; + } else if (directParent.equals("linux-arm")) { + directParent = OsType.LinuxArm.getName(); + } else if (directParent.equals("linux-x86")) { + directParent = OsType.Linux32.getName(); + } else if (directParent.equals("linux-x86-64")) { + directParent = OsType.Linux64.getName(); + } else if (directParent.startsWith("openbsd")) { + directParent = null; + } else if (directParent.startsWith("freebsd")) { + directParent = null; + } else if (directParent.startsWith("sunos")) { + directParent = null; + } else if (directParent.startsWith("w32ce")) { + directParent = null; + } else if (directParent.equals("win32-x86")) { + directParent = OsType.Windows32.getName(); + } else if (directParent.equals("win32-x86-64")) { + directParent = OsType.Windows64.getName(); + } + + + // only do something if we have a target specified + if (directParent != null) { + System.err.println("\t - Moving " + directParent + " library..."); + + // now we copy the file to disk + File parentFile = new File(parentLocation, directParent); + parentFile.mkdir(); + + File file = new File(parentFile, nameOnly); + + FileOutputStream fileOutputStream = new FileOutputStream(file); + Sys.copyStream(jarInputStream, fileOutputStream); + fileOutputStream.flush(); + jarInputStream.closeEntry(); + } + + } + else { + // create a new entry to avoid ZipException: invalid entry compressed size + // we want to COPY this over. hashes should remain the same between builds! + writeZipEntry(entry, jarInputStream, jarOutputStream); + } + } + + // finish the stream that we have been writing to + jarOutputStream.finish(); + Sys.close(jarOutputStream); + Sys.close(jarInputStream); + Sys.close(inputStream); + + + + + // NOW -- we load it again, and REMOVE the empty dirs from the jar + System.err.println("\t - Cleaning library directories in jar..."); + int length = byteArrayOutputStream.size(); + inputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray(), 0, length); + + + // by default, this will not have access to the manifest! (not that we care...) + // we will ALSO lose entry comments! + jarInputStream = new JarInputStream(inputStream, false); + + byteArrayOutputStream = new ByteArrayOutputStream(); + jarOutputStream = new JarOutputStream(byteArrayOutputStream); + jarOutputStream.setLevel(JAR_COMPRESSION_LEVEL); + + JAR_PROCESSING: + while ((entry = jarInputStream.getNextJarEntry()) != null) { + String name = entry.getName(); + + boolean moveEntry = entry.isDirectory(); + + if (moveEntry) { + moveEntry = false; + for (String path : pathsToRemove) { + if (name.startsWith(path)) { + continue JAR_PROCESSING; + } + } + } + + // create a new entry to avoid ZipException: invalid entry compressed size + // we want to COPY this over. hashes should remain the same between builds! + writeZipEntry(entry, jarInputStream, jarOutputStream); + } + + // finish the stream that we have been writing to + jarOutputStream.finish(); + Sys.close(jarOutputStream); + Sys.close(jarInputStream); + Sys.close(inputStream); + + + // return the regular stream if we didn't strip anything! + // convert the output stream to an input stream + length = byteArrayOutputStream.size(); + return new ByteArrayInputStream(byteArrayOutputStream.toByteArray(), 0, length); + } + + /** + * This will ALSO normalize (pack+unpack) the jar + * + * Note about JarOutputStream: + * The JAR_MAGIC "0xCAFE" in the extra field data of the first JAR entry from our JarOutputStream implementation is + * not required by JAR specification. It's an "internal implementation detail" to support "executable" jar on Solaris + * platform. see#4138619. It would be incorrect to reject a JAR file that does not have this extra field data, from + * specification point of view. + * + * (basically, if you use a JarOutputStream, it adds in extra crap we don't want) + */ + public static void jar(JarOptions options) throws IOException { + + if (options.outputFile == null) { + throw new IllegalArgumentException("jarFile cannot be null."); + } + if (options.inputPaths == null) { + throw new IllegalArgumentException("inputPaths cannot be null."); + } + + options.inputPaths = options.inputPaths.filesOnly(); + if (options.inputPaths.isEmpty()) { + System.err.println("No files to JAR."); + return; + } + + List fullPaths = options.inputPaths.getPaths(); + List relativePaths = options.inputPaths.getRelativePaths(); + String manifestFile = null; + + String manifestName = JarFile.MANIFEST_NAME; + int manifestIndex = relativePaths.indexOf(manifestName); + + // manage the MANIFEST + if (manifestIndex > 0) { + // Ensure MANIFEST.MF is first. + relativePaths.remove(manifestIndex); + relativePaths.add(0, manifestName); + + String manifestFullPath = fullPaths.get(manifestIndex); + fullPaths.remove(manifestIndex); + fullPaths.add(0, manifestFullPath); + } else + + if (options.mainClass != null) { + manifestFile = FileUtil.tempFile("manifest").getAbsolutePath(); + relativePaths.add(0, manifestName); + fullPaths.add(0, manifestFile); + + Manifest manifest = new Manifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0"); + + attributes.putValue(Attributes.Name.MAIN_CLASS.toString(), options.mainClass); + + if (options.otherManifestAttributes != null) { + for (Entry entry : options.otherManifestAttributes.entrySet()) { + attributes.putValue(entry.getKey(), entry.getValue()); + } + } + + + StringBuilder buffer = new StringBuilder(512); + buffer.append("."); + if (options.classpath != null) { + for (String name : options.classpath.getRelativePaths()) { + buffer.append(' '); + buffer.append(name); + } + } + attributes.putValue(Attributes.Name.CLASS_PATH.toString(), buffer.toString()); + + FileOutputStream output = new FileOutputStream(manifestFile); + try { + manifest.write(output); + } finally { + Sys.close(output); + } + } + + Build.log().title(" Creating JAR").message("(" + options.inputPaths.count() + " entries)", + FileUtil.normalizeAsFile(options.outputFile)); + + + // CLEANUP DIRECTORIES + Set directories = figureOutDirectories(fullPaths, relativePaths); + + // NOW WE ACTUALLY MAKE THE JAR + FileUtil.mkdir(new File(options.outputFile).getParent()); + ByteArrayOutputStream jarOutputStream = new ByteArrayOutputStream(); + JarOutputStream output = new JarOutputStream(jarOutputStream); + output.setLevel(JAR_COMPRESSION_LEVEL); + + try { + // quirks & zip standards. + // - Directory names must end with a slash '/' + // - All paths must use '/' style slashes, not '\' + // - JarEntry names should NOT begin with '/' + InputStream input = null; + boolean foundManifest = false; + + // MANIFEST FIRST! There is only the manifest, as we are creating + // the jar from scratch. + // this means that there won't be any other "META-INF" files. + if (manifestIndex >= 0) { + for (int i = 0, n = fullPaths.size(); i < n; i++) { + String fileName = relativePaths.get(i).replace('\\', '/'); + if (fileName.equals(manifestName)) { + File file = new File(fullPaths.get(i)); + + JarEntry jarEntry = new JarEntry(fileName); + jarEntry.setTime(file.lastModified()); + output.putNextEntry(jarEntry); + + input = new BufferedInputStream(new FileInputStream(file)); + Sys.copyStream(input, output); + Sys.close(input); + output.closeEntry(); + + FileUtil.delete(manifestFile); + foundManifest = true; + } + if (foundManifest) { + fullPaths.remove(i); + relativePaths.remove(i); + + break; + } + } + } + + // there won't be any OTHER manifest files, since we haven't signed + // the jar yet... + + // NEXT all directories + List sortList = new ArrayList(directories.size()); + for (String dirName : directories) { + if (!dirName.endsWith("/")) { + dirName += "/"; + } + + sortList.add(dirName); + } + + // sort them + Collections.sort(sortList); + for (String dirName : sortList) { + JarEntry jarEntry = new JarEntry(dirName); + output.putNextEntry(jarEntry); + output.closeEntry(); + } + + + class SortedFiles implements Comparable { + public String fileName; + public File file; + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (this.fileName == null ? 0 : this.fileName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + SortedFiles other = (SortedFiles) obj; + if (this.fileName == null) { + if (other.fileName != null) { + return false; + } + } else if (!this.fileName.equals(other.fileName)) { + return false; + } + return true; + } + + @Override + public int compareTo(SortedFiles o) { + return this.fileName.compareTo(o.fileName); + } + } + + /////////////////////////////////////////////// + // THEN all CLASS files. Skip the MANIFEST + /////////////////////////////////////////////// + List sortList2 = new ArrayList(fullPaths.size()); + for (int i = 0, n = fullPaths.size(); i < n; i++) { + String fileName = relativePaths.get(i).replace('\\', '/'); + if (!fileName.equals(manifestName) && fileName.endsWith(".class")) { + + SortedFiles file = new SortedFiles(); + file.file = new File(fullPaths.get(i)); + file.fileName = fileName; + sortList2.add(file); + + // mucking with the backing array so our indexing still works + fullPaths.remove(i); + relativePaths.remove(i); + i--; + n--; + } + } + + //sort them + Collections.sort(sortList2); + for (SortedFiles cf : sortList2) { + JarEntry jarEntry = new JarEntry(cf.fileName); + jarEntry.setTime(cf.file.lastModified()); + output.putNextEntry(jarEntry); + + // else just copy the file over + input = new BufferedInputStream(new FileInputStream(cf.file)); + Sys.copyStream(input, output); + Sys.close(input); + output.closeEntry(); + } + + + /////////////////////////////////////////////// + // files other than class files. + /////////////////////////////////////////////// + sortList2 = new ArrayList(fullPaths.size()); + for (int i = 0, n = fullPaths.size(); i < n; i++) { + String fileName = relativePaths.get(i).replace('\\', '/'); + + SortedFiles file = new SortedFiles(); + file.file = new File(fullPaths.get(i)); + file.fileName = fileName; + sortList2.add(file); + } + + // sort them + Collections.sort(sortList2); + for (SortedFiles cf : sortList2) { + //System.err.println('\t' + fullPaths.get(i)); + + JarEntry jarEntry = new JarEntry(cf.fileName); + jarEntry.setTime(cf.file.lastModified()); + output.putNextEntry(jarEntry); + + // else just copy the file over + input = new BufferedInputStream(new FileInputStream(cf.file)); + Sys.copyStream(input, output); + Sys.close(input); + output.closeEntry(); + } + + + /////////////////////////////////////////////// + // NOW we do the EXTRA files. + // These files will MATCH the path hierarchy in the jar + /////////////////////////////////////////////// + if (options.extraPaths != null) { + sortList2 = new ArrayList(options.extraPaths.count()); + + Build.log().message(" Adding extras"); + + fullPaths = options.extraPaths.getPaths(); + relativePaths = options.extraPaths.getRelativePaths(); + + for (int i = 0, n = fullPaths.size(); i < n; i++) { + String fileName; + fileName = relativePaths.get(i).replace('\\', '/'); + + Build.log().message("\t" + fileName); + + SortedFiles file = new SortedFiles(); + file.file = new File(fullPaths.get(i)); + file.fileName = fileName; + sortList2.add(file); + } + + // sort them + Collections.sort(sortList2); + for (SortedFiles cf : sortList2) { + + JarEntry jarEntry = new JarEntry(cf.fileName); + jarEntry.setTime(cf.file.lastModified()); + output.putNextEntry(jarEntry); + + // else just copy the file over + input = new BufferedInputStream(new FileInputStream(cf.file)); + Sys.copyStream(input, output); + Sys.close(input); + output.closeEntry(); + } + } + + /////////////////////////////////////////////// + // include the source code if possible + /////////////////////////////////////////////// + if (options.sourcePaths != null && !options.sourcePaths.isEmpty()) { + sortList2 = new ArrayList(options.sourcePaths.count()); + + Build.log().message(" Adding sources (" + options.sourcePaths.count() + " entries)..."); + + fullPaths = options.sourcePaths.getPaths(); + relativePaths = options.sourcePaths.getRelativePaths(); + + for (int i = 0, n = fullPaths.size(); i < n; i++) { + String fileName = relativePaths.get(i).replace('\\', '/'); +// System.err.println("\t\t: " + fileName); + + SortedFiles file = new SortedFiles(); + file.file = new File(fullPaths.get(i)); + file.fileName = fileName; + sortList2.add(file); + } + + // sort them + Collections.sort(sortList2); + for (SortedFiles cf : sortList2) { + + JarEntry jarEntry = new JarEntry(cf.fileName); + jarEntry.setTime(cf.file.lastModified()); + output.putNextEntry(jarEntry); + + // else just copy the file over + input = new BufferedInputStream(new FileInputStream(cf.file)); + Sys.copyStream(input, output); + Sys.close(input); + output.closeEntry(); + } + } + + /////////////////////////////////////////////// + // now include the license, if possible + /////////////////////////////////////////////// + if (options.licenses != null) { + Build.log().message(" Adding license"); + License.install(output, options.licenses); + } + } finally { + output.finish(); + Sys.close(output); + } + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(jarOutputStream.toByteArray()); + + // now we normalize the JAR. + ByteArrayOutputStream repacked = Pack200Util.Java.repackJar(byteArrayInputStream); + + byteArrayInputStream = new ByteArrayInputStream(repacked.toByteArray()); + FileOutputStream fileOutputStream = new FileOutputStream(options.outputFile); + + Sys.copyStream(byteArrayInputStream, fileOutputStream); + Sys.close(fileOutputStream); + } + + /** + * Figures out what are going to be directories that should be created in the war. + */ + private static Set figureOutDirectories(List fullPaths, List relativePaths) { + Set directories = new HashSet(); + + for (int i = 0, n = fullPaths.size(); i < n; i++) { + String fileName = relativePaths.get(i); + String pathName = fullPaths.get(i); + + // determine if we have a directory or not. + if (fileName.indexOf("/") > -1 || fileName.indexOf("\\") > -1) { + int indexOf = pathName.indexOf(fileName); + + // if our filename is a part of the path (this is when loading classes) + if (indexOf > -1) { + String dir = fileName.replace('\\', '/'); + + // keep the trailing slash! (needed later on) + int lastIndex = dir.lastIndexOf('/'); + dir = dir.substring(0, lastIndex); + + // now we add ourself, then recursively add our parent dirs + // always add back in the slash! + directories.add(dir + "/"); + lastIndex = dir.lastIndexOf('/'); + + + while (lastIndex > 0) { + dir = dir.substring(0, lastIndex); + lastIndex = dir.lastIndexOf('/'); + // now we add ourself, then recursively add our parent dirs + directories.add(dir + "/"); + } + } + // when loading up jars and other resources. + else { + // have to fetch the directory. + String dirName = fileName.substring(0, fileName.lastIndexOf("/")); + directories.add(dirName + "/"); // set the same in this instance! + } + } + } + + return directories; + } + + /** + * Similar to 'jar', however this is for war files instead. + */ + public static void war(String warFilePath, List fullPaths, List relativePaths) throws FileNotFoundException, IOException { + // CLEANUP DIRECTORIES + Set directories = figureOutDirectories(fullPaths, relativePaths); + + // NOW WE ACTUALLY MAKE THE JAR + FileUtil.mkdir(new File(warFilePath).getParent()); + JarOutputStream output = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(warFilePath))); + output.setLevel(JAR_COMPRESSION_LEVEL); + + try { + // quirks & zip standards. + // - Directory names must end with a slash '/' + // - All paths must use '/' style slashes, not '\' + // - JarEntry names should NOT begin with '/' + BufferedInputStream input = null; + + // FIRST all directories + for (String dirName : directories) { + if (!dirName.endsWith("/")) { + dirName += "/"; + } + + JarEntry jarEntry = new JarEntry(dirName); + output.putNextEntry(jarEntry); + output.closeEntry(); + } + + // regular files + for (int i = 0, n = fullPaths.size(); i < n; i++) { + String fileName = relativePaths.get(i).replace('\\', '/'); + + File file = new File(fullPaths.get(i)); + + JarEntry jarEntry = new JarEntry(fileName); + jarEntry.setTime(file.lastModified()); + output.putNextEntry(jarEntry); + + input = new BufferedInputStream(new FileInputStream(file)); + Sys.copyStream(input, output); + Sys.close(input); + output.closeEntry(); + } + } finally { + output.finish(); + Sys.close(output); + } + } + + public static void removeArchiveCommentFromJar(String jarName) throws IOException { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + JarOutputStream jarOutputStream = new JarOutputStream(new BufferedOutputStream(byteArrayOutputStream)); + jarOutputStream.setLevel(JAR_COMPRESSION_LEVEL); + + // cannot use the jarInputStream technique here, since i'm reordering + // the contents of the jar. + JarFile jarFile = new JarFile(jarName); + + // MANIFEST ENTRIES MUST BE FIRST + Enumeration metaEntries = jarFile.entries(); + + while (metaEntries.hasMoreElements()) { + JarEntry metaEntry = metaEntries.nextElement(); + String name = metaEntry.getName(); + if (name.startsWith(metaInfName) && !metaEntry.isDirectory()) { + JarUtil.writeZipEntry(metaEntry, jarFile, jarOutputStream); + } else { + // since this is already a valid jar, the META-INF data is + // already first. + break; + } + } + + // now guarantee that directories are NEXT + Enumeration directoryEntries = jarFile.entries(); + while (directoryEntries.hasMoreElements()) { + JarEntry entry = directoryEntries.nextElement(); + if (entry.isDirectory()) { + JarUtil.writeZipEntry(entry, jarFile, jarOutputStream); + } + } + + // now write out the rest of the files to the stream + Enumeration allEntries = jarFile.entries(); + while (allEntries.hasMoreElements()) { + JarEntry entry = allEntries.nextElement(); + if (!entry.isDirectory() && !entry.getName().startsWith(metaInfName)) { + JarUtil.writeZipEntry(entry, jarFile, jarOutputStream); + } + } + + // don't add the archive comment + + // finish the stream that we have been writing to + jarOutputStream.finish(); + Sys.close(jarOutputStream); + + jarFile.close(); + + OutputStream outputStream = new FileOutputStream(jarName, false); + byteArrayOutputStream.writeTo(outputStream); + Sys.close(outputStream); + } + + + public static long addTimeStampToJar(String jarName) throws IOException { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + JarOutputStream jarOutputStream = new JarOutputStream(new BufferedOutputStream(byteArrayOutputStream)); + jarOutputStream.setLevel(JAR_COMPRESSION_LEVEL); + + // cannot use the jarInputStream technique here, since i'm reordering + // the contents of the jar. + JarFile jarFile = new JarFile(jarName); + + // MANIFEST ENTRIES MUST BE FIRST + Enumeration metaEntries = jarFile.entries(); + + while (metaEntries.hasMoreElements()) { + JarEntry metaEntry = metaEntries.nextElement(); + String name = metaEntry.getName(); + + if (name.startsWith(metaInfName) && !metaEntry.isDirectory()) { + JarUtil.writeZipEntry(metaEntry, jarFile, jarOutputStream); + } else { + // since this is already a valid jar, the META-INF data is + // already first. + break; + } + } + + // now add our TIMESTAMP. + // It will ALWAYS calculate the timestamp from the BUILD SYSTEM, not the + // LOCAL/REMOTE SYSTEM (which can exist with incorrect/different clocks) + long timeStamp = System.currentTimeMillis(); + JarEntry jarEntry = new JarEntry(metaInfName + "___" + Long.toString(timeStamp)); + jarOutputStream.putNextEntry(jarEntry); + jarOutputStream.closeEntry(); + + // now guarantee that directories are NEXT + Enumeration directoryEntries = jarFile.entries(); + while (directoryEntries.hasMoreElements()) { + JarEntry entry = directoryEntries.nextElement(); + if (entry.isDirectory()) { + JarUtil.writeZipEntry(entry, jarFile, jarOutputStream); + } + } + + // now write out the rest of the files to the stream + Enumeration allEntries = jarFile.entries(); + while (allEntries.hasMoreElements()) { + JarEntry entry = allEntries.nextElement(); + if (!entry.isDirectory() && !entry.getName().startsWith(metaInfName)) { + JarUtil.writeZipEntry(entry, jarFile, jarOutputStream); + } + } + + // finish the stream that we have been writing to + jarOutputStream.finish(); + Sys.close(jarOutputStream); + + jarFile.close(); + + OutputStream outputStream = new FileOutputStream(jarName, false); + byteArrayOutputStream.writeTo(outputStream); + Sys.close(outputStream); + + return timeStamp; + } + + /** + * Adds args (launcher or VM args) to the ini file. + * @throws IOException + */ + public static void addArgsToIniInJar(String jarName, String... args) throws IOException { + Build.log().message("Modifying config.ini file in jar..."); + + for (String arg : args) { + Build.log().message("\t" + arg); + } + + // we have to use a JarFile, so we preserve the comments that might already be in the file. + JarFile origJarFile = new JarFile(jarName); + JarEntry entry; + + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + JarOutputStream jarOutputStream = new JarOutputStream(new BufferedOutputStream(byteArrayOutputStream)); + jarOutputStream.setLevel(JAR_COMPRESSION_LEVEL); + + boolean foundConfigFile = false; + // now write out the rest of the files to the stream + + // THIS DOES NOT MESS WITH THE ORDER OF THE FILES IN THE JAR! + Enumeration metaEntries = origJarFile.entries(); + while (metaEntries.hasMoreElements()) { + entry = metaEntries.nextElement(); + String name = entry.getName(); + + if (!name.equals(configFile)) { + JarUtil.writeZipEntry(entry, origJarFile, jarOutputStream); + } else { + foundConfigFile = true; + addArgsToIniContents(entry, origJarFile.getInputStream(entry), jarOutputStream, args); + } + } + + + if (!foundConfigFile) { + addArgsToIniContents(null, jarOutputStream, args); + } + + origJarFile.close(); + + + // finish the stream that we have been writing to + jarOutputStream.finish(); + Sys.close(jarOutputStream); + + OutputStream outputStream = new FileOutputStream(jarName, false); + byteArrayOutputStream.writeTo(outputStream); + Sys.close(outputStream); + } + + + public static void addArgsToIniFile(String iniFile, String... args) throws IOException { + InputStream inFile = new BufferedInputStream(new FileInputStream(iniFile)); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + + addArgsToIniContents(inFile, outStream, args); + FileOutputStream outFile = new FileOutputStream(iniFile); + outStream.writeTo(outFile); + outFile.flush(); + Sys.close(outFile); + } + + /** + * Fixes up the ini file inside the jar. + */ + private static void addArgsToIniContents(InputStream inputStream, OutputStream outputStream, String... args) throws IOException { + addArgsToIniContents(null, inputStream, outputStream, args); + } + /** + * Fixes up the ini file inside the jar. + */ + private static void addArgsToIniContents(JarEntry entry, InputStream inputStream, OutputStream outputStream, + String... args) throws IOException { + + ByteArrayOutputStream outputStreamCopy = new ByteArrayOutputStream(); + if (inputStream != null) { + Sys.copyStream(inputStream, outputStreamCopy); + } + ByteArrayInputStream inputStreamCopy = new ByteArrayInputStream(outputStreamCopy.toByteArray()); + + + List iniFileArgs = new ArrayList(16); + + BufferedReader input = null; + try { + input = new BufferedReader(new InputStreamReader(inputStreamCopy)); + //FileReader always assumes default encoding is OK! + String line = null; + /* + * returns the content of a line MINUS the newline. + * returns null only for the END of the stream. + * returns an empty String if two newlines appear in a row. + */ + while (( line = input.readLine()) != null) { + iniFileArgs.add(line); + } + } + catch (IOException e) { } + finally { + Sys.close(input); + } + + // now we write the args. + outputStreamCopy = new ByteArrayOutputStream(); + Writer output = null; + try { + output = new BufferedWriter(new OutputStreamWriter(outputStreamCopy)); + // FileWriter always assumes default encoding is OK + + // write all of the original args + for (String arg : iniFileArgs) { + output.write(arg); + output.write(OS.LINE_SEPARATOR); + } + + // write our new args + for (String arg : args) { + output.write(arg); + output.write(OS.LINE_SEPARATOR); + } + } catch (IOException e) { + } finally { + Sys.close(output); + } + + inputStreamCopy = new ByteArrayInputStream(outputStreamCopy.toByteArray()); + + if (outputStream instanceof JarOutputStream) { + JarOutputStream jarOutputStream = (JarOutputStream) outputStream; + + JarEntry entry2 = new JarEntry(configFile); + entry2.setComment(entry.getComment()); + entry2.setExtra(entry.getExtra()); + jarOutputStream.putNextEntry(entry2); + + Sys.copyStream(inputStreamCopy, outputStream); + jarOutputStream.closeEntry(); + Sys.close(inputStreamCopy); + } else { + Sys.copyStream(inputStreamCopy, outputStream); + outputStream.flush(); + Sys.close(outputStream); + Sys.close(inputStreamCopy); + } + } + + /** + * Adds the specified files AS REGULAR FILES to the jar. + * + * @param filesToAdd + * a PAIR of strings. First in pair is SOURCE, second in pair is + * DEST + */ + public static void addFilesToJar(String jarName, BuildOptions options, ExtraDataInterface extraDataWriter, Pack... filesToAdd) throws IOException { + addFilesToJar(jarName, options, null, extraDataWriter, filesToAdd); + } + + + /** + * Adds the specified files AS REGULAR FILES to the jar. Will ALSO let us REPLACE files in the jar + * + * @param filesToAdd + * a PAIR of strings. First in pair is SOURCE, second in pair is DEST + */ + public static void addFilesToJar(String jarName, BuildOptions properties, EncryptInterface encryption, ExtraDataInterface extraDataWriter, + Pack... filesToAdd) throws IOException { + PackAction[] actionsToRemove; + if (properties.compiler.enableDebugSpeedImprovement) { + actionsToRemove = new PackAction[] {PackAction.Pack, PackAction.Lzma, PackAction.Encrypt}; + } else { + actionsToRemove = new PackAction[] {PackAction.Encrypt}; + } + + boolean addDebug = properties.compiler.debugEnabled; + boolean release = properties.compiler.release; + + Build.log().message("Adding files to jar: '" + jarName + "'"); + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + JarOutputStream jarOutputStream = new JarOutputStream(new BufferedOutputStream(byteArrayOutputStream)); + jarOutputStream.setLevel(JAR_COMPRESSION_LEVEL); + + // we have to use a JarFile, so we preserve the comments that might already be in the file. + JarFile jarFile = new JarFile(jarName); + JarEntry entry; + + // THIS DOES NOT MESS WITH THE ORDER OF THE FILES IN THE JAR! + // This will also let us replace a jar with a different pack-action (ie, change something to LGPL, LoadAction, etc) + Enumeration metaEntries = jarFile.entries(); + while (metaEntries.hasMoreElements()) { + entry = metaEntries.nextElement(); + String name = entry.getName(); + + boolean canAdd = true; + for (Pack pack : filesToAdd) { + String destPath = pack.getDestPath(); + if (name.equals(destPath)) { + Build.log().message(" Replacing '" + destPath + "'"); + canAdd = false; + break; + } + } + if (canAdd) { + JarUtil.writeZipEntry(entry, jarFile, jarOutputStream); + } + } + + // now add the files that we want to add. + for (Pack pack : filesToAdd) { + if (!release) { + pack.remove(actionsToRemove); + } + + String sourcePath = pack.getSourcePath(); + String destPath = pack.getDestPath(); + + Build.log().message(" '" + sourcePath + "' -> '" + destPath + "'"); + + InputStream inputStream; + int length = 0; + long time = 0L; + entry = new JarEntry(destPath); + if (sourcePath != null) { + File fileToAdd = new File(sourcePath); + time = fileToAdd.lastModified(); + entry.setTime(time); + inputStream = new FileInputStream(fileToAdd); + length = (int) fileToAdd.length(); // yea, yea, yea, the length truncates... + } else { + inputStream = new ByteArrayInputStream(new byte[0]); + } + + ///////////// + // now load the entry to the jar + ///////////// + + PackTask task = new PackTask(pack, inputStream); + task.time = time; + task.debug = addDebug; + task.length = length; // have to do this, because of how FileInputStream works. + task.encryption = encryption; + + + if (pack.canDo(PackAction.Extract)) { + // we do this here, so that the unpack will copy over/duplicate our custom extra data field + if (extraDataWriter != null) { + extraDataWriter.write(entry, null); + } + + unpackEntry(task, extraDataWriter, jarOutputStream); + } else { + packEntry(task); + + if (extraDataWriter != null) { + extraDataWriter.write(entry, task); + } + + jarOutputStream.putNextEntry(entry); + Sys.copyStream(task.inputStream, jarOutputStream); + jarOutputStream.closeEntry(); + Sys.close(task.inputStream); + } + } + + // finish the stream that we have been writing to + jarOutputStream.finish(); + Sys.close(jarOutputStream); + + OutputStream outputStream = new FileOutputStream(jarName, false); + byteArrayOutputStream.writeTo(outputStream); + Sys.close(outputStream); + jarFile.close(); + } + + /** + * Repackages the JAR, compressing/etc based on specific rules. + * + * Also makes sure to have our custom header (in 'extra data') written for each entry + * + * This is how we get the JAR file size down. + * @return + */ + public static void packageJar(String jarName, BuildOptions properties, + EncryptInterface encryption, ExtraDataInterface extraDataWriter, + List fileExtensionToHandle, + Repack... specialActions) throws IOException { + + PackAction[] actionsToRemove; + if (properties.compiler.enableDebugSpeedImprovement) { + actionsToRemove = new PackAction[] {PackAction.Pack, PackAction.Lzma, PackAction.Encrypt}; + } else { + actionsToRemove = new PackAction[] {PackAction.Encrypt}; + } + + boolean release = properties.compiler.release; + + String tempJarName = jarName+".tmp"; + JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(tempJarName, false)); + jarOutputStream.setLevel(JAR_COMPRESSION_LEVEL); + + JarFile jarFile = new JarFile(jarName); + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String name = entry.getName(); + long time = entry.getTime(); + + // DO NOT handle manifest dir, subdirs or directories! + if (JarFile.MANIFEST_NAME.equals(name) || entry.isDirectory() || name.indexOf('/') > -1) { + // abusing the system -- but by doing this, we will have our extra data copied over + if (extraDataWriter != null) { + extraDataWriter.write(entry, null); + } + JarUtil.writeZipEntry(entry, jarFile, jarOutputStream); + } else { + // only handle if we match one of our extensions! + boolean handle = false; + for (String fileExtension : fileExtensionToHandle) { + if (name.endsWith(fileExtension)) { + handle = true; + break; + } + } + + if (!handle) { + // abusing the system -- but by doing this, we will have our extra data copied over + if (extraDataWriter != null) { + extraDataWriter.write(entry, null); + } + JarUtil.writeZipEntry(entry, jarFile, jarOutputStream); + } else { + Repack repack = null; + + for (Repack specialRepack : specialActions) { + if (name.equals(specialRepack.getName())) { + repack = specialRepack; + break; + } + } + + // default is all actions. + if (repack == null) { + repack = new Repack(name, PackAction.Package); + } + + // undo PACK, LZMA, GZIP, and encrypt so debug/testing is faster + if (!release) { + repack.remove(actionsToRemove); + } + + System.err.print("."); + + // load the entry into memory + ByteArrayOutputStream baos = new ByteArrayOutputStream(8192); + InputStream is = jarFile.getInputStream(entry); + + Sys.copyStream(is, baos); + Sys.close(is); + + // read the bytes into a buffer. + byte[] entryAsBytes = baos.toByteArray(); + + PackTask task = new PackTask(repack, entryAsBytes); + task.time = time; + task.debug = properties.compiler.debugEnabled; + task.encryption = encryption; + + if (repack.canDo(PackAction.Extract)) { + // this also writes out (and overrides) our custom extra data header + unpackEntry(task, extraDataWriter, jarOutputStream); + } else { + packEntry(task); + + // now write (single entry) to the outputStream + // figure out the ACTION file name extension + JarEntry destEntry = new JarEntry(name); + destEntry.setTime(entry.getTime()); + if (extraDataWriter != null) { + extraDataWriter.write(destEntry, task); + } + + jarOutputStream.putNextEntry(destEntry); + + Sys.copyStream(task.inputStream, jarOutputStream); + jarOutputStream.flush(); + jarOutputStream.closeEntry(); + } + } + } + } + + jarOutputStream.finish(); + Sys.close(jarOutputStream); + + jarFile.close(); + + System.err.println("."); + + FileUtil.moveFile(tempJarName, jarName); + } + + private static void unpackEntry(PackTask task, ExtraDataInterface extraDataWriter, JarOutputStream jarOutputStream) { + InputStream inputStream = task.inputStream; + Repack repack = task.pack; + + // sometimes we want to extract the contents of a compressed file to the root of our 'box' file! + // supports tar, tar.gz, gzip, zip + String name = repack.getName(); + + String extension = repack.getExtension(); + if (extension.endsWith("tar")) { + TarInputStream newInputStream = null; + newInputStream = new TarInputStream(inputStream); + + try { + TarEntry entry; + while ((entry = newInputStream.getNextEntry()) != null) { + if (entry.isDirectory()) { + continue; + } + + String name2 = entry.getName(); + + // now write (the inside entry) to the outputStream + // figure out the ACTION file name extension + JarEntry destEntry = new JarEntry(name2); + destEntry.setTime(entry.getModTime().getTime()); + if (extraDataWriter != null) { + extraDataWriter.write(destEntry, task); + } + jarOutputStream.putNextEntry(destEntry); + + Sys.copyStream(newInputStream, jarOutputStream); + jarOutputStream.flush(); + jarOutputStream.closeEntry(); + } + } catch (Exception e) { + System.err.println("Unable to extract contents of tar file!"); + } + } else { + if (extension.equals("gz") || extension.equals("gzip")) { + TarInputStream newInputStream = null; + GZIPInputStream gzipInputStream = null; + try { + if (name.endsWith("tar.gz") || name.endsWith("tar.gzip")) { + // ungzip AND untar + gzipInputStream = new GZIPInputStream(inputStream); + newInputStream = new TarInputStream(gzipInputStream); + + TarEntry entry; + while ((entry = newInputStream.getNextEntry()) != null) { + if (entry.isDirectory()) { + continue; + } + + String name2 = entry.getName(); + + // now write (the inside entry) to the outputStream + // figure out the ACTION file name extension + JarEntry destEntry = new JarEntry(name2); + destEntry.setTime(entry.getModTime().getTime()); + if (extraDataWriter != null) { + extraDataWriter.write(destEntry, task); + } + jarOutputStream.putNextEntry(destEntry); + + Sys.copyStream(newInputStream, jarOutputStream); + jarOutputStream.flush(); + jarOutputStream.closeEntry(); + } + } else { + // ONLY ungzip (gzip only works on ONE file) + + // this is a regular file (such as a txt file, etc) + // now write (the inside entry) to the outputStream + // figure out the ACTION file name extension + JarEntry destEntry = new JarEntry(name); + destEntry.setTime(task.time); // set the time to whatever the compressed entry time was + if (extraDataWriter != null) { + extraDataWriter.write(destEntry, task); + } + jarOutputStream.putNextEntry(destEntry); + + gzipInputStream = new GZIPInputStream(inputStream); + byte[] buffer = new byte[8192]; + int read = 0; + while ((read = gzipInputStream.read(buffer)) > 0) { + jarOutputStream.write(buffer, 0, read); + } + jarOutputStream.flush(); + jarOutputStream.closeEntry(); + } + } catch (Exception e) { + System.err.println("Unable to extract contents of compressed file!"); + } + Sys.close(newInputStream); + Sys.close(gzipInputStream); + } else { + // input stream can be fileInputStream (if it was a file) + // or a bytearrayinput stream if it was a stream from another file + boolean isZip = false; + if (inputStream instanceof FileInputStream) { + File file = new File(name); + isZip = isZipFile(file); + } else { + ByteArrayInputStream s = (ByteArrayInputStream) inputStream; + isZip = isZipStream(s); + } + + // can be zip (ie: jar) + if (isZip) { + ZipInputStream zipInputStream = null; + try { + zipInputStream = new ZipInputStream(inputStream); + + ZipEntry entry; + while ((entry = zipInputStream.getNextEntry()) != null) { + // create a new entry to avoid ZipException: invalid entry compressed size + // we want to COPY this over. hashes should remain the same between builds! + if (extraDataWriter != null) { + extraDataWriter.write(entry, task); + } + writeZipEntry(entry, zipInputStream, jarOutputStream); + } + } catch (Exception e) { + System.err.println("Unable to extract contents of compressed file!"); + } + Sys.close(zipInputStream); + } else { + System.err.println("Unable to extract contents of compressed file!"); + } + } + } + } + + private static void packEntry(PackTask task) throws IOException { + InputStream inputStream = task.inputStream; + int length = task.length; + Repack repack = task.pack; + + // now handle pack/compress/encrypt + if (Pack200Util.canPack200(repack, task.inputStream)) { + // Create the Packer object + ByteArrayOutputStream outputPackStream = Pack200Util.Java.pack200(inputStream, task.debug); + + // convert the output stream to an input stream + inputStream = new ByteArrayInputStream(outputPackStream.toByteArray()); + length = inputStream.available(); + } + + // we RELY on the the jar ALREADY being NORMALIZED (PACK+UNPACK). pack200 -repack DOES NOT WORK! You must EXPLICITY + // use the programmatic safePack200 and safeUnpack200 so the jar will be consistent between pack/unpack cycles. + if (repack.canDo(PackAction.LoadLibray)) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(8192); + Sys.copyStream(inputStream, baos); + + // we make it NOT pack, and since we ARE NOT modifying the input stream, it's safe to read it directly + byte[] unpackBuffer = baos.toByteArray(); + int unpackLength = unpackBuffer.length; + SHA512Digest digest = new SHA512Digest(); + + // now run the hash on it! + byte[] hashBytes = new byte[digest.getDigestSize()]; + + digest.update(unpackBuffer, 0, unpackLength); + digest.doFinal(hashBytes, 0); + + task.extraData = hashBytes; + + // since we can only read the input stream once, make sure to make it again. + inputStream = new ByteArrayInputStream(unpackBuffer); + } + + if (repack.canDo(PackAction.Lzma)) { + ByteArrayOutputStream packedOutputStream = new ByteArrayOutputStream(length); // will be size or smaller. + LZMA.encode(length, inputStream, packedOutputStream); + Sys.close(inputStream); + + // convert the output stream to an input stream + inputStream = new ByteArrayInputStream(packedOutputStream.toByteArray()); + length = inputStream.available(); + } + + // we cannot do BOTH encrypt + LGPL. They are mutually exclusive. + // LGPL will also not be hashed in the signature generation + if (repack.canDo(PackAction.Encrypt) && !repack.canDo(PackAction.LGPL)) { + if (task.encryption != null) { + ByteArrayOutputStream encryptedOutputStream = task.encryption.encrypt(inputStream, length); + + // convert the output stream to an input stream + inputStream = new ByteArrayInputStream(encryptedOutputStream.toByteArray()); + length = inputStream.available(); + } else { + throw new RuntimeException("** Unable to encrypt data when AES information is null!!"); + } + } + + task.inputStream = inputStream; + } + + /** + * Merge the specified files into the primaryFile + * + * @param primaryFile This is the file that will contain all of the other files. + * @param files Array of files (zips/jars) to be added into the primary file + * @throws IOException + * @throws FileNotFoundException + */ + public static void merge(File primaryFile, File... files) throws FileNotFoundException, IOException { + String[] fileNames = new String[files.length]; + + for (int i=0; i packOptions = new HashMap(3); + static { + // have to make sure to use the "unpack200" associated with the java binary that is running. + Map systemProperties = java.lang.management.ManagementFactory.getRuntimeMXBean().getSystemProperties(); + javaBinLocation = systemProperties.get("java.home") + File.separatorChar + "bin"; + + // from http://stackoverflow.com/questions/3312401/how-to-make-an-ant-task-to-sign-and-pack200-all-my-jar-files + // these are the REQUIRED OPTIONS in order to make BOUNCYCASTLE jar verification PASS. + // The HASH on the file will be different than the ORIGINAL hash. The "unpacked" hash will remain constant between pack/unpack cycles + + packOptions.put(Packer.MODIFICATION_TIME, Packer.KEEP); + + // keep whatever sort of jar compression was originally used + packOptions.put(Packer.DEFLATE_HINT, Packer.KEEP); + + // use largest-possible archive segments (>10% better compression). + packOptions.put(Packer.SEGMENT_LIMIT, "-1"); + } + + + /** + * @return true if the file is a pack200 file + */ + public static boolean isPack200File(File file) { + boolean isZip = true; + byte[] buffer = new byte[PACK200_HEADER.length]; + + RandomAccessFile raf = null; + try { + raf = new RandomAccessFile(file, "r"); + raf.readFully(buffer); + for (int i = 0; i < PACK200_HEADER.length; i++) { + if (buffer[i] != PACK200_HEADER[i]) { + isZip = false; + break; + } + } + } catch (Exception e) { + isZip = false; + } finally { + if (raf != null) { + try { + raf.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return isZip; + } + + /** + * @return true if the file is a pack200 stream + */ + public static boolean isPack200Stream(ByteArrayInputStream input) { + boolean isZip = true; + int length = PACK200_HEADER.length; + + try { + input.mark(length+1); + + for (int i = 0; i < length; i++) { + byte read = (byte) input.read(); + if (read != PACK200_HEADER[i]) { + isZip = false; + break; + } + } + input.reset(); + } catch (Exception e) { + isZip = false; + } + return isZip; + } + + /** + * @param inputStream + * @return true if the file can (or rather should) be packed. + */ + public static boolean canPack200(Repack repack, InputStream inputStream ) { + if (repack.canDo(PackAction.Pack)) { + String extension = repack.getExtension(); + + if (inputStream instanceof ByteArrayInputStream) { + // only pack200 certain files. + if (JarUtil.isZipStream((ByteArrayInputStream)inputStream)) { + return true; + } + } else if (".jar".equals(extension) || ".war".equals(extension) || ".ear".equals(extension) || ".ejb".equals(extension)) { + File file = new File(repack.getName()); + // only pack200 certain files. + if (JarUtil.isZipFile(file)) { + return true; + } + } + } + return false; + } + + + public static class CommandLine { + public static ByteArrayOutputStream pack200(InputStream inputStream) throws IOException { + + File source = File.createTempFile("temp", ".jar").getAbsoluteFile(); + File dest = File.createTempFile("temp", ".pack").getAbsoluteFile(); + + source.delete(); + dest.delete(); + + FileOutputStream fileOutputStream = new FileOutputStream(source); + Sys.copyStream(inputStream, fileOutputStream); + fileOutputStream.close(); + + String unpackName = "pack200"; + if (OS.isWindows()) { + unpackName += ".exe"; + } + + commandLine(javaBinLocation + File.separatorChar + unpackName, "--no-gzip", "--segment-limit=-1", "--effort=1", + dest.getAbsolutePath(), source.getAbsolutePath()); + + FileInputStream fileInputStream = new FileInputStream(dest); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(fileInputStream.available()); + Sys.copyStream(fileInputStream, outputStream); + + source.delete(); + dest.delete(); + + return outputStream; + } + + public static ByteArrayOutputStream unpack200(InputStream inputStream) throws IOException { + File source = File.createTempFile("temp", ".pack").getAbsoluteFile(); + File dest = File.createTempFile("temp", ".jar").getAbsoluteFile(); + + source.delete(); + dest.delete(); + + FileOutputStream fileOutputStream = new FileOutputStream(source); + Sys.copyStream(inputStream, fileOutputStream); + fileOutputStream.close(); + + String unpackName = "unpack200"; + if (OS.isWindows()) { + unpackName += ".exe"; + } + + commandLine(javaBinLocation + File.separatorChar + unpackName, source.getAbsolutePath(), dest.getAbsolutePath()); + + FileInputStream fileInputStream = new FileInputStream(dest); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(fileInputStream.available()); + Sys.copyStream(fileInputStream, outputStream); + + source.delete(); + dest.delete(); + + return outputStream; + } + + + // run a command line, and wait for it to finish. There is no parsing the OUTPUT, so one must be EXTREMELY careful + // that the output isn't spammy. + private static void commandLine(String... command) { + try { + Process start = new ProcessBuilder(command).start(); + while (true) { + try { + // throws an exception if the process hasn't exited yet. + start.exitValue(); + // if we get here, we know the process finished. + break; + } catch (Exception e) { + try { + Thread.sleep(200); + } catch (InterruptedException e1) { + } + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public static class Java { + /** + * SAFELY pack200 an input stream. There is an un-reported bug (java 6 is discontinued, so blah) concerning JarInputStream and + * reading the manifest during the Pack200 process. The problem is that the manifest, during a read/write cycle will CHANGE THE + * ORDER of the entries of the manifest. This is HORRID, since with jars that are SIGNED, it is CRITICAL that the manifest + * remains UNCHANGED. + * + * What this does, is read out the manifest, as a byte array, then when it's requested by the Pack200 process, it writes it + * back as the byte array --- skipping the parse/write cycle which mangles the order of the entries. + * + * NOTE: This version WILL BREAK jar signatures! + * + * @return a new ByteArrayOutputStream that contains the packed data. + */ + public static ByteArrayOutputStream pack200(InputStream inputStream, boolean addDebug) throws IOException { + // the problem is that the manifest RE-WRITES the bytes. verification of the RE-PACKED jar fails + // because the manifest bytes are different. Here, we want to DUPLICATE the bytes. By hook or by crook. + + if (!(inputStream instanceof ByteArrayInputStream)) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(inputStream.available()); + Sys.copyStream(inputStream, baos); + Sys.close(inputStream); + inputStream = new ByteArrayInputStream(baos.toByteArray()); + } + + JarInputStream jarInputStream = SafeJarInputStream.create((ByteArrayInputStream)inputStream); + + // Create the Packer object + Packer packer = Pack200.newPacker(); + + // Initialize the state by setting the desired properties + Map p = packer.properties(); + p.putAll(packOptions); + + // reorder files for better compression. + // THIS WILL BREAK JAR SIGNATURES! + p.put(Packer.KEEP_FILE_ORDER, Packer.FALSE); + + // extra options that are not required + p.put(Packer.EFFORT, "9"); // default is "5" + + if (!addDebug) { + // discard debug attributes + p.put(Packer.CODE_ATTRIBUTE_PFX + "LineNumberTable", Packer.STRIP); + p.put(Packer.CODE_ATTRIBUTE_PFX + "SourceFile", Packer.STRIP); + p.put(Packer.CODE_ATTRIBUTE_PFX + "LocalVariableTable", Packer.STRIP); + p.put(Packer.CODE_ATTRIBUTE_PFX + "LocalVariableTypeTable", Packer.STRIP); + } + + // we want to pack to a byte stream, which we will THEN compress, THEN encrypt! + ByteArrayOutputStream outputPackStream = new ByteArrayOutputStream(8096); // approx guess. + + // stupid Pack200 warnings. This should do the trick... + PrintStream error = System.err; + System.setErr(new PrintStream(new OutputStream() { + @Override + public void write(int b) throws IOException { + // do NOTHING. + } + })); + + // Call the packer + packer.pack(jarInputStream, outputPackStream); + System.setErr(error); + + outputPackStream.flush(); + Sys.close(outputPackStream); + Sys.close(jarInputStream); + + return outputPackStream; + } + + /** + * SAFELY pack200 an input stream. There is an un-reported bug (java 6 is discontinued, so blah) concerning JarInputStream and + * reading the manifest during the Pack200 process. The problem is that the manifest, during a read/write cycle will CHANGE THE + * ORDER of the entries of the manifest. This is HORRID, since with jars that are SIGNED, it is CRITICAL that the manifest + * remains UNCHANGED. + * + * What this does, is read out the manifest, as a byte array, then when it's requested by the Pack200 process, it writes it + * back as the byte array --- skipping the parse/write cycle which mangles the order of the entries. + * + * @return a new ByteArrayOutputStream that contains the packed data. + */ + public static ByteArrayOutputStream pack200_Default(InputStream inputStream) throws IOException { + // the problem is that the manifest RE-WRITES the bytes. verification of the RE-PACKED jar fails + // because the manifest bytes are different. Here, we want to DUPLICATE the bytes. By hook or by crook. + + if (!(inputStream instanceof ByteArrayInputStream)) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(inputStream.available()); + Sys.copyStream(inputStream, baos); + Sys.close(inputStream); + inputStream = new ByteArrayInputStream(baos.toByteArray()); + } + + JarInputStream jarInputStream = SafeJarInputStream.create((ByteArrayInputStream)inputStream); + + // Create the Packer object + Packer packer = Pack200.newPacker(); + + // Initialize the state by setting the desired properties + Map p = packer.properties(); + // use largest-possible archive segments (>10% better compression). + + p.put(Packer.MODIFICATION_TIME, Packer.KEEP); + + // keep whatever sort of jar compression was originally used + p.put(Packer.DEFLATE_HINT, Packer.KEEP); + + // use largest-possible archive segments (>10% better compression). + p.put(Packer.SEGMENT_LIMIT, "-1"); + + + p.put(Packer.EFFORT, "1"); + + + // we want to pack to a byte stream, which we will THEN compress, THEN encrypt! + ByteArrayOutputStream outputPackStream = new ByteArrayOutputStream(8096); // approx guess. + + // stupid Pack200 warnings. This should do the trick... + PrintStream error = System.err; + System.setErr(new PrintStream(new OutputStream() { + @Override + public void write(int b) throws IOException { + // do NOTHING. + } + })); + + // Call the packer + packer.pack(jarInputStream, outputPackStream); + System.setErr(error); + Sys.close(jarInputStream); + + outputPackStream.flush(); + Sys.close(outputPackStream); + + return outputPackStream; + } + + /** + * Safely and properly UnPack200 a jar file - the RESULTANT jar will be DEFAULT COMPRESSION LEVEL + * + * + * Because we verify these files using the COMMAND LINE version of unpack200, which + * creates DIFFERENT files than calling unpack200 in java. Because we are hashing these files + * in their UNpacked state, we have to guarantee they are the same. + * + * In order to do so, we must ALSO use the commandline version of unpack200. This is + * very unfortunate, but alas it is necessary. There is no other way around this... I've tried many. + * + * See the implementation in the Jar UltraSigner on how I did it. + */ + public static ByteArrayOutputStream unpack200(ByteArrayInputStream inputStream) throws IOException { + // check to make sure that WE ARE INDEED a PACK200 archive! + if (!isPack200Stream(inputStream)) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(inputStream.available()); + Sys.copyStream(inputStream, baos); + Sys.close(inputStream); + + return baos; + } + + ByteArrayOutputStream unpackOutputStream = new ByteArrayOutputStream(8192); + JarOutputStream unpackJarOutputStream = new JarOutputStream(unpackOutputStream); + + // stupid Pack200 warnings. This should do the trick... + PrintStream error = System.err; + System.setErr(new PrintStream(new OutputStream() { + @Override + public void write(int b) throws IOException { + // do NOTHING. + } + })); + + // Call the unpacker + Unpacker unpacker = Pack200.newUnpacker(); + + + // Initialize the state by setting the desired properties + Map p = unpacker.properties(); + // use largest-possible archive segments (>10% better compression). + // also required for repacking to work! + p.put(Packer.SEGMENT_LIMIT, "-1"); + + + unpacker.unpack(inputStream, unpackJarOutputStream); // auto-closes the INPUT stream! + + System.setErr(error); + Sys.close(inputStream); // Must explicitly close the input. + + unpackJarOutputStream.flush(); + unpackJarOutputStream.finish(); + Sys.close(unpackJarOutputStream); // closing the stream ALSO adds meta-data to the output! + + return unpackOutputStream; + } + + + /** + * Repack (or NORMALIZE) a jar with pack200. The file size will increase SLIGHTLY, however, it will be consistent with + * future pack200 operations. + *

+ * The file will be left in an UNPACKED state. + */ + public static ByteArrayOutputStream repackJar(String filePath) throws IOException { + ByteArrayInputStream inputStream = new ByteArrayInputStream(Sys.getBytesFromStream(new FileInputStream(new File(filePath)))); + return repackJar(inputStream); + } + + /** + * Repack (or NORMALIZE) a jar with pack200. The filesize will increase SLIGHTLY, however, it will be consistent with + * future pack200 operations. + *

+ * The file will be left in an UNPACKED state. + */ + public static ByteArrayOutputStream repackJar(File file) throws IOException { + ByteArrayInputStream inputStream = new ByteArrayInputStream(Sys.getBytesFromStream(new FileInputStream(file))); + return repackJar(inputStream); + } + + /** + * Repack (or NORMALIZE) a jar with pack200. The filesize will increase SLIGHTLY, however, it will be consistent with + * future pack200 operations. + *

+ * The file will be left in an UNPACKED state. + */ + public static ByteArrayOutputStream repackJar(InputStream inputStream) throws IOException { + // always make a copy, because pack200 closes the input stream! + ByteArrayOutputStream baos = new ByteArrayOutputStream(inputStream.available()); + Sys.copyStream(inputStream, baos); + inputStream = new ByteArrayInputStream(baos.toByteArray()); + + ByteArrayOutputStream outputStream; + + // make sure we are unpacked to begin with. + if (isPack200Stream((ByteArrayInputStream)inputStream)) { + outputStream = unpack200((ByteArrayInputStream)inputStream); + inputStream = new ByteArrayInputStream(outputStream.toByteArray()); + } + + outputStream = pack200_Default(inputStream); + + // we want to UNPACK the jar now so we can get a proper file "packed" state + ByteArrayInputStream inputStream2 = new ByteArrayInputStream(outputStream.toByteArray()); + outputStream = unpack200(inputStream2); + + return outputStream; + } + } +} diff --git a/src/dorkbox/build/util/jar/PackAction.java b/src/dorkbox/build/util/jar/PackAction.java new file mode 100644 index 0000000..75affd3 --- /dev/null +++ b/src/dorkbox/build/util/jar/PackAction.java @@ -0,0 +1,104 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build.util.jar; + +public enum PackAction { + // NOTE: these are packed into a TWO BYTES (for a total of 16 bitfields) + // if you add/change this, MAKE SURE you also check in Launcher and Bootstrap!! + // search for '// From PackAction' in all files to find the instances used! + // + // ALSO: the LGPL bit is 0, which means - NOTHING IS IN THE EXTRA DATA FIELD! + + // By default, everything will be pack200+compressed + Store (1 << 31), // MAX_INT just store this file. Nothing will be done to it. + Extract (1 << 30), // MAX_INT/2 extract the contents of this compressed file to the root of the 'box' file (this is at compile time) + + + + // The following affect the file load action + Pack (1 << 0), // 1 pack200 - everything (jar, etc) that can be pack200, IS pack200. We can detect this, so no + Lzma (1 << 1), // 2 we only use LZMA, since it offers better compression than gzip + Encrypt (1 << 2), // 4 aes encryption + + /** + * means we want to load our classloader DIRECTLY via JNI, so we can set our classloader up before anything else + * WARNING. The files load in the order they are put in the jar - currently lexically, in alphabetical order. This matters a lot. + */ + ClassLoader (1 << 3, Pack.getValue() | Lzma.getValue()), // 8 + + + /** + * means we want to load this into our classloader before our launcher is started. + */ + Load(1 << 4, Pack.getValue() | Lzma.getValue()), // 16 + + + /** + * Load native libraries, or load jar's that are incompatible with our box file. + *

+ * There's a comment that is the hash and pack+LZMA + *

+ * It's not always possible to load our OWN libraries dll's, since some Java libraries have their + * own method to loading dll's. + *

+ * we RELY on the the jar ALREADY being NORMALIZED (PACK+UNPACK) - if it's not, hashes won't match + */ + LoadLibray(1 << 5, Pack.getValue() | Lzma.getValue()), // 32 + + /** + * This is necessary for LGPL content. It will NOT be a part of the signature hash, and will NOT BE ENCRYPTED when using this. + * Resources/Javascript can also be LGPL (or variants). + *

+ * This DOES NOT MEAN that the entire jar file/resource that is tagged LGPL is LGPL, just that part of it is. + *

+ * Even if encrypt is "enabled" on an LGPL resource, the package process will IGNORE IT, since encrypt is not compatible with the + * LGPL license. It also means that this file can be REPLACED in the box container, since that is also part of the LGPL requirement + * so it is not hashed when ultra-signing the box containers! + */ + LGPL (1 << 6, Pack.getValue() | Lzma.getValue()), // 64 + + /** + * This means that files will be loaded by the bootstrap launcher. IF THIS ISN'T THERE, THEY WILL NOT BE LOADED!! + */ + Package(1 << 7, Pack.getValue() | Lzma.getValue() | Encrypt.getValue()), // 128 + ; + + + private final int value; + private final int baseValue; + + private PackAction(int baseValue) { + this.value = baseValue; + this.baseValue = baseValue; + } + + /** + * @param baseValue this is what the base value is for this pack action + * @param extraActions these are extra actions to perform in addition to whatever the "base value" action is. + */ + private PackAction(int baseValue, int extraActions) { + this.baseValue = baseValue; + this.value = baseValue | extraActions; + } + + public int getBaseValue() { + return this.baseValue; + } + + int getValue() { + return this.value; + } +} \ No newline at end of file diff --git a/src/dorkbox/build/util/jar/PackTask.java b/src/dorkbox/build/util/jar/PackTask.java new file mode 100644 index 0000000..495a3f1 --- /dev/null +++ b/src/dorkbox/build/util/jar/PackTask.java @@ -0,0 +1,50 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build.util.jar; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +public class PackTask { + + InputStream inputStream; + public Repack pack; + long time; + + int length; + boolean debug = false; + public byte[] extraData; + + // info on signing the jar, if it's going to be encrypted. + EncryptInterface encryption; + + public PackTask(Repack pack, byte[] entryAsBytes) { + this.pack = pack; + this.inputStream = new ByteArrayInputStream(entryAsBytes); + this.length = entryAsBytes.length; + } + + public PackTask(Pack pack, InputStream inputStream) { + this.pack = pack; + this.inputStream = inputStream; + // length = inputStream.available(); // set in the calling method + } + + @Override + public String toString() { + return "PackTask [" + this.pack.getName() + "]"; + } +} diff --git a/src/dorkbox/build/util/jar/Repack.java b/src/dorkbox/build/util/jar/Repack.java new file mode 100644 index 0000000..173fb92 --- /dev/null +++ b/src/dorkbox/build/util/jar/Repack.java @@ -0,0 +1,104 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build.util.jar; + +public class Repack { + private final String name; + private int actionValue = 0; + + public Repack(String name, PackAction... actions) { + this.name = name; + + for (PackAction action : actions) { + this.actionValue |= action.getValue(); + } + } + + /** + * Remove an action, if it exists. + */ + public void remove(PackAction... actions) { + for (PackAction action : actions) { + if (canDo(action)) { + // undo the action. + this.actionValue ^= action.getValue(); + } + } + } + + public String getName() { + return this.name; + } + + public boolean canDo(PackAction actionType) { + return (this.actionValue & actionType.getBaseValue()) == actionType.getBaseValue(); + } + + public int getAction() { + return this.actionValue; + } + + public String getExtension() { + int extensionIndex = this.name.lastIndexOf('.'); + if (extensionIndex > 0) { + return this.name.substring(extensionIndex).toLowerCase(); + } else { + return ""; + } + } + + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + this.actionValue; + result = prime * result + (this.name == null ? 0 : this.name.hashCode()); + return result; + } + + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Repack other = (Repack) obj; + if (this.actionValue != other.actionValue) { + return false; + } + if (this.name == null) { + if (other.name != null) { + return false; + } + } else if (!this.name.equals(other.name)) { + return false; + } + return true; + } + + + @Override + public String toString() { + return this.name; + } +} \ No newline at end of file diff --git a/src/dorkbox/build/util/jar/SafeJarInputStream.java b/src/dorkbox/build/util/jar/SafeJarInputStream.java new file mode 100644 index 0000000..e97f385 --- /dev/null +++ b/src/dorkbox/build/util/jar/SafeJarInputStream.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build.util.jar; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import dorkbox.util.Sys; + +/** + * Necessary for preventing the manifest from mangling the order of it's contents. + */ +public class SafeJarInputStream extends JarInputStream { + private final byte[] manifestBytes; + + public static SafeJarInputStream create(ByteArrayInputStream inputStream) throws IOException { + // we KNOW that the manifest is the FIRST zip entry! + // This is stupid, but the only way i know how to get the first entry + // ALSO.. might not have a manifest! + final byte[] manifestBytes; + + ZipInputStream zipInputStream = new ZipInputStream(inputStream); + + // this is the SAME as what a JarInputStream does, however the difference is + // that here we SAVE OUT the bytes, instead of parse them. + ZipEntry e = zipInputStream.getNextEntry(); + + if (e != null && e.getName().equalsIgnoreCase("META-INF/")) { + e = zipInputStream.getNextEntry(); + } + + if (e != null && JarFile.MANIFEST_NAME.equalsIgnoreCase(e.getName())) { + manifestBytes = Sys.getBytesFromStream(zipInputStream); + } else { + manifestBytes = null; + } + Sys.close(zipInputStream); + inputStream.reset(); + + return new SafeJarInputStream(inputStream, true, manifestBytes); + } + + + private SafeJarInputStream(InputStream in, boolean verify, byte[] manifestBytes) throws IOException { + super(in, verify); + this.manifestBytes = manifestBytes; + } + + @Override + public Manifest getManifest() { + if (this.manifestBytes == null) { + return null; + } + + return new SafeManifest(this.manifestBytes); + } +} diff --git a/src/dorkbox/build/util/jar/SafeManifest.java b/src/dorkbox/build/util/jar/SafeManifest.java new file mode 100644 index 0000000..021987d --- /dev/null +++ b/src/dorkbox/build/util/jar/SafeManifest.java @@ -0,0 +1,42 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.build.util.jar; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.jar.Manifest; + +/** + * Necessary for preventing the manifest from mangling the order of it's contents. + */ +public class SafeManifest extends Manifest { + private final byte[] manifestBytes; + + SafeManifest(byte[] manifestBytes) { + this.manifestBytes = manifestBytes; + } + + @Override + public void write(OutputStream out) throws IOException { + if (this.manifestBytes != null) { + out.write(this.manifestBytes, 0, this.manifestBytes.length); + } + } + + public byte[] getBytes() { + return this.manifestBytes; + } +} diff --git a/src/dorkbox/license/LICENSE.Apachev2 b/src/dorkbox/license/LICENSE.Apachev2 new file mode 100644 index 0000000..79d1b97 --- /dev/null +++ b/src/dorkbox/license/LICENSE.Apachev2 @@ -0,0 +1,218 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/src/dorkbox/license/LICENSE.BSD b/src/dorkbox/license/LICENSE.BSD new file mode 100644 index 0000000..4ac1e8f --- /dev/null +++ b/src/dorkbox/license/LICENSE.BSD @@ -0,0 +1,26 @@ + BSD License + + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL TINKERPOP BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/src/dorkbox/license/LICENSE.CC0 b/src/dorkbox/license/LICENSE.CC0 new file mode 100644 index 0000000..1625c17 --- /dev/null +++ b/src/dorkbox/license/LICENSE.CC0 @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. \ No newline at end of file diff --git a/src/dorkbox/license/LICENSE.EPLv1 b/src/dorkbox/license/LICENSE.EPLv1 new file mode 100644 index 0000000..e2c0ade --- /dev/null +++ b/src/dorkbox/license/LICENSE.EPLv1 @@ -0,0 +1,210 @@ +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC +LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM +CONSTITUTES RECIPIENT’S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + + a) in the case of the initial Contributor, the initial code and documentation + distributed under this Agreement, and + b) in the case of each subsequent Contributor: + + i) changes to the Program, and + + ii) additions to the Program; + + where such changes and/or additions to the Program originate from and are + distributed by that particular Contributor. A Contribution 'originates' from + a Contributor if it was added to the Program by such Contributor itself or + anyone acting on such Contributor’s behalf. Contributions do not include + additions to the Program which: (i) are separate modules of software + distributed in conjunction with the Program under their own license agreement, + and (ii) are not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents " mean patent claims licensable by a Contributor which are + necessarily infringed by the use or sale of its Contribution alone or when + combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, + including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants + Recipient a non-exclusive, worldwide, royalty-free copyright license to + reproduce, prepare derivative works of, publicly display, publicly perform, + distribute and sublicense the Contribution of such Contributor, if any, and + such derivative works, in source code and object code form. + +b) Subject to the terms of this Agreement, each Contributor hereby grants + Recipient a non-exclusive, worldwide, royalty-free patent license under + Licensed Patents to make, use, sell, offer to sell, import and otherwise + transfer the Contribution of such Contributor, if any, in source code and + object code form. This patent license shall apply to the combination of the + Contribution and the Program if, at the time the Contribution is added by + the Contributor, such addition of the Contribution causes such combination + to be covered by the Licensed Patents. The patent license shall not apply + to any other combinations which include the Contribution. No hardware per + se is licensed hereunder. + +c) Recipient understands that although each Contributor grants the licenses to + its Contributions set forth herein, no assurances are provided by any + Contributor that the Program does not infringe the patent or other + intellectual property rights of any other entity. Each Contributor disclaims + any liability to Recipient for claims brought by any other entity based on + infringement of intellectual property rights or otherwise. As a condition to + exercising the rights and licenses granted hereunder, each Recipient hereby + assumes sole responsibility to secure any other intellectual property rights + needed, if any. For example, if a third party patent license is required to + allow Recipient to distribute the Program, it is Recipient’s responsibility + to acquire that license before distributing the Program. + +d) Each Contributor represents that to its knowledge it has sufficient copyright + rights in its Contribution, if any, to grant the copyright license set forth + in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under + its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and + +b) its license agreement: + +i) effectively disclaims on behalf of all Contributors all warranties and + conditions, express and implied, including warranties or conditions of title + and non-infringement, and implied warranties or conditions of merchantability + and fitness for a particular purpose; + +ii) effectively excludes on behalf of all Contributors all liability for damages, + including direct, indirect, special, incidental and consequential damages, + such as lost profits; + +iii) states that any provisions which differ from this Agreement are offered by + that Contributor alone and not by any other party; and + +iv) states that source code for the Program is available from such Contributor, + and informs licensees how to obtain it in a reasonable manner on or through + a medium customarily used for software exchange. + +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and + +b) a copy of this Agreement must be included with each copy of the Program. + +Contributors may not remove or alter any copyright notices contained within the +Program. + +Each Contributor must identify itself as the originator of its Contribution, +if any, in a manner that reasonably allows subsequent Recipients to identify +the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with +respect to end users, business partners and the like. While this license is +intended to facilitate the commercial use of the Program, the Contributor who + includes the Program in a commercial product offering should do so in a manner + which does not create potential liability for other Contributors. Therefore, +if a Contributor includes the Program in a commercial product offering, such +Contributor ("Commercial Contributor") hereby agrees to defend and indemnify +every other Contributor ("Indemnified Contributor") against any losses, damages + and costs (collectively "Losses") arising from claims, lawsuits and other legal + actions brought by a third party against the Indemnified Contributor to the +extent caused by the acts or omissions of such Commercial Contributor in +connection with its distribution of the Program in a commercial product +offering. The obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In order +to qualify, an Indemnified Contributor must: a) promptly notify the Commercial +Contributor in writing of such claim, and b) allow the Commercial Contributor +to control, and cooperate with the Commercial Contributor in, the defense and +any related settlement negotiations. The Indemnified Contributor may participate +in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product +offering, Product X. That Contributor is then a Commercial Contributor. If that + Commercial Contributor then makes performance claims, or offers warranties +related to Product X, those performance claims and warranties are such +Commercial Contributor’s responsibility alone. Under this section, the +Commercial Contributor would have to defend claims against the other +Contributors related to those performance claims and warranties, and if a +court requires any other Contributor to pay any damages as a result, the +Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR +IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each +Recipient is solely responsible for determining the appropriateness of using +and distributing the Program and assumes all risks associated with its exercise +of rights under this Agreement , including but not limited to the risks and +costs of program errors, compliance with applicable laws, damage to or loss of +data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY +CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION +LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF +ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable +law, it shall not affect the validity or enforceability of the remainder of the +terms of this Agreement, and without further action by the parties hereto, such +provision shall be reformed to the minimum extent necessary to make such +provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Program itself +(excluding combinations of the Program with other software or hardware) +infringes such Recipient’s patent(s), then such Recipient’s rights granted +under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient’s rights under this Agreement shall terminate if it fails to +comply with any of the material terms or conditions of this Agreement and does +not cure such failure in a reasonable period of time after becoming aware of +such noncompliance. If all Recipient’s rights under this Agreement terminate, +Recipient agrees to cease use and distribution of the Program as soon as +reasonably practicable. However, Recipient’s obligations under this Agreement +and any licenses granted by Recipient relating to the Program shall continue +and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in +order to avoid inconsistency the Agreement is copyrighted and may only be +modified in the following manner. The Agreement Steward reserves the right to +publish new versions (including revisions) of this Agreement from time to time. +No one other than the Agreement Steward has the right to modify this Agreement. +The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation +may assign the responsibility to serve as the Agreement Steward to a suitable +separate entity. Each new version of the Agreement will be given a +distinguishing version number. The Program (including Contributions) may always +be distributed subject to the version of the Agreement under which it was +received. In addition, after a new version of the Agreement is published, +Contributor may elect to distribute the Program (including its Contributions) +under the new version. Except as expressly stated in Sections 2(a) and 2(b) +above, Recipient receives no rights or licenses to the intellectual property +of any Contributor under this Agreement, whether expressly, by implication, +estoppel or otherwise. All rights in the Program not expressly granted under +this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the +intellectual property laws of the United States of America. No party to this +Agreement will bring a legal action under this Agreement more than one year +after the cause of action arose. Each party waives its rights to a jury trial +in any resulting litigation. \ No newline at end of file diff --git a/src/dorkbox/license/LICENSE.GPLv2_CP b/src/dorkbox/license/LICENSE.GPLv2_CP new file mode 100644 index 0000000..b40a0f4 --- /dev/null +++ b/src/dorkbox/license/LICENSE.GPLv2_CP @@ -0,0 +1,347 @@ +The GNU General Public License (GPL) + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public License is intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. This General Public License applies to +most of the Free Software Foundation's software and to any other program whose +authors commit to using it. (Some other Free Software Foundation software is +covered by the GNU Library General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for this service if you wish), +that you receive source code or can get it if you want it, that you can change +the software or use pieces of it in new free programs; and that you know you +can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny +you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of the +software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must give the recipients all the rights that you have. You must +make sure that they, too, receive or can get the source code. And you must +show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If the +software is modified by someone else and passed on, we want its recipients to +know that what they have is not the original, so that any problems introduced +by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We +wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program proprietary. +To prevent this, we have made it clear that any patent must be licensed for +everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification +follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice +placed by the copyright holder saying it may be distributed under the terms of +this General Public License. The "Program", below, refers to any such program +or work, and a "work based on the Program" means either the Program or any +derivative work under copyright law: that is to say, a work containing the +Program or a portion of it, either verbatim or with modifications and/or +translated into another language. (Hereinafter, translation is included +without limitation in the term "modification".) Each licensee is addressed as +"you". + +Activities other than copying, distribution and modification are not covered by +this License; they are outside its scope. The act of running the Program is +not restricted, and the output from the Program is covered only if its contents +constitute a work based on the Program (independent of having been made by +running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as +you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this License +and to the absence of any warranty; and give any other recipients of the +Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may +at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus +forming a work based on the Program, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all of +these conditions: + + a) You must cause the modified files to carry prominent notices stating + that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole or + in part contains or is derived from the Program or any part thereof, to be + licensed as a whole at no charge to all third parties under the terms of + this License. + + c) If the modified program normally reads commands interactively when run, + you must cause it, when started running for such interactive use in the + most ordinary way, to print or display an announcement including an + appropriate copyright notice and a notice that there is no warranty (or + else, saying that you provide a warranty) and that users may redistribute + the program under these conditions, and telling the user how to view a copy + of this License. (Exception: if the Program itself is interactive but does + not normally print such an announcement, your work based on the Program is + not required to print an announcement.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Program, and can be reasonably +considered independent and separate works in themselves, then this License, and +its terms, do not apply to those sections when you distribute them as separate +works. But when you distribute the same sections as part of a whole which is a +work based on the Program, the distribution of the whole must be on the terms +of this License, whose permissions for other licensees extend to the entire +whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise the +right to control the distribution of derivative or collective works based on +the Program. + +In addition, mere aggregation of another work not based on the Program with the +Program (or with a work based on the Program) on a volume of a storage or +distribution medium does not bring the other work under the scope of this +License. + +3. You may copy and distribute the Program (or a work based on it, under +Section 2) in object code or executable form under the terms of Sections 1 and +2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source + code, which must be distributed under the terms of Sections 1 and 2 above + on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to + give any third party, for a charge no more than your cost of physically + performing source distribution, a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of Sections 1 + and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed only + for noncommercial distribution and only if you received the program in + object code or executable form with such an offer, in accord with + Subsection b above.) + +The source code for a work means the preferred form of the work for making +modifications to it. For an executable work, complete source code means all +the source code for all modules it contains, plus any associated interface +definition files, plus the scripts used to control compilation and installation +of the executable. However, as a special exception, the source code +distributed need not include anything that is normally distributed (in either +source or binary form) with the major components (compiler, kernel, and so on) +of the operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the source +code from the same place counts as distribution of the source code, even though +third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as +expressly provided under this License. Any attempt otherwise to copy, modify, +sublicense or distribute the Program is void, and will automatically terminate +your rights under this License. However, parties who have received copies, or +rights, from you under this License will not have their licenses terminated so +long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. +However, nothing else grants you permission to modify or distribute the Program +or its derivative works. These actions are prohibited by law if you do not +accept this License. Therefore, by modifying or distributing the Program (or +any work based on the Program), you indicate your acceptance of this License to +do so, and all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), +the recipient automatically receives a license from the original licensor to +copy, distribute or modify the Program subject to these terms and conditions. +You may not impose any further restrictions on the recipients' exercise of the +rights granted herein. You are not responsible for enforcing compliance by +third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), conditions +are imposed on you (whether by court order, agreement or otherwise) that +contradict the conditions of this License, they do not excuse you from the +conditions of this License. If you cannot distribute so as to satisfy +simultaneously your obligations under this License and any other pertinent +obligations, then as a consequence you may not distribute the Program at all. +For example, if a patent license would not permit royalty-free redistribution +of the Program by all those who receive copies directly or indirectly through +you, then the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply and +the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or +other property right claims or to contest validity of any such claims; this +section has the sole purpose of protecting the integrity of the free software +distribution system, which is implemented by public license practices. Many +people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose that +choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain +countries either by patents or by copyrighted interfaces, the original +copyright holder who places the Program under this License may add an explicit +geographical distribution limitation excluding those countries, so that +distribution is permitted only in or among countries not thus excluded. In +such case, this License incorporates the limitation as if written in the body +of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the +General Public License from time to time. Such new versions will be similar in +spirit to the present version, but may differ in detail to address new problems +or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any later +version", you have the option of following the terms and conditions either of +that version or of any later version published by the Free Software Foundation. +If the Program does not specify a version number of this License, you may +choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs +whose distribution conditions are different, write to the author to ask for +permission. For software which is copyrighted by the Free Software Foundation, +write to the Free Software Foundation; we sometimes make exceptions for this. +Our decision will be guided by the two goals of preserving the free status of +all derivatives of our free software and of promoting the sharing and reuse of +software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR +THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE +PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, +YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL +ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE +PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR +INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA +BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER +OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible +use to the public, the best way to achieve this is to make it free software +which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach +them to the start of each source file to most effectively convey the exclusion +of warranty; and each file should have at least the "copyright" line and a +pointer to where the full notice is found. + + One line to give the program's name and a brief idea of what it does. + + Copyright (C) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it +starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes + with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free + software, and you are welcome to redistribute it under certain conditions; + type 'show c' for details. + +The hypothetical commands 'show w' and 'show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may be +called something other than 'show w' and 'show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the program, if necessary. Here +is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + 'Gnomovision' (which makes passes at compilers) written by James Hacker. + + signature of Ty Coon, 1 April 1989 + + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General Public +License instead of this License. + + +"CLASSPATH" EXCEPTION TO THE GPL + +Certain source files distributed by Oracle America and/or its affiliates are +subject to the following clarification and special exception to the GPL, but +only where Oracle has expressly included in the particular source file's header +the words "Oracle designates this particular file as subject to the "Classpath" +exception as provided by Oracle in the LICENSE file that accompanied this code." + + Linking this library statically or dynamically with other modules is making + a combined work based on this library. Thus, the terms and conditions of + the GNU General Public License cover the whole combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent modules, + and to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent module, + the terms and conditions of the license of that module. An independent + module is a module which is not derived from or based on this library. If + you modify this library, you may extend this exception to your version of + the library, but you are not obligated to do so. If you do not wish to do + so, delete this exception statement from your version. diff --git a/src/dorkbox/license/LICENSE.LGPLv3 b/src/dorkbox/license/LICENSE.LGPLv3 new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/src/dorkbox/license/LICENSE.LGPLv3 @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/src/dorkbox/license/LICENSE.MIT b/src/dorkbox/license/LICENSE.MIT new file mode 100644 index 0000000..2e9f5aa --- /dev/null +++ b/src/dorkbox/license/LICENSE.MIT @@ -0,0 +1,21 @@ + MIT License + + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/src/dorkbox/license/LICENSE.MPLv1.1 b/src/dorkbox/license/LICENSE.MPLv1.1 new file mode 100644 index 0000000..08c28a6 --- /dev/null +++ b/src/dorkbox/license/LICENSE.MPLv1.1 @@ -0,0 +1,469 @@ + MOZILLA PUBLIC LICENSE + Version 1.1 + + --------------- + +1. Definitions. + + 1.0.1. "Commercial Use" means distribution or otherwise making the + Covered Code available to a third party. + + 1.1. "Contributor" means each entity that creates or contributes to + the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Code, prior Modifications used by a Contributor, and the Modifications + made by that particular Contributor. + + 1.3. "Covered Code" means the Original Code or Modifications or the + combination of the Original Code and Modifications, in each case + including portions thereof. + + 1.4. "Electronic Distribution Mechanism" means a mechanism generally + accepted in the software development community for the electronic + transfer of data. + + 1.5. "Executable" means Covered Code in any form other than Source + Code. + + 1.6. "Initial Developer" means the individual or entity identified + as the Initial Developer in the Source Code notice required by Exhibit + A. + + 1.7. "Larger Work" means a work which combines Covered Code or + portions thereof with code not governed by the terms of this License. + + 1.8. "License" means this document. + + 1.8.1. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means any addition to or deletion from the + substance or structure of either the Original Code or any previous + Modifications. When Covered Code is released as a series of files, a + Modification is: + A. Any addition to or deletion from the contents of a file + containing Original Code or previous Modifications. + + B. Any new file that contains any part of the Original Code or + previous Modifications. + + 1.10. "Original Code" means Source Code of computer software code + which is described in the Source Code notice required by Exhibit A as + Original Code, and which, at the time of its release under this + License is not already Covered Code governed by this License. + + 1.10.1. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.11. "Source Code" means the preferred form of the Covered Code for + making modifications to it, including all modules it contains, plus + any associated interface definition files, scripts used to control + compilation and installation of an Executable, or source code + differential comparisons against either the Original Code or another + well known, available Covered Code of the Contributor's choice. The + Source Code can be in a compressed or archival form, provided the + appropriate decompression or de-archiving software is widely available + for no charge. + + 1.12. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, this + License or a future version of this License issued under Section 6.1. + For legal entities, "You" includes any entity which controls, is + controlled by, or is under common control with You. For purposes of + this definition, "control" means (a) the power, direct or indirect, + to cause the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty percent + (50%) of the outstanding shares or beneficial ownership of such + entity. + +2. Source Code License. + + 2.1. The Initial Developer Grant. + The Initial Developer hereby grants You a world-wide, royalty-free, + non-exclusive license, subject to third party intellectual property + claims: + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Code (or portions thereof) with or without Modifications, and/or + as part of a Larger Work; and + + (b) under Patents Claims infringed by the making, using or + selling of Original Code, to make, have made, use, practice, + sell, and offer for sale, and/or otherwise dispose of the + Original Code (or portions thereof). + + (c) the licenses granted in this Section 2.1(a) and (b) are + effective on the date Initial Developer first distributes + Original Code under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: 1) for code that You delete from the Original Code; 2) + separate from the Original Code; or 3) for infringements caused + by: i) the modification of the Original Code or ii) the + combination of the Original Code with other software or devices. + + 2.2. Contributor Grant. + Subject to third party intellectual property claims, each Contributor + hereby grants You a world-wide, royalty-free, non-exclusive license + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor, to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof) either on an + unmodified basis, with other Modifications, as Covered Code + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either alone + and/or in combination with its Contributor Version (or portions + of such combination), to make, use, sell, offer for sale, have + made, and/or otherwise dispose of: 1) Modifications made by that + Contributor (or portions thereof); and 2) the combination of + Modifications made by that Contributor with its Contributor + Version (or portions of such combination). + + (c) the licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first makes Commercial Use of + the Covered Code. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: 1) for any code that Contributor has deleted from the + Contributor Version; 2) separate from the Contributor Version; + 3) for infringements caused by: i) third party modifications of + Contributor Version or ii) the combination of Modifications made + by that Contributor with other software (except as part of the + Contributor Version) or other devices; or 4) under Patent Claims + infringed by Covered Code in the absence of Modifications made by + that Contributor. + +3. Distribution Obligations. + + 3.1. Application of License. + The Modifications which You create or to which You contribute are + governed by the terms of this License, including without limitation + Section 2.2. The Source Code version of Covered Code may be + distributed only under the terms of this License or a future version + of this License released under Section 6.1, and You must include a + copy of this License with every copy of the Source Code You + distribute. You may not offer or impose any terms on any Source Code + version that alters or restricts the applicable version of this + License or the recipients' rights hereunder. However, You may include + an additional document offering the additional rights described in + Section 3.5. + + 3.2. Availability of Source Code. + Any Modification which You create or to which You contribute must be + made available in Source Code form under the terms of this License + either on the same media as an Executable version or via an accepted + Electronic Distribution Mechanism to anyone to whom you made an + Executable version available; and if made available via Electronic + Distribution Mechanism, must remain available for at least twelve (12) + months after the date it initially became available, or at least six + (6) months after a subsequent version of that particular Modification + has been made available to such recipients. You are responsible for + ensuring that the Source Code version remains available even if the + Electronic Distribution Mechanism is maintained by a third party. + + 3.3. Description of Modifications. + You must cause all Covered Code to which You contribute to contain a + file documenting the changes You made to create that Covered Code and + the date of any change. You must include a prominent statement that + the Modification is derived, directly or indirectly, from Original + Code provided by the Initial Developer and including the name of the + Initial Developer in (a) the Source Code, and (b) in any notice in an + Executable version or related documentation in which You describe the + origin or ownership of the Covered Code. + + 3.4. Intellectual Property Matters + (a) Third Party Claims. + If Contributor has knowledge that a license under a third party's + intellectual property rights is required to exercise the rights + granted by such Contributor under Sections 2.1 or 2.2, + Contributor must include a text file with the Source Code + distribution titled "LEGAL" which describes the claim and the + party making the claim in sufficient detail that a recipient will + know whom to contact. If Contributor obtains such knowledge after + the Modification is made available as described in Section 3.2, + Contributor shall promptly modify the LEGAL file in all copies + Contributor makes available thereafter and shall take other steps + (such as notifying appropriate mailing lists or newsgroups) + reasonably calculated to inform those who received the Covered + Code that new knowledge has been obtained. + + (b) Contributor APIs. + If Contributor's Modifications include an application programming + interface and Contributor has knowledge of patent licenses which + are reasonably necessary to implement that API, Contributor must + also include this information in the LEGAL file. + + (c) Representations. + Contributor represents that, except as disclosed pursuant to + Section 3.4(a) above, Contributor believes that Contributor's + Modifications are Contributor's original creation(s) and/or + Contributor has sufficient rights to grant the rights conveyed by + this License. + + 3.5. Required Notices. + You must duplicate the notice in Exhibit A in each file of the Source + Code. If it is not possible to put such notice in a particular Source + Code file due to its structure, then You must include such notice in a + location (such as a relevant directory) where a user would be likely + to look for such a notice. If You created one or more Modification(s) + You may add your name as a Contributor to the notice described in + Exhibit A. You must also duplicate this License in any documentation + for the Source Code where You describe recipients' rights or ownership + rights relating to Covered Code. You may choose to offer, and to + charge a fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Code. However, You + may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear than + any such warranty, support, indemnity or liability obligation is + offered by You alone, and You hereby agree to indemnify the Initial + Developer and every Contributor for any liability incurred by the + Initial Developer or such Contributor as a result of warranty, + support, indemnity or liability terms You offer. + + 3.6. Distribution of Executable Versions. + You may distribute Covered Code in Executable form only if the + requirements of Section 3.1-3.5 have been met for that Covered Code, + and if You include a notice stating that the Source Code version of + the Covered Code is available under the terms of this License, + including a description of how and where You have fulfilled the + obligations of Section 3.2. The notice must be conspicuously included + in any notice in an Executable version, related documentation or + collateral in which You describe recipients' rights relating to the + Covered Code. You may distribute the Executable version of Covered + Code or ownership rights under a license of Your choice, which may + contain terms different from this License, provided that You are in + compliance with the terms of this License and that the license for the + Executable version does not attempt to limit or alter the recipient's + rights in the Source Code version from the rights set forth in this + License. If You distribute the Executable version under a different + license You must make it absolutely clear that any terms which differ + from this License are offered by You alone, not by the Initial + Developer or any Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by + the Initial Developer or such Contributor as a result of any such + terms You offer. + + 3.7. Larger Works. + You may create a Larger Work by combining Covered Code with other code + not governed by the terms of this License and distribute the Larger + Work as a single product. In such a case, You must make sure the + requirements of this License are fulfilled for the Covered Code. + +4. Inability to Comply Due to Statute or Regulation. + + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Code due to + statute, judicial order, or regulation then You must: (a) comply with + the terms of this License to the maximum extent possible; and (b) + describe the limitations and the code they affect. Such description + must be included in the LEGAL file described in Section 3.4 and must + be included with all distributions of the Source Code. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Application of this License. + + This License applies to code to which the Initial Developer has + attached the notice in Exhibit A and to related Covered Code. + +6. Versions of the License. + + 6.1. New Versions. + Netscape Communications Corporation ("Netscape") may publish revised + and/or new versions of the License from time to time. Each version + will be given a distinguishing version number. + + 6.2. Effect of New Versions. + Once Covered Code has been published under a particular version of the + License, You may always continue to use it under the terms of that + version. You may also choose to use such Covered Code under the terms + of any subsequent version of the License published by Netscape. No one + other than Netscape has the right to modify the terms applicable to + Covered Code created under this License. + + 6.3. Derivative Works. + If You create or use a modified version of this License (which you may + only do in order to apply it to code which is not already Covered Code + governed by this License), You must (a) rename Your license so that + the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", + "MPL", "NPL" or any confusingly similar phrase do not appear in your + license (except to note that your license differs from this License) + and (b) otherwise make it clear that Your version of the license + contains terms which differ from the Mozilla Public License and + Netscape Public License. (Filling in the name of the Initial + Developer, Original Code or Contributor in the notice described in + Exhibit A shall not of themselves be deemed to be modifications of + this License.) + +7. DISCLAIMER OF WARRANTY. + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE + IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, + YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE + COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER + OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +8. TERMINATION. + + 8.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. All + sublicenses to the Covered Code which are properly granted shall + survive any termination of this License. Provisions which, by their + nature, must remain in effect beyond the termination of this License + shall survive. + + 8.2. If You initiate litigation by asserting a patent infringement + claim (excluding declatory judgment actions) against Initial Developer + or a Contributor (the Initial Developer or Contributor against whom + You file such action is referred to as "Participant") alleging that: + + (a) such Participant's Contributor Version directly or indirectly + infringes any patent, then any and all rights granted by such + Participant to You under Sections 2.1 and/or 2.2 of this License + shall, upon 60 days notice from Participant terminate prospectively, + unless if within 60 days after receipt of notice You either: (i) + agree in writing to pay Participant a mutually agreeable reasonable + royalty for Your past and future use of Modifications made by such + Participant, or (ii) withdraw Your litigation claim with respect to + the Contributor Version against such Participant. If within 60 days + of notice, a reasonable royalty and payment arrangement are not + mutually agreed upon in writing by the parties or the litigation claim + is not withdrawn, the rights granted by Participant to You under + Sections 2.1 and/or 2.2 automatically terminate at the expiration of + the 60 day notice period specified above. + + (b) any software, hardware, or device, other than such Participant's + Contributor Version, directly or indirectly infringes any patent, then + any rights granted to You by such Participant under Sections 2.1(b) + and 2.2(b) are revoked effective as of the date You first made, used, + sold, distributed, or had made, Modifications made by that + Participant. + + 8.3. If You assert a patent infringement claim against Participant + alleging that such Participant's Contributor Version directly or + indirectly infringes any patent where such claim is resolved (such as + by license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 8.4. In the event of termination under Sections 8.1 or 8.2 above, + all end user license agreements (excluding distributors and resellers) + which have been validly granted by You or any distributor hereunder + prior to termination shall survive termination. + +9. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, + OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR + ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY + CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, + WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE + EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO + THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +10. U.S. GOVERNMENT END USERS. + + The Covered Code is a "commercial item," as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" and "commercial computer software documentation," as such + terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 + C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), + all U.S. Government End Users acquire Covered Code with only those + rights set forth herein. + +11. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + California law provisions (except to the extent applicable law, if + any, provides otherwise), excluding its conflict-of-law provisions. + With respect to disputes in which at least one party is a citizen of, + or an entity chartered or registered to do business in the United + States of America, any litigation relating to this License shall be + subject to the jurisdiction of the Federal Courts of the Northern + District of California, with venue lying in Santa Clara County, + California, with the losing party responsible for costs, including + without limitation, court costs and reasonable attorneys' fees and + expenses. The application of the United Nations Convention on + Contracts for the International Sale of Goods is expressly excluded. + Any law or regulation which provides that the language of a contract + shall be construed against the drafter shall not apply to this + License. + +12. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +13. MULTIPLE-LICENSED CODE. + + Initial Developer may designate portions of the Covered Code as + "Multiple-Licensed". "Multiple-Licensed" means that the Initial + Developer permits you to utilize portions of the Covered Code under + Your choice of the NPL or the alternative licenses, if any, specified + by the Initial Developer in the file described in Exhibit A. + +EXHIBIT A -Mozilla Public License. + + ``The contents of this file are subject to the Mozilla Public License + Version 1.1 (the "License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + License for the specific language governing rights and limitations + under the License. + + The Original Code is ______________________________________. + + The Initial Developer of the Original Code is ________________________. + Portions created by ______________________ are Copyright (C) ______ + _______________________. All Rights Reserved. + + Contributor(s): ______________________________________. + + Alternatively, the contents of this file may be used under the terms + of the _____ license (the "[___] License"), in which case the + provisions of [______] License are applicable instead of those + above. If you wish to allow use of your version of this file only + under the terms of the [____] License and not to allow others to use + your version of this file under the MPL, indicate your decision by + deleting the provisions above and replace them with the notice and + other provisions required by the [___] License. If you do not delete + the provisions above, a recipient may use your version of this file + under either the MPL or the [___] License." + + [NOTE: The text of this Exhibit A may differ slightly from the text of + the notices in the Source Code files of the Original Code. You should + use the text of this Exhibit A rather than the text found in the + Original Code Source Code for Your Modifications.] \ No newline at end of file diff --git a/src/dorkbox/license/LICENSE.OFLv1.1 b/src/dorkbox/license/LICENSE.OFLv1.1 new file mode 100644 index 0000000..04df8a5 --- /dev/null +++ b/src/dorkbox/license/LICENSE.OFLv1.1 @@ -0,0 +1,86 @@ +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file diff --git a/src/dorkbox/license/LICENSE.Public b/src/dorkbox/license/LICENSE.Public new file mode 100644 index 0000000..1625c17 --- /dev/null +++ b/src/dorkbox/license/LICENSE.Public @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. \ No newline at end of file diff --git a/src/dorkbox/license/License.java b/src/dorkbox/license/License.java new file mode 100644 index 0000000..7c082ca --- /dev/null +++ b/src/dorkbox/license/License.java @@ -0,0 +1,464 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.license; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +import dorkbox.util.LocationResolver; + +public class License implements Comparable { + private static final Charset UTF_8 = Charset.forName("UTF-8"); + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + private static int maxLicenseFileSize = Integer.MAX_VALUE/16; + + /** + * Returns the LICENSE text file, as a combo of the listed licenses. Duplicates are removed. + */ + public static String buildString(List licenses) { + StringBuilder b = new StringBuilder(); + + + // The FIRST one is always FIRST! (the rest are alphabetical) + License firstLicense = licenses.remove(0); + // remove dupes + Set dedupe = new HashSet(licenses); + licenses.add(0, firstLicense); + + licenses = new ArrayList(dedupe); + Collections.sort(licenses); + licenses.add(0, firstLicense); + + + String NL = LINE_SEPARATOR; + String HEADER = " - "; + String SPACER = " "; + String SPACR1 = " "; + String SPACR2 = " "; + + boolean first = true; + + for (License l : licenses) { + if (first) { + first = false; + } else { + b.append(NL).append(NL); + } + + b.append(HEADER).append(l.name).append(" - ").append(l.type.getDescription()).append(NL); + if (l.urls != null) { + for (String s : l.urls) { + b.append(SPACER).append(s).append(NL); + } + } + if (l.copyrights != null) { + for (String s : l.copyrights) { + b.append(SPACER).append(s).append(NL); + } + } + if (l.authors != null) { + for (String s : l.authors) { + b.append(SPACR1).append(s).append(NL); + } + } + if (l.notes != null) { + for (String s : l.notes) { + b.append(SPACR2).append(s).append(NL); + } + } + } + + return b.toString(); + } + + + public static void install(File targetLocation) throws IOException { + if (targetLocation == null) { + throw new IllegalArgumentException("targetLocation cannot be null."); + } + + // copy over full text licenses + Map licenseAsBytes = License.getActualLicensesAsBytes(null); + for (Entry entry : licenseAsBytes.entrySet()) { + LicenseType key = entry.getKey(); + byte[] bytes = entry.getValue(); + + File targetLicenseFile = new File(targetLocation, "LICENSE." + key.getExtension()); + FileOutputStream fileOutputStream = new FileOutputStream(targetLicenseFile); + copyStream(new ByteArrayInputStream(bytes), fileOutputStream); + fileOutputStream.close(); + } + } + + /** + * Install the listed license files + full text licenses into the target directory. + */ + public static void install(String targetLocation, List licenses) throws IOException { + if (targetLocation == null) { + throw new IllegalArgumentException("targetLocation cannot be null."); + } + if (licenses == null || licenses.isEmpty()) { + throw new IllegalArgumentException("licenses cannot be null or empty"); + } + + install(new File(targetLocation), licenses); + } + + /** + * Install the listed license files + full text licenses into the target directory. + */ + public static void install(File targetLocation, List licenses) throws IOException { + if (targetLocation == null) { + throw new IllegalArgumentException("targetLocation cannot be null."); + } + if (licenses == null || licenses.isEmpty()) { + throw new IllegalArgumentException("licenses cannot be null or empty"); + } + + // remove all old license files + File[] listFiles = targetLocation.listFiles(); + if (listFiles != null) { + for (File f : listFiles) { + if (f.isFile() && f.getName().startsWith("LICENSE.")) { + f.delete(); + } + } + } + + // create main license file + String licenseFile = License.buildString(licenses); + + InputStream input = new ByteArrayInputStream(licenseFile.getBytes(UTF_8)); + OutputStream output = new FileOutputStream(new File(targetLocation, "LICENSE")); + + copyStream(input, output); + output.close(); + + // copy over full text licenses + Map licenseAsBytes = License.getActualLicensesAsBytes(licenses); + for (Entry entry : licenseAsBytes.entrySet()) { + LicenseType key = entry.getKey(); + byte[] bytes = entry.getValue(); + + File targetLicenseFile = new File(targetLocation, "LICENSE." + key.getExtension()); + FileOutputStream fileOutputStream = new FileOutputStream(targetLicenseFile); + copyStream(new ByteArrayInputStream(bytes), fileOutputStream); + fileOutputStream.close(); + } + } + + /** + * Install the listed license files + full text licenses into the target zip file. + */ + public static void install(ZipOutputStream zipOutputStream, List licenses) throws IOException { + if (zipOutputStream == null) { + throw new IllegalArgumentException("zipOutputStream cannot be null."); + } + if (licenses == null || licenses.isEmpty()) { + throw new IllegalArgumentException("licenses cannot be null or empty"); + } + + long time = System.currentTimeMillis(); + + String licenseFile = License.buildString(licenses); + + // WHAT IF LICENSE ALREADY EXISTS?!?! + ZipEntry zipEntry = new ZipEntry("LICENSE"); + zipEntry.setTime(time); + zipOutputStream.putNextEntry(zipEntry); + + ByteArrayInputStream input = new ByteArrayInputStream(licenseFile.getBytes(UTF_8)); + copyStream(input, zipOutputStream); + zipOutputStream.closeEntry(); + + + Map licenseAsBytes = License.getActualLicensesAsBytes(licenses); + for (Entry entry : licenseAsBytes.entrySet()) { + LicenseType key = entry.getKey(); + byte[] bytes = entry.getValue(); + + zipEntry = new ZipEntry("LICENSE." + key.getExtension()); + zipEntry.setTime(time); + zipOutputStream.putNextEntry(zipEntry); + + zipOutputStream.write(bytes, 0, bytes.length); + zipOutputStream.closeEntry(); + } + } + + /** + * @param licenses if NULL, then it returns ALL of the license types + */ + private static Map getActualLicensesAsBytes(List licenses) throws IOException { + // de-duplicate types + Set types = new HashSet(0); + if (licenses != null) { + for (License l : licenses) { + types.add(l.type); + } + } else { + for (LicenseType lt : LicenseType.values()) { + types.add(lt); + } + } + + HashMap hashMap = new HashMap(types.size()); + + // look on disk, or look in a jar for the licenses. + // Either way, we want the BYTES of those files! + String rootPath = LocationResolver.get(License.class).getPath(); + File rootFile = new File(rootPath); + String fileName = License.class.getCanonicalName(); + + int maxLicenseFileSize = License.maxLicenseFileSize; + + // this is PRIMARILY when running in an IDE + if (rootFile.isDirectory()) { + String nameAsFile = fileName.replace('.', File.separatorChar).substring(0, fileName.lastIndexOf('.')); + String location = rootFile.getParent(); + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(location).append(File.separator) + .append("src").append(File.separator) + .append(nameAsFile).append(File.separator) + .append("LICENSE."); + + String baseName = stringBuilder.toString(); + + for (LicenseType lt : types) { + String pathname = baseName + lt.getExtension(); + File f = new File(pathname); + if (f.length() > maxLicenseFileSize) { + throw new RuntimeException("WTF are you doing?!?"); + } + + FileInputStream input = new FileInputStream(f); + ByteArrayOutputStream output = new ByteArrayOutputStream((int) f.length()); + copyStream(input, output); + input.close(); + + hashMap.put(lt, output.toByteArray()); + } + } else if (rootPath.endsWith(".jar") && isZipFile(rootFile)) { + // have to go digging for it! + String nameAsFile = fileName.replace('.', '/').substring(0, fileName.lastIndexOf('.')+1); + + HashMap licenseNames = new HashMap(types.size()); + for (LicenseType l : types) { + licenseNames.put(nameAsFile + "LICENSE." + l.getExtension(), l); + } + + ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(rootFile)); + ZipEntry entry; + while ((entry = zipInputStream.getNextEntry()) != null) { + String name = entry.getName(); + + LicenseType licenseType = licenseNames.get(name); + if (licenseType != null) { + // read out bytes! + ByteArrayOutputStream output = new ByteArrayOutputStream(4096); + copyStream(zipInputStream, output); + + hashMap.put(licenseType, output.toByteArray()); + zipInputStream.closeEntry(); + } + } + zipInputStream.close(); + } else { + throw new IOException("Don't know what this is, but - KAPOW_ON_GET_CLASS_PATH"); + } + + return hashMap; + } + + /** + * Copy the contents of the input stream to the output stream. + *

+ * DOES NOT CLOSE THE STEAMS! + */ + private static T copyStream(InputStream inputStream, T outputStream) throws IOException { + byte[] buffer = new byte[4096]; + int read = 0; + while ((read = inputStream.read(buffer)) > 0) { + outputStream.write(buffer, 0, read); + } + + return outputStream; + } + + /** + * @return true if the file is a zip/jar file + */ + private static boolean isZipFile(File file) { + byte[] ZIP_HEADER = { 'P', 'K', 0x3, 0x4 }; + boolean isZip = true; + byte[] buffer = new byte[ZIP_HEADER.length]; + + RandomAccessFile raf = null; + try { + raf = new RandomAccessFile(file, "r"); + raf.readFully(buffer); + for (int i = 0; i < ZIP_HEADER.length; i++) { + if (buffer[i] != ZIP_HEADER[i]) { + isZip = false; + break; + } + } + } catch (Exception e) { + isZip = false; + } finally { + if (raf != null) { + try { + raf.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return isZip; + } + + public String name; + public LicenseType type; + + public List urls; + private List copyrights; + private List authors; + private List notes; + + public License(String licenseName, LicenseType licenseType) { + this.name = licenseName; + this.type = licenseType; + } + + /** URL **/ + public License u(String url) { + if (this.urls == null) { + this.urls = new ArrayList(); + } + this.urls.add(url); + return this; + } + + /** COPYRIGHT **/ + public License c(String copyright) { + if (this.copyrights == null) { + this.copyrights = new ArrayList(); + } + this.copyrights.add(copyright); + return this; + } + + /** License_NOTE **/ + public License n(String note) { + if (this.notes == null) { + this.notes = new ArrayList(); + } + this.notes.add(note); + return this; + } + + /** AUTHOR **/ + public License a(String author) { + if (this.authors == null) { + this.authors = new ArrayList(); + } + this.authors.add(author); + return this; + } + + public License clear() { + if (this.urls != null) { + this.urls.clear(); + } + if (this.copyrights != null) { + this.copyrights.clear(); + } + if (this.authors != null) { + this.authors.clear(); + } + if (this.notes != null) { + this.notes.clear(); + } + return this; + } + + /** + * ignore case when sorting these + */ + @Override + public int compareTo(License o) { + return this.name.toLowerCase().compareTo(o.name.toLowerCase()); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (this.name == null ? 0 : this.name.hashCode()); + result = prime * result + (this.type == null ? 0 : this.type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + License other = (License) obj; + if (this.name == null) { + if (other.name != null) { + return false; + } + } else if (!this.name.equals(other.name)) { + return false; + } + if (this.type != other.type) { + return false; + } + return true; + } + + @Override + public String toString() { + return this.name; + } +} diff --git a/src/dorkbox/license/LicenseType.java b/src/dorkbox/license/LicenseType.java new file mode 100644 index 0000000..62ce06b --- /dev/null +++ b/src/dorkbox/license/LicenseType.java @@ -0,0 +1,47 @@ +/* + * Copyright 2012 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.license; + +public enum LicenseType { + APACHE("Apachev2", "Apache 2.0 License"), + BSD("BSD", "BSD License"), // same as New BSD + CC0("CC0", "CC0 License"), + EPL("EPLv1", "Eclipse Public License"), + GPLv2_CP("GPLv2_CP", "GPL v2 License, with Classpath exception"), + LGPLv3("LGPLv3", "LGPL v3 License"), + MIT("MIT", "MIT License"), // same as MIT X11 + MPL("MPLv1.1", "Mozilla Public License"), + OFL("OFLv1.1", "SIL Open Font License"), + PUBLIC("Public", "Public Domain"), // not quite the same as CC0 (CC0 is better) + ; + + private final String name; + private final String description; + + private LicenseType(String name, String description) { + this.name = name; + this.description = description; + } + + /** This is the file name extension for the license file */ + public String getExtension() { + return this.name; + } + + public String getDescription() { + return this.description; + } +}