Visitar URL original
GitHub - ottlinger/mailversendala: Application to send out emails to lists of people · GitHub
Skip to content

About

Application to send out emails to lists of people

Topics

Resources

Code of conduct

Stars

0 stars

Watchers

1 watching

Forks

Latest commit

 

History

1,365 Commits

Folders and files

Repository files navigation

mailversendala

Application to send out mass emails based on a list of recipients in a CSV file.

CSV format

The application accepts UTF-8 CSV files (separator is ,) with a header line. The columns firstname, surname, email, and salutation are required. Columns are matched by name, so their order does not matter.

firstname surname email salutation
Erika Müller erika@example.test Liebe
Peter Schmidt peter@example.test Lieber

Enter the greeting prefix in salutation, for example Liebe, Lieber, or Guten Tag, without the recipient's name. The application uses this value as supplied; it does not infer a greeting from the name or gender.

Every recipient must have a complete mail address in email. Empty values or values consisting only of whitespace are not accepted. Rows with a missing or blank email field are logged and skipped; they are not sent. An example file is mailversendala-example.csv.

Project webpage

A Maven-generated site report is also available here.

Project Status

Average time to resolve an issue Percentage of issues still open Known Vulnerabilities ASF 2.0

GitHub integrations

CI / GHA

GitHub Action master branch status

Code coverage

codecov

Codacy - code quality and static analysis

Codacy Badge

How to run and use the application

Check out the application and build it with Java (17 or newer) and the included Maven Wrapper. A separate Maven installation is not required; the wrapper downloads the configured Maven version. Use ./mvnw verify on macOS/Linux or .\mvnw.cmd verify on Windows to build and run all unit and packaged-JAR integration tests.

Mail delivery uses Commons Email 2's Jakarta variant (2.0.0-M1, currently a milestone release) with Eclipse Angus as the Jakarta Mail/Activation implementation. Local delivery tests use GreenMail 2.1.x with the same mail stack; the executable JAR includes the runtime providers but not GreenMail or the legacy javax.mail/javax.activation APIs.

$ git clone https://github.com/ottlinger/mailversendala.git
Cloning into 'mailversendala'...
...

$ ./mvnw verify
....
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8.864 s
[INFO] Final Memory: 34M/481M
[INFO] ------------------------------------------------------------------------

 $ java -jar target/mailversendala-2.0.0-SNAPSHOT-executable.jar
2024-12-22 00:48:00,714 DEBUG d.a.m.MailConfig [main] Reading Tamaya configuration ...
Dez. 22, 2024 12:48:00 AM org.apache.tamaya.spi.ServiceContextManager loadDefaultServiceProvider
INFORMATION: Using Service Context of type: org.apache.tamaya.spisupport.DefaultServiceContext
2024-12-22 00:48:00,755 DEBUG d.a.m.MailConfig [main] Configuration: DONE.
2024-12-22 00:48:00,755 INFO d.a.m.Mailversendala [main] **** MAILVERSENDALA: Starting .... ****
2024-12-22 00:48:00,757 INFO d.a.m.Mailversendala [main] Consuming CSV: mailversendala-example.csv
2024-12-22 00:48:00,758 INFO d.a.m.Mailversendala [main] mailversendala-example.csv
2024-12-22 00:48:00,758 WARN d.a.m.Mailversendala [main] Nothing to do - please configure your CSV path properly, either as environment variable or as a runtime parameter. Example: java -Dcsvpath=foo -jar fatJar.jar
2024-12-22 00:48:00,758 INFO d.a.m.Mailversendala [main] **** MAILVERSENDALA: Application shutdown .... ****
2024-12-22 00:48:00,758 INFO d.a.m.App [main] Mailversendala is shutting down. Bye Bye :-)

In case you want to use the application productively you need to configure it, please continue reading the following paragraph.

How to configure the application

In order to properly configure the application you need to set some mail-related properties. Use javaconfiguration.properties.template as a starting point and pass the saved UTF-8 properties file using -Dconfig=path. See the external configuration example below.

Technically the configuration is based on Apache Tamaya (incubating)

Available configuration modes

If you are running the application as a fat JAR you may want to overwrite each of the configuration keys either by environment variable or runtime property:

$ java -jar target/mailversendala-2.0.0-SNAPSHOT-executable.jar
// runs with default parameters from within the JAR

