blob: ad92be40b5a13f9bf07064611ca1469ae7266239 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.netbeans.nbbuild;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Copy;
import org.apache.tools.ant.taskdefs.Delete;
import org.apache.tools.ant.taskdefs.ManifestException;
import org.apache.tools.ant.taskdefs.SignJar;
import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.zip.ZipOutputStream;
/**
*
* @author mkozeny
*/
public class JNLPUpdateManifestStartup extends Task {
private static final String MANIFEST = "META-INF/MANIFEST.MF"; //NOI18N
private static final String UTF_8 = "UTF-8"; //NOI18N
private static final String ATTR_CODEBASE = "Codebase"; //NOI18N
private static final String ATTR_PERMISSIONS = "Permissions"; //NOI18N
private static final String ATTR_APPLICATION_NAME = "Application-Name"; //NOI18N
private File jar;
public void setJar(File jar) {
this.jar = jar;
}
private File destJar;
public void setDestJar(File destJar) {
this.destJar = destJar;
}
private String appName;
public void setAppName(String appName) {
this.appName = appName;
}
private File masterJnlp;
public void setMasterJnlp(File masterJnlp) {
this.masterJnlp = masterJnlp;
}
private final String permissions = "all-permissions";
private final String codebase = "*";
private SignJar signTask;
private SignJar getSignTask() {
if (signTask == null) {
signTask = (SignJar) getProject().createTask("signjar");
}
return signTask;
}
public void setAlias(String a) {
getSignTask().setAlias(a);
}
public void setStorePass(String p) {
getSignTask().setStorepass(p);
}
public void setKeystore(String k) {
getSignTask().setKeystore(k);
}
public void setStoreType(String t) {
getSignTask().setStoretype(t);
}
@Override
public void execute() throws BuildException {
File tmpFile = null;
try {
if (isSigned(jar) == null) {
tmpFile = extendLibraryManifest(getProject(), jar, destJar, codebase, permissions, appName);
}
} catch (IOException | ManifestException ex) {
getProject().log(
"Failed to extend libraries manifests: " + ex.getMessage(), //NOI18N
Project.MSG_WARN);
}
if (tmpFile != null) {
sign(tmpFile, destJar);
deleteTmpFile(tmpFile);
} else {
sign(jar, destJar);
}
}
private File extendLibraryManifest(
final Project prj,
final File sourceJar,
final File signedJar,
final String codebase,
final String permissions,
final String appName) throws IOException, ManifestException {
org.apache.tools.ant.taskdefs.Manifest manifest = null;
Copy cp = new Copy();
File tmpFile = new File(String.format("%s.tmp", signedJar.getAbsolutePath()));
cp.setFile(sourceJar);
cp.setTofile(tmpFile);
cp.execute();
boolean success = false;
try {
final Map<String, String> extendedAttrs = new HashMap<>();
final org.apache.tools.zip.ZipFile zf = new org.apache.tools.zip.ZipFile(sourceJar);
try {
final org.apache.tools.zip.ZipEntry manifestEntry = zf.getEntry(MANIFEST);
if (manifestEntry != null) {
final Reader in = new InputStreamReader(zf.getInputStream(manifestEntry), Charset.forName(UTF_8)); //NOI18N
try {
manifest = new org.apache.tools.ant.taskdefs.Manifest(in);
} finally {
in.close();
}
} else {
manifest = new org.apache.tools.ant.taskdefs.Manifest();
}
final org.apache.tools.ant.taskdefs.Manifest.Section mainSection = manifest.getMainSection();
String attr = mainSection.getAttributeValue(ATTR_CODEBASE);
if (attr == null) {
mainSection.addAttributeAndCheck(new org.apache.tools.ant.taskdefs.Manifest.Attribute(
ATTR_CODEBASE,
codebase));
extendedAttrs.put(ATTR_CODEBASE, codebase);
}
attr = mainSection.getAttributeValue(ATTR_PERMISSIONS);
if (attr == null) {
mainSection.addAttributeAndCheck(new org.apache.tools.ant.taskdefs.Manifest.Attribute(
ATTR_PERMISSIONS,
permissions));
extendedAttrs.put(ATTR_PERMISSIONS, permissions);
}
attr = mainSection.getAttributeValue(ATTR_APPLICATION_NAME);
if (attr == null) {
mainSection.addAttributeAndCheck(new org.apache.tools.ant.taskdefs.Manifest.Attribute(
ATTR_APPLICATION_NAME,
appName));
extendedAttrs.put(ATTR_APPLICATION_NAME, appName);
}
if (!extendedAttrs.isEmpty()) {
final Enumeration<? extends org.apache.tools.zip.ZipEntry> zent = zf.getEntries();
final ZipOutputStream out = new ZipOutputStream(tmpFile);
try {
org.apache.tools.zip.ZipEntry masterJnlpEntry = new org.apache.tools.zip.ZipEntry("JNLP-INF/APPLICATION.JNLP");
out.putNextEntry(masterJnlpEntry);
FileInputStream masterFis = new FileInputStream(masterJnlp.getAbsolutePath());
try {
copy(masterFis, out);
} finally {
masterFis.close();
}
while (zent.hasMoreElements()) {
final org.apache.tools.zip.ZipEntry entry = zent.nextElement();
final InputStream in = zf.getInputStream(entry);
try {
out.putNextEntry(entry);
if (MANIFEST.equals(entry.getName())) {
final PrintWriter manifestOut = new PrintWriter(new OutputStreamWriter(out, Charset.forName(UTF_8)));
manifest.write(manifestOut);
manifestOut.flush();
} else {
copy(in, out);
}
} finally {
in.close();
}
}
} finally {
out.close();
}
success = true;
final StringBuilder message = new StringBuilder("Updating library "). //NOI18N
append(safeRelativePath(prj.getBaseDir(), tmpFile)).
append(" manifest"); //NOI18N
for (Map.Entry<String, String> e : extendedAttrs.entrySet()) {
message.append(String.format(" %s: %s,", e.getKey(), e.getValue()));
}
message.deleteCharAt(message.length() - 1);
prj.log(message.toString(), Project.MSG_VERBOSE);
}
} finally {
zf.close();
}
} finally {
if (!success) {
final Delete rm = new Delete();
rm.setFile(tmpFile);
rm.setQuiet(true);
rm.execute();
tmpFile = null;
}
}
return tmpFile;
}
private static void deleteTmpFile(File tmpFile) {
final Delete del = new Delete();
del.setFile(tmpFile);
del.execute();
}
private static void copy(final InputStream in, final OutputStream out) throws IOException {
final byte[] BUFFER = new byte[4096];
int len;
for (;;) {
len = in.read(BUFFER);
if (len == -1) {
return;
}
out.write(BUFFER, 0, len);
}
}
private static String safeRelativePath(File from, File to) {
try {
return FileUtils.getRelativePath(from, to);
} catch (Exception ex) {
return to.getAbsolutePath();
}
}
/**
* return alias if signed, or null if not
*/
private static String isSigned(File f) throws IOException {
try (JarFile jar = new JarFile(f)) {
Enumeration<JarEntry> en = jar.entries();
while (en.hasMoreElements()) {
Matcher m = SF.matcher(en.nextElement().getName());
if (m.matches()) {
return m.group(1);
}
}
return null;
}
}
private static final Pattern SF = Pattern.compile("META-INF/(.+)\\.SF");
/**
* Signs the given files according to the signJars variable value.
*/
private void sign(File from, File to) {
if (!from.exists() && from.getParentFile().getName().equals("locale")) {
// skip missing locale files, probably the best fix for #103301
log("Localization file " + from + " is referenced, but cannot be found. Skipping.", Project.MSG_WARN);
return;
}
getSignTask().setJar(from);
if (to != null) {
// #125970: might be .../modules/locale/something_ja.jar
to.getParentFile().mkdirs();
}
getSignTask().setSignedjar(to);
getSignTask().setDigestAlg("SHA1");
getSignTask().execute();
}
}