1 /*******************************************************************************
2  * Copyright (c) 2007, 2019 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Red Hat Inc. - Bug 460967
14  *     SAP SE - bug 465602
15  *******************************************************************************/
16 package org.eclipse.equinox.internal.p2.touchpoint.natives;
17 
18 import java.io.*;
19 import java.net.URI;
20 import java.util.*;
21 import java.util.regex.Matcher;
22 import java.util.regex.Pattern;
23 import java.util.zip.ZipEntry;
24 import java.util.zip.ZipInputStream;
25 import org.eclipse.core.runtime.*;
26 import org.eclipse.equinox.internal.p2.core.helpers.LogHelper;
27 import org.eclipse.equinox.p2.core.*;
28 import org.eclipse.equinox.p2.engine.IProfile;
29 import org.eclipse.equinox.p2.repository.IRepository;
30 import org.eclipse.equinox.p2.repository.artifact.*;
31 import org.eclipse.osgi.util.NLS;
32 
33 public class Util {
34 
log(String message)35 	public static void log(String message) {
36 		LogHelper.log(createError(message));
37 	}
38 
createError(String message)39 	public static IStatus createError(String message) {
40 		return new Status(IStatus.ERROR, Activator.ID, message);
41 	}
42 
createError(String message, Throwable exception)43 	public static IStatus createError(String message, Throwable exception) {
44 		return new Status(IStatus.ERROR, Activator.ID, message, exception);
45 	}
46 
getInstallFolder(IProfile profile)47 	public static String getInstallFolder(IProfile profile) {
48 		return profile.getProperty(IProfile.PROP_INSTALL_FOLDER);
49 	}
50 
getAgentLocation(IProvisioningAgent agent)51 	private static IAgentLocation getAgentLocation(IProvisioningAgent agent) {
52 		return agent.getService(IAgentLocation.class);
53 	}
54 
getArtifactRepositoryManager(IProvisioningAgent agent)55 	public static IArtifactRepositoryManager getArtifactRepositoryManager(IProvisioningAgent agent) {
56 		return agent.getService(IArtifactRepositoryManager.class);
57 	}
58 
getDownloadCacheRepo(IProvisioningAgent agent)59 	public static IFileArtifactRepository getDownloadCacheRepo(IProvisioningAgent agent) throws ProvisionException {
60 		URI location = getDownloadCacheLocation(agent);
61 		if (location == null)
62 			throw new IllegalStateException(Messages.could_not_obtain_download_cache);
63 		IArtifactRepositoryManager manager = getArtifactRepositoryManager(agent);
64 		if (manager == null)
65 			throw new IllegalStateException(Messages.artifact_repo_not_found);
66 		IArtifactRepository repository;
67 		try {
68 			repository = manager.loadRepository(location, null);
69 		} catch (ProvisionException e) {
70 			// the download cache doesn't exist or couldn't be read. Create new cache.
71 			String repositoryName = location + " - Agent download cache"; //$NON-NLS-1$
72 			Map<String, String> properties = new HashMap<>(1);
73 			properties.put(IRepository.PROP_SYSTEM, Boolean.TRUE.toString());
74 			repository = manager.createRepository(location, repositoryName,
75 					IArtifactRepositoryManager.TYPE_SIMPLE_REPOSITORY, properties);
76 		}
77 
78 		IFileArtifactRepository downloadCache = repository.getAdapter(IFileArtifactRepository.class);
79 		if (downloadCache == null)
80 			throw new ProvisionException(NLS.bind(Messages.download_cache_not_writeable, location));
81 		return downloadCache;
82 	}
83 
getDownloadCacheLocation(IProvisioningAgent agent)84 	static private URI getDownloadCacheLocation(IProvisioningAgent agent) {
85 		IAgentLocation location = getAgentLocation(agent);
86 		if (location == null)
87 			return null;
88 		return URIUtil.append(location.getDataArea("org.eclipse.equinox.p2.core"), "cache/"); //$NON-NLS-1$ //$NON-NLS-2$
89 	}
90 
91 	/**
92 	 * Unzip from a File to an output directory, with progress indication and
93 	 * backup. monitor and backup store may be null.
94 	 */
unzipFile(File zipFile, File outputDir, IBackupStore store, String taskName, IProgressMonitor monitor)95 	public static File[] unzipFile(File zipFile, File outputDir, IBackupStore store, String taskName,
96 			IProgressMonitor monitor) throws IOException {
97 		return unzipFile(zipFile, outputDir, null /* path */, null /* includes */, null /* excludes */, store, taskName,
98 				monitor);
99 	}
100 
101 	/**
102 	 * Unzip from a File to an output directory, with progress indication and
103 	 * backup. monitor and backup store may be null. It takes in count
104 	 * exclude/exclude pattern (that can be null, case when everything is unzipped).
105 	 * If a path is specified, the path is consider as entry point in zip, as when
106 	 * the to directory in zip would have been the specified path.
107 	 */
unzipFile(File zipFile, File outputDir, String path, String[] includePatterns, String[] excludePatterns, IBackupStore store, String taskName, IProgressMonitor monitor)108 	public static File[] unzipFile(File zipFile, File outputDir, String path, String[] includePatterns,
109 			String[] excludePatterns, IBackupStore store, String taskName, IProgressMonitor monitor)
110 			throws IOException {
111 		try (InputStream in = new FileInputStream(zipFile)) {
112 			return unzipStream(in, zipFile.length(), outputDir, path, includePatterns, excludePatterns, store, taskName,
113 					monitor);
114 		} catch (IOException e) {
115 			// add the file name to the message
116 			IOException ioException = new IOException(NLS.bind(Messages.Util_Error_Unzipping, zipFile, e.getMessage()));
117 			ioException.initCause(e);
118 			throw ioException;
119 		}
120 	}
121 
122 	/**
123 	 * Unzip from an InputStream to an output directory using backup of overwritten
124 	 * files if backup store is not null.
125 	 */
unzipStream(InputStream stream, long size, File outputDir, IBackupStore store, String taskName, IProgressMonitor monitor)126 	public static File[] unzipStream(InputStream stream, long size, File outputDir, IBackupStore store, String taskName,
127 			IProgressMonitor monitor) throws IOException {
128 		return unzipStream(stream, size, outputDir, null /* path */, null /* includes */, null /* excludes */, store,
129 				taskName, monitor);
130 	}
131 
132 	/**
133 	 * Unzip from an InputStream to an output directory using backup of overwritten
134 	 * files if backup store is not null. It takes in count exclude/exclude pattern
135 	 * (that can be null, case when everything is unzipped). If a path is specified,
136 	 * the path is consider as entry point in zip, as when the to directory in zip
137 	 * would have been the specified path.
138 	 */
unzipStream(InputStream stream, long size, File outputDir, String path, String[] includePatterns, String[] excludePatterns, IBackupStore store, String taskName, IProgressMonitor monitor)139 	public static File[] unzipStream(InputStream stream, long size, File outputDir, String path,
140 			String[] includePatterns, String[] excludePatterns, IBackupStore store, String taskName,
141 			IProgressMonitor monitor) throws IOException {
142 		InputStream is = monitor == null ? stream : stream; // new ProgressMonitorInputStream(stream, size, size,
143 															// taskName, monitor); TODO Commented code
144 		try (ZipInputStream in = new ZipInputStream(new BufferedInputStream(is))) {
145 			ZipEntry ze = in.getNextEntry();
146 			if (ze == null) {
147 				// There must be at least one entry in a zip file.
148 				// When there isn't getNextEntry returns null.
149 				in.close();
150 				throw new IOException(Messages.Util_Invalid_Zip_File_Format);
151 			}
152 
153 			if (path != null && path.trim().length() == 0)
154 				path = null;
155 			Pattern pathRegex = path == null ? null : createAntStylePattern("(" + path + ")(*)"); //$NON-NLS-1$ //$NON-NLS-2$
156 
157 			Collection<Pattern> includeRegexp = new ArrayList<>();
158 			Collection<Pattern> excludeRegexp = new ArrayList<>();
159 			if (includePatterns != null) {
160 				for (String pattern : includePatterns) {
161 					if (pattern != null) {
162 						includeRegexp.add(createAntStylePattern(pattern));
163 					}
164 				}
165 			}
166 			if (excludePatterns != null) {
167 				for (String pattern : excludePatterns) {
168 					if (pattern != null) {
169 						excludeRegexp.add(createAntStylePattern(pattern));
170 					}
171 				}
172 			}
173 			ArrayList<File> unzippedFiles = new ArrayList<>();
174 			do {
175 				String name = ze.getName();
176 				if (pathRegex == null || pathRegex.matcher(name).matches()) {
177 					boolean unzip = includeRegexp.isEmpty();
178 					for (Pattern pattern : includeRegexp) {
179 						unzip = pattern.matcher(name).matches();
180 						if (unzip)
181 							break;
182 					}
183 					if (unzip && !excludeRegexp.isEmpty()) {
184 						for (Pattern pattern : excludeRegexp) {
185 							if (pattern.matcher(name).matches()) {
186 								unzip = false;
187 								break;
188 							}
189 						}
190 					}
191 					if (unzip) {
192 						if (pathRegex != null) {
193 							Matcher matcher = pathRegex.matcher(name);
194 							if (matcher.matches()) {
195 								name = matcher.group(2);
196 								if (name.startsWith("/")) //$NON-NLS-1$
197 									name = name.substring(1);
198 							}
199 						}
200 						File outFile = createSubPathFile(outputDir, name);
201 						unzippedFiles.add(outFile);
202 						if (ze.isDirectory()) {
203 							outFile.mkdirs();
204 						} else {
205 							if (outFile.exists()) {
206 								if (store != null)
207 									store.backup(outFile);
208 								else
209 									outFile.delete();
210 							} else {
211 								outFile.getParentFile().mkdirs();
212 							}
213 							try {
214 								copyStream(in, false, new FileOutputStream(outFile), true);
215 							} catch (FileNotFoundException e) {
216 								// TEMP: ignore this for now in case we're trying to replace
217 								// a running eclipse.exe
218 								// TODO: This is very questionable as it will shadow any other
219 								// issue with extraction!!
220 							}
221 							outFile.setLastModified(ze.getTime());
222 						}
223 					}
224 				}
225 				in.closeEntry();
226 			} while ((ze = in.getNextEntry()) != null);
227 			return unzippedFiles.toArray(new File[unzippedFiles.size()]);
228 		}
229 
230 	}
231 
createSubPathFile(File root, String subPath)232 	private static File createSubPathFile(File root, String subPath) throws IOException {
233 		File result = new File(root, subPath).getCanonicalFile();
234 		String resultCanonical = result.getPath();
235 		String rootCanonical = root.getCanonicalPath();
236 		if (!resultCanonical.startsWith(rootCanonical + File.separator) && !resultCanonical.equals(rootCanonical)) {
237 			throw new IOException("Invalid path: " + subPath); //$NON-NLS-1$
238 		}
239 		return result;
240 	}
241 
242 	/**
243 	 * Copy an input stream to an output stream. Optionally close the streams when
244 	 * done. Return the number of bytes written.
245 	 */
copyStream(InputStream in, boolean closeIn, OutputStream out, boolean closeOut)246 	public static int copyStream(InputStream in, boolean closeIn, OutputStream out, boolean closeOut)
247 			throws IOException {
248 		try {
249 			int written = 0;
250 			byte[] buffer = new byte[16 * 1024];
251 			int len;
252 			while ((len = in.read(buffer)) != -1) {
253 				out.write(buffer, 0, len);
254 				written += len;
255 			}
256 			return written;
257 		} finally {
258 			try {
259 				if (closeIn) {
260 					in.close();
261 				}
262 			} finally {
263 				if (closeOut) {
264 					out.close();
265 				}
266 			}
267 		}
268 	}
269 
createAntStylePattern(String pattern)270 	private static Pattern createAntStylePattern(String pattern) {
271 		StringBuffer sb = new StringBuffer();
272 		for (int c = 0; c < pattern.length(); c++) {
273 			switch (pattern.charAt(c)) {
274 			case '.':
275 				sb.append("\\."); //$NON-NLS-1$
276 				break;
277 			case '*':
278 				sb.append(".*"); //$NON-NLS-1$
279 				break;
280 			case '?':
281 				sb.append(".?"); //$NON-NLS-1$
282 				break;
283 			default:
284 				sb.append(pattern.charAt(c));
285 				break;
286 			}
287 		}
288 		String string = sb.toString();
289 		if (string.endsWith("\\..*")) { //$NON-NLS-1$
290 			sb.append("|"); //$NON-NLS-1$
291 			sb.append(string.substring(0, string.length() - 4));
292 		}
293 		return Pattern.compile(sb.toString());
294 	}
295 
296 }
297