$ export csvpath=/tmp/my.csv; java -jar target/mailversendala-2.0.0-SNAPSHOT-executable.jar
// configuration set as environment variable

$ java -Dcsvpath=/tmp/my.csv -jar target/mailversendala-2.0.0-SNAPSHOT-executable.jar
// configuration option set as JVM parameter

External configuration without credentials on the command line

You can keep your SMTP credentials in an external UTF-8 configuration file without including them in the JAR. Pass the file explicitly using -Dconfig=path before -jar. Simply placing the file beside the JAR is not sufficient.

Configuration precedence (highest first): JVM properties (-Dkey=value), environment variables, the explicit config file, Tamaya configuration from the classpath, built-in defaults. An unreadable or malformed explicit config file aborts startup; it does not silently fall back to the JAR values. The loaded file's path is logged, but its contents are not.

For example, organise your files as follows:

mailing/
├── mailversendala-2.0.0-SNAPSHOT-executable.jar
├── config/
│   └── META-INF/
│       └── javaconfiguration.properties
├── data/
│   └── recipients.csv
└── templates/
    ├── mailtemplate.html
    ├── mailtemplate.txt
    └── attachments/
        └── information.pdf         # optional

config/META-INF/javaconfiguration.properties

host=smtp.example.com
port=465
username=user@example.com
password=YOUR_SMTP_PASSWORD

from=user@example.com
# Optional: send replies to a different address.
# replyto=replies@example.com
subject=Your invitation

# Test without actually sending any emails first.
demomode=true

csvpath=data/recipients.csv
templatepath=templates
htmltemplate=mailtemplate.html
plaintexttemplate=mailtemplate.txt

# Optional:
# attachments=attachments/information.pdf

Relative paths are resolved from the working directory when the application starts, not from the configuration file's directory. In this example, start from mailing/. Alternatively, use absolute paths.

data/recipients.csv (UTF-8)

firstname,surname,email,salutation
Erika,Müller,erika@example.test,Dear
Peter,Schmidt,peter@example.test,Dear

templates/mailtemplate.html (UTF-8)

<html><body>
<p>${salutation} ${firstName},</p>
<p>Here is your invitation.</p>
</body></html>

templates/mailtemplate.txt (UTF-8)

${salutation} ${firstName},

Here is your invitation.

From mailing/, start the application with (also works on Windows):

java -Dconfig=config/META-INF/javaconfiguration.properties \
  -jar mailversendala-2.0.0-SNAPSHOT-executable.jar

On Windows, enter the command on a single line. The explicit file can have any name and does not need a META-INF directory.

Legacy classpath configuration via -cp "config:mailversendala-2.0.0-SNAPSHOT-executable.jar" and main class de.aikiit.mailversendala.App remains supported (use ; on Windows). However, classpath order alone does not guarantee precedence between multiple Tamaya configuration resources. Prefer -Dconfig for a deterministic external override.

Keep demomode=true for the initial test. Set demomode=false in the configuration file only when you are ready to send emails. Replace the example recipient addresses with real addresses before sending.

The password is still stored in plain text, but is not included in shell history or process arguments. Restrict access to the configuration on macOS or Linux:

chmod 700 config config/META-INF
chmod 600 config/META-INF/javaconfiguration.properties

On Windows, restrict access using the file and folder security settings. Do not commit the credentials file to version control or package it inside the JAR.

Delivery protocol for each run

Every application start writes an additional UTF-8 log file into the current working directory, alongside the existing console output. A fixed run ID combines a timestamp and UUID, keeping the filename unique and stable even if logging is reconfigured. The filename is Windows-compatible, for example:

mailversendala-2026-11-08_16-30-42-123-789f7c38-35e9-44a4-8e32-f2a5e301a9ae.log

The INFO-level protocol contains per-recipient delivery/preparation messages, the final success/error report, and application errors. Demo-mode messages are explicitly marked as preparation rather than delivery. Writes are flushed immediately. Existing files are never overwritten; reinitializing the logger appends to the same run's protocol.

*.log is excluded by .gitignore, so these protocols are not checked into Git. Files are retained until you archive or delete them yourself; there is no automatic rotation or deletion. The working directory must be writable.

