/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Yahoo! Inc., Seiji Sogabe, * Andrew Bayer * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.model; import hudson.PluginWrapper; import hudson.PluginManager; import hudson.model.UpdateCenter.UpdateCenterJob; import hudson.lifecycle.Lifecycle; import hudson.util.TextFile; import hudson.util.VersionNumber; import static hudson.util.TimeUnit2.DAYS; import net.sf.json.JSONObject; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerResponse; import org.jvnet.hudson.crypto.CertificateUtil; import org.jvnet.hudson.crypto.SignatureOutputStream; import org.apache.commons.io.output.NullOutputStream; import org.apache.commons.io.output.TeeOutputStream; import java.io.File; import java.io.IOException; import java.io.ByteArrayInputStream; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.Set; import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; import java.security.GeneralSecurityException; import java.security.MessageDigest; import java.security.DigestOutputStream; import java.security.Signature; import java.security.cert.X509Certificate; import java.security.cert.CertificateFactory; import java.security.cert.TrustAnchor; import com.trilead.ssh2.crypto.Base64; import javax.servlet.ServletContext; /** * Source of the update center information, like "http://hudson-ci.org/update-center.json" * *
* Hudson can have multiple {@link UpdateSite}s registered in the system, so that it can pick up plugins * from different locations. * * @author Andrew Bayer * @author Kohsuke Kawaguchi * @since 1.MULTIUPDATE */ public class UpdateSite { /** * What's the time stamp of data file? */ private transient long dataTimestamp = -1; /** * When was the last time we asked a browser to check the data for us? * *
* There's normally some delay between when we send HTML that includes the check code,
* until we get the data back, so this variable is used to avoid asking too many browseres
* all at once.
*/
private transient volatile long lastAttempt = -1;
/**
* ID string for this update source.
*/
private final String id;
/**
* Path to update-center.json, like http://hudson-ci.org/update-center.json.
*/
private final String url;
public UpdateSite(String id, String url) {
this.id = id;
this.url = url;
}
/**
* When read back from XML, initialize them back to -1.
*/
private Object readResolve() {
dataTimestamp = lastAttempt = -1;
return this;
}
/**
* Get ID string.
*/
public String getId() {
return id;
}
public long getDataTimestamp() {
return dataTimestamp;
}
/**
* This is the endpoint that receives the update center data file from the browser.
*/
public void doPostBack(@QueryParameter String json) throws IOException, GeneralSecurityException {
dataTimestamp = System.currentTimeMillis();
JSONObject o = JSONObject.fromObject(json);
int v = o.getInt("updateCenterVersion");
if(v !=1) {
LOGGER.warning("Unrecognized update center version: "+v);
return;
}
if (signatureCheck)
verifySignature(o);
LOGGER.info("Obtained the latest update center data file for UpdateSource "+ id);
getDataFile().write(json);
}
/**
* Verifies the signature in the update center data file.
*/
private boolean verifySignature(JSONObject o) throws GeneralSecurityException, IOException {
JSONObject signature = o.getJSONObject("signature");
if (signature.isNullObject()) {
LOGGER.severe("No signature block found");
return false;
}
o.remove("signature");
List
* beware of XSS vulnerability since this data comes from Wiki
*/
public final String title;
/**
* Optional excerpt string.
*/
public final String excerpt;
/**
* Optional version # from which this plugin release is configuration-compatible.
*/
public final String compatibleSinceVersion;
@DataBoundConstructor
public Plugin(String sourceId, JSONObject o) {
super(sourceId, o);
this.wiki = get(o,"wiki");
this.title = get(o,"title");
this.excerpt = get(o,"excerpt");
this.compatibleSinceVersion = get(o,"compatibleSinceVersion");
}
private String get(JSONObject o, String prop) {
if(o.has(prop))
return o.getString(prop);
else
return null;
}
public String getDisplayName() {
if(title!=null) return title;
return name;
}
/**
* If some version of this plugin is currently installed, return {@link PluginWrapper}.
* Otherwise null.
*/
public PluginWrapper getInstalled() {
PluginManager pm = Hudson.getInstance().getPluginManager();
return pm.getPlugin(name);
}
/**
* If the plugin is already installed, and the new version of the plugin has a "compatibleSinceVersion"
* value (i.e., it's only directly compatible with that version or later), this will check to
* see if the installed version is older than the compatible-since version. If it is older, it'll return false.
* If it's not older, or it's not installed, or it's installed but there's no compatibleSinceVersion
* specified, it'll return true.
*/
public boolean isCompatibleWithInstalledVersion() {
PluginWrapper installedVersion = getInstalled();
if (installedVersion != null) {
if (compatibleSinceVersion != null) {
if (new VersionNumber(installedVersion.getVersion())
.isOlderThan(new VersionNumber(compatibleSinceVersion))) {
return false;
}
}
}
return true;
}
/**
* @deprecated as of 1.326
* Use {@link #deploy()}.
*/
public void install() {
deploy();
}
/**
* Schedules the installation of this plugin.
*
*
* This is mainly intended to be called from the UI. The actual installation work happens
* asynchronously in another thread.
*/
public Future