~ubuntu-branches/ubuntu/trusty/jenkins/trusty

« back to all changes in this revision

Viewing changes to core/src/main/java/hudson/tasks/Mailer.java

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-08-13 12:35:19 UTC
  • mfrom: (1.1.13)
  • Revision ID: package-import@ubuntu.com-20130813123519-tizgfxcr70trl7r0
Tags: 1.509.2+dfsg-1
* New upstream release (Closes: #706725):
  - d/control: Update versioned BD's:
    * jenkins-executable-war >= 1.28.
    * jenkins-instance-identity >= 1.3.
    * libjenkins-remoting-java >= 2.23.
    * libjenkins-winstone-java >= 0.9.10-jenkins-44.
    * libstapler-java >= 1.207.
    * libjenkins-json-java >= 2.4-jenkins-1.
    * libstapler-adjunct-timeline-java >= 1.4.
    * libstapler-adjunct-codemirror-java >= 1.2.
    * libmaven-hpi-plugin-java >= 1.93.
    * libjenkins-xstream-java >= 1.4.4-jenkins-3.
  - d/maven.rules: Map to older version of animal-sniffer-maven-plugin.
  - Add patch for compatibility with guava >= 0.14.
  - Add patch to exclude asm4 dependency via jnr-posix.
  - Fixes the following security vulnerabilities:
    CVE-2013-2034, CVE-2013-2033, CVE-2013-2034, CVE-2013-1808
* d/patches/*: Switch to using git patch-queue for managing patches.
* De-duplicate jars between libjenkins-java and jenkins-external-job-monitor
  (Closes: #701163):
  - d/control: Add dependency between jenkins-external-job-monitor ->
    libjenkins-java.
  - d/rules: 
    Drop installation of jenkins-core in jenkins-external-job-monitor.
  - d/jenkins-external-job-monitor.{links,install}: Link to jenkins-core
    in /usr/share/java instead of included version.
* Wait longer for jenkins to stop during restarts (Closes: #704848):
  - d/jenkins.init: Re-sync init script from upstream codebase.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * The MIT License
3
 
 * 
4
 
 * Copyright (c) 2004-2011, Sun Microsystems, Inc., Kohsuke Kawaguchi,
5
 
 * Bruce Chapman, Erik Ramfelt, Jean-Baptiste Quenot, Luca Domenico Milanesio
6
 
 * 
7
 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
 
 * of this software and associated documentation files (the "Software"), to deal
9
 
 * in the Software without restriction, including without limitation the rights
10
 
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
 
 * copies of the Software, and to permit persons to whom the Software is
12
 
 * furnished to do so, subject to the following conditions:
13
 
 * 
14
 
 * The above copyright notice and this permission notice shall be included in
15
 
 * all copies or substantial portions of the Software.
16
 
 * 
17
 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
 
 * THE SOFTWARE.
24
 
 */
25
 
package hudson.tasks;
26
 
 
27
 
import static hudson.Util.fixEmptyAndTrim;
28
 
 
29
 
import hudson.EnvVars;
30
 
import hudson.Extension;
31
 
import hudson.Functions;
32
 
import hudson.Launcher;
33
 
import hudson.RestrictedSince;
34
 
import hudson.Util;
35
 
import hudson.model.AbstractBuild;
36
 
import hudson.model.AbstractProject;
37
 
import hudson.model.BuildListener;
38
 
import hudson.model.User;
39
 
import hudson.model.UserPropertyDescriptor;
40
 
import hudson.util.FormValidation;
41
 
import hudson.util.Secret;
42
 
 
43
 
import org.apache.commons.lang.StringUtils;
44
 
import org.apache.tools.ant.types.selectors.SelectorUtils;
45
 
import org.kohsuke.accmod.Restricted;
46
 
import org.kohsuke.accmod.restrictions.NoExternalUse;
47
 
import org.kohsuke.stapler.QueryParameter;
48
 
import org.kohsuke.stapler.StaplerRequest;
49
 
import org.kohsuke.stapler.export.Exported;
50
 
 
51
 
import java.io.File;
52
 
import java.io.IOException;
53
 
import java.io.UnsupportedEncodingException;
54
 
import java.net.InetAddress;
55
 
import java.net.UnknownHostException;
56
 
import java.util.Date;
57
 
import java.util.Properties;
58
 
import java.util.logging.Level;
59
 
import java.util.logging.Logger;
60
 
import java.util.regex.Pattern;
61
 
import java.util.regex.Matcher;
62
 
import javax.mail.Address;
63
 
import javax.mail.Authenticator;
64
 
import javax.mail.Message;
65
 
import javax.mail.MessagingException;
66
 
import javax.mail.PasswordAuthentication;
67
 
import javax.mail.Session;
68
 
import javax.mail.Transport;
69
 
import javax.mail.internet.AddressException;
70
 
import javax.mail.internet.InternetAddress;
71
 
import javax.mail.internet.MimeMessage;
72
 
import javax.servlet.ServletException;
73
 
 
74
 
import org.apache.tools.ant.types.selectors.SelectorUtils;
75
 
import org.kohsuke.accmod.Restricted;
76
 
import org.kohsuke.accmod.restrictions.NoExternalUse;
77
 
import org.kohsuke.stapler.QueryParameter;
78
 
import org.kohsuke.stapler.StaplerRequest;
79
 
import org.kohsuke.stapler.export.Exported;
80
 
 
81
 
import jenkins.model.Jenkins;
82
 
import net.sf.json.JSONObject;
83
 
 
84
 
/**
85
 
 * {@link Publisher} that sends the build result in e-mail.
86
 
 *
87
 
 * @author Kohsuke Kawaguchi
88
 
 */
89
 
public class Mailer extends Notifier {
90
 
    protected static final Logger LOGGER = Logger.getLogger(Mailer.class.getName());
91
 
 
92
 
    /**
93
 
     * Whitespace-separated list of e-mail addresses that represent recipients.
94
 
     */
95
 
    public String recipients;
96
 
 
97
 
    /**
98
 
     * If true, only the first unstable build will be reported.
99
 
     */
100
 
    public boolean dontNotifyEveryUnstableBuild;
101
 
 
102
 
    /**
103
 
     * If true, individuals will receive e-mails regarding who broke the build.
104
 
     */
105
 
    public boolean sendToIndividuals;
106
 
 
107
 
    @Override
108
 
    public boolean perform(AbstractBuild<?,?> build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException {
109
 
        if(debug)
110
 
            listener.getLogger().println("Running mailer");
111
 
        // substitute build parameters
112
 
        EnvVars env = build.getEnvironment(listener);
113
 
        String recip = env.expand(recipients);
114
 
 
115
 
        return new MailSender(recip, dontNotifyEveryUnstableBuild, sendToIndividuals, descriptor().getCharset()) {
116
 
            /** Check whether a path (/-separated) will be archived. */
117
 
            @Override
118
 
            public boolean artifactMatches(String path, AbstractBuild<?,?> build) {
119
 
                ArtifactArchiver aa = build.getProject().getPublishersList().get(ArtifactArchiver.class);
120
 
                if (aa == null) {
121
 
                    LOGGER.finer("No ArtifactArchiver found");
122
 
                    return false;
123
 
                }
124
 
                String artifacts = aa.getArtifacts();
125
 
                for (String include : artifacts.split("[, ]+")) {
126
 
                    String pattern = include.replace(File.separatorChar, '/');
127
 
                    if (pattern.endsWith("/")) {
128
 
                        pattern += "**";
129
 
                    }
130
 
                    if (SelectorUtils.matchPath(pattern, path)) {
131
 
                        LOGGER.log(Level.FINER, "DescriptorImpl.artifactMatches true for {0} against {1}", new Object[] {path, pattern});
132
 
                        return true;
133
 
                    }
134
 
                }
135
 
                LOGGER.log(Level.FINER, "DescriptorImpl.artifactMatches for {0} matched none of {1}", new Object[] {path, artifacts});
136
 
                return false;
137
 
            }
138
 
        }.execute(build,listener);
139
 
    }
140
 
 
141
 
    /**
142
 
     * This class does explicit check pointing.
143
 
     */
144
 
    public BuildStepMonitor getRequiredMonitorService() {
145
 
        return BuildStepMonitor.NONE;
146
 
    }
147
 
 
148
 
    private static Pattern ADDRESS_PATTERN = Pattern.compile("\\s*([^<]*)<([^>]+)>\\s*");
149
 
    public static InternetAddress StringToAddress(String strAddress, String charset) throws AddressException, UnsupportedEncodingException {
150
 
        Matcher m = ADDRESS_PATTERN.matcher(strAddress);
151
 
        if(!m.matches()) {
152
 
            return new InternetAddress(strAddress);
153
 
        }
154
 
 
155
 
        String personal = m.group(1);
156
 
        String address = m.group(2);
157
 
        return new InternetAddress(address, personal, charset);
158
 
    }
159
 
 
160
 
    /**
161
 
     * @deprecated as of 1.286
162
 
     *      Use {@link #descriptor()} to obtain the current instance.
163
 
     */
164
 
    @Restricted(NoExternalUse.class)
165
 
    @RestrictedSince("1.355")
166
 
    public static DescriptorImpl DESCRIPTOR;
167
 
 
168
 
    public static DescriptorImpl descriptor() {
169
 
        return Jenkins.getInstance().getDescriptorByType(Mailer.DescriptorImpl.class);
170
 
    }
171
 
 
172
 
    @Extension
173
 
    public static final class DescriptorImpl extends BuildStepDescriptor<Publisher> {
174
 
        /**
175
 
         * The default e-mail address suffix appended to the user name found from changelog,
176
 
         * to send e-mails. Null if not configured.
177
 
         */
178
 
        private String defaultSuffix;
179
 
 
180
 
        /**
181
 
         * Hudson's own URL, to put into the e-mail.
182
 
         */
183
 
        private String hudsonUrl;
184
 
 
185
 
        /**
186
 
         * If non-null, use SMTP-AUTH with these information.
187
 
         */
188
 
        private String smtpAuthUsername;
189
 
 
190
 
        private Secret smtpAuthPassword;
191
 
 
192
 
        /**
193
 
         * The e-mail address that Hudson puts to "From:" field in outgoing e-mails.
194
 
         * Null if not configured.
195
 
         */
196
 
        private String adminAddress;
197
 
 
198
 
        /**
199
 
         * The e-mail address that Jenkins puts to "Reply-To" header in outgoing e-mails.
200
 
         * Null if not configured.
201
 
         */
202
 
        private String replyToAddress;
203
 
 
204
 
        /**
205
 
         * The SMTP server to use for sending e-mail. Null for default to the environment,
206
 
         * which is usually <tt>localhost</tt>.
207
 
         */
208
 
        private String smtpHost;
209
 
        
210
 
        /**
211
 
         * If true use SSL on port 465 (standard SMTPS) unless <code>smtpPort</code> is set.
212
 
         */
213
 
        private boolean useSsl;
214
 
 
215
 
        /**
216
 
         * The SMTP port to use for sending e-mail. Null for default to the environment,
217
 
         * which is usually <tt>25</tt>.
218
 
         */
219
 
        private String smtpPort;
220
 
 
221
 
        /**
222
 
         * The charset to use for the text and subject.
223
 
         */
224
 
        private String charset;
225
 
        
226
 
        /**
227
 
         * Used to keep track of number test e-mails.
228
 
         */
229
 
        private static transient int testEmailCount = 0;
230
 
        
231
 
 
232
 
        public DescriptorImpl() {
233
 
            load();
234
 
            DESCRIPTOR = this;
235
 
        }
236
 
 
237
 
        public String getDisplayName() {
238
 
            return Messages.Mailer_DisplayName();
239
 
        }
240
 
 
241
 
        @Override
242
 
        public String getHelpFile() {
243
 
            return "/help/project-config/mailer.html";
244
 
        }
245
 
 
246
 
        public String getDefaultSuffix() {
247
 
            return defaultSuffix;
248
 
        }
249
 
 
250
 
        public String getReplyToAddress() {
251
 
            return replyToAddress;
252
 
        }
253
 
 
254
 
        public void setReplyToAddress(String address) {
255
 
            this.replyToAddress = Util.fixEmpty(address);
256
 
        }
257
 
 
258
 
        /** JavaMail session. */
259
 
        public Session createSession() {
260
 
            return createSession(smtpHost,smtpPort,useSsl,smtpAuthUsername,smtpAuthPassword);
261
 
        }
262
 
        private static Session createSession(String smtpHost, String smtpPort, boolean useSsl, String smtpAuthUserName, Secret smtpAuthPassword) {
263
 
            smtpPort = fixEmptyAndTrim(smtpPort);
264
 
            smtpAuthUserName = fixEmptyAndTrim(smtpAuthUserName);
265
 
 
266
 
            Properties props = new Properties(System.getProperties());
267
 
            if(fixEmptyAndTrim(smtpHost)!=null)
268
 
                props.put("mail.smtp.host",smtpHost);
269
 
            if (smtpPort!=null) {
270
 
                props.put("mail.smtp.port", smtpPort);
271
 
            }
272
 
            if (useSsl) {
273
 
                /* This allows the user to override settings by setting system properties but
274
 
                 * also allows us to use the default SMTPs port of 465 if no port is already set.
275
 
                 * It would be cleaner to use smtps, but that's done by calling session.getTransport()...
276
 
                 * and thats done in mail sender, and it would be a bit of a hack to get it all to
277
 
                 * coordinate, and we can make it work through setting mail.smtp properties.
278
 
                 */
279
 
                if (props.getProperty("mail.smtp.socketFactory.port") == null) {
280
 
                    String port = smtpPort==null?"465":smtpPort;
281
 
                    props.put("mail.smtp.port", port);
282
 
                    props.put("mail.smtp.socketFactory.port", port);
283
 
                }
284
 
                if (props.getProperty("mail.smtp.socketFactory.class") == null) {
285
 
                        props.put("mail.smtp.socketFactory.class","javax.net.ssl.SSLSocketFactory");
286
 
                }
287
 
                                props.put("mail.smtp.socketFactory.fallback", "false");
288
 
                        }
289
 
            if(smtpAuthUserName!=null)
290
 
                props.put("mail.smtp.auth","true");
291
 
 
292
 
            // avoid hang by setting some timeout. 
293
 
            props.put("mail.smtp.timeout","60000");
294
 
            props.put("mail.smtp.connectiontimeout","60000");
295
 
 
296
 
            return Session.getInstance(props,getAuthenticator(smtpAuthUserName,Secret.toString(smtpAuthPassword)));
297
 
        }
298
 
 
299
 
        private static Authenticator getAuthenticator(final String smtpAuthUserName, final String smtpAuthPassword) {
300
 
            if(smtpAuthUserName==null)    return null;
301
 
            return new Authenticator() {
302
 
                @Override
303
 
                protected PasswordAuthentication getPasswordAuthentication() {
304
 
                    return new PasswordAuthentication(smtpAuthUserName,smtpAuthPassword);
305
 
                }
306
 
            };
307
 
        }
308
 
 
309
 
        @Override
310
 
        public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
311
 
            // this code is brain dead
312
 
            smtpHost = nullify(json.getString("smtpServer"));
313
 
            setAdminAddress(json.getString("adminAddress"));
314
 
            setReplyToAddress(json.getString("replyToAddress"));
315
 
 
316
 
            defaultSuffix = nullify(json.getString("defaultSuffix"));
317
 
            String url = nullify(json.getString("url"));
318
 
            if(url!=null && !url.endsWith("/"))
319
 
                url += '/';
320
 
            hudsonUrl = url;
321
 
 
322
 
            if(json.has("useSMTPAuth")) {
323
 
                JSONObject auth = json.getJSONObject("useSMTPAuth");
324
 
                smtpAuthUsername = nullify(auth.getString("smtpAuthUserName"));
325
 
                smtpAuthPassword = Secret.fromString(nullify(auth.getString("smtpAuthPassword")));
326
 
            } else {
327
 
                smtpAuthUsername = null;
328
 
                smtpAuthPassword = null;
329
 
            }
330
 
            smtpPort = nullify(json.getString("smtpPort"));
331
 
            useSsl = json.getBoolean("useSsl");
332
 
            charset = json.getString("charset");
333
 
            if (charset == null || charset.length() == 0)
334
 
                charset = "UTF-8";
335
 
            
336
 
            save();
337
 
            return true;
338
 
        }
339
 
 
340
 
        private String nullify(String v) {
341
 
            if(v!=null && v.length()==0)    v=null;
342
 
            return v;
343
 
        }
344
 
 
345
 
        public String getSmtpServer() {
346
 
            return smtpHost;
347
 
        }
348
 
 
349
 
        public String getAdminAddress() {
350
 
            String v = adminAddress;
351
 
            if(v==null)     v = Messages.Mailer_Address_Not_Configured();
352
 
            return v;
353
 
        }
354
 
 
355
 
        public String getUrl() {
356
 
            return hudsonUrl;
357
 
        }
358
 
 
359
 
        public String getSmtpAuthUserName() {
360
 
            return smtpAuthUsername;
361
 
        }
362
 
 
363
 
        public String getSmtpAuthPassword() {
364
 
            if (smtpAuthPassword==null) return null;
365
 
            return Secret.toString(smtpAuthPassword);
366
 
        }
367
 
        
368
 
        public boolean getUseSsl() {
369
 
                return useSsl;
370
 
        }
371
 
 
372
 
        public String getSmtpPort() {
373
 
                return smtpPort;
374
 
        }
375
 
        
376
 
        public String getCharset() {
377
 
                String c = charset;
378
 
                if (c == null || c.length() == 0)       c = "UTF-8";
379
 
                return c;
380
 
        }
381
 
 
382
 
        public void setDefaultSuffix(String defaultSuffix) {
383
 
            this.defaultSuffix = defaultSuffix;
384
 
        }
385
 
 
386
 
        public void setHudsonUrl(String hudsonUrl) {
387
 
            this.hudsonUrl = hudsonUrl;
388
 
        }
389
 
 
390
 
        public void setAdminAddress(String adminAddress) {
391
 
            if(adminAddress.startsWith("\"") && adminAddress.endsWith("\"")) {
392
 
                // some users apparently quote the whole thing. Don't konw why
393
 
                // anyone does this, but it's a machine's job to forgive human mistake
394
 
                adminAddress = adminAddress.substring(1,adminAddress.length()-1);
395
 
            }
396
 
            this.adminAddress = adminAddress;
397
 
        }
398
 
 
399
 
        public void setSmtpHost(String smtpHost) {
400
 
            this.smtpHost = smtpHost;
401
 
        }
402
 
 
403
 
        public void setUseSsl(boolean useSsl) {
404
 
            this.useSsl = useSsl;
405
 
        }
406
 
 
407
 
        public void setSmtpPort(String smtpPort) {
408
 
            this.smtpPort = smtpPort;
409
 
        }
410
 
        
411
 
        public void setCharset(String chaset) {
412
 
            this.charset = chaset;
413
 
        }
414
 
 
415
 
        public void setSmtpAuth(String userName, String password) {
416
 
            this.smtpAuthUsername = userName;
417
 
            this.smtpAuthPassword = Secret.fromString(password);
418
 
        }
419
 
 
420
 
        @Override
421
 
        public Publisher newInstance(StaplerRequest req, JSONObject formData) {
422
 
            Mailer m = new Mailer();
423
 
            req.bindParameters(m,"mailer_");
424
 
            m.dontNotifyEveryUnstableBuild = req.getParameter("mailer_notifyEveryUnstableBuild")==null;
425
 
 
426
 
            if(hudsonUrl==null) {
427
 
                // if Hudson URL is not configured yet, infer some default
428
 
                hudsonUrl = Functions.inferHudsonURL(req);
429
 
                save();
430
 
            }
431
 
 
432
 
            return m;
433
 
        }
434
 
 
435
 
        /**
436
 
         * Checks the URL in <tt>global.jelly</tt>
437
 
         */
438
 
        public FormValidation doCheckUrl(@QueryParameter String value) {
439
 
            if(value.startsWith("http://localhost"))
440
 
                return FormValidation.warning(Messages.Mailer_Localhost_Error());
441
 
            return FormValidation.ok();
442
 
        }
443
 
 
444
 
        public FormValidation doAddressCheck(@QueryParameter String value) {
445
 
            try {
446
 
                new InternetAddress(value);
447
 
                return FormValidation.ok();
448
 
            } catch (AddressException e) {
449
 
                return FormValidation.error(e.getMessage());
450
 
            }
451
 
        }
452
 
 
453
 
        public FormValidation doCheckSmtpServer(@QueryParameter String value) {
454
 
            try {
455
 
                if (fixEmptyAndTrim(value)!=null)
456
 
                    InetAddress.getByName(value);
457
 
                return FormValidation.ok();
458
 
            } catch (UnknownHostException e) {
459
 
                return FormValidation.error(Messages.Mailer_Unknown_Host_Name()+value);
460
 
            }
461
 
        }
462
 
 
463
 
        public FormValidation doCheckAdminAddress(@QueryParameter String value) {
464
 
            return doAddressCheck(value);
465
 
        }
466
 
 
467
 
        public FormValidation doCheckDefaultSuffix(@QueryParameter String value) {
468
 
            if (value.matches("@[A-Za-z0-9.\\-]+") || fixEmptyAndTrim(value)==null)
469
 
                return FormValidation.ok();
470
 
            else
471
 
                return FormValidation.error(Messages.Mailer_Suffix_Error());
472
 
        }
473
 
 
474
 
        /**
475
 
         * Send an email to the admin address
476
 
         * @throws IOException
477
 
         * @throws ServletException
478
 
         * @throws InterruptedException
479
 
         */
480
 
        public FormValidation doSendTestMail(
481
 
                @QueryParameter String smtpServer, @QueryParameter String adminAddress, @QueryParameter boolean useSMTPAuth,
482
 
                @QueryParameter String smtpAuthUserName, @QueryParameter String smtpAuthPassword,
483
 
                @QueryParameter boolean useSsl, @QueryParameter String smtpPort, @QueryParameter String charset,
484
 
                @QueryParameter String sendTestMailTo) throws IOException, ServletException, InterruptedException {
485
 
            try {
486
 
                if (!useSMTPAuth)   smtpAuthUserName = smtpAuthPassword = null;
487
 
                
488
 
                MimeMessage msg = new MimeMessage(createSession(smtpServer,smtpPort,useSsl,smtpAuthUserName,Secret.fromString(smtpAuthPassword)));
489
 
                msg.setSubject(Messages.Mailer_TestMail_Subject(++testEmailCount), charset);
490
 
                msg.setText(Messages.Mailer_TestMail_Content(testEmailCount, Jenkins.getInstance().getDisplayName()), charset);
491
 
                msg.setFrom(StringToAddress(adminAddress, charset));
492
 
                if (StringUtils.isNotBlank(replyToAddress)) {
493
 
                    msg.setReplyTo(new Address[]{StringToAddress(replyToAddress, charset)});
494
 
                }
495
 
                msg.setSentDate(new Date());
496
 
                msg.setRecipient(Message.RecipientType.TO, StringToAddress(sendTestMailTo, charset));
497
 
 
498
 
                Transport.send(msg);                
499
 
                return FormValidation.ok(Messages.Mailer_EmailSentSuccessfully());
500
 
            } catch (MessagingException e) {
501
 
                return FormValidation.errorWithMarkup("<p>"+Messages.Mailer_FailedToSendEmail()+"</p><pre>"+Util.escape(Functions.printThrowable(e))+"</pre>");
502
 
            }
503
 
        }
504
 
 
505
 
        public boolean isApplicable(Class<? extends AbstractProject> jobType) {
506
 
            return true;
507
 
        }
508
 
    }
509
 
 
510
 
    /**
511
 
     * Per user property that is e-mail address.
512
 
     */
513
 
    public static class UserProperty extends hudson.model.UserProperty {
514
 
        /**
515
 
         * The user's e-mail address.
516
 
         * Null to leave it to default.
517
 
         */
518
 
        private final String emailAddress;
519
 
 
520
 
        public UserProperty(String emailAddress) {
521
 
            this.emailAddress = emailAddress;
522
 
        }
523
 
 
524
 
        @Exported
525
 
        public String getAddress() {
526
 
            if(hasExplicitlyConfiguredAddress())
527
 
                return emailAddress;
528
 
 
529
 
            // try the inference logic
530
 
            return MailAddressResolver.resolve(user);
531
 
        }
532
 
 
533
 
        /**
534
 
         * Has the user configured a value explicitly (true), or is it inferred (false)?
535
 
         */
536
 
        public boolean hasExplicitlyConfiguredAddress() {
537
 
            return Util.fixEmptyAndTrim(emailAddress)!=null;
538
 
        }
539
 
 
540
 
        @Extension
541
 
        public static final class DescriptorImpl extends UserPropertyDescriptor {
542
 
            public String getDisplayName() {
543
 
                return Messages.Mailer_UserProperty_DisplayName();
544
 
            }
545
 
 
546
 
            public UserProperty newInstance(User user) {
547
 
                return new UserProperty(null);
548
 
            }
549
 
 
550
 
            @Override
551
 
            public UserProperty newInstance(StaplerRequest req, JSONObject formData) throws FormException {
552
 
                return new UserProperty(req.getParameter("email.address"));
553
 
            }
554
 
        }
555
 
    }
556
 
 
557
 
    /**
558
 
     * Debug probe point to be activated by the scripting console.
559
 
     */
560
 
    public static boolean debug = false;
561
 
}