Skip to content

Commit

Permalink
fix: fixed IMAP response (per <nodemailer/wildduck#726>), fixed backu…
Browse files Browse the repository at this point in the history
…p check in worker, wip quota merge
  • Loading branch information
titanism committed Aug 30, 2024
1 parent d8c5b9f commit 665eb9f
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 22 deletions.
12 changes: 12 additions & 0 deletions app/models/aliases.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
const Boom = require('@hapi/boom');
const RE2 = require('re2');
const _ = require('lodash');
const bytes = require('bytes');
const captainHook = require('captain-hook');
const isFQDN = require('is-fqdn');
const isSANB = require('is-string-and-not-blank');
Expand Down Expand Up @@ -92,6 +93,17 @@ APS.plugin(mongooseCommonPlugin, {
});

const Aliases = new mongoose.Schema({
// alias specific max quota (set by admins only)
max_quota: {
type: Number,
default: config.maxQuotaPerAlias,
min: 0,
//
// NOTE: hard-coded max of 100 GB (safeguard)
//
max: bytes('100GB')
},

// apple push notification support
// <https://github.com/nodemailer/wildduck/issues/711>
aps: [APS],
Expand Down
22 changes: 15 additions & 7 deletions app/models/domains.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,16 @@ Invite.plugin(mongooseCommonPlugin, {
});

const Domains = new mongoose.Schema({
// domain specific max quota per alias
max_quota_per_alias: {
type: Number,
default: config.maxQuotaPerAlias,
min: 0,
//
// NOTE: hard-coded max of 100 GB (safeguard)
//
max: bytes('100GB')
},
// URL to POST to when a bounce is detected from outbound SMTP servers
// (NOTE: see additional validation below where we prevent localhost and private IP's from being webhooks)
bounce_webhook: {
Expand Down Expand Up @@ -2216,12 +2226,6 @@ async function getMaxQuota(_id, locale = i18n.config.defaultLocale) {
.lean()
.exec();

if (!domain) {
throw Boom.badRequest(
i18n.translateError('DOMAIN_DOES_NOT_EXIST_ANYWHERE', locale)
);
}

if (!domain)
throw Boom.badRequest(
i18n.translateError('DOMAIN_DOES_NOT_EXIST_ANYWHERE', locale)
Expand Down Expand Up @@ -2276,6 +2280,10 @@ async function getMaxQuota(_id, locale = i18n.config.defaultLocale) {
);
}

// TODO: add in form and API endpoint and API docs
// TODO: if the alias had a limit set on `alias.max_quota` by an admin
// TODO: if the domain had an alias-wide limit set on `domain.max_quota_per_alias`

// go through all admins and get the max value
const max = _.max(
adminMembers.map((member) =>
Expand All @@ -2288,7 +2296,7 @@ async function getMaxQuota(_id, locale = i18n.config.defaultLocale) {
//
// NOTE: hard-coded max of 100 GB (safeguard)
//
return _.clamp(max, config.maxQuotaPerAlias, bytes('100GB'));
return _.clamp(max, 0, bytes('100GB'));
}

Domains.statics.getMaxQuota = getMaxQuota;
Expand Down
8 changes: 7 additions & 1 deletion app/models/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

const Boom = require('@hapi/boom');
const _ = require('lodash');
const bytes = require('bytes');
const captainHook = require('captain-hook');
const countryList = require('country-list');
const cryptoRandomString = require('crypto-random-string');
Expand Down Expand Up @@ -152,7 +153,12 @@ const object = {};

object[config.userFields.maxQuotaPerAlias] = {
type: Number,
default: config.maxQuotaPerAlias
default: config.maxQuotaPerAlias,
min: 0,
//
// NOTE: hard-coded max of 100 GB (safeguard)
//
max: bytes('100GB')
};

object[config.userFields.smtpLimit] = {
Expand Down
2 changes: 1 addition & 1 deletion app/views/about/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,6 @@ Throughout March to July 2024, we released major optimizations and improvements

In July 2024, [we added iOS Push support](https://github.com/nodemailer/wildduck/issues/711#issuecomment-2254114016) since Apple Mail on iOS does not support IMAP `IDLE` command. Now users can get real-time notifications of new mail on their Apple iOS devices. We also added time to inbox ("TTI") monitoring for our own service – as well as Yahoo/AOL (now that they support app-generated passwords again) to the footer of every page on our website. Additionally, we now allow users to encrypt their plaintext DNS TXT records even on the free plan at no cost. Privacy should not be a feature, it should be inherently built-in to all aspects of a product. As highly requested in a [Privacy Guides discussion](https://discuss.privacyguides.net/t/forward-email-email-provider/13370) and on [our GitHub issues](https://github.com/forwardemail/forwardemail.net/issues/254) we've added this. Lastly, we added the ability for aliases to either quietly reject `250`, soft reject `421`, or hard reject `550` if they are disabled. Previously, disabled aliases only routed to a blackhole (e.g. `/dev/null`) and it appeared to senders as if their messages to these disabled aliases succeeded.

In August 2024, we added support for exporting mailboxes as [EML](https://en.wikipedia.org/wiki/Email#Filename_extensions) and [Mbox](https://en.wikipedia.org/wiki/Mbox) formats (in addition to the [SQLite](https://en.wikipedia.org/wiki/SQLite) export format already supported) – and we also added [webhook signature support](https://forwardemail.net/faq#do-you-support-webhooks:\~:text=If%20you%27re%20on%20a%20paid%20plan%2C%20then%20go%20to%20My%20Account%20%E2%86%92%20Domains%20%E2%86%92%20Settings%20%E2%86%92%20Webhook%20Signature%20Payload%20Verification%20Key%20to%20obtain%20your%20webhook%20key.) via `X-Webhook-Signature` header. We also [added support for bounce webhooks](/faq#do-you-support-bounce-webhooks) and now allow users to send newsletters, announcements, and email marketing through our outbound SMTP service.
In August 2024, we added support for exporting mailboxes as [EML](https://en.wikipedia.org/wiki/Email#Filename_extensions) and [Mbox](https://en.wikipedia.org/wiki/Mbox) formats (in addition to the [SQLite](https://en.wikipedia.org/wiki/SQLite) export format already supported). [Webhook signature support](https://forwardemail.net/faq#do-you-support-webhooks:\~:text=If%20you%27re%20on%20a%20paid%20plan%2C%20then%20go%20to%20My%20Account%20%E2%86%92%20Domains%20%E2%86%92%20Settings%20%E2%86%92%20Webhook%20Signature%20Payload%20Verification%20Key%20to%20obtain%20your%20webhook%20key.) via `X-Webhook-Signature` header was added. [Bounce webhook support was added](/faq#do-you-support-bounce-webhooks) and we now allow users to send newsletters, announcements, and email marketing through our outbound SMTP service. We also added the ability to set domain-wide and alias-specific storage quotas for IMAP/POP3/CalDAV.

[arc]: https://en.wikipedia.org/wiki/Authenticated_Received_Chain
16 changes: 4 additions & 12 deletions helpers/on-auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -675,26 +675,18 @@ async function onAuth(auth, session, fn) {
.catch((err) => this.logger.warn(err, { session }));
}
} catch (err) {
//
// NOTE: if err.response === 'NO' then WildDuck POP3 will return error message too
// NOTE: if err.response is a message then WildDuck IMAP will return the error message
//
// NOTE: we should actually share error message if it was not a code bug
// (otherwise it won't be intuitive to users if they're late on payment)
// (and we now do this via "ALERT" `imapResponse` code set in refineAndLogError
//
// <https://github.com/nodemailer/smtp-server/blob/a570d0164e4b4ef463eeedd80cadb37d5280e9da/lib/sasl.js#L189-L222>
const error = refineAndLogError(
err,
session,
this.server instanceof IMAPServer,
this
// <https://github.com/nodemailer/wildduck/issues/726>
fn(
refineAndLogError(err, session, this.server instanceof IMAPServer, this)
);
if (this.server instanceof IMAPServer) {
error.response = error.message;
fn(error);
} else {
fn(error);
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion helpers/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -787,7 +787,7 @@ async function backup(payload) {
// NOTE: if the SQLite file is 2x larger than the backup, then we
// should run a VACUUM since auto vacuum isn't optimal
//
if (payload.format === 'sqlite' && tmp) {
if (payload.format === 'sqlite' && tmp && backup) {
try {
// check how much space is remaining on storage location
const storagePath = getPathToDatabase({
Expand Down

0 comments on commit 665eb9f

Please sign in to comment.