1 /* 2 * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.jpackage.internal; 27 28 import java.io.ByteArrayOutputStream; 29 import java.io.File; 30 import java.io.IOException; 31 import java.io.PrintStream; 32 import java.nio.file.Files; 33 import java.text.MessageFormat; 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.Collection; 37 import java.util.LinkedHashSet; 38 import java.util.List; 39 import java.util.Map; 40 import java.util.ResourceBundle; 41 import java.util.regex.Matcher; 42 import java.util.regex.Pattern; 43 44 import static jdk.jpackage.internal.StandardBundlerParam.*; 45 46 public abstract class MacBaseInstallerBundler extends AbstractBundler { 47 48 private static final ResourceBundle I18N = ResourceBundle.getBundle( 49 "jdk.jpackage.internal.resources.MacResources"); 50 51 // This could be generalized more to be for any type of Image Bundler 52 public static final BundlerParamInfo<MacAppBundler> APP_BUNDLER = 53 new StandardBundlerParam<>( 54 "mac.app.bundler", 55 MacAppBundler.class, 56 params -> new MacAppBundler(), 57 (s, p) -> null); 58 59 public final BundlerParamInfo<File> APP_IMAGE_TEMP_ROOT = 60 new StandardBundlerParam<>( 61 "mac.app.imageRoot", 62 File.class, 63 params -> { 64 File imageDir = IMAGES_ROOT.fetchFrom(params); 65 if (!imageDir.exists()) imageDir.mkdirs(); 66 try { 67 return Files.createTempDirectory( 68 imageDir.toPath(), "image-").toFile(); 69 } catch (IOException e) { 70 return new File(imageDir, getID()+ ".image"); 71 } 72 }, 73 (s, p) -> new File(s)); 74 75 public static final BundlerParamInfo<String> SIGNING_KEY_USER = 76 new StandardBundlerParam<>( 77 Arguments.CLIOptions.MAC_SIGNING_KEY_NAME.getId(), 78 String.class, 79 params -> "", 80 null); 81 82 public static final BundlerParamInfo<String> SIGNING_KEYCHAIN = 83 new StandardBundlerParam<>( 84 Arguments.CLIOptions.MAC_SIGNING_KEYCHAIN.getId(), 85 String.class, 86 params -> "", 87 null); 88 89 public static final BundlerParamInfo<String> INSTALLER_NAME = 90 new StandardBundlerParam<> ( 91 "mac.installerName", 92 String.class, 93 params -> { 94 String nm = APP_NAME.fetchFrom(params); 95 if (nm == null) return null; 96 97 String version = VERSION.fetchFrom(params); 98 if (version == null) { 99 return nm; 100 } else { 101 return nm + "-" + version; 102 } 103 }, 104 (s, p) -> s); 105 106 protected void validateAppImageAndBundeler( 107 Map<String, ? super Object> params) throws ConfigException { 108 if (PREDEFINED_APP_IMAGE.fetchFrom(params) != null) { 109 File applicationImage = PREDEFINED_APP_IMAGE.fetchFrom(params); 110 if (!applicationImage.exists()) { 111 throw new ConfigException( 112 MessageFormat.format(I18N.getString( 113 "message.app-image-dir-does-not-exist"), 114 PREDEFINED_APP_IMAGE.getID(), 115 applicationImage.toString()), 116 MessageFormat.format(I18N.getString( 117 "message.app-image-dir-does-not-exist.advice"), 118 PREDEFINED_APP_IMAGE.getID())); 119 } 120 if (APP_NAME.fetchFrom(params) == null) { 121 throw new ConfigException( 122 I18N.getString("message.app-image-requires-app-name"), 123 I18N.getString( 124 "message.app-image-requires-app-name.advice")); 125 } 126 if (IDENTIFIER.fetchFrom(params) == null) { 127 throw new ConfigException( 128 I18N.getString("message.app-image-requires-identifier"), 129 I18N.getString( 130 "message.app-image-requires-identifier.advice")); 131 } 132 } else { 133 APP_BUNDLER.fetchFrom(params).validate(params); 134 } 135 } 136 137 protected File prepareAppBundle(Map<String, ? super Object> params, 138 boolean pkg) throws PackagerException { 139 File predefinedImage = 140 StandardBundlerParam.getPredefinedAppImage(params); 141 if (predefinedImage != null) { 142 return predefinedImage; 143 } 144 File appImageRoot = APP_IMAGE_TEMP_ROOT.fetchFrom(params); 145 if (pkg) { 146 // create pkg in dmg 147 return new MacPkgBundler().bundle(params, appImageRoot); 148 } else { 149 return APP_BUNDLER.fetchFrom(params).doBundle( 150 params, appImageRoot, true); 151 } 152 } 153 154 @Override 155 public String getBundleType() { 156 return "INSTALLER"; 157 } 158 159 public static String findKey(String key, String keychainName, 160 boolean verbose) { 161 if (Platform.getPlatform() != Platform.MAC) { 162 return null; 163 } 164 165 try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); 166 PrintStream ps = new PrintStream(baos)) { 167 List<String> searchOptions = new ArrayList<>(); 168 searchOptions.add("security"); 169 searchOptions.add("find-certificate"); 170 searchOptions.add("-c"); 171 searchOptions.add(key); 172 searchOptions.add("-a"); 173 if (keychainName != null && !keychainName.isEmpty()) { 174 searchOptions.add(keychainName); 175 } 176 177 ProcessBuilder pb = new ProcessBuilder(searchOptions); 178 179 IOUtils.exec(pb, false, ps); 180 Pattern p = Pattern.compile("\"alis\"<blob>=\"([^\"]+)\""); 181 Matcher m = p.matcher(baos.toString()); 182 if (!m.find()) { 183 Log.error("Did not find a key matching '" + key + "'"); 184 return null; 185 } 186 String matchedKey = m.group(1); 187 if (m.find()) { 188 Log.error("Found more than one key matching '" + key + "'"); 189 return null; 190 } 191 Log.debug("Using key '" + matchedKey + "'"); 192 return matchedKey; 193 } catch (IOException ioe) { 194 Log.verbose(ioe); 195 return null; 196 } 197 } 198 }