private boolean executePresendScript( AbstractBuild<?, ?> build, BuildListener listener, MimeMessage msg, EmailTrigger trigger, Map<String, EmailTrigger> triggered) throws RuntimeException { boolean cancel = false; presendScript = new ContentBuilder().transformText(presendScript, this, build, listener); if (StringUtils.isNotBlank(presendScript)) { listener.getLogger().println("Executing pre-send script"); ClassLoader cl = Jenkins.getInstance().getPluginManager().uberClassLoader; ScriptSandbox sandbox = null; CompilerConfiguration cc = new CompilerConfiguration(); cc.addCompilationCustomizers( new ImportCustomizer() .addStarImports("jenkins", "jenkins.model", "hudson", "hudson.model")); if (ExtendedEmailPublisher.DESCRIPTOR.isSecurityEnabled()) { debug(listener.getLogger(), "Setting up sandbox for pre-send script"); cc.addCompilationCustomizers(new SandboxTransformer()); sandbox = new ScriptSandbox(); } Binding binding = new Binding(); binding.setVariable("build", build); binding.setVariable("msg", msg); binding.setVariable("logger", listener.getLogger()); binding.setVariable("cancel", cancel); binding.setVariable("trigger", trigger); binding.setVariable("triggered", Collections.unmodifiableMap(triggered)); GroovyShell shell = new GroovyShell(cl, binding, cc); StringWriter out = new StringWriter(); PrintWriter pw = new PrintWriter(out); if (sandbox != null) { sandbox.register(); } try { Object output = shell.evaluate(presendScript); if (output != null) { pw.println("Result: " + output); cancel = ((Boolean) shell.getVariable("cancel")).booleanValue(); debug(listener.getLogger(), "Pre-send script set cancel to %b", cancel); } } catch (SecurityException e) { listener .getLogger() .println("Pre-send script tried to access secured objects: " + e.getMessage()); } catch (Throwable t) { t.printStackTrace(pw); listener.getLogger().println(out.toString()); // should we cancel the sending of the email??? } debug(listener.getLogger(), out.toString()); } return !cancel; }
@Override public boolean prebuild(AbstractBuild<?, ?> build, BuildListener listener) { debug(listener.getLogger(), "Checking for pre-build"); if (!(build instanceof MatrixRun) || isExecuteOnMatrixNodes()) { debug(listener.getLogger(), "Executing pre-build step"); return _perform(build, listener, true); } return true; }
@Override public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { debug(listener.getLogger(), "Checking for post-build"); if (!(build instanceof MatrixRun) || isExecuteOnMatrixNodes()) { debug(listener.getLogger(), "Performing post-build step"); return _perform(build, listener, false); } return true; }
private MimeBodyPart getContent( final EmailType type, final AbstractBuild<?, ?> build, BuildListener listener, String charset, EmailTrigger trigger) throws MessagingException { final String text = new ContentBuilder().transformText(type.getBody(), this, build, listener); String messageContentType = contentType; // contentType is null if the project was not reconfigured after upgrading. if (messageContentType == null || "default".equals(messageContentType)) { messageContentType = DESCRIPTOR.getDefaultContentType(); // The defaultContentType is null if the main Jenkins configuration // was not reconfigured after upgrading. if (messageContentType == null) { messageContentType = "text/plain"; } } messageContentType += "; charset=" + charset; try { if (saveOutput) { Random random = new Random(); String extension = ".html"; if (messageContentType.startsWith("text/plain")) { extension = ".txt"; } FilePath savedOutput = new FilePath( build.getWorkspace(), String.format( "%s-%s%d%s", trigger.getDescriptor().getDisplayName(), build.getId(), random.nextInt(), extension)); savedOutput.write(text, charset); } } catch (IOException e) { listener.getLogger().println("Error trying to save email output to file. " + e.getMessage()); } catch (InterruptedException e) { listener.getLogger().println("Error trying to save email output to file. " + e.getMessage()); } // set the email message text // (plain text or HTML depending on the content type) MimeBodyPart msgPart = new MimeBodyPart(); debug(listener.getLogger(), "messageContentType = %s", messageContentType); if (messageContentType.startsWith("text/html")) { String inlinedCssHtml = new CssInliner().process(text); msgPart.setContent(inlinedCssHtml, messageContentType); } else { msgPart.setContent(text, messageContentType); } return msgPart; }
private boolean _perform(AbstractBuild<?, ?> build, BuildListener listener, boolean forPreBuild) { boolean emailTriggered = false; debug(listener.getLogger(), "Checking if email needs to be generated"); Map<String, EmailTrigger> triggered = new HashMap<String, EmailTrigger>(); for (EmailTrigger trigger : configuredTriggers) { if (trigger.isPreBuild() == forPreBuild && trigger.trigger(build, listener)) { String tName = trigger.getDescriptor().getDisplayName(); triggered.put(tName, trigger); listener.getLogger().println("Email was triggered for: " + tName); emailTriggered = true; } } // Go through and remove triggers that are replaced by others List<String> replacedTriggers = new ArrayList<String>(); for (String triggerName : triggered.keySet()) { replacedTriggers.addAll(triggered.get(triggerName).getDescriptor().getTriggerReplaceList()); } for (String triggerName : replacedTriggers) { triggered.remove(triggerName); listener .getLogger() .println( "Trigger " + triggerName + " was overridden by another trigger and will not send an email."); } if (emailTriggered && triggered.isEmpty()) { listener .getLogger() .println( "There is a circular trigger replacement with the email triggers. No email is sent."); return false; } else if (triggered.isEmpty()) { listener.getLogger().println("No emails were triggered."); return true; } for (String triggerName : triggered.keySet()) { listener.getLogger().println("Sending email for trigger: " + triggerName); sendMail( triggered.get(triggerName).getEmail(), build, listener, triggered.get(triggerName), triggered); } return true; }
private MimeMessage createMail( EmailType type, AbstractBuild<?, ?> build, BuildListener listener, EmailTrigger trigger) throws MessagingException, IOException, InterruptedException { boolean overrideGlobalSettings = ExtendedEmailPublisher.DESCRIPTOR.getOverrideGlobalSettings(); MimeMessage msg; // If not overriding global settings, use the Mailer class to create a session and set the from // address // Else we'll do it ourselves if (!overrideGlobalSettings) { debug( listener.getLogger(), "NOT overriding default server settings, using Mailer to create session"); msg = new MimeMessage(Mailer.descriptor().createSession()); msg.setFrom(new InternetAddress(Mailer.descriptor().getAdminAddress())); } else { debug(listener.getLogger(), "Overriding default server settings, creating our own session"); msg = new MimeMessage(ExtendedEmailPublisher.DESCRIPTOR.createSession()); msg.setFrom(new InternetAddress(ExtendedEmailPublisher.DESCRIPTOR.getAdminAddress())); } String charset = Mailer.descriptor().getCharset(); if (overrideGlobalSettings) { String overrideCharset = ExtendedEmailPublisher.DESCRIPTOR.getCharset(); if (StringUtils.isNotBlank(overrideCharset)) { debug(listener.getLogger(), "Overriding charset %s", overrideCharset); charset = overrideCharset; } } // Set the contents of the email msg.addHeader("X-Jenkins-Job", build.getProject().getDisplayName()); if (build.getResult() != null) { msg.addHeader("X-Jenkins-Result", build.getResult().toString()); } msg.setSentDate(new Date()); setSubject(type, build, msg, listener, charset); Multipart multipart = new MimeMultipart(); multipart.addBodyPart(getContent(type, build, listener, charset, trigger)); AttachmentUtils attachments = new AttachmentUtils(attachmentsPattern); attachments.attach(multipart, this, build, listener); // add attachments from the email type if they are setup if (StringUtils.isNotBlank(type.getAttachmentsPattern())) { AttachmentUtils typeAttachments = new AttachmentUtils(type.getAttachmentsPattern()); typeAttachments.attach(multipart, this, build, listener); } if (attachBuildLog || type.getAttachBuildLog()) { debug(listener.getLogger(), "Request made to attach build log"); AttachmentUtils.attachBuildLog( multipart, build, listener, compressBuildLog || type.getCompressBuildLog()); } msg.setContent(multipart); EnvVars env = null; try { env = build.getEnvironment(listener); } catch (Exception e) { listener.getLogger().println("Error retrieving environment vars: " + e.getMessage()); // create an empty set of env vars env = new EnvVars(); } // Get the recipients from the global list of addresses Set<InternetAddress> recipientAddresses = new LinkedHashSet<InternetAddress>(); Set<InternetAddress> ccAddresses = new LinkedHashSet<InternetAddress>(); if (type.getSendToRecipientList()) { debug(listener.getLogger(), "Adding recipients from recipient list"); addAddressesFromRecipientList( recipientAddresses, ccAddresses, getRecipientList(type, build, recipientList, listener, charset), env, listener); } // Get the list of developers who made changes between this build and the last // if this mail type is configured that way if (type.getSendToDevelopers()) { debug(listener.getLogger(), "Adding developers"); Set<User> users; if (type.getIncludeCulprits()) { users = build.getCulprits(); } else { users = new HashSet<User>(); for (Entry change : build.getChangeSet()) { users.add(change.getAuthor()); } } for (User user : users) { if (!isExcludedCommitter(user.getFullName())) { String userAddress = EmailRecipientUtils.getUserConfiguredEmail(user); if (userAddress != null) { addAddressesFromRecipientList( recipientAddresses, ccAddresses, userAddress, env, listener); } else { listener .getLogger() .println( "Failed to send e-mail to " + user.getFullName() + " because no e-mail address is known, and no default e-mail domain is configured"); } } } } if (type.isSendToRequester()) { debug(listener.getLogger(), "Sending to requester"); // looking for Upstream build. AbstractBuild<?, ?> cur = build; Cause.UpstreamCause upc = build.getCause(Cause.UpstreamCause.class); while (upc != null) { // UpstreamCause.getUpStreamProject() returns the full name, so use getItemByFullName AbstractProject<?, ?> p = (AbstractProject<?, ?>) Hudson.getInstance().getItemByFullName(upc.getUpstreamProject()); cur = p.getBuildByNumber(upc.getUpstreamBuild()); upc = cur.getCause(Cause.UpstreamCause.class); } addUserTriggeringTheBuild(cur, recipientAddresses, ccAddresses, env, listener); } // Get the list of recipients that are uniquely specified for this type of email if (StringUtils.isNotBlank(type.getRecipientList())) { addAddressesFromRecipientList( recipientAddresses, ccAddresses, getRecipientList(type, build, type.getRecipientList(), listener, charset), env, listener); } String emergencyReroute = ExtendedEmailPublisher.DESCRIPTOR.getEmergencyReroute(); boolean isEmergencyReroute = StringUtils.isNotBlank(emergencyReroute); if (isEmergencyReroute) { debug(listener.getLogger(), "Emergency reroute turned on"); recipientAddresses.clear(); addAddressesFromRecipientList( recipientAddresses, ccAddresses, emergencyReroute, env, listener); listener.getLogger().println("Emergency reroute is set to: " + emergencyReroute); } msg.setRecipients( Message.RecipientType.TO, recipientAddresses.toArray(new InternetAddress[recipientAddresses.size()])); if (ccAddresses.size() > 0) { msg.setRecipients( Message.RecipientType.CC, ccAddresses.toArray(new InternetAddress[ccAddresses.size()])); } Set<InternetAddress> replyToAddresses = new LinkedHashSet<InternetAddress>(); if (StringUtils.isNotBlank(replyTo)) { addAddressesFromRecipientList( replyToAddresses, null, getRecipientList(type, build, replyTo, listener, charset), env, listener); } if (StringUtils.isNotBlank(type.getReplyTo())) { addAddressesFromRecipientList( replyToAddresses, null, getRecipientList(type, build, type.getReplyTo(), listener, charset), env, listener); } if (replyToAddresses.size() > 0) { msg.setReplyTo(replyToAddresses.toArray(new InternetAddress[replyToAddresses.size()])); } AbstractBuild<?, ?> pb = build.getPreviousBuild(); if (pb != null) { // Send mails as replies until next successful build MailMessageIdAction b = pb.getAction(MailMessageIdAction.class); if (b != null && pb.getResult() != Result.SUCCESS) { debug(listener.getLogger(), "Setting In-Reply-To since last build was not successful"); msg.setHeader("In-Reply-To", b.messageId); msg.setHeader("References", b.messageId); } } if (CONTENT_TRANSFER_ENCODING != null) { msg.setHeader("Content-Transfer-Encoding", CONTENT_TRANSFER_ENCODING); } String listId = ExtendedEmailPublisher.DESCRIPTOR.getListId(); if (listId != null) { msg.setHeader("List-ID", listId); } if (ExtendedEmailPublisher.DESCRIPTOR.getPrecedenceBulk()) { msg.setHeader("Precedence", "bulk"); } return msg; }
private boolean sendMail( EmailType mailType, AbstractBuild<?, ?> build, BuildListener listener, EmailTrigger trigger, Map<String, EmailTrigger> triggered) { try { MimeMessage msg = createMail(mailType, build, listener, trigger); debug(listener.getLogger(), "Successfully created MimeMessage"); Address[] allRecipients = msg.getAllRecipients(); int retries = 0; if (allRecipients != null) { StringBuilder buf = new StringBuilder("Sending email to:"); for (Address a : allRecipients) { buf.append(' ').append(a); } listener.getLogger().println(buf); if (executePresendScript(build, listener, msg, trigger, triggered)) { while (true) { try { Transport.send(msg); break; } catch (SendFailedException e) { if (e.getNextException() != null && ((e.getNextException() instanceof SocketException) || (e.getNextException() instanceof ConnectException))) { listener .getLogger() .println("Socket error sending email, retrying once more in 10 seconds..."); Thread.sleep(10000); } else { Address[] addresses = e.getValidSentAddresses(); if (addresses != null && addresses.length > 0) { buf = new StringBuilder("Successfully sent to the following addresses:"); for (Address a : addresses) { buf.append(' ').append(a); } listener.getLogger().println(buf); } addresses = e.getValidUnsentAddresses(); if (addresses != null && addresses.length > 0) { buf = new StringBuilder("Error sending to the following VALID addresses:"); for (Address a : addresses) { buf.append(' ').append(a); } listener.getLogger().println(buf); } addresses = e.getInvalidAddresses(); if (addresses != null && addresses.length > 0) { buf = new StringBuilder("Error sending to the following INVALID addresses:"); for (Address a : addresses) { buf.append(' ').append(a); } listener.getLogger().println(buf); } debug(listener.getLogger(), "SendFailedException message: " + e.getMessage()); break; } } retries++; if (retries > 1) { listener.getLogger().println("Failed after second try sending email"); break; } } if (build.getAction(MailMessageIdAction.class) == null) { build.addAction(new MailMessageIdAction(msg.getMessageID())); } } else { listener.getLogger().println("Email sending was cancelled" + " by user script."); } return true; } else { listener .getLogger() .println("An attempt to send an e-mail" + " to empty list of recipients, ignored."); } } catch (Exception e) { LOGGER.log(Level.WARNING, "Could not send email.", e); e.printStackTrace( listener.error("Could not send email as a part of the post-build publishers.")); } debug( listener.getLogger(), "Some error occured trying to send the email...check the Jenkins log"); return false; }