Protocols contain recipient email addresses and may contain personal data in error messages. Restrict access and keep them only as long as needed. These are application logs, not a guarantee that a message reached an inbox: a successful SMTP send means the SMTP server accepted it for further delivery.

Configurable program options

The following parameters can be set:

Configuration option Description Example/default setting
host SMTP mail host smtp.example.com
port SMTP host port 465
username SMTP plaintext username user@example.com
password SMTP plaintext password chooseMeWisely
demomode boolean, whether to not send mails true
csvpath fully qualified path to your CSV data input file /tmp/mailversendala-example.csv
templatepath Path to the external UTF-8 Velocity template directory /tmp/mailversendala
htmltemplate HTML template filename, relative to templatepath mailtemplate.html
plaintexttemplate Plain-text template filename, relative to templatepath mailtemplate.txt
attachments Optional comma-separated attachment paths, relative to templatepath attachments/information.pdf
subject full subject line of the example mail Whatever you want is a subject line
from mail address that is sent from santa@cruz.com
replyto Optional single Reply-To email address; empty or omitted means replies go to from replies@example.com (default: empty)
to Optional recipient of one additional, unrendered test mail during the normal CSV run xmas@man.com (default: empty)

Additional test mail to yourself during the CSV run

Set to to your own email address in the config file:

to=xmas@man.com
demomode=true

Start the application normally:

java -Dconfig=config/META-INF/javaconfiguration.properties \
  -jar mailversendala-2.0.0-SNAPSHOT-executable.jar

During the CSV run, one additional test mail is prepared for to and sent before any personalized CSV messages. It contains the original HTML and plain-text template contents, including unresolved placeholders such as ${firstName} or ${unknownField}. Rendering errors for CSV recipients therefore do not prevent this test mail. CSV recipients are still personalized separately; to does not replace them. The report includes the additional test mail in its success/error counts.

The test mail uses the same SMTP settings, subject, Reply-To, images and attachments. Template files and resource references must still be valid and readable; safety checks are not bypassed (unresolved placeholders in image paths cannot identify a real file). Omit to or leave it empty to disable the additional test mail. There is no default test recipient.

Keep demomode=true for preparation without delivery. With demomode=false, both the test mail and all successfully rendered CSV messages are sent; setting to is not a test-only mode.

HTML and plain-text mail templates

Each CSV recipient receives one email with both HTML and plain-text alternatives. The mail client chooses which version to display. Both bodies are rendered with Apache Velocity; the configured subject is used unchanged and both alternatives use UTF-8.

Create two UTF-8 files outside the JAR, for example:

/tmp/mailversendala/welcome.html

<html><body>
<p>${salutation} ${firstName},</p>
<p>Your invitation is ready.</p>
</body></html>

/tmp/mailversendala/welcome.txt

${salutation} ${firstName},

Your invitation is ready.

Configure the directory and filenames in javaconfiguration.properties, or override these settings using JVM properties before -jar:

java -Dtemplatepath=/tmp/mailversendala \
     -Dhtmltemplate=welcome.html \
     -Dplaintexttemplate=welcome.txt \
     -Dcsvpath=/tmp/recipients.csv \
     -jar target/mailversendala-2.0.0-SNAPSHOT-executable.jar

SMTP credentials and from must also be configured. Demo mode is enabled by default; set demomode=false only when ready to deliver the messages.

Available recipient variables are ${salutation}, ${firstName}, ${lastName}, and ${email}. ${firstname} and ${surname} are aliases matching the CSV column names. With the CSV above, ${salutation} ${firstName} renders Liebe Erika for Erika and Lieber Peter for Peter, in both HTML and plain text. The template supplies the separating space and any punctuation; the CSV value supplies the greeting.

References in HTML are HTML-escaped automatically; plain-text values are not HTML-escaped. Use these variables in HTML text or double-quoted attributes, not as JavaScript or unvalidated URLs.

Both templates are loaded and compiled once per run. Missing or syntactically invalid templates abort the run before delivery; rendering or SMTP errors are counted per recipient. Unknown variables are rendering errors rather than being sent as literal placeholders. Templates can use Velocity directives such as #if and #foreach; only use trusted template files. The template-based sending flow never downloads remote images or attachments.

HTML markup checks before delivery

