A native SMTP, POP3, and IMAP server module for RISC OS, enabling local mail delivery and retrieval.
Mail is a lightweight mail server implementation designed specifically for RISC OS. It accepts incoming SMTP connections, stores messages locally, and provides POP3/IMAP retrieval with per-user authentication. Recent updates add richer greeting banners with version information, shared authentication helpers, and protocol capability advertising. Perfect for local development, testing, or small-scale deployments on RISC OS systems.
- Listens on port 25 (configurable)
- Domain-based acceptance rules
- Per-user storage organized by domain
- Configurable message size limits
- Custom greeting banner with
%HOST%and%VERSION%tokens plus host aliases - Session timeout management
- Per-mailbox quota enforcement
- EHLO capability advertising (PIPELINING, 8BITMIME, SIZE)
- Listens on port 110 (configurable)
- User authentication from configuration file
- Standard POP3 commands: USER, PASS, STAT, LIST, RETR, DELE, RSET, QUIT
- Extended commands: UIDL, LAST, TOP
- CAPA advertisement including implementation version
- Session timeout management
- Mailbox locking during retrieval
- Listens on port 143 (configurable)
- Shares the POP3 user database for authentication through a common verifier
- Supports core commands: CAPABILITY, LOGIN/LOGOUT, NOOP, LIST/LSUB, SELECT/EXAMINE, SEARCH (ALL/UNSEEN), FETCH/UID FETCH, STORE, EXPUNGE, CLOSE
- Maintains unique identifiers (UIDs) and message flags (
\Seen,\Deleted) - Coordinates mailbox locking with POP3 sessions
- Advertises IMAP4rev1, UIDPLUS, and LITERAL+ capabilities
- Daily rotating log files with per-session context
- Real-time session monitoring via
*Mail_Status, including listener state and capability summaries - Hot configuration reload with
*Mail_Reload - Queue status reporting with
*Mail_Queue - Native RISC OS module using SWI calls
- Flexible user aliases with multi-target expansion
Edit <Mail$Dir>.Config.System:
Hostname = riscos.local
HostAliases = riscos.local,riscos.test.local
MailboxQuota = 52428800
SmtpEnabled = 1
SmtpListenPort = 25
SmtpSessionTimeout = 300
SmtpBanner = 220 %HOST% Mail %VERSION% ready
MaxMessageSize = 5242880
MaxRecipients = 100
Pop3Enabled = 1
Pop3ListenPort = 110
Pop3SessionTimeout = 300
Pop3Banner = +OK %HOST% POP3 %VERSION% ready
ImapEnabled = 1
ImapListenPort = 143
ImapSessionTimeout = 300
ImapBanner = * OK %HOST% IMAP %VERSION% ready
%HOST% is replaced with the configured hostname, while %VERSION% expands to the module build string logged at start-up. Leave any banner field blank to fall back to the built-in Mail %VERSION% ready text.
Edit <Mail$Dir>.Config.Domains (one domain per line):
localhost
riscos.local
test.local
Edit <Mail$Dir>.Config.Users (format: username@domain password):
# POP3 user accounts
andy@test.local mypassword
user@riscos.local secret123
Note: Passwords are stored in plain text. Protect this file appropriately.
Edit <Mail$Dir>.Config.Aliases to define alternate addresses and forwarding targets:
# alias target1 target2 ...
postmaster@localhost postmaster@riscos.local
andy@riscos.local andy.timmins@riscos.local andrew@riscos.local
Each line lists the alias mailbox followed by one or more delivery targets (local or remote addresses). Aliases are resolved during SMTP delivery; partial matches are case-insensitive. Run *Mail_Reload after editing the file.
*Mail_Reload Reload all configuration files
*Mail_Status Display server status, active sessions, listeners, and capabilities
*Mail_Queue Show per-mailbox message counts and storage usage
Connect to port 25 and use standard SMTP commands:
telnet localhost 25
HELO client.local
MAIL FROM:<sender@example.com>
RCPT TO:<user@riscos.local>
DATA
Subject: Test message
This is a test.
.
QUIT
Connect to port 110 using any POP3 client or manually:
telnet localhost 110
USER andy@test.local
PASS mypassword
STAT
LIST
RETR 1
DELE 1
QUIT
Most IMAP clients work out-of-the-box. For manual testing:
telnet localhost 143
A1 CAPABILITY
A2 LOGIN andy@test.local mypassword
A3 SELECT INBOX
A4 FETCH 1 BODY[]
A5 LOGOUT
<Mail$Dir>/
├── Config/
│ ├── System Server configuration
│ ├── Domains Accepted domain list
│ ├── Users POP3 user accounts
│ └── Aliases User alias mappings
├── !Mail/
│ ├── !Boot,feb Module initialization
│ ├── !Run,feb Module loader
│ ├── !Help,fff User documentation
│ └── Config/ (Symlinked configs)
├── rm/
│ └── Mail,ffa Compiled module
└── c/
└── mail Source code
<MailDir$Dir>/
├── Storage/
│ ├── !quota Quota tracking data
│ └── domain.user.timestamp Stored messages
├── Logs/
│ └── YYYYMMDD Daily log files
└── Queue/
└── !manifest Queue manifest
- RISC OS C compiler (DDE)
- CMHG (C Module Header Generator)
- Make utility
- Set up the build environment:
amu -f makefile clean - Compile the module:
amu -f makefile - The compiled module will be in
rm/Mail,ffa
c/mail- Main source code (SMTP, POP3, IMAP implementation)cmhg/modhead- Module header definitions/cstart- Module initialization assemblymakefile- Build configurationVersionNum- Version information
HELO/EHLO- Client greeting (EHLO advertises supported extensions)MAIL FROM- Sender specificationRCPT TO- Recipient specificationDATA- Message content transferRSET- Clear current transactionNOOP- Keep-aliveQUIT- Close connectionVRFY/EXPN- RFC compliance (returns "cannot verify")
USER/PASS- AuthenticationSTAT- Message count and total sizeLIST [n]- List messages with sizesUIDL [n]- Unique-ID listingRETR n- Retrieve messageTOP n lines- Retrieve headers plus specified body linesDELE n- Mark message for deletionRSET- Clear pending deletionsLAST- Highest accessed message numberNOOP- Keep-aliveQUIT- Close and apply deletions
CAPABILITY- Advertise IMAP4rev1 UIDPLUS LITERAL+LOGIN/LOGOUT- Authenticate and disconnectNOOP- Keep-aliveLIST/LSUB- Enumerate available mailboxes/subscriptionsSELECT/EXAMINE- Enter mailbox (read-write/read-only)SEARCH- Basic search (ALL / UNSEEN)FETCH/UID FETCH- Retrieve message data and flagsSTORE- Update message flags (\Seen,\Deleted)EXPUNGE/CLOSE- Remove deleted messages
Logs are written to <MailDir$Dir>.Logs.YYYYMMDD with automatic daily rotation. Each log entry includes:
- Timestamp
- Socket ID (if applicable)
- Event type (SMTP/POP3/IMAP connection, command, error)
- Details (remote address, message content, authentication outcome, etc.)
- No authentication on SMTP (yet) - only deploy on trusted networks
- Plain text passwords in POP3/IMAP - use secure networks or VPN tunnels
- No TLS/STARTTLS support - all traffic is unencrypted
- Domain validation prevents open relay
- Message size and quota limits protect against resource exhaustion
- Session timeouts prevent connection exhaustion
This server is designed for local development and trusted network use only. Do not expose it directly to the Internet.
- Verify the domain is listed in
<Mail$Dir>.Config.Domains - Check port 25 is accessible (
*Mail_Statusshows listener socket) - Review logs in
<MailDir$Dir>.Logs.YYYYMMDD - Confirm sender is using correct recipient domain
- Verify user exists in
<Mail$Dir>.Config.Users - Check username includes full email address (user@domain)
- Ensure password matches exactly (case-sensitive)
- Verify domain is also listed in Config.Domains
- Run
*Mail_Reloadafter editing Users file - Review logs for
AUTHentries to confirm the shared helper’s result
- Ensure
<Mail$Dir>system variable is set - Confirm Internet module is loaded (
*RMEnsure Internet) - Check sufficient RMA available (
*ROMModules) - Verify all required directories exist
- Run
*Mail_Reloadafter editing any config file - Check file paths are correct (RISC OS uses
.as separator) - Ensure config files are accessible and readable
- Reduce
MaxMessageSizein System config - Lower
SmtpSessionTimeoutandPop3SessionTimeout - Check RMA usage with
*ROMModules - Consider reducing
MaxRecipients
Messages are stored with a Received: header prepended to the original content:
Received: from client.local by riscos.local
for <user@domain.com>; 20251009145258
Date: Thu, 9 Oct 2025 14:53:35 +0100
From: Sender <sender@example.com>
To: user@domain.com
Subject: Example message
Message body content...
Filenames use the pattern: <MailDir$Dir>.Storage.domain.user.YYYYMMDDHHMMSS
- Per-mailbox quotas tracked in
<MailDir$Dir>.Storage.!quota - Quota check performed before accepting messages
- Set
MailboxQuota = 0to disable quota enforcement - Quotas are per user@domain combination
The module uses RISC OS Internet events:
Event_Internet(event 19) for socket state changes- Ticker events for session timeout management
- Callback events for background processing
Contributions are welcome! Please ensure:
- Code follows existing style conventions
- RISC OS compatibility is maintained
- Changes are tested on actual RISC OS hardware/emulation
- Documentation is updated accordingly