From f27e2b5c8a5182b9bf03680d473d109bcf268bda Mon Sep 17 00:00:00 2001 From: kohsuke Date: Mon, 23 Mar 2009 03:26:55 +0000 Subject: [PATCH] Using new form validation code git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@16462 71c3de6d-444a-0410-be80-ed276b4c234a --- core/pom.xml | 2 +- core/src/main/java/hudson/FilePath.java | 107 ++++++++- .../java/hudson/maven/MavenModuleSet.java | 16 +- .../main/java/hudson/model/ComputerSet.java | 28 +-- core/src/main/java/hudson/model/Hudson.java | 132 +++-------- core/src/main/java/hudson/model/ListView.java | 27 +-- core/src/main/java/hudson/model/Slave.java | 29 +-- core/src/main/java/hudson/scm/CVSSCM.java | 63 +++-- .../main/java/hudson/scm/SubversionSCM.java | 201 +++++++--------- .../java/hudson/scm/browsers/FishEyeCVS.java | 71 +++--- .../java/hudson/scm/browsers/FishEyeSVN.java | 71 +++--- .../java/hudson/scm/browsers/Sventon.java | 49 ++-- .../GlobalMatrixAuthorizationStrategy.java | 79 +++--- .../hudson/security/LDAPSecurityRealm.java | 115 +++++---- .../java/hudson/slaves/CommandLauncher.java | 20 +- .../SimpleScheduledRetentionStrategy.java | 64 ++--- core/src/main/java/hudson/tasks/Ant.java | 34 +-- .../java/hudson/tasks/ArtifactArchiver.java | 7 +- .../main/java/hudson/tasks/BuildTrigger.java | 39 ++- .../main/java/hudson/tasks/Fingerprinter.java | 7 +- .../java/hudson/tasks/JavadocArchiver.java | 11 +- core/src/main/java/hudson/tasks/Mailer.java | 38 +-- core/src/main/java/hudson/tasks/Maven.java | 40 ++-- core/src/main/java/hudson/tasks/Shell.java | 6 +- .../tasks/junit/JUnitResultArchiver.java | 24 +- .../test/AggregatedTestResultPublisher.java | 26 +- .../java/hudson/triggers/TimerTrigger.java | 33 +-- .../java/hudson/util/FormFieldValidator.java | 17 +- .../main/java/hudson/util/FormValidation.java | 224 ++++++++++++++++-- 29 files changed, 829 insertions(+), 751 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index c4e775da3e..d102a649ad 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -404,7 +404,7 @@ THE SOFTWARE. org.kohsuke.stapler stapler-jelly - 1.97 + 1.98 dom4j diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index e513b671f1..242c0d3ec8 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -27,6 +27,8 @@ import hudson.Launcher.LocalLauncher; import hudson.Launcher.RemoteLauncher; import hudson.model.Hudson; import hudson.model.TaskListener; +import hudson.model.AbstractProject; +import hudson.model.Item; import hudson.remoting.Callable; import hudson.remoting.Channel; import hudson.remoting.DelegatingCallable; @@ -35,10 +37,11 @@ import hudson.remoting.Pipe; import hudson.remoting.RemoteOutputStream; import hudson.remoting.VirtualChannel; import hudson.remoting.RemoteInputStream; -import hudson.util.FormFieldValidator; import hudson.util.IOException2; import hudson.util.HeadBufferingStream; +import hudson.util.FormValidation; import hudson.util.jna.GNUCLibrary; +import static hudson.Util.fixEmpty; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.Project; @@ -51,6 +54,7 @@ import org.apache.tools.zip.ZipOutputStream; import org.apache.tools.zip.ZipEntry; import org.apache.commons.io.IOUtils; import org.apache.commons.fileupload.FileItem; +import org.kohsuke.stapler.Stapler; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -1222,12 +1226,12 @@ public final class FilePath implements Serializable { * against this directory, and try to point out the problem. * *

- * This is useful in conjunction with {@link FormFieldValidator}. + * This is useful in conjunction with {@link FormValidation}. * * @return * null if no error was found. Otherwise returns a human readable error message. * @since 1.90 - * @see FormFieldValidator.WorkspaceFileMask + * @see #validateFileMask(FilePath, String) */ public String validateAntFileMask(final String fileMasks) throws IOException, InterruptedException { return act(new FileCallable() { @@ -1351,6 +1355,103 @@ public final class FilePath implements Serializable { }); } + /** + * Shortcut for {@link #validateFileMask(String)} in case the left-hand side can be null. + */ + public static FormValidation validateFileMask(FilePath pathOrNull, String value) throws IOException { + if(pathOrNull==null) return FormValidation.ok(); + return pathOrNull.validateFileMask(value); + } + + /** + * Short for {@code validateFileMask(value,true)} + */ + public FormValidation validateFileMask(String value) throws IOException { + return validateFileMask(value,true); + } + + /** + * Checks the GLOB-style file mask. See {@link #validateAntFileMask(String)} + * @since 1.294 + */ + public FormValidation validateFileMask(String value, boolean errorIfNotExist) throws IOException { + value = fixEmpty(value); + if(value==null) + return FormValidation.ok(); + + try { + if(!exists()) // no workspace. can't check + return FormValidation.ok(); + + String msg = validateAntFileMask(value); + if(errorIfNotExist) return FormValidation.error(msg); + else return FormValidation.warning(msg); + } catch (InterruptedException e) { + return FormValidation.ok(); + } + } + + /** + * Validates a relative file path from this {@link FilePath}. + * + * @param value + * The relative path being validated. + * @param errorIfNotExist + * If true, report an error if the given relative path doesn't exist. Otherwise it's a warning. + * @param expectingFile + * If true, we expect the relative path to point to a file. + * Otherwise, the relative path is expected to be pointing to a directory. + */ + public FormValidation validateRelativePath(String value, boolean errorIfNotExist, boolean expectingFile) throws IOException { + AbstractProject subject = Stapler.getCurrentRequest().findAncestorObject(AbstractProject.class); + subject.checkPermission(Item.CONFIGURE); + + value = fixEmpty(value); + + // none entered yet, or something is seriously wrong + if(value==null || (AbstractProject)subject ==null) return FormValidation.ok(); + + // a common mistake is to use wildcard + if(value.contains("*")) return FormValidation.error("Wildcard is not allowed here"); + + try { + if(!exists()) // no base directory. can't check + return FormValidation.ok(); + + FilePath path = child(value); + if(path.exists()) { + if (expectingFile) { + if(!path.isDirectory()) + return FormValidation.ok(); + else + return FormValidation.error(value+" is not a file"); + } else { + if(path.isDirectory()) + return FormValidation.ok(); + else + return FormValidation.error(value+" is not a directory"); + } + } + + String msg = "No such "+(expectingFile?"file":"directory")+": " + value; + if(errorIfNotExist) return FormValidation.error(msg); + else return FormValidation.warning(msg); + } catch (InterruptedException e) { + return FormValidation.ok(); + } + } + + /** + * A convenience method over {@link #validateRelativePath(String, boolean, boolean)}. + */ + public FormValidation validateRelativeDirectory(String value, boolean errorIfNotExist) throws IOException { + return validateRelativePath(value,errorIfNotExist,false); + } + + public FormValidation validateRelativeDirectory(String value) throws IOException { + return validateRelativeDirectory(value,true); + } + @Deprecated public String toString() { // to make writing JSPs easily, return local diff --git a/core/src/main/java/hudson/maven/MavenModuleSet.java b/core/src/main/java/hudson/maven/MavenModuleSet.java index 1216035aec..63d8ab405e 100644 --- a/core/src/main/java/hudson/maven/MavenModuleSet.java +++ b/core/src/main/java/hudson/maven/MavenModuleSet.java @@ -35,10 +35,11 @@ import hudson.tasks.Maven.MavenInstallation; import hudson.tasks.*; import hudson.util.CopyOnWriteMap; import hudson.util.DescribableList; -import hudson.util.FormFieldValidator; import hudson.util.Function1; +import hudson.util.FormValidation; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.QueryParameter; import javax.servlet.ServletException; import java.io.File; @@ -635,15 +636,10 @@ public final class MavenModuleSet extends AbstractMavenProject getProject() { - return MavenModuleSet.this; - } - protected FilePath getBaseDirectory(AbstractProject p) { - return p.getModuleRoot(); - } - }.process(); + public FormValidation doCheckRootPOM(@QueryParameter String value) throws IOException, ServletException { + FilePath ws = getModuleRoot(); + if(ws==null) return FormValidation.ok(); + return ws.validateRelativePath(value,true,true); } public TopLevelItemDescriptor getDescriptor() { diff --git a/core/src/main/java/hudson/model/ComputerSet.java b/core/src/main/java/hudson/model/ComputerSet.java index 7951e65d19..ca341080a9 100644 --- a/core/src/main/java/hudson/model/ComputerSet.java +++ b/core/src/main/java/hudson/model/ComputerSet.java @@ -27,7 +27,7 @@ import hudson.Util; import hudson.slaves.NodeDescriptor; import hudson.model.Descriptor.FormException; import hudson.node_monitors.NodeMonitor; -import hudson.util.FormFieldValidator; +import hudson.util.FormValidation; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; @@ -35,7 +35,6 @@ import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; import javax.servlet.ServletException; -import javax.servlet.http.HttpServletResponse; import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import java.io.IOException; import java.text.ParseException; @@ -249,19 +248,18 @@ public final class ComputerSet extends AbstractModelObject { /** * Makes sure that the given name is good as a slave name. */ - public void doCheckName(StaplerRequest req, StaplerResponse rsp, @QueryParameter final String value) throws IOException, ServletException { - new FormFieldValidator(req,rsp,Hudson.ADMINISTER) { // TODO: new permission? - protected void check() throws IOException, ServletException { - if(Util.fixEmpty(value)==null) - ok(); // nothing is entered yet - else try { - checkName(value); - ok(); - } catch (ParseException e) { - error(e.getMessage()); - } - } - }.process(); + public FormValidation doCheckName(@QueryParameter String value) throws IOException, ServletException { + Hudson.getInstance().checkPermission(Hudson.ADMINISTER); + + if(Util.fixEmpty(value)==null) + return FormValidation.ok(); + + try { + checkName(value); + return FormValidation.ok(); + } catch (ParseException e) { + return FormValidation.error(e.getMessage()); + } } public Api getApi() { diff --git a/core/src/main/java/hudson/model/Hudson.java b/core/src/main/java/hudson/model/Hudson.java index 97c1020462..43a3c84204 100644 --- a/core/src/main/java/hudson/model/Hudson.java +++ b/core/src/main/java/hudson/model/Hudson.java @@ -93,7 +93,6 @@ import hudson.util.ClockDifference; import hudson.util.CopyOnWriteList; import hudson.util.CopyOnWriteMap; import hudson.util.DaemonThreadFactory; -import hudson.util.FormFieldValidator; import hudson.util.HudsonIsLoading; import hudson.util.MultipartFormDataParser; import hudson.util.RemotingDiagnostics; @@ -2678,14 +2677,6 @@ public final class Hudson extends Node implements ItemGroup, Stapl rsp.getWriter().println("Invoked"); } - /** - * Checks if the path is a valid path. - */ - public void doCheckLocalFSRoot( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { - // this can be used to check the existence of a file on the server, so needs to be protected - new FormFieldValidator.WorkspaceDirectory(req,rsp,true).process(); - } - /** * Checks if the JAVA_HOME is a valid JAVA_HOME path. */ @@ -2730,7 +2721,7 @@ public final class Hudson extends Node implements ItemGroup, Stapl String job = fixEmpty(value); if(job==null) - return FormValidation.ok(); // nothing is entered yet + return FormValidation.ok(); if(getItem(job)==null) return FormValidation.ok(); @@ -2745,7 +2736,7 @@ public final class Hudson extends Node implements ItemGroup, Stapl checkPermission(View.CREATE); String view = fixEmpty(value); - if(view==null) return FormValidation.ok(); // nothing is entered yet + if(view==null) return FormValidation.ok(); if(getView(view)==null) return FormValidation.ok(); @@ -2759,78 +2750,39 @@ public final class Hudson extends Node implements ItemGroup, Stapl * If the parameter "value" is not set then the parameter "errorText" is displayed * as an error text. If the parameter "errorText" is not set, then the parameter "warningText" is * displayed as a warning text. - *

+ *

* If the text is set and the parameter "type" is set, it will validate that the value is of the * correct type. Supported types are "number, "number-positive" and "number-negative". - * @param req containing the parameter value and the errorText to display if the value isnt set - * @param rsp used by FormFieldValidator - * @throws IOException thrown by FormFieldValidator.check() - * @throws ServletException thrown by FormFieldValidator.check() */ - public void doFieldCheck(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - new FormFieldValidator(req, rsp, false) { + public FormValidation doFieldCheck(@QueryParameter(fixEmpty=true) String value, + @QueryParameter(fixEmpty=true) String type, + @QueryParameter(fixEmpty=true) String errorText, + @QueryParameter(fixEmpty=true) String warningText) { + if (value == null) { + if (errorText != null) + return FormValidation.error(errorText); + if (warningText != null) + return FormValidation.warning(warningText); + return FormValidation.error("No error or warning text was set for fieldCheck()."); + } - /** - * Display the error text or warning text. - */ - private void fieldCheckFailed() throws IOException, ServletException { - String v = fixEmpty(request.getParameter("errorText")); - if (v != null) { - error(v); - return; + if (type != null) { + try { + if (type.equalsIgnoreCase("number")) { + NumberFormat.getInstance().parse(value); + } else if (type.equalsIgnoreCase("number-positive")) { + if (NumberFormat.getInstance().parse(value).floatValue() <= 0) + return FormValidation.error(Messages.Hudson_NotAPositiveNumber()); + } else if (type.equalsIgnoreCase("number-negative")) { + if (NumberFormat.getInstance().parse(value).floatValue() >= 0) + return FormValidation.error(Messages.Hudson_NotANegativeNumber()); } - v = fixEmpty(request.getParameter("warningText")); - if (v != null) { - warning(v); - return; - } - error("No error or warning text was set for fieldCheck()."); + } catch (ParseException e) { + return FormValidation.error(Messages.Hudson_NotANumber()); } + } - /** - * Checks if the value is of the correct type. - * @param type the type of string - * @param value the actual value to check - * @return true, if the type was valid; false otherwise - */ - private boolean checkType(String type, String value) throws IOException, ServletException { - try { - if (type.equalsIgnoreCase("number")) { - NumberFormat.getInstance().parse(value); - } else if (type.equalsIgnoreCase("number-positive")) { - if (NumberFormat.getInstance().parse(value).floatValue() <= 0) { - error(Messages.Hudson_NotAPositiveNumber()); - return false; - } - } else if (type.equalsIgnoreCase("number-negative")) { - if (NumberFormat.getInstance().parse(value).floatValue() >= 0) { - error(Messages.Hudson_NotANegativeNumber()); - return false; - } - } - } catch (ParseException e) { - error(Messages.Hudson_NotANumber()); - return false; - } - return true; - } - - @Override - protected void check() throws IOException, ServletException { - String value = fixEmpty(request.getParameter("value")); - if (value == null) { - fieldCheckFailed(); - return; - } - String type = fixEmpty(request.getParameter("type")); - if (type != null) { - if (!checkType(type, value)) { - return; - } - } - ok(); - } - }.process(); + return FormValidation.ok(); } /** @@ -2873,27 +2825,15 @@ public final class Hudson extends Node implements ItemGroup, Stapl /** * Checks if container uses UTF-8 to decode URLs. See * http://hudson.gotdns.com/wiki/display/HUDSON/Tomcat#Tomcat-i18n - * - * @param req containing the parameter value - * @param rsp used by FormFieldValidator - * @throws IOException thrown by FormFieldValidator.check() - * @throws ServletException thrown by FormFieldValidator.check() */ - public void doCheckURIEncoding(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - new FormFieldValidator(req, rsp, true) { - @Override - protected void check() throws IOException, ServletException { - request.setCharacterEncoding("UTF-8"); - // expected is non-ASCII String - final String expected = "\u57f7\u4e8b"; - final String value = fixEmpty(request.getParameter("value")); - if (!expected.equals(value)) { - warningWithMarkup(Messages.Hudson_NotUsesUTF8ToDecodeURL()); - return; - } - ok(); - } - }.process(); + public FormValidation doCheckURIEncoding(StaplerRequest request, @QueryParameter String value) throws IOException { + request.setCharacterEncoding("UTF-8"); + // expected is non-ASCII String + final String expected = "\u57f7\u4e8b"; + value = fixEmpty(value); + if (!expected.equals(value)) + return FormValidation.warningWithMarkup(Messages.Hudson_NotUsesUTF8ToDecodeURL()); + return FormValidation.ok(); } /** diff --git a/core/src/main/java/hudson/model/ListView.java b/core/src/main/java/hudson/model/ListView.java index 9eafccf08c..85a5cff5ee 100644 --- a/core/src/main/java/hudson/model/ListView.java +++ b/core/src/main/java/hudson/model/ListView.java @@ -34,14 +34,14 @@ import hudson.views.LastSuccessColumn; import hudson.views.ListViewColumn; import hudson.views.StatusColumn; import hudson.views.WeatherColumn; -import hudson.views.StatusColumn.DescriptorImpl; import hudson.model.Descriptor.FormException; import hudson.util.CaseInsensitiveComparator; -import hudson.util.FormFieldValidator; +import hudson.util.FormValidation; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.QueryParameter; import javax.servlet.ServletException; import java.io.IOException; @@ -239,21 +239,16 @@ public class ListView extends View { /** * Checks if the include regular expression is valid. */ - public void doCheckIncludeRegex( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException, InterruptedException { - new FormFieldValidator(req, rsp, false) { - @Override - protected void check() throws IOException, ServletException { - String v = Util.fixEmpty(request.getParameter("value")); - if (v != null) { - try { - Pattern.compile(v); - } catch (PatternSyntaxException pse) { - error(pse.getMessage()); - } - } - ok(); + public FormValidation doCheckIncludeRegex( @QueryParameter String value ) throws IOException, ServletException, InterruptedException { + String v = Util.fixEmpty(value); + if (v != null) { + try { + Pattern.compile(v); + } catch (PatternSyntaxException pse) { + return FormValidation.error(pse.getMessage()); } - }.process(); + } + return FormValidation.ok(); } } } diff --git a/core/src/main/java/hudson/model/Slave.java b/core/src/main/java/hudson/model/Slave.java index 76e91835b4..e2a790bc86 100644 --- a/core/src/main/java/hudson/model/Slave.java +++ b/core/src/main/java/hudson/model/Slave.java @@ -43,8 +43,7 @@ import hudson.tasks.DynamicLabeler; import hudson.tasks.LabelFinder; import hudson.util.ClockDifference; import hudson.util.DescribableList; -import hudson.util.FormFieldValidator; -import hudson.util.FormFieldValidator.NonNegativeInteger; +import hudson.util.FormValidation; import java.io.File; import java.io.IOException; @@ -425,30 +424,22 @@ public abstract class Slave extends Node implements Serializable { } public static abstract class SlaveDescriptor extends NodeDescriptor { - public void doCheckNumExecutors() throws IOException, ServletException { - new NonNegativeInteger().process(); + public FormValidation doCheckNumExecutors(@QueryParameter String value) { + return FormValidation.validateNonNegativeInteger(value); } /** * Performs syntactical check on the remote FS for slaves. */ - public void doCheckRemoteFs(StaplerRequest req, StaplerResponse rsp, @QueryParameter final String value) throws IOException, ServletException { - new FormFieldValidator(req,rsp,false) { - protected void check() throws IOException, ServletException { - if(Util.fixEmptyAndTrim(value)==null) { - error("Remote directory is mandatory"); - return; - } + public FormValidation doCheckRemoteFs(@QueryParameter String value) throws IOException, ServletException { + if(Util.fixEmptyAndTrim(value)==null) + return FormValidation.error("Remote directory is mandatory"); - if(value.startsWith("\\\\") || value.startsWith("/net/")) { - warning("Are you sure you want to use network mounted file system for FS root? " + - "Note that this directory needs not be visible to the master."); - return; - } + if(value.startsWith("\\\\") || value.startsWith("/net/")) + return FormValidation.warning("Are you sure you want to use network mounted file system for FS root? " + + "Note that this directory needs not be visible to the master."); - ok(); - } - }.process(); + return FormValidation.ok(); } } diff --git a/core/src/main/java/hudson/scm/CVSSCM.java b/core/src/main/java/hudson/scm/CVSSCM.java index fbe1e1f064..666ebcb8ba 100644 --- a/core/src/main/java/hudson/scm/CVSSCM.java +++ b/core/src/main/java/hudson/scm/CVSSCM.java @@ -41,6 +41,7 @@ import hudson.model.ModelObject; import hudson.model.Run; import hudson.model.TaskListener; import hudson.model.TaskThread; +import hudson.model.Item; import hudson.org.apache.tools.ant.taskdefs.cvslib.ChangeLogTask; import hudson.remoting.Future; import hudson.remoting.RemoteOutputStream; @@ -48,7 +49,6 @@ import hudson.remoting.VirtualChannel; import hudson.security.Permission; import hudson.util.ArgumentListBuilder; import hudson.util.ForkOutputStream; -import hudson.util.FormFieldValidator; import hudson.util.IOException2; import hudson.util.FormValidation; import org.apache.commons.io.FileUtils; @@ -61,6 +61,7 @@ import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.framework.io.ByteBuffer; import javax.servlet.ServletException; @@ -1110,40 +1111,37 @@ public class CVSSCM extends SCM implements Serializable { return x.getCvsRoot().equals(y.getCvsRoot()); } - // + // // web methods // - public void doCvsPassCheck(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { + public FormValidation doCvsPassCheck(@AncestorInPath AbstractProject project, @QueryParameter String value) { // this method can be used to check if a file exists anywhere in the file system, // so it should be protected. - new FormFieldValidator(req,rsp,true) { - protected void check() throws IOException, ServletException { - String v = fixEmpty(request.getParameter("value")); - if(v==null) { - // default. - ok(); - } else { - File cvsPassFile = new File(v); + if(!project.hasPermission(Item.CONFIGURE)) + return FormValidation.ok(); - if(cvsPassFile.exists()) { - if(cvsPassFile.isDirectory()) - error(cvsPassFile+" is a directory"); - else - ok(); - } else { - error("No such file exists"); - } - } - } - }.process(); + value = fixEmpty(value); + if(value==null) // not entered + return FormValidation.ok(); + + File cvsPassFile = new File(value); + + if(cvsPassFile.exists()) { + if(cvsPassFile.isDirectory()) + return FormValidation.error(cvsPassFile+" is a directory"); + else + return FormValidation.ok(); + } + + return FormValidation.error("No such file exists"); } /** * Checks if cvs executable exists. */ - public void doCvsExeCheck(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - new FormFieldValidator.Executable(req,rsp).process(); + public FormValidation doCvsExeCheck(@QueryParameter String value) { + return FormValidation.validateExecutable(value); } /** @@ -1330,18 +1328,11 @@ public class CVSSCM extends SCM implements Serializable { /** * Checks if the value is a valid CVS tag name. */ - public synchronized void doCheckTag(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - new FormFieldValidator(req,rsp,false) { - protected void check() throws IOException, ServletException { - String tag = fixNull(request.getParameter("value")).trim(); - if(tag.length()==0) {// nothing entered yet - ok(); - return; - } - - error(isInvalidTag(tag)); - } - }.check(); + public synchronized FormValidation doCheckTag(@QueryParameter String value) { + String tag = fixNull(value).trim(); + if(tag.length()==0) // nothing entered yet + return FormValidation.ok(); + return FormValidation.error(isInvalidTag(tag)); } @Override diff --git a/core/src/main/java/hudson/scm/SubversionSCM.java b/core/src/main/java/hudson/scm/SubversionSCM.java index e519481cb0..1b5926493d 100644 --- a/core/src/main/java/hudson/scm/SubversionSCM.java +++ b/core/src/main/java/hudson/scm/SubversionSCM.java @@ -33,7 +33,6 @@ import hudson.Util; import hudson.XmlFile; import hudson.Functions; import hudson.Extension; -import static hudson.Util.fixEmptyAndTrim; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.BuildListener; @@ -46,18 +45,19 @@ import hudson.remoting.Channel; import hudson.remoting.VirtualChannel; import hudson.triggers.SCMTrigger; import hudson.util.EditDistance; -import hudson.util.FormFieldValidator; import hudson.util.IOException2; import hudson.util.MultipartFormDataParser; import hudson.util.Scrambler; import hudson.util.StreamCopyThread; import hudson.util.XStream2; +import hudson.util.FormValidation; import org.apache.commons.fileupload.FileItem; import org.apache.commons.io.FileUtils; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Chmod; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.QueryParameter; import org.kohsuke.putty.PuTTYKey; import org.tmatesoft.svn.core.SVNDirEntry; import org.tmatesoft.svn.core.SVNErrorCode; @@ -1433,83 +1433,75 @@ public class SubversionSCM extends SCM implements Serializable { /** * validate the value for a remote (repository) location. */ - public void doSvnRemoteLocationCheck(final StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - // false==No permisison needed for basic check - new FormFieldValidator(req, rsp, false) { - protected void check() throws IOException, ServletException { - // syntax check first - String url = Util.nullify(request.getParameter("value")); - if (url == null) { - ok(); // not entered yet - return; + public FormValidation doSvnRemoteLocationCheck(StaplerRequest req, @QueryParameter String value) { + // syntax check first + String url = Util.nullify(value); + if (url == null) + return FormValidation.ok(); + + // remove unneeded whitespaces + url = url.trim(); + if(!URL_PATTERN.matcher(url).matches()) + return FormValidation.errorWithMarkup("Invalid URL syntax. See " + + "this " + + "for information about valid URLs."); + + // Test the connection only if we have admin permission + if (!Hudson.getInstance().hasPermission(Hudson.ADMINISTER)) + return FormValidation.ok(); + + try { + SVNURL repoURL = SVNURL.parseURIDecoded(url); + if (checkRepositoryPath(repoURL)!=SVNNodeKind.NONE) + // something exists + return FormValidation.ok(); + + SVNRepository repository = null; + try { + repository = getRepository(repoURL); + long rev = repository.getLatestRevision(); + // now go back the tree and find if there's anything that exists + String repoPath = getRelativePath(repoURL, repository); + String p = repoPath; + while(p.length()>0) { + p = SVNPathUtil.removeTail(p); + if(repository.checkPath(p,rev)==SVNNodeKind.DIR) { + // found a matching path + List entries = new ArrayList(); + repository.getDir(p,rev,false,entries); + + // build up the name list + List paths = new ArrayList(); + for (SVNDirEntry e : entries) + if(e.getKind()==SVNNodeKind.DIR) + paths.add(e.getName()); + + String head = SVNPathUtil.head(repoPath.substring(p.length() + 1)); + String candidate = EditDistance.findNearest(head,paths); + + return FormValidation.error("'%1$s/%2$s' doesn't exist in the repository. Maybe you meant '%1$s/%3$s'?", + p, head, candidate); + } } - // remove unneeded whitespaces - url = url.trim(); - if(!URL_PATTERN.matcher(url).matches()) { - errorWithMarkup("Invalid URL syntax. See " - + "this " - + "for information about valid URLs."); - return; - } - - // Test the connection only if we have admin permission - if (!Hudson.getInstance().hasPermission(Hudson.ADMINISTER)) { - ok(); - } else try { - SVNURL repoURL = SVNURL.parseURIDecoded(url); - if (checkRepositoryPath(repoURL)==SVNNodeKind.NONE) { - SVNRepository repository = null; - try { - repository = getRepository(repoURL); - long rev = repository.getLatestRevision(); - // now go back the tree and find if there's anything that exists - String repoPath = getRelativePath(repoURL, repository); - String p = repoPath; - while(p.length()>0) { - p = SVNPathUtil.removeTail(p); - if(repository.checkPath(p,rev)==SVNNodeKind.DIR) { - // found a matching path - List entries = new ArrayList(); - repository.getDir(p,rev,false,entries); - - // build up the name list - List paths = new ArrayList(); - for (SVNDirEntry e : entries) - if(e.getKind()==SVNNodeKind.DIR) - paths.add(e.getName()); - - String head = SVNPathUtil.head(repoPath.substring(p.length() + 1)); - String candidate = EditDistance.findNearest(head,paths); - - error("'%1$s/%2$s' doesn't exist in the repository. Maybe you meant '%1$s/%3$s'?", - p, head, candidate); - return; - } - } - - error(repoPath+" doesn't exist in the repository"); - } finally { - if (repository != null) - repository.closeSession(); - } - } else - ok(); - } catch (SVNException e) { - String message=""; - message += "Unable to access "+Util.escape(url)+" : "+Util.escape( e.getErrorMessage().getFullMessage()); - message += " (show details)"; - message += "

"; - message += " (Maybe you need to enter credential?)"; - message += "
"; - LOGGER.log(Level.INFO, "Failed to access subversion repository "+url,e); - errorWithMarkup(message); - } + return FormValidation.error(repoPath+" doesn't exist in the repository"); + } finally { + if (repository != null) + repository.closeSession(); } - }.process(); + } catch (SVNException e) { + String message=""; + message += "Unable to access "+Util.escape(url)+" : "+Util.escape( e.getErrorMessage().getFullMessage()); + message += " (show details)"; + message += ""; + message += " (Maybe you need to enter credential?)"; + message += "
"; + LOGGER.log(Level.INFO, "Failed to access subversion repository "+url,e); + return FormValidation.errorWithMarkup(message); + } } public SVNNodeKind checkRepositoryPath(SVNURL repoURL) throws SVNException { @@ -1558,53 +1550,34 @@ public class SubversionSCM extends SCM implements Serializable { /** * validate the value for a local location (local checkout directory). */ - public void doSvnLocalLocationCheck(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - // false==No permission needed for this syntax check - new FormFieldValidator(req, rsp, false) { - protected void check() throws IOException, ServletException { - String v = Util.nullify(request.getParameter("value")); - if (v == null) { - // local directory is optional so this is ok - ok(); - return; - } + public FormValidation doSvnLocalLocationCheck(@QueryParameter String value) throws IOException, ServletException { + String v = Util.nullify(value); + if (v == null) + // local directory is optional so this is ok + return FormValidation.ok(); - v = v.trim(); + v = v.trim(); - // check if a absolute path has been supplied - // (the last check with the regex will match windows drives) - if (v.startsWith("/") || v.startsWith("\\") || v.startsWith("..") || v.matches("^[A-Za-z]:")) { - error("absolute path is not allowed"); - } + // check if a absolute path has been supplied + // (the last check with the regex will match windows drives) + if (v.startsWith("/") || v.startsWith("\\") || v.startsWith("..") || v.matches("^[A-Za-z]:")) + return FormValidation.error("absolute path is not allowed"); - // all tests passed so far - ok(); - } - }.process(); + // all tests passed so far + return FormValidation.ok(); } /** * Validates the excludeRegions Regex */ - public void doExcludeRegionsCheck(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - new FormFieldValidator(req,rsp,false) { - protected void check() throws IOException, ServletException { - String v = fixEmptyAndTrim(request.getParameter("value")); - - if(v != null) { - String[] regions = v.split("[\\r\\n]+"); - for (String region : regions) { - try { - Pattern.compile(region); - } - catch (PatternSyntaxException e) { - error("Invalid regular expression. " + e.getMessage()); - } - } - } - ok(); + public FormValidation doExcludeRegionsCheck(@QueryParameter String value) throws IOException, ServletException { + for (String region : Util.fixNull(value).trim().split("[\\r\\n]+")) + try { + Pattern.compile(region); + } catch (PatternSyntaxException e) { + return FormValidation.error("Invalid regular expression. " + e.getMessage()); } - }.process(); + return FormValidation.ok(); } static { diff --git a/core/src/main/java/hudson/scm/browsers/FishEyeCVS.java b/core/src/main/java/hudson/scm/browsers/FishEyeCVS.java index 8e952b8509..0a821ce366 100644 --- a/core/src/main/java/hudson/scm/browsers/FishEyeCVS.java +++ b/core/src/main/java/hudson/scm/browsers/FishEyeCVS.java @@ -23,8 +23,8 @@ */ package hudson.scm.browsers; -import hudson.Util; import hudson.Extension; +import hudson.Util; import hudson.model.Descriptor; import hudson.model.Hudson; import hudson.scm.CVSChangeLogSet; @@ -32,14 +32,14 @@ import hudson.scm.CVSChangeLogSet.File; import hudson.scm.CVSChangeLogSet.Revision; import hudson.scm.CVSRepositoryBrowser; import hudson.scm.RepositoryBrowser; -import hudson.util.FormFieldValidator; +import hudson.util.FormValidation; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.QueryParameter; + +import javax.servlet.ServletException; import java.io.IOException; import java.net.URL; import java.util.regex.Pattern; -import javax.servlet.ServletException; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; -import org.kohsuke.stapler.DataBoundConstructor; /** * Repository browser for CVS in a FishEye server. @@ -84,45 +84,32 @@ public final class FishEyeCVS extends CVSRepositoryBrowser { return "FishEye"; } - public void doCheck(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - // false==No permission needed for basic check - new FormFieldValidator(req,rsp,false) { + public FormValidation doCheck(@QueryParameter String value) throws IOException, ServletException { + value = Util.fixEmpty(value); + if (value == null) return FormValidation.ok(); + if (!value.endsWith("/")) value += '/'; + if (!URL_PATTERN.matcher(value).matches()) + return FormValidation.errorWithMarkup("The URL should end like .../browse/foobar/"); + + // Connect to URL and check content only if we have admin permission + if (!Hudson.getInstance().hasPermission(Hudson.ADMINISTER)) + return FormValidation.ok(); + + final String finalValue = value; + return new FormValidation.URLCheck() { @Override - protected void check() throws IOException, ServletException { - String value = Util.fixEmpty(request.getParameter("value")); - if (value == null) { - ok(); - return; - } - if (!value.endsWith("/")) { - value += '/'; - } - if (!URL_PATTERN.matcher(value).matches()) { - errorWithMarkup("The URL should end like .../browse/foobar/"); - return; - } - // Connect to URL and check content only if we have admin permission - if (Hudson.getInstance().hasPermission(Hudson.ADMINISTER)) { - final String finalValue = value; - new FormFieldValidator.URLCheck(request,response) { - @Override - protected void check() throws IOException, ServletException { - try { - if (findText(open(new URL(finalValue)), "FishEye")) { - ok(); - } else { - error("This is a valid URL but it doesn't look like FishEye"); - } - } catch (IOException e) { - handleIOException(finalValue, e); - } - } - }.process(); - } else { - ok(); + protected FormValidation check() throws IOException, ServletException { + try { + if (findText(open(new URL(finalValue)), "FishEye")) { + return FormValidation.ok(); + } else { + return FormValidation.error("This is a valid URL but it doesn't look like FishEye"); + } + } catch (IOException e) { + return handleIOException(finalValue, e); } } - }.process(); + }.check(); } private static final Pattern URL_PATTERN = Pattern.compile(".+/browse/[^/]+/"); diff --git a/core/src/main/java/hudson/scm/browsers/FishEyeSVN.java b/core/src/main/java/hudson/scm/browsers/FishEyeSVN.java index 218eace79f..caa253dec6 100644 --- a/core/src/main/java/hudson/scm/browsers/FishEyeSVN.java +++ b/core/src/main/java/hudson/scm/browsers/FishEyeSVN.java @@ -23,19 +23,18 @@ */ package hudson.scm.browsers; -import static hudson.Util.fixEmpty; +import hudson.Extension; import hudson.model.Descriptor; import hudson.model.Hudson; +import hudson.scm.EditType; import hudson.scm.RepositoryBrowser; import hudson.scm.SubversionChangeLogSet.LogEntry; import hudson.scm.SubversionChangeLogSet.Path; import hudson.scm.SubversionRepositoryBrowser; -import hudson.scm.EditType; -import hudson.util.FormFieldValidator; -import hudson.Extension; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; +import hudson.util.FormValidation; +import hudson.util.FormValidation.URLCheck; import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.QueryParameter; import javax.servlet.ServletException; import java.io.IOException; @@ -131,45 +130,33 @@ public class FishEyeSVN extends SubversionRepositoryBrowser { /** * Performs on-the-fly validation of the URL. */ - public void doCheck(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - // false==No permission needed for basic check - new FormFieldValidator(req,rsp,false) { + public FormValidation doCheck(@QueryParameter(fixEmpty=true) String value) throws IOException, ServletException { + if(value==null) // nothing entered yet + return FormValidation.ok(); + + if(!value.endsWith("/")) value+='/'; + if(!URL_PATTERN.matcher(value).matches()) + return FormValidation.errorWithMarkup("The URL should end like .../browse/foobar/"); + + // Connect to URL and check content only if we have admin permission + if (!Hudson.getInstance().hasPermission(Hudson.ADMINISTER)) + return FormValidation.ok(); + + final String finalValue = value; + return new URLCheck() { @Override - protected void check() throws IOException, ServletException { - String value = fixEmpty(request.getParameter("value")); - if(value==null) {// nothing entered yet - ok(); - return; - } - - if(!value.endsWith("/")) value+='/'; - if(!URL_PATTERN.matcher(value).matches()) { - errorWithMarkup("The URL should end like .../browse/foobar/"); - return; - } - - // Connect to URL and check content only if we have admin permission - if (Hudson.getInstance().hasPermission(Hudson.ADMINISTER)) { - final String finalValue = value; - new FormFieldValidator.URLCheck(request,response) { - @Override - protected void check() throws IOException, ServletException { - try { - if(findText(open(new URL(finalValue)),"FishEye")) { - ok(); - } else { - error("This is a valid URL but it doesn't look like FishEye"); - } - } catch (IOException e) { - handleIOException(finalValue,e); - } - } - }.process(); - } else { - ok(); + protected FormValidation check() throws IOException, ServletException { + try { + if(findText(open(new URL(finalValue)),"FishEye")) { + return FormValidation.ok(); + } else { + return FormValidation.error("This is a valid URL but it doesn't look like FishEye"); + } + } catch (IOException e) { + return handleIOException(finalValue,e); } } - }.process(); + }.check(); } private static final Pattern URL_PATTERN = Pattern.compile(".+/browse/[^/]+/"); diff --git a/core/src/main/java/hudson/scm/browsers/Sventon.java b/core/src/main/java/hudson/scm/browsers/Sventon.java index 5e70a3892d..6fb8b358c4 100644 --- a/core/src/main/java/hudson/scm/browsers/Sventon.java +++ b/core/src/main/java/hudson/scm/browsers/Sventon.java @@ -23,18 +23,20 @@ */ package hudson.scm.browsers; -import static hudson.Util.fixEmpty; import hudson.model.Descriptor; +import hudson.model.AbstractProject; +import hudson.model.Item; import hudson.scm.RepositoryBrowser; import hudson.scm.SubversionChangeLogSet.LogEntry; import hudson.scm.SubversionChangeLogSet.Path; import hudson.scm.SubversionRepositoryBrowser; import hudson.scm.EditType; -import hudson.util.FormFieldValidator; +import hudson.util.FormValidation; +import hudson.util.FormValidation.URLCheck; import hudson.Extension; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.AncestorInPath; import javax.servlet.ServletException; import java.io.IOException; @@ -105,17 +107,6 @@ public class Sventon extends SubversionRepositoryBrowser { return s; } - /** - * Pick up "FOOBAR" from "http://site/browse/FOOBAR/" - */ - private String getProjectName() { - String p = url.getPath(); - if(p.endsWith("/")) p = p.substring(0,p.length()-1); - - int idx = p.lastIndexOf('/'); - return p.substring(idx+1); - } - @Override public URL getChangeSetLink(LogEntry changeSet) throws IOException { return new URL(url, String.format("revinfo.svn?name=%s&revision=%d", @@ -131,29 +122,27 @@ public class Sventon extends SubversionRepositoryBrowser { /** * Performs on-the-fly validation of the URL. */ - public void doCheck(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - // URLCheck requires Admin permission - new FormFieldValidator.URLCheck(req,rsp) { - protected void check() throws IOException, ServletException { - String value = fixEmpty(request.getParameter("value")); - if(value==null) {// nothing entered yet - ok(); - return; - } + public FormValidation doCheck(@AncestorInPath AbstractProject project, @QueryParameter(fixEmpty=true) final String value) throws IOException, ServletException { + if(!project.hasPermission(Item.CONFIGURE)) return FormValidation.ok(); // can't check + if(value==null) // nothing entered yet + return FormValidation.ok(); - if(!value.endsWith("/")) value+='/'; + return new URLCheck() { + protected FormValidation check() throws IOException, ServletException { + String v = value; + if(!v.endsWith("/")) v+='/'; try { - if(findText(open(new URL(value)),"sventon")) { - ok(); + if(findText(open(new URL(v)),"sventon")) { + return FormValidation.ok(); } else { - error("This is a valid URL but it doesn't look like Sventon"); + return FormValidation.error("This is a valid URL but it doesn't look like Sventon"); } } catch (IOException e) { - handleIOException(value,e); + return handleIOException(v,e); } } - }.process(); + }.check(); } } diff --git a/core/src/main/java/hudson/security/GlobalMatrixAuthorizationStrategy.java b/core/src/main/java/hudson/security/GlobalMatrixAuthorizationStrategy.java index 674fdb9e9c..13b877eef9 100644 --- a/core/src/main/java/hudson/security/GlobalMatrixAuthorizationStrategy.java +++ b/core/src/main/java/hudson/security/GlobalMatrixAuthorizationStrategy.java @@ -30,7 +30,8 @@ import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import hudson.model.Descriptor; import hudson.model.Hudson; -import hudson.util.FormFieldValidator; +import hudson.util.FormValidation; +import hudson.util.FormValidation.Kind; import hudson.Functions; import hudson.Extension; import net.sf.json.JSONObject; @@ -240,55 +241,47 @@ public class GlobalMatrixAuthorizationStrategy extends AuthorizationStrategy { return true; } - public void doCheckName(@QueryParameter String value ) throws IOException, ServletException { - doCheckName(value, Hudson.getInstance(), Hudson.ADMINISTER); + public FormValidation doCheckName(@QueryParameter String value ) throws IOException, ServletException { + return doCheckName(value, Hudson.getInstance(), Hudson.ADMINISTER); } - void doCheckName(String value, AccessControlled subject, Permission permission) throws IOException, ServletException { + FormValidation doCheckName(String value, AccessControlled subject, Permission permission) throws IOException, ServletException { + if(!subject.hasPermission(permission)) return FormValidation.ok(); // can't check + final String v = value.substring(1,value.length()-1); - new FormFieldValidator(subject, permission) { - protected void check() throws IOException, ServletException { - SecurityRealm sr = Hudson.getInstance().getSecurityRealm(); - String ev = Functions.escape(v); + SecurityRealm sr = Hudson.getInstance().getSecurityRealm(); + String ev = Functions.escape(v); - if(v.equals("authenticated")) { - // systerm reserved group - respond(""+ makeImg("user.gif") +ev+""); - return; - } + if(v.equals("authenticated")) + // systerm reserved group + return FormValidation.respond(Kind.OK, makeImg("user.gif") +ev); - try { - sr.loadUserByUsername(v); - respond(""+ makeImg("person.gif") +ev+""); - return; - } catch (UserMayOrMayNotExistException e) { - // undecidable, meaning the user may exist - respond(""+ev+""); - return; - } catch (UsernameNotFoundException e) { - // fall through next - } catch (DataAccessException e) { - // fall through next - } + try { + sr.loadUserByUsername(v); + return FormValidation.respond(Kind.OK, makeImg("person.gif")+ev); + } catch (UserMayOrMayNotExistException e) { + // undecidable, meaning the user may exist + return FormValidation.respond(Kind.OK, ev); + } catch (UsernameNotFoundException e) { + // fall through next + } catch (DataAccessException e) { + // fall through next + } - try { - sr.loadGroupByGroupname(v); - respond(""+ makeImg("user.gif") +ev+""); - return; - } catch (UserMayOrMayNotExistException e) { - // undecidable, meaning the group may exist - respond(""+ev+""); - return; - } catch (UsernameNotFoundException e) { - // fall through next - } catch (DataAccessException e) { - // fall through next - } + try { + sr.loadGroupByGroupname(v); + return FormValidation.respond(Kind.OK, makeImg("user.gif") +ev); + } catch (UserMayOrMayNotExistException e) { + // undecidable, meaning the group may exist + return FormValidation.respond(Kind.OK, ev); + } catch (UsernameNotFoundException e) { + // fall through next + } catch (DataAccessException e) { + // fall through next + } - // couldn't find it. it doesn't exit - respond(""+ makeImg("error.gif") +ev+""); - } - }.process(); + // couldn't find it. it doesn't exit + return FormValidation.respond(Kind.ERROR, makeImg("error.gif") +ev); } private String makeImg(String gif) { diff --git a/core/src/main/java/hudson/security/LDAPSecurityRealm.java b/core/src/main/java/hudson/security/LDAPSecurityRealm.java index aaec57dd58..bcc4479d01 100644 --- a/core/src/main/java/hudson/security/LDAPSecurityRealm.java +++ b/core/src/main/java/hudson/security/LDAPSecurityRealm.java @@ -25,39 +25,38 @@ package hudson.security; import com.sun.jndi.ldap.LdapCtxFactory; import groovy.lang.Binding; -import hudson.Util; import hudson.Extension; -import hudson.tasks.MailAddressResolver; +import hudson.Util; import hudson.model.Descriptor; import hudson.model.Hudson; import hudson.model.User; -import hudson.util.FormFieldValidator; +import hudson.tasks.MailAddressResolver; +import hudson.util.FormValidation; import hudson.util.Scrambler; import hudson.util.spring.BeanBuilder; import org.acegisecurity.AuthenticationManager; import org.acegisecurity.GrantedAuthority; -import org.acegisecurity.GrantedAuthorityImpl; -import org.acegisecurity.userdetails.UserDetailsService; +import org.acegisecurity.ldap.InitialDirContextFactory; +import org.acegisecurity.ldap.LdapDataAccessException; +import org.acegisecurity.ldap.LdapTemplate; +import org.acegisecurity.ldap.LdapUserSearch; +import org.acegisecurity.ldap.search.FilterBasedLdapUserSearch; +import org.acegisecurity.providers.ldap.LdapAuthoritiesPopulator; +import org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator; import org.acegisecurity.userdetails.UserDetails; +import org.acegisecurity.userdetails.UserDetailsService; import org.acegisecurity.userdetails.UsernameNotFoundException; import org.acegisecurity.userdetails.ldap.LdapUserDetails; import org.acegisecurity.userdetails.ldap.LdapUserDetailsImpl; -import org.acegisecurity.ldap.search.FilterBasedLdapUserSearch; -import org.acegisecurity.ldap.LdapUserSearch; -import org.acegisecurity.ldap.LdapDataAccessException; -import org.acegisecurity.ldap.InitialDirContextFactory; -import org.acegisecurity.ldap.LdapTemplate; -import org.acegisecurity.providers.ldap.LdapAuthoritiesPopulator; -import org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; -import org.springframework.web.context.WebApplicationContext; import org.springframework.dao.DataAccessException; +import org.springframework.web.context.WebApplicationContext; -import javax.naming.NamingException; import javax.naming.Context; +import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.directory.DirContext; @@ -66,9 +65,9 @@ import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; +import java.util.Collections; import java.util.Hashtable; import java.util.Set; -import java.util.Collections; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -431,55 +430,51 @@ public class LDAPSecurityRealm extends SecurityRealm { return Messages.LDAPSecurityRealm_DisplayName(); } - public void doServerCheck(StaplerRequest req, StaplerResponse rsp, @QueryParameter final String server, + public FormValidation doServerCheck( + @QueryParameter final String server, @QueryParameter final String managerDN, - @QueryParameter final String managerPassword - ) throws IOException, ServletException { - new FormFieldValidator(req,rsp,true) { - protected void check() throws IOException, ServletException { - try { - Hashtable props = new Hashtable(); - if(managerDN!=null && managerDN.trim().length() > 0 && !"undefined".equals(managerDN)) { - props.put(Context.SECURITY_PRINCIPAL,managerDN); - } - if(managerPassword!=null && managerPassword.trim().length() > 0 && !"undefined".equals(managerPassword)) { - props.put(Context.SECURITY_CREDENTIALS,managerPassword); - } - DirContext ctx = LdapCtxFactory.getLdapCtxInstance(addPrefix(server)+'/', props); - ctx.getAttributes(""); - ok(); // connected - } catch (NamingException e) { - // trouble-shoot - Matcher m = Pattern.compile("(ldaps://)?([^:]+)(?:\\:(\\d+))?").matcher(server.trim()); - if(!m.matches()) { - error("Syntax of server field is SERVER or SERVER:PORT or ldaps://SERVER[:PORT]"); - return; - } + @QueryParameter final String managerPassword) { - try { - InetAddress adrs = InetAddress.getByName(m.group(2)); - int port = m.group(1)!=null ? 636 : 389; - if(m.group(3)!=null) - port = Integer.parseInt(m.group(3)); - Socket s = new Socket(adrs,port); - s.close(); - } catch (UnknownHostException x) { - error("Unknown host: "+x.getMessage()); - return; - } catch (IOException x) { - error("Unable to connect to "+server+" : "+x.getMessage()); - return; - } + if(!Hudson.getInstance().hasPermission(Hudson.ADMINISTER)) + return FormValidation.ok(); - // otherwise we don't know what caused it, so fall back to the general error report - // getMessage() alone doesn't offer enough - error("Unable to connect to "+server+": "+e); - } catch (NumberFormatException x) { - // The getLdapCtxInstance method throws this if it fails to parse the port number - error("Invalid port number"); - } + try { + Hashtable props = new Hashtable(); + if(managerDN!=null && managerDN.trim().length() > 0 && !"undefined".equals(managerDN)) { + props.put(Context.SECURITY_PRINCIPAL,managerDN); } - }.check(); + if(managerPassword!=null && managerPassword.trim().length() > 0 && !"undefined".equals(managerPassword)) { + props.put(Context.SECURITY_CREDENTIALS,managerPassword); + } + DirContext ctx = LdapCtxFactory.getLdapCtxInstance(addPrefix(server)+'/', props); + ctx.getAttributes(""); + return FormValidation.ok(); // connected + } catch (NamingException e) { + // trouble-shoot + Matcher m = Pattern.compile("(ldaps://)?([^:]+)(?:\\:(\\d+))?").matcher(server.trim()); + if(!m.matches()) + return FormValidation.error("Syntax of server field is SERVER or SERVER:PORT or ldaps://SERVER[:PORT]"); + + try { + InetAddress adrs = InetAddress.getByName(m.group(2)); + int port = m.group(1)!=null ? 636 : 389; + if(m.group(3)!=null) + port = Integer.parseInt(m.group(3)); + Socket s = new Socket(adrs,port); + s.close(); + } catch (UnknownHostException x) { + return FormValidation.error("Unknown host: "+x.getMessage()); + } catch (IOException x) { + return FormValidation.error("Unable to connect to "+server+" : "+x.getMessage()); + } + + // otherwise we don't know what caused it, so fall back to the general error report + // getMessage() alone doesn't offer enough + return FormValidation.error("Unable to connect to "+server+": "+e); + } catch (NumberFormatException x) { + // The getLdapCtxInstance method throws this if it fails to parse the port number + return FormValidation.error("Invalid port number"); + } } } diff --git a/core/src/main/java/hudson/slaves/CommandLauncher.java b/core/src/main/java/hudson/slaves/CommandLauncher.java index c8e3e58c6c..68acaf2a6c 100644 --- a/core/src/main/java/hudson/slaves/CommandLauncher.java +++ b/core/src/main/java/hudson/slaves/CommandLauncher.java @@ -28,22 +28,18 @@ import hudson.Util; import hudson.Extension; import hudson.model.Descriptor; import hudson.remoting.Channel; -import hudson.util.FormFieldValidator; import hudson.util.ProcessTreeKiller; import hudson.util.StreamCopyThread; import hudson.util.StreamTaskListener; +import hudson.util.FormValidation; import java.io.IOException; import java.util.Date; import java.util.logging.Level; import java.util.logging.Logger; -import javax.servlet.ServletException; - import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; /** * {@link ComputerLauncher} through a remote login mechanism like ssh/rsh. @@ -155,15 +151,11 @@ public class CommandLauncher extends ComputerLauncher { return Messages.CommandLauncher_displayName(); } - public void doCheckCommand(StaplerRequest req, StaplerResponse rsp, @QueryParameter final String value) throws IOException, ServletException { - new FormFieldValidator(req,rsp,false) { - protected void check() throws IOException, ServletException { - if(Util.fixEmptyAndTrim(value)==null) - error("Command is empty"); - else - ok(); - } - }.process(); + public FormValidation doCheckCommand(@QueryParameter String value) { + if(Util.fixEmptyAndTrim(value)==null) + return FormValidation.error("Command is empty"); + else + return FormValidation.ok(); } } } diff --git a/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java b/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java index a147768975..9a5d213e10 100644 --- a/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java +++ b/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java @@ -23,27 +23,23 @@ */ package hudson.slaves; -import java.util.logging.Logger; -import java.util.logging.Level; +import antlr.ANTLRException; +import hudson.Extension; +import static hudson.Util.fixNull; +import hudson.model.Computer; +import hudson.model.Descriptor; +import hudson.scheduler.CronTabList; +import hudson.util.FormValidation; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.QueryParameter; + +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.concurrent.ExecutionException; -import java.io.IOException; -import java.io.ObjectStreamException; -import java.io.InvalidObjectException; - -import javax.servlet.ServletException; - -import hudson.model.Descriptor; -import hudson.model.Computer; -import hudson.scheduler.CronTabList; -import hudson.util.FormFieldValidator; -import static hudson.Util.fixNull; -import hudson.Extension; -import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; -import antlr.ANTLRException; +import java.util.logging.Level; +import java.util.logging.Logger; /** * {@link RetentionStrategy} that controls the slave based on a schedule. @@ -162,11 +158,11 @@ public class SimpleScheduledRetentionStrategy extends RetentionStrategy now) || (nextStart < now && nextStop > now); @@ -243,23 +239,15 @@ public class SimpleScheduledRetentionStrategy extends RetentionStrategy jobType) { diff --git a/core/src/main/java/hudson/tasks/Mailer.java b/core/src/main/java/hudson/tasks/Mailer.java index c49e22bbb9..b1eaac65e9 100644 --- a/core/src/main/java/hudson/tasks/Mailer.java +++ b/core/src/main/java/hudson/tasks/Mailer.java @@ -33,7 +33,7 @@ import hudson.model.BuildListener; import hudson.model.User; import hudson.model.UserPropertyDescriptor; import hudson.model.Hudson; -import hudson.util.FormFieldValidator; +import hudson.util.FormValidation; import org.apache.tools.ant.types.selectors.SelectorUtils; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; @@ -350,41 +350,29 @@ public class Mailer extends Notifier { /** * Checks the URL in global.jelly */ - public void doCheckUrl(StaplerRequest req, StaplerResponse rsp, - @QueryParameter final String value) throws IOException, ServletException { - new FormFieldValidator(req,rsp,false) { - protected void check() throws IOException, ServletException { - if(value.startsWith("http://localhost")) - warning("Please set a valid host name, instead of localhost"); - else - ok(); - } - }.process(); + public FormValidation doCheckUrl(@QueryParameter String value) { + if(value.startsWith("http://localhost")) + return FormValidation.warning("Please set a valid host name, instead of localhost"); + return FormValidation.ok(); } - public void doAddressCheck(StaplerRequest req, StaplerResponse rsp, - @QueryParameter final String value) throws IOException, ServletException { - new FormFieldValidator(req,rsp,false) { - protected void check() throws IOException, ServletException { - try { - new InternetAddress(value); - ok(); - } catch (AddressException e) { - error(e.getMessage()); - } - } - }.process(); + public FormValidation doAddressCheck(@QueryParameter String value) { + try { + new InternetAddress(value); + return FormValidation.ok(); + } catch (AddressException e) { + return FormValidation.error(e.getMessage()); + } } /** * Send an email to the admin address - * @param req ignored request * @param rsp used to write the result of the sending * @throws IOException * @throws ServletException * @throws InterruptedException */ - public void doSendTestMail(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, InterruptedException { + public void doSendTestMail(StaplerResponse rsp) throws IOException, ServletException, InterruptedException { rsp.setContentType("text/plain"); PrintStream writer = new PrintStream(rsp.getOutputStream()); try { diff --git a/core/src/main/java/hudson/tasks/Maven.java b/core/src/main/java/hudson/tasks/Maven.java index 55558d8ea2..0c3d82eb07 100644 --- a/core/src/main/java/hudson/tasks/Maven.java +++ b/core/src/main/java/hudson/tasks/Maven.java @@ -38,7 +38,6 @@ import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.BuildListener; import hudson.model.Computer; -import hudson.model.Descriptor; import hudson.model.EnvironmentSpecific; import hudson.model.Node; import hudson.model.Hudson; @@ -49,17 +48,16 @@ import hudson.tools.ToolDescriptor; import hudson.tools.ToolInstallation; import hudson.tools.ToolLocationNodeProperty; import hudson.util.ArgumentListBuilder; -import hudson.util.FormFieldValidator; import hudson.util.NullStream; import hudson.util.StreamTaskListener; import hudson.util.VariableResolver; +import hudson.util.FormValidation; import net.sf.json.JSONObject; import org.apache.maven.embedder.MavenEmbedderException; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.QueryParameter; -import javax.servlet.ServletException; import java.io.File; import java.io.IOException; import java.util.Map; @@ -324,31 +322,23 @@ public class Maven extends Builder { /** * Checks if the MAVEN_HOME is valid. */ - public void doCheckMavenHome( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { + public FormValidation doCheckMavenHome(@QueryParameter File value) { // this can be used to check the existence of a file on the server, so needs to be protected - new FormFieldValidator(req,rsp,true) { - public void check() throws IOException, ServletException { - File f = getFileParameter("value"); - if(f.getPath().equals("")) { - error(Messages.Maven_MavenHomeRequired()); - return; - } - if(!f.isDirectory()) { - error(Messages.Maven_NotADirectory(f)); - return; - } + if(!Hudson.getInstance().hasPermission(Hudson.ADMINISTER)) + return FormValidation.ok(); - File maven1File = new File(f,MAVEN_1_INSTALLATION_COMMON_FILE); - File maven2File = new File(f,MAVEN_2_INSTALLATION_COMMON_FILE); + if(value.getPath().equals("")) + return FormValidation.error(Messages.Maven_MavenHomeRequired()); + if(!value.isDirectory()) + return FormValidation.error(Messages.Maven_NotADirectory(value)); - if(!maven1File.exists() && !maven2File.exists()) { - error(Messages.Maven_NotMavenDirectory(f)); - return; - } + File maven1File = new File(value,MAVEN_1_INSTALLATION_COMMON_FILE); + File maven2File = new File(value,MAVEN_2_INSTALLATION_COMMON_FILE); - ok(); - } - }.process(); + if(!maven1File.exists() && !maven2File.exists()) + return FormValidation.error(Messages.Maven_NotMavenDirectory(value)); + + return FormValidation.ok(); } } diff --git a/core/src/main/java/hudson/tasks/Shell.java b/core/src/main/java/hudson/tasks/Shell.java index 17a4eb6b85..8fb1f5fb1b 100644 --- a/core/src/main/java/hudson/tasks/Shell.java +++ b/core/src/main/java/hudson/tasks/Shell.java @@ -30,9 +30,11 @@ import hudson.model.Descriptor; import hudson.model.AbstractProject; import static hudson.model.Hudson.isWindows; import hudson.util.FormFieldValidator; +import hudson.util.FormValidation; import net.sf.json.JSONObject; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.QueryParameter; import javax.servlet.ServletException; import java.io.IOException; @@ -153,9 +155,9 @@ public class Shell extends CommandInterpreter { /** * Check the existence of sh in the given location. */ - public void doCheck(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { + public FormValidation doCheck(@QueryParameter String value) { // Executable requires admin permission - new FormFieldValidator.Executable(req,rsp).process(); + return FormValidation.validateExecutable(value); } } } diff --git a/core/src/main/java/hudson/tasks/junit/JUnitResultArchiver.java b/core/src/main/java/hudson/tasks/junit/JUnitResultArchiver.java index f926ec593a..cfebbe5cf6 100644 --- a/core/src/main/java/hudson/tasks/junit/JUnitResultArchiver.java +++ b/core/src/main/java/hudson/tasks/junit/JUnitResultArchiver.java @@ -23,29 +23,33 @@ */ package hudson.tasks.junit; +import hudson.Extension; +import hudson.FilePath; import hudson.FilePath.FileCallable; import hudson.Launcher; import hudson.Util; -import hudson.Extension; -import hudson.maven.AbstractMavenProject; import hudson.matrix.MatrixAggregatable; import hudson.matrix.MatrixAggregator; import hudson.matrix.MatrixBuild; -import hudson.model.*; +import hudson.maven.AbstractMavenProject; +import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; +import hudson.model.Action; +import hudson.model.BuildListener; +import hudson.model.Result; import hudson.remoting.VirtualChannel; -import hudson.tasks.Publisher; import hudson.tasks.BuildStepDescriptor; +import hudson.tasks.Publisher; import hudson.tasks.Recorder; import hudson.tasks.test.TestResultAggregator; import hudson.tasks.test.TestResultProjectAction; -import hudson.util.FormFieldValidator; +import hudson.util.FormValidation; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.types.FileSet; +import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.QueryParameter; -import javax.servlet.ServletException; import java.io.File; import java.io.IOException; import java.io.Serializable; @@ -147,8 +151,8 @@ public class JUnitResultArchiver extends Recorder implements Serializable, Matri /** * Performs on-the-fly validation on the file mask wildcard. */ - public void doCheck(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - new FormFieldValidator.WorkspaceFileMask(req,rsp).process(); + public FormValidation doCheck(@AncestorInPath AbstractProject project, @QueryParameter String value) throws IOException { + return FilePath.validateFileMask(project.getWorkspace(),value); } public boolean isApplicable(Class jobType) { diff --git a/core/src/main/java/hudson/tasks/test/AggregatedTestResultPublisher.java b/core/src/main/java/hudson/tasks/test/AggregatedTestResultPublisher.java index 3fb0a225f1..799093102b 100644 --- a/core/src/main/java/hudson/tasks/test/AggregatedTestResultPublisher.java +++ b/core/src/main/java/hudson/tasks/test/AggregatedTestResultPublisher.java @@ -40,13 +40,12 @@ import hudson.tasks.BuildStepDescriptor; import hudson.tasks.Publisher; import hudson.tasks.Recorder; import hudson.tasks.Fingerprinter.FingerprintAction; -import hudson.util.FormFieldValidator; +import hudson.util.FormValidation; import net.sf.json.JSONObject; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.AncestorInPath; -import javax.servlet.ServletException; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; @@ -267,20 +266,17 @@ public class AggregatedTestResultPublisher extends Recorder { return "/help/tasks/aggregate-test/help.html"; } - public void doCheck(StaplerRequest req, StaplerResponse rsp, @QueryParameter final String value) throws IOException, ServletException { + public FormValidation doCheck(@AncestorInPath AbstractProject project, @QueryParameter(fixEmpty=true) String value) { // Require CONFIGURE permission on this project - AbstractProject project = req.findAncestorObject(AbstractProject.class); + if(!project.hasPermission(Item.CONFIGURE)) return FormValidation.ok(); - new FormFieldValidator(req,rsp,project,Item.CONFIGURE) { - protected void check() throws IOException, ServletException { - for (String name : Util.tokenize(value, ",")) { - name = name.trim(); - if(Hudson.getInstance().getItemByFullName(name)==null) { - error(hudson.tasks.Messages.BuildTrigger_NoSuchProject(name,AbstractProject.findNearest(name).getName())); - } - } - } - }.process(); + for (String name : Util.tokenize(value, ",")) { + name = name.trim(); + if(Hudson.getInstance().getItemByFullName(name)==null) + return FormValidation.error(hudson.tasks.Messages.BuildTrigger_NoSuchProject(name,AbstractProject.findNearest(name).getName())); + } + + return FormValidation.ok(); } public AggregatedTestResultPublisher newInstance(StaplerRequest req, JSONObject formData) throws FormException { diff --git a/core/src/main/java/hudson/triggers/TimerTrigger.java b/core/src/main/java/hudson/triggers/TimerTrigger.java index 0b07f675eb..16adcfde7a 100644 --- a/core/src/main/java/hudson/triggers/TimerTrigger.java +++ b/core/src/main/java/hudson/triggers/TimerTrigger.java @@ -28,16 +28,11 @@ import hudson.model.BuildableItem; import hudson.model.Cause; import hudson.model.Item; import hudson.scheduler.CronTabList; -import hudson.util.FormFieldValidator; +import hudson.util.FormValidation; import hudson.Extension; -import java.io.IOException; - -import javax.servlet.ServletException; - import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.QueryParameter; import antlr.ANTLRException; @@ -74,22 +69,14 @@ public class TimerTrigger extends Trigger { /** * Performs syntax check. */ - public void doCheck(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - // false==No permission needed for this syntax check - new FormFieldValidator(req,rsp,false) { - @Override - protected void check() throws IOException, ServletException { - try { - String msg = CronTabList.create(fixNull(request.getParameter("value"))).checkSanity(); - if(msg!=null) - warning(msg); - else - ok(); - } catch (ANTLRException e) { - error(e.getMessage()); - } - } - }.process(); + public FormValidation doCheck(@QueryParameter String value) { + try { + String msg = CronTabList.create(fixNull(value)).checkSanity(); + if(msg!=null) return FormValidation.warning(msg); + return FormValidation.ok(); + } catch (ANTLRException e) { + return FormValidation.error(e.getMessage()); + } } } diff --git a/core/src/main/java/hudson/util/FormFieldValidator.java b/core/src/main/java/hudson/util/FormFieldValidator.java index 4831497c32..186bcd3a10 100644 --- a/core/src/main/java/hudson/util/FormFieldValidator.java +++ b/core/src/main/java/hudson/util/FormFieldValidator.java @@ -27,6 +27,7 @@ import static hudson.Util.fixEmpty; import hudson.EnvVars; import hudson.FilePath; import hudson.Util; +import hudson.tasks.JavadocArchiver; import hudson.model.AbstractProject; import hudson.model.Hudson; import hudson.model.Item; @@ -56,6 +57,8 @@ import org.kohsuke.stapler.Stapler; * the validation. See hudson-behavior.js 'validated' CSS class and 'checkUrl' attribute. * * @author Kohsuke Kawaguchi + * @deprecated as of 1.294 + * Use {@link FormValidation} as a return value in your check method. */ public abstract class FormFieldValidator { public static final Permission CHECK = Hudson.ADMINISTER; @@ -230,7 +233,10 @@ public abstract class FormFieldValidator { } /** - * Convenient base class for checking the validity of URLs + * Convenient base class for checking the validity of URLs + * + * @deprecated as of 1.294 + * Use {@link FormValidation.URLCheck} */ public static abstract class URLCheck extends FormFieldValidator { @@ -339,6 +345,7 @@ public abstract class FormFieldValidator { * Checks the file mask (specified in the 'value' query parameter) against * the current workspace. * @since 1.90. + * @deprecated as of 1.294. Use {@link FilePath#validateFileMask(String, boolean)} */ public static class WorkspaceFileMask extends FormFieldValidator { private final boolean errorIfNotExist; @@ -389,7 +396,9 @@ public abstract class FormFieldValidator { /** * Checks a valid directory name (specified in the 'value' query parameter) against * the current workspace. - * @since 1.116. + * @since 1.116 + * @deprecated as of 1.294. Use {@link FilePath#validateRelativeDirectory(String, boolean)} + * (see {@link JavadocArchiver.DescriptorImpl#doCheck(AbstractProject, String)} */ public static class WorkspaceDirectory extends WorkspaceFilePath { public WorkspaceDirectory(StaplerRequest request, StaplerResponse response, boolean errorIfNotExist) { @@ -405,6 +414,7 @@ public abstract class FormFieldValidator { * Checks a valid file name or directory (specified in the 'value' query parameter) against * the current workspace. * @since 1.160 + * @deprecated as of 1.294. Use {@link FilePath#validateRelativePath(String, boolean, boolean)} */ public static class WorkspaceFilePath extends FormFieldValidator { private final boolean errorIfNotExist; @@ -487,6 +497,7 @@ public abstract class FormFieldValidator { * needed. * * @since 1.124 + * @deprecated as of 1.294. Use {@link FormValidation#validateExecutable(String)} */ public static class Executable extends FormFieldValidator { @@ -615,6 +626,8 @@ public abstract class FormFieldValidator { * Verifies that the 'value' parameter is an integer >= 0. * * @since 1.282 + * @deprecated as of 1.294 + * Use {@link FormValidation#validateNonNegativeInteger(String)} */ public static class NonNegativeInteger extends FormFieldValidator { public NonNegativeInteger() { diff --git a/core/src/main/java/hudson/util/FormValidation.java b/core/src/main/java/hudson/util/FormValidation.java index c1dc141277..f8d3432a27 100644 --- a/core/src/main/java/hudson/util/FormValidation.java +++ b/core/src/main/java/hudson/util/FormValidation.java @@ -23,16 +23,22 @@ */ package hudson.util; +import hudson.EnvVars; +import hudson.Util; +import static hudson.Util.fixEmpty; +import hudson.model.Hudson; +import hudson.scm.CVSSCM; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import javax.servlet.ServletException; +import java.io.File; import java.io.IOException; - -import hudson.Util; -import hudson.scm.CVSSCM; -import hudson.model.Hudson; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; /** * Represents the result of the form field validation. @@ -69,10 +75,6 @@ public abstract class FormValidation implements HttpResponse { ERROR } - public static FormValidation ok() { - return OK; - } - /** * Sends out a string error message that indicates an error. * @@ -92,6 +94,15 @@ public abstract class FormValidation implements HttpResponse { return okWithMarkup(message==null?null:Util.escape(message)); } + /** + * Singleton instance that represents "OK". + */ + private static final FormValidation OK = respond(Kind.OK,"
"); + + public static FormValidation ok() { + return OK; + } + /** * Sends out a string error message that indicates an error, * by formatting it with {@link String#format(String, Object[])} @@ -144,6 +155,194 @@ public abstract class FormValidation implements HttpResponse { }; } + /** + * Sends out an arbitrary HTML fragment as the output. + */ + public static FormValidation respond(Kind kind, final String html) { + return new FormValidation(kind) { + public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException { + respond(rsp,html); + } + }; + } + + /** + * Performs an application-specific validation on the given file. + * + *

+ * This is used as a piece in a bigger validation effort. + */ + public static abstract class FileValidator { + public abstract FormValidation validate(File f); + + /** + * Singleton instance that does no check. + */ + public static final FileValidator NOOP = new FileValidator() { + public FormValidation validate(File f) { + return ok(); + } + }; + } + + /** + * Makes sure that the given string points to an executable file. + */ + public static FormValidation validateExecutable(String exe) { + return validateExecutable(exe, FileValidator.NOOP); + } + + /** + * Makes sure that the given string points to an executable file. + * + * @param exeValidator + * If the validation process discovers a valid executable program on the given path, + * the specified {@link FileValidator} can perform additional checks (such as making sure + * that it has the right version, etc.) + */ + public static FormValidation validateExecutable(String exe, FileValidator exeValidator) { + // insufficient permission to perform validation? + if(Hudson.getInstance().hasPermission(Hudson.ADMINISTER)) return ok(); + + exe = fixEmpty(exe); + if(exe==null) + return ok(); + + if(exe.indexOf(File.separatorChar)>=0) { + // this is full path + File f = new File(exe); + if(f.exists()) return exeValidator.validate(f); + + File fexe = new File(exe+".exe"); + if(fexe.exists()) return exeValidator.validate(fexe); + + return error("There's no such file: "+exe); + } + + // look in PATH + String path = EnvVars.masterEnvVars.get("PATH"); + String tokenizedPath = ""; + String delimiter = null; + if(path!=null) { + for (String _dir : Util.tokenize(path.replace("\\", "\\\\"),File.pathSeparator)) { + if (delimiter == null) { + delimiter = ", "; + } + else { + tokenizedPath += delimiter; + } + + tokenizedPath += _dir.replace('\\', '/'); + + File dir = new File(_dir); + + File f = new File(dir,exe); + if(f.exists()) return exeValidator.validate(f); + + File fexe = new File(dir,exe+".exe"); + if(fexe.exists()) return exeValidator.validate(fexe); + } + + tokenizedPath += "."; + } else { + tokenizedPath = "unavailable."; + } + + // didn't find it + return error("There's no such executable "+exe+" in PATH: "+tokenizedPath); + } + + /** + * Makes sure that the given string is a non-negative integer. + */ + public static FormValidation validateNonNegativeInteger(String value) { + try { + if(Integer.parseInt(value)<0) + return error(hudson.model.Messages.Hudson_NotAPositiveNumber()); + return ok(); + } catch (NumberFormatException e) { + return error(hudson.model.Messages.Hudson_NotANumber()); + } + } + + /** + * Convenient base class for checking the validity of URLs. + * + *

+ * This allows the check method to call various utility methods in a concise syntax. + */ + public static abstract class URLCheck { + /** + * Opens the given URL and reads text content from it. + * This method honors Content-type header. + */ + protected BufferedReader open(URL url) throws IOException { + // use HTTP content type to find out the charset. + URLConnection con = url.openConnection(); + if (con == null) { // XXX is this even permitted by URL.openConnection? + throw new IOException(url.toExternalForm()); + } + return new BufferedReader( + new InputStreamReader(con.getInputStream(),getCharset(con))); + } + + /** + * Finds the string literal from the given reader. + * @return + * true if found, false otherwise. + */ + protected boolean findText(BufferedReader in, String literal) throws IOException { + String line; + while((line=in.readLine())!=null) + if(line.indexOf(literal)!=-1) + return true; + return false; + } + + /** + * Calls the {@link FormValidation#error(String)} method with a reasonable error message. + * Use this method when the {@link #open(URL)} or {@link #findText(BufferedReader, String)} fails. + * + * @param url + * Pass in the URL that was connected. Used for error diagnosis. + */ + protected FormValidation handleIOException(String url, IOException e) throws IOException, ServletException { + // any invalid URL comes here + if(e.getMessage().equals(url)) + // Sun JRE (and probably others too) often return just the URL in the error. + return error("Unable to connect "+url); + else + return error(e.getMessage()); + } + + /** + * Figures out the charset from the content-type header. + */ + private String getCharset(URLConnection con) { + for( String t : con.getContentType().split(";") ) { + t = t.trim().toLowerCase(); + if(t.startsWith("charset=")) + return t.substring(8); + } + // couldn't find it. HTML spec says default is US-ASCII, + // but UTF-8 is a better choice since + // (1) it's compatible with US-ASCII + // (2) a well-written web applications tend to use UTF-8 + return "UTF-8"; + } + + protected abstract FormValidation check() throws IOException, ServletException; + } + + + + + + + + + + public final Kind kind; /** @@ -161,13 +360,4 @@ public abstract class FormValidation implements HttpResponse { rsp.setContentType("text/html"); rsp.getWriter().print(html); } - - /** - * Singleton instance that represents "OK" without any message. By far the most common case. - */ - private static final FormValidation OK = new FormValidation(Kind.OK) { - public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException { - respond(rsp,"

"); - } - }; }