The normal CSV run checks every successfully rendered HTML body with Jsoup before any SMTP delivery, including delivery of the additional test mail. Errors such as mismatched closing tags, invalid nesting, duplicate attributes or unterminated quotes abort the entire run. Ordinary unknown HTML tags such as <spna> instead of <span> are also rejected, since Jsoup would otherwise accept them silently.

Diagnostics include the HTML template path, the affected recipient, and line/column positions in the rendered HTML. Velocity directives may change line numbers relative to the original template. Up to 20 errors are reported per failed body. For example:

Invalid HTML markup in templates/welcome.html (recipient: ada@example.test) ...
line/column 2:1: Unknown HTML tag <spna>

Full HTML documents and body fragments are supported, as are HTML void elements such as <br> and <img> without XML-style closing tags. The check does not rewrite the HTML or fetch external resources. It also runs in demo mode, so demomode=true lets you diagnose template problems without sending any mail.

This is a typo/parser check, not a complete HTML conformance validator or a guarantee of correct rendering in every mail client. Custom elements with hyphenated names, namespaced markup, and Outlook conditional comments are allowed. Not every misspelled attribute can be detected.

Only rendered CSV bodies are checked: the additional unrendered test mail retains placeholders and Velocity directives intentionally. Checking those directives as finished HTML could reject a valid template. If a CSV body's markup check fails, no messages, including that test mail, are sent. Existing image and attachment checks still apply to the unrendered test mail.

Inline images and file attachments

Images referenced by HTML <img src="..."> elements are automatically embedded in the email using MIME Content-IDs. The image is sent with the message rather than downloaded from a website. PDF documents, images, and other files can also be added as ordinary, downloadable attachments. HTML and plain-text alternatives are preserved in both cases.

For example:

/path/to/templates/
├── welcome.html
├── welcome.txt
├── images/
│   └── logo.png
└── attachments/
    └── information.pdf

In welcome.html:

<p>Hello ${firstName} ${lastName},</p>
<img src="images/logo.png" alt="Our logo">
<p>Please see the attached information document.</p>

Configure the ordinary attachment separately:

templatepath=/path/to/templates
htmltemplate=welcome.html
plaintexttemplate=welcome.txt
attachments=attachments/information.pdf

The equivalent JVM override is -Dattachments=attachments/information.pdf before -jar. To attach several files, separate their paths with commas, for example attachments=attachments/information.pdf,images/logo.png. Leave the setting empty for no ordinary attachments. Repeated paths to the same file are attached only once. Merely linking a PDF with <a href="..."> does not attach it. Attachment configuration is shared by all recipients and is not interpreted as a Velocity template.

All image and attachment paths are relative to templatepath, even when the HTML template itself is in a subdirectory. UTF-8 filenames and percent-encoded paths are supported; use / as the separator. Filenames containing commas cannot be listed in attachments. MIME types are inferred from filenames, so use appropriate extensions. Inline resources must have an image MIME type.

Absolute paths, paths escaping the template directory (including symlinks), remote URLs, file:/data:/pre-existing cid: image sources, and query strings/fragments are rejected. srcset is not supported; use img src. CSS background images and other HTML resource references are not automatically embedded. Ordinary web hyperlinks remain unchanged and are not fetched by the application.

Before the first SMTP delivery, the application renders the recipients and validates all referenced images and configured attachments. Missing or forbidden resources abort the entire run, including when referenced only by a later recipient. Resource bytes are snapshotted in memory once per unique file, so a file changed or deleted afterward does not change already validated content. Allow enough memory for the files and the rendered messages in a large mailing. Demo mode performs these checks without sending.

For a complete working example, copy the existing template directory, including its img/ and attachments/ subdirectories:

mkdir -p /tmp/mailversendala
cp -R src/test/resources/template/. /tmp/mailversendala/

Use htmltemplate=mailtemplate.html, plaintexttemplate=mailtemplate.txt, and attachments=attachments/information.pdf. The example HTML embeds a local logo; the example PDF is a harmless information document. Image attribution is preserved in src/test/resources/template/img/readme.txt.

Development

In order to properly integrate the project into your IDE you need to setup lombok support - please follow the guidelines about integration, e.g. Eclipse integration.

Changelog

See CHANGELOG.md.

Copyright

© P. Ottlinger, 2017

About

Application to send out emails to lists of people

Topics

Resources

Code of conduct

Stars

0 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages