() {
+ private static final long serialVersionUID = 1L;
+
+ public Void call() throws IOException {
+ final OutputStream out = new java.util.zip.GZIPOutputStream(p.getOut(), 8192);
+ RandomAccessFile raf = null;
+ try {
+ raf = new RandomAccessFile(new File(remote), "r");
+ raf.seek(offset);
+ byte[] buf = new byte[8192];
+ int len;
+ while ((len = raf.read(buf)) >= 0) {
+ out.write(buf, 0, len);
+ }
+ return null;
+ } finally {
+ IOUtils.closeQuietly(out);
+ if (raf != null) {
+ try {
+ raf.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ }
+ });
+
+ return new java.util.zip.GZIPInputStream(p.getIn());
+ }
+
/**
* Reads this file into a string, by using the current system encoding.
*/
diff --git a/core/src/main/java/hudson/WebAppMain.java b/core/src/main/java/hudson/WebAppMain.java
index 1f332e9751..11d438d5ab 100644
--- a/core/src/main/java/hudson/WebAppMain.java
+++ b/core/src/main/java/hudson/WebAppMain.java
@@ -56,6 +56,7 @@ import javax.xml.transform.TransformerFactoryConfigurationError;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Date;
@@ -116,6 +117,8 @@ public class WebAppMain implements ServletContextListener {
installLogger();
+ markCookieAsHttpOnly(context);
+
final FileAndDescription describedHomeDir = getHomeDir(event);
home = describedHomeDir.file.getAbsoluteFile();
home.mkdirs();
@@ -251,6 +254,31 @@ public class WebAppMain implements ServletContextListener {
}
}
+ /**
+ * Set the session cookie as HTTP only.
+ *
+ * @see discussion of this topic in OWASP
+ */
+ private void markCookieAsHttpOnly(ServletContext context) {
+ try {
+ Method m;
+ try {
+ m = context.getClass().getMethod("getSessionCookieConfig");
+ } catch (NoSuchMethodException x) { // 3.0+
+ LOGGER.log(Level.FINE, "Failed to set secure cookie flag", x);
+ return;
+ }
+ Object sessionCookieConfig = m.invoke(context);
+
+ // not exposing session cookie to JavaScript to mitigate damage caused by XSS
+ Class scc = Class.forName("javax.servlet.SessionCookieConfig");
+ Method setHttpOnly = scc.getMethod("setHttpOnly",boolean.class);
+ setHttpOnly.invoke(sessionCookieConfig,true);
+ } catch (Exception e) {
+ LOGGER.log(Level.WARNING, "Failed to set HTTP-only cookie flag", e);
+ }
+ }
+
public void joinInit() throws InterruptedException {
initThread.join();
}
diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java
index 987f6a2033..9712ca58d3 100644
--- a/core/src/main/java/hudson/model/Computer.java
+++ b/core/src/main/java/hudson/model/Computer.java
@@ -24,6 +24,8 @@
*/
package hudson.model;
+import edu.umd.cs.findbugs.annotations.OverrideMustInvoke;
+import edu.umd.cs.findbugs.annotations.When;
import hudson.EnvVars;
import hudson.Launcher.ProcStarter;
import hudson.Util;
@@ -108,14 +110,14 @@ import static javax.servlet.http.HttpServletResponse.*;
*
*
* {@link Executor}s on one {@link Computer} are transparently interchangeable
- * (that is the definition of {@link Computer}.)
+ * (that is the definition of {@link Computer}).
*
*
- * This object is related to {@link Node} but they have some significant difference.
+ * This object is related to {@link Node} but they have some significant differences.
* {@link Computer} primarily works as a holder of {@link Executor}s, so
* if a {@link Node} is configured (probably temporarily) with 0 executors,
* you won't have a {@link Computer} object for it (except for the master node,
- * which always get its {@link Computer} in case we have no static executors and
+ * which always gets its {@link Computer} in case we have no static executors and
* we need to run a {@link FlyweightTask} - see JENKINS-7291 for more discussion.)
*
* Also, even if you remove a {@link Node}, it takes time for the corresponding
@@ -124,8 +126,8 @@ import static javax.servlet.http.HttpServletResponse.*;
* remains intact, while all the {@link Node} objects will go away.
*
*
- * This object also serves UI (since {@link Node} is an interface and can't have
- * related side pages.)
+ * This object also serves UI (unlike {@link Node}), and can be used along with
+ * {@link TransientComputerActionFactory} to add {@link Action}s to {@link Computer}s.
*
* @author Kohsuke Kawaguchi
*/
@@ -207,7 +209,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
* The method also creates a log directory if required.
* @see #relocateOldLogs()
*/
- protected File getLogFile() {
+ public File getLogFile() {
File dir = new File(Jenkins.getInstance().getRootDir(),"logs/slaves/"+nodeName);
dir.mkdirs();
return new File(dir,"slave.log");
@@ -1318,9 +1320,13 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
* scheduling that does not overlap with being offline.
*
* @return {@code true} if the computer is accepting tasks
+ * @see hudson.slaves.RetentionStrategy#isAcceptingTasks(Computer)
+ * @see hudson.model.Node#isAcceptingTasks()
*/
+ @OverrideMustInvoke(When.ANYTIME)
public boolean isAcceptingTasks() {
- return true;
+ final Node node = getNode();
+ return getRetentionStrategy().isAcceptingTasks(this) && (node == null || node.isAcceptingTasks());
}
/**
diff --git a/core/src/main/java/hudson/model/FileParameterValue.java b/core/src/main/java/hudson/model/FileParameterValue.java
index c9303fabe5..930eb41fd8 100644
--- a/core/src/main/java/hudson/model/FileParameterValue.java
+++ b/core/src/main/java/hudson/model/FileParameterValue.java
@@ -140,7 +140,7 @@ public class FileParameterValue extends ParameterValue {
return new BuildWrapper() {
@Override
public Environment setUp(AbstractBuild build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException {
- if (!StringUtils.isEmpty(location)) {
+ if (!StringUtils.isEmpty(location) && !StringUtils.isEmpty(file.getName())) {
listener.getLogger().println("Copying file to "+location);
FilePath locationFilePath = build.getWorkspace().child(location);
locationFilePath.getParent().mkdirs();
@@ -162,7 +162,8 @@ public class FileParameterValue extends ParameterValue {
}
/**
- * In practice this will always be false, since location should be unique.
+ * Compares file parameters (existing files will be considered as different).
+ * @since 1.586 Function has been modified in order to avoid JENKINS-19017 issue (wrong merge of builds in the queue).
*/
@Override
public boolean equals(Object obj) {
@@ -173,12 +174,13 @@ public class FileParameterValue extends ParameterValue {
if (getClass() != obj.getClass())
return false;
FileParameterValue other = (FileParameterValue) obj;
- if (location == null) {
- if (other.location != null)
- return false;
- } else if (!location.equals(other.location))
- return false;
- return true;
+
+ if (location == null && other.location == null)
+ return true; // Consider null parameters as equal
+
+ //TODO: check fingerprints or checksums to improve the behavior (JENKINS-25211)
+ // Return false even if files are equal
+ return false;
}
@Override
diff --git a/core/src/main/java/hudson/model/Node.java b/core/src/main/java/hudson/model/Node.java
index 03f2c2413e..2175ee215c 100644
--- a/core/src/main/java/hudson/model/Node.java
+++ b/core/src/main/java/hudson/model/Node.java
@@ -69,7 +69,7 @@ import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.export.Exported;
/**
- * Base type of Jenkins slaves (although in practice, you probably extend {@link Slave} to define a new slave type.)
+ * Base type of Jenkins slaves (although in practice, you probably extend {@link Slave} to define a new slave type).
*
*
* As a special case, {@link Jenkins} extends from here.
@@ -77,6 +77,10 @@ import org.kohsuke.stapler.export.Exported;
*
* Nodes are persisted objects that capture user configurations, and instances get thrown away and recreated whenever
* the configuration changes. Running state of nodes are captured by {@link Computer}s.
+ *
+ *
+ * There is no URL binding for {@link Node}. {@link Computer} and {@link TransientComputerActionFactory} must
+ * be used to associate new {@link Action}s to slaves.
*
* @author Kohsuke Kawaguchi
* @see NodeMonitor
@@ -190,6 +194,20 @@ public abstract class Node extends AbstractModelObject implements Reconfigurable
*/
protected abstract Computer createComputer();
+ /**
+ * Returns {@code true} if the node is accepting tasks. Needed to allow slaves programmatic suspension of task
+ * scheduling that does not overlap with being offline. Called by {@link Computer#isAcceptingTasks()}.
+ * This method is distinct from {@link Computer#isAcceptingTasks()} as sometimes the {@link Node} concrete
+ * class may not have control over the {@link hudson.model.Computer} concrete class associated with it.
+ *
+ * @return {@code true} if the node is accepting tasks.
+ * @see Computer#isAcceptingTasks()
+ * @since 1.586
+ */
+ public boolean isAcceptingTasks() {
+ return true;
+ }
+
/**
* Let Nodes be aware of the lifecycle of their own {@link Computer}.
*/
diff --git a/core/src/main/java/hudson/model/User.java b/core/src/main/java/hudson/model/User.java
index 46ab4365c3..c4190ee4af 100644
--- a/core/src/main/java/hudson/model/User.java
+++ b/core/src/main/java/hudson/model/User.java
@@ -81,6 +81,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
/**
* Represents a user.
@@ -191,11 +192,11 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
return id;
}
- public String getUrl() {
+ public @Nonnull String getUrl() {
return "user/"+Util.rawEncode(idStrategy().keyFor(id));
}
- public String getSearchUrl() {
+ public @Nonnull String getSearchUrl() {
return "/user/"+Util.rawEncode(idStrategy().keyFor(id));
}
@@ -203,24 +204,22 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
* The URL of the user page.
*/
@Exported(visibility=999)
- public String getAbsoluteUrl() {
+ public @Nonnull String getAbsoluteUrl() {
return Jenkins.getInstance().getRootUrl()+getUrl();
}
/**
* Gets the human readable name of this user.
* This is configurable by the user.
- *
- * @return
- * never null.
*/
@Exported(visibility=999)
- public String getFullName() {
+ public @Nonnull String getFullName() {
return fullName;
}
/**
- * Sets the human readable name of thie user.
+ * Sets the human readable name of the user.
+ * If the input parameter is empty, the user's ID will be set.
*/
public void setFullName(String name) {
if(Util.fixEmptyAndTrim(name)==null) name=id;
@@ -228,7 +227,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
}
@Exported
- public String getDescription() {
+ public @CheckForNull String getDescription() {
return description;
}
@@ -242,7 +241,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
/**
* Updates the user object by adding a property.
*/
- public synchronized void addProperty(UserProperty p) throws IOException {
+ public synchronized void addProperty(@Nonnull UserProperty p) throws IOException {
UserProperty old = getProperty(p.getClass());
List ps = new ArrayList(properties);
if(old!=null)
@@ -284,7 +283,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
* If this user is not a valid user in the backend {@link SecurityRealm}.
* @since 1.419
*/
- public Authentication impersonate() throws UsernameNotFoundException {
+ public @Nonnull Authentication impersonate() throws UsernameNotFoundException {
try {
UserDetails u = new ImpersonatingUserDetailsService(
Jenkins.getInstance().getSecurityRealm().getSecurityComponents().userDetails).loadUserByUsername(id);
@@ -332,9 +331,11 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
* (by creating a new {@link User} object if none exists.)
* If false, this method will return null if {@link User} object
* with the given name doesn't exist.
+ * @return Requested user. May be {@code null} if a user does not exist and
+ * {@code create} is false.
* @deprecated use {@link User#get(String, boolean, java.util.Map)}
*/
- public static User get(String idOrFullName, boolean create) {
+ public static @Nullable User get(String idOrFullName, boolean create) {
return get(idOrFullName, create, Collections.emptyMap());
}
@@ -350,8 +351,12 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
* @param context
* contextual environment this user idOfFullName was retrieved from,
* that can help resolve the user ID
+ *
+ * @return
+ * An existing or created user. May be {@code null} if a user does not exist and
+ * {@code create} is false.
*/
- public static User get(String idOrFullName, boolean create, Map context) {
+ public static @Nullable User get(String idOrFullName, boolean create, Map context) {
if(idOrFullName==null)
return null;
@@ -369,14 +374,19 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
}
}
// DefaultUserCanonicalIdResolver will always return a non-null id if all other CanonicalIdResolver failed
-
+ if (id == null) {
+ throw new IllegalStateException("The user id should be always non-null thanks to DefaultUserCanonicalIdResolver");
+ }
return getOrCreate(id, idOrFullName, create);
}
/**
- * retrieve a user by its ID, and create a new one if requested
+ * Retrieve a user by its ID, and create a new one if requested.
+ * @return
+ * An existing or created user. May be {@code null} if a user does not exist and
+ * {@code create} is false.
*/
- private static User getOrCreate(String id, String fullName, boolean create) {
+ private static @Nullable User getOrCreate(@Nonnull String id, @Nonnull String fullName, boolean create) {
String idkey = idStrategy().keyFor(id);
byNameLock.readLock().lock();
@@ -468,7 +478,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
/**
* Gets all the users.
*/
- public static Collection getAll() {
+ public static @Nonnull Collection getAll() {
final IdStrategy strategy = idStrategy();
if(System.currentTimeMillis() -lastScanned>10000) {
// occasionally scan the file system to check new users
@@ -555,12 +565,12 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
/**
* Returns the user name.
*/
- public String getDisplayName() {
+ public @Nonnull String getDisplayName() {
return getFullName();
}
/** true if {@link AbstractBuild#hasParticipant} or {@link hudson.model.Cause.UserIdCause} */
- private boolean relatedTo(AbstractBuild,?> b) {
+ private boolean relatedTo(@Nonnull AbstractBuild,?> b) {
if (b.hasParticipant(this)) {
return true;
}
@@ -580,7 +590,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
* by the timestamp order.
*/
@WithBridgeMethods(List.class)
- public RunList getBuilds() {
+ public @Nonnull RunList getBuilds() {
return new RunList>(Jenkins.getInstance().getAllItems(Job.class)).filter(new Predicate>() {
@Override public boolean apply(Run,?> r) {
return r instanceof AbstractBuild && relatedTo((AbstractBuild,?>) r);
@@ -592,7 +602,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
* Gets all the {@link AbstractProject}s that this user has committed to.
* @since 1.191
*/
- public Set> getProjects() {
+ public @Nonnull Set> getProjects() {
Set> r = new HashSet>();
for (AbstractProject,?> p : Jenkins.getInstance().getAllItems(AbstractProject.class))
if(p.hasParticipant(this))
diff --git a/core/src/main/java/hudson/slaves/NodeProperty.java b/core/src/main/java/hudson/slaves/NodeProperty.java
index 42da21efeb..68c50230c4 100644
--- a/core/src/main/java/hudson/slaves/NodeProperty.java
+++ b/core/src/main/java/hudson/slaves/NodeProperty.java
@@ -54,8 +54,11 @@ import javax.annotation.Nonnull;
* Extensible property of {@link Node}.
*
*
- * Plugins can contribute this extension point to add additional data or UI actions to {@link Node}.
+ * Plugins can contribute this extension point to add additional data to {@link Node}.
* {@link NodeProperty}s show up in the configuration screen of a node, and they are persisted with the {@link Node} object.
+ *
+ *
+ * To add UI action to {@link Node}s, i.e. a new link shown in the left side menu on a node page (./computer/<a node>), see instead {@link hudson.model.TransientComputerActionFactory}.
*
*
*
Views
diff --git a/core/src/main/java/hudson/slaves/NodeProvisioner.java b/core/src/main/java/hudson/slaves/NodeProvisioner.java
index 5bb95d18fa..c63a16180a 100644
--- a/core/src/main/java/hudson/slaves/NodeProvisioner.java
+++ b/core/src/main/java/hudson/slaves/NodeProvisioner.java
@@ -182,7 +182,7 @@ public class NodeProvisioner {
cl.onComplete(f,node);
hudson.addNode(node);
- LOGGER.info(f.displayName+" provisioningE successfully completed. We have now "+hudson.getComputers().length+" computer(s)");
+ LOGGER.info(f.displayName+" provisioning successfully completed. We have now "+hudson.getComputers().length+" computer(s)");
} catch (InterruptedException e) {
throw new AssertionError(e); // since we confirmed that the future is already done
} catch (ExecutionException e) {
diff --git a/core/src/main/java/hudson/slaves/RetentionStrategy.java b/core/src/main/java/hudson/slaves/RetentionStrategy.java
index 95cb99b375..6a350e85d6 100644
--- a/core/src/main/java/hudson/slaves/RetentionStrategy.java
+++ b/core/src/main/java/hudson/slaves/RetentionStrategy.java
@@ -67,6 +67,20 @@ public abstract class RetentionStrategy extends AbstractDesc
return true;
}
+ /**
+ * Returns {@code true} if the computer is accepting tasks. Needed to allow retention strategies programmatic
+ * suspension of task scheduling that in preparation for going offline. Called by
+ * {@link hudson.model.Computer#isAcceptingTasks()}
+ *
+ * @param c the computer.
+ * @return {@code true} if the computer is accepting tasks
+ * @see hudson.model.Computer#isAcceptingTasks()
+ * @since 1.586
+ */
+ public boolean isAcceptingTasks(T c) {
+ return true;
+ }
+
/**
* Called when a new {@link Computer} object is introduced (such as when Hudson started, or when
* a new slave is added.)
diff --git a/core/src/main/java/hudson/slaves/SlaveComputer.java b/core/src/main/java/hudson/slaves/SlaveComputer.java
index b08f646570..c631903705 100644
--- a/core/src/main/java/hudson/slaves/SlaveComputer.java
+++ b/core/src/main/java/hudson/slaves/SlaveComputer.java
@@ -23,6 +23,8 @@
*/
package hudson.slaves;
+import edu.umd.cs.findbugs.annotations.OverrideMustInvoke;
+import edu.umd.cs.findbugs.annotations.When;
import hudson.AbortException;
import hudson.remoting.ChannelBuilder;
import hudson.util.IOUtils;
@@ -141,8 +143,11 @@ public class SlaveComputer extends Computer {
* {@inheritDoc}
*/
@Override
+ @OverrideMustInvoke(When.ANYTIME)
public boolean isAcceptingTasks() {
- return acceptingTasks;
+ // our boolean flag is an override on any additional programmatic reasons why this slave might not be
+ // accepting tasks.
+ return acceptingTasks && super.isAcceptingTasks();
}
/**
@@ -153,8 +158,11 @@ public class SlaveComputer extends Computer {
}
/**
- * Allows a {@linkplain hudson.slaves.ComputerLauncher} or a {@linkplain hudson.slaves.RetentionStrategy} to
- * suspend tasks being accepted by the slave computer.
+ * Allows suspension of tasks being accepted by the slave computer. While this could be called by a
+ * {@linkplain hudson.slaves.ComputerLauncher} or a {@linkplain hudson.slaves.RetentionStrategy}, such usage
+ * can result in fights between multiple actors calling setting differential values. A better approach
+ * is to override {@link hudson.slaves.RetentionStrategy#isAcceptingTasks(hudson.model.Computer)} if the
+ * {@link hudson.slaves.RetentionStrategy} needs to control availability.
*
* @param acceptingTasks {@code true} if the slave can accept tasks.
*/
diff --git a/core/src/main/java/hudson/tasks/ArtifactArchiver.java b/core/src/main/java/hudson/tasks/ArtifactArchiver.java
index 43839ce936..ade511433d 100644
--- a/core/src/main/java/hudson/tasks/ArtifactArchiver.java
+++ b/core/src/main/java/hudson/tasks/ArtifactArchiver.java
@@ -297,6 +297,9 @@ public class ArtifactArchiver extends Recorder implements SimpleBuildStep {
* Performs on-the-fly validation on the file mask wildcard.
*/
public FormValidation doCheckArtifacts(@AncestorInPath AbstractProject project, @QueryParameter String value) throws IOException {
+ if (project == null) {
+ return FormValidation.ok();
+ }
return FilePath.validateFileMask(project.getSomeWorkspace(),value);
}
diff --git a/core/src/main/java/hudson/tasks/Fingerprinter.java b/core/src/main/java/hudson/tasks/Fingerprinter.java
index cb38f02496..ef14b0d6ed 100644
--- a/core/src/main/java/hudson/tasks/Fingerprinter.java
+++ b/core/src/main/java/hudson/tasks/Fingerprinter.java
@@ -260,6 +260,9 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD
}
public FormValidation doCheckTargets(@AncestorInPath AbstractProject,?> project, @QueryParameter String value) throws IOException {
+ if (project == null) {
+ return FormValidation.ok();
+ }
return FilePath.validateFileMask(project.getSomeWorkspace(),value);
}
diff --git a/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java b/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java
index 6836467217..c10e51d526 100644
--- a/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java
+++ b/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java
@@ -14,6 +14,7 @@ import javax.mail.internet.InternetAddress;
import javax.servlet.ServletContext;
import java.io.File;
import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -117,14 +118,17 @@ public class JenkinsLocationConfiguration extends GlobalConfiguration {
}
Object sessionCookieConfig = m.invoke(context);
- // not exposing session cookie to JavaScript to mitigate damage caused by XSS
Class scc = Class.forName("javax.servlet.SessionCookieConfig");
- Method setHttpOnly = scc.getMethod("setHttpOnly",boolean.class);
- setHttpOnly.invoke(sessionCookieConfig,true);
-
- Method setSecure = scc.getMethod("setSecure",boolean.class);
+ Method setSecure = scc.getMethod("setSecure", boolean.class);
boolean v = fixNull(jenkinsUrl).startsWith("https");
- setSecure.invoke(sessionCookieConfig,v);
+ setSecure.invoke(sessionCookieConfig, v);
+ } catch (InvocationTargetException e) {
+ if (e.getTargetException() instanceof IllegalStateException) {
+ // servlet 3.0 spec seems to prohibit this from getting set at runtime,
+ // though Winstone is happy to accept i. see JENKINS-25019
+ return;
+ }
+ LOGGER.log(Level.WARNING, "Failed to set secure cookie flag", e);
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Failed to set secure cookie flag", e);
}
diff --git a/core/src/main/java/jenkins/util/JSONSignatureValidator.java b/core/src/main/java/jenkins/util/JSONSignatureValidator.java
index 77470f39ba..758b95cedc 100644
--- a/core/src/main/java/jenkins/util/JSONSignatureValidator.java
+++ b/core/src/main/java/jenkins/util/JSONSignatureValidator.java
@@ -13,11 +13,14 @@ import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.security.DigestOutputStream;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.Signature;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
@@ -74,19 +77,61 @@ public class JSONSignatureValidator {
Set anchors = new HashSet(); // CertificateUtil.getDefaultRootCAs();
Jenkins j = Jenkins.getInstance();
for (String cert : (Set) j.servletContext.getResourcePaths("/WEB-INF/update-center-rootCAs")) {
- if (cert.endsWith(".txt")) continue; // skip text files that are meant to be documentation
- anchors.add(new TrustAnchor((X509Certificate)cf.generateCertificate(j.servletContext.getResourceAsStream(cert)),null));
+ if (cert.endsWith("/") || cert.endsWith(".txt")) {
+ continue; // skip directories also any text files that are meant to be documentation
+ }
+ InputStream in = j.servletContext.getResourceAsStream(cert);
+ if (in == null) continue; // our test for paths ending in / should prevent this from happening
+ Certificate certificate;
+ try {
+ certificate = cf.generateCertificate(in);
+ } catch (CertificateException e) {
+ LOGGER.log(Level.WARNING, String.format("Webapp resources in /WEB-INF/update-center-rootCAs are "
+ + "expected to be either certificates or .txt files documenting the "
+ + "certificates, but %s did not parse as a certificate. Skipping this "
+ + "resource for now.",
+ cert), e);
+ continue;
+ } finally {
+ in.close();
+ }
+ try {
+ anchors.add(new TrustAnchor((X509Certificate) certificate, null));
+ } catch (IllegalArgumentException e) {
+ LOGGER.log(Level.WARNING,
+ String.format("The name constraints in the certificate resource %s could not be "
+ + "decoded. Skipping this resource for now.",
+ cert), e);
+ }
}
File[] cas = new File(j.root, "update-center-rootCAs").listFiles();
if (cas!=null) {
for (File cert : cas) {
- if (cert.getName().endsWith(".txt")) continue; // skip text files that are meant to be documentation
+ if (cert.isDirectory() || cert.getName().endsWith(".txt")) {
+ continue; // skip directories also any text files that are meant to be documentation
+ }
FileInputStream in = new FileInputStream(cert);
+ Certificate certificate;
try {
- anchors.add(new TrustAnchor((X509Certificate)cf.generateCertificate(in),null));
+ certificate = cf.generateCertificate(in);
+ } catch (CertificateException e) {
+ LOGGER.log(Level.WARNING, String.format("Files in %s are expected to be either "
+ + "certificates or .txt files documenting the certificates, "
+ + "but %s did not parse as a certificate. Skipping this file for now.",
+ cert.getParentFile().getAbsolutePath(),
+ cert.getAbsolutePath()), e);
+ continue;
} finally {
in.close();
}
+ try {
+ anchors.add(new TrustAnchor((X509Certificate) certificate, null));
+ } catch (IllegalArgumentException e) {
+ LOGGER.log(Level.WARNING,
+ String.format("The name constraints in the certificate file %s could not be "
+ + "decoded. Skipping this file for now.",
+ cert.getAbsolutePath()), e);
+ }
}
}
CertificateUtil.validatePath(certs, anchors);
diff --git a/core/src/main/resources/hudson/cli/Messages.properties b/core/src/main/resources/hudson/cli/Messages.properties
index 2abac75d81..57c9299f81 100644
--- a/core/src/main/resources/hudson/cli/Messages.properties
+++ b/core/src/main/resources/hudson/cli/Messages.properties
@@ -71,7 +71,7 @@ SessionIdCommand.ShortDescription=Outputs the session ID, which changes every ti
UpdateViewCommand.ShortDescription=\
Updates the view definition XML from stdin. The opposite of the get-view command.
-BuildCommand.CLICause.ShortDescription=Started by command line by {0}
+BuildCommand.CLICause.ShortDescription=Started from command line by {0}
BuildCommand.CLICause.CannotBuildDisabled=\
Cannot build {0} because it is disabled.
BuildCommand.CLICause.CannotBuildConfigNotSaved=\
diff --git a/core/src/main/resources/hudson/cli/Messages_pt_BR.properties b/core/src/main/resources/hudson/cli/Messages_pt_BR.properties
index c4aa5b9062..007038f34b 100644
--- a/core/src/main/resources/hudson/cli/Messages_pt_BR.properties
+++ b/core/src/main/resources/hudson/cli/Messages_pt_BR.properties
@@ -46,7 +46,7 @@ ConsoleCommand.ShortDescription=Retorna a sa\u00edda do console de um build.
UpdateJobCommand.ShortDescription=Atualiza a defini\u00e7\u00e3o XML do job da stdin. \u00c9 o contr\u00e1rio do comando get-job.
# Lists all the available commands or a detailed description of single command.
HelpCommand.ShortDescription=Lista todos os comandos dispon\u00edveis ou uma descri\u00e7\u00e3o detalhada de um \u00fanico comando.
-# Started by command line by {0}
+# Started from command line by {0}
BuildCommand.CLICause.ShortDescription=Iniciado pela linha de comando por {0}
# Reads stdin and sends that out as an e-mail.
MailCommand.ShortDescription=L\u00ea a stdin e envia como um e-mail.
diff --git a/core/src/main/resources/lib/form/repeatableHeteroProperty.jelly b/core/src/main/resources/lib/form/repeatableHeteroProperty.jelly
index b1ab6b739b..77e40529bd 100644
--- a/core/src/main/resources/lib/form/repeatableHeteroProperty.jelly
+++ b/core/src/main/resources/lib/form/repeatableHeteroProperty.jelly
@@ -65,7 +65,7 @@ THE SOFTWARE.
-
Color of the ball or null to skip drawing.
+
+ Specifies the size of the icon. If unspecified, it inherits from
+ the nearest setting in the ancestor call tree.
+ Possible values are "icon-sm" (16x16), "icon-md" (24x24), "icon-lg" (32x32), "icon-xlg" (48x48)
+
Additional CSS styles to apply.
@@ -38,6 +43,10 @@ THE SOFTWARE.
+
+
+
+
diff --git a/core/src/main/resources/lib/hudson/listScmBrowsers.jelly b/core/src/main/resources/lib/hudson/listScmBrowsers.jelly
index b655da6d59..6236f6c369 100644
--- a/core/src/main/resources/lib/hudson/listScmBrowsers.jelly
+++ b/core/src/main/resources/lib/hudson/listScmBrowsers.jelly
@@ -29,10 +29,11 @@ THE SOFTWARE.
form field name.
+
-
+
.
+ *
+ * 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 java.io.File;
+import org.junit.Assert;
+import org.junit.Test;
+import org.jvnet.hudson.test.Issue;
+
+/**
+ * Test for {@link FileParameterValue}.
+ * @author Oleg Nenashev
+ */
+public class FileParameterValueTest {
+
+ @Issue("JENKINS-19017")
+ @Test public void compareParamsWithSameName() {
+ final String paramName = "MY_FILE_PARAM"; // Same paramName (location) reproduces the bug
+ final FileParameterValue param1 = new FileParameterValue(paramName, new File("ws_param1.txt"), "param1.txt");
+ final FileParameterValue param2 = new FileParameterValue(paramName, new File("ws_param2.txt"), "param2.txt");
+
+ Assert.assertNotEquals("Files with same locations shoud be considered as different", param1, param2);
+ Assert.assertNotEquals("Files with same locations shoud be considered as different", param2, param1);
+ }
+
+ @Test public void compareNullParams() {
+ final String paramName = "MY_FILE_PARAM";
+ FileParameterValue nonNullParam = new FileParameterValue(paramName, new File("ws_param1.txt"), "param1.txt");
+ FileParameterValue nullParam1 = new FileParameterValue(null, new File("null_param1.txt"), "null_param1.txt");
+ FileParameterValue nullParam2 = new FileParameterValue(null, new File("null_param2.txt"), "null_param2.txt");
+
+ // Combine nulls
+ Assert.assertEquals(nullParam1, nullParam1);
+ Assert.assertEquals(nullParam1, nullParam2);
+ Assert.assertEquals(nullParam2, nullParam1);
+ Assert.assertEquals(nullParam2, nullParam2);
+
+ // Compare with non-null
+ Assert.assertNotEquals(nullParam1, nonNullParam);
+ Assert.assertNotEquals(nonNullParam, nullParam1);
+ }
+}
diff --git a/debian/debian/changelog b/debian/debian/changelog
index 53bc3f96b2..4ac12482f1 100644
--- a/debian/debian/changelog
+++ b/debian/debian/changelog
@@ -1,3 +1,9 @@
+jenkins (1.585) unstable; urgency=low
+
+ * See http://jenkins-ci.org/changelog for more details.
+
+ -- Kohsuke Kawaguchi Sun, 19 Oct 2014 21:00:43 -0700
+
jenkins (1.584) unstable; urgency=low
* See http://jenkins-ci.org/changelog for more details.
diff --git a/licenseCompleter.groovy b/licenseCompleter.groovy
index 8b8f493a00..802b266659 100644
--- a/licenseCompleter.groovy
+++ b/licenseCompleter.groovy
@@ -72,10 +72,6 @@ complete {
rewriteLicense([],jenkinsLicense)
}
- match("*:jna") {
- rewriteLicense([],lgpl)
- }
-
match(["org.jvnet.localizer:localizer"]) {
// see http://java.net/projects/localizer
// see http://java.net/projects/trilead-putty-extension/
diff --git a/plugins/pom.xml b/plugins/pom.xml
index d42c2c9d95..55a6a495bf 100644
--- a/plugins/pom.xml
+++ b/plugins/pom.xml
@@ -11,7 +11,7 @@
org.jenkins-ci.plugins
plugin
Jenkins plugin POM
- 1.585-SNAPSHOT
+ 1.586-SNAPSHOT
pom
org.jenkins-ci
winstone
- 2.7
+ 2.8
test