Allow to set up plugins from file:// filesystem

This commit is contained in:
Roman Shevchenko
2011-06-24 19:46:34 +04:00
parent cb9a48ba48
commit 3fa6ce31f2
3 changed files with 108 additions and 119 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2011 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.
@@ -13,18 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* User: anna
* Date: 10-Aug-2007
*/
package com.intellij.openapi.updateSettings.impl;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.IdeaPluginDescriptorImpl;
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;
@@ -41,15 +35,17 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.io.UrlConnectionUtil;
import com.intellij.util.io.ZipUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.net.URLConnection;
/**
* @author anna
* Date: 10-Aug-2007
*/
public class PluginDownloader {
private static final Logger LOG = Logger.getInstance("#" + PluginDownloader.class.getName());
@NonNls private static final String FILENAME = "filename=";
@@ -143,12 +139,14 @@ public class PluginDownloader {
}
final BuildNumber currentBuildNumber = ApplicationInfo.getInstance().getBuild();
String sinceBuildString = descriptor.getSinceBuild();
final BuildNumber sinceBuild = StringUtil.isEmptyOrSpaces(sinceBuildString) ? null : BuildNumber.fromString(sinceBuildString, descriptor.getName());
final BuildNumber sinceBuild = StringUtil.isEmptyOrSpaces(sinceBuildString)
? null : BuildNumber.fromString(sinceBuildString, descriptor.getName());
if (sinceBuild != null && sinceBuild.compareTo(currentBuildNumber) > 0) {
return false;
}
String untilBuildString = descriptor.getUntilBuild();
final BuildNumber untilBuild = StringUtil.isEmptyOrSpaces(untilBuildString) ? null : BuildNumber.fromString(untilBuildString, descriptor.getName());
final BuildNumber untilBuild = StringUtil.isEmptyOrSpaces(untilBuildString)
? null : BuildNumber.fromString(untilBuildString, descriptor.getName());
if (untilBuild != null && untilBuild.compareTo(currentBuildNumber) < 0) {
return false;
}
@@ -190,85 +188,85 @@ public class PluginDownloader {
StartupActionScriptManager.addActionCommand(deleteTemp);
}
private File downloadPlugin(ProgressIndicator pi) throws IOException {
HttpURLConnection connection = (HttpURLConnection)new URL(myPluginUrl).openConnection();
try
{
pi.setText(IdeBundle.message("progress.connecting"));
private File downloadPlugin(final ProgressIndicator pi) throws IOException {
final 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);
InputStream is = UrlConnectionUtil.getConnectionInputStream(connection, pi);
pi.setText(IdeBundle.message("progress.connecting"));
final URLConnection connection = new URL(myPluginUrl).openConnection();
try {
if (connection instanceof HttpURLConnection) {
final int responseCode = ((HttpURLConnection)connection).getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
throw new IOException(IdeBundle.message("error.connection.failed.with.http.code.N", responseCode));
}
}
final InputStream is = UrlConnectionUtil.getConnectionInputStream(connection, pi);
if (is == null) {
throw new IOException("Failed to open connection");
}
pi.setText(IdeBundle.message("progress.downloading.plugin", getPluginName()));
final File pluginsTemp = new File(PathManager.getPluginTempPath());
if (!pluginsTemp.exists()) {
pluginsTemp.mkdirs();
}
File file = FileUtil.createTempFile(pluginsTemp, "plugin", "download", true, false);
int responseCode = connection.getResponseCode();
switch (responseCode) {
case HttpURLConnection.HTTP_OK:
break;
default:
// some problems
throw new IOException(IdeBundle.message("error.connection.failed.with.http.code.N", responseCode));
}
pi.setIndeterminate(connection.getContentLength() == -1);
OutputStream fos = null;
try {
fos = new BufferedOutputStream(new FileOutputStream(file, false));
StreamUtil.copyStreamContent(is, fos);
}
finally {
if (fos != null) {
final OutputStream fos = new BufferedOutputStream(new FileOutputStream(file, false));
try {
StreamUtil.copyStreamContent(is, fos);
}
finally {
fos.close();
}
}
finally {
is.close();
}
if (myFileName == null) {
String contentDisposition = connection.getHeaderField("Content-Disposition");
if (contentDisposition == null || contentDisposition.indexOf(FILENAME) < 0) {
// try to find filename in URL
String usedURL = connection.getURL().toString();
int startPos = usedURL.lastIndexOf("/");
myFileName = usedURL.substring(startPos + 1);
if (myFileName.length() == 0 || myFileName.contains("?")) {
myFileName = myPluginUrl.substring(myPluginUrl.lastIndexOf("/") + 1);
}
}
else {
int startIdx = contentDisposition.indexOf(FILENAME);
myFileName = contentDisposition.substring(startIdx + FILENAME.length(), contentDisposition.length());
// according to the HTTP spec, the filename is a quoted string, but some servers don't quote it
// for example: http://www.jspformat.com/Download.do?formAction=d&id=8
if (myFileName.startsWith("\"") && myFileName.endsWith("\"")) {
myFileName = myFileName.substring(1, myFileName.length()-1);
}
if (myFileName.indexOf('\\') >= 0 || myFileName.indexOf('/') >= 0 || myFileName.indexOf(File.separatorChar) >= 0 ||
myFileName.indexOf('\"') >= 0) {
// invalid path name passed by the server - fail to download
FileUtil.delete(file);
throw new IOException("Invalid filename returned by server");
}
}
if (myFileName == null) {
guessFileName(connection, file);
}
File newFile = new File (file.getParentFile(), myFileName);
final File newFile = new File(file.getParentFile(), myFileName);
FileUtil.rename(file, newFile);
return newFile;
}
finally {
connection.disconnect();
if (connection instanceof HttpURLConnection) {
((HttpURLConnection)connection).disconnect();
}
}
}
private void guessFileName(final URLConnection connection, final File file) throws IOException {
String contentDisposition = connection.getHeaderField("Content-Disposition");
if (contentDisposition == null || !contentDisposition.contains(FILENAME)) {
// try to find filename in URL
String usedURL = connection.getURL().toString();
int startPos = usedURL.lastIndexOf("/");
myFileName = usedURL.substring(startPos + 1);
if (myFileName.length() == 0 || myFileName.contains("?")) {
myFileName = myPluginUrl.substring(myPluginUrl.lastIndexOf("/") + 1);
}
}
else {
int startIdx = contentDisposition.indexOf(FILENAME);
myFileName = contentDisposition.substring(startIdx + FILENAME.length(), contentDisposition.length());
// according to the HTTP spec, the filename is a quoted string, but some servers don't quote it
// for example: http://www.jspformat.com/Download.do?formAction=d&id=8
if (myFileName.startsWith("\"") && myFileName.endsWith("\"")) {
myFileName = myFileName.substring(1, myFileName.length() - 1);
}
if (myFileName.indexOf('\\') >= 0 || myFileName.indexOf('/') >= 0 || myFileName.indexOf(File.separatorChar) >= 0 ||
myFileName.indexOf('\"') >= 0) {
// invalid path name passed by the server - fail to download
FileUtil.delete(file);
throw new IOException("Invalid filename returned by server");
}
}
}
@@ -293,19 +291,4 @@ public class PluginDownloader {
public String getPluginVersion() {
return myPluginVersion;
}
/**
* Updates given plugin from Repository
* @param pluginId given plugin id
* @param pluginVersion available version or null if plugin must be uploaded even if current version is greater than uploading
* @throws IOException
*/
public static void updateFromRepository(final String pluginId, final @Nullable String pluginVersion) throws IOException {
@NonNls final String url =
RepositoryHelper.DOWNLOAD_URL + URLEncoder.encode(pluginId, "UTF8") + "&build=" + ApplicationInfo.getInstance().getBuild().asString();
final PluginDownloader downloader = new PluginDownloader(pluginId, url, pluginVersion);
if (downloader.prepareToInstall()) {
downloader.install();
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
* Copyright 2000-2011 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.
@@ -13,15 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created by IntelliJ IDEA.
* User: mike
* Date: Oct 31, 2002
* Time: 6:33:01 PM
* To change template for new class use
* Code Style | Class Templates options (Tools | IDE Options).
*/
package com.intellij.openapi.updateSettings.impl;
import com.intellij.ide.IdeBundle;
@@ -44,9 +35,9 @@ import com.intellij.openapi.util.JDOMUtil;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.ex.http.HttpFileSystem;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.VirtualFileSystem;
import com.intellij.util.PlatformUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.io.UrlConnectionUtil;
@@ -72,15 +63,20 @@ import java.util.concurrent.TimeoutException;
/**
* XML sample:
* <pre>{@code
* <idea>
* <build>456</build>
* <version>4.5.2</version>
* <title>New Intellij IDEA Version</title>
* <message>
* New version of IntelliJ IDEA is available.
* Please visit http://www.intellij.com/ for more info.
* </message>
* <build>456</build>
* <version>4.5.2</version>
* <title>New Intellij IDEA Version</title>
* <message>
* New version of IntelliJ IDEA is available.
* Please visit http://www.intellij.com/ for more info.
* </message>
* </idea>
* }</pre>
*
* @author mike
* Date: Oct 31, 2002
*/
public final class UpdateChecker {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.updateSettings.impl.UpdateChecker");
@@ -110,6 +106,7 @@ public final class UpdateChecker {
private static class StringHolder {
private static final String UPDATE_URL = ApplicationInfoEx.getInstanceEx().getUpdateUrls().getCheckingUrl();
private static final String PATCHES_URL = ApplicationInfoEx.getInstanceEx().getUpdateUrls().getPatchesUrl();
private StringHolder() { }
}
private static String getUpdateUrl() {
@@ -190,12 +187,14 @@ public final class UpdateChecker {
public static boolean checkPluginsHost(final String host, final List<PluginDownloader> downloaded) throws Exception {
final Document document = loadVersionInfo(host);
if (document == null) return false;
boolean success = true;
for (Object plugin : document.getRootElement().getChildren("plugin")) {
Element pluginElement = (Element)plugin;
final Element pluginElement = (Element)plugin;
final String pluginId = pluginElement.getAttributeValue("id");
String pluginUrl = pluginElement.getAttributeValue("url");
final String pluginUrl = pluginElement.getAttributeValue("url");
final String pluginVersion = pluginElement.getAttributeValue("version");
if (pluginId == null) {
LOG.info("plugin id should not be null");
success = false;
@@ -208,19 +207,25 @@ public final class UpdateChecker {
continue;
}
if (!pluginUrl.startsWith(HttpFileSystem.PROTOCOL)) {
final HttpFileSystem fileSystem = HttpFileSystem.getInstance();
final VirtualFile hostFile = fileSystem.findFileByPath(VfsUtil.urlToPath(host));
LOG.assertTrue(hostFile != null);
final VirtualFile pluginByRelativePath = findPluginByRelativePath(hostFile.getParent(), pluginUrl, fileSystem);
if (pluginByRelativePath != null) {
pluginUrl = pluginByRelativePath.getUrl();
final VirtualFileManager fileManager = VirtualFileManager.getInstance();
VirtualFile pluginFile = fileManager.findFileByUrl(pluginUrl);
if (pluginFile == null) {
final VirtualFile hostFile = fileManager.findFileByUrl(host);
if (hostFile == null) {
LOG.error("can't find file by url '" + host + "'");
success = false;
break;
}
pluginFile = findPluginByRelativePath(hostFile.getParent(), pluginUrl, hostFile.getFileSystem());
if (pluginFile == null) {
LOG.error("can't find '" + pluginUrl + "' relative to '" + host + "'");
success = false;
continue;
}
}
final String finalPluginUrl = pluginUrl;
Runnable updatePluginRunnable = new Runnable() {
final String finalPluginUrl = pluginFile.getUrl();
final Runnable updatePluginRunnable = new Runnable() {
public void run() {
try {
final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
@@ -248,9 +253,9 @@ public final class UpdateChecker {
}
@Nullable
public static VirtualFile findPluginByRelativePath(VirtualFile hostFile,
@NotNull @NonNls String relPath,
final HttpFileSystem fileSystem) {
public static VirtualFile findPluginByRelativePath(@NotNull final VirtualFile hostFile,
@NotNull @NonNls final String relPath,
@NotNull final VirtualFileSystem fileSystem) {
if (relPath.length() == 0) return hostFile;
int index = relPath.indexOf('/');
if (index < 0) index = relPath.length();
@@ -512,6 +512,7 @@ plugin.status.installed=Installed
progress.waiting.for.reply.from.plugin.manager=Waiting for reply from {0}
progress.connecting=Connecting...
progress.downloading.plugin=Downloading plugin ''{0}''
error.cannot.create.temp.dir=Unable to create temp directory ''{0}''
error.connection.failed.with.http.code.N=Connection failed with HTTP code {0}
progress.connecting.to.plugin.manager=Connecting to {0}
label.plugin.vendor.email=E-mail: