<commit_before>package hudson.plugins.emailext;

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.init.InitMilestone;
import hudson.init.Initializer;
import hudson.matrix.MatrixProject;
import hudson.model.AbstractProject;
import hudson.plugins.emailext.plugins.EmailTriggerDescriptor;
import hudson.plugins.emailext.plugins.trigger.FailureTrigger;
import hudson.security.Permission;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Publisher;
import hudson.util.FormValidation;
import hudson.util.ListBoxModel;
import hudson.util.ReflectionUtils;
import hudson.util.Secret;
import jenkins.model.Jenkins;
import jenkins.model.JenkinsLocationConfiguration;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.Symbol;
import org.jenkinsci.plugins.scriptsecurity.scripts.ApprovalContext;
import org.jenkinsci.plugins.scriptsecurity.scripts.ClasspathEntry;
import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval;
import org.jenkinsci.plugins.scriptsecurity.scripts.languages.GroovyLanguage;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;


import javax.mail.Authenticator;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.servlet.ServletException;
import java.io.IOException;
import java.io.PrintStream;
import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import static org.apache.commons.lang.StringUtils.isBlank;

/**
 * These settings are global configurations
 */
@Extension
@Symbol("email-ext")
public final class ExtendedEmailPublisherDescriptor extends BuildStepDescriptor<Publisher> {

    public static final Logger LOGGER = Logger.getLogger(ExtendedEmailPublisherDescriptor.class.getName());
    /**
     * The default e-mail address suffix appended to the user name found from
     * changelog, to send e-mails. Null if not configured.
     */
    private String defaultSuffix;

    /**
     * Jenkins's own URL, to put into the e-mail.
     */
    private transient String hudsonUrl;

    private MailAccount mailAccount = new MailAccount();

    private List<MailAccount> addAccounts = new ArrayList<>();

    /**
     * The e-mail address that Jenkins puts to "From:" field in outgoing
     * e-mails. Null if not configured.
     */
    private transient String adminAddress;

    private String charset;

    /**
     * This is a global default content type (mime type) for emails.
     */
    private String defaultContentType;

    /**
     * This is a global default subject line for sending emails.
     */
    private String defaultSubject;

    /**
     * This is a global default body for sending emails.
     */
    private String defaultBody;

    /**
     * This is the global default pre-send script.
     */
    private String defaultPresendScript = "";

    /**
     * This is the global default post-send script.
     */
    private String defaultPostsendScript = "";

    private List<GroovyScriptPath> defaultClasspath = new ArrayList<>();

    private transient List<EmailTriggerDescriptor> defaultTriggers = new ArrayList<>();

    private List<String> defaultTriggerIds = new ArrayList<>();

    /**
     * This is the global emergency email address
     */
    private String emergencyReroute;

    /**
     * The maximum size of all the attachments (in bytes)
     */
    private long maxAttachmentSize = -1;

    /*
     * This is a global default recipient list for sending emails.
     */
    private String recipientList = "";

    /*
     * The default Reply-To header value
     */
    private String defaultReplyTo = "";

    /*
     * This is a global list of domains where we can send emails to
     */
    private String allowedDomains = null;

    /*
     * This is a global excluded committers list for not sending commit emails.
     */
    private String excludedCommitters = "";

    private boolean overrideGlobalSettings;

    /**
     * If non-null, set a List-ID email header.
     */
    private String listId;

    private boolean precedenceBulk;

    private boolean debugMode = false;

    private transient boolean enableSecurity = false;

    /**
     * If true, then the 'Email Template Testing' link will only be displayed
     * for users with ADMINISTER privileges.
     */
    private boolean requireAdminForTemplateTesting = false;

    /**
     * Enables the "Watch This Job" feature
     */
    private boolean enableWatching;

    /**
     * Enables the "Allow Unregistered Emails" feature
     */
    private boolean enableAllowUnregistered;

    private transient String smtpHost;
    private transient String smtpPort;
    private transient String smtpAuthUsername;
    private transient Secret smtpAuthPassword;
    private transient boolean useSsl = false;

    private Object readResolve(){
        if(smtpHost != null) mailAccount.setSmtpHost(smtpHost);
        if(smtpPort != null) mailAccount.setSmtpPort(smtpPort);
        if(smtpAuthUsername != null) mailAccount.setSmtpUsername(smtpAuthUsername);
        if(smtpAuthPassword != null) mailAccount.setSmtpPassword(smtpAuthPassword);
        if(useSsl) mailAccount.setUseSsl(useSsl);
        return this;
    }

