Security News
PyPI’s New Archival Feature Closes a Major Security Gap
PyPI now allows maintainers to archive projects, improving security and helping users make informed decisions about their dependencies.
Wild Duck is a distributed IMAP/POP3 server built with Node.js, MongoDB and Redis. Node.js runs the application, MongoDB is used as the mail store and Redis is used for ephemeral actions like publish/subscribe, locking and caching.
NB! Wild Duck is currently in beta. You should not use it in production.
Here's a list of email/IMAP servers that use database for storing email messages
Wild Duck IMAP server supports the following IMAP standards:
Wild Duck more or less passes the ImapTest. Common errors that arise in the test are unknown labels (Wild Duck doesn't send unsolicited FLAGS
updates even though it does send unsolicited FETCH FLAGS
updates) and sometimes NO for STORE
(messages deleted in one session can not be updated in another).
In addition to the required POP3 commands (RFC1939) Wild Duck supports the following extensions:
All changes to messages like deleting messages or marking messages as seen are stored in storage only in the UPDATE stage (eg. after calling QUIT). Until then the changes are preserved in memory only. This also means that if a message is downloaded but QUIT is not issued then the message does not get marked as Seen.
POP3 listing displays the newest 250 messages in INBOX (configurable)
Wild Duck uses message _id
value (24 byte hex) as the unique ID. If a message is moved from one mailbox to another then it might re-appear in the listing.
If a messages is downloaded by a client this message gets marked as Seen
If a messages is deleted by a client this message gets marked as Seen and moved to Trash folder
Yes, it does. You can run the server and get working IMAP and POP3 servers for mail store, SMTP server for pushing messages to the mail store and HTTP API server to create new users. All handled by Node.js, MongoDB and Redis, no additional dependencies needed. The IMAP server hosting уайлддак.орг uses a MongoDB replica set of 3 hosts.
+
-labels: андрис+ööö@уайлддак.орг is delivered to андрис@уайлддак.оргYes, historically it has been considered a bad practice to store emails in a database. And for a good reason. The data model of relational databases like MySQL does not work well with tree like structures (email mime tree) or large blobs (email source).
Notice the word "relational"? In fact document stores like MongoDB work very well with emails. Document store is great for storing tree-like structures and while GridFS is not as good as "real" object storage, it is good enough for storing the raw parts of the message. Additionally there's nothing too GridFS specific, so (at least in theory) it could be replaced with any object store.
You can see an example mail entry here. Lines 184-217 demonstrate a node that has its body missing as it was big enough to be moved to GridStore and not be included with the main entry.
Somewhat yes. Even though on some parts Wild Duck is already fast (Wild Duck is successfully tested with mailboxes up to 200K messages), there are still some important improvements that need to be done:
Whenever a message is received Wild Duck parses it into a tree-like structure based on the MIME tree and stores this tree to MongoDB. Attachments are removed from the tree and stored separately in GridStore. If a message needs to be loaded then Wild Duck fetches the tree structure first and, if needed, loads attachments from GridStore and then compiles it back into the original RFC822 message. The result should be identical to the original messages unless the original message used unix newlines, these might be partially replaced with windows newlines.
Wild Duck tries to keep minimal state for sessions (basically just a list of currently known UIDs and latest MODSEQ value) to be able to distribute sessions between different hosts. Whenever a mailbox is opened the entire message list is loaded as an array of UID values. The first UID in the array element points to the message #1 in IMAP, second one points to message #2 etc.
Actual update data (information about new and deleted messages, flag updates and such) is stored to a journal log and an update beacon is propagated through Redis pub/sub whenever something happens. If a session detects that there have been some changes in the current mailbox and it is possible to notify the user about it (eg. a NOOP call was made), journaled log is loaded from the database and applied to the UID array one action at a time. Once all journaled updates have applied then the result should match the latest state. If it is not possible to notify the user (eg a FETCH call was made), then journal log is not loaded and the user continues to see the old state.
Assuming you have MongoDB and Redis running somewhere.
$ git clone git://github.com/wildduck-email/wildduck.git
$ cd wildduck
Install dependencies from npm
$ npm install --production
You can either modify the default config file or alternatively generate an environment related config file that gets merged with the default values. Read about the config module here
To use the default config file, run the following
npm start
Or if you want to use environment related config file, eg from production.js
, run the following
NODE_ENV=production npm start
See see below for details about creating new user accounts
Users, mailboxes and messages can be managed with HTTP requests against Wild Duck API
TODO:
Creates a new user.
Arguments
Example
curl -XPOST "http://localhost:8080/user/create" -H 'content-type: application/json' -d '{
"username": "testuser",
"password": "secretpass"
}'
The response for successful operation should look like this:
{
"success": true,
"username": "testuser"
}
After you have created an user you can use these credentials to log in to the IMAP server. To be able to receive mail for that user you need to register an email address.
Creates a new email address alias for an existing user. You can use internationalized email addresses like андрис@уайлддак.орг.
Arguments
First added address becomes main by default
Example
curl -XPOST "http://localhost:8080/user/address/create" -H 'content-type: application/json' -d '{
"username": "testuser",
"address": "user@example.com"
}'
The response for successful operation should look like this:
{
"success": true,
"username": "testuser",
"address": "user@example.com"
}
After you have registered a new address then SMTP maildrop server starts accepting mail for it and store the messages to the users mailbox.
Updates maximum allowed quota for an user
Arguments
Example
curl -XPOST "http://localhost:8080/user/quota" -H 'content-type: application/json' -d '{
"username": "testuser",
"quota": 1234567
}'
The response for successful operation should look like this:
{
"success": true,
"username": "testuser",
"previousQuota": 0,
"quota": 1234567
}
Quota changes apply immediately.
Recalculates used storage for an user. Use this when it seems that quota counters for an user do not match with reality.
Arguments
Example
curl -XPOST "http://localhost:8080/user/quota/reset" -H 'content-type: application/json' -d '{
"username": "testuser"
}'
The response for successful operation should look like this:
{
"success": true,
"username": "testuser",
"previousStorageUsed": 1000,
"storageUsed": 800
}
Be aware though that this method is not atomic and should be done only if quota counters are way off.
Updates password for an user
Arguments
Example
curl -XPOST "http://localhost:8080/user/password" -H 'content-type: application/json' -d '{
"username": "testuser",
"password": "newpass"
}'
The response for successful operation should look like this:
{
"success": true,
"username": "testuser"
}
Password change applies immediately.
Returns user information including quota usage and registered addresses
Arguments
Example
curl "http://localhost:8080/user?username=testuser"
The response for successful operation should look like this:
{
"success": true,
"username": "testuser",
"quota": 1234567,
"storageUsed": 1822,
"addresses": [
{
"id": "58d8fccb645b0deb23d6c37d",
"address": "user@example.com",
"main": true,
"created": "2017-03-27T11:51:39.639Z"
}
]
}
Returns all mailbox names for the user
Arguments
Example
curl "http://localhost:8080/user/mailboxes?username=testuser"
The response for successful operation should look like this:
{
"success": true,
"username": "testuser",
"mailboxes": [
{
"id": "58d8f2ae240366dfd5d8049c",
"path": "INBOX",
"special": "Inbox",
"messages": 100
},
{
"id": "58d8f2ae240366dfd5d8049d",
"path": "Sent Mail",
"special": "Sent",
"messages": 45
},
{
"id": "58d8f2ae240366dfd5d8049f",
"path": "Junk",
"special": "Junk",
"messages": 10
},
{
"id": "58d8f2ae240366dfd5d8049e",
"path": "Trash",
"special": "Trash",
"messages": 11
}
]
}
List messages in a mailbox.
Parameters
Response includes the following fields
mailbox is an object that lists some metadata about the current mailbox
next is an URL fragment for retrieving the next page (or false if there are no more pages)
prev is an URL fragment for retrieving the previous page (or false if it is the first page)
messages is an array of messages in the mailbox
The response for successful listing should look like this:
{
"success": true,
"mailbox": {
"id": "58dbf87fcff690a8c30470c7",
"path": "INBOX"
},
"next": "/mailbox/58dbf87fcff690a8c30470c7?before=34&size=20",
"prev": false,
"messages": [
{
"id": "58e25243ab71621c3890417e",
"date": "2017-04-03T13:46:44.226Z",
"hasAttachments": true,
"intro": "Welcome to Ryan Finnie's MIME torture test. This message was designed to introduce a couple of the newer features of MIME-aware MUAs, features that have come around since the days of the original MIME torture test. Just to be clear, this message SUPPLEMENT…",
"subject": "ryan finnie's mime torture test v1.0",
"from": "ryan finnie <rfinnie@domain.dom>",
"to": "bob@domain.dom"
}
]
}
Retrieves message information
Parameters
Example
curl "http://localhost:8080/message/58d8299c5195c38e77c2daa5"
Response message includes the following fields
id is the id of the message
headers is an array that lists all headers of the message. A header is an object:
date is the receive date (not header Date: field)
mailbox is the id of the mailbox this messages belongs to
flags is an array of IMAP flags for this message
text is the plaintext version of the message (derived from html if not present in message source)
html is the HTML version of the message (derived from plaintext if not present in message source). It is an array of strings, each array element corresponds to different MIME node and might have its own html header
attachments is an array of attachment objects. Attachments can be shared between messages.
HTML content has embedded images linked with the following URL structure:
attachment:MESSAGE_ID/ATTACHMENT_ID
For example:
<img src="attachment:aaaaaa/bbbbbb">
To fetch the actual attachment contents for this image, use the following url:
http://localhost:8080/message/aaaaaa/attachment/bbbbbb
The response for successful operation should look like this:
{
"success": true,
"message": {
"id": "58d8299c5195c38e77c2daa5",
"mailbox": "58dbf87fcff690a8c30470c7",
"headers": [
{
"key": "delivered-to",
"value": "andris@addrgw.com"
}
],
"date": "2017-04-03T10:34:43.007Z",
"flags": ["\\Seen"],
"text": "Hello world!",
"html": ["<p>Hello world!</p>"],
"attachments": [
{
"id": "58e2254289cccb742fd6c015",
"fileName": "image.png",
"contentType": "image/png",
"disposition": "attachment",
"transferEncoding": "base64",
"related": true,
"sizeKb": 1
}
]
}
}
Retrieves an attachment of the message
Parameters
Example
curl "http://localhost:8080/message/58d8299c5195c38e77c2daa5/attachment/58e2254289cccb742fd6c015"
Retrieves RFC822 source of the message
Parameters
Example
curl "http://localhost:8080/message/58d8299c5195c38e77c2daa5/raw"
Deletes a message from a mailbox.
Parameters
Example
curl -XDELETE "http://localhost:8080/message/58d8299c5195c38e77c2daa5"
The response for successful operation should look like this:
{
"success": true,
"message":{
"id": "58d8299c5195c38e77c2daa5"
}
}
This is a list of known differences from the IMAP specification. Listed differences are either intentional or are bugs that became features.
\Recent
flags is not implemented and most probably never will be (RFC3501 2.3.2.)RENAME
does not touch subfolders which is against the spec (RFC3501 6.3.5. If the name has inferior hierarchical names, then the inferior hierarchical names MUST also be renamed.). Wild Duck stores all folders using flat hierarchy, the "/" separator is fake and only used for listing mailboxesFLAGS
responses (RFC3501 7.2.6.) and PERMANENTFLAGS
are not sent (except for as part of SELECT
and EXAMINE
responses). Wild Duck notifies about flag updates only with unsolicited FETCH updates.NO
for STORE
if matching messages were deleted in another sessionCHARSET
argument for the SEARCH
command is ignored (RFC3501 6.4.4.)SEARCH MODSEQ
are ignored (RFC7162 3.1.5.). You can define <entry-name>
and <entry-type-req>
values but these are not used for anythingSEARCH TEXT
and SEARCH BODY
both use MongoDB $text index against decoded plaintext version of the message. RFC3501 assumes that it should be a string match either against full message (TEXT
) or body section (BODY
).Any other differences are most probably real bugs and unintentional.
Wild Duck does not plan to be the most feature-rich IMAP client in the world. Most IMAP extensions are useless because there aren't too many clients that are able to benefit from these extensions. There are a few extensions though that would make sense to be added to Wild Duck
Create an email account and use your IMAP client to connect to it. To send mail to this account, run the example script:
node examples/push-mail.js username@example.com
This should "deliver" a new message to the INBOX of username@example.com by using the built-in SMTP maildrop interface. If your email client is connected then you should promptly see the new message.
Use ZoneMTA with the ZoneMTA-WildDuck plugin. This gives you an outbound SMTP server that uses Wild Duck accounts for authentication.
Wild Duck Mail Agent is licensed under the European Union Public License 1.1.
FAQs
IMAP/POP3 server built with Node.js and MongoDB
The npm package wildduck receives a total of 306 weekly downloads. As such, wildduck popularity was classified as not popular.
We found that wildduck demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
PyPI now allows maintainers to archive projects, improving security and helping users make informed decisions about their dependencies.
Research
Security News
Malicious npm package postcss-optimizer delivers BeaverTail malware, targeting developer systems; similarities to past campaigns suggest a North Korean connection.
Security News
CISA's KEV data is now on GitHub, offering easier access, API integration, commit history tracking, and automated updates for security teams and researchers.