File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\hudson\util\FormValidation.java.html

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2004-2009, Sun Microsystems, Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 package hudson.util;
25
26 import hudson.EnvVars;
27 import hudson.Functions;
28 import hudson.Launcher;
29 import hudson.ProxyConfiguration;
30 import hudson.Util;
31 import hudson.FilePath;
32 import hudson.model.AbstractBuild;
33 import hudson.model.BuildListener;
34 import hudson.tasks.Builder;
35 import static hudson.Util.fixEmpty;
36 import jenkins.model.Jenkins;
37 import org.kohsuke.stapler.HttpResponse;
38 import org.kohsuke.stapler.StaplerRequest;
39 import org.kohsuke.stapler.StaplerResponse;
40 import org.kohsuke.stapler.Stapler;
41
42 import javax.servlet.ServletException;
43 import java.io.File;
44 import java.io.IOException;
45 import java.io.BufferedReader;
46 import java.io.InputStreamReader;
47 import java.net.URL;
48 import java.net.URLConnection;
49 import java.util.Locale;
50
51 /**
52 * Represents the result of the form field validation.
53 *
54 * <p>
55 * Use one of the factory methods to create an instance, then return it from your <tt>doCheckXyz</tt>
56 * method. (Via {@link HttpResponse}, the returned object will render the result into {@link StaplerResponse}.)
57 * This way of designing form field validation allows you to reuse {@code doCheckXyz()} methods
58 * programmatically as well (by using {@link #kind}.
59 *
60 * <p>
61 * For typical validation needs, this class offers a number of {@code validateXXX(...)} methods, such as
62 * {@link #validateExecutable(String)}. {@link FilePath} also has a number of {@code validateXXX(...)} methods
63 * that you may be able to reuse.
64 *
65 * <p>
66 * Also see {@link CVSSCM.DescriptorImpl#doCheckCvsRoot(String)} as an example.
67 *
68 * <p>
69 * This class extends {@link IOException} so that it can be thrown from a method. This allows one to reuse
70 * the checking logic as a part of the real computation, such as:
71 *
72 * <pre>
73 * String getAntVersion(File antHome) throws FormValidation {
74 * if(!antHome.isDirectory())
75 * throw FormValidation.error(antHome+" doesn't look like a home directory");
76 * ...
77 * return IOUtils.toString(new File(antHome,"version"));
78 * }
79 *
80 * ...
81 *
82 * public FormValidation doCheckAntVersion(@QueryParameter String f) {
83 * try {
84 * return ok(getAntVersion(new File(f)));
85 * } catch (FormValidation f) {
86 * return f;
87 * }
88 * }
89 *
90 * ...
91 *
92 * public void {@linkplain Builder#perform(AbstractBuild, Launcher, BuildListener) perform}(...) {
93 * String version = getAntVersion(antHome);
94 * ...
95 * }
96 * </pre>
97 *
98 * @author Kohsuke Kawaguchi
99 * @since 1.294
100 */
101 public abstract class FormValidation extends IOException implements HttpResponse {
102 /**
103 * Indicates the kind of result.
104 */
105 public enum Kind {
106 /**
107 * Form field value was OK and no problem was detected.
108 */
109 OK,
110 /**
111 * Form field value contained something suspicious. For some limited use cases
112 * the value could be valid, but we suspect the user made a mistake.
113 */
114 WARNING,
115 /**
116 * Form field value contained a problem that should be corrected.
117 */
118 ERROR
119 }
120
121 /**
122 * Sends out a string error message that indicates an error.
123 *
124 * @param message
125 * Human readable message to be sent. <tt>error(null)</tt>
126 * can be used as <tt>ok()</tt>.
127 */
128 public static FormValidation error(String message) {
129 return errorWithMarkup(message==null?null: Util.escape(message));
130 }
131
132 public static FormValidation warning(String message) {
133 return warningWithMarkup(message==null?null:Util.escape(message));
134 }
135
136 public static FormValidation ok(String message) {
137 return okWithMarkup(message==null?null:Util.escape(message));
138 }
139
140 /**
141 * Singleton instance that represents "OK".
142 */
143 private static final FormValidation OK = respond(Kind.OK,"<div/>");
144
145 public static FormValidation ok() {
146 return OK;
147 }
148
149 /**
150 * Sends out a string error message that indicates an error,
151 * by formatting it with {@link String#format(String, Object[])}
152 */
153 public static FormValidation error(String format, Object... args) {
154 return error(String.format(format,args));
155 }
156
157 public static FormValidation warning(String format, Object... args) {
158 return warning(String.format(format,args));
159 }
160
161 public static FormValidation ok(String format, Object... args) {
162 return ok(String.format(format,args));
163 }
164
165 /**
166 * Sends out a string error message, with optional "show details" link that expands to the full stack trace.
167 *
168 * <p>
169 * Use this with caution, so that anonymous users do not gain too much insights into the state of the system,
170 * as error stack trace often reveals a lot of information. Consider if a check operation needs to be exposed
171 * to everyone or just those who have higher access to job/hudson/etc.
172 */
173 public static FormValidation error(Throwable e, String message) {
174 return _error(Kind.ERROR, e, message);
175 }
176
177 public static FormValidation warning(Throwable e, String message) {
178 return _error(Kind.WARNING, e, message);
179 }
180
181 private static FormValidation _error(Kind kind, Throwable e, String message) {
182 if (e==null) return _errorWithMarkup(Util.escape(message),kind);
183
184 return _errorWithMarkup(Util.escape(message)+
185 " <a href='#' class='showDetails'>"
186 + Messages.FormValidation_Error_Details()
187 + "</a><pre style='display:none'>"
188 + Functions.printThrowable(e) +
189 "</pre>",kind
190 );
191 }
192
193 public static FormValidation error(Throwable e, String format, Object... args) {
194 return error(e,String.format(format,args));
195 }
196
197 public static FormValidation warning(Throwable e, String format, Object... args) {
198 return warning(e,String.format(format,args));
199 }
200
201
202
203 /**
204 * Sends out an HTML fragment that indicates an error.
205 *
206 * <p>
207 * This method must be used with care to avoid cross-site scripting
208 * attack.
209 *
210 * @param message
211 * Human readable message to be sent. <tt>error(null)</tt>
212 * can be used as <tt>ok()</tt>.
213 */
214 public static FormValidation errorWithMarkup(String message) {
215 return _errorWithMarkup(message,Kind.ERROR);
216 }
217
218 public static FormValidation warningWithMarkup(String message) {
219 return _errorWithMarkup(message,Kind.WARNING);
220 }
221
222 public static FormValidation okWithMarkup(String message) {
223 return _errorWithMarkup(message,Kind.OK);
224 }
225
226 private static FormValidation _errorWithMarkup(final String message, final Kind kind) {
227 if(message==null)
228 return ok();
229 return new FormValidation(kind, message) {
230 public String renderHtml() {
231 // 1x16 spacer needed for IE since it doesn't support min-height
232 return "<div class="+ kind.name().toLowerCase(Locale.ENGLISH) +"><img src='"+
233 Stapler.getCurrentRequest().getContextPath()+ Jenkins.RESOURCE_PATH+"/images/none.gif' height=16 width=1>"+
234 message+"</div>";
235 }
236 @Override public String toString() {
237 return kind + ": " + message;
238 }
239 };
240 }
241
242 /**
243 * Sends out an arbitrary HTML fragment as the output.
244 */
245 public static FormValidation respond(Kind kind, final String html) {
246 return new FormValidation(kind) {
247 public String renderHtml() {
248 return html;
249 }
250 @Override public String toString() {
251 return kind + ": " + html;
252 }
253 };
254 }
255
256 /**
257 * Performs an application-specific validation on the given file.
258 *
259 * <p>
260 * This is used as a piece in a bigger validation effort.
261 */
262 public static abstract class FileValidator {
263 public abstract FormValidation validate(File f);
264
265 /**
266 * Singleton instance that does no check.
267 */
268 public static final FileValidator NOOP = new FileValidator() {
269 public FormValidation validate(File f) {
270 return ok();
271 }
272 };
273 }
274
275 /**
276 * Makes sure that the given string points to an executable file.
277 */
278 public static FormValidation validateExecutable(String exe) {
279 return validateExecutable(exe, FileValidator.NOOP);
280 }
281
282 /**
283 * Makes sure that the given string points to an executable file.
284 *
285 * @param exeValidator
286 * If the validation process discovers a valid executable program on the given path,
287 * the specified {@link FileValidator} can perform additional checks (such as making sure
288 * that it has the right version, etc.)
289 */
290 public static FormValidation validateExecutable(String exe, FileValidator exeValidator) {
291 // insufficient permission to perform validation?
292 if(!Jenkins.getInstance().hasPermission(Jenkins.ADMINISTER)) return ok();
293
294 exe = fixEmpty(exe);
295 if(exe==null)
296 return ok();
297
298 if(exe.indexOf(File.separatorChar)>=0) {
299 // this is full path
300 File f = new File(exe);
301 if(f.exists()) return exeValidator.validate(f);
302
303 File fexe = new File(exe+".exe");
304 if(fexe.exists()) return exeValidator.validate(fexe);
305
306 return error("There's no such file: "+exe);
307 }
308
309 // look in PATH
310 String path = EnvVars.masterEnvVars.get("PATH");
311 String tokenizedPath = "";
312 String delimiter = null;
313 if(path!=null) {
314 for (String _dir : Util.tokenize(path.replace("\\", "\\\\"),File.pathSeparator)) {
315 if (delimiter == null) {
316 delimiter = ", ";
317 }
318 else {
319 tokenizedPath += delimiter;
320 }
321
322 tokenizedPath += _dir.replace('\\', '/');
323
324 File dir = new File(_dir);
325
326 File f = new File(dir,exe);
327 if(f.exists()) return exeValidator.validate(f);
328
329 File fexe = new File(dir,exe+".exe");
330 if(fexe.exists()) return exeValidator.validate(fexe);
331 }
332
333 tokenizedPath += ".";
334 } else {
335 tokenizedPath = "unavailable.";
336 }
337
338 // didn't find it
339 return error("There's no such executable "+exe+" in PATH: "+tokenizedPath);
340 }
341
342 /**
343 * Makes sure that the given string is a non-negative integer.
344 */
345 public static FormValidation validateNonNegativeInteger(String value) {
346 try {
347 if(Integer.parseInt(value)<0)
348 return error(hudson.model.Messages.Hudson_NotANonNegativeNumber());
349 return ok();
350 } catch (NumberFormatException e) {
351 return error(hudson.model.Messages.Hudson_NotANumber());
352 }
353 }
354
355 /**
356 * Makes sure that the given string is a positive integer.
357 */
358 public static FormValidation validatePositiveInteger(String value) {
359 try {
360 if(Integer.parseInt(value)<=0)
361 return error(hudson.model.Messages.Hudson_NotAPositiveNumber());
362 return ok();
363 } catch (NumberFormatException e) {
364 return error(hudson.model.Messages.Hudson_NotANumber());
365 }
366 }
367
368 /**
369 * Makes sure that the given string is not null or empty.
370 */
371 public static FormValidation validateRequired(String value) {
372 if (Util.fixEmptyAndTrim(value) == null)
373 return error(Messages.FormValidation_ValidateRequired());
374 return ok();
375 }
376
377 /**
378 * Makes sure that the given string is a base64 encoded text.
379 *
380 * @param allowWhitespace
381 * if you allow whitespace (CR,LF,etc) in base64 encoding
382 * @param allowEmpty
383 * Is empty string allowed?
384 * @param errorMessage
385 * Error message.
386 * @since 1.305
387 */
388 public static FormValidation validateBase64(String value, boolean allowWhitespace, boolean allowEmpty, String errorMessage) {
389 try {
390 String v = value;
391 if(!allowWhitespace) {
392 if(v.indexOf(' ')>=0 || v.indexOf('\n')>=0)
393 return error(errorMessage);
394 }
395 v=v.trim();
396 if(!allowEmpty && v.length()==0)
397 return error(errorMessage);
398
399 com.trilead.ssh2.crypto.Base64.decode(v.toCharArray());
400 return ok();
401 } catch (IOException e) {
402 return error(errorMessage);
403 }
404 }
405
406 /**
407 * Convenient base class for checking the validity of URLs.
408 *
409 * <p>
410 * This allows the check method to call various utility methods in a concise syntax.
411 */
412 public static abstract class URLCheck {
413 /**
414 * Opens the given URL and reads text content from it.
415 * This method honors Content-type header.
416 */
417 protected BufferedReader open(URL url) throws IOException {
418 // use HTTP content type to find out the charset.
419 URLConnection con = ProxyConfiguration.open(url);
420 if (con == null) { // XXX is this even permitted by URL.openConnection?
421 throw new IOException(url.toExternalForm());
422 }
423 return new BufferedReader(
424 new InputStreamReader(con.getInputStream(),getCharset(con)));
425 }
426
427 /**
428 * Finds the string literal from the given reader.
429 * @return
430 * true if found, false otherwise.
431 */
432 protected boolean findText(BufferedReader in, String literal) throws IOException {
433 String line;
434 while((line=in.readLine())!=null)
435 if(line.indexOf(literal)!=-1)
436 return true;
437 return false;
438 }
439
440 /**
441 * Calls the {@link FormValidation#error(String)} method with a reasonable error message.
442 * Use this method when the {@link #open(URL)} or {@link #findText(BufferedReader, String)} fails.
443 *
444 * @param url
445 * Pass in the URL that was connected. Used for error diagnosis.
446 */
447 protected FormValidation handleIOException(String url, IOException e) throws IOException, ServletException {
448 // any invalid URL comes here
449 if(e.getMessage().equals(url))
450 // Sun JRE (and probably others too) often return just the URL in the error.
451 return error("Unable to connect "+url);
452 else
453 return error(e.getMessage());
454 }
455
456 /**
457 * Figures out the charset from the content-type header.
458 */
459 private String getCharset(URLConnection con) {
460 for( String t : con.getContentType().split(";") ) {
461 t = t.trim().toLowerCase(Locale.ENGLISH);
462 if(t.startsWith("charset="))
463 return t.substring(8);
464 }
465 // couldn't find it. HTML spec says default is US-ASCII,
466 // but UTF-8 is a better choice since
467 // (1) it's compatible with US-ASCII
468 // (2) a well-written web applications tend to use UTF-8
469 return "UTF-8";
470 }
471
472 /**
473 * Implement the actual form validation logic, by using other convenience methosd defined in this class.
474 * If you are not using any of those, you don't need to extend from this class.
475 */
476 protected abstract FormValidation check() throws IOException, ServletException;
477 }
478
479
480
481 public final Kind kind;
482
483 /**
484 * Instances should be created via one of the factory methods above.
485 * @param kind
486 */
487 private FormValidation(Kind kind) {
488 this.kind = kind;
489 }
490
491 private FormValidation(Kind kind, String message) {
492 super(message);
493 this.kind = kind;
494 }
495
496 public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException {
497 respond(rsp, renderHtml());
498 }
499
500 public abstract String renderHtml();
501
502 /**
503 * Sends out an arbitrary HTML fragment as the output.
504 */
505 protected void respond(StaplerResponse rsp, String html) throws IOException, ServletException {
506 rsp.setContentType("text/html;charset=UTF-8");
507 rsp.getWriter().print(html);
508 }
509 }
510