/*
* The MIT License
*
* Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
* Yahoo! Inc., Stephen Connolly, Tom Huybrechts, Alan Harder, Manufacture
* Francaise des Pneumatiques Michelin, Romain Seguy
*
* 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;
import hudson.cli.CLICommand;
import hudson.console.ConsoleAnnotationDescriptor;
import hudson.console.ConsoleAnnotatorFactory;
import hudson.matrix.MatrixProject;
import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.model.Describable;
import hudson.model.Descriptor;
import hudson.model.DescriptorVisibilityFilter;
import hudson.model.Hudson;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.Items;
import hudson.model.JDK;
import hudson.model.Job;
import hudson.model.JobPropertyDescriptor;
import hudson.model.ModelObject;
import hudson.model.Node;
import hudson.model.PageDecorator;
import hudson.model.ParameterDefinition;
import hudson.model.ParameterDefinition.ParameterDescriptor;
import hudson.model.Project;
import hudson.model.Run;
import hudson.model.TopLevelItem;
import hudson.model.User;
import hudson.model.View;
import hudson.scm.SCM;
import hudson.scm.SCMDescriptor;
import hudson.search.SearchableModelObject;
import hudson.security.AccessControlled;
import hudson.security.AuthorizationStrategy;
import hudson.security.GlobalSecurityConfiguration;
import hudson.security.Permission;
import hudson.security.SecurityRealm;
import hudson.security.captcha.CaptchaSupport;
import hudson.security.csrf.CrumbIssuer;
import hudson.slaves.Cloud;
import hudson.slaves.ComputerLauncher;
import hudson.slaves.NodeProperty;
import hudson.slaves.NodePropertyDescriptor;
import hudson.slaves.RetentionStrategy;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.BuildWrapper;
import hudson.tasks.BuildWrappers;
import hudson.tasks.Builder;
import hudson.tasks.Publisher;
import hudson.tasks.UserAvatarResolver;
import hudson.util.Area;
import hudson.util.FormValidation.CheckMethod;
import hudson.util.Iterators;
import hudson.util.Secret;
import hudson.views.MyViewsTabBar;
import hudson.views.ViewsTabBar;
import hudson.widgets.RenderOnDemandClosure;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.lang.management.LockInfo;
import java.lang.management.ManagementFactory;
import java.lang.management.MonitorInfo;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jenkins.model.GlobalConfiguration;
import jenkins.model.GlobalConfigurationCategory;
import jenkins.model.GlobalConfigurationCategory.Unclassified;
import jenkins.model.Jenkins;
import jenkins.model.ModelObjectWithChildren;
import jenkins.model.ModelObjectWithContextMenu;
import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
import org.apache.commons.jelly.JellyContext;
import org.apache.commons.jelly.JellyTagException;
import org.apache.commons.jelly.Script;
import org.apache.commons.jelly.XMLOutput;
import org.apache.commons.jexl.parser.ASTSizeFunction;
import org.apache.commons.jexl.util.Introspector;
import org.apache.commons.lang.StringUtils;
import org.jvnet.tiger_types.Types;
import org.kohsuke.stapler.Ancestor;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.jelly.InternationalizedStringExpression.RawHtmlArgument;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import java.util.concurrent.atomic.AtomicLong;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.DoNotUse;
import org.kohsuke.accmod.restrictions.NoExternalUse;
/**
* Utility functions used in views.
*
*
* An instance of this class is created for each request and made accessible
* from view pages via the variable 'h' (h stands for Hudson.)
*
* @author Kohsuke Kawaguchi
*/
@SuppressWarnings("rawtypes")
public class Functions {
private static final AtomicLong iota = new AtomicLong();
public Functions() {
}
/**
* Generates an unique ID.
*/
public String generateId() {
return "id" + iota.getAndIncrement();
}
public static boolean isModel(Object o) {
return o instanceof ModelObject;
}
public static boolean isModelWithContextMenu(Object o) {
return o instanceof ModelObjectWithContextMenu;
}
public static boolean isModelWithChildren(Object o) {
return o instanceof ModelObjectWithChildren;
}
/**
* @since 1.524
*/
public static boolean isMatrixProject(Object o) {
return o instanceof MatrixProject;
}
public static String xsDate(Calendar cal) {
return Util.XS_DATETIME_FORMATTER.format(cal.getTime());
}
public static String rfc822Date(Calendar cal) {
return Util.RFC822_DATETIME_FORMATTER.format(cal.getTime());
}
public static void initPageVariables(JellyContext context) {
String rootURL = Stapler.getCurrentRequest().getContextPath();
Functions h = new Functions();
context.setVariable("h", h);
// The path starts with a "/" character but does not end with a "/" character.
context.setVariable("rootURL", rootURL);
/*
load static resources from the path dedicated to a specific version.
This "/static/VERSION/abc/def.ghi" path is interpreted by stapler to be
the same thing as "/abc/def.ghi", but this avoids the stale cache
problem when the user upgrades to new Jenkins. Stapler also sets a long
future expiration dates for such static resources.
see https://wiki.jenkins-ci.org/display/JENKINS/Hyperlinks+in+HTML
*/
context.setVariable("resURL",rootURL+getResourcePath());
context.setVariable("imagesURL",rootURL+getResourcePath()+"/images");
}
/**
* Given {@code c=MyList (extends ArrayList), base=List}, compute the parameterization of 'base'
* that's assignable from 'c' (in this case {@code List}), and return its n-th type parameter
* (n=0 would return {@code Foo}).
*
*
* This method is useful for doing type arithmetic.
*
* @throws AssertionError
* if c' is not parameterized.
*/
public static Class getTypeParameter(Class extends B> c, Class base, int n) {
Type parameterization = Types.getBaseClass(c,base);
if (parameterization instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) parameterization;
return Types.erasure(Types.getTypeArgument(pt,n));
} else {
throw new AssertionError(c+" doesn't properly parameterize "+base);
}
}
public JDK.DescriptorImpl getJDKDescriptor() {
return Jenkins.getInstance().getDescriptorByType(JDK.DescriptorImpl.class);
}
/**
* Prints the integer as a string that represents difference,
* like "-5", "+/-0", "+3".
*/
public static String getDiffString(int i) {
if(i==0) return "±0";
String s = Integer.toString(i);
if(i>0) return "+"+s;
else return s;
}
/**
* {@link #getDiffString(int)} that doesn't show anything for +/-0
*/
public static String getDiffString2(int i) {
if(i==0) return "";
String s = Integer.toString(i);
if(i>0) return "+"+s;
else return s;
}
/**
* {@link #getDiffString2(int)} that puts the result into prefix and suffix
* if there's something to print
*/
public static String getDiffString2(String prefix, int i, String suffix) {
if(i==0) return "";
String s = Integer.toString(i);
if(i>0) return prefix+"+"+s+suffix;
else return prefix+s+suffix;
}
/**
* Adds the proper suffix.
*/
public static String addSuffix(int n, String singular, String plural) {
StringBuilder buf = new StringBuilder();
buf.append(n).append(' ');
if(n==1)
buf.append(singular);
else
buf.append(plural);
return buf.toString();
}
public static RunUrl decompose(StaplerRequest req) {
List ancestors = req.getAncestors();
// find the first and last Run instances
Ancestor f=null,l=null;
for (Ancestor anc : ancestors) {
if(anc.getObject() instanceof Run) {
if(f==null) f=anc;
l=anc;
}
}
if(l==null) return null; // there was no Run object
String head = f.getPrev().getUrl()+'/';
String base = l.getUrl();
String reqUri = req.getOriginalRequestURI();
// Find "rest" or URI by removing N path components.
// Not using reqUri.substring(f.getUrl().length()) to avoid mismatches due to
// url-encoding or extra slashes. Former may occur in Tomcat (despite the spec saying
// this string is not decoded, Tomcat apparently decodes this string. You see ' '
// instead of '%20', which is what the browser has sent), latter may occur in some
// proxy or URL-rewriting setups where extra slashes are inadvertently added.
String furl = f.getUrl();
int slashCount = 0;
// Count components in ancestor URL
for (int i = furl.indexOf('/'); i >= 0; i = furl.indexOf('/', i + 1)) slashCount++;
// Remove that many from request URL, ignoring extra slashes
String rest = reqUri.replaceFirst("(?:/+[^/]*){" + slashCount + "}", "");
return new RunUrl( (Run) f.getObject(), head, base, rest);
}
/**
* If we know the user's screen resolution, return it. Otherwise null.
* @since 1.213
*/
public static Area getScreenResolution() {
Cookie res = Functions.getCookie(Stapler.getCurrentRequest(),"screenResolution");
if(res!=null)
return Area.parse(res.getValue());
return null;
}
/**
* URL decomposed for easier computation of relevant URLs.
*
*
* The decomposed URL will be of the form:
*
* aaaaaa/524/bbbbb/cccc
* -head-| N |---rest---
* ----- base -----|
*
*
*
* The head portion is the part of the URL from the {@link jenkins.model.Jenkins}
* object to the first {@link Run} subtype. When "next/prev build"
* is chosen, this part remains intact.
*
*
* The 524 is the path from {@link Job} to {@link Run}.
*
*
* The bbb portion is the path after that till the last
* {@link Run} subtype. The ccc portion is the part
* after that.
*/
public static final class RunUrl {
private final String head, base, rest;
private final Run run;
public RunUrl(Run run, String head, String base, String rest) {
this.run = run;
this.head = head;
this.base = base;
this.rest = rest;
}
public String getBaseUrl() {
return base;
}
/**
* Returns the same page in the next build.
*/
public String getNextBuildUrl() {
return getUrl(run.getNextBuild());
}
/**
* Returns the same page in the previous build.
*/
public String getPreviousBuildUrl() {
return getUrl(run.getPreviousBuild());
}
private String getUrl(Run n) {
if(n ==null)
return null;
else {
return head+n.getNumber()+rest;
}
}
}
public static Node.Mode[] getNodeModes() {
return Node.Mode.values();
}
public static String getProjectListString(List projects) {
return Items.toNameList(projects);
}
/**
* @deprecated as of 1.294
* JEXL now supports the real ternary operator "x?y:z", so this work around
* is no longer necessary.
*/
public static Object ifThenElse(boolean cond, Object thenValue, Object elseValue) {
return cond ? thenValue : elseValue;
}
public static String appendIfNotNull(String text, String suffix, String nullText) {
return text == null ? nullText : text + suffix;
}
public static Map getSystemProperties() {
return new TreeMap