/* * Copyright 2000-2013 JetBrains s.r.o. * * 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 com.intellij.openapi.updateSettings.impl; import com.intellij.ide.IdeBundle; import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.InstalledPluginsTableModel; import com.intellij.ide.plugins.PluginManager; import com.intellij.ide.plugins.RepositoryHelper; import com.intellij.ide.startup.StartupActionScriptManager; import com.intellij.openapi.application.ApplicationInfo; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.StreamUtil; import com.intellij.util.io.HttpRequests; import com.intellij.util.io.ZipUtil; import consulo.ide.updateSettings.UpdateSettings; import consulo.ide.updateSettings.impl.PlatformOrPluginUpdateChecker; import consulo.util.io2.PathUtil; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; /** * @author anna * @since 10-Aug-2007 */ public class PluginDownloader { private static final Logger LOG = Logger.getInstance(PluginDownloader.class); @NotNull public static PluginDownloader createDownloader(@NotNull IdeaPluginDescriptor descriptor, boolean viaUpdate) { return createDownloader(descriptor, null, viaUpdate); } @NotNull public static PluginDownloader createDownloader(@NotNull IdeaPluginDescriptor descriptor, @Nullable String platformVersion, boolean viaUpdate) { String url = RepositoryHelper .buildUrlForDownload(UpdateSettings.getInstance().getChannel(), descriptor.getPluginId().toString(), platformVersion, false, viaUpdate); return new PluginDownloader(descriptor, url); } private final PluginId myPluginId; private String myPluginUrl; private String myPluginName; private File myFile; private File myOldFile; private String myDescription; private final IdeaPluginDescriptor myDescriptor; private boolean myIsPlatform; public PluginDownloader(@NotNull IdeaPluginDescriptor pluginDescriptor, @NotNull String pluginUrl) { myPluginId = pluginDescriptor.getPluginId(); myDescriptor = pluginDescriptor; myPluginUrl = pluginUrl; myPluginName = pluginDescriptor.getName(); myIsPlatform = PlatformOrPluginUpdateChecker.getPlatformPluginId() == pluginDescriptor.getPluginId(); } public boolean prepareToInstall(ProgressIndicator pi) throws IOException { IdeaPluginDescriptor descriptor = null; if (!Boolean.getBoolean(StartupActionScriptManager.STARTUP_WIZARD_MODE) && PluginManager.isPluginInstalled(myPluginId)) { //store old plugins file descriptor = PluginManager.getPlugin(myPluginId); myOldFile = descriptor.getPath(); } // download plugin String errorMessage = IdeBundle.message("unknown.error"); try { myFile = downloadPlugin(pi); } catch (IOException ex) { myFile = null; errorMessage = ex.getMessage(); } if (myFile == null) { final String text = IdeBundle.message("error.plugin.was.not.installed", getPluginName(), errorMessage); final String title = IdeBundle.message("title.failed.to.download"); ApplicationManager.getApplication().invokeLater(() -> Messages.showErrorDialog(text, title)); return false; } return !InstalledPluginsTableModel.wasUpdated(myDescriptor.getPluginId()); } public void install(boolean deleteTempFile) throws IOException { install(null, deleteTempFile); } public void install(@Nullable ProgressIndicator indicator, boolean deleteTempFile) throws IOException { LOG.assertTrue(myFile != null); if (myOldFile != null) { // add command to delete the 'action script' file StartupActionScriptManager.ActionCommand deleteOld = new StartupActionScriptManager.DeleteCommand(myOldFile); StartupActionScriptManager.addActionCommand(deleteOld); } if (myIsPlatform) { if (indicator != null) { indicator.setText2(IdeBundle.message("progress.extracting.platform")); } String prefix = SystemInfo.isMac ? "Consulo.app/Contents/platform/" : "Consulo/platform/"; File platformDirectory = PathManager.getExternalPlatformDirectory(); try (TarArchiveInputStream ais = new TarArchiveInputStream(new GzipCompressorInputStream(new FileInputStream(myFile)))) { TarArchiveEntry tempEntry; while ((tempEntry = (TarArchiveEntry)ais.getNextEntry()) != null) { String name = tempEntry.getName(); // we interest only in new build if (name.startsWith(prefix) && name.length() != prefix.length()) { File targetFile = new File(platformDirectory, name.substring(prefix.length(), name.length())); if (tempEntry.isDirectory()) { FileUtil.createDirectory(targetFile); } else if (tempEntry.isSymbolicLink()) { FileUtil.createParentDirs(targetFile); Files.createSymbolicLink(targetFile.toPath(), Paths.get(tempEntry.getLinkName())); } else { FileUtil.createParentDirs(targetFile); try (OutputStream stream = new FileOutputStream(targetFile)) { StreamUtil.copyStreamContent(ais, stream); } targetFile.setLastModified(tempEntry.getLastModifiedDate().getTime()); // it's a fix for TarArchiveEntry.DEFAULT_FILE_MODE if (tempEntry.getMode() == 0b111_101_101) { PathUtil.setPosixFilePermissions(targetFile.toPath(), PathUtil.convertModeToFilePermissions(tempEntry.getMode())); } } } } } // at start - delete old version, after restart. On mac - we can't delete boot build String buildNumber = ApplicationInfo.getInstance().getBuild().asString(); File oldBuild = new File(platformDirectory, "build" + buildNumber); if (oldBuild.exists()) { StartupActionScriptManager.ActionCommand deleteTemp = new StartupActionScriptManager.DeleteCommand(oldBuild); StartupActionScriptManager.addActionCommand(deleteTemp); } FileUtil.delete(myFile); myFile = null; } else { install(myFile, getPluginName(), deleteTempFile); } } public static void install(final File fromFile, final String pluginName, boolean deleteFromFile) throws IOException { // add command to unzip file to the IDEA/plugins path String unzipPath; if (ZipUtil.isZipContainsFolder(fromFile)) { unzipPath = PathManager.getPluginsPath(); } else { unzipPath = PathManager.getPluginsPath() + File.separator + pluginName; } StartupActionScriptManager.ActionCommand unzip = new StartupActionScriptManager.UnzipCommand(fromFile, new File(unzipPath)); StartupActionScriptManager.addActionCommand(unzip); // add command to remove temp plugin file if (deleteFromFile) { StartupActionScriptManager.ActionCommand deleteTemp = new StartupActionScriptManager.DeleteCommand(fromFile); StartupActionScriptManager.addActionCommand(deleteTemp); } } private File downloadPlugin(final ProgressIndicator indicator) throws IOException { File pluginsTemp = new File(PathManager.getPluginTempPath()); if (!pluginsTemp.exists() && !pluginsTemp.mkdirs()) { throw new IOException(IdeBundle.message("error.cannot.create.temp.dir", pluginsTemp)); } final File file = FileUtil.createTempFile(pluginsTemp, "plugin_", "_download", true, false); indicator.checkCanceled(); if (myIsPlatform) { indicator.setText2(IdeBundle.message("progress.downloading.platform")); } else { indicator.setText2(IdeBundle.message("progress.downloading.plugin", getPluginName())); } return HttpRequests.request(myPluginUrl).gzip(false).connect(request -> { request.saveToFile(file, indicator); String fileName = getFileName(); File newFile = new File(file.getParentFile(), fileName); FileUtil.rename(file, newFile); return newFile; }); } @NotNull public String getFileName() { if (myIsPlatform) { return "platform.tar.gz"; } else { return myPluginId + ".zip"; } } public String getPluginName() { if (myPluginName == null) { myPluginName = FileUtil.getNameWithoutExtension(getFileName()); } return myPluginName; } public void setDescription(String description) { myDescription = description; } public String getDescription() { return myDescription; } public IdeaPluginDescriptor getDescriptor() { return myDescriptor; } }