aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorakozak2011-11-25 02:16:14 (EST)
committerWinston Prakash2011-12-01 20:47:32 (EST)
commit101a7fe4657b6462056509760f5994c51e751e71 (patch)
tree9746ee008a7231574aeea733934ca12014aad906
parentf8c8d782030eff36be1420bd54dca6ec4480de8a (diff)
downloadorg.eclipse.hudson.core-101a7fe4657b6462056509760f5994c51e751e71.zip
org.eclipse.hudson.core-101a7fe4657b6462056509760f5994c51e751e71.tar.gz
org.eclipse.hudson.core-101a7fe4657b6462056509760f5994c51e751e71.tar.bz2
FIxed HUDSON-8938 (Option to notify user of Hudson account creation Refactored MailSender in order to simplify the logic Improved mail templates.
Signed-off-by: Winston Prakash <winston.prakash@gmail.com>
-rw-r--r--hudson-core/src/main/java/hudson/mail/BaseMailSender.java153
-rw-r--r--hudson-core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java66
-rw-r--r--hudson-core/src/main/java/hudson/tasks/MailSender.java311
-rw-r--r--hudson-core/src/main/java/hudson/tasks/Mailer.java39
-rw-r--r--hudson-core/src/main/java/hudson/tasks/mail/BuildResultMail.java40
-rw-r--r--hudson-core/src/main/java/hudson/tasks/mail/impl/BackToNormalBuildMail.java54
-rw-r--r--hudson-core/src/main/java/hudson/tasks/mail/impl/BaseBuildResultMail.java271
-rw-r--r--hudson-core/src/main/java/hudson/tasks/mail/impl/FailureBuildMail.java164
-rw-r--r--hudson-core/src/main/java/hudson/tasks/mail/impl/UnstableBuildMail.java67
-rw-r--r--hudson-core/src/main/resources/hudson/mail/Messages.properties10
-rw-r--r--hudson-core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/config.jelly7
11 files changed, 856 insertions, 326 deletions
diff --git a/hudson-core/src/main/java/hudson/mail/BaseMailSender.java b/hudson-core/src/main/java/hudson/mail/BaseMailSender.java
new file mode 100644
index 0000000..4e35043
--- /dev/null
+++ b/hudson-core/src/main/java/hudson/mail/BaseMailSender.java
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2011 Oracle Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ * Anton Kozak
+ *
+ *******************************************************************************/
+package hudson.mail;
+
+import hudson.tasks.HudsonMimeMessage;
+import hudson.tasks.Mailer;
+import java.util.Date;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.StringTokenizer;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+
+/**
+ * Base logic of sending out notification e-mail.
+ */
+public abstract class BaseMailSender {
+ private static final String DEFAULT_TEXT = "Should be overridden";
+
+ protected static final String DEFAULT_CHARSET = "UTF-8";
+ /**
+ * Whitespace-separated list of e-mail addresses that represent recipients.
+ */
+ private String recipients;
+
+ /**
+ * The charset to use for the text and subject.
+ */
+ private String charset;
+
+
+ public BaseMailSender(String recipients) {
+ this(recipients, DEFAULT_CHARSET);
+ }
+
+ public BaseMailSender(String recipients, String charset) {
+ this.recipients = recipients;
+ this.charset = charset;
+ }
+
+ /**
+ * Returns recipients.
+ *
+ * @return recipients.
+ */
+ public String getRecipients() {
+ return recipients;
+ }
+
+ /**
+ * Returns charset.
+ *
+ * @return charset.
+ */
+ public String getCharset() {
+ return charset;
+ }
+
+ public boolean execute() {
+ try {
+ MimeMessage mail = getMail();
+ if (mail != null) {
+ if (mail.getAllRecipients() != null) {
+ Mailer.descriptor().send((HudsonMimeMessage) mail);
+ }
+ }
+ } catch (MessagingException ignore) {
+ //TODO add logging
+ }
+ return true;
+ }
+
+ /**
+ * Returns prepared email.
+ *
+ * @return prepared email.
+ * @throws MessagingException exception if any.
+ */
+ protected MimeMessage getMail() throws MessagingException {
+ MimeMessage msg = new HudsonMimeMessage(Mailer.descriptor().createSession());
+ msg.setContent("", "text/plain");
+ msg.setFrom(new InternetAddress(Mailer.descriptor().getAdminAddress()));
+ msg.setSentDate(new Date());
+
+ Set<InternetAddress> rcp = new LinkedHashSet<InternetAddress>();
+ StringTokenizer tokens = new StringTokenizer(recipients);
+ while (tokens.hasMoreTokens()) {
+ String address = tokens.nextToken();
+ try {
+ rcp.add(new InternetAddress(address));
+ } catch (AddressException ignore) {
+ // ignore bad address, but try to send to other addresses
+ }
+ }
+ msg.setRecipients(Message.RecipientType.TO, rcp.toArray(new InternetAddress[rcp.size()]));
+ msg.setSubject(new StringBuilder().append(getSubjectPrefix()).append(" ").append(getSubject()).toString(),
+ charset);
+ msg.setText(new StringBuilder().append(getText()).append(getTextFooter()).toString(), charset);
+ return msg;
+ }
+
+ /**
+ * Returns the text of the email.
+ *
+ * @return the text of the email.
+ */
+ protected String getText() {
+ return DEFAULT_TEXT;
+ }
+
+ /**
+ * Returns the subject of the email.
+ *
+ * @return the subject of the email.
+ */
+ protected String getSubject() {
+ return DEFAULT_TEXT;
+ }
+
+ /**
+ * Returns text footer for all automatically generated emails.
+ *
+ * @return text footer.
+ */
+ protected String getTextFooter() {
+ return hudson.mail.Messages.hudson_email_footer();
+ }
+
+ /**
+ * Returns prefix for subject of automatically generated emails.
+ *
+ * @return prefix for subject.
+ */
+ protected String getSubjectPrefix() {
+ return hudson.mail.Messages.hudson_email_subject_prefix();
+ }
+
+}
diff --git a/hudson-core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java b/hudson-core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java
index dbfb67a..85b471c 100644
--- a/hudson-core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java
+++ b/hudson-core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java
@@ -16,19 +16,21 @@
package hudson.security;
-import hudson.security.captcha.CaptchaSupport;
+import hudson.mail.BaseMailSender;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import hudson.Extension;
import hudson.Util;
import hudson.diagnosis.OldDataMonitor;
import hudson.model.*;
import hudson.security.FederatedLoginService.FederatedIdentity;
+import hudson.security.captcha.CaptchaSupport;
import hudson.tasks.Mailer;
import hudson.util.PluginServletFilter;
import hudson.util.Protector;
import hudson.util.Scrambler;
import hudson.util.XStream2;
import net.sf.json.JSONObject;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.security.Authentication;
import org.springframework.security.AuthenticationException;
import org.springframework.security.BadCredentialsException;
@@ -74,21 +76,34 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
* If true, captcha will be enabled.
*/
private final boolean enableCaptcha;
-
-
+
+ /**
+ * If true, user will be notified of Hudson account creation.
+ */
+ private final boolean notifyUser;
+
/**
* @deprecated as of 2.0.1
*/
@Deprecated
public HudsonPrivateSecurityRealm(boolean allowsSignup) {
- this(allowsSignup, true, null);
+ this(allowsSignup, true);
+ }
+
+ /**
+ * @deprecated as of 2.2.0
+ */
+ @Deprecated
+ public HudsonPrivateSecurityRealm(boolean allowsSignup, boolean enableCaptcha) {
+ this(allowsSignup, true, null, false);
}
@DataBoundConstructor
- public HudsonPrivateSecurityRealm(boolean allowsSignup, boolean enableCaptcha, CaptchaSupport captchaSupport) {
+ public HudsonPrivateSecurityRealm(boolean allowsSignup, boolean enableCaptcha, CaptchaSupport captchaSupport, boolean notifyUser) {
this.disableSignup = !allowsSignup;
this.enableCaptcha = enableCaptcha;
setCaptchaSupport(captchaSupport);
+ this.notifyUser = notifyUser;
if(!allowsSignup && !hasSomeUser()) {
// if Hudson is newly set up with the security realm and there's no user account created yet,
@@ -114,7 +129,16 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
public boolean isEnableCaptcha() {
return enableCaptcha;
}
-
+
+ /**
+ * Returns true if Hudson should notify user of account creation.
+ *
+ * @return true if Hudson should notify user of account creation.
+ */
+ public boolean isNotifyUser() {
+ return notifyUser;
+ }
+
/**
* Computes if this Hudson has some user accounts configured.
*
@@ -138,7 +162,7 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
@Override
public Details loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
- User u = User.get(username,false);
+ User u = User.get(username, false);
Details p = u!=null ? u.getProperty(Details.class) : null;
if(p==null)
throw new UsernameNotFoundException("Password is not set: "+username);
@@ -258,7 +282,7 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
AuthorizationStrategy as = Hudson.getInstance().getAuthorizationStrategy();
if (as instanceof GlobalMatrixAuthorizationStrategy) {
GlobalMatrixAuthorizationStrategy ma = (GlobalMatrixAuthorizationStrategy) as;
- ma.add(Hudson.ADMINISTER,u.getId());
+ ma.add(Hudson.ADMINISTER, u.getId());
}
}
@@ -303,13 +327,33 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
}
// register the user
- User user = createAccount(si.username,si.password1);
+ final User user = createAccount(si.username,si.password1);
user.addProperty(new Mailer.UserProperty(si.email));
user.setFullName(si.fullname);
user.save();
+ if (notifyUser && StringUtils.isNotEmpty(si.email)) {
+ notifyUser(si.username, si.email, si.fullname, si.password1);
+ }
return user;
}
+ private void notifyUser(final String username, final String email, final String fullname, final String passwd) {
+ new BaseMailSender(email) {
+ @Override
+ protected String getText() {
+ String baseUrl = Mailer.descriptor().getUrl();
+ return hudson.mail.Messages
+ .account_creation_email_text(fullname != null ? fullname : "", baseUrl, email, username,
+ passwd);
+ }
+
+ @Override
+ protected String getSubject() {
+ return hudson.mail.Messages.account_creation_email_subject();
+ }
+ }.execute();
+ }
+
/**
* Creates a new user account by registering a password to the user.
*/
@@ -550,7 +594,7 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
public static final class ManageUserLinks extends ManagementLink {
public String getIconFileName() {
if(Hudson.getInstance().getSecurityRealm() instanceof HudsonPrivateSecurityRealm)
- return "user.png";
+ return "user.gif";
else
return null; // not applicable now
}
@@ -578,7 +622,7 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
*
* <p>
* This abbreviates the need to store the salt separately, which in turn allows us to hide the salt handling
- * in this little class. The rest of the Spring Security thinks that we are not using salt.
+ * in this little class. The rest of the Acegi thinks that we are not using salt.
*/
public static final PasswordEncoder PASSWORD_ENCODER = new PasswordEncoder() {
private final PasswordEncoder passwordEncoder = new ShaPasswordEncoder(256);
diff --git a/hudson-core/src/main/java/hudson/tasks/MailSender.java b/hudson-core/src/main/java/hudson/tasks/MailSender.java
index 0d583c0..3458df1 100644
--- a/hudson-core/src/main/java/hudson/tasks/MailSender.java
+++ b/hudson-core/src/main/java/hudson/tasks/MailSender.java
@@ -1,6 +1,6 @@
/*******************************************************************************
*
- * Copyright (c) 2004-2010 Oracle Corporation.
+ * Copyright (c) 2004-2011 Oracle Corporation.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -9,42 +9,28 @@
*
* Contributors:
*
- * Kohsuke Kawaguchi, Bruce Chapman, Daniel Dyer, Jean-Baptiste Quenot
+ * Kohsuke Kawaguchi, Bruce Chapman, Daniel Dyer, Jean-Baptiste Quenot, Anton Kozak
*
*
*******************************************************************************/
package hudson.tasks;
-import hudson.FilePath;
-import hudson.Functions;
-import hudson.Util;
+import hudson.mail.BaseMailSender;
+import hudson.tasks.mail.impl.BackToNormalBuildMail;
+import hudson.tasks.mail.impl.FailureBuildMail;
+import hudson.tasks.mail.impl.UnstableBuildMail;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.CheckPoint;
-import hudson.model.Hudson;
import hudson.model.Result;
-import hudson.model.User;
-import hudson.scm.ChangeLogSet;
-import java.io.File;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
import java.util.List;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import javax.mail.Address;
-import javax.mail.Message;
import javax.mail.MessagingException;
-import javax.mail.internet.AddressException;
-import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
/**
@@ -53,13 +39,9 @@ import javax.mail.internet.MimeMessage;
* @author Jesse Glick
* @author Kohsuke Kawaguchi
*/
-public class MailSender {
- /**
- * Whitespace-separated list of e-mail addresses that represent recipients.
- */
- private String recipients;
-
- private List<AbstractProject> includeUpstreamCommitters = new ArrayList<AbstractProject>();
+public class MailSender extends BaseMailSender {
+
+ private List<AbstractProject> upstreamProjects = new ArrayList<AbstractProject>();
/**
* If true, only the first unstable build will be reported.
@@ -70,27 +52,24 @@ public class MailSender {
* If true, individuals will receive e-mails regarding who broke the build.
*/
private boolean sendToIndividuals;
-
- /**
- * The charset to use for the text and subject.
- */
- private String charset;
public MailSender(String recipients, boolean dontNotifyEveryUnstableBuild, boolean sendToIndividuals) {
- this(recipients, dontNotifyEveryUnstableBuild, sendToIndividuals, "UTF-8");
+ this(recipients, dontNotifyEveryUnstableBuild, sendToIndividuals, DEFAULT_CHARSET);
}
- public MailSender(String recipients, boolean dontNotifyEveryUnstableBuild, boolean sendToIndividuals, String charset) {
- this(recipients,dontNotifyEveryUnstableBuild,sendToIndividuals,charset, Collections.<AbstractProject>emptyList());
+ public MailSender(String recipients, boolean dontNotifyEveryUnstableBuild, boolean sendToIndividuals,
+ String charset) {
+ this(recipients, dontNotifyEveryUnstableBuild, sendToIndividuals, charset,
+ Collections.<AbstractProject>emptyList());
}
-
- public MailSender(String recipients, boolean dontNotifyEveryUnstableBuild, boolean sendToIndividuals, String charset, Collection<AbstractProject> includeUpstreamCommitters) {
- this.recipients = recipients;
+
+ public MailSender(String recipients, boolean dontNotifyEveryUnstableBuild, boolean sendToIndividuals,
+ String charset, Collection<AbstractProject> includeUpstreamCommitters) {
+ super(recipients, charset);
this.dontNotifyEveryUnstableBuild = dontNotifyEveryUnstableBuild;
this.sendToIndividuals = sendToIndividuals;
- this.charset = charset;
- this.includeUpstreamCommitters.addAll(includeUpstreamCommitters);
+ this.upstreamProjects.addAll(includeUpstreamCommitters);
}
public boolean execute(AbstractBuild<?, ?> build, BuildListener listener) throws InterruptedException {
@@ -147,271 +126,41 @@ public class MailSender {
protected MimeMessage getMail(AbstractBuild<?, ?> build, BuildListener listener) throws MessagingException, InterruptedException {
if (build.getResult() == Result.FAILURE) {
- return createFailureMail(build, listener);
+ return new FailureBuildMail(getRecipients(), sendToIndividuals, upstreamProjects, getCharset()).
+ getMail(build, listener);
}
if (build.getResult() == Result.UNSTABLE) {
if (!dontNotifyEveryUnstableBuild)
- return createUnstableMail(build, listener);
+ return new UnstableBuildMail(getRecipients(), sendToIndividuals, upstreamProjects, getCharset()).
+ getMail(build, listener);
Result prev = findPreviousBuildResult(build);
if (prev == Result.SUCCESS)
- return createUnstableMail(build, listener);
+ return new UnstableBuildMail(getRecipients(), sendToIndividuals, upstreamProjects, getCharset()).
+ getMail(build, listener);
}
if (build.getResult() == Result.SUCCESS) {
Result prev = findPreviousBuildResult(build);
if (prev == Result.FAILURE)
- return createBackToNormalMail(build, Messages.MailSender_BackToNormal_Normal(), listener);
+ return new BackToNormalBuildMail(getRecipients(), sendToIndividuals, upstreamProjects, getCharset(),
+ Messages.MailSender_BackToNormal_Normal()).getMail(build, listener);
if (prev == Result.UNSTABLE)
- return createBackToNormalMail(build, Messages.MailSender_BackToNormal_Stable(), listener);
+ return new BackToNormalBuildMail(getRecipients(), sendToIndividuals, upstreamProjects, getCharset(),
+ Messages.MailSender_BackToNormal_Stable()).getMail(build, listener);
}
return null;
}
- private MimeMessage createBackToNormalMail(AbstractBuild<?, ?> build, String subject, BuildListener listener) throws MessagingException {
- MimeMessage msg = createEmptyMail(build, listener);
-
- msg.setSubject(getSubject(build, Messages.MailSender_BackToNormalMail_Subject(subject)),charset);
- StringBuilder buf = new StringBuilder();
- appendBuildUrl(build, buf);
- msg.setText(buf.toString(),charset);
-
- return msg;
- }
-
- private MimeMessage createUnstableMail(AbstractBuild<?, ?> build, BuildListener listener) throws MessagingException {
- MimeMessage msg = createEmptyMail(build, listener);
-
- String subject = Messages.MailSender_UnstableMail_Subject();
-
- AbstractBuild<?, ?> prev = build.getPreviousBuild();
- boolean still = false;
- if(prev!=null) {
- if(prev.getResult()==Result.SUCCESS)
- subject =Messages.MailSender_UnstableMail_ToUnStable_Subject();
- else if(prev.getResult()==Result.UNSTABLE) {
- subject = Messages.MailSender_UnstableMail_StillUnstable_Subject();
- still = true;
- }
- }
-
- msg.setSubject(getSubject(build, subject),charset);
- StringBuilder buf = new StringBuilder();
- // Link to project changes summary for "still unstable" if this or last build has changes
- if (still && !(build.getChangeSet().isEmptySet() && prev.getChangeSet().isEmptySet()))
- appendUrl(Util.encode(build.getProject().getUrl()) + "changes", buf);
- else
- appendBuildUrl(build, buf);
- msg.setText(buf.toString(), charset);
-
- return msg;
- }
-
- private void appendBuildUrl(AbstractBuild<?, ?> build, StringBuilder buf) {
- appendUrl(Util.encode(build.getUrl())
- + (build.getChangeSet().isEmptySet() ? "" : "changes"), buf);
- }
-
- private void appendUrl(String url, StringBuilder buf) {
- String baseUrl = Mailer.descriptor().getUrl();
- if (baseUrl != null)
- buf.append(Messages.MailSender_Link(baseUrl, url)).append("\n\n");
- }
-
- private MimeMessage createFailureMail(AbstractBuild<?, ?> build, BuildListener listener) throws MessagingException, InterruptedException {
- MimeMessage msg = createEmptyMail(build, listener);
-
- msg.setSubject(getSubject(build, Messages.MailSender_FailureMail_Subject()),charset);
-
- StringBuilder buf = new StringBuilder();
- appendBuildUrl(build, buf);
-
- boolean firstChange = true;
- for (ChangeLogSet.Entry entry : build.getChangeSet()) {
- if (firstChange) {
- firstChange = false;
- buf.append(Messages.MailSender_FailureMail_Changes()).append("\n\n");
- }
- buf.append('[');
- buf.append(entry.getAuthor().getFullName());
- buf.append("] ");
- String m = entry.getMsg();
- if (m!=null) {
- buf.append(m);
- if (!m.endsWith("\n")) {
- buf.append('\n');
- }
- }
- buf.append('\n');
- }
-
- buf.append("------------------------------------------\n");
-
- try {
- // Restrict max log size to avoid sending enormous logs over email.
- // Interested users can always look at the log on the web server.
- List<String> lines = build.getLog(MAX_LOG_LINES);
-
- String workspaceUrl = null, artifactUrl = null;
- Pattern wsPattern = null;
- String baseUrl = Mailer.descriptor().getUrl();
- if (baseUrl != null) {
- // Hyperlink local file paths to the repository workspace or build artifacts.
- // Note that it is possible for a failure mail to refer to a file using a workspace
- // URL which has already been corrected in a subsequent build. To fix, archive.
- workspaceUrl = baseUrl + Util.encode(build.getProject().getUrl()) + "ws/";
- artifactUrl = baseUrl + Util.encode(build.getUrl()) + "artifact/";
- FilePath ws = build.getWorkspace();
- // Match either file or URL patterns, i.e. either
- // c:\hudson\workdir\jobs\foo\workspace\src\Foo.java
- // file:/c:/hudson/workdir/jobs/foo/workspace/src/Foo.java
- // will be mapped to one of:
- // http://host/hudson/job/foo/ws/src/Foo.java
- // http://host/hudson/job/foo/123/artifact/src/Foo.java
- // Careful with path separator between $1 and $2:
- // workspaceDir will not normally end with one;
- // workspaceDir.toURI() will end with '/' if and only if workspaceDir.exists() at time of call
- wsPattern = Pattern.compile("(" +
- Pattern.quote(ws.getRemote()) + "|" + Pattern.quote(ws.toURI().toString()) + ")[/\\\\]?([^:#\\s]*)");
- }
- for (String line : lines) {
- line = line.replace('\0',' '); // shall we replace other control code? This one is motivated by http://www.nabble.com/Problems-with-NULL-characters-in-generated-output-td25005177.html
- if (wsPattern != null) {
- // Perl: $line =~ s{$rx}{$path = $2; $path =~ s!\\\\!/!g; $workspaceUrl . $path}eg;
- Matcher m = wsPattern.matcher(line);
- int pos = 0;
- while (m.find(pos)) {
- String path = m.group(2).replace(File.separatorChar, '/');
- String linkUrl = artifactMatches(path, build) ? artifactUrl : workspaceUrl;
- String prefix = line.substring(0, m.start()) + '<' + linkUrl + Util.encode(path) + '>';
- pos = prefix.length();
- line = prefix + line.substring(m.end());
- // XXX better style to reuse Matcher and fix offsets, but more work
- m = wsPattern.matcher(line);
- }
- }
- buf.append(line);
- buf.append('\n');
- }
- } catch (IOException e) {
- // somehow failed to read the contents of the log
- buf.append(Messages.MailSender_FailureMail_FailedToAccessBuildLog()).append("\n\n").append(Functions.printThrowable(e));
- }
-
- msg.setText(buf.toString(),charset);
-
- return msg;
- }
-
- private MimeMessage createEmptyMail(AbstractBuild<?, ?> build, BuildListener listener) throws MessagingException {
- MimeMessage msg = new HudsonMimeMessage(Mailer.descriptor().createSession());
- // TODO: I'd like to put the URL to the page in here,
- // but how do I obtain that?
- msg.setContent("", "text/plain");
- msg.setFrom(new InternetAddress(Mailer.descriptor().getAdminAddress()));
- msg.setSentDate(new Date());
-
- Set<InternetAddress> rcp = new LinkedHashSet<InternetAddress>();
- StringTokenizer tokens = new StringTokenizer(recipients);
- while (tokens.hasMoreTokens()) {
- String address = tokens.nextToken();
- if(address.startsWith("upstream-individuals:")) {
- // people who made a change in the upstream
- String projectName = address.substring("upstream-individuals:".length());
- AbstractProject up = Hudson.getInstance().getItemByFullName(projectName,AbstractProject.class);
- if(up==null) {
- listener.getLogger().println("No such project exist: "+projectName);
- continue;
- }
- includeCulpritsOf(up, build, listener, rcp);
- } else {
- // ordinary address
- try {
- rcp.add(new InternetAddress(address));
- } catch (AddressException e) {
- // report bad address, but try to send to other addresses
- e.printStackTrace(listener.error(e.getMessage()));
- }
- }
- }
-
- for (AbstractProject project : includeUpstreamCommitters) {
- includeCulpritsOf(project, build, listener, rcp);
- }
-
- if (sendToIndividuals) {
- Set<User> culprits = build.getCulprits();
-
- if(debug)
- listener.getLogger().println("Trying to send e-mails to individuals who broke the build. sizeof(culprits)=="+culprits.size());
-
- rcp.addAll(buildCulpritList(listener,culprits));
- }
- msg.setRecipients(Message.RecipientType.TO, rcp.toArray(new InternetAddress[rcp.size()]));
-
- AbstractBuild<?, ?> pb = build.getPreviousBuild();
- if(pb!=null) {
- MailMessageIdAction b = pb.getAction(MailMessageIdAction.class);
- if(b!=null) {
- msg.setHeader("In-Reply-To",b.messageId);
- msg.setHeader("References",b.messageId);
- }
- }
-
- return msg;
- }
-
- private void includeCulpritsOf(AbstractProject upstreamProject, AbstractBuild<?, ?> currentBuild, BuildListener listener, Set<InternetAddress> recipientList) throws AddressException {
- AbstractBuild<?,?> upstreamBuild = currentBuild.getUpstreamRelationshipBuild(upstreamProject);
- AbstractBuild<?,?> previousBuild = currentBuild.getPreviousBuild();
- AbstractBuild<?,?> previousBuildUpstreamBuild = previousBuild!=null ? previousBuild.getUpstreamRelationshipBuild(upstreamProject) : null;
- if(previousBuild==null && upstreamBuild==null && previousBuildUpstreamBuild==null) {
- listener.getLogger().println("Unable to compute the changesets in "+ upstreamProject +". Is the fingerprint configured?");
- return;
- }
- if(previousBuild==null || upstreamBuild==null || previousBuildUpstreamBuild==null) {
- listener.getLogger().println("Unable to compute the changesets in "+ upstreamProject);
- return;
- }
- AbstractBuild<?,?> b=previousBuildUpstreamBuild;
- do {
- recipientList.addAll(buildCulpritList(listener,b.getCulprits()));
- b = b.getNextBuild();
- } while ( b != upstreamBuild && b != null );
- }
-
- private Set<InternetAddress> buildCulpritList(BuildListener listener, Set<User> culprits) throws AddressException {
- Set<InternetAddress> r = new HashSet<InternetAddress>();
- for (User a : culprits) {
- String adrs = Util.fixEmpty(a.getProperty(Mailer.UserProperty.class).getAddress());
- if(debug)
- listener.getLogger().println(" User "+a.getId()+" -> "+adrs);
- if (adrs != null)
- r.add(new InternetAddress(adrs));
- else {
- listener.getLogger().println(Messages.MailSender_NoAddress(a.getFullName()));
- }
- }
- return r;
- }
-
- private String getSubject(AbstractBuild<?, ?> build, String caption) {
- return caption + ' ' + build.getFullDisplayName();
- }
-
/**
* Check whether a path (/-separated) will be archived.
*/
+ //TODO investigate where it's used.
protected boolean artifactMatches(String path, AbstractBuild<?, ?> build) {
return false;
}
- public static boolean debug = false;
-
- private static final int MAX_LOG_LINES = Integer.getInteger(MailSender.class.getName()+".maxLogLines",250);
-
-
/**
* Sometimes the outcome of the previous build affects the e-mail we send, hence this checkpoint.
*/
diff --git a/hudson-core/src/main/java/hudson/tasks/Mailer.java b/hudson-core/src/main/java/hudson/tasks/Mailer.java
index e198a9a..3e36a3c 100644
--- a/hudson-core/src/main/java/hudson/tasks/Mailer.java
+++ b/hudson-core/src/main/java/hudson/tasks/Mailer.java
@@ -1,6 +1,6 @@
/*******************************************************************************
*
- * Copyright (c) 2004-2010 Oracle Corporation.
+ * Copyright (c) 2004-2011 Oracle Corporation.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -9,7 +9,7 @@
*
* Contributors:
*
- * Kohsuke Kawaguchi, Bruce Chapman, Erik Ramfelt, Jean-Baptiste Quenot, Luca Domenico Milanesio
+ * Kohsuke Kawaguchi, Bruce Chapman, Erik Ramfelt, Jean-Baptiste Quenot, Luca Domenico Milanesio, Anton Kozak
*
*
*******************************************************************************/
@@ -33,13 +33,11 @@ import hudson.model.UserPropertyDescriptor;
import hudson.util.FormValidation;
import hudson.util.Secret;
import hudson.util.XStream2;
-import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.Properties;
-import java.util.logging.Level;
import java.util.logging.Logger;
import javax.mail.Authenticator;
import javax.mail.Message;
@@ -53,7 +51,6 @@ import javax.mail.internet.MimeMessage;
import javax.servlet.ServletException;
import net.sf.json.JSONObject;
import org.apache.commons.lang3.StringUtils;
-import org.apache.tools.ant.types.selectors.SelectorUtils;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.QueryParameter;
@@ -108,37 +105,17 @@ public class Mailer extends Notifier {
private transient String charset;
@Override
- public boolean perform(AbstractBuild<?,?> build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException {
- if(debug)
+ public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener)
+ throws IOException, InterruptedException {
+ if (debug) {
listener.getLogger().println("Running mailer");
+ }
// substitute build parameters
EnvVars env = build.getEnvironment(listener);
String recip = env.expand(recipients);
- return new MailSender(recip, dontNotifyEveryUnstableBuild, sendToIndividuals, descriptor().getCharset()) {
- /** Check whether a path (/-separated) will be archived. */
- @Override
- public boolean artifactMatches(String path, AbstractBuild<?,?> build) {
- ArtifactArchiver aa = build.getProject().getPublishersList().get(ArtifactArchiver.class);
- if (aa == null) {
- LOGGER.finer("No ArtifactArchiver found");
- return false;
- }
- String artifacts = aa.getArtifacts();
- for (String include : artifacts.split("[, ]+")) {
- String pattern = include.replace(File.separatorChar, '/');
- if (pattern.endsWith("/")) {
- pattern += "**";
- }
- if (SelectorUtils.matchPath(pattern, path)) {
- LOGGER.log(Level.FINER, "DescriptorImpl.artifactMatches true for {0} against {1}", new Object[] {path, pattern});
- return true;
- }
- }
- LOGGER.log(Level.FINER, "DescriptorImpl.artifactMatches for {0} matched none of {1}", new Object[] {path, artifacts});
- return false;
- }
- }.execute(build,listener);
+ return new MailSender(recip, dontNotifyEveryUnstableBuild, sendToIndividuals,
+ descriptor().getCharset()).execute(build, listener);
}
/**
diff --git a/hudson-core/src/main/java/hudson/tasks/mail/BuildResultMail.java b/hudson-core/src/main/java/hudson/tasks/mail/BuildResultMail.java
new file mode 100644
index 0000000..b0cbfd0
--- /dev/null
+++ b/hudson-core/src/main/java/hudson/tasks/mail/BuildResultMail.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2011 Oracle Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ * Anton Kozak
+ *
+ *******************************************************************************/
+package hudson.tasks.mail;
+
+import hudson.model.AbstractBuild;
+import hudson.model.BuildListener;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+/**
+ * Interface describes mail with project build result.
+ */
+public interface BuildResultMail {
+
+ /**
+ * Returns prepared mail.
+ *
+ * @param build build.
+ * @param listener listener.
+ *
+ * @return prepared mail.
+ * @throws MessagingException exception if any.
+ * @throws InterruptedException exception if any.
+ */
+ MimeMessage getMail(AbstractBuild<?, ?> build, BuildListener listener)
+ throws MessagingException, InterruptedException;
+
+} \ No newline at end of file
diff --git a/hudson-core/src/main/java/hudson/tasks/mail/impl/BackToNormalBuildMail.java b/hudson-core/src/main/java/hudson/tasks/mail/impl/BackToNormalBuildMail.java
new file mode 100644
index 0000000..bd15df3
--- /dev/null
+++ b/hudson-core/src/main/java/hudson/tasks/mail/impl/BackToNormalBuildMail.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2011 Oracle Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ * Anton Kozak
+ *
+ *******************************************************************************/
+package hudson.tasks.mail.impl;
+
+import hudson.model.AbstractBuild;
+import hudson.model.AbstractProject;
+import hudson.model.BuildListener;
+import hudson.tasks.Messages;
+import java.util.List;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+/**
+ * Class used for the mail preparation if build was returned to normal state.
+ */
+public class BackToNormalBuildMail extends BaseBuildResultMail {
+
+ /**
+ * Current state.
+ */
+ private String currentState;
+
+ public BackToNormalBuildMail(String recipients, boolean sendToIndividuals,
+ List<AbstractProject> upstreamProjects, String charset, String currentState) {
+ super(recipients, sendToIndividuals, upstreamProjects, charset);
+ this.currentState = currentState;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public MimeMessage getMail(AbstractBuild<?, ?> build, BuildListener listener)
+ throws MessagingException, InterruptedException {
+ MimeMessage msg = createEmptyMail(build, listener);
+ msg.setSubject(getSubject(build, Messages.MailSender_BackToNormalMail_Subject(currentState)), getCharset());
+ StringBuilder buf = new StringBuilder();
+ appendBuildUrl(build, buf);
+ appendFooter(buf);
+ msg.setText(buf.toString(), getCharset());
+ return msg;
+ }
+} \ No newline at end of file
diff --git a/hudson-core/src/main/java/hudson/tasks/mail/impl/BaseBuildResultMail.java b/hudson-core/src/main/java/hudson/tasks/mail/impl/BaseBuildResultMail.java
new file mode 100644
index 0000000..c3a5127
--- /dev/null
+++ b/hudson-core/src/main/java/hudson/tasks/mail/impl/BaseBuildResultMail.java
@@ -0,0 +1,271 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2011 Oracle Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ * Anton Kozak
+ *
+ *******************************************************************************/
+package hudson.tasks.mail.impl;
+
+import hudson.Util;
+import hudson.tasks.mail.BuildResultMail;
+import hudson.model.AbstractBuild;
+import hudson.model.AbstractProject;
+import hudson.model.BuildListener;
+import hudson.model.Hudson;
+import hudson.model.User;
+import hudson.tasks.HudsonMimeMessage;
+import hudson.tasks.MailMessageIdAction;
+import hudson.tasks.MailSender;
+import hudson.tasks.Mailer;
+import hudson.tasks.Messages;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.StringTokenizer;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import org.apache.commons.collections.CollectionUtils;
+
+/**
+ * Base class for all project build result mails.
+ */
+public abstract class BaseBuildResultMail implements BuildResultMail {
+
+ protected static final int MAX_LOG_LINES = Integer.getInteger(MailSender.class.getName()+".maxLogLines",250);
+
+ //TODO where it's used?
+ public static boolean debug = false;
+
+ /**
+ * Whitespace-separated list of e-mail addresses that represent recipients.
+ */
+ private String recipients;
+
+ /**
+ * The charset to use for the text and subject.
+ */
+ private String charset;
+
+ /**
+ * The list of upstream projects.
+ */
+ private List<AbstractProject> upstreamProjects;
+
+ /**
+ * If true, individuals will receive e-mails regarding who broke the build.
+ */
+ private boolean sendToIndividuals;
+
+
+ public BaseBuildResultMail(String recipients, boolean sendToIndividuals, List<AbstractProject> upstreamProjects,
+ String charset) {
+ this.recipients = recipients;
+ this.sendToIndividuals = sendToIndividuals;
+ this.upstreamProjects = upstreamProjects;
+ this.charset = charset;
+ }
+
+ /**
+ * Returns recipients.
+ *
+ * @return recipients.
+ */
+ public String getRecipients() {
+ return recipients;
+ }
+
+ /**
+ * Returns charset.
+ *
+ * @return charset.
+ */
+ public String getCharset() {
+ return charset;
+ }
+
+ /**
+ * Returns prefix for subject of automatically generated emails.
+ *
+ * @return prefix for subject.
+ */
+ protected String getSubjectPrefix() {
+ return hudson.mail.Messages.hudson_email_subject_prefix();
+ }
+ /**
+ * Creates empty mail.
+ *
+ * @param build build.
+ * @param listener listener.
+ * @return empty mail.
+ * @throws MessagingException exception if any.
+ */
+ protected MimeMessage createEmptyMail(AbstractBuild<?, ?> build, BuildListener listener) throws MessagingException {
+ MimeMessage msg = new HudsonMimeMessage(Mailer.descriptor().createSession());
+ // TODO: I'd like to put the URL to the page in here,
+ // but how do I obtain that?
+ msg.setContent("", "text/plain");
+ msg.setFrom(new InternetAddress(Mailer.descriptor().getAdminAddress()));
+ msg.setSentDate(new Date());
+
+ Set<InternetAddress> rcp = new LinkedHashSet<InternetAddress>();
+ StringTokenizer tokens = new StringTokenizer(getRecipients());
+ while (tokens.hasMoreTokens()) {
+ String address = tokens.nextToken();
+ if(address.startsWith("upstream-individuals:")) {
+ // people who made a change in the upstream
+ String projectName = address.substring("upstream-individuals:".length());
+ AbstractProject up = Hudson.getInstance().getItemByFullName(projectName,AbstractProject.class);
+ if(up==null) {
+ listener.getLogger().println("No such project exist: "+projectName);
+ continue;
+ }
+ includeCulpritsOf(up, build, listener, rcp);
+ } else {
+ // ordinary address
+ try {
+ rcp.add(new InternetAddress(address));
+ } catch (AddressException e) {
+ // report bad address, but try to send to other addresses
+ e.printStackTrace(listener.error(e.getMessage()));
+ }
+ }
+ }
+
+ if (CollectionUtils.isNotEmpty(upstreamProjects)) {
+ for (AbstractProject project : upstreamProjects) {
+ includeCulpritsOf(project, build, listener, rcp);
+ }
+ }
+
+ if (sendToIndividuals) {
+ Set<User> culprits = build.getCulprits();
+
+ if(debug)
+ listener.getLogger().println("Trying to send e-mails to individuals who broke the build. sizeof(culprits)=="+culprits.size());
+
+ rcp.addAll(buildCulpritList(listener,culprits));
+ }
+ msg.setRecipients(Message.RecipientType.TO, rcp.toArray(new InternetAddress[rcp.size()]));
+
+ AbstractBuild<?, ?> pb = build.getPreviousBuild();
+ if(pb!=null) {
+ MailMessageIdAction b = pb.getAction(MailMessageIdAction.class);
+ if(b!=null) {
+ msg.setHeader("In-Reply-To",b.messageId);
+ msg.setHeader("References",b.messageId);
+ }
+ }
+
+ return msg;
+ }
+
+ /**
+ * Appends build URL to the builder.
+ *
+ * @param build build.
+ * @param buf {@link StringBuilder}.
+ */
+ protected void appendBuildUrl(AbstractBuild<?, ?> build, StringBuilder buf) {
+ appendUrl(Util.encode(build.getUrl())
+ + (build.getChangeSet().isEmptySet() ? "" : "changes"), buf);
+ }
+
+ /**
+ * Appends URL to the builder.
+ *
+ * @param url url.
+ * @param buf {@link StringBuilder}.
+ */
+ protected void appendUrl(String url, StringBuilder buf) {
+ String baseUrl = Mailer.descriptor().getUrl();
+ if (baseUrl != null)
+ buf.append(Messages.MailSender_Link(baseUrl, url)).append("\n\n");
+ }
+
+ /**
+ * Appends footer to the mail builder.
+ *
+ * @param buf {@link StringBuilder}.
+ */
+ protected void appendFooter(StringBuilder buf) {
+ String footer = getTextFooter();
+ if (footer != null) {
+ buf.append(footer);
+ }
+ }
+
+
+ /**
+ * Returns the subject of the mail.
+ *
+ * @param build build.
+ * @param caption the caption.
+ * @return prepared subject.
+ */
+ protected String getSubject(AbstractBuild<?, ?> build, String caption) {
+ return new StringBuilder().append(getSubjectPrefix())
+ .append(" ")
+ .append(caption)
+ .append(" ")
+ .append(build.getFullDisplayName())
+ .toString();
+ }
+
+ private void includeCulpritsOf(AbstractProject upstreamProject, AbstractBuild<?, ?> currentBuild, BuildListener listener, Set<InternetAddress> recipientList) throws AddressException {
+ AbstractBuild<?,?> upstreamBuild = currentBuild.getUpstreamRelationshipBuild(upstreamProject);
+ AbstractBuild<?,?> previousBuild = currentBuild.getPreviousBuild();
+ AbstractBuild<?,?> previousBuildUpstreamBuild = previousBuild!=null ? previousBuild.getUpstreamRelationshipBuild(upstreamProject) : null;
+ if(previousBuild==null && upstreamBuild==null && previousBuildUpstreamBuild==null) {
+ listener.getLogger().println("Unable to compute the changesets in "+ upstreamProject +". Is the fingerprint configured?");
+ return;
+ }
+ if(previousBuild==null || upstreamBuild==null || previousBuildUpstreamBuild==null) {
+ listener.getLogger().println("Unable to compute the changesets in "+ upstreamProject);
+ return;
+ }
+ AbstractBuild<?,?> b=previousBuildUpstreamBuild;
+ do {
+ recipientList.addAll(buildCulpritList(listener,b.getCulprits()));
+ b = b.getNextBuild();
+ } while ( b != upstreamBuild && b != null );
+ }
+
+
+ private Set<InternetAddress> buildCulpritList(BuildListener listener, Set<User> culprits) throws AddressException {
+ Set<InternetAddress> r = new HashSet<InternetAddress>();
+ for (User a : culprits) {
+ String adrs = Util.fixEmpty(a.getProperty(Mailer.UserProperty.class).getAddress());
+ if(debug)
+ listener.getLogger().println(" User "+a.getId()+" -> "+adrs);
+ if (adrs != null)
+ r.add(new InternetAddress(adrs));
+ else {
+ listener.getLogger().println(Messages.MailSender_NoAddress(a.getFullName()));
+ }
+ }
+ return r;
+ }
+
+ /**
+ * Returns text footer for all automatically generated emails.
+ *
+ * @return text footer.
+ */
+ private String getTextFooter() {
+ return hudson.mail.Messages.hudson_email_footer();
+ }
+
+}
diff --git a/hudson-core/src/main/java/hudson/tasks/mail/impl/FailureBuildMail.java b/hudson-core/src/main/java/hudson/tasks/mail/impl/FailureBuildMail.java
new file mode 100644
index 0000000..e97f8a0
--- /dev/null
+++ b/hudson-core/src/main/java/hudson/tasks/mail/impl/FailureBuildMail.java
@@ -0,0 +1,164 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2011 Oracle Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ * Anton Kozak
+ *
+ *******************************************************************************/
+package hudson.tasks.mail.impl;
+
+import hudson.FilePath;
+import hudson.Functions;
+import hudson.Util;
+import hudson.model.AbstractBuild;
+import hudson.model.AbstractProject;
+import hudson.model.BuildListener;
+import hudson.scm.ChangeLogSet;
+import hudson.tasks.ArtifactArchiver;
+import hudson.tasks.Mailer;
+import hudson.tasks.Messages;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+import org.apache.tools.ant.types.selectors.SelectorUtils;
+
+/**
+ * Class used for the mail preparation if build was returned to normal state.
+ */
+public class FailureBuildMail extends BaseBuildResultMail {
+
+ public FailureBuildMail(String recipients, boolean sendToIndividuals,
+ List<AbstractProject> upstreamProjects, String charset) {
+ super(recipients, sendToIndividuals, upstreamProjects, charset);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public MimeMessage getMail(AbstractBuild<?, ?> build, BuildListener listener)
+ throws MessagingException, InterruptedException {
+ MimeMessage msg = createEmptyMail(build, listener);
+
+ msg.setSubject(getSubject(build, Messages.MailSender_FailureMail_Subject()), getCharset());
+
+ StringBuilder buf = new StringBuilder();
+ appendBuildUrl(build, buf);
+
+ boolean firstChange = true;
+ for (ChangeLogSet.Entry entry : build.getChangeSet()) {
+ if (firstChange) {
+ firstChange = false;
+ buf.append(Messages.MailSender_FailureMail_Changes()).append("\n\n");
+ }
+ buf.append('[');
+ buf.append(entry.getAuthor().getFullName());
+ buf.append("] ");
+ String m = entry.getMsg();
+ if (m != null) {
+ buf.append(m);
+ if (!m.endsWith("\n")) {
+ buf.append('\n');
+ }
+ }
+ buf.append('\n');
+ }
+
+ buf.append("------------------------------------------\n");
+
+ try {
+ // Restrict max log size to avoid sending enormous logs over email.
+ // Interested users can always look at the log on the web server.
+ List<String> lines = build.getLog(MAX_LOG_LINES);
+
+ String workspaceUrl = null, artifactUrl = null;
+ Pattern wsPattern = null;
+ String baseUrl = Mailer.descriptor().getUrl();
+ if (baseUrl != null) {
+ // Hyperlink local file paths to the repository workspace or build artifacts.
+ // Note that it is possible for a failure mail to refer to a file using a workspace
+ // URL which has already been corrected in a subsequent build. To fix, archive.
+ workspaceUrl = baseUrl + Util.encode(build.getProject().getUrl()) + "ws/";
+ artifactUrl = baseUrl + Util.encode(build.getUrl()) + "artifact/";
+ FilePath ws = build.getWorkspace();
+ // Match either file or URL patterns, i.e. either
+ // c:\hudson\workdir\jobs\foo\workspace\src\Foo.java
+ // file:/c:/hudson/workdir/jobs/foo/workspace/src/Foo.java
+ // will be mapped to one of:
+ // http://host/hudson/job/foo/ws/src/Foo.java
+ // http://host/hudson/job/foo/123/artifact/src/Foo.java
+ // Careful with path separator between $1 and $2:
+ // workspaceDir will not normally end with one;
+ // workspaceDir.toURI() will end with '/' if and only if workspaceDir.exists() at time of call
+ wsPattern = Pattern.compile("(" +
+ Pattern.quote(ws.getRemote()) + "|" + Pattern.quote(ws.toURI().toString())
+ + ")[/\\\\]?([^:#\\s]*)");
+ }
+ for (String line : lines) {
+ line = line.replace('\0',
+ ' '); // shall we replace other control code? This one is motivated by http://www.nabble.com/Problems-with-NULL-characters-in-generated-output-td25005177.html
+ if (wsPattern != null) {
+ // Perl: $line =~ s{$rx}{$path = $2; $path =~ s!\\\\!/!g; $workspaceUrl . $path}eg;
+ Matcher m = wsPattern.matcher(line);
+ int pos = 0;
+ while (m.find(pos)) {
+ String path = m.group(2).replace(File.separatorChar, '/');
+ String linkUrl = artifactMatches(path, build) ? artifactUrl : workspaceUrl;
+ String prefix = line.substring(0, m.start()) + '<' + linkUrl + Util.encode(path) + '>';
+ pos = prefix.length();
+ line = prefix + line.substring(m.end());
+ // XXX better style to reuse Matcher and fix offsets, but more work
+ m = wsPattern.matcher(line);
+ }
+ }
+ buf.append(line);
+ buf.append('\n');
+ }
+ } catch (IOException e) {
+ // somehow failed to read the contents of the log
+ buf.append(Messages.MailSender_FailureMail_FailedToAccessBuildLog()).append("\n\n").append(
+ Functions.printThrowable(e));
+ }
+ appendFooter(buf);
+ msg.setText(buf.toString(), getCharset());
+
+ return msg;
+ }
+
+ /**
+ * Check whether a path (/-separated) will be archived.
+ */
+ public boolean artifactMatches(String path, AbstractBuild<?, ?> build) {
+ ArtifactArchiver aa = build.getProject().getPublishersList().get(ArtifactArchiver.class);
+ if (aa == null) {
+ //LOGGER.finer("No ArtifactArchiver found");
+ return false;
+ }
+ String artifacts = aa.getArtifacts();
+ for (String include : artifacts.split("[, ]+")) {
+ String pattern = include.replace(File.separatorChar, '/');
+ if (pattern.endsWith("/")) {
+ pattern += "**";
+ }
+ if (SelectorUtils.matchPath(pattern, path)) {
+ //LOGGER.log(Level.FINER, "DescriptorImpl.artifactMatches true for {0} against {1}",
+ // new Object[]{path, pattern});
+ return true;
+ }
+ }
+ //LOGGER.log(Level.FINER, "DescriptorImpl.artifactMatches for {0} matched none of {1}",
+ // new Object[]{path, artifacts});
+ return false;
+ }
+
+}
diff --git a/hudson-core/src/main/java/hudson/tasks/mail/impl/UnstableBuildMail.java b/hudson-core/src/main/java/hudson/tasks/mail/impl/UnstableBuildMail.java
new file mode 100644
index 0000000..554d8ce
--- /dev/null
+++ b/hudson-core/src/main/java/hudson/tasks/mail/impl/UnstableBuildMail.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2011 Oracle Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ * Anton Kozak
+ *
+ *******************************************************************************/
+package hudson.tasks.mail.impl;
+
+import hudson.Util;
+import hudson.model.AbstractBuild;
+import hudson.model.AbstractProject;
+import hudson.model.BuildListener;
+import hudson.model.Result;
+import hudson.tasks.Messages;
+import java.util.List;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+public class UnstableBuildMail extends BaseBuildResultMail {
+
+ public UnstableBuildMail(String recipients, boolean sendToIndividuals,
+ List<AbstractProject> upstreamProjects, String charset) {
+ super(recipients, sendToIndividuals, upstreamProjects, charset);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public MimeMessage getMail(AbstractBuild<?, ?> build, BuildListener listener)
+ throws MessagingException, InterruptedException {
+ MimeMessage msg = createEmptyMail(build, listener);
+
+ String subject = Messages.MailSender_UnstableMail_Subject();
+
+ AbstractBuild<?, ?> prev = build.getPreviousBuild();
+ boolean still = false;
+ if (prev != null) {
+ if (prev.getResult() == Result.SUCCESS) {
+ subject = Messages.MailSender_UnstableMail_ToUnStable_Subject();
+ } else if (prev.getResult() == Result.UNSTABLE) {
+ subject = Messages.MailSender_UnstableMail_StillUnstable_Subject();
+ still = true;
+ }
+ }
+
+ msg.setSubject(getSubject(build, subject), getCharset());
+ StringBuilder buf = new StringBuilder();
+ // Link to project changes summary for "still unstable" if this or last build has changes
+ if (still && !(build.getChangeSet().isEmptySet() && prev.getChangeSet().isEmptySet())) {
+ appendUrl(Util.encode(build.getProject().getUrl()) + "changes", buf);
+ } else {
+ appendBuildUrl(build, buf);
+ }
+ appendFooter(buf);
+ msg.setText(buf.toString(), getCharset());
+
+ return msg;
+ }
+}
diff --git a/hudson-core/src/main/resources/hudson/mail/Messages.properties b/hudson-core/src/main/resources/hudson/mail/Messages.properties
new file mode 100644
index 0000000..9765f8d
--- /dev/null
+++ b/hudson-core/src/main/resources/hudson/mail/Messages.properties
@@ -0,0 +1,10 @@
+hudson.email.subject.prefix=[Hudson]
+
+hudson.email.footer=\n--\nThis message is automatically generated by Hudson. \n\
+For more information on Hudson, see: http://hudson-ci.org/
+
+account.creation.email.text=Dear {0} \n\nYou have signed up for a Hudson account at: {1} \n\
+Here are the details of your account: \n--------------------------------------------------------------------- \
+\n Email: {2}\n Username: {3}\n Password: {4}\n\n\
+You can change the password on the page: {1}securityRealm/user/{3}/configure
+account.creation.email.subject=Account signup \ No newline at end of file
diff --git a/hudson-core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/config.jelly b/hudson-core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/config.jelly
index 0fa3df7..e0c8e8e 100644
--- a/hudson-core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/config.jelly
+++ b/hudson-core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/config.jelly
@@ -1,6 +1,6 @@
<!-- **************************************************************************
#
-# Copyright (c) 2004-2010 Oracle Corporation.
+# Copyright (c) 2004-2011 Oracle Corporation.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
@@ -20,7 +20,9 @@
<f:checkbox name="privateRealm.allowsSignup" checked="${h.defaultToTrue(instance.allowsSignup())}"
title="${%Allow users to sign up}"/>
- </f:entry>
+ <f:checkbox name="privateRealm.notifyUser" checked="${h.defaultToTrue(instance.isNotifyUser())}"
+ title="${%Notify user of Hudson account creation}"/>
+ </f:entry>
<j:if test="${size(h.captchaSupportDescriptors) gt 0}">
@@ -40,5 +42,4 @@
</j:forEach>
</f:dropdownList>
</j:if>
-
</j:jelly>