    public ExtendedEmailPublisherDescriptor() {
        super(ExtendedEmailPublisher.class);
        load();

        if (defaultBody == null && defaultSubject == null && emergencyReroute == null) {
            defaultBody = ExtendedEmailPublisher.DEFAULT_BODY_TEXT;
            defaultSubject = ExtendedEmailPublisher.DEFAULT_SUBJECT_TEXT;
            emergencyReroute = ExtendedEmailPublisher.DEFAULT_EMERGENCY_REROUTE_TEXT;
        }
        
        if(mailAccount == null) {
            mailAccount = new MailAccount();
            mailAccount.setAddress(getAdminAddress());
        }

        mailAccount.setDefaultAccount(true);
    }
    
    @Initializer(after = InitMilestone.EXTENSIONS_AUGMENTED, before = InitMilestone.JOB_LOADED)
    public static void autoConfigure() {
        ExtendedEmailPublisherDescriptor descriptor = ExtendedEmailPublisher.descriptor();

        if (Jenkins.get().isUseSecurity()
                && (!StringUtils.isBlank(descriptor.getDefaultPostsendScript())) || !StringUtils.isBlank(descriptor.getDefaultPresendScript())) {
            descriptor.setDefaultPostsendScript(descriptor.getDefaultPostsendScript());
            descriptor.setDefaultPresendScript(descriptor.getDefaultPresendScript());
            try {
                descriptor.setDefaultClasspath(descriptor.getDefaultClasspath());
            } catch (FormException e) {
                //Some of the old configured classpaths probably used some environment variable, let's clean those out
                List<GroovyScriptPath> newList = new ArrayList<>();
                for (GroovyScriptPath path : descriptor.getDefaultClasspath()) {
                    URL u = path.asURL();
                    if (u != null) {
                        try {
                            new ClasspathEntry(u.toString());
                            newList.add(path);
                        } catch (MalformedURLException mfue) {
                            LOGGER.log(Level.WARNING, "The default classpath contained a malformed url, will be ignored.", mfue);
                        }
                    }
                }
                try {
                    descriptor.setDefaultClasspath(newList);
                } catch (FormException e1) {
                    assert false : e1;
                }
            }
        }
    }

    @Override
    public String getDisplayName() {
        return Messages.ExtendedEmailPublisherDescriptor_DisplayName();
    }

    public String getAdminAddress() {
        JenkinsLocationConfiguration config = JenkinsLocationConfiguration.get();
        if(config != null) {
            if(StringUtils.isBlank(mailAccount.getAddress())) {
                mailAccount.setAddress(config.getAdminAddress());
            }
        }
        return mailAccount.getAddress();
    }

    public String getDefaultSuffix() {
        return defaultSuffix;
    }

    @DataBoundSetter
    public void setDefaultSuffix(String defaultSuffix) {
        this.defaultSuffix = defaultSuffix;
    }

