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.incubator.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.List; 36 import java.util.Map; 37 import java.util.ResourceBundle; 38 import java.util.regex.Matcher; 39 import java.util.regex.Pattern; 40 41 import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; 42 43 public abstract class MacBaseInstallerBundler extends AbstractBundler { 44 45 private static final ResourceBundle I18N = ResourceBundle.getBundle( 46 "jdk.incubator.jpackage.internal.resources.MacResources"); 47 48 // This could be generalized more to be for any type of Image Bundler 49 public static final BundlerParamInfo<MacAppBundler> APP_BUNDLER = 50 new StandardBundlerParam<>( 51 "mac.app.bundler", 52 MacAppBundler.class, 53 params -> new MacAppBundler(), 54 (s, p) -> null); 55 56 public final BundlerParamInfo<File> APP_IMAGE_TEMP_ROOT = 57 new StandardBundlerParam<>( 58 "mac.app.imageRoot", 59 File.class, 60 params -> { 61 File imageDir = IMAGES_ROOT.fetchFrom(params); 62 if (!imageDir.exists()) imageDir.mkdirs(); 63 try { 64 return Files.createTempDirectory( 65 imageDir.toPath(), "image-").toFile(); 66 } catch (IOException e) { 67 return new File(imageDir, getID()+ ".image"); 68 } 69 }, 70 (s, p) -> new File(s)); 71 72 public static final BundlerParamInfo<String> SIGNING_KEY_USER = 73 new StandardBundlerParam<>( 74 Arguments.CLIOptions.MAC_SIGNING_KEY_NAME.getId(), 75 String.class, 76 params -> "", 77 null); 78 79 public static final BundlerParamInfo<String> SIGNING_KEYCHAIN = 80 new StandardBundlerParam<>( 81 Arguments.CLIOptions.MAC_SIGNING_KEYCHAIN.getId(), 82 String.class, 83 params -> "", 84 null); 85 86 public static final BundlerParamInfo<String> INSTALLER_NAME = 87 new StandardBundlerParam<> ( 88 "mac.installerName", 89 String.class, 90 params -> { 91 String nm = APP_NAME.fetchFrom(params); 92 if (nm == null) return null; 93 94 String version = VERSION.fetchFrom(params); 95 if (version == null) { 96 return nm; 97 } else { 98 return nm + "-" + version; 99 } 100 }, 101 (s, p) -> s); 102 103 protected void validateAppImageAndBundeler( 104 Map<String, ? super Object> params) throws ConfigException { 105 if (PREDEFINED_APP_IMAGE.fetchFrom(params) != null) { 106 File applicationImage = PREDEFINED_APP_IMAGE.fetchFrom(params); 107 if (!applicationImage.exists()) { 108 throw new ConfigException( 109 MessageFormat.format(I18N.getString( 110 "message.app-image-dir-does-not-exist"), 111 PREDEFINED_APP_IMAGE.getID(), 112 applicationImage.toString()), 113 MessageFormat.format(I18N.getString( 114 "message.app-image-dir-does-not-exist.advice"), 115 PREDEFINED_APP_IMAGE.getID())); 116 } 117 if (APP_NAME.fetchFrom(params) == null) { 118 throw new ConfigException( 119 I18N.getString("message.app-image-requires-app-name"), 120 I18N.getString( 121 "message.app-image-requires-app-name.advice")); 122 } 123 } else { 124 APP_BUNDLER.fetchFrom(params).validate(params); 125 } 126 } 127 128 protected File prepareAppBundle(Map<String, ? super Object> params) 129 throws PackagerException { 130 File predefinedImage = 131 StandardBundlerParam.getPredefinedAppImage(params); 132 if (predefinedImage != null) { 133 return predefinedImage; 134 } 135 File appImageRoot = APP_IMAGE_TEMP_ROOT.fetchFrom(params); 136 137 return APP_BUNDLER.fetchFrom(params).doBundle( 138 params, appImageRoot, true); 139 } 140 141 @Override 142 public String getBundleType() { 143 return "INSTALLER"; 144 } 145 146 public static String findKey(String key, String keychainName, 147 boolean verbose) { 148 if (Platform.getPlatform() != Platform.MAC) { 149 return null; 150 } 151 152 try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); 153 PrintStream ps = new PrintStream(baos)) { 154 List<String> searchOptions = new ArrayList<>(); 155 searchOptions.add("security"); 156 searchOptions.add("find-certificate"); 157 searchOptions.add("-c"); 158 searchOptions.add(key); 159 searchOptions.add("-a"); 160 if (keychainName != null && !keychainName.isEmpty()) { 161 searchOptions.add(keychainName); 162 } 163 164 ProcessBuilder pb = new ProcessBuilder(searchOptions); 165 166 IOUtils.exec(pb, false, ps); 167 Pattern p = Pattern.compile("\"alis\"<blob>=\"([^\"]+)\""); 168 Matcher m = p.matcher(baos.toString()); 169 if (!m.find()) { 170 Log.error("Did not find a key matching '" + key + "'"); 171 return null; 172 } 173 String matchedKey = m.group(1); 174 if (m.find()) { 175 Log.error("Found more than one key matching '" + key + "'"); 176 return null; 177 } 178 Log.verbose("Using key '" + matchedKey + "'"); 179 return matchedKey; 180 } catch (IOException ioe) { 181 Log.verbose(ioe); 182 return null; 183 } 184 } 185 }