    public Session createSession(String from) throws MessagingException {
        Properties props = new Properties(System.getProperties());

        MailAccount acc = mailAccount;
        if(StringUtils.isNotBlank(from)){
            InternetAddress fromAddress = new InternetAddress(from);
            for(MailAccount ma : addAccounts) {
                if(ma == null || !ma.isValid() || !ma.getAddress().equalsIgnoreCase(fromAddress.getAddress())) continue;
                acc = ma;
                break;
            }
        }

        if(!acc.isValid()) {
            // what do we want to do here?
        }

        if (acc.getSmtpHost() != null) {
            props.put("mail.smtp.host", acc.getSmtpHost());
        }
        if (acc.getSmtpPort() != null) {
            props.put("mail.smtp.port", acc.getSmtpPort());
        }
        if (acc.isUseSsl()) {
            /* This allows the user to override settings by setting system properties but
             * also allows us to use the default SMTPs port of 465 if no port is already set.
             * It would be cleaner to use smtps, but that's done by calling session.getTransport()...
             * and thats done in mail sender, and it would be a bit of a hack to get it all to
             * coordinate, and we can make it work through setting mail.smtp properties.
             */
            if (props.getProperty("mail.smtp.socketFactory.port") == null) {
                String port = acc.getSmtpPort() == null ? "465" : mailAccount.getSmtpPort();
                props.put("mail.smtp.port", port);
                props.put("mail.smtp.socketFactory.port", port);
            }
            if (props.getProperty("mail.smtp.socketFactory.class") == null) {
                props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
            }
            props.put("mail.smtp.socketFactory.fallback", "false");
        }
        if (!StringUtils.isBlank(acc.getSmtpUsername())) {
            props.put("mail.smtp.auth", "true");<commit_msg>[SECURITY-1851]<commit_after>package hudson.plugins.emailext;

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.init.InitMilestone;
import hudson.init.Initializer;
import hudson.matrix.MatrixProject;
import hudson.model.AbstractProject;
import hudson.plugins.emailext.plugins.EmailTriggerDescriptor;
import hudson.plugins.emailext.plugins.trigger.FailureTrigger;
import hudson.security.Permission;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Publisher;
import hudson.util.FormValidation;
import hudson.util.ListBoxModel;
import hudson.util.ReflectionUtils;
import hudson.util.Secret;
import jenkins.model.Jenkins;
import jenkins.model.JenkinsLocationConfiguration;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.Symbol;
import org.jenkinsci.plugins.scriptsecurity.scripts.ApprovalContext;
import org.jenkinsci.plugins.scriptsecurity.scripts.ClasspathEntry;
import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval;
import org.jenkinsci.plugins.scriptsecurity.scripts.languages.GroovyLanguage;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;


import javax.mail.Authenticator;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.servlet.ServletException;
import java.io.IOException;
import java.io.PrintStream;
import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import static org.apache.commons.lang.StringUtils.isBlank;

/**
 * These settings are global configurations
 */
@Extension
@Symbol("email-ext")
public final class ExtendedEmailPublisherDescriptor extends BuildStepDescriptor<Publisher> {

    public static final Logger LOGGER = Logger.getLogger(ExtendedEmailPublisherDescriptor.class.getName());
    /**
     * The default e-mail address suffix appended to the user name found from
     * changelog, to send e-mails. Null if not configured.
     */
    private String defaultSuffix;

    /**
     * Jenkins's own URL, to put into the e-mail.
     */
    private transient String hudsonUrl;

    private MailAccount mailAccount = new MailAccount();

    private List<MailAccount> addAccounts = new ArrayList<>();

    /**
     * The e-mail address that Jenkins puts to "From:" field in outgoing
     * e-mails. Null if not configured.
     */
    private transient String adminAddress;

    private String charset;

    /**
     * This is a global default content type (mime type) for emails.
     */
    private String defaultContentType;

    /**
     * This is a global default subject line for sending emails.
     */
    private String defaultSubject;

    /**
     * This is a global default body for sending emails.
     */
    private String defaultBody;

    /**
     * This is the global default pre-send script.
     */
    private String defaultPresendScript = "";

    /**
     * This is the global default post-send script.
     */
    private String defaultPostsendScript = "";

    private List<GroovyScriptPath> defaultClasspath = new ArrayList<>();

    private transient List<EmailTriggerDescriptor> defaultTriggers = new ArrayList<>();

    private List<String> defaultTriggerIds = new ArrayList<>();

    /**
     * This is the global emergency email address
     */
    private String emergencyReroute;

    /**
     * The maximum size of all the attachments (in bytes)
     */
    private long maxAttachmentSize = -1;

    /*
     * This is a global default recipient list for sending emails.
     */
    private String recipientList = "";

    /*
     * The default Reply-To header value
     */
    private String defaultReplyTo = "";

    /*
     * This is a global list of domains where we can send emails to
     */
    private String allowedDomains = null;

    /*
     * This is a global excluded committers list for not sending commit emails.
     */
    private String excludedCommitters = "";

    private boolean overrideGlobalSettings;

    /**
     * If non-null, set a List-ID email header.
     */
    private String listId;

    private boolean precedenceBulk;

    private boolean debugMode = false;

    private transient boolean enableSecurity = false;

    /**
     * If true, then the 'Email Template Testing' link will only be displayed
     * for users with ADMINISTER privileges.
     */
    private boolean requireAdminForTemplateTesting = false;

    /**
     * Enables the "Watch This Job" feature
     */
    private boolean enableWatching;

    /**
     * Enables the "Allow Unregistered Emails" feature
     */
    private boolean enableAllowUnregistered;

    private transient String smtpHost;
    private transient String smtpPort;
    private transient String smtpAuthUsername;
    private transient Secret smtpAuthPassword;
    private transient boolean useSsl = false;

    private Object readResolve(){
        if(smtpHost != null) mailAccount.setSmtpHost(smtpHost);
        if(smtpPort != null) mailAccount.setSmtpPort(smtpPort);
        if(smtpAuthUsername != null) mailAccount.setSmtpUsername(smtpAuthUsername);
        if(smtpAuthPassword != null) mailAccount.setSmtpPassword(smtpAuthPassword);
        if(useSsl) mailAccount.setUseSsl(useSsl);
        return this;
    }

    public ExtendedEmailPublisherDescriptor() {
        super(ExtendedEmailPublisher.class);
        load();

        if (defaultBody == null && defaultSubject == null && emergencyReroute == null) {
            defaultBody = ExtendedEmailPublisher.DEFAULT_BODY_TEXT;
            defaultSubject = ExtendedEmailPublisher.DEFAULT_SUBJECT_TEXT;
            emergencyReroute = ExtendedEmailPublisher.DEFAULT_EMERGENCY_REROUTE_TEXT;
        }
        
        if(mailAccount == null) {
            mailAccount = new MailAccount();
            mailAccount.setAddress(getAdminAddress());
        }

        mailAccount.setDefaultAccount(true);
    }
    
    @Initializer(after = InitMilestone.EXTENSIONS_AUGMENTED, before = InitMilestone.JOB_LOADED)
    public static void autoConfigure() {
        ExtendedEmailPublisherDescriptor descriptor = ExtendedEmailPublisher.descriptor();

        if (Jenkins.get().isUseSecurity()
                && (!StringUtils.isBlank(descriptor.getDefaultPostsendScript())) || !StringUtils.isBlank(descriptor.getDefaultPresendScript())) {
            descriptor.setDefaultPostsendScript(descriptor.getDefaultPostsendScript());
            descriptor.setDefaultPresendScript(descriptor.getDefaultPresendScript());
            try {
                descriptor.setDefaultClasspath(descriptor.getDefaultClasspath());
            } catch (FormException e) {
                //Some of the old configured classpaths probably used some environment variable, let's clean those out
                List<GroovyScriptPath> newList = new ArrayList<>();
                for (GroovyScriptPath path : descriptor.getDefaultClasspath()) {
                    URL u = path.asURL();
                    if (u != null) {
                        try {
                            new ClasspathEntry(u.toString());
                            newList.add(path);
                        } catch (MalformedURLException mfue) {
                            LOGGER.log(Level.WARNING, "The default classpath contained a malformed url, will be ignored.", mfue);
                        }
                    }
                }
                try {
                    descriptor.setDefaultClasspath(newList);
                } catch (FormException e1) {
                    assert false : e1;
                }
            }
        }
    }

    @Override
    public String getDisplayName() {
        return Messages.ExtendedEmailPublisherDescriptor_DisplayName();
    }

    public String getAdminAddress() {
        JenkinsLocationConfiguration config = JenkinsLocationConfiguration.get();
        if(config != null) {
            if(StringUtils.isBlank(mailAccount.getAddress())) {
                mailAccount.setAddress(config.getAdminAddress());
            }
        }
        return mailAccount.getAddress();
    }

    public String getDefaultSuffix() {
        return defaultSuffix;
    }

    @DataBoundSetter
    public void setDefaultSuffix(String defaultSuffix) {
        this.defaultSuffix = defaultSuffix;
    }

    public Session createSession(String from) throws MessagingException {
        Properties props = new Properties(System.getProperties());

        MailAccount acc = mailAccount;
        if(StringUtils.isNotBlank(from)){
            InternetAddress fromAddress = new InternetAddress(from);
            for(MailAccount ma : addAccounts) {
                if(ma == null || !ma.isValid() || !ma.getAddress().equalsIgnoreCase(fromAddress.getAddress())) continue;
                acc = ma;
                break;
            }
        }

        if(!acc.isValid()) {
            // what do we want to do here?
        }

        if (acc.getSmtpHost() != null) {
            props.put("mail.smtp.host", acc.getSmtpHost());
        }
        if (acc.getSmtpPort() != null) {
            props.put("mail.smtp.port", acc.getSmtpPort());
        }
        if (acc.isUseSsl()) {
            /* This allows the user to override settings by setting system properties but
             * also allows us to use the default SMTPs port of 465 if no port is already set.
             * It would be cleaner to use smtps, but that's done by calling session.getTransport()...
             * and thats done in mail sender, and it would be a bit of a hack to get it all to
             * coordinate, and we can make it work through setting mail.smtp properties.
             */
            if (props.getProperty("mail.smtp.socketFactory.port") == null) {
                String port = acc.getSmtpPort() == null ? "465" : mailAccount.getSmtpPort();
                props.put("mail.smtp.port", port);
                props.put("mail.smtp.socketFactory.port", port);
            }
            if (props.getProperty("mail.smtp.socketFactory.class") == null) {
                props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
            }
            props.put("mail.smtp.socketFactory.fallback", "false");

            // RFC 2595 specifies additional checks that must be performed on the server's
            // certificate to ensure that the server you connected to is the server you intended
            // to connect to. This reduces the risk of "man in the middle" attacks.
            if (props.getProperty("mail.smtp.ssl.checkserveridentity") == null) {
                props.put("mail.smtp.ssl.checkserveridentity", "true");
            }
        }
        if (!StringUtils.isBlank(acc.getSmtpUsername())) {
            props.put("mail.smtp.auth", "true");
{'fraction_non_alphanumeric': 0.09177171670868668, 'fraction_numerical': 0.0011918404767361907, 'mean_word_length': 2.641462193289935, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '535836', 'n_tokens_mistral': 5981, 'n_tokens_neox': 5610, 'n_words': 1473}