Full Code of mail-in-a-box/mailinabox for AI

main de4ec82a5afa cached
110 files
832.9 KB
223.4k tokens
318 symbols
1 requests
Download .txt
Showing preview only (870K chars total). Download the full file or copy to clipboard to get everything.
Repository: mail-in-a-box/mailinabox
Branch: main
Commit: de4ec82a5afa
Files: 110
Total size: 832.9 KB

Directory structure:
gitextract_y6gyh9lx/

├── .editorconfig
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── Vagrantfile
├── api/
│   ├── docs/
│   │   ├── generate-docs.sh
│   │   └── template.hbs
│   └── mailinabox.yml
├── conf/
│   ├── dovecot-mailboxes.conf
│   ├── fail2ban/
│   │   ├── filter.d/
│   │   │   ├── dovecotimap.conf
│   │   │   ├── miab-management-daemon.conf
│   │   │   ├── miab-munin.conf
│   │   │   ├── miab-owncloud.conf
│   │   │   ├── miab-postfix-submission.conf
│   │   │   └── miab-roundcube.conf
│   │   └── jails.conf
│   ├── ios-profile.xml
│   ├── mailinabox.service
│   ├── mozilla-autoconfig.xml
│   ├── mta-sts.txt
│   ├── munin.service
│   ├── nginx-alldomains.conf
│   ├── nginx-primaryonly.conf
│   ├── nginx-ssl.conf
│   ├── nginx-top.conf
│   ├── nginx.conf
│   ├── postfix_outgoing_mail_header_filters
│   ├── sieve-spam.txt
│   ├── www_default.html
│   └── zpush/
│       ├── autodiscover_config.php
│       ├── backend_caldav.php
│       ├── backend_carddav.php
│       ├── backend_combined.php
│       └── backend_imap.php
├── management/
│   ├── auth.py
│   ├── backup.py
│   ├── cli.py
│   ├── csr_country_codes.tsv
│   ├── daemon.py
│   ├── daily_tasks.sh
│   ├── dns_update.py
│   ├── email_administrator.py
│   ├── mail_log.py
│   ├── mailconfig.py
│   ├── mfa.py
│   ├── munin_start.sh
│   ├── ssl_certificates.py
│   ├── status_checks.py
│   ├── templates/
│   │   ├── aliases.html
│   │   ├── custom-dns.html
│   │   ├── external-dns.html
│   │   ├── index.html
│   │   ├── login.html
│   │   ├── mail-guide.html
│   │   ├── mfa.html
│   │   ├── munin.html
│   │   ├── ssl.html
│   │   ├── sync-guide.html
│   │   ├── system-backup.html
│   │   ├── system-status.html
│   │   ├── users.html
│   │   ├── web.html
│   │   └── welcome.html
│   ├── utils.py
│   ├── web_update.py
│   └── wsgi.py
├── pyproject.toml
├── security.md
├── setup/
│   ├── bootstrap.sh
│   ├── dkim.sh
│   ├── dns.sh
│   ├── firstuser.sh
│   ├── functions.sh
│   ├── mail-dovecot.sh
│   ├── mail-postfix.sh
│   ├── mail-users.sh
│   ├── management.sh
│   ├── migrate.py
│   ├── munin.sh
│   ├── network-checks.sh
│   ├── nextcloud.sh
│   ├── preflight.sh
│   ├── questions.sh
│   ├── spamassassin.sh
│   ├── ssl.sh
│   ├── start.sh
│   ├── system.sh
│   ├── web.sh
│   ├── webmail.sh
│   └── zpush.sh
├── tests/
│   ├── fail2ban.py
│   ├── pip-requirements.txt
│   ├── test_dns.py
│   ├── test_mail.py
│   ├── test_smtp_server.py
│   ├── tls.py
│   └── tls_results.txt
└── tools/
    ├── archive_conf_files.sh
    ├── dns_update
    ├── editconf.py
    ├── mail.py
    ├── owncloud-restore.sh
    ├── owncloud-unlockadmin.sh
    ├── parse-nginx-log-bootstrap-accesses.py
    ├── readable_bash.py
    ├── ssl_cleanup
    └── web_update

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org

root = true

[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[Makefile]
indent_style = tab
indent_size = 4

[Vagrantfile]
indent_size = 2

[*.rb]
indent_size = 2

[*.py]
indent_style = tab

[*.js]
indent_size = 2



================================================
FILE: .gitignore
================================================
*~
tests/__pycache__/
management/__pycache__/
tools/__pycache__/
externals/
.env
.vagrant
api/docs/api-docs.html
*.code-workspace


================================================
FILE: CHANGELOG.md
================================================
CHANGELOG
=========

Version 74 (January 4, 2026)
----------------------------

* Updated Roundcube to 1.6.12, fixing a security vulnerability.
* Updated zpush to version 2.7.6.
* Fixed fail2ban filter for Nextcloud.
* Fixed Thunderbird auto configuration.
* Updated links in the control panel.

Version 73 (July 11, 2025)
--------------------------

Mail:

* Quotas for mail storage can now be set per user in the control panel.
* Autoconfig now includes POP3 and CardDAV/CalDAV.

Backups:

* Fix for S3-compatible backups (other than AWS S3 itself).

Control Panel:

* Backup status is added to the status checks.
* S3 backup credentials can now be stored in environment variables.
* Fix for when an AAAA record is set up the box's own IP address.
* Fix for when logged out of the control panel.
* Fix link to Z-Push client compatibility list.

Setup:

* The Ubuntu version check is updated.

Other:

* Code cleanup using the Ruff Python linter.
* Other minor changes.

Version 72 (June 3, 2025)
-------------------------

Upgrades

* Roundcube upgraded to version 1.6.11, fixing a security vulnerability.

Control Panel

* A warning during daily tasks related to no TLS certificates being expired is fixed.

Version 71 (January 4, 2025)
----------------------------

(Version 71a was posted on January 6, 2025 and fixes a setup regression.)

Upgrades

* Roundcube upgraded to version 1.6.9.
* Z-Push upgraded to version 2.7.5.

Automated Maintenance

* Daily automated tasks are now run at 1am in the box's timezone and full backups are now restricted to running only on Saturdays and Sundays at that time.
* Backups now exclude the owncloud-backup folder so that we're not backing up backups.
* Old TLS certificates are now automatically deleted to improve control panel performance.

Setup

* Fixed broken setup if SSH was configured to listen on multiple ports.
* Ubuntu MOTD advertisements are now disabled.
* Fixed missing Roundcube dependency package if NextCloud isn't installed.

Control Panel

* Improved status checks for secondary nameservers.
* Spamhaus is now queried for the box's IPv6 address also.
* DSA and EC private keys are now accepted for TLS certificates.
* Timeouts for loading slow control panel pages are reduced.

And other minor fixes.

Version 70 (August 15, 2024)
----------------------------

* Roundcube is updated to version 1.6.8 fixing security vulnerabilities.

Version 69 (July 20, 2024)
--------------------------

Package updates:

* Nextcloud is updated to 26.0.13.
* Z-Push is updated to 2.7.3.

Other updates:

* Fixed an error generating the weekly statistics.
* Fixed file permissions when setting up Nextcloud.
* Added an undocumented option to proxy websockets.
* Internal improvements to the code to make it more reliable and readable.

Version 69a (July 21, 2024) and 69b (July 23, 2024) correct setup failures.

Version 68 (April 1, 2024)
--------------------------

Package updates:

* Roundcube updated to version 1.6.6.
* Nextcloud is updated to version 26.0.12.

Mail:

* Updated postfix's configuration to guard against SMTP smuggling to the long-term fix (https://www.postfix.org/smtp-smuggling.html).

Control Panel:

* Improved reporting of Spamhaus response codes.
* Improved detection of SSH port.
* Fixed an error if last saved status check results were corrupted.
* Other minor fixes.

Other:

* fail2ban is updated to see "HTTP/2.0" requests to munin also.
* Internal improvements to the code to make it more reliable and readable.

Version 67 (December 22, 2023)
------------------------------

* Guard against a newly published vulnerability called SMTP Smuggling. See https://sec-consult.com/blog/detail/smtp-smuggling-spoofing-e-mails-worldwide/.

Version 66 (December 17, 2023)
------------------------------

* Some users reported an error installing Mail-in-a-Box related to the virtualenv command. This is hopefully fixed.
* Roundcube is updated to 1.6.5 fixing a security vulnerability.
* For Mail-in-a-Box developers, a new setup variable is added to pull the source code from a different repository.

Version 65 (October 27, 2023)
-----------------------------

* Roundcube updated to 1.6.4 fixing a security vulnerability.
* zpush.sh updated to version 2.7.1.
* Fixed a typo in the control panel.

Version 64 (September 2, 2023)
------------------------------

* Fixed broken installation when upgrading from Mail-in-a-Box version 56 (Nextcloud 22) and earlier because of an upstream packaging issue.
* Fixed backups to work with the latest duplicity package which was not backwards compatible.
* Fixed setting B2 as a backup target with a slash in the application key.
* Turned off OpenDMARC diagnostic reports sent in response to incoming mail.
* Fixed some crashes when using an unreleased version of Mail-in-a-Box.
* Added z-push administration scripts.

Version 63 (July 27, 2023)
--------------------------

* Nextcloud updated to 25.0.7.

Version 62 (May 20, 2023)
-------------------------

Package updates:

* Nextcloud updated to 23.0.12 (and its apps also updated).
* Roundcube updated to 1.6.1.
* Z-Push to 2.7.0, which has compatibility for Ubuntu 22.04, so it works again.

Mail:

* Roundcube's password change page is now working again.

Control panel:

* Allow setting the backup location's S3 region name for non-AWS S3-compatible backup hosts.
* Control panel pages can be opened in a new tab/window and bookmarked and browser history navigation now works.
* Add a Copy button to put the rsync backup public key on clipboard.
* Allow secondary DNS xfr: items added in the control panel to be hostnames too.
* Fixed issue where sshkeygen fails when IPv6 is disabled.
* Fixed issue opening munin reports.
* Fixed report formatting in status emails sent to the administrator.

Version 61.1 (January 28, 2023)
-------------------------------

* Fixed rsync backups not working with the default port.
* Reverted "Improve error messages in the management tools when external command-line tools are run." because of the possibility of user secrets being included in error messages.
* Fix for TLS certificate SHA fingerprint not being displayed during setup.

Version 61 (January 21, 2023)
-----------------------------

System:

* fail2ban didn't start after setup.

Mail:

* Disable Roundcube password plugin since it was corrupting the user database.

Control panel:

* Fix changing existing backup settings when the rsync type is used.
* Allow setting a custom port for rsync backups.
* Fixes to DNS lookups during status checks when there are timeouts, enforce timeouts better.
* A new check is added to ensure fail2ban is running.
* Fixed a color.
* Improve error messages in the management tools when external command-line tools are run.

Version 60.1 (October 30, 2022)
-------------------------------

* A setup issue where the DNS server nsd isn't running at the end of setup is (hopefully) fixed.
* Nextcloud is updated to 23.0.10 (contacts to 4.2.2, calendar to 3.5.1).

Version 60 (October 11, 2022)
-----------------------------

This is the first release for Ubuntu 22.04.

**Before upgrading**, you must **first upgrade your existing Ubuntu 18.04 box to Mail-in-a-Box v0.51 or later**, if you haven't already done so. That may not be possible after Ubuntu 18.04 reaches its end of life in April 2023, so please complete the upgrade well before then. (If you are not using Nextcloud's contacts or calendar, you can migrate to the latest version of Mail-in-a-Box from any previous version.)

For complete upgrade instructions, see:

https://discourse.mailinabox.email/t/version-60-for-ubuntu-22-04-is-about-to-be-released/9558

No major features of Mail-in-a-Box have changed in this release, although some minor fixes were made.

With the newer version of Ubuntu the following software packages we use are updated:

* dovecot is upgraded to 2.3.16, postfix to 3.6.4, opendmark to 1.4 (which adds ARC-Authentication-Results headers), and spampd to 2.53 (alleviating a mail delivery rate limiting bug).
* Nextcloud is upgraded to 23.0.4 (contacts to 4.2.0, calendar to 3.5.0).
* Roundcube is upgraded to 1.6.0.
* certbot is upgraded to 1.21 (via the Ubuntu repository instead of a PPA).
* fail2ban is upgraded to 0.11.2.
* nginx is upgraded to 1.18.
* PHP is upgraded from 7.2 to 8.0.

Also:

* Roundcube's login session cookie was tightened. Existing sessions may require a manual logout.
* Moved Postgrey's database under $STORAGE_ROOT.

Version 57a (June 19, 2022)
---------------------------

* The Backblaze backups fix posted in Version 57 was incomplete. It's now fixed.

Version 57 (June 12, 2022)
--------------------------

Setup:

* Fixed issue upgrading from Mail-in-a-Box v0.40-v0.50 because of a changed URL that Nextcloud is downloaded from.

Backups:

* Fixed S3 backups which broke with duplicity 0.8.23.
* Fixed Backblaze backups which broke with latest b2sdk package by rolling back its version.

Control panel:

* Fixed spurious changes in system status checks messages by sorting DNSSEC DS records.
* Fixed fail2ban lockout over IPv6 from excessive loads of the system status checks.
* Fixed an incorrect IPv6 system status check message.

Version 56 (January 19, 2022)
-----------------------------

Software updates:

* Roundcube updated to 1.5.2 (from 1.5.0), and the persistent_login and CardDAV (to 4.3.0 from 3.0.3) plugins are updated.
* Nextcloud updated to 20.0.14 (from 20.0.8), contacts to 4.0.7 (from 3.5.1), and calendar to 3.0.4 (from 2.2.0).

Setup:

* Fixed failed setup if a previous attempt failed while updating Nextcloud.

Control panel:

* Fixed a crash if a custom DNS entry is not under a zone managed by the box.
* Fix DNSSEC instructions typo.

Other:

* Set systemd journald log retention to 10 days (from no limit) to reduce disk usage.
* Fixed log processing for submission lines that have a sasl_sender or other extra information.
* Fix DNS secondary nameserver refresh failure retry period.

Version 55 (October 18, 2021)
-----------------------------

Mail:

* "SMTPUTF8" is now disabled in Postfix. Because Dovecot still does not support SMTPUTF8, incoming mail to internationalized addresses was bouncing. This fixes incoming mail to internationalized domains (which was probably working prior to v0.40), but it will prevent sending outbound mail to addresses with internationalized local-parts.
* Upgraded to Roundcube 1.5.

Control panel:

* The control panel menus are now hidden before login, but now non-admins can log in to access the mail and contacts/calendar instruction pages.
* The login form now disables browser autocomplete in the two-factor authentication code field.
* After logging in, the default page is now a fast-loading welcome page rather than the slow-loading system status checks page.
* The backup retention period option now displays for B2 backup targets.
* The DNSSEC DS record recommendations are cleaned up and now recommend changing records that use SHA1.
* The Munin monitoring pages no longer require a separate HTTP basic authentication login and can be used if two-factor authentication is turned on.
* Control panel logins are now tied to a session backend that allows true logouts (rather than an encrypted cookie).
* Failed logins no longer directly reveal whether the email address corresponds to a user account.
* Browser dark mode now inverts the color scheme.

Other:

* Fail2ban's IPv6 support is enabled.
* The mail log tool now doesn't crash if there are email addresses in log messages with invalid UTF-8 characters.
* Additional nsd.conf files can be placed in /etc/nsd.conf.d.

v0.54 (June 20, 2021)
---------------------

Mail:

* Forwarded mail using mail filter rules (in Roundcube; "sieve" rules) stopped re-writing the envelope address at some point, causing forwarded mail to often be marked as spam by the final recipient. These forwards will now re-write the envelope as the Mail-in-a-Box user receiving the mail to comply with SPF/DMARC rules.
* Sending mail is now possible on port 465 with the "SSL" or "TLS" option in mail clients, and this is now the recommended setting. Port 587 with STARTTLS remains available but should be avoided when configuring new mail clients.
* Roundcube's login cookie is updated to use a new encryption algorithm (AES-256-CBC instead of DES-EDE-CBC).

DNS:

* The ECDSAP256SHA256 DNSSEC algorithm is now available. If a DS record is set for any of your domain names that have DNS hosted on your box, you will be prompted by status checks to update the DS record at your convenience.
* Null MX records are added for domains that do not serve mail.

Contacts/calendar:

* Updated Nextcloud to 20.0.8, contacts to 3.5.1, calendar to 2.2.0 (#1960).

Control panel:

* Fixed a crash in the status checks.
* Small wording improvements.

Setup:

* Minor improvements to the setup scripts.

v0.53a (May 8, 2021)
--------------------

The download URL for Z-Push has been revised because the old URL stopped working.

v0.53 (April 12, 2021)
----------------------

Software updates:

* Upgraded Roundcube to version 1.4.11 addressing a security issue, and its desktop notifications plugin.
* Upgraded Z-Push (for Exchange/ActiveSync) to version 2.6.2.

Control panel:

* Backblaze B2 is now a supported backup protocol.
* Fixed an issue in the daily mail reports.
* Sort the Custom DNS by zone and qname, and add an option to go back to the old sort order (creation order).

Mail:

* Enable sending DMARC failure reports to senders that request them.

Setup:

* Fixed error when upgrading from Nextcloud 13.

v0.52 (January 31, 2021)
------------------------

Software updates:

* Upgraded Roundcube to version 1.4.10.
* Upgraded Z-Push to 2.6.1.

Mail:

* Incoming emails with SPF/DKIM/DMARC failures now get a higher spam score, and these messages are more likely to appear in the junk folder, since they are often spam/phishing.
* Fixed the MTA-STS policy file's line endings.

Control panel:

* A new Download button in the control panel's External DNS page can be used to download the required DNS records in zonefile format.
* Fixed the problem when the control panel would report DNS entries as Not Set by increasing a bind query limit.
* Fixed a control panel startup bug on some systems.
* Improved an error message on a DNS lookup timeout.
* A typo was fixed.

DNS:

* The TTL for NS records has been increased to 1 day to comply with some registrar requirements.

System:

* Nextcloud's photos, dashboard, and activity apps are disabled since we only support contacts and calendar.

v0.51 (November 14, 2020)
-------------------------

Software updates:

* Upgraded Nextcloud from 17.0.6 to 20.0.1 (with Contacts from 3.3.0 to 3.4.1 and Calendar from 2.0.3 to 2.1.2)
* Upgraded Roundcube to version 1.4.9.

Mail:

* The MTA-STA max_age value was increased to the normal one week.

Control panel:

* Two-factor authentication can now be enabled for logins to the control panel. However, keep in mind that many online services (including domain name registrars, cloud server providers, and TLS certificate providers) may allow an attacker to take over your account or issue a fraudulent TLS certificate with only access to your email address, and this new two-factor authentication does not protect access to your inbox. It therefore remains very important that user accounts with administrative email addresses have strong passwords.
* TLS certificate expiry dates are now shown in ISO8601 format for clarity.

v0.50 (September 25, 2020)
--------------------------

Setup:

* When upgrading from versions before v0.40, setup will now warn that ownCloud/Nextcloud data cannot be migrated rather than failing the installation.

Mail:

* An MTA-STS policy for incoming mail is now published (in DNS and over HTTPS) when the primary hostname and email address domain both have a signed TLS certificate installed, allowing senders to know that an encrypted connection should be enforced.
* The per-IP connection limit to the IMAP server has been doubled to allow more devices to connect at once, especially with multiple users behind a NAT.

DNS:

* autoconfig and autodiscover subdomains and CalDAV/CardDAV SRV records are no longer generated for domains that don't have user accounts since they are unnecessary.
* IPv6 addresses can now be specified for secondary DNS nameservers in the control panel.

TLS:

* TLS certificates are now provisioned in groups by parent domain to limit easy domain enumeration and make provisioning more resilient to errors for particular domains.

Control panel:

* The control panel API is now fully documented at https://mailinabox.email/api-docs.html.
* User passwords can now have spaces.
* Status checks for automatic subdomains have been moved into the section for the parent domain.
* Typo fixed.

Web:

* The default web page served on fresh installations now adds the `noindex` meta tag.
* The HSTS header is revised to also be sent on non-success responses.

v0.48 (August 26, 2020)
-----------------------

Security fixes:

* Roundcube is updated to version 1.4.8 fixing additional cross-site scripting (XSS) vulnerabilities.

v0.47 (July 29, 2020)
---------------------

Security fixes:

* Roundcube is updated to version 1.4.7 fixing a cross-site scripting (XSS) vulnerability with HTML messages with malicious svg/namespace (CVE-2020-15562) (https://roundcube.net/news/2020/07/05/security-updates-1.4.7-1.3.14-and-1.2.11).
* SSH connections are now rate-limited at the firewall level (in addition to fail2ban).

v0.46 (June 11, 2020)
---------------------

Security fixes:

* Roundcube is updated to version 1.4.6 (https://roundcube.net/news/2020/06/02/security-updates-1.4.5-and-1.3.12).

v0.45 (May 16, 2020)
--------------------

Security fixes:

* Fix missing brute force login protection for Roundcube logins.

Software updates:

* Upgraded Roundcube from 1.4.2 to 1.4.4.
* Upgraded Nextcloud from 17.0.2 to 17.0.6 (with Contacts from 3.1.6 to 3.3.0 and Calendar from 1.7.1 to v2.0.3)
* Upgraded Z-Push to 2.5.2.

System:

* Nightly backups now occur on a random minute in the 3am hour (in the system time zone). The minute is chosen during Mail-in-a-Box installation/upgrade and remains the same until the next upgrade.
* Fix for mail log statistics report on leap days.
* Fix Mozilla autoconfig useGlobalPreferredServer setting.

Web:

* Add a new hidden feature to set nginx alias in www/custom.yaml.

Setup:

* Improved error handling.

v0.44 (February 15, 2020)
-------------------------

System:

* TLS settings have been upgraded following Mozilla's recommendations for servers. TLS1.2 and 1.3 are now the only supported protocols for web, IMAP, and SMTP (submission).
* Fixed an issue starting services when Mail-in-a-Box isn't on the root filesystem.
* Changed some performance options affecting Roundcube and Nextcloud.

Software updates:

* Upgraded Nextcloud from 15.0.8 to 17.0.2 (with Contacts from 3.1.1 to 3.1.6 and Calendar from 1.6.5 to 1.7.1)
* Upgraded Z-Push to 2.5.1.
* Upgraded Roundcube from 1.3.10 to 1.4.2 and changed the default skin (theme) to Elastic.

Control panel:

* The Custom DNS list of records is now sorted.
* The emails that report TLS provisioning results now has a less scary subject line.

Mail:

* Fetching of updated whitelist for greylisting was fetching each day instead of every month.
* OpenDKIM signing has been changed to 'relaxed' mode so that some old mail lists that forward mail can do so.

DNS:

* Automatic autoconfig.* subdomains can now be suppressed with custom DNS records.
* DNS zone transfer now works with IPv6 addresses.

Setup:

* An Ubuntu package source was missing on systems where it defaults off.

v0.43 (September 1, 2019)
-------------------------

Security fixes:

* A security issue was discovered in rsync backups. If you have enabled rsync backups, the file `id_rsa_miab` may have been copied to your backup destination. This file can be used to access your backup destination. If the file was copied to your backup destination, we recommend that you delete the file on your backup destination, delete `/root/.ssh/id_rsa_miab` on your Mail-in-a-Box, then re-run Mail-in-a-Box setup, and re-configure your SSH public key at your backup destination according to the instructions in the Mail-in-a-Box control panel.
* Brute force attack prevention was missing for the managesieve service.

Setup:

* Nextcloud was not upgraded properly after restoring Mail-in-a-Box from a backup from v0.40 or earlier.

Mail:

* Upgraded Roundcube to 1.3.10.
* Fetch an updated whitelist for greylisting on a monthly basis to reduce the number of delayed incoming emails.

Control panel:

* When using secondary DNS, it is now possible to specify a subnet range with the `xfr:` option.
* Fixed an issue when the secondary DNS option is used and the secondary DNS hostname resolves to multiple IP addresses.
* Fix a bug in how a backup configuration error is shown.

v0.42b (August 3, 2019)
-----------------------

Changes:

* Decreased the minimum supported RAM to 502 Mb.
* Improved mail client autoconfiguration.
* Added support for S3-compatible backup services besides Amazon S3.
* Fixed the control panel login page to let LastPass save passwords.
* Fixed an error in the user privileges API.
* Silenced some spurious messages.

Software updates:

* Upgraded Roundcube from 1.3.8 to 1.3.9.
* Upgraded Nextcloud from 14.0.6 to 15.0.8 (with Contacts from 2.1.8 to 3.1.1 and Calendar from 1.6.4 to 1.6.5).
* Upgraded Z-Push from 2.4.4 to 2.5.0.

Note that v0.42 (July 4, 2019) was pulled shortly after it was released to fix a Nextcloud upgrade issue.

v0.41 (February 26, 2019)
-------------------------

System:

* Missing brute force login attack prevention (fail2ban) filters which stopped working on Ubuntu 18.04 were added back.
* Upgrades would fail if Mail-in-a-Box moved to a different directory in `systemctl link`.

Mail:

* Incoming messages addressed to more than one local user were rejected because of a bug in spampd packaged by Ubuntu 18.04. A workaround was added.

Contacts/Calendar:

* Upgraded Nextcloud from 13.0.6 to 14.0.6.
* Upgraded Contacts from 2.1.5 to 2.1.8.
* Upgraded Calendar from 1.6.1 to 1.6.4.

v0.40 (January 12, 2019)
------------------------

This is the first release for Ubuntu 18.04. This version and versions going forward can **only** be installed on Ubuntu 18.04; however, upgrades of existing Ubuntu 14.04 boxes to the latest version supporting Ubuntu 14.04 (v0.30) continue to work as normal.

When **upgrading**, you **must first upgrade your existing Ubuntu 14.04 Mail-in-a-Box box** to the latest release supporting Ubuntu 14.04 --- that's v0.30 --- before you migrate to Ubuntu 18.04. If you are running an older version of Mail-in-a-Box which has an old version of ownCloud or Nextcloud, you will *not* be able to upgrade your data because older versions of ownCloud and Nextcloud that are required to perform the upgrade *cannot* be run on Ubuntu 18.04. To upgrade from Ubuntu 14.04 to Ubuntu 18.04, you **must create a fresh Ubuntu 18.04 machine** before installing this version. In-place upgrades of servers are not supported. Since Ubuntu's support for Ubuntu 14.04 has almost ended, everyone is encouraged to create a new Ubuntu 18.04 machine and migrate to it.

For complete upgrade instructions, see:

https://discourse.mailinabox.email/t/mail-in-a-box-version-v0-40-and-moving-to-ubuntu-18-04/4289

The changelog for this release follows.

Setup:

* Mail-in-a-Box now targets Ubuntu 18.04 LTS, which will have support from Ubuntu through 2022.
* Some of the system packages updated in virtue of using Ubuntu 18.04 include postfix (2.11=>3.3) nsd (4.0=>4.1), nginx (1.4=>1.14), PHP (7.0=>7.2), Python (3.4=>3.6), fail2ban (0.8=>0.10), Duplicity (0.6=>0.7).
* [Unofficial Bash Strict Mode](http://redsymbol.net/articles/unofficial-bash-strict-mode/) is turned on for setup, which might catch previously uncaught issues during setup.

Mail:

* IMAP server-side full text search is no longer supported because we were using a custom-built `dovecot-lucene` package that we are no longer maintaining.
* Sending email is now disabled on port 25 --- you must log in to port 587 to send email, per the long-standing mail instructions.
* Greylisting may delay more emails from new senders. We were using a custom-built postgrey package previously that whitelisted sending domains in dnswl.org, but we are no longer maintaining that package.

v0.30 (January 9, 2019)
-----------------------

Setup:

* Update to Roundcube 1.3.8 and the CardDAV plugin to 3.0.3.
* Add missing rsyslog package to install line since some OS images don't have it installed by default.
* A log file for nsd was added.

Control Panel:

* The users page now documents that passwords should only have ASCII characters to prevent character encoding mismatches between clients and the server.
* The users page no longer shows user mailbox sizes because this was extremely slow for very large mailboxes.
* The Mail-in-a-Box version is now shown in the system status checks even when the new-version check is disabled.
* The alises page now warns that alises should not be used to forward mail off of the box. Mail filters within Roundcube are better for that.
* The explanation of greylisting has been improved.

v0.29 (October 25, 2018)
------------------------

* Starting with v0.28, TLS certificate provisioning wouldn't work on new boxes until the mailinabox setup command was run a second time because of a problem with the non-interactive setup.
* Update to Nextcloud 13.0.6.
* Update to Roundcube 1.3.7.
* Update to Z-Push 2.4.4.
* Backup dates listed in the control panel now use an internationalized format.

v0.28 (July 30, 2018)
---------------------

System:

* We now use EFF's `certbot` to provision TLS certificates (from Let's Encrypt) instead of our home-grown ACME library.

Contacts/Calendar:

* Fix for Mac OS X autoconfig of the calendar.

Setup:

* Installing Z-Push broke because of what looks like a change or problem in their git server HTTPS certificate. That's fixed.

v0.27 (June 14, 2018)
---------------------

Mail:

* A report of box activity, including sent/received mail totals and logins by user, is now emailed to the box's administrator user each week.
* Update Roundcube to version 1.3.6 and Z-Push to version 2.3.9.

Control Panel:

* The undocumented feature for proxying web requests to another server now sets X-Forwarded-For.

v0.26c (February 13, 2018)
--------------------------

Setup:

* Upgrades from v0.21c (February 1, 2017) or earlier were broken because the intermediate versions of ownCloud used in setup were no longer available from ownCloud.
* Some download errors had no output --- there is more output on error now.

Control Panel:

* The background service for the control panel was not restarting on updates, leaving the old version running. This was broken in v0.26 and is now fixed.
* Installing your own TLS/SSL certificate had been broken since v0.24 because the new version of openssl became stricter about CSR generation parameters.
* Fixed password length help text.

Contacts/Calendar:

* Upgraded Nextcloud from 12.0.3 to 12.0.5.

v0.26b (January 25, 2018)
-------------------------

* Fix new installations which broke at the step of asking for the user's desired email address, which was broken by v0.26's changes related to the control panel.
* Fix the provisioning of TLS certificates by pinning a Python package we rely on (acme) to an earlier version because our code isn't yet compatible with its current version.
* Reduce munin's log_level from debug to warning to prevent massive log files.

v0.26 (January 18, 2018)
------------------------

Security:

* HTTPS, IMAP, and POP's TLS settings have been updated to Mozilla's intermediate cipher list recommendation. Some extremely old devices that use less secure TLS ciphers may no longer be able to connect to IMAP/POP.
* Updated web HSTS header to use longer six month duration.

Mail:

* Adding attachments in Roundcube broke after the last update for some users after rebooting because a temporary directory was deleted on reboot. The temporary directory is now moved from /tmp to /var so that it is persistent.
* `X-Spam-Score` header is added to incoming mail.

Control panel:

* RSASHA256 is now used for DNSSEC for .lv domains.
* Some documentation/links improvements.

Installer:

* We now run `apt-get autoremove` at the start of setup to clear out old packages, especially old kernels that take up a lot of space. On the first run, this step may take a long time.
* We now fetch Z-Push from its tagged git repository, fixing an installation problem.
* Some old PHP5 packages are removed from setup, fixing an installation bug where Apache would get installed.
* Python 3 packages for the control panel are now installed using a virtualenv to prevent installation errors due to conflicts in the cryptography/openssl packages between OS-installed packages and pip-installed packages.

v0.25 (November 15, 2017)
-------------------------

This update is a security update addressing [CVE-2017-16651, a vulnerability in Roundcube webmail that allows logged-in users to access files on the local filesystem](https://roundcube.net/news/2017/11/08/security-updates-1.3.3-1.2.7-and-1.1.10).

Mail:

* Update to Roundcube 1.3.3.

Control Panel:

* Allow custom DNS records to be set for DNS wildcard subdomains (i.e. `*`).

v0.24 (October 3, 2017)
-----------------------

System:

* Install PHP7 via a PPA. Switch to the on-demand process manager.

Mail:

* Updated to [Roundcube 1.3.1](https://roundcube.net/news/2017/06/26/roundcube-webmail-1.3.0-released), but unfortunately dropping the Vacation plugin because it has not been supported by its author and is not compatible with Roundcube 1.3, and updated the persistent login plugin.
* Updated to [Z-Push 2.3.8](http://download.z-push.org/final/2.3/z-push-2.3.8.txt).
* Dovecot now uses stronger 2048 bit DH params for better forward secrecy.

Nextcloud:

* Nextcloud updated to 12.0.3, using PHP7.

Control Panel:

* Nameserver (NS) records can now be set on custom domains.
* Fix an erroneous status check error due to IPv6 address formatting.
* Aliases for administrative addresses can now be set to send mail to +tag administrative addresses.

v0.23a (May 31, 2017)
---------------------

Corrects a problem in the new way third-party assets are downloaded during setup for the control panel, since v0.23.

v0.23 (May 30, 2017)
--------------------

Mail:

* The default theme for Roundcube was changed to the nicer Larry theme.
* Exchange/ActiveSync support has been replaced with z-push 2.3.6 from z-push.org (rather than z-push-contrib).

ownCloud (now Nextcloud):

* ownCloud is replaced with Nextcloud 10.0.5.
* Fixed an error in Owncloud/Nextcloud setup not updating domain when changing hostname.

Control Panel/Management:

* Fix an error in the control panel showing rsync backup status.
* Fix an error in the control panel related to IPv6 addresses.
* TLS certificates for internationalized domain names can now be provisioned from Let's Encrypt automatically.
* Third-party assets used in the control panel (jQuery/Bootstrap) are now downloaded during setup and served from the box rather than from a CDN.

DNS:

* Add support for custom CAA records.

v0.22 (April 2, 2017)
---------------------

Mail:

* The CardDAV plugin has been added to Roundcube so that your ownCloud contacts are available in webmail.
* Upgraded to Roundcube 1.2.4 and updated the persistent login plugin.
* Allow larger messages to be checked by SpamAssassin.
* Dovecot's vsz memory limit has been increased proportional to system memory.
* Newly set user passwords must be at least eight characters.

ownCloud:

* Upgraded to ownCloud 9.1.4.

Control Panel/Management:

* The status checks page crashed when the mailinabox.email website was down - that's fixed.
* Made nightly re-provisioning of TLS certificates less noisy.
* Fixed bugs in rsync backup method and in the list of recent backups.
* Fixed incorrect status checks errors about IPv6 addresses.
* Fixed incorrect status checks errors for secondary nameservers if round-robin custom A records are set.
* The management mail_log.py tool has been rewritten.

DNS:

* Added support for DSA, ED25519, and custom SSHFP records.

System:

* The SSH fail2ban jail was not activated.

Installation:

* At the end of installation, the SHA256 -- rather than SHA1 -- hash of the system's TLS certificate is shown.

v0.21c (February 1, 2017)
-------------------------

Installations and upgrades started failing about 10 days ago with the error "ImportError: No module named 'packaging'" after an upstream package (Python's setuptools) was updated by its maintainers. The updated package conflicted with Ubuntu 14.04's version of another package (Python's pip). This update upgrades both packages to remove the conflict.

If you already encountered the error during installation or upgrade of Mail-in-a-Box, this update may not correct the problem on your existing system. See https://discourse.mailinabox.email/t/v0-21c-release-fixes-python-package-installation-issue/1881 for help if the problem persists after upgrading to this version of Mail-in-a-Box.

v0.21b (December 4, 2016)
-------------------------

This update corrects a first-time installation issue introduced in v0.21 caused by the new Exchange/ActiveSync feature.

v0.21 (November 30, 2016)
-------------------------

This version updates ownCloud, which may include security fixes, and makes some other smaller improvements.

Mail:

* Header privacy filters were improperly running on the contents of forwarded email --- that's fixed.
* We have another go at fixing a long-standing issue with training the spam filter (because of a file permissions issue).
* Exchange/ActiveSync will now use your display name set in Roundcube in the From: line of outgoing email.

ownCloud:

* Updated ownCloud to version 9.1.1.

Control panel:

* Backups can now be made using rsync-over-ssh!
* Status checks failed if the system doesn't support iptables or doesn't have ufw installed.
* Added support for SSHFP records when sshd listens on non-standard ports.
* Recommendations for TLS certificate providers were removed now that everyone mostly uses Let's Encrypt.

System:

* Ubuntu's "Upgrade to 16.04" notice is suppressed since you should not do that.
* Lowered memory requirements to 512MB, display a warning if system memory is below 768MB.

v0.20 (September 23, 2016)
--------------------------

ownCloud:

* Updated to ownCloud to 8.2.7.

Control Panel:

* Fixed a crash that occurs when there are IPv6 DNS records due to a bug in dnspython 1.14.0.
* Improved the wonky low disk space check.

v0.19b (August 20, 2016)
------------------------

This update corrects a security issue introduced in v0.18.

* A remote code execution vulnerability is corrected in how the munin system monitoring graphs are generated for the control panel. The vulnerability involves an administrative user visiting a carefully crafted URL.

v0.19a (August 18, 2016)
------------------------

This update corrects a security issue in v0.19.

* fail2ban won't start if Roundcube had not yet been used - new installations probably do not have fail2ban running.

v0.19 (August 13, 2016)
-----------------------

Mail:

* Roundcube is updated to version 1.2.1.
* SSLv3 and RC4 are now no longer supported in incoming and outgoing mail (SMTP port 25).

Control panel:

* The users and aliases APIs are now documented on their control panel pages.
* The HSTS header was missing.
* New status checks were added for the ufw firewall.

DNS:

* Add SRV records for CardDAV/CalDAV to facilitate autoconfiguration (e.g. in DavDroid, whose latest version didn't seem to work to configure with entering just a hostname).

System:

* fail2ban jails added for SMTP submission, Roundcube, ownCloud, the control panel, and munin.
* Mail-in-a-Box can now be installed on the i686 architecture.

v0.18c (June 2, 2016)
---------------------

* Domain aliases (and misconfigured aliases/catch-alls with non-existent local targets) would accept mail and deliver it to new mailbox folders on disk even if the target address didn't correspond with an existing mail user, instead of rejecting the mail. This issue was introduced in v0.18.
* The Munin Monitoring link in the control panel now opens a new window.
* Added an undocumented before-backup script.

v0.18b (May 16, 2016)
---------------------

* Fixed a Roundcube user accounts issue introduced in v0.18.

v0.18 (May 15, 2016)
--------------------

ownCloud:

* Updated to ownCloud to 8.2.3

Mail:

* Roundcube is updated to version 1.1.5 and the Roundcube login screen now says "[hostname] Webmail" instead of "Mail-in-a-Box/Roundcube webmail".
* Fixed a long-standing issue with training the spam filter not working (because of a file permissions issue).

Control panel:

* Munin system monitoring graphs are now zoomable.
* When a reboot is required (due to Ubuntu security updates automatically installed), a Reboot Box button now appears on the System Status Checks page of the control panel.
* It is now possible to add SRV and secondary MX records in the Custom DNS page.
* Other minor fixes.

System:

* The fail2ban recidive jail, which blocks long-duration brute force attacks, now no longer sends the administrator emails (which were not helpful).

Setup:

* The system hostname is now set during setup.
* A swap file is now created if system memory is less than 2GB, 5GB of free disk space is available, and if no swap file yet exists.
* We now install Roundcube from the official GitHub repository instead of our own mirror, which we had previously created to solve problems with SourceForge.
* DKIM was incorrectly set up on machines where "localhost" was defined as something other than "127.0.0.1".

v0.17c (April 1, 2016)
----------------------

This update addresses some minor security concerns and some installation issues.

ownCloud:

* Block web access to the configuration parameters (config.php). There is no immediate impact (see [#776](https://github.com/mail-in-a-box/mailinabox/pull/776)), although advanced users may want to take note.

Mail:

* Roundcube html5_notifier plugin updated from version 0.6 to 0.6.2 to fix Roundcube getting stuck for some people.

Control panel:

* Prevent click-jacking of the management interface by adding HTTP headers.
* Failed login no longer reveals whether an account exists on the system.

Setup:

* Setup dialogs did not appear correctly when connecting to SSH using Putty on Windows.
* We now install Roundcube from our own mirror because Sourceforge's downloads experience frequent intermittent unavailability.

v0.17b (March 1, 2016)
----------------------

ownCloud moved their source code to a new location, breaking our installation script.

v0.17 (February 25, 2016)
-------------------------

Mail:

* Roundcube updated to version 1.1.4.
* When there's a problem delivering an outgoing message, a new 'warning' bounce will come after 3 hours and the box will stop trying after 2 days (instead of 5).
* On multi-homed machines, Postfix now binds to the right network interface when sending outbound mail so that SPF checks on the receiving end will pass.
* Mail sent from addresses on subdomains of other domains hosted by this box would not be DKIM-signed and so would fail DMARC checks by recipients, since version v0.15.

Control panel:

* TLS certificate provisioning would crash if DNS propagation was in progress and a challenge failed; might have shown the wrong error when provisioning fails.
* Backup times were displayed with the wrong time zone.
* Thresholds for displaying messages when the system is running low on memory have been reduced from 30% to 20% for a warning and from 15% to 10% for an error.
* Other minor fixes.

System:

* Backups to some AWS S3 regions broke in version 0.15 because we reverted the version of boto. That's now fixed.
* On low-usage systems, don't hold backups for quite so long by taking a full backup more often.
* Nightly status checks might fail on systems not configured with a default Unicode locale.
* If domains need a TLS certificate and the user hasn't installed one yet using Let's Encrypt, the administrator would get a nightly email with weird interactive text asking them to agree to Let's Encrypt's ToS. Now just say that the provisioning can't be done automatically.
* Reduce the number of background processes used by the management daemon to lower memory consumption.

Setup:

* The first screen now warns users not to install on a machine used for other things.

v0.16 (January 30, 2016)
------------------------

This update primarily adds automatic SSL (now "TLS") certificate provisioning from Let's Encrypt (https://letsencrypt.org/).

Control Panel:

* The SSL certificates (now referred to as "TLS certificates") page now supports provisioning free certificates from Let's Encrypt.
* Report free memory usage.
* Fix a crash when the git directory is not checked out to a tag.
* When IPv6 is enabled, check that all domains (besides the system hostname) resolve over IPv6.
* When a domain doesn't resolve to the box, don't bother checking if the TLS certificate is valid.
* Remove rounded border on the menu bar.

Other:

* The Sieve port is now open so tools like the Thunderbird Sieve extension can be used to edit mail filters.
* .be domains now offer DNSSEC options supported by the TLD
* The daily backup will now email the administrator if there is a problem.
* Expiring TLS certificates are now automatically renewed via Let's Encrypt.
* File ownership for installed Roundcube files is fixed.
* Typos fixed.

v0.15a (January 9, 2016)
------------------------

Mail:

* Sending mail through Exchange/ActiveSync (Z-Push) had been broken since v0.14 in some setups. This is now fixed.

v0.15 (January 1, 2016)
-----------------------

Mail:

* Updated Roundcube to version 1.1.3.
* Auto-create aliases for abuse@, as required by RFC2142.
* The DANE TLSA record is changed to use the certificate subject public key rather than the whole certificate, which means the record remains valid after certificate changes (so long as the private key remains the same, which it does for us).

Control panel:

* When IPv6 is enabled, check that system services are accessible over IPv6 too, that the box's hostname resolves over IPv6, and that reverse DNS is setup correctly for IPv6.
* Explanatory text for setting up secondary nameserver is added/fixed.
* DNS checks now have a timeout in case a DNS server is not responding, so the checks don't stall indefinitely.
* Better messages if external DNS is used and, weirdly, custom secondary nameservers are set.
* Add POP to the mail client settings documentation.
* The box's IP address is added to the fail2ban whitelist so that the status checks don't trigger the machine banning itself, which results in the status checks showing services down even though they are running.
* For SSL certificates, rather than asking you what country you are in during setup, ask at the time a CSR is generated. The default system self-signed certificate now omits a country in the subject (it was never needed). The CSR_COUNTRY Mail-in-a-Box setting is dropped entirely.

System:

* Nightly backups and system status checks are now moved to 3am in the system's timezone.
* fail2ban's recidive jail is now active, which guards against persistent brute force login attacks over long periods of time.
* Setup (first run only) now asks for your timezone to set the system time.
* The Exchange/ActiveSync server is now taken offline during nightly backups (along with SMTP and IMAP).
* The machine's random number generator (/dev/urandom) is now seeded with Ubuntu Pollinate and a blocking read on /dev/random.
* DNSSEC key generation during install now uses /dev/urandom (instead of /dev/random), which is faster.
* The $STORAGE_ROOT/ssl directory is flattened by a migration script and the system SSL certificate path is now a symlink to the actual certificate.
* If ownCloud sends out email, it will use the box's administrative address now (admin@yourboxname).
* Z-Push (Exchange/ActiveSync) logs now exclude warnings and are now rotated to save disk space.
* Fix pip command that might have not installed all necessary Python packages.
* The control panel and backup would not work on Google Compute Engine because GCE installs a conflicting boto package.
* Added a new command `management/backup.py --restore` to restore files from a backup to a target directory (command line arguments are passed to `duplicity restore`).

v0.14 (November 4, 2015)
------------------------

Mail:

* Spamassassin's network-based tests (Pyzor, others) and DKIM tests are now enabled. (Pyzor had always been installed but was not active due to a misconfiguration.)
* Moving spam out of the Spam folder and into Trash would incorrectly train Spamassassin that those messages were not spam.
* Automatically create the Sent and Archive folders for new users.
* The HTML5_Notifier plugin for Roundcube is now included, which when turned on in Roundcube settings provides desktop notifications for new mail.
* The Exchange/ActiveSync backend Z-Push has been updated to fix a problem with CC'd emails not being sent to the CC recipients.

Calender/Contacts:

* CalDAV/CardDAV and Exchange/ActiveSync for calendar/contacts wasn't working in some network configurations.

Web:

* When a new domain is added to the box, rather than applying a new self-signed certificate for that domain, the SSL certificate for the box's primary hostname will be used instead.
* If a custom DNS record is set on a domain or 'www'+domain, web would not be served for that domain. If the custom DNS record is just the box's IP address, that's a configuration mistake, but allow it and let web continue to be served.
* Accommodate really long domain names by increasing an nginx setting.

Control panel:

* Added an option to check for new Mail-in-a-Box versions within status checks. It is off by default so that boxes don't "phone home" without permission.
* Added a random password generator on the users page to simplify creating new accounts.
* When S3 backup credentials are set, the credentials are now no longer ever sent back from the box to the client, for better security.
* Fixed the jumpiness when a modal is displayed.
* Focus is put into the login form fields when the login form is displayed.
* Status checks now include a warning if a custom DNS record has been set on a domain that would normally serve web and as a result that domain no longer is serving web.
* Status checks now check that secondary nameservers, if specified, are actually serving the domains.
* Some errors in the control panel when there is invalid data in the database or an improperly named archived user account have been suppressed.
* Added subresource integrity attributes to all remotely-sourced resources (i.e. via CDNs) to guard against CDNs being used as an attack vector.

System:

* Tweaks to fail2ban settings.
* Fixed a spurious warning while installing munin.

v0.13b (August 30, 2015)
------------------------

Another ownCloud 8.1.1 issue was found. New installations left ownCloud improperly setup ("You are accessing the server from an untrusted domain."). Upgrading to this version will fix that.

v0.13a (August 23, 2015)
------------------------

Note: v0.13 (no 'a', August 19, 2015) was pulled immediately due to an ownCloud bug that prevented upgrades. v0.13a works around that problem.

Mail:

* Outbound mail headers (the Received: header) are tweaked to possibly improve deliverability.
* Some MIME messages would hang Roundcube due to a missing package.
* The users permitted to send as an alias can now be different from where an alias forwards to.

DNS:

* The secondary nameservers option in the control panel now accepts more than one nameserver and a special xfr:IP format to specify zone-transfer-only IP addresses.
* A TLSA record is added for HTTPS for DNSSEC-aware clients that support it.

System:

* Backups can now be turned off, or stored in Amazon S3, through new control panel options.
* Munin was not working on machines confused about their hostname and had lots of errors related to PANGO, NTP peers and network interfaces that were not up.
* ownCloud updated to version 8.1.1 (with upgrade work-around), its memcached caching enabled.
* When upgrading, network checks like blocked port 25 are now skipped.
* Tweaks to the intrusion detection rules for IMAP.
* Mail-in-a-Box's setup is a lot quieter, hiding lots of irrelevant messages.

Control panel:

* SSL certificate checks were failing on OVH/OpenVZ servers due to missing /dev/stdin.
* Improve the sort order of the domains in the status checks.
* Some links in the control panel were only working in Chrome.

v0.12c (July 19, 2015)
----------------------

v0.12c was posted to work around the current Sourceforge.net outage: pyzor's remote server is now hard-coded rather than accessing a file hosted on Sourceforge, and roundcube is now downloaded from a Mail-in-a-Box mirror rather than from Sourceforge.

v0.12b (July 4, 2015)
---------------------

This version corrects a minor regression in v0.12 related to creating aliases targeting multiple addresses.

v0.12 (July 3, 2015)
--------------------

This is a minor update to v0.11, which was a major update. Please read v0.11's advisories.

* The administrator@ alias was incorrectly created starting with v0.11. If your first install was v0.11, check that the administrator@ alias forwards mail to you.
* Intrusion detection rules (fail2ban) are relaxed (i.e. less is blocked).
* SSL certificates could not be installed for the new automatic 'www.' redirect domains.
* PHP's default character encoding is changed from no default to UTF8. The effect of this change is unclear but should prevent possible future text conversion issues.
* User-installed SSL private keys in the BEGIN PRIVATE KEY format were not accepted.
* SSL certificates with SAN domains with IDNA encoding were broken in v0.11.
* Some IDNA functionality was using IDNA 2003 rather than IDNA 2008.

v0.11b (June 29, 2015)
----------------------

v0.11b was posted shortly after the initial posting of v0.11 to correct a missing dependency for the new PPA.

v0.11 (June 29, 2015)
---------------------

Advisories:
* Users can no longer spoof arbitrary email addresses in outbound mail. When sending mail, the email address configured in your mail client must match the SMTP login username being used, or the email address must be an alias with the SMTP login username listed as one of the alias's targets.
* This update replaces your DKIM signing key with a stronger key. Because of DNS caching/propagation, mail sent within a few hours after this update could be marked as spam by recipients. If you use External DNS, you will need to update your DNS records.
* The box will now install software from a new Mail-in-a-Box PPA on Launchpad.net, where we are distributing two of our own packages: a patched postgrey and dovecot-lucene.

Mail:
* Greylisting will now let some reputable senders pass through immediately.
* Searching mail (via IMAP) will now be much faster using the dovecot lucene full text search plugin.
* Users can no longer spoof arbitrary email addresses in outbound mail (see above).
* Fix for deleting admin@ and postmaster@ addresses.
* Roundcube is updated to version 1.1.2, plugins updated.
* Exchange/ActiveSync autoconfiguration was not working on all devices (e.g. iPhone) because of a case-sensitive URL.
* The DKIM signing key has been increased to 2048 bits, from 1024, replacing the existing key.

Web:
* 'www' subdomains now automatically redirect to their parent domain (but you'll need to install an SSL certificate).
* OCSP no longer uses Google Public DNS.
* The installed PHP version is no longer exposed through HTTP response headers, for better security.

DNS:
* Default IPv6 AAAA records were missing since version 0.09.

Control panel:
* Resetting a user's password now forces them to log in again everywhere.
* Status checks were not working if an ssh server was not installed.
* SSL certificate validation now uses the Python cryptography module in some places where openssl was used.
* There is a new tab to show the installed version of Mail-in-a-Box and to fetch the latest released version.

System:
* The munin system monitoring tool is now installed and accessible at /admin/munin.
* ownCloud updated to version 8.0.4. The ownCloud installation step now is resilient to download problems. The ownCloud configuration file is now stored in STORAGE_ROOT to fix loss of data when moving STORAGE_ROOT to a new machine.
* The setup scripts now run `apt-get update` prior to installing anything to ensure the apt database is in sync with the packages actually available.


v0.10 (June 1, 2015)
--------------------

* SMTP Submission (port 587) began offering the insecure SSLv3 protocol due to a misconfiguration in the previous version.
* Roundcube now allows persistent logins using Roundcube-Persistent-Login-Plugin.
* ownCloud is updated to version 8.0.3.
* SPF records for non-mail domains were tightened.
* The minimum greylisting delay has been reduced from 5 minutes to 3 minutes.
* Users and aliases weren't working if they were entered with any uppercase letters. Now only lowercase is allowed.
* After installing an SSL certificate from the control panel, the page wasn't being refreshed.
* Backups broke if the box's hostname was changed after installation.
* Dotfiles (i.e. .svn) stored in ownCloud Files were not accessible from ownCloud's mobile/desktop clients.
* Fix broken install on OVH VPS's.


v0.09 (May 8, 2015)
-------------------

Mail:

* Spam checking is now performed on messages larger than the previous limit of 64KB.
* POP3S is now enabled (port 995).
* Roundcube is updated to version 1.1.1.
* Minor security improvements (more mail headers with user agent info are anonymized; crypto settings were tightened).

ownCloud:

* Downloading files you uploaded to ownCloud broke because of a change in ownCloud 8.

DNS:

* Internationalized Domain Names (IDNs) should now work in email. If you had custom DNS or custom web settings for internationalized domains, check that they are still working.
* It is now possible to set multiple TXT and other types of records on the same domain in the control panel.
* The custom DNS API was completely rewritten to support setting multiple records of the same type on a domain. Any existing client code using the DNS API will have to be rewritten. (Existing code will just get 404s back.)
* On some systems the `nsd` service failed to start if network interfaces were not ready.

System / Control Panel:

* In order to guard against misconfiguration that can lead to domain control validation hijacking, email addresses that begin with admin, administrator, postmaster, hostmaster, and webmaster can no longer be used for (new) mail user accounts, and aliases for these addresses may direct mail only to the box's administrator(s).
* Backups now use duplicity's built-in gpg symmetric AES256 encryption rather than my home-brewed encryption. Old backups will be incorporated inside the first backup after this update but then deleted from disk (i.e. your backups from the previous few days will be backed up).
* There was a race condition between backups and the new nightly status checks.
* The control panel would sometimes lock up with an unnecessary loading indicator.
* You can no longer delete your own account from the control panel.

Setup:

* All Mail-in-a-Box release tags are now signed on github, instructions for verifying the signature are added to the README, and the integrity of some packages downloaded during setup is now verified against a SHA1 hash stored in the tag itself.
* Bugs in first user account creation were fixed.

v0.08 (April 1, 2015)
---------------------

Mail:

* The Roundcube vacation_sieve plugin by @arodier is now installed to make it easier to set vacation auto-reply messages from within Roundcube.
* Authentication-Results headers for DMARC, added in v0.07, were mistakenly added for outbound mail --- that's now removed.
* The Trash folder is now created automatically for new mail accounts, addressing a Roundcube error.

DNS:

* Custom DNS TXT records were not always working and they can now override the default SPF, DKIM, and DMARC records.

System:

* ownCloud updated to version 8.0.2.
* Brute-force SSH and IMAP login attempts are now prevented by properly configuring fail2ban.
* Status checks are run each night and any changes from night to night are emailed to the box administrator (the first user account).

Control panel:

* The new check that system services are running mistakenly checked that the Dovecot Managesieve service is publicly accessible. Although the service binds to the public network interface we don't open the port in ufw. On some machines it seems that ufw blocks the connection from the status checks (which seems correct) and on some machines (mine) it doesn't, which is why I didn't notice the problem.
* The current backup chain will now try to predict how many days until it is deleted (always at least 3 days after the next full backup).
* The list of aliases that forward to a user are removed from the Mail Users page because when there are many alises it is slow and times-out.
* Some status check errors are turned into warnings, especially those that might not apply if External DNS is used.

v0.07 (February 28, 2015)
-------------------------

Mail:

* If the box manages mail for a domain and a subdomain of that domain, outbound mail from the subdomain was not DKIM-signed and would therefore fail DMARC tests on the receiving end, possibly result in the mail heading into spam folders.
* Auto-configuration for Mozilla Thunderbird, Evolution, KMail, and Kontact is now available.
* Domains that only have a catch-all alias or domain alias no longer automatically create/require admin@ and postmaster@ addresses since they'll forward anyway.
* Roundcube is updated to version 1.1.0.
* Authentication-Results headers for DMARC are now added to incoming mail.

DNS:

* If a custom CNAME record is set on a 'www' subdomain, the default A/AAAA records were preventing the CNAME from working.
* If a custom DNS A record overrides one provided by the box, the a corresponding default IPv6 record by the box is removed since it will probably be incorrect.
* Internationalized domain names (IDNs) are now supported for DNS and web, but email is not yet tested.

Web:

* Static websites now deny access to certain dot (.) files and directories which typically have sensitive info: .ht*, .svn*, .git*, .hg*, .bzr*.
* The nginx server no longer reports its version and OS for better privacy.
* The HTTP->HTTPS redirect is now more efficient.
* When serving a 'www.' domain, reuse the SSL certificate for the parent domain if it covers the 'www' subdomain too
* If a custom DNS CNAME record is set on a domain, don't offer to put a website on that domain. (Same logic already applies to custom A/AAAA records.)

Control panel:

* Status checks now check that system services are actually running by pinging each port that should have something running on it.
* The status checks are now parallelized so they may be a little faster.
* The status check for MX records now allow any priority, in case an unusual setup is required.
* The interface for setting website domain-specific directories is simplified.
* The mail guide now says that to use Outlook, Outlook 2007 or later on Windows 7 and later is required.
* External DNS settings now skip the special "_secondary_nameserver" key which is used for storing secondary NS information.

Setup:

* Install cron if it isn't already installed.
* Fix a units problem in the minimum memory check.
* If you override the STORAGE_ROOT, your setting will now persist if you re-run setup.
* Hangs due to apt wanting the user to resolve a conflict should now be fixed (apt will just clobber the problematic file now).

v0.06 (January 4, 2015)
-----------------------

Mail:

* Set better default system limits to accommodate boxes handling mail for 20+ users.

Contacts/calendar:

* Update to ownCloud to 7.0.4.
* Contacts syncing via ActiveSync wasn't working.

Control panel:

* New control panel for setting custom DNS settings (without having to use the API).
* Status checks showed a false positive for Spamhause blacklists and for secondary DNS in some cases.
* Status checks would fail to load if openssh-sever was not pre-installed, but openssh-server is not required.
* The local DNS cache is cleared before running the status checks using 'rncd' now rather than restarting 'bind9', which should be faster and wont interrupt other services.
* Multi-domain and wildcard certificate can now be installed through the control panel.
* The DNS API now allows the setting of SRV records.

Misc:

* IPv6 configuration error in postgrey, nginx.
* Missing dependency on sudo.

v0.05 (November 18, 2014)
-------------------------

Mail:

* The maximum size of outbound mail sent via webmail and Exchange/ActiveSync has been increased to 128 MB, the same as when using SMTP.
* Spam is no longer wrapped as an attachment inside a scary Spamassassin explanation. The original message is simply moved straight to the Spam folder unchanged.
* There is a new iOS/Mac OS X Configuration Profile link in the control panel which makes it easier to configure IMAP/SMTP/CalDAV/CardDAV on iOS devices and Macs.
* "Domain aliases" can now be configured in the control panel.
* Updated to [Roundcube 1.0.3](http://trac.roundcube.net/wiki/Changelog).
* IMAP/SMTP is now recommended even on iOS devices as Exchange/ActiveSync is terribly buggy.

Control panel:

* Installing an SSL certificate for the primary hostname would cause problems until a restart (services needed to be restarted).
* Installing SSL certificates would fail if /tmp was on a different filesystem.
* Better error messages when installing a SSL certificate fails.
* The local DNS cache is now cleared each time the system status checks are run.
* Documented how to use +tag addressing.
* Minor UI tweaks.

Other:

* Updated to [ownCloud 7.0.3](http://owncloud.org/changelog/).
* The ownCloud API is now exposed properly.
* DNSSEC now works on `.guide` domains now too (RSASHA256).

v0.04 (October 15, 2014)
------------------------

Breaking changes:

* On-disk backups are now retained for a minimum of 3 days instead of 14. Beyond that the user is responsible for making off-site copies.
* IMAP no longer supports the legacy SSLv3 protocol. SSLv3 is now known to be insecure. I don't believe any modern devices will be affected by this. HTTPS and SMTP submission already had SSLv3 disabled.

Control panel:

* The control panel has a new page for installing SSL certificates.
* The control panel has a new page for hosting static websites.
* The control panel now shows mailbox sizes on disk.
* It is now possible to create catch-all aliases from the control panel.
* Many usability improvements in the control panel.

DNS:

* Custom DNS A/AAAA records on subdomains were ignored.
* It is now possible to set up a secondary DNS server.
* DNS zones were updating even when nothing changed.
* Strict SPF and DMARC settings are now set on all subdomains not used for mail.

Security:

* DNSSEC is now supported for the .email TLD which required a different key algorithm.
* Nginx and Postfix now use 2048 bits of DH parameters instead of 1024.

Other:

* Spam filter learning by dragging mail in and out of the Spam folder should hopefully be working now.
* Some things were broken if the machine had an IPv6 address.
* Other things were broken if the machine was on a non-utf8 locale.
* No longer implementing webfinger.
* Removes apache before installing nginx, in case it has been installed by distro.

v0.03 (September 24, 2014)
--------------------------

* Update existing installs of Roundcube.
* Disabled catch-alls pending figuring out how to get users to take precedence.
* Z-Push was not working because in v0.02 we had accidentally moved to a different version.
* Z-Push is now locked to a specific commit so it doesn't change on us accidentally.
* The start script is now symlinked to /usr/local/bin/mailinabox.

v0.02 (September 21, 2014)
--------------------------

* Open the firewall to an alternative SSH port if set.
* Fixed missing dependencies.
* Set Z-Push to use sync command with ownCloud.
* Support more concurrent connections for z-push.
* In the status checks, handle wildcard certificates.
* Show the status of backups in the control panel.
* The control panel can now update a user's password.
* Some usability improvements in the control panel.
* Warn if a SSL cert is expiring in 30 days.
* Use SHA2 to generate CSRs.
* Better logic for determining when to take a full backup.
* Reduce DNS TTL, not that it seems to really matter.
* Add SSHFP DNS records.
* Add an API for setting custom DNS records
* Update to ownCloud 7.0.2.
* Some things were broken if the machine had an IPv6 address.
* Use a dialogs library to ask users questions during setup.
* Other fixes.

v0.01 (August 19, 2014)
-----------------------

First versioned release after a year of unversioned development.


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Mail-in-a-Box Code of Conduct

Mail-in-a-Box is an open source community project about working, as a group, to empower ourselves and others to have control over our own digital communications. Just as we hope to increase technological diversity on the Internet through decentralization, we also believe that diverse viewpoints and voices among our community members foster innovation and creative solutions to the challenges we face.

We are committed to providing a safe, welcoming, and harassment-free space for collaboration, for everyone, without regard to age, disability, economic situation, ethnicity, gender identity and expression, language fluency, level of knowledge or experience, nationality, personal appearance, race, religion, sexual identity and orientation, or any other attribute. Community comes first. This policy supersedes all other project goals.

The maintainers of Mail-in-a-Box share the dual responsibility of leading by example and enforcing these policies as necessary to maintain an open and welcoming environment. All community members should be excellent to each other.

## Scope

This Code of Conduct applies to all places where Mail-in-a-Box community activity is occurring, including on GitHub, in discussion forums, on Slack, on social media, and in real life. The Code of Conduct applies not only on websites/at events run by the Mail-in-a-Box community (e.g. our GitHub organization, our Slack team) but also at any other location where the Mail-in-a-Box community is present (e.g. in issues of other GitHub organizations where Mail-in-a-Box community members are discussing problems related to Mail-in-a-Box, or real-life professional conferences), or whenever a Mail-in-a-Box community member is representing Mail-in-a-Box to the public at large or acting on behalf of Mail-in-a-Box.

This code does not apply to activity on a server running Mail-in-a-Box software, unless your server is hosting a service for the Mail-in-a-Box community at large.

## Our Standards

Examples of behavior that contributes to creating a positive environment include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Showing empathy towards other community members
* Making room for new and quieter voices

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory/unwelcome comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Aggressive and micro-aggressive behavior, such as unconstructive criticism, providing corrections that do not improve the conversation (sometimes referred to as "well actually"s), repeatedly interrupting or talking over someone else, feigning surprise at someone's lack of knowledge or awareness about a topic, or subtle prejudice (for example, comments like "That's so easy my grandmother could do it.", which is prejudicial toward grandmothers).
* Other conduct which could reasonably be considered inappropriate in a professional setting
* Retaliating against anyone who reports a violation of this code.

We will not tolerate harassment. Harassment is any unwelcome or hostile behavior towards another person for any reason. This includes, but is not limited to, offensive verbal comments related to personal characteristics or choices, sexual images or comments, deliberate intimidation, bullying, stalking, following, harassing photography or recording, sustained disruption of discussion or events, nonconsensual publication of private comments, inappropriate physical contact, or unwelcome sexual attention. Conduct need not be intentional to be harassment.

## Enforcement

We will remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not consistent with this Code of Conduct. We may ban, temporarily or permanently, any contributor for violating this code, when appropriate.

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project lead, [Joshua Tauberer](https://razor.occams.info/). All reports will be treated confidentially, impartially, consistently, and swiftly.

Because the need for confidentiality for all parties involved in an enforcement action outweighs the goals of openness, limited information will be shared with the Mail-in-a-Box community regarding enforcement actions that have taken place.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant, version 1.4](http://contributor-covenant.org/version/1/4) and the code of conduct of [Code for DC](http://codefordc.org/resources/codeofconduct.html).



================================================
FILE: CONTRIBUTING.md
================================================
# Contributing

Mail-in-a-Box is an open source project. Your contributions and pull requests are welcome.

## Development

To start developing Mail-in-a-Box, [clone the repository](https://github.com/mail-in-a-box/mailinabox) and familiarize yourself with the code.

    $ git clone https://github.com/mail-in-a-box/mailinabox

### Vagrant and VirtualBox

We recommend you use [Vagrant](https://www.vagrantup.com/intro/getting-started/install.html) and [VirtualBox](https://www.virtualbox.org/wiki/Downloads) for development. Please install them first.

With Vagrant set up, the following should boot up Mail-in-a-Box inside a virtual machine:

    $ vagrant up --provision

_If you're seeing an error message about your *IP address being listed in the Spamhaus Block List*, simply uncomment the `export SKIP_NETWORK_CHECKS=1` line in `Vagrantfile`. It's normal, you're probably using a dynamic IP address assigned by your Internet provider–they're almost all listed._

### Modifying your `hosts` file

After a while, Mail-in-a-Box will be available at `192.168.56.4` (unless you changed that in your `Vagrantfile`). To be able to use the web-based bits, we recommend to add a hostname to your `hosts` file:

    $ echo "192.168.56.4 mailinabox.lan" | sudo tee -a /etc/hosts

You should now be able to navigate to https://mailinabox.lan/admin using your browser. There should be an initial admin user with the name `me@mailinabox.lan` and the password `12345678`.

### Making changes

Your working copy of Mail-in-a-Box will be mounted inside your VM at `/vagrant`. Any change you make locally will appear inside your VM automatically.

Running `vagrant up --provision` again will repeat the installation with your modifications.

Alternatively, you can also ssh into the VM using:

    $ vagrant ssh

Once inside the VM, you can re-run individual parts of the setup like in this example:

    vm$ cd /vagrant
    vm$ sudo setup/owncloud.sh # replace with script you'd like to re-run

### Tests

Mail-in-a-Box needs more tests. If you're still looking for a way to help out, writing and contributing tests would be a great start!

## Public domain

This project is in the public domain. Copyright and related rights in the work worldwide are waived through the [CC0 1.0 Universal public domain dedication][CC0]. See the LICENSE file in this directory.

All contributions to this project must be released under the same CC0 wavier. By submitting a pull request or patch, you are agreeing to comply with this waiver of copyright interest.

[CC0]: http://creativecommons.org/publicdomain/zero/1.0/

## Code of Conduct

This project has a [Code of Conduct](CODE_OF_CONDUCT.md). Please review it when joining our community.


================================================
FILE: LICENSE
================================================
Creative Commons Legal Code

CC0 1.0 Universal

    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
    HEREUNDER.

Statement of Purpose

The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").

Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.

For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.

1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:

  i. the right to reproduce, adapt, distribute, perform, display,
     communicate, and translate a Work;
 ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
     likeness depicted in a Work;
 iv. rights protecting against unfair competition in regards to a Work,
     subject to the limitations in paragraph 4(a), below;
  v. rights protecting the extraction, dissemination, use and reuse of data
     in a Work;
 vi. database rights (such as those arising under Directive 96/9/EC of the
     European Parliament and of the Council of 11 March 1996 on the legal
     protection of databases, and under any national implementation
     thereof, including any amended or successor version of such
     directive); and
vii. other similar, equivalent or corresponding rights throughout the
     world based on applicable law or treaty, and any national
     implementations thereof.

2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.

3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.

4. Limitations and Disclaimers.

 a. No trademark or patent rights held by Affirmer are waived, abandoned,
    surrendered, licensed or otherwise affected by this document.
 b. Affirmer offers the Work as-is and makes no representations or
    warranties of any kind concerning the Work, express, implied,
    statutory or otherwise, including without limitation warranties of
    title, merchantability, fitness for a particular purpose, non
    infringement, or the absence of latent or other defects, accuracy, or
    the present or absence of errors, whether or not discoverable, all to
    the greatest extent permissible under applicable law.
 c. Affirmer disclaims responsibility for clearing rights of other persons
    that may apply to the Work or any use thereof, including without
    limitation any person's Copyright and Related Rights in the Work.
    Further, Affirmer disclaims responsibility for obtaining any necessary
    consents, permissions or other rights required for any use of the
    Work.
 d. Affirmer understands and acknowledges that Creative Commons is not a
    party to this document and has no duty or obligation with respect to
    this CC0 or use of the Work.


================================================
FILE: README.md
================================================
Mail-in-a-Box
=============

By [@JoshData](https://github.com/JoshData) and [contributors](https://github.com/mail-in-a-box/mailinabox/graphs/contributors).

Mail-in-a-Box helps individuals take back control of their email by defining a one-click, easy-to-deploy SMTP+everything else server: a mail server in a box.

**Please see [https://mailinabox.email](https://mailinabox.email) for the project's website and setup guide!**

* * *

Our goals are to:

* Make deploying a good mail server easy.
* Promote [decentralization](http://redecentralize.org/), innovation, and privacy on the web.
* Have automated, auditable, and [idempotent](https://web.archive.org/web/20190518072631/https://sharknet.us/2014/02/01/automated-configuration-management-challenges-with-idempotency/) configuration.
* **Not** make a totally unhackable, NSA-proof server.
* **Not** make something customizable by power users.

Additionally, this project has a [Code of Conduct](CODE_OF_CONDUCT.md), which supersedes the goals above. Please review it when joining our community.


In The Box
----------

Mail-in-a-Box turns a fresh Ubuntu 22.04 LTS 64-bit machine into a working mail server by installing and configuring various components.

It is a one-click email appliance. There are no user-configurable setup options. It "just works."

The components installed are:

* SMTP ([postfix](http://www.postfix.org/)), IMAP ([Dovecot](http://dovecot.org/)), CardDAV/CalDAV ([Nextcloud](https://nextcloud.com/)), and Exchange ActiveSync ([z-push](http://z-push.org/)) servers
* Webmail ([Roundcube](http://roundcube.net/)), mail filter rules (thanks to Roundcube and Dovecot), and email client autoconfig settings (served by [nginx](http://nginx.org/))
* Spam filtering ([spamassassin](https://spamassassin.apache.org/)) and greylisting ([postgrey](http://postgrey.schweikert.ch/))
* DNS ([nsd4](https://www.nlnetlabs.nl/projects/nsd/)) with [SPF](https://en.wikipedia.org/wiki/Sender_Policy_Framework), DKIM ([OpenDKIM](http://www.opendkim.org/)), [DMARC](https://en.wikipedia.org/wiki/DMARC), [DNSSEC](https://en.wikipedia.org/wiki/DNSSEC), [DANE TLSA](https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities), [MTA-STS](https://tools.ietf.org/html/rfc8461), and [SSHFP](https://tools.ietf.org/html/rfc4255) policy records automatically set
* TLS certificates are automatically provisioned using [Let's Encrypt](https://letsencrypt.org/) for protecting https and all of the other services on the box
* Backups ([duplicity](http://duplicity.nongnu.org/)), firewall ([ufw](https://launchpad.net/ufw)), intrusion protection ([fail2ban](http://www.fail2ban.org/wiki/index.php/Main_Page)), and basic system monitoring ([munin](http://munin-monitoring.org/))

It also includes system management tools:

* Comprehensive health monitoring that checks each day that services are running, ports are open, TLS certificates are valid, and DNS records are correct
* A control panel for adding/removing mail users, aliases, custom DNS records, configuring backups, etc.
* An API for all of the actions on the control panel

Internationalized domain names are supported and configured easily (but SMTPUTF8 is not supported, unfortunately).

It also supports static website hosting since the box is serving HTTPS anyway. (To serve a website for your domains elsewhere, just add a custom DNS "A" record in you Mail-in-a-Box's control panel to point domains to another server.)

For more information on how Mail-in-a-Box handles your privacy, see the [security details page](security.md).


Installation
------------

See the [setup guide](https://mailinabox.email/guide.html) for detailed, user-friendly instructions.

For experts, start with a completely fresh (really, I mean it) Ubuntu 22.04 LTS 64-bit machine. On the machine...

Clone this repository and checkout the tag corresponding to the most recent release (which you can find in the tags or releases lists on GitHub):

	$ git clone https://github.com/mail-in-a-box/mailinabox
	$ cd mailinabox
	$ git checkout TAGNAME

Begin the installation.

	$ sudo setup/start.sh

The installation will install, uninstall, and configure packages to turn the machine into a working, good mail server.

For help, DO NOT contact Josh directly --- I don't do tech support by email or tweet (no exceptions).

Post your question on the [discussion forum](https://discourse.mailinabox.email/) instead, where maintainers and Mail-in-a-Box users may be able to help you.

Note that while we want everything to "just work," we can't control the rest of the Internet. Other mail services might block or spam-filter email sent from your Mail-in-a-Box.
This is a challenge faced by everyone who runs their own mail server, with or without Mail-in-a-Box. See our discussion forum for tips about that.


Contributing and Development
----------------------------

Mail-in-a-Box is an open source project. Your contributions and pull requests are welcome. See [CONTRIBUTING](CONTRIBUTING.md) to get started. 


The Acknowledgements
--------------------

This project was inspired in part by the ["NSA-proof your email in 2 hours"](http://sealedabstract.com/code/nsa-proof-your-e-mail-in-2-hours/) blog post by Drew Crawford, [Sovereign](https://github.com/sovereign/sovereign) by Alex Payne, and conversations with <a href="https://twitter.com/shevski" target="_blank">@shevski</a>, <a href="https://github.com/konklone" target="_blank">@konklone</a>, and <a href="https://github.com/gregelin" target="_blank">@GregElin</a>.

Mail-in-a-Box is similar to [iRedMail](http://www.iredmail.org/) and [Modoboa](https://github.com/tonioo/modoboa).


The History
-----------

* In 2007 I wrote a relatively popular Mozilla Thunderbird extension that added client-side SPF and DKIM checks to mail to warn users about possible phishing: [add-on page](https://addons.mozilla.org/en-us/thunderbird/addon/sender-verification-anti-phish/), [source](https://github.com/JoshData/thunderbird-spf).
* In August 2013 I began Mail-in-a-Box by combining my own mail server configuration with the setup in ["NSA-proof your email in 2 hours"](http://sealedabstract.com/code/nsa-proof-your-e-mail-in-2-hours/) and making the setup steps reproducible with bash scripts.
* Mail-in-a-Box was a semifinalist in the 2014 [Knight News Challenge](https://www.newschallenge.org/challenge/2014/submissions/mail-in-a-box), but it was not selected as a winner.
* Mail-in-a-Box hit the front page of Hacker News in [April](https://news.ycombinator.com/item?id=7634514) 2014, [September](https://news.ycombinator.com/item?id=8276171) 2014, [May](https://news.ycombinator.com/item?id=9624267) 2015, and [November](https://news.ycombinator.com/item?id=13050500) 2016.
* FastCompany mentioned Mail-in-a-Box a [roundup of privacy projects](http://www.fastcompany.com/3047645/your-own-private-cloud) on June 26, 2015.


================================================
FILE: Vagrantfile
================================================
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/jammy64"

  # Network config: Since it's a mail server, the machine must be connected
  # to the public web. However, we currently don't want to expose SSH since
  # the machine's box will let anyone log into it. So instead we'll put the
  # machine on a private network.
  config.vm.hostname = "mailinabox.lan"
  config.vm.network "private_network", ip: "192.168.56.4"

  config.vm.provision :shell, :inline => <<-SH
    # Set environment variables so that the setup script does
    # not ask any questions during provisioning. We'll let the
    # machine figure out its own public IP.
    export NONINTERACTIVE=1
    export PUBLIC_IP=auto
    export PUBLIC_IPV6=auto
    export PRIMARY_HOSTNAME=auto
    #export SKIP_NETWORK_CHECKS=1

    # Start the setup script.
    cd /vagrant
    setup/start.sh
SH
end


================================================
FILE: api/docs/generate-docs.sh
================================================
#!/usr/bin/env sh

# Requirements:
# - Node.js
# - redoc-cli (`npm install redoc-cli -g`)

redoc-cli bundle ../mailinabox.yml \
  -t template.hbs \
  -o api-docs.html \
  --templateOptions.metaDescription="Mail-in-a-Box HTTP API" \
  --title="Mail-in-a-Box HTTP API" \
  --options.expandSingleSchemaField \
  --options.hideSingleRequestSampleTab \
  --options.jsonSampleExpandLevel=10 \
  --options.hideDownloadButton \
  --options.theme.logo.maxHeight=180px \
  --options.theme.logo.maxWidth=180px \
  --options.theme.colors.primary.main="#C52" \
  --options.theme.typography.fontSize=16px \
  --options.theme.typography.fontFamily="Raleway, sans-serif" \
  --options.theme.typography.headings.fontFamily="Ubuntu, Arial, sans-serif" \
  --options.theme.typography.code.fontSize=15px \
  --options.theme.typography.code.fontFamily='"Source Code Pro", monospace'

================================================
FILE: api/docs/template.hbs
================================================
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf8" />
  <title>{{title}}</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="description" content="{{templateOptions.metaDescription}}" />
  <link rel="icon" type="image/png" href="https://mailinabox.email/static/logo_small.png">
  <link rel="apple-touch-icon" type="image/png" href="https://mailinabox.email/static/logo_small.png">
  <link href="https://fonts.googleapis.com/css?family=Raleway:400,700" rel="stylesheet" />
  <link href="https://fonts.googleapis.com/css?family=Ubuntu:300" rel="stylesheet" />
  <link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" />
  <style>
    body {
      margin: 0;
      padding: 0;
    }

    h1 {
      color: #000 !important;
    }
  </style>
  {{{redocHead}}}
</head>

<body>
  {{{redocHTML}}}
</body>

</html>


================================================
FILE: api/mailinabox.yml
================================================
openapi: 3.0.3
info:
  title: Mail-in-a-Box
  description: |
    Mail-in-a-Box API HTTP specification.

    # Introduction
    This API is documented in [**OpenAPI format**](http://spec.openapis.org/oas/v3.0.3).
    ([View the full HTTP specification](https://raw.githubusercontent.com/mail-in-a-box/mailinabox/api-spec/api/mailinabox.yml).)

    All endpoints are relative to `https://{host}/admin` and are secured with [`Basic Access` authentication](https://en.wikipedia.org/wiki/Basic_access_authentication). If you have multi-factor authentication enabled, authentication with a `user:password` combination will fail unless a valid OTP is supplied via the `x-auth-token` header. Authentication via a `user:user_key` pair is possible without the header being present.
  contact:
    name: Mail-in-a-Box support
    url: https://mailinabox.email/
  license:
    name: CC0 1.0 Universal
    url: https://creativecommons.org/publicdomain/zero/1.0/legalcode
  version: 0.51.0
  x-logo:
    url: https://mailinabox.email/static/logo.png
    altText: Mail-in-a-Box logo
externalDocs:
  description: Find out more about Mail-in-a-box.
  url: https://mailinabox.email/
servers:
  - url: https://{host}/admin
    variables:
      host:
        default: box.example.com
        description: The API hostname.
security:
  - basicAuth: []
tags:
  - name: User
    description: Endpoints related to user authentication.
  - name: Mail
    description: |
      Mail operations, which include getting all users, getting all aliases, adding/updating/removing users and aliases and getting all mail domains.
  - name: DNS
    description: |
      DNS operations, which include adding custom records, adding a secondary nameserver and viewing all DNS records.
  - name: SSL
    description: |
      TLS (SSL) Certificates operations, which include checking certificate status
      and installing custom certificates.
  - name: Web
    description: |
      Static web hosting operations, which include getting domain information and updating domain root directories.
  - name: MFA
    description: |
      Manage multi-factor authentication schemes. Currently, only TOTP is supported.
  - name: System
    description: |
      System operations, which include system status checks, new version checks
      and reboot status.
paths:
  /login:
    post:
      tags:
        - User
      summary: Exchange a username and password for a session API key.
      description: |
        Returns user information and a session API key.

        Authenticate a user by supplying the auth token as a base64 encoded string in
        format `email:password` using basic authentication headers.

        If successful, a long-lived `api_key` is returned which can be used for subsequent
        requests to the API in place of the password.
      operationId: login
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/login" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MeResponse'
              examples:
                invalid:
                  value:
                    reason: Incorrect username or password
                    status: invalid
                ok:
                  value:
                    api_key: 1a2b3c4d5e6f7g8h9i0j
                    email: user@example.com
                    privileges:
                      - admin
                    status: ok
  /logout:
    post:
      tags:
        - User
      summary: Invalidates a session API key.
      description: |
        Invalidates a session API key so that it cannot be used after this API call.
      operationId: logout
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/logout" \
              -u "<email>:<session_key>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LogoutResponse'
  /system/status:
    post:
      tags:
        - System
      summary: Get system status
      description: |
        Returns an array of statuses which can include headings.
      operationId: getSystemStatus
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/system/status" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemStatusResponse'
              example:
                - type: heading
                  text: System
                  extra: []
                - type: warning
                  text: This domain's DNSSEC DS record is not set
                  extra:
                    - monospace: false
                      text: 'Digest Type: 2 / SHA-25'
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /system/version:
    get:
      tags:
        - System
      summary: Get system version
      description: Returns installed Mail-in-a-Box version.
      operationId: getSystemVersion
      x-codeSamples:
        - lang: curl
          source: |
            curl -X GET "https://{host}/admin/system/version" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/SystemVersionResponse'
              example: v0.46
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /system/latest-upstream-version:
    post:
      tags:
        - System
      summary: Get system upstream version
      description: Returns Mail-in-a-Box upstream version.
      operationId: getSystemUpstreamVersion
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/system/latest-upstream-version" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/SystemVersionUpstreamResponse'
              example: v0.47
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /system/updates:
    get:
      tags:
        - System
      summary: Get system updates
      description: Returns system (apt) updates.
      operationId: getSystemUpdates
      x-codeSamples:
        - lang: curl
          source: |
            curl -X GET "https://{host}/admin/system/updates" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/SystemUpdatesResponse'
              example: |
                libgnutls30 (3.5.18-1ubuntu1.4)
                libxau6 (1:1.0.8-1ubuntu1)
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /system/update-packages:
    post:
      tags:
        - System
      summary: Update system packages
      description: Updates system (apt) packages.
      operationId: updateSystemPackages
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/system/update-packages" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/SystemUpdatePackagesResponse'
              example: |
                Calculating upgrade...
                The following packages will be upgraded:
                  cloud-init grub-common
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /system/privacy:
    get:
      tags:
        - System
      summary: Get system privacy status
      description: |
        Returns system privacy (new-version check) status.

        Response:

          - `true`: Private, new-version checks will not be performed
          - `false`: Not private, new-version checks will be performed
      operationId: getSystemPrivacyStatus
      x-codeSamples:
        - lang: curl
          source: |
            curl -X GET "https://{host}/admin/system/privacy" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemPrivacyStatusResponse'
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
    post:
      tags:
        - System
      summary: Update system privacy
      description: |
        Updates system privacy (new-version checks).

        Request:

          - `value: private`: Disable new version checks
          - `value: off`: Enable new version checks
      operationId: updateSystemPrivacy
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/SystemPrivacyUpdateRequest'
            examples:
              enable:
                summary: Enable new version checks
                value:
                  value: 'off'
              disable:
                summary: Disable new version checks
                value:
                  value: private
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/system/privacy" \
              -d "value=<string>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/SystemPrivacyUpdateResponse'
              example: OK
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /system/reboot:
    get:
      tags:
        - System
      summary: Get system reboot status
      description: |
        Returns the system reboot status.

        Response:

          - `true`: A reboot is required
          - `false`: A reboot is not required
      operationId: getSystemRebootStatus
      x-codeSamples:
        - lang: curl
          source: |
            curl -X GET "https://{host}/admin/system/reboot" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemRebootStatusResponse'
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
    post:
      tags:
        - System
      summary: Reboot system
      description: Reboots the system.
      operationId: rebootSystem
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/system/reboot" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/SystemRebootResponse'
              example: No reboot is required, so it is not allowed.
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /system/backup/status:
    get:
      tags:
        - System
      summary: Get system backup status
      description: |
        Returns the system backup status.

        If the list of backups is empty, this implies no backups have been made yet.
      operationId: getSystemBackupStatus
      x-codeSamples:
        - lang: curl
          source: |
            curl -X GET "https://{host}/admin/system/backup/status" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemBackupStatusResponse'
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /system/backup/config:
    get:
      tags:
        - System
      summary: Get system backup config
      description: Returns the system backup config.
      operationId: getSystemBackupConfig
      x-codeSamples:
        - lang: curl
          source: |
            curl -X GET "https://{host}/admin/system/backup/config" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemBackupConfigResponse'
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
    post:
      tags:
        - System
      summary: Update system backup config
      description: Updates the system backup config.
      operationId: updateSystemBackupConfig
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/SystemBackupConfigUpdateRequest'
            examples:
              s3:
                summary: S3 backup
                value:
                  target: s3://s3.eu-central-1.amazonaws.com/box-example-com
                  target_user: ACCESS_KEY
                  target_pass: SECRET_ACCESS_KEY
                  minAge: 3
              local:
                summary: Local backup
                value:
                  target: local
                  target_user: ''
                  target_pass: ''
                  minAge: 3
              rsync:
                summary: Rsync backup
                value:
                  target: rsync://username@box.example.com//backups/box.example.com
                  target_user: ''
                  target_pass: ''
                  minAge: 3
              off:
                summary: Disable backups
                value:
                  target: 'off'
                  target_user: ''
                  target_pass: ''
                  minAge: 0
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/system/backup/config" \
              -d "target=<hostname>" \
              -d "target_user=<string>" \
              -d "target_pass=<password>" \
              -d "min_age=<integer>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/SystemBackupConfigUpdateResponse'
              example: OK
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /ssl/status:
    get:
      tags:
        - SSL
      summary: Get SSL status
      description: Returns the SSL status for all domains.
      operationId: getSSLStatus
      x-codeSamples:
        - lang: curl
          source: |
            curl -X GET "https://{host}/admin/ssl/status" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SSLStatusResponse'
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /ssl/csr/{domain}:
    post:
      tags:
        - SSL
      summary: Generate SSL CSR
      description: |
        Generates a Certificate Signing Request (CSR) for a domain & country code.
      operationId: generateSSLCSR
      parameters:
        - in: path
          name: domain
          schema:
            $ref: '#/components/schemas/Hostname'
          required: true
          description: Domain to generate CSR for.
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/SSLCSRGenerateRequest'
            example:
              countrycode: 'GB'
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/ssl/csr/<hostname>" \
              -d "countrycode=<string>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/SSLCSRGenerateResponse'
              example: |
                -----BEGIN CERTIFICATE REQUEST-----
                MIICaDCCAVACAQAwIzELMAkGA1UEBhMCQlMxFDASBgNVBAMMC2V4YW1wbGUuY29t
                ...
                JmFDQESSfUxLPHLC660Wnf3GmrP/duZHpPC+qTe8b1AlQ7zDT3cOaAQ+Mb0=
                -----END CERTIFICATE REQUEST-----
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /ssl/install:
    post:
      tags:
        - SSL
      summary: Install SSL certificate
      description: |
        Installs a custom certificate. The chain certificate is optional.
      operationId: installSSLCertificate
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/SSLCertificateInstallRequest'
            example:
              domain: example.com
              cert: CERT_STRING
              chain: CHAIN_STRING
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/ssl/install" \
              -d "domain=<hostname>" \
              -d "cert=<string>" \
              -d "chain=<string>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/SSLCertificateInstallResponse'
              example: OK
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /ssl/provision:
    post:
      tags:
        - SSL
      summary: Provision SSL certificates
      description: |
        Provisions certificates for all domains.
      operationId: provisionSSLCertificates
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/ssl/provision" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SSLCertificatesProvisionResponse'
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /dns/secondary-nameserver:
    get:
      tags:
        - DNS
      summary: Get DNS secondary nameserver
      description: |
        Returns a list of nameserver hostnames.
      operationId: getDnsSecondaryNameserver
      x-codeSamples:
        - lang: curl
          source: |
            curl -X GET "https://{host}/admin/dns/secondary-nameserver" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DNSSecondaryNameserverResponse'
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
    post:
      tags:
        - DNS
      summary: Add DNS secondary nameserver
      description: |
        Adds one or more secondary nameservers.
      operationId: addDnsSecondaryNameserver
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/DNSSecondaryNameserverAddRequest'
            example:
              hostnames: ns2.hostingcompany.com, ns3.hostingcompany.com
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/dns/secondary-nameserver" \
              -d "hostnames=<string>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/DNSSecondaryNameserverAddResponse'
              example: 'updated DNS: example.com'
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
                example: Could not resolve the IP address of badhostname
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /dns/zones:
    get:
      tags:
        - DNS
      summary: Get DNS zones
      description: Returns an array of all managed top-level domains.
      operationId: getDnsZones
      x-codeSamples:
        - lang: curl
          source: |
            curl -X GET "https://{host}/admin/dns/zones" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DNSZonesResponse'
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /dns/zonefile/{zone}:
    parameters:
      - in: path
        name: zone
        schema:
          $ref: '#/components/schemas/Hostname'
        required: true
        description: Hostname
    get:
      tags:
        - DNS
      summary: Get DNS zonefile
      description: Returns a DNS zone file for a hostname.
      operationId: getDnsZonefile
      x-codeSamples:
        - lang: curl
          source: |
            curl -X GET "https://{host}/admin/dns/zonefile/<zone>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DNSZonefileResponse'
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /dns/update:
    post:
      tags:
        - DNS
      summary: Update DNS
      description: Updates the DNS. Involves creating zone files and restarting `nsd`.
      operationId: updateDns
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/DNSUpdateRequest'
            example:
              force: 1
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/dns/update" \
              -d "force=<integer>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DNSUpdateResponse'
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /dns/custom:
    get:
      tags:
        - DNS
      summary: Get DNS custom records
      description: Returns all custom DNS records.
      operationId: getDnsCustomRecords
      x-codeSamples:
        - lang: curl
          source: |
            curl -X GET "https://{host}/admin/dns/custom" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DNSCustomRecordsResponse'
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /dns/custom/{qname}/{rtype}:
    parameters:
      - in: path
        name: qname
        schema:
          $ref: '#/components/schemas/Hostname'
        required: true
        description: DNS record query name
      - in: path
        name: rtype
        schema:
          $ref: '#/components/schemas/DNSRecordType'
        required: true
        description: Record type
    get:
      tags:
        - DNS
      summary: Get DNS custom records
      description: Returns all custom records for the specified query name and type.
      operationId: getDnsCustomRecordsForQNameAndType
      x-codeSamples:
        - lang: curl
          source: |
            curl -X GET "https://{host}/admin/dns/custom/<qname>/<rtype>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DNSCustomRecordsResponse'
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
    post:
      tags:
        - DNS
      summary: Add DNS custom record
      description: Adds a custom DNS record for the specified query name and type.
      operationId: addDnsCustomRecord
      requestBody:
        $ref: '#/components/requestBodies/DNSCustomRecordRequest'
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/dns/custom/<qname>/<rtype>" \
              -H "Content-Type: text/plain" \
              --data-raw "<string>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/DNSCustomRecordUpsertResponse'
              example: 'updated DNS: example.com'
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
                example: "'badhostname' does not appear to be an IPv4 or IPv6 address"
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
    put:
      tags:
        - DNS
      summary: Update DNS custom record
      description: Updates an existing DNS custom record value for the specified qname and type.
      operationId: updateDnsCustomRecord
      requestBody:
        $ref: '#/components/requestBodies/DNSCustomRecordRequest'
      x-codeSamples:
        - lang: curl
          source: |
            curl -x PUT "https://{host}/admin/dns/custom/<qname>/<rtype>" \
              -H "Content-Type: text/plain" \
              --data-raw "<string>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/DNSCustomRecordUpsertResponse'
              example: 'updated DNS: example.com'
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
                example: "'badhostname' does not appear to be an IPv4 or IPv6 address"
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
    delete:
      tags:
        - DNS
      summary: Remove DNS custom record
      description: Removes a DNS custom record for the specified domain, type & value.
      operationId: removeDnsCustomRecord
      requestBody:
        $ref: '#/components/requestBodies/DNSCustomRecordRequest'
      x-codeSamples:
        - lang: curl
          source: |
            curl -X DELETE "https://{host}/admin/dns/custom/<qname>/<rtype>" \
              -H "Content-Type: text/plain" \
              --data-raw "<string>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/DNSCustomRecordRemoveResponse'
              example: 'updated DNS: example.com'
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
                example: badhostname is not a domain name or a subdomain of a domain name managed by this box
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /dns/custom/{qname}:
    parameters:
      - in: path
        name: qname
        schema:
          $ref: '#/components/schemas/Hostname'
        required: true
        description: DNS query name.
    get:
      tags:
        - DNS
      summary: Get DNS custom A records
      description: Returns all custom A records for the specified query name.
      operationId: getDnsCustomARecordsForQName
      x-codeSamples:
        - lang: curl
          source: |
            curl -X GET "https://{host}/admin/dns/custom/<qname>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DNSCustomRecordsResponse'
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
    post:
      tags:
        - DNS
      summary: Add DNS custom A record
      description: Adds a custom DNS A record for the specified query name.
      operationId: addDnsCustomARecord
      requestBody:
        $ref: '#/components/requestBodies/DNSCustomRecordRequest'
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/dns/custom/<qname>" \
              -H "Content-Type: text/plain" \
              --data-raw "<string>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/DNSCustomRecordUpsertResponse'
              example: 'updated DNS: example.com'
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
                example: "'badhostname' does not appear to be an IPv4 or IPv6 address"
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
    put:
      tags:
        - DNS
      summary: Update DNS custom A record
      description: Updates an existing DNS custom A record value for the specified qname.
      operationId: updateDnsCustomARecord
      requestBody:
        $ref: '#/components/requestBodies/DNSCustomRecordRequest'
      x-codeSamples:
        - lang: curl
          source: |
            curl -x PUT "https://{host}/admin/dns/custom/<qname>" \
              -H "Content-Type: text/plain" \
              --data-raw "<string>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/DNSCustomRecordUpsertResponse'
              example: 'updated DNS: example.com'
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
                example: "'badhostname' does not appear to be an IPv4 or IPv6 address"
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
    delete:
      tags:
        - DNS
      summary: Remove DNS custom A record
      description: Removes a DNS custom A record for the specified domain & value.
      operationId: removeDnsCustomARecord
      requestBody:
        $ref: '#/components/requestBodies/DNSCustomRecordRequest'
      x-codeSamples:
        - lang: curl
          source: |
            curl -X DELETE "https://{host}/admin/dns/custom/<qname>" \
              -H "Content-Type: text/plain" \
              --data-raw "<string>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/DNSCustomRecordRemoveResponse'
              example: 'updated DNS: example.com'
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
                example: badhostname is not a domain name or a subdomain of a domain name managed by this box
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /dns/dump:
    get:
      tags:
        - DNS
      summary: Get DNS dump
      description: Returns all DNS records.
      operationId: getDnsDump
      x-codeSamples:
        - lang: curl
          source: |
            curl -X GET "https://{host}/admin/dns/dump" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DNSDumpResponse'
              example:
                - - example1.com
                  - - explanation: Required. Specifies the hostname (and priority) of the machine that handles @example.com mail.
                      qname: example1.com
                      rtype: MX
                      value: 10 box.example1.com.
                - - example2.com
                  - - explanation: Required. Specifies the hostname (and priority) of the machine that handles @example.com mail.
                      qname: example2.com
                      rtype: MX
                      value: 10 box.example2.com.
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /mail/users:
    get:
      tags:
        - Mail
      summary: Get mail users
      description: Returns all mail users.
      operationId: getMailUsers
      parameters:
        - in: query
          name: format
          schema:
            $ref: '#/components/schemas/MailUsersResponseFormat'
          description: The format of the response.
      x-codeSamples:
        - lang: curl
          source: |
            curl -X GET "https://{host}/admin/mail/users?format=<string>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MailUsersResponse'
            text/html:
              schema:
                $ref: '#/components/schemas/MailUsersSimpleResponse'
              example: |
                user1@example.com
                user2@example.com
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /mail/users/add:
    post:
      tags:
        - Mail
      summary: Add mail user
      description: Adds a new mail user.
      operationId: addMailUser
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/MailUserAddRequest'
            examples:
              normal:
                summary: Normal user
                value:
                  email: user@example.com
                  password: s3curE_pa5Sw0rD
                  privileges: ''
              admin:
                summary: Admin user
                value:
                  email: user@example.com
                  password: s3curE_pa5Sw0rD
                  privileges: admin
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/mail/users/add" \
              -d "email=<email>" \
              -d "password=<password>" \
              -d "privileges=<string>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/MailUserAddResponse'
              example: |
                mail user added
                updated DNS: OpenDKIM configuration
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
                example: Invalid email address
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /mail/users/remove:
    post:
      tags:
        - Mail
      summary: Remove mail user
      description: Removes an existing mail user.
      operationId: removeMailUser
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/MailUserRemoveRequest'
            example:
              email: user@example.com
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/mail/users/remove" \
              -d "email=<email>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/MailUserRemoveResponse'
              example: OK
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
                example: That's not a user (invalid@example.com)
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /mail/users/privileges/add:
    post:
      tags:
        - Mail
      summary: Add mail user privilege
      description: Adds a privilege to an existing mail user.
      operationId: addMailUserPrivilege
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/MailUserAddPrivilegeRequest'
            example:
              email: user@example.com
              privilege: admin
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/mail/users/privileges/add" \
              -d "email=<email>" \
              -d "privilege=<string>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/MailUserAddPrivilegeResponse'
              example: OK
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
                example: That's not a user (invalid@example.com)
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /mail/users/privileges/remove:
    post:
      tags:
        - Mail
      summary: Remove mail user privilege
      description: Removes a privilege from an existing mail user.
      operationId: removeMailUserPrivilege
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/MailUserRemovePrivilegeRequest'
            example:
              email: user@example.com
              privilege: admin
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/mail/users/privileges/remove" \
              -d "email=<email>" \
              -d "privilege=<string>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/MailUserRemovePrivilegeResponse'
              example: OK
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
                example: That's not a user (invalid@example.com)
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /mail/users/password:
    post:
      tags:
        - Mail
      summary: Set mail user password
      description: Sets a password for an existing mail user.
      operationId: setMailUserPassword
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/MailUserSetPasswordRequest'
            example:
              email: user@example.com
              password: s3curE_pa5Sw0rD
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/mail/users/password" \
              -d "email=<email>" \
              -d "password=<password>" \
              -u "<email>:<password>" \
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/MailUserSetPasswordResponse'
              example: OK
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
                example: Passwords must be at least eight characters
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /mail/users/privileges:
    get:
      tags:
        - Mail
      summary: Get mail user privileges
      description: Returns all privileges for an existing mail user.
      operationId: getMailUserPrivileges
      parameters:
        - in: query
          name: email
          schema:
            $ref: '#/components/schemas/Email'
          description: The email you want to get privileges for.
      x-codeSamples:
        - lang: curl
          source: |
            curl -X GET "https://{host}/admin/mail/users/privileges?email=<email>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/MailUserPrivilegesResponse'
              example: admin
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /mail/domains:
    get:
      tags:
        - Mail
      summary: Get mail domains
      description: Returns all mail domains.
      operationId: getMailDomains
      x-codeSamples:
        - lang: curl
          source: |
            curl -X GET "https://{host}/admin/mail/domains" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/MailDomainsResponse'
              example: |
                example1.com
                example2.com
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /mail/aliases:
    get:
      tags:
        - Mail
      summary: Get mail aliases
      description: Returns all mail aliases.
      operationId: getMailAliases
      parameters:
        - in: query
          name: format
          schema:
            $ref: '#/components/schemas/MailAliasesResponseFormat'
          description: The format of the response.
      x-codeSamples:
        - lang: curl
          source: |
            curl -X GET "https://{host}/admin/mail/aliases?format=<string>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/MailAliasByDomain'
            text/html:
              schema:
                $ref: '#/components/schemas/MailAliasesSimpleResponse'
                example: |
                  abuse@example.com	administrator@example.com
                  admin@example.com	administrator@example.com
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /mail/aliases/add:
    post:
      tags:
        - Mail
      summary: Upsert mail alias
      description: |
        Adds or updates a mail alias. If updating, you need to set `update_if_exists: 1`.
      operationId: upsertMailAlias
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/MailAliasUpsertRequest'
            examples:
              regular:
                summary: Regular alias
                value:
                  update_if_exists: 0
                  address: user@example.com
                  forwards_to: user2@example.com
                  permitted_senders:
              catchall:
                summary: Catch-all
                value:
                  update_if_exists: 0
                  address: '@example.com'
                  forwards_to: user@otherexample.com
                  permitted_senders:
              domainalias:
                summary: Domain alias
                value:
                  update_if_exists: 0
                  address: '@example.com'
                  forwards_to: '@otherexample.com'
                  permitted_senders:
              update:
                summary: Update existing alias
                value:
                  update_if_exists: 1
                  address: user@example.com
                  forwards_to: user2@example.com
                  permitted_senders: user3@example.com, user4@example.com
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/mail/aliases/add" \
              -d "update_if_exists=<integer>" \
              -d "address=<email>" \
              -d "forwards_to=<string>" \
              -d "permitted_senders=<string>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/MailAliasUpsertResponse'
              example: alias updated
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
                example: Invalid email address (invalid@example.com)
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /mail/aliases/remove:
    post:
      tags:
        - Mail
      summary: Remove mail alias
      description: Removes a mail alias.
      operationId: removeMailAlias
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/MailAliasRemoveRequest'
            example:
              address: user@example.com
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/mail/aliases/remove" \
              -d "address=<email>" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/MailAliasRemoveResponse'
              example: alias removed
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
                example: That's not an alias (invalid@example)
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /web/domains:
    get:
      tags:
        - Web
      summary: Get web domains
      description: Returns all static web domains.
      operationId: getWebDomains
      x-codeSamples:
        - lang: curl
          source: |
            curl -X GET "https://{host}/admin/web/domains" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/WebDomain'
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /web/update:
    post:
      tags:
        - Web
      summary: Update web
      description: Updates static websites, used for updating domain root directories.
      operationId: updateWeb
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/web/update" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/WebUpdateResponse'
              example: web updated
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /mfa/status:
    post:
      tags:
        - MFA
      summary: Retrieve MFA status for you or another user
      description: Retrieves which type of MFA is used and configuration
      operationId: mfaStatus
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/mfa/status" \
              -u "<email>:<password>"
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MfaStatusResponse'
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /mfa/totp/enable:
    post:
      tags:
        - MFA
      summary: Enable TOTP authentication
      description: Enables TOTP authentication for the currently logged-in admin user
      operationId: mfaTotpEnable
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/mfa/totp/enable" \
              -d "code=123456" \
              -d "secret=<string>" \
              -u "<email>:<password>"
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/MfaEnableRequest'
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/MfaEnableSuccessResponse'
        400:
          description: Bad request
          content:
            text/html:
              schema:
                type: string
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
  /mfa/disable:
    post:
      tags:
        - MFA
      summary: Disable multi-factor authentication for you or another user
      description: Disables multi-factor authentication for the currently logged-in admin user or another user if a 'user' parameter is submitted. Either disables all multi-factor authentication methods or the method corresponding to the optional property `mfa_id`.
      operationId: mfaTotpDisable
      requestBody:
        required: false
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/MfaDisableRequest'
      x-codeSamples:
        - lang: curl
          source: |
            curl -X POST "https://{host}/admin/mfa/totp/disable" \
              -u "<email>:<user_key>"
      responses:
        200:
          description: Successful operation
          content:
            text/html:
              schema:
                $ref: '#/components/schemas/MfaDisableSuccessResponse'
        403:
          description: Forbidden
          content:
            text/html:
              schema:
                type: string
components:
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic
      description: |
        Credentials can be supplied using the `Authorization` header in
        format `Authorization: Basic {access-token}`.

        The `access-token` is comprised of the Base64 encoding of `username:password`.
        The `username` is the mail user's email address, and `password` can either be the mail user's
        password, or the `api_key` returned from the `login` operation.

        When using `curl`, you can supply user credentials using the `-u` or `--user` parameter.
  requestBodies:
    DNSCustomRecordRequest:
      required: true
      content:
        text/plain:
          schema:
            type: string
            example: '1.2.3.4'
            description: The value of the DNS record.
          example: '1.2.3.4'
  schemas:
    MailUsersResponseFormat:
      type: string
      enum:
        - text
        - json
      example: json
      description: Response format (`application/json` or `text/html`).
    MailAliasesResponseFormat:
      type: string
      enum:
        - text
        - json
      example: json
      description: Response format (`application/json` or `text/html`).
    MailUserSetPasswordResponse:
      type: string
      example: OK
      description: Mail user set password response.
    MailUserRemoveResponse:
      type: string
      example: OK
      description: Mail user remove response.
    MailUserAddResponse:
      type: string
      example: |
        mail user added
        updated DNS: OpenDKIM configuration
      description: |
        Mail user add response.

        Can include information about operations related to adding new users, like updating DNS.
    MailUserAddPrivilegeResponse:
      type: string
      example: OK
      description: Mail user add admin privilege response.
    MailUserRemovePrivilegeResponse:
      type: string
      example: OK
      description: Mail user remove admin privilege response.
    MailUsersSimpleResponse:
      type: string
      example: |
        user1@example.com
        user2@example.com
      description: Get mail users text format response.
    MailUserPrivilegesResponse:
      $ref: '#/components/schemas/MailUserPrivilege'
      description: Mail user privileges response.
      example: admin
    MailDomainsResponse:
      type: string
      example: |
        example1.com
        example2.com
      description: Mail domains response.
    MailUsersResponse:
      type: array
      items:
        $ref: '#/components/schemas/MailUserByDomain'
      description: Get mail aliases JSON format response.
    MailUserByDomain:
      type: object
      required:
        - domain
        - users
      properties:
        domain:
          $ref: '#/components/schemas/Hostname'
        users:
          type: array
          items:
            $ref: '#/components/schemas/MailUser'
      description: Mail users by domain.
    MailUser:
      type: object
      required:
        - email
        - privileges
        - status
      properties:
        email:
          $ref: '#/components/schemas/Email'
        privileges:
          type: array
          items:
            $ref: '#/components/schemas/MailUserPrivilege'
        status:
          $ref: '#/components/schemas/MailUserStatus'
        mailbox:
          type: string
          example: /home/user-data/mail/mailboxes/example.com/user
      description: Mail user details.
    MailAliasesSimpleResponse:
      type: string
      example: |
        abuse@example.com	administrator@example.com
        admin@example.com	administrator@example.com
      description: Get mail aliases text format response.
    MailAliasByDomain:
      type: object
      required:
        - domain
        - aliases
      properties:
        domain:
          $ref: '#/components/schemas/Hostname'
        aliases:
          type: array
          items:
            $ref: '#/components/schemas/MailAlias'
      description: Mail aliases by domain.
    MailAlias:
      type: object
      required:
        - address
        - address_display
        - forwards_to
        - permitted_senders
        - required
      properties:
        address:
          $ref: '#/components/schemas/Email'
        address_display:
          $ref: '#/components/schemas/Email'
        forwards_to:
          type: array
          items:
            $ref: '#/components/schemas/Email'
        permitted_senders:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/Email'
        required:
          type: boolean
          example: true
      description: Mail alias details.
    MailAliasUpsertResponse:
      type: string
      example: alias updated
      description: Mail alias add/update response.
    MailAliasUpsertRequest:
      type: object
      required:
        - update_if_exists
        - address
        - forwards_to
        - permitted_senders
      properties:
        update_if_exists:
          type: integer
          format: int32
          minimum: 0
          maximum: 1
          example: 1
          description: Set to `1` when updating an alias.
        address:
          $ref: '#/components/schemas/Email'
        forwards_to:
          type: string
          example: user1@example.com, user2@example.com
          description: |
            If adding a regular or catch-all alias, the format needs to be `user@example.com`.
            Multiple address can be separated by newlines or commas.

            If adding a domain alias, the format needs to be `@example.com`.
        permitted_senders:
          type: string
          nullable: true
          example: user1@example.com, user2@example.com
          description: |
            Mail users that can send mail claiming to be from any address on the alias domain.
            Multiple address can be separated by newlines or commas.

            Leave empty to allow any mail user listed in `forwards_to` to send mail claiming to be from any address on the alias domain.
      description: Mail alias upsert request.
    MailAliasRemoveResponse:
      type: string
      example: alias removed
      description: Mail alias remove response.
    MailAliasRemoveRequest:
      type: object
      required:
        - address
      properties:
        address:
          $ref: '#/components/schemas/Email'
      description: Mail aliases remove request.
    DNSRecordType:
      enum:
        - A
        - AAAA
        - CAA
        - CNAME
        - TXT
        - MX
        - SRV
        - SSHFP
        - NS
      example: MX
      description: DNS record type.
    DNSDumpResponse:
      type: array
      items:
        $ref: '#/components/schemas/DNSDumpDomains'
      description: DNS dump response.
    DNSDumpDomains:
      type: array
      items:
        oneOf:
          - $ref: '#/components/schemas/Hostname'
          - $ref: '#/components/schemas/DNSDumpDomainRecords'
      description: |
        A list of records per domain.

        The first item in the list is the domain and the second item is the list of records.
    DNSDumpDomainRecords:
      type: array
      items:
        $ref: '#/components/schemas/DNSDumpDomainRecord'
      description: List of domain records.
    DNSDumpDomainRecord:
      type: object
      required:
        - explanation
        - qname
        - type
        - value
      properties:
        explanation:
          type: string
          example: Required. Specifies the hostname (and priority) of the machine that handles @example.com mail
        qname:
          $ref: '#/components/schemas/Hostname'
        rtype:
          $ref: '#/components/schemas/DNSRecordType'
        value:
          type: string
          example: 10 example.com.
      description: Domain DNS record details.
    DNSCustomRecord:
      type: object
      required:
        - qname
        - rtype
        - value
      properties:
        qname:
          $ref: '#/components/schemas/Hostname'
        rtype:
          $ref: '#/components/schemas/DNSRecordType'
        value:
          type: string
          example: 10 example.com.
      description: Custom DNS record detail detail.
    DNSCustomRecordsResponse:
      type: array
      items:
        $ref: '#/components/schemas/DNSCustomRecord'
      description: Custom DNS records response.
    DNSZonesResponse:
      type: array
      items:
        $ref: '#/components/schemas/Hostname'
      description: DNS zones response.
    DNSZonefileResponse:
      type: string
    DNSSecondaryNameserverResponse:
      type: object
      required:
        - hostnames
      properties:
        hostnames:
          type: array
          items:
            type: string
            example: ns1.example.com
      description: Secondary nameserver/s response.
    DNSCustomRecordRemoveResponse:
      type: string
      example: 'updated DNS: example.com'
      description: Custom DNS record remove response.
    DNSCustomRecordUpsertResponse:
      type: string
      example: 'updated DNS: example.com'
      description: Custom DNS record add response.
    DNSUpdateRequest:
      type: object
      required:
        - force
      properties:
        force:
          type: integer
          format: int32
          minimum: 0
          maximum: 1
          example: 1
          description: Force an update even if mailinabox detects no changes are required.
      description: DNS update request.
    DNSUpdateResponse:
      type: string
      example: |
        updated DNS: example1.com,example2.com
      description: DNS update response.
    DNSSecondaryNameserverAddRequest:
      type: object
      required:
        - hostnames
      properties:
        hostnames:
          type: string
          description: Hostnames separated with commas or spaces.
          example: ns2.hostingcompany.com, ns3.hostingcompany.com
      description: Secondary nameserver/s add request.
    DNSSecondaryNameserverAddResponse:
      type: string
      example: 'updated DNS: example.com'
      description: Secondary nameserver/s add response.
    SystemPrivacyUpdateRequest:
      type: object
      required:
        - value
      properties:
        value:
          $ref: '#/components/schemas/SystemPrivacyStatus'
      description: Update system privacy request.
    SystemPrivacyStatus:
      type: string
      enum:
        - private
        - 'off'
      example: private
      description: System privacy status.
    MailUserSetPasswordRequest:
      type: object
      required:
        - email
        - password
      properties:
        email:
          $ref: '#/components/schemas/Email'
        password:
          type: string
          format: password
      description: Mail user set password request.
    MailUserAddRequest:
      type: object
      required:
        - email
        - password
        - privileges
      properties:
        email:
          $ref: '#/components/schemas/Email'
        password:
          type: string
          format: password
        privileges:
          $ref: '#/components/schemas/MailUserPrivilege'
      description: Mail user add request.
    MailUserRemoveRequest:
      type: object
      required:
        - email
      properties:
        email:
          $ref: '#/components/schemas/Email'
      description: Mail user remove request.
    MailUserStatus:
      type: string
      enum:
        - active
        - inactive
      example: active
      description: Mail user status.
    MailUserPrivilege:
      type: string
      enum:
        - admin
        - ''
      example: admin
      description: Mail user privilege.
    MailUserAddPrivilegeRequest:
      type: object
      required:
        - email
        - privilege
      properties:
        email:
          $ref: '#/components/schemas/Email'
        privilege:
          $ref: '#/components/schemas/MailUserPrivilege'
      description: Mail user add privilege request.
    MailUserRemovePrivilegeRequest:
      type: object
      required:
        - email
        - privilege
      properties:
        email:
          $ref: '#/components/schemas/Email'
        privilege:
          $ref: '#/components/schemas/MailUserPrivilege'
      description: Mail user remove privilege request.
    SSLCSRGenerateRequest:
      type: object
      required:
        - countrycode
      properties:
        countrycode:
          type: string
          example: GB
      description: Generate SSL CSR request.
    SSLCSRGenerateResponse:
      type: string
      example: |
        -----BEGIN CERTIFICATE REQUEST-----
        MIICaDCCAVACAQAwIzELMAkGA1UEBhMCQlMxFDASBgNVBAMMC2V4YW1wbGUuY29t
        MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3K6dwLM2Nk8kVhIBaZmp
        eY6y7O0T3jrexEKlW839TVYdcH+K35V1NxilbMFKMuHeowGwFyyiqOy/OUYNeq+T
        Rz3s4b1qG2p01dwlsXHHYmXLYTAhvqvY+CU5ksieuZbyHRTwbHViQ0xtRXwoVCnj
        CkN7kJVpkLfVN0/BG6NBFpv/JI8F+hwp+IHdkC1gUXRrLJNC79ERqFP8HoqdQWNw
        OGGFaOe2aQhvj2zt8wFncyKVc40UKVbSzGGzdL2MPiAJHgZ2lmeY1xDyX1lOt12R
        IFPwtxmbxaxYaVfe2hxl7m88xV3OjYcKgwVYDusk2XJ37cGew5g+NbBvzEeEUpF9
        5wIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAD7UPC3/Nkgpn53mT9puUonYdJg9
        SD8vvTK/N78CzoEgPNyq+bYbqlcvVPKIdItf9TMiqfOSvW3e3NvkRisYle8Qp+0C
        8pafXBvQ9eHt5CFeJn4sH9GnxeflOZT/P9Jnp71KtZQvOobirX4GgEWs79g+/NHb
        Zyf8rbadt9HruNhKA5nlP8cn7Rdc/iuJU8MVSQszI1s1DEcXMPxr6iqb2g87/ifH
        lWcK59kvRJkCcPhPzjpUy9NulucH4WFA/WqKeDNFS/oC+upV5w8EDEcfnenJFG+N
        JmFDQESSfUxLPHLC660Wnf3GmrP/duZHpPC+qTe8b1AlQ7zDT3cOaAQ+Mb0=
        -----END CERTIFICATE REQUEST-----
      description: Generate SSL CSR response.
    SSLCertificateInstallRequest:
      type: object
      required:
        - domain
        - cert
        - chain
      properties:
        domain:
          $ref: '#/components/schemas/Hostname'
        cert:
          type: string
          description: TLS/SSL certificate.
          example: |
            -----BEGIN CERTIFICATE-----
            MIICaDCCAVACAQAwIzELMAkGA1UEBhMCQlMxFDASBgNVBAMMC2V4YW1wbGUuY29t
            MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3K6dwLM2Nk8kVhIBaZmp
            eY6y7O0T3jrexEKlW839TVYdcH+K35V1NxilbMFKMuHeowGwFyyiqOy/OUYNeq+T
            Rz3s4b1qG2p01dwlsXHHYmXLYTAhvqvY+CU5ksieuZbyHRTwbHViQ0xtRXwoVCnj
            CkN7kJVpkLfVN0/BG6NBFpv/JI8F+hwp+IHdkC1gUXRrLJNC79ERqFP8HoqdQWNw
            OGGFaOe2aQhvj2zt8wFncyKVc40UKVbSzGGzdL2MPiAJHgZ2lmeY1xDyX1lOt12R
            IFPwtxmbxaxYaVfe2hxl7m88xV3OjYcKgwVYDusk2XJ37cGew5g+NbBvzEeEUpF9
            5wIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAD7UPC3/Nkgpn53mT9puUonYdJg9
            SD8vvTK/N78CzoEgPNyq+bYbqlcvVPKIdItf9TMiqfOSvW3e3NvkRisYle8Qp+0C
            8pafXBvQ9eHt5CFeJn4sH9GnxeflOZT/P9Jnp71KtZQvOobirX4GgEWs79g+/NHb
            Zyf8rbadt9HruNhKA5nlP8cn7Rdc/iuJU8MVSQszI1s1DEcXMPxr6iqb2g87/ifH
            lWcK59kvRJkCcPhPzjpUy9NulucH4WFA/WqKeDNFS/oC+upV5w8EDEcfnenJFG+N
            JmFDQESSfUxLPHLC660Wnf3GmrP/duZHpPC+qTe8b1AlQ7zDT3cOaAQ+Mb0=
            -----END CERTIFICATE-----
        chain:
          type: string
          description: TLS/SSL intermediate chain (if provided, else empty string).
          example: |
            -----BEGIN CERTIFICATE-----
            MIICaDCCAVACAQAwIzELMAkGA1UEBhMCQlMxFDASBgNVBAMMC2V4YW1wbGUuY29t
            MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3K6dwLM2Nk8kVhIBaZmp
            eY6y7O0T3jrexEKlW839TVYdcH+K35V1NxilbMFKMuHeowGwFyyiqOy/OUYNeq+T
            Rz3s4b1qG2p01dwlsXHHYmXLYTAhvqvY+CU5ksieuZbyHRTwbHViQ0xtRXwoVCnj
            CkN7kJVpkLfVN0/BG6NBFpv/JI8F+hwp+IHdkC1gUXRrLJNC79ERqFP8HoqdQWNw
            OGGFaOe2aQhvj2zt8wFncyKVc40UKVbSzGGzdL2MPiAJHgZ2lmeY1xDyX1lOt12R
            IFPwtxmbxaxYaVfe2hxl7m88xV3OjYcKgwVYDusk2XJ37cGew5g+NbBvzEeEUpF9
            5wIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAD7UPC3/Nkgpn53mT9puUonYdJg9
            SD8vvTK/N78CzoEgPNyq+bYbqlcvVPKIdItf9TMiqfOSvW3e3NvkRisYle8Qp+0C
            8pafXBvQ9eHt5CFeJn4sH9GnxeflOZT/P9Jnp71KtZQvOobirX4GgEWs79g+/NHb
            Zyf8rbadt9HruNhKA5nlP8cn7Rdc/iuJU8MVSQszI1s1DEcXMPxr6iqb2g87/ifH
            lWcK59kvRJkCcPhPzjpUy9NulucH4WFA/WqKeDNFS/oC+upV5w8EDEcfnenJFG+N
            JmFDQESSfUxLPHLC660Wnf3GmrP/duZHpPC+qTe8b1AlQ7zDT3cOaAQ+Mb0=
            -----END CERTIFICATE-----
      description: Install certificate request. `chain` can be an empty string.
    SSLCertificateInstallResponse:
      type: string
      example: OK
      description: Install certificate response.
    SSLCertificatesProvisionResponse:
      type: object
      required:
        - requests
      properties:
        requests:
          type: array
          items:
            type: object
            required:
              - log
              - result
              - domains
            properties:
              log:
                type: array
                items:
                  type: string
                example:
                  - 'The domain name does not resolve to this machine: [Not Set] (A), [Not Set] (AAAA).'
              result:
                type: string
                enum:
                  - installed
                  - error
                  - skipped
                example: installed
              domains:
                type: array
                items:
                  $ref: '#/components/schemas/Hostname'
      description: SSL certificates provision response.
    SystemPrivacyStatusResponse:
      type: boolean
      description: |
        System privacy status response.

          - `true`: Private, new-version checks will not be performed
          - `false`: Not private, new-version checks will be performed
      example: false
    SystemVersionResponse:
      type: string
      description: System version response.
      example: v0.46
    SystemVersionUpstreamResponse:
      type: string
      description: System version upstream response.
      example: v0.47
    SystemUpdatesResponse:
      type: string
      description: System updates response.
      example: |
        libgnutls30 (3.5.18-1ubuntu1.4)
        libxau6 (1:1.0.8-1ubuntu1)
    SystemUpdatePackagesResponse:
      type: string
      example: |
        Reading package lists...
        Building dependency tree...
        Reading state information...
        Calculating upgrade...
        The following packages will be upgraded:
          cloud-init grub-common grub-pc grub-pc-bin grub2-common libgnutls30
          libldap-2.4-2 libldap-common libxau6 linux-firmware python3-distupgrade
          qemu-guest-agent sosreport ubuntu-release-upgrader-core
        14 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
        Need to get 79.9 MB of archives.
        After this operation, 3893 kB of additional disk space will be used.
        Get:1 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libgnutls30 amd64 3.5.18-1ubuntu1.4 [645 kB]
        Preconfiguring packages ...
        Fetched 79.9 MB in 2s (52.4 MB/s)
        (Reading database ... 48457 files and directories currently installed.)
      description: System update packages response.
    SystemPrivacyUpdateResponse:
      type: string
      example: OK
      description: System privacy update response.
    SystemRebootStatusResponse:
      type: boolean
      description: |
        System reboot status response.

          - `true`: A reboot is required
          - `false`: A reboot is not required
      example: true
    SystemRebootResponse:
      type: string
      example: No reboot is required, so it is not allowed.
      description: System reboot response.
    SystemStatusResponse:
      type: array
      items:
        $ref: '#/components/schemas/StatusEntry'
      description: System status response.
    StatusEntry:
      type: object
      required:
        - type
        - text
        - extra
      properties:
        type:
          $ref: '#/components/schemas/StatusEntryType'
        text:
          type: string
          example: This domain"s DNSSEC DS record is not set
        extra:
          type: array
          items:
            $ref: '#/components/schemas/StatusEntryExtra'
      description: System status entry.
    StatusEntryType:
      type: string
      enum:
        - heading
        - ok
        - warning
        - error
      example: warning
      description: System status entry type.
    StatusEntryExtra:
      type: object
      required:
        - monospace
        - text
      properties:
        monospace:
          type: boolean
          example: false
        text:
          type: string
          example: 'Digest Type: 2 / SHA-256'
      description: System entry extra information.
    SystemBackupConfigUpdateRequest:
      type: object
      required:
        - target
        - target_user
        - target_pass
        - min_age
      properties:
        target:
          type: string
          format: hostname
          example: s3://s3.eu-central-1.amazonaws.com/box-example-com
        target_user:
          type: string
          example: username
        target_pass:
          type: string
          example: password
          format: password
        min_age:
          type: integer
          format: int32
          minimum: 1
          example: 3
      description: Backup config update request.
    SystemBackupConfigUpdateResponse:
      type: string
      example: OK
      description: Backup config update response.
    SystemBackupConfigResponse:
      type: object
      required:
        - enc_pw_file
        - file_target_directory
        - min_age_in_days
        - ssh_pub_key
        - target
      properties:
        enc_pw_file:
          type: string
          example: /home/user-data/backup/secret_key.txt
        file_target_directory:
          type: string
          example: /home/user-data/backup/encrypted
        min_age_in_days:
          type: integer
          format: int32
          minimum: 1
          example: 3
        ssh_pub_key:
          type: string
          example: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDb root@box.example.com\n
        target:
          type: string
          format: hostname
          example: s3://s3.eu-central-1.amazonaws.com/box-example-com
        target_user:
          type: string
        target_pass:
          type: string
      description: Backup config response.
    SystemBackupStatusResponse:
      type: object
      required:
        - unmatched_file_size
      properties:
        backups:
          type: array
          items:
            $ref: '#/components/schemas/SystemBackupStatus'
        unmatched_file_size:
          type: integer
          format: int32
          example: 0
        error:
          type: string
          example: Something is wrong with the backup
      description: Backup status response. Lists the status for all backups.
    SystemBackupStatus:
      type: object
      required:
        - date
        - date_delta
        - date_str
        - full
        - size
        - volumes
      properties:
        date:
          type: string
          format: date-time
          example: 20200801T023706Z
        date_delta:
          type: string
          example: 15 hours, 40 minutes
        date_str:
          type: string
          example: 2020-08-01 03:37:06 BST
        deleted_in:
          type: string
          example: approx. 6 days
        full:
          type: boolean
          example: false
        size:
          type: integer
          format: int32
          example: 125332
        volumes:
          type: integer
          format: int32
          example: 1
      description: Backup status details.
    SSLStatusResponse:
      type: object
      required:
        - can_provision
        - status
      properties:
        can_provision:
          type: array
          items:
            type: string
        status:
          type: array
          items:
            $ref: '#/components/schemas/SSLStatus'
      description: SSL status response for all relevant domains.
    SSLStatus:
      type: object
      required:
        - domain
        - status
        - text
      properties:
        domain:
          $ref: '#/components/schemas/Hostname'
        status:
          $ref: '#/components/schemas/SSLStatusType'
        text:
          type: string
          example: Signed & valid. The certificate expires in 87 days on 10/28/20.
      description: SSL status details for domain.
    SSLStatusType:
      type: string
      enum:
        - success
        - danger
        - not-applicable
      example: success
      description: SSL status type.
    Email:
      type: string
      format: email
      example: user@example.com
      description: Email format.
    Hostname:
      type: string
      format: hostname
      example: example.com
      description: Hostname format.
    MeResponse:
      type: object
      required:
        - status
      properties:
        api_key:
          type: string
          example: 12345abcde
        email:
          $ref: '#/components/schemas/Email'
        privileges:
          type: array
          items:
            $ref: '#/components/schemas/MailUserPrivilege'
        reason:
          type: string
          example: Incorrect username or password
        status:
          $ref: '#/components/schemas/MeAuthStatus'
      description: Me (user) response.
    MeAuthStatus:
      type: string
      enum:
        - ok
        - invalid
      example: invalid
      description: Me (user) authentication result.
    WebDomain:
      type: object
      required:
        - custom_root
        - domain
        - root
        - ssl_certificate
        - static_enabled
      properties:
        custom_root:
          type: string
          example: /home/user-data/www/example.com
        domain:
          $ref: '#/components/schemas/Hostname'
        root:
          type: string
          example: /home/user-data/www/default
        ssl_certificate:
          type: array
          minItems: 2
          maxItems: 2
          uniqueItems: true
          items:
            oneOf:
              - type: string
                example: No certificate installed.
              - type: string
                enum:
                  - danger
                  - success
                example: danger
        static_enabled:
          type: boolean
          example: true
      description: Web domain details.
    WebUpdateResponse:
      type: string
      example: web updated
      description: Web update response.
    MfaStatusResponse:
      type: object
      properties:
        enabled_mfa:
          type: object
          properties:
            id:
              type: string
            type:
              type: string
            label:
              type: string
          nullable: true
        new_mfa:
          type: object
          properties:
            type:
              type: string
            secret:
              type: string
            qr_code_base64:
              type: string
    MfaEnableRequest:
      type: object
      required:
        - secret
        - code
      properties:
        secret:
          type: string
        code:
          type: string
        label:
          type: string
    MfaEnableSuccessResponse:
      type: string
    MfaDisableRequest:
      type: object
      properties:
        mfa_id:
          type: string
          nullable: true
    MfaDisableSuccessResponse:
      type: string
    LogoutResponse:
      type: object
      properties:
        status:
          type: string


================================================
FILE: conf/dovecot-mailboxes.conf
================================================
## NOTE: This file is automatically generated by Mail-in-a-Box.
##       Do not edit this file. It is continually updated by
##       Mail-in-a-Box and your changes will be lost.
##
##       Mail-in-a-Box machines are not meant to be modified.
##       If you modify any system configuration you are on
##       your own --- please do not ask for help from us.

namespace inbox {
  # Automatically create & subscribe some folders.
  # * Create and subscribe the INBOX folder.
  # * Our sieve rule for spam expects that the Spam folder exists.
  # * Z-Push must be configured with the same settings in conf/zpush/backend_imap.php (#580).

  # MUA notes:
  # * Roundcube will show an error if the user tries to delete a message before the Trash folder exists (#359).
  # * K-9 mail will poll every 90 seconds if a Drafts folder does not exist.
  # * Apple's OS X Mail app will create 'Sent Messages' if it doesn't see a folder with the \Sent flag (#571, #573) and won't be able to archive messages unless 'Archive' exists (#581).
  # * Thunderbird's default in its UI is 'Archives' (plural) but it will configure new accounts to use whatever we say here (#581).

  # auto:
  # 'create' will automatically create this mailbox.
  # 'subscribe' will both create and subscribe to the mailbox.

  # special_use is a space separated list of IMAP SPECIAL-USE
  # attributes as specified by RFC 6154:
  # \All \Archive \Drafts \Flagged \Junk \Sent \Trash

  mailbox INBOX {
    auto = subscribe
  }
  mailbox Spam {
    special_use = \Junk
    auto = subscribe
  }
  mailbox Drafts {
    special_use = \Drafts
    auto = subscribe
  }
  mailbox Sent {
    special_use = \Sent
    auto = subscribe
  }
  mailbox Trash {
    special_use = \Trash
    auto = subscribe
  }
  mailbox Archive {
    special_use = \Archive
    auto = subscribe
  }

  # dovevot's standard mailboxes configuration file marks two sent folders
  # with the \Sent attribute, just in case clients don't agree about which
  # they're using. We'll keep that, plus add Junk as an alternative for Spam.
  # These are not auto-created.
  mailbox "Sent Messages" {
    special_use = \Sent
  }
  mailbox Junk {
    special_use = \Junk
  }
}


================================================
FILE: conf/fail2ban/filter.d/dovecotimap.conf
================================================
# Fail2Ban filter Dovecot authentication and pop3/imap/managesieve server
# For Mail-in-a-Box

[INCLUDES]

before = common.conf

[Definition]

_daemon = (auth|dovecot(-auth)?|auth-worker)

failregex = ^%(__prefix_line)s(pop3|imap|managesieve)-login: (Info: )?(Aborted login|Disconnected)(: Inactivity)? \(((no auth attempts|auth failed, \d+ attempts)( in \d+ secs)?|tried to use (disabled|disallowed) \S+ auth)\):( user=<\S*>,)?( method=\S+,)? rip=<HOST>, lip=(\d{1,3}\.){3}\d{1,3}(, TLS( handshaking)?(: Disconnected)?)?(, session=<\S+>)?\s*$

ignoreregex =

# DEV Notes:
# * the first regex is essentially a copy of pam-generic.conf
# * Probably doesn't do dovecot sql/ldap backends properly
#
# Author: Martin Waschbuesch
#         Daniel Black (rewrote with begin and end anchors)
#         Mail-in-a-Box (swapped session=...)


================================================
FILE: conf/fail2ban/filter.d/miab-management-daemon.conf
================================================
# Fail2Ban filter Mail-in-a-Box management daemon

[INCLUDES]

before = common.conf

[Definition]

_daemon = mailinabox

failregex = Mail-in-a-Box Management Daemon: Failed login attempt from ip <HOST> - timestamp .*
ignoreregex =


================================================
FILE: conf/fail2ban/filter.d/miab-munin.conf
================================================
[INCLUDES]

before = common.conf

[Definition]
failregex=<HOST> - .*GET /admin/munin/.* HTTP/\d+\.\d+\" 401.*
ignoreregex =


================================================
FILE: conf/fail2ban/filter.d/miab-owncloud.conf
================================================
[INCLUDES]

before = common.conf

[Definition]
_groupsre = (?:(?:,?\s*"\w+":(?:"[^"]+"|\w+))*)
failregex = ^\{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"message":"Login failed:
            ^\{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"message":"Trusted domain error.
datepattern = ,?\s*"time"\s*:\s*"%%Y-%%m-%%d[T ]%%H:%%M:%%S(%%z)?"



================================================
FILE: conf/fail2ban/filter.d/miab-postfix-submission.conf
================================================
[INCLUDES]

before = common.conf

[Definition]
failregex=postfix/submission/smtpd.*warning.*\[<HOST>\]: .* authentication (failed|aborted)
ignoreregex =


================================================
FILE: conf/fail2ban/filter.d/miab-roundcube.conf
================================================
[INCLUDES]

before = common.conf

[Definition]

failregex = IMAP Error: Login failed for .*? from <HOST>\. AUTHENTICATE.*

ignoreregex = 


================================================
FILE: conf/fail2ban/jails.conf
================================================
# Fail2Ban configuration file for Mail-in-a-Box. Do not edit.
# This file is re-generated on updates.

[DEFAULT]
# Whitelist our own IP addresses. 127.0.0.1/8 is the default. But our status checks
# ping services over the public interface so we should whitelist that address of
# ours too. The string is substituted during installation.
ignoreip = 127.0.0.1/8 PUBLIC_IP ::1 PUBLIC_IPV6

[dovecot]
enabled = true
filter  = dovecotimap
logpath = /var/log/mail.log
findtime = 30
maxretry = 20

[miab-management]
enabled = true
filter = miab-management-daemon
port = http,https
logpath = /var/log/syslog
maxretry = 20
findtime = 30

[miab-munin]
enabled  = true
port     = http,https
filter   = miab-munin
logpath  = /var/log/nginx/access.log
maxretry = 20
findtime = 30

[miab-owncloud]
enabled  = true
port     = http,https
filter   = miab-owncloud
logpath  = STORAGE_ROOT/owncloud/nextcloud.log
maxretry = 20
findtime = 120

[miab-postfix465]
enabled  = true
port     = 465
filter   = miab-postfix-submission
logpath  = /var/log/mail.log
maxretry = 20
findtime = 30

[miab-postfix587]
enabled  = true
port     = 587
filter   = miab-postfix-submission
logpath  = /var/log/mail.log
maxretry = 20
findtime = 30

[miab-roundcube]
enabled  = true
port     = http,https
filter   = miab-roundcube
logpath  = /var/log/roundcubemail/errors.log
maxretry = 20
findtime = 30

[recidive]
enabled  = true
maxretry = 10
action   = iptables-allports[name=recidive]
# In the recidive section of jail.conf the action contains:
#
# action   = iptables-allports[name=recidive]
#            sendmail-whois-lines[name=recidive, logpath=/var/log/fail2ban.log]
#
# The last line on the action will sent an email to the configured address. This mail will
# notify the administrator that someone has been repeatedly triggering one of the other jails.
# By default we don't configure this address and no action is required from the admin anyway.
# So the notification is omitted. This will prevent message appearing in the mail.log that mail
# can't be delivered to fail2ban@$HOSTNAME.

[postfix-sasl]
enabled  = true

[sshd]
enabled = true
maxretry = 7
bantime = 3600


================================================
FILE: conf/ios-profile.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!--
 iOS/OS X Configuration Profile

 Mobileconfig for iOS/OS X users to setup IMAP, SMTP, Contacts & Calendar

 https://developer.apple.com/library/ios/featuredarticles/iPhoneConfigurationProfileRef/Introduction/Introduction.html
-->
<plist version="1.0">
<dict>
  <key>PayloadContent</key>
  <array>
    <dict>
      <key>CalDAVAccountDescription</key>
      <string>PRIMARY_HOSTNAME calendar</string>
      <key>CalDAVHostName</key>
      <string>PRIMARY_HOSTNAME</string>
      <key>CalDAVPort</key>
      <real>443</real>
      <key>CalDAVUseSSL</key>
      <true/>
      <key>PayloadDescription</key>
      <string>PRIMARY_HOSTNAME (Mail-in-a-Box)</string>
      <key>PayloadDisplayName</key>
      <string>PRIMARY_HOSTNAME calendar</string>
      <key>PayloadIdentifier</key>
      <string>email.mailinabox.mobileconfig.PRIMARY_HOSTNAME.CalDAV</string>
      <key>PayloadOrganization</key>
      <string></string>
      <key>PayloadType</key>
      <string>com.apple.caldav.account</string>
      <key>PayloadUUID</key>
      <string>UUID1</string>
      <key>PayloadVersion</key>
      <integer>1</integer>
    </dict>
    <dict>
      <key>EmailAccountDescription</key>
      <string>PRIMARY_HOSTNAME mail</string>
      <key>EmailAccountType</key>
      <string>EmailTypeIMAP</string>
      <key>IncomingMailServerAuthentication</key>
      <string>EmailAuthPassword</string>
      <key>IncomingMailServerHostName</key>
      <string>PRIMARY_HOSTNAME</string>
      <key>IncomingMailServerPortNumber</key>
      <integer>993</integer>
      <key>IncomingMailServerUseSSL</key>
      <true/>
      <key>OutgoingMailServerAuthentication</key>
      <string>EmailAuthPassword</string>
      <key>OutgoingMailServerHostName</key>
      <string>PRIMARY_HOSTNAME</string>
      <key>OutgoingMailServerPortNumber</key>
      <integer>465</integer>
      <key>OutgoingMailServerUseSSL</key>
      <true/>
      <key>OutgoingPasswordSameAsIncomingPassword</key>
      <true/>
      <key>PayloadDescription</key>
      <string>PRIMARY_HOSTNAME (Mail-in-a-Box)</string>
      <key>PayloadDisplayName</key>
      <string>PRIMARY_HOSTNAME mail</string>
      <key>PayloadIdentifier</key>
      <string>email.mailinabox.mobileconfig.PRIMARY_HOSTNAME.E-Mail</string>
      <key>PayloadOrganization</key>
      <string></string>
      <key>PayloadType</key>
      <string>com.apple.mail.managed</string>
      <key>PayloadUUID</key>
      <string>UUID2</string>
      <key>PayloadVersion</key>
      <integer>1</integer>
      <key>PreventAppSheet</key>
      <false/>
      <key>PreventMove</key>
      <false/>
      <key>SMIMEEnabled</key>
      <false/>
    </dict>
    <dict>
      <key>CardDAVAccountDescription</key>
      <string>PRIMARY_HOSTNAME contacts</string>
      <key>CardDAVHostName</key>
      <string>PRIMARY_HOSTNAME</string>
      <key>CardDAVPort</key>
      <integer>443</integer>
      <key>CardDAVPrincipalURL</key>
      <string>/cloud/remote.php/carddav/addressbooks/</string>
      <key>CardDAVUseSSL</key>
      <true/>
      <key>PayloadDescription</key>
      <string>PRIMARY_HOSTNAME (Mail-in-a-Box)</string>
      <key>PayloadDisplayName</key>
      <string>PRIMARY_HOSTNAME contacts</string>
      <key>PayloadIdentifier</key>
      <string>email.mailinabox.mobileconfig.PRIMARY_HOSTNAME.carddav</string>
      <key>PayloadOrganization</key>
      <string></string>
      <key>PayloadType</key>
      <string>com.apple.carddav.account</string>
      <key>PayloadUUID</key>
      <string>UUID3</string>
      <key>PayloadVersion</key>
      <integer>1</integer>
    </dict>
  </array>
  <key>PayloadDescription</key>
  <string>PRIMARY_HOSTNAME (Mail-in-a-Box)</string>
  <key>PayloadDisplayName</key>
  <string>PRIMARY_HOSTNAME</string>
  <key>PayloadIdentifier</key>
  <string>email.mailinabox.mobileconfig.PRIMARY_HOSTNAME</string>
  <key>PayloadOrganization</key>
  <string></string>
  <key>PayloadRemovalDisallowed</key>
  <false/>
  <key>PayloadType</key>
  <string>Configuration</string>
  <key>PayloadUUID</key>
  <string>UUID4</string>
  <key>PayloadVersion</key>
  <integer>1</integer>
</dict>
</plist>


================================================
FILE: conf/mailinabox.service
================================================
[Unit]
Description=Mail-in-a-Box System Management Service
After=multi-user.target

[Service]
Type=idle
IgnoreSIGPIPE=False
ExecStart=/usr/local/lib/mailinabox/start

[Install]
WantedBy=multi-user.target


================================================
FILE: conf/mozilla-autoconfig.xml
================================================
<?xml version="1.0"?>
<clientConfig version="1.1">
    <emailProvider id="PRIMARY_HOSTNAME">
      <domain>PRIMARY_HOSTNAME</domain>
      <domain purpose="mx">PRIMARY_HOSTNAME</domain>

      <displayName>PRIMARY_HOSTNAME (Mail-in-a-Box)</displayName>
      <displayShortName>PRIMARY_HOSTNAME</displayShortName>

      <incomingServer type="imap">
         <hostname>PRIMARY_HOSTNAME</hostname>
         <port>993</port>
         <socketType>SSL</socketType>
         <username>%EMAILADDRESS%</username>
         <authentication>password-cleartext</authentication>
      </incomingServer>

      <incomingServer type="pop3">
         <hostname>PRIMARY_HOSTNAME</hostname>
         <port>995</port>
         <socketType>SSL</socketType>
         <username>%EMAILADDRESS%</username>
         <authentication>password-cleartext</authentication>
      </incomingServer>

      <outgoingServer type="smtp">
         <hostname>PRIMARY_HOSTNAME</hostname>
         <port>465</port>
         <socketType>SSL</socketType>
         <username>%EMAILADDRESS%</username>
         <authentication>password-cleartext</authentication>
         <addThisServer>true</addThisServer>
         <useGlobalPreferredServer>false</useGlobalPreferredServer>
      </outgoingServer>

      <documentation url="https://PRIMARY_HOSTNAME/">
         <descr lang="en">PRIMARY_HOSTNAME website.</descr>
      </documentation>
    </emailProvider>

    <addressbook type="carddav">
      <username>%EMAILADDRESS%</username>
      <authentication system="http">basic</authentication>
      <!-- Redirects to: https://PRIMARY_HOSTNAME/cloud/remote.php/carddav/ -->
      <url>https://PRIMARY_HOSTNAME/.well-known/carddav</url>
    </addressbook>

    <calendar type="caldav">
      <username>%EMAILADDRESS%</username>
      <authentication system="http">basic</authentication>
      <!-- Redirects to: https://PRIMARY_HOSTNAME/cloud/remote.php/caldav/ -->
      <url>https://PRIMARY_HOSTNAME/.well-known/caldav</url>
    </calendar>

    <webMail>
      <loginPage url="https://PRIMARY_HOSTNAME/mail/" />
      <loginPageInfo url="https://PRIMARY_HOSTNAME/mail/" >
        <username>%EMAILADDRESS%</username>
        <usernameField id="rcmloginuser" name="_user" />
        <passwordField id="rcmloginpwd" name="_pass" />
        <loginButton id="rcmloginsubmit" />
      </loginPageInfo>
    </webMail>

    <clientConfigUpdate url="https://PRIMARY_HOSTNAME/.well-known/autoconfig/mail/config-v1.1.xml" />

</clientConfig>


================================================
FILE: conf/mta-sts.txt
================================================
version: STSv1
mode: MODE
mx: PRIMARY_HOSTNAME
max_age: 604800


================================================
FILE: conf/munin.service
================================================
[Unit]
Description=Munin System Monitoring Startup Script
After=multi-user.target

[Service]
Type=idle
ExecStart=/usr/local/lib/mailinabox/munin_start.sh

[Install]
WantedBy=multi-user.target


================================================
FILE: conf/nginx-alldomains.conf
================================================
	# Expose this directory as static files.
	root $ROOT;
	index index.html index.htm;

	location = /robots.txt {
		log_not_found off;
		access_log off;
	}

	location = /favicon.ico {
		log_not_found off;
		access_log off;
	}

	location = /mailinabox.mobileconfig {
		alias /var/lib/mailinabox/mobileconfig.xml;
	}
	location = /.well-known/autoconfig/mail/config-v1.1.xml {
		alias /var/lib/mailinabox/mozilla-autoconfig.xml;
	}
	location = /mail/config-v1.1.xml {
		alias /var/lib/mailinabox/mozilla-autoconfig.xml;
	}
	location = /.well-known/mta-sts.txt {
		alias /var/lib/mailinabox/mta-sts.txt;
	}

	# Roundcube Webmail configuration.
	rewrite ^/mail$ /mail/ redirect;
	rewrite ^/mail/$ /mail/index.php;
	location /mail/ {
		index index.php;
		alias /usr/local/lib/roundcubemail/;
	}
	location ~ /mail/config/.* {
		# A ~-style location is needed to give this precedence over the next block.
		return 403;
	}
	location ~ /mail/.*\.php {
		# note: ~ has precedence over a regular location block
		include fastcgi_params;
		fastcgi_split_path_info ^/mail(/.*)()$;
		fastcgi_index index.php;
		fastcgi_param SCRIPT_FILENAME /usr/local/lib/roundcubemail/$fastcgi_script_name;
		fastcgi_pass php-fpm;

		# Outgoing mail also goes through this endpoint, so increase the maximum
		# file upload limit to match the corresponding Postfix limit.
		client_max_body_size 128M;
	}

	# Z-Push (Microsoft Exchange ActiveSync)
	location /Microsoft-Server-ActiveSync {
		include /etc/nginx/fastcgi_params;
		fastcgi_param SCRIPT_FILENAME /usr/local/lib/z-push/index.php;
		fastcgi_param PHP_VALUE "include_path=.:/usr/share/php:/usr/share/pear:/usr/share/awl/inc";
		fastcgi_read_timeout 630;
		fastcgi_pass php-fpm;

		# Outgoing mail also goes through this endpoint, so increase the maximum
		# file upload limit to match the corresponding Postfix limit.
		client_max_body_size 128M;
	}
	location ~* ^/autodiscover/autodiscover.xml$ {
		include fastcgi_params;
		fastcgi_param SCRIPT_FILENAME /usr/local/lib/z-push/autodiscover/autodiscover.php;
		fastcgi_param PHP_VALUE "include_path=.:/usr/share/php:/usr/share/pear:/usr/share/awl/inc";
		fastcgi_pass php-fpm;
	}


	# ADDITIONAL DIRECTIVES HERE

	# Disable viewing dotfiles (.htaccess, .svn, .git, etc.)
	# This block is placed at the end. Nginx's precedence rules means this block
	# takes precedence over all non-regex matches and only regex matches that
	# come after it (i.e. none of those, since this is the last one.) That means
	# we're blocking dotfiles in the static hosted sites but not the FastCGI-
	# handled locations for Nextcloud (which serves user-uploaded files that might
	# have this pattern, see #414) or some of the other services.
	location ~ /\.(ht|svn|git|hg|bzr) {
		log_not_found off;
		access_log off;
		deny all;
	}


================================================
FILE: conf/nginx-primaryonly.conf
================================================
	# Control Panel
	# Proxy /admin to our Python based control panel daemon. It is
	# listening on IPv4 only so use an IP address and not 'localhost'.
	location /admin/assets {
		alias /usr/local/lib/mailinabox/vendor/assets;
	}
	rewrite ^/admin$ /admin/;
	rewrite ^/admin/munin$ /admin/munin/ redirect;
	location /admin/ {
		proxy_pass http://127.0.0.1:10222/;
		proxy_set_header X-Forwarded-For $remote_addr;
		add_header X-Frame-Options "DENY";
		add_header X-Content-Type-Options nosniff;
		add_header Content-Security-Policy "frame-ancestors 'none';";
	}

	# Nextcloud configuration.
	rewrite ^/cloud$ /cloud/ redirect;
	rewrite ^/cloud/$ /cloud/index.php;
	rewrite ^/cloud/(contacts|calendar|files)$ /cloud/index.php/apps/$1/ redirect;
	rewrite ^(/cloud/core/doc/[^\/]+/)$ $1/index.html;
	rewrite ^(/cloud/oc[sm]-provider)/$ $1/index.php redirect;
	location /cloud/ {
		alias /usr/local/lib/owncloud/;
		location ~ ^/cloud/(build|tests|config|lib|3rdparty|templates|data|README)/ {
			deny all;
		}
		location ~ ^/cloud/(?:\.|autotest|occ|issue|indie|db_|console) {
			deny all;
		}
		# Enable paths for service and cloud federation discovery
		# Resolves warning in Nextcloud Settings panel
		location ~ ^/cloud/(oc[sm]-provider)?/([^/]+\.php)$ {
			index index.php;
			include fastcgi_params;
			fastcgi_param SCRIPT_FILENAME /usr/local/lib/owncloud/$1/$2;
			fastcgi_pass php-fpm;
		}
	}
	location ~ ^(/cloud)((?:/ocs)?/[^/]+\.php)(/.*)?$ {
		# note: ~ has precedence over a regular location block
		# Accept URLs like:
		# /cloud/index.php/apps/files/
		# /cloud/index.php/apps/files/ajax/scan.php (it's really index.php; see 6fdef379adfdeac86cc2220209bdf4eb9562268d)
		# /cloud/ocs/v1.php/apps/files_sharing/api/v1 (see #240)
		# /cloud/remote.php/webdav/yourfilehere...
		include fastcgi_params;
		fastcgi_param SCRIPT_FILENAME /usr/local/lib/owncloud/$2;
		fastcgi_param SCRIPT_NAME $1$2;
		fastcgi_param PATH_INFO $3;
		fastcgi_param MOD_X_ACCEL_REDIRECT_ENABLED on;
		fastcgi_param MOD_X_ACCEL_REDIRECT_PREFIX /owncloud-xaccel;
		fastcgi_read_timeout 630;
		fastcgi_pass php-fpm;
		client_max_body_size 1G;
		fastcgi_buffers 64 4K;
	}
	location ^~ /owncloud-xaccel/ {
		# This directory is for MOD_X_ACCEL_REDIRECT_ENABLED. Nextcloud sends the full file
		# path on disk as a subdirectory under this virtual path.
		# We must only allow 'internal' redirects within nginx so that the filesystem
		# is not exposed to the world.
		internal;
		alias /;
	}
	location ~ ^/((caldav|carddav|webdav).*)$ {
		# Z-Push doesn't like getting a redirect, and a plain rewrite didn't work either.
		# Properly proxying like this seems to work fine.
		proxy_pass https://127.0.0.1/cloud/remote.php/$1;
	}
	rewrite ^/.well-known/host-meta /cloud/public.php?service=host-meta last;
	rewrite ^/.well-known/host-meta.json /cloud/public.php?service=host-meta-json last;
	rewrite ^/.well-known/carddav /cloud/remote.php/carddav/ redirect;
	rewrite ^/.well-known/caldav /cloud/remote.php/caldav/ redirect;

	# This addresses those service discovery issues mentioned in:
	# https://docs.nextcloud.com/server/23/admin_manual/issues/general_troubleshooting.html#service-discovery
	rewrite ^/.well-known/webfinger /cloud/index.php/.well-known/webfinger redirect;
	rewrite ^/.well-known/nodeinfo /cloud/index.php/.well-known/nodeinfo redirect;

	# ADDITIONAL DIRECTIVES HERE


================================================
FILE: conf/nginx-ssl.conf
================================================
# We track the Mozilla "intermediate" compatibility TLS recommendations.
# Note that these settings are repeated in the SMTP and IMAP configuration.
# ssl_protocols has moved to nginx.conf in bionic, check there for enabled protocols.
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_dhparam STORAGE_ROOT/ssl/dh2048.pem;

# as recommended by http://nginx.org/en/docs/http/configuring_https_servers.html
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;

# Buffer size of 1400 bytes fits in one MTU.
# nginx 1.5.9+ ONLY
ssl_buffer_size 1400; 

resolver 127.0.0.1 valid=86400;
resolver_timeout 10;

# h/t https://gist.github.com/konklone/6532544


================================================
FILE: conf/nginx-top.conf
================================================
## NOTE: This file is automatically generated by Mail-in-a-Box.
##       Do not edit this file. It is continually updated by
##       Mail-in-a-Box and your changes will be lost.
##
##       Mail-in-a-Box machines are not meant to be modified.
##       If you modify any system configuration you are on
##       your own --- please do not ask for help from us.

upstream php-fpm {
	server unix:/var/run/php/php8.0-fpm.sock;
}



================================================
FILE: conf/nginx.conf
================================================
## $HOSTNAME

# Redirect all HTTP to HTTPS *except* the ACME challenges (Let's Encrypt TLS certificate
# domain validation challenges) path, which must be served over HTTP per the ACME spec
# (due to some Apache vulnerability).
server {
	listen 80;
	listen [::]:80;

	server_name $HOSTNAME;
	root /tmp/invalid-path-nothing-here;

	# Improve privacy: Hide version an OS information on
	# error pages and in the "Server" HTTP-Header.
	server_tokens off;

	location / {
		# Redirect using the 'return' directive and the built-in
		# variable '$request_uri' to avoid any capturing, matching
		# or evaluation of regular expressions.
		return 301 https://$HOSTNAME$request_uri;
	}

	location /.well-known/acme-challenge/ {
		# This path must be served over HTTP for ACME domain validation.
		# We map this to a special path where our TLS cert provisioning
		# tool knows to store challenge response files.
		alias $STORAGE_ROOT/ssl/lets_encrypt/webroot/.well-known/acme-challenge/;
	}
}

# The secure HTTPS server.
server {
	listen 443 ssl http2;
	listen [::]:443 ssl http2;

	server_name $HOSTNAME;

	# Improve privacy: Hide version an OS information on
	# error pages and in the "Server" HTTP-Header.
	server_tokens off;

	ssl_certificate $SSL_CERTIFICATE;
	ssl_certificate_key $SSL_KEY;

	# ADDITIONAL DIRECTIVES HERE
}


================================================
FILE: conf/postfix_outgoing_mail_header_filters
================================================
# Remove the first line of the Received: header. Note that we cannot fully remove the Received: header
# because OpenDKIM requires that a header be present when signing outbound mail. The first line is
# where the user's home IP address would be.
/^\s*Received:[^\n]*(.*)/         REPLACE Received: from authenticated-user (PRIMARY_HOSTNAME [PUBLIC_IP])$1

# Remove other typically private information.
/^\s*User-Agent:/        IGNORE
/^\s*X-Enigmail:/        IGNORE
/^\s*X-Mailer:/          IGNORE
/^\s*X-Originating-IP:/  IGNORE
/^\s*X-Pgp-Agent:/       IGNORE

# The Mime-Version header can leak the user agent too, e.g. in Mime-Version: 1.0 (Mac OS X Mail 8.1 \(2010.6\)).
/^\s*(Mime-Version:\s*[0-9\.]+)\s.+/  REPLACE $1


================================================
FILE: conf/sieve-spam.txt
================================================
Download .txt
gitextract_y6gyh9lx/

├── .editorconfig
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── Vagrantfile
├── api/
│   ├── docs/
│   │   ├── generate-docs.sh
│   │   └── template.hbs
│   └── mailinabox.yml
├── conf/
│   ├── dovecot-mailboxes.conf
│   ├── fail2ban/
│   │   ├── filter.d/
│   │   │   ├── dovecotimap.conf
│   │   │   ├── miab-management-daemon.conf
│   │   │   ├── miab-munin.conf
│   │   │   ├── miab-owncloud.conf
│   │   │   ├── miab-postfix-submission.conf
│   │   │   └── miab-roundcube.conf
│   │   └── jails.conf
│   ├── ios-profile.xml
│   ├── mailinabox.service
│   ├── mozilla-autoconfig.xml
│   ├── mta-sts.txt
│   ├── munin.service
│   ├── nginx-alldomains.conf
│   ├── nginx-primaryonly.conf
│   ├── nginx-ssl.conf
│   ├── nginx-top.conf
│   ├── nginx.conf
│   ├── postfix_outgoing_mail_header_filters
│   ├── sieve-spam.txt
│   ├── www_default.html
│   └── zpush/
│       ├── autodiscover_config.php
│       ├── backend_caldav.php
│       ├── backend_carddav.php
│       ├── backend_combined.php
│       └── backend_imap.php
├── management/
│   ├── auth.py
│   ├── backup.py
│   ├── cli.py
│   ├── csr_country_codes.tsv
│   ├── daemon.py
│   ├── daily_tasks.sh
│   ├── dns_update.py
│   ├── email_administrator.py
│   ├── mail_log.py
│   ├── mailconfig.py
│   ├── mfa.py
│   ├── munin_start.sh
│   ├── ssl_certificates.py
│   ├── status_checks.py
│   ├── templates/
│   │   ├── aliases.html
│   │   ├── custom-dns.html
│   │   ├── external-dns.html
│   │   ├── index.html
│   │   ├── login.html
│   │   ├── mail-guide.html
│   │   ├── mfa.html
│   │   ├── munin.html
│   │   ├── ssl.html
│   │   ├── sync-guide.html
│   │   ├── system-backup.html
│   │   ├── system-status.html
│   │   ├── users.html
│   │   ├── web.html
│   │   └── welcome.html
│   ├── utils.py
│   ├── web_update.py
│   └── wsgi.py
├── pyproject.toml
├── security.md
├── setup/
│   ├── bootstrap.sh
│   ├── dkim.sh
│   ├── dns.sh
│   ├── firstuser.sh
│   ├── functions.sh
│   ├── mail-dovecot.sh
│   ├── mail-postfix.sh
│   ├── mail-users.sh
│   ├── management.sh
│   ├── migrate.py
│   ├── munin.sh
│   ├── network-checks.sh
│   ├── nextcloud.sh
│   ├── preflight.sh
│   ├── questions.sh
│   ├── spamassassin.sh
│   ├── ssl.sh
│   ├── start.sh
│   ├── system.sh
│   ├── web.sh
│   ├── webmail.sh
│   └── zpush.sh
├── tests/
│   ├── fail2ban.py
│   ├── pip-requirements.txt
│   ├── test_dns.py
│   ├── test_mail.py
│   ├── test_smtp_server.py
│   ├── tls.py
│   └── tls_results.txt
└── tools/
    ├── archive_conf_files.sh
    ├── dns_update
    ├── editconf.py
    ├── mail.py
    ├── owncloud-restore.sh
    ├── owncloud-unlockadmin.sh
    ├── parse-nginx-log-bootstrap-accesses.py
    ├── readable_bash.py
    ├── ssl_cleanup
    └── web_update
Download .txt
SYMBOL INDEX (318 symbols across 18 files)

FILE: conf/zpush/backend_combined.php
  class BackendCombinedConfig (line 9) | class BackendCombinedConfig {
    method GetBackendCombinedConfig (line 10) | public static function GetBackendCombinedConfig() {

FILE: management/auth.py
  class AuthService (line 13) | class AuthService:
    method __init__ (line 14) | def __init__(self):
    method init_system_api_key (line 22) | def init_system_api_key(self):
    method authenticate (line 28) | def authenticate(self, request, env, login_only=False, logout=False):
    method check_user_auth (line 95) | def check_user_auth(self, email, pw, request, env):
    method create_user_password_state_token (line 129) | def create_user_password_state_token(self, email, env):
    method create_session_key (line 142) | def create_session_key(self, username, env, type=None):
    method get_session (line 152) | def get_session(self, user_email, session_key, session_type, env):

FILE: management/backup.py
  function backup_status (line 19) | def backup_status(env):
  function should_force_full (line 157) | def should_force_full(config, env):
  function get_passphrase (line 184) | def get_passphrase(env):
  function get_duplicity_target_url (line 198) | def get_duplicity_target_url(config):
  function get_duplicity_additional_args (line 219) | def get_duplicity_additional_args(env):
  function get_duplicity_env_vars (line 252) | def get_duplicity_env_vars(env):
  function get_target_type (line 265) | def get_target_type(config):
  function perform_backup (line 268) | def perform_backup(full_backup):
  function run_duplicity_verification (line 396) | def run_duplicity_verification():
  function run_duplicity_restore (line 415) | def run_duplicity_restore(args):
  function print_duplicity_command (line 428) | def print_duplicity_command():
  function list_target_files (line 441) | def list_target_files(config):
  function backup_set_custom (line 555) | def backup_set_custom(env, target, target_user, target_pass, min_age):
  function get_backup_config (line 580) | def get_backup_config(env, for_save=False, for_ui=False):
  function write_backup_config (line 621) | def write_backup_config(env, newconfig):

FILE: management/cli.py
  function mgmt (line 12) | def mgmt(cmd, data=None, is_json=False):
  function read_password (line 35) | def read_password():
  function setup_key_auth (line 48) | def setup_key_auth(mgmt_uri):

FILE: management/daemon.py
  function authorized_personnel_only (line 48) | def authorized_personnel_only(viewfunc):
  function unauthorized (line 105) | def unauthorized(error):
  function json_response (line 108) | def json_response(data, status=200):
  function index (line 116) | def index():
  function login (line 140) | def login():
  function logout (line 171) | def logout():
  function mail_users (line 184) | def mail_users():
  function mail_users_add (line 191) | def mail_users_add():
  function get_mail_users_quota (line 200) | def get_mail_users_quota():
  function mail_users_quota (line 214) | def mail_users_quota():
  function mail_users_password (line 222) | def mail_users_password():
  function mail_users_remove (line 230) | def mail_users_remove():
  function mail_user_privs (line 236) | def mail_user_privs():
  function mail_user_privs_add (line 243) | def mail_user_privs_add():
  function mail_user_privs_remove (line 248) | def mail_user_privs_remove():
  function mail_aliases (line 254) | def mail_aliases():
  function mail_aliases_add (line 261) | def mail_aliases_add():
  function mail_aliases_remove (line 272) | def mail_aliases_remove():
  function mail_domains (line 277) | def mail_domains():
  function dns_zones (line 284) | def dns_zones():
  function dns_update (line 290) | def dns_update():
  function dns_get_secondary_nameserver (line 299) | def dns_get_secondary_nameserver():
  function dns_set_secondary_nameserver (line 305) | def dns_set_secondary_nameserver():
  function dns_get_records (line 314) | def dns_get_records(qname=None, rtype=None):
  function dns_set_record (line 363) | def dns_set_record(qname, rtype="A"):
  function dns_get_dump (line 414) | def dns_get_dump():
  function dns_get_zonefile (line 420) | def dns_get_zonefile(zone):
  function ssl_get_status (line 428) | def ssl_get_status():
  function ssl_get_csr (line 459) | def ssl_get_csr(domain):
  function ssl_install_cert (line 466) | def ssl_install_cert():
  function ssl_provision_certs (line 478) | def ssl_provision_certs():
  function mfa_get_status (line 487) | def mfa_get_status():
  function totp_post_enable (line 509) | def totp_post_enable():
  function totp_post_disable (line 524) | def totp_post_disable():
  function web_get_domains (line 542) | def web_get_domains():
  function web_update (line 548) | def web_update():
  function system_version (line 556) | def system_version():
  function system_latest_upstream_version (line 565) | def system_latest_upstream_version():
  function system_status (line 574) | def system_status():
  function show_updates (line 599) | def show_updates():
  function do_updates (line 607) | def do_updates():
  function needs_reboot (line 616) | def needs_reboot():
  function do_reboot (line 624) | def do_reboot():
  function backup_status (line 634) | def backup_status():
  function backup_get_custom (line 643) | def backup_get_custom():
  function backup_set_custom (line 649) | def backup_set_custom():
  function privacy_status_get (line 660) | def privacy_status_get():
  function privacy_status_set (line 666) | def privacy_status_set():
  function munin_start (line 676) | def munin_start():
  function check_request_cookie_for_admin_access (line 686) | def check_request_cookie_for_admin_access():
  function authorized_personnel_only_via_cookie (line 693) | def authorized_personnel_only_via_cookie(f):
  function munin_static_file (line 703) | def munin_static_file(filename=""):
  function munin_cgi (line 710) | def munin_cgi(filename):
  function log_failed_login (line 763) | def log_failed_login(request):

FILE: management/dns_update.py
  function get_dns_domains (line 21) | def get_dns_domains(env):
  function get_dns_zones (line 33) | def get_dns_zones(env):
  function do_dns_update (line 61) | def do_dns_update(env, force=False):
  function build_zones (line 130) | def build_zones(env):
  function build_zone (line 175) | def build_zone(domain, domain_properties, additional_records, env, is_zo...
  function is_domain_cert_signed_and_valid (line 378) | def is_domain_cert_signed_and_valid(domain, env):
  function build_tlsa_record (line 386) | def build_tlsa_record(env):
  function build_sshfp_records (line 423) | def build_sshfp_records():
  function write_nsd_zone (line 474) | def write_nsd_zone(domain, zonefile, records, env, force):
  function get_dns_zonefile (line 588) | def get_dns_zonefile(zone, env):
  function write_nsd_conf (line 602) | def write_nsd_conf(zonefiles, additional_records, env):
  function find_dnssec_signing_keys (line 637) | def find_dnssec_signing_keys(domain, env):
  function hash_dnssec_keys (line 659) | def hash_dnssec_keys(domain, env):
  function sign_zone (line 671) | def sign_zone(domain, zonefile, env):
  function write_opendkim_tables (line 750) | def write_opendkim_tables(domains, env):
  function get_custom_dns_config (line 803) | def get_custom_dns_config(env, only_real_records=False):
  function filter_custom_records (line 837) | def filter_custom_records(domain, custom_dns_iter):
  function write_custom_dns_config (line 855) | def write_custom_dns_config(config, env):
  function set_custom_dns_record (line 890) | def set_custom_dns_record(qname, rtype, value, action, env):
  function get_secondary_dns (line 982) | def get_secondary_dns(custom_dns, mode=None):
  function set_secondary_dns (line 1027) | def set_secondary_dns(hostnames, env):
  function get_custom_dns_records (line 1066) | def get_custom_dns_records(custom_dns, qname, rtype):
  function build_recommended_dns (line 1073) | def build_recommended_dns(env):

FILE: management/mail_log.py
  function scan_files (line 63) | def scan_files(collector):
  function scan_mail_log (line 93) | def scan_mail_log(env):
  function scan_mail_log_line (line 326) | def scan_mail_log_line(line, collector):
  function scan_postgrey_line (line 390) | def scan_postgrey_line(date, log, collector):
  function scan_postfix_smtpd_line (line 421) | def scan_postfix_smtpd_line(date, log, collector):
  function scan_dovecot_login_line (line 467) | def scan_dovecot_login_line(date, log, collector, protocol_name):
  function add_login (line 480) | def add_login(user, date, protocol_name, host, collector):
  function scan_postfix_lmtp_line (line 506) | def scan_postfix_lmtp_line(date, log, collector):
  function scan_postfix_submission_line (line 541) | def scan_postfix_submission_line(date, log, collector):
  function readline (line 585) | def readline(filename):
  function user_match (line 596) | def user_match(user):
  function email_sort (line 601) | def email_sort(email):
  function valid_date (line 606) | def valid_date(string):
  function print_time_table (line 618) | def print_time_table(labels, data, do_print=True):
  function print_user_table (line 641) | def print_user_table(users, data=None, sub_data=None, activity=None, lat...
  function print_header (line 782) | def print_header(msg):

FILE: management/mailconfig.py
  function validate_email (line 19) | def validate_email(email, mode=None):
  function sanitize_idn_email_address (line 57) | def sanitize_idn_email_address(email):
  function prettify_idn_email_address (line 77) | def prettify_idn_email_address(email):
  function is_dcv_address (line 89) | def is_dcv_address(email):
  function open_database (line 93) | def open_database(env, with_connection=False):
  function get_mail_users (line 99) | def get_mail_users(env):
  function sizeof_fmt (line 106) | def sizeof_fmt(num):
  function get_mail_users_ex (line 117) | def get_mail_users_ex(env, with_archived=False):
  function get_admins (line 220) | def get_admins(env):
  function get_mail_aliases (line 229) | def get_mail_aliases(env):
  function get_mail_aliases_ex (line 238) | def get_mail_aliases_ex(env):
  function get_domain (line 289) | def get_domain(emailaddr, as_unicode=True):
  function get_mail_domains (line 302) | def get_mail_domains(env, filter_aliases=lambda alias : True, users_only...
  function add_mail_user (line 313) | def add_mail_user(email, pw, privs, quota, env):
  function set_mail_password (line 368) | def set_mail_password(email, pw, env):
  function hash_password (line 383) | def hash_password(pw):
  function get_mail_quota (line 390) | def get_mail_quota(email, env):
  function set_mail_quota (line 400) | def set_mail_quota(email, quota, env):
  function dovecot_quota_recalc (line 415) | def dovecot_quota_recalc(email):
  function validate_quota (line 425) | def validate_quota(quota):
  function get_mail_password (line 441) | def get_mail_password(email, env):
  function remove_mail_user (line 454) | def remove_mail_user(email, env):
  function parse_privs (line 465) | def parse_privs(value):
  function get_mail_user_privileges (line 468) | def get_mail_user_privileges(email, env, empty_on_error=False):
  function validate_privilege (line 478) | def validate_privilege(priv):
  function add_remove_mail_user_privilege (line 483) | def add_remove_mail_user_privilege(email, priv, action, env):
  function add_mail_alias (line 510) | def add_mail_alias(address, forwards_to, permitted_senders, env, update_...
  function remove_mail_alias (line 602) | def remove_mail_alias(address, env, do_kick=True):
  function add_auto_aliases (line 618) | def add_auto_aliases(aliases, env):
  function get_system_administrator (line 625) | def get_system_administrator(env):
  function get_required_aliases (line 628) | def get_required_aliases(env):
  function kick (line 660) | def kick(env, mail_result=None):
  function validate_password (line 709) | def validate_password(pw):

FILE: management/mfa.py
  function get_user_id (line 10) | def get_user_id(email, c):
  function get_mfa_state (line 16) | def get_mfa_state(email, env):
  function get_public_mfa_state (line 24) | def get_public_mfa_state(email, env):
  function get_hash_mfa_state (line 31) | def get_hash_mfa_state(email, env):
  function enable_mfa (line 38) | def enable_mfa(email, type, secret, token, label, env):
  function set_mru_token (line 54) | def set_mru_token(email, mfa_id, token, env):
  function disable_mfa (line 59) | def disable_mfa(email, mfa_id, env):
  function validate_totp_secret (line 70) | def validate_totp_secret(secret):
  function provision_totp (line 78) | def provision_totp(email, env):
  function validate_auth_mfa (line 101) | def validate_auth_mfa(email, request, env):

FILE: management/ssl_certificates.py
  function get_ssl_certificates (line 12) | def get_ssl_certificates(env):
  function get_domain_ssl_files (line 141) | def get_domain_ssl_files(domain, ssl_certificates, env, allow_missing_ce...
  function get_certificates_to_provision (line 172) | def get_certificates_to_provision(env, limit_domains=None, show_valid_ce...
  function provision_certificates (line 240) | def provision_certificates(env, limit_domains):
  function provision_certificates_cmdline (line 374) | def provision_certificates_cmdline():
  function create_csr (line 410) | def create_csr(domain, ssl_key, country_code, env):
  function install_cert (line 417) | def install_cert(domain, ssl_cert, ssl_chain, env, raw=False):
  function install_cert_copy_file (line 445) | def install_cert_copy_file(fn, env):
  function post_install_func (line 464) | def post_install_func(env):
  function check_certificate (line 503) | def check_certificate(domain, ssl_certificate, ssl_private_key, warn_if_...
  function load_cert_chain (line 618) | def load_cert_chain(pemfile):
  function load_pem (line 630) | def load_pem(pem):
  function get_certificate_domains (line 647) | def get_certificate_domains(cert):

FILE: management/status_checks.py
  function get_services (line 24) | def get_services():
  function run_checks (line 47) | def run_checks(rounded_values, env, output, pool, domains_to_check=None):
  function run_services_checks (line 70) | def run_services_checks(env, output, pool):
  function check_service (line 92) | def check_service(i, service, env):
  function run_system_checks (line 153) | def run_system_checks(rounded_values, env, output):
  function check_ufw (line 162) | def check_ufw(env, output):
  function is_port_allowed (line 190) | def is_port_allowed(ufw, port):
  function check_ssh_password (line 193) | def check_ssh_password(env, output):
  function is_reboot_needed_due_to_package_installation (line 204) | def is_reboot_needed_due_to_package_installation():
  function check_software_updates (line 207) | def check_software_updates(env, output):
  function check_system_aliases (line 219) | def check_system_aliases(env, output):
  function check_free_disk_space (line 224) | def check_free_disk_space(rounded_values, env, output):
  function check_free_memory (line 251) | def check_free_memory(rounded_values, env, output):
  function check_backup (line 266) | def check_backup(rounded_values, env, output):
  function run_network_checks (line 296) | def run_network_checks(env, output):
  function evaluate_spamhaus_lookup (line 333) | def evaluate_spamhaus_lookup(lookupaddress, lookuptype, lookupdomain, ou...
  function run_domain_checks (line 359) | def run_domain_checks(rounded_time, env, output, pool, domains_to_check=...
  function run_domain_checks_on_domain (line 399) | def run_domain_checks_on_domain(domain, rounded_time, env, dns_domains, ...
  function check_primary_hostname_dns (line 456) | def check_primary_hostname_dns(domain, env, output, dns_domains, dns_zon...
  function check_alias_exists (line 528) | def check_alias_exists(alias_name, alias, env, output):
  function check_dns_zone (line 538) | def check_dns_zone(domain, env, output, dns_zonefiles):
  function check_dns_zone_suggestions (line 622) | def check_dns_zone_suggestions(domain, env, output, dns_zonefiles, domai...
  function check_dnssec (line 636) | def check_dnssec(domain, env, output, dns_zonefiles, is_checking_primary...
  function check_mail_domain (line 740) | def check_mail_domain(domain, env, output):
  function check_web_domain (line 825) | def check_web_domain(domain, rounded_time, ssl_certificates, env, output):
  function query_dns (line 851) | def query_dns(qname, rtype, nxdomain='[Not Set]', at=None, as_list=False):
  function check_ssl_cert (line 902) | def check_ssl_cert(domain, rounded_time, ssl_certificates, env, output):
  function list_apt_updates (line 942) | def list_apt_updates(apt_update=True):
  function what_version_is_this (line 975) | def what_version_is_this(env):
  function get_latest_miab_version (line 982) | def get_latest_miab_version():
  function check_miab_version (line 992) | def check_miab_version(env, output):
  function run_and_output_changes (line 1012) | def run_and_output_changes(env, pool):
  function normalize_ip (line 1084) | def normalize_ip(ip):
  class FileOutput (line 1094) | class FileOutput:
    method __init__ (line 1095) | def __init__(self, buf, width):
    method add_heading (line 1099) | def add_heading(self, heading):
    method print_ok (line 1104) | def print_ok(self, message):
    method print_error (line 1107) | def print_error(self, message):
    method print_warning (line 1110) | def print_warning(self, message):
    method print_block (line 1113) | def print_block(self, message, first_line="   "):
    method print_line (line 1128) | def print_line(self, message, monospace=False):
  class ConsoleOutput (line 1132) | class ConsoleOutput(FileOutput):
    method __init__ (line 1133) | def __init__(self):
  class BufferedOutput (line 1152) | class BufferedOutput:
    method __init__ (line 1154) | def __init__(self, with_lines=None):
    method __getattr__ (line 1156) | def __getattr__(self, attr):
    method playback (line 1163) | def playback(self, output):

FILE: management/utils.py
  function load_environment (line 9) | def load_environment():
  function load_env_vars_from_file (line 13) | def load_env_vars_from_file(fn):
  function save_environment (line 22) | def save_environment(env):
  function write_settings (line 28) | def write_settings(config, env):
  function load_settings (line 34) | def load_settings(env):
  function safe_domain_name (line 47) | def safe_domain_name(name):
  function sort_domains (line 52) | def sort_domains(domain_names, env):
  function sort_email_addresses (line 99) | def sort_email_addresses(email_addresses, env):
  function shell (line 110) | def shell(method, cmd_args, env=None, capture_stderr=False, return_bytes...
  function create_syslog_handler (line 142) | def create_syslog_handler():
  function du (line 148) | def du(path):
  function wait_for_service (line 167) | def wait_for_service(port, public, env, timeout):
  function get_ssh_port (line 183) | def get_ssh_port():
  function get_ssh_config_value (line 191) | def get_ssh_config_value(parameter_name):

FILE: management/web_update.py
  function get_web_domains (line 12) | def get_web_domains(env, include_www_redirects=True, include_auto=True, ...
  function get_domains_with_a_records (line 51) | def get_domains_with_a_records(env):
  function get_web_domains_with_root_overrides (line 59) | def get_web_domains_with_root_overrides(env):
  function do_web_update (line 74) | def do_web_update(env):
  function make_domain_config (line 131) | def make_domain_config(domain, templates, ssl_certificates, env):
  function get_web_root (line 237) | def get_web_root(domain, env, test_exists=True):
  function get_web_domains_info (line 244) | def get_web_domains_info(env):

FILE: setup/migrate.py
  function migration_1 (line 14) | def migration_1(env):
  function migration_2 (line 38) | def migration_2(env):
  function migration_3 (line 47) | def migration_3(env):
  function migration_4 (line 53) | def migration_4(env):
  function migration_5 (line 58) | def migration_5(env):
  function migration_6 (line 62) | def migration_6(env):
  function migration_7 (line 69) | def migration_7(env):
  function migration_8 (line 97) | def migration_8(env):
  function migration_9 (line 103) | def migration_9(env):
  function migration_10 (line 113) | def migration_10(env):
  function migration_11 (line 139) | def migration_11(env):
  function migration_12 (line 150) | def migration_12(env):
  function migration_13 (line 183) | def migration_13(env):
  function migration_14 (line 188) | def migration_14(env):
  function migration_15 (line 193) | def migration_15(env):
  function get_current_migration (line 201) | def get_current_migration():
  function run_migrations (line 210) | def run_migrations():

FILE: tests/fail2ban.py
  class IsBlocked (line 24) | class IsBlocked(Exception):
  function smtp_test (line 28) | def smtp_test():
  function imap_test (line 53) | def imap_test():
  function pop_test (line 73) | def pop_test():
  function managesieve_test (line 95) | def managesieve_test():
  function http_test (line 116) | def http_test(url, expected_status, postdata=None, qsargs=None, auth=None):
  function restart_fail2ban_service (line 150) | def restart_fail2ban_service(final=False):
  function testfunc_runner (line 158) | def testfunc_runner(i, testfunc, *args):
  function run_test (line 162) | def run_test(testfunc, args, count, within_seconds, parallel):

FILE: tests/test_dns.py
  function test (line 22) | def test(server, description):
  function test_ptr (line 37) | def test_ptr(server, description):
  function test2 (line 44) | def test2(tests, server, description):

FILE: tests/tls.py
  function sslyze (line 70) | def sslyze(opts, port, ok_ciphers):

FILE: tools/readable_bash.py
  function generate_documentation (line 10) | def generate_documentation():
  class HashBang (line 155) | class HashBang(Grammar):
    method value (line 157) | def value(self):
  function strip_indent (line 160) | def strip_indent(s):
  class Comment (line 171) | class Comment(Grammar):
    method value (line 173) | def value(self):
  class Source (line 183) | class Source(Grammar):
    method filename (line 185) | def filename(self):
    method value (line 187) | def value(self):
  class CatEOF (line 190) | class CatEOF(Grammar):
    method value (line 192) | def value(self):
  class HideOutput (line 199) | class HideOutput(Grammar):
    method value (line 201) | def value(self):
  class EchoLine (line 204) | class EchoLine(Grammar):
    method value (line 206) | def value(self):
  class EditConf (line 211) | class EditConf(Grammar):
    method value (line 224) | def value(self):
  class CaptureOutput (line 235) | class CaptureOutput(Grammar):
    method value (line 237) | def value(self):
  class SedReplace (line 242) | class SedReplace(Grammar):
    method value (line 244) | def value(self):
  class EchoPipe (line 247) | class EchoPipe(Grammar):
    method value (line 249) | def value(self):
  function shell_line (line 253) | def shell_line(bash):
  class AptGet (line 256) | class AptGet(Grammar):
    method value (line 258) | def value(self):
  class UfwAllow (line 260) | class UfwAllow(Grammar):
    method value (line 262) | def value(self):
  class UfwLimit (line 264) | class UfwLimit(Grammar):
    method value (line 266) | def value(self):
  class RestartService (line 268) | class RestartService(Grammar):
    method value (line 270) | def value(self):
  class OtherLine (line 273) | class OtherLine(Grammar):
    method value (line 275) | def value(self):
  class BashElement (line 281) | class BashElement(Grammar):
    method value (line 283) | def value(self):
  function quasitokenize (line 299) | def quasitokenize(bashscript):
  function recode_bash (line 373) | def recode_bash(s):
  function fixup_tokens (line 387) | def fixup_tokens(s):
  class BashScript (line 396) | class BashScript(Grammar):
    method value (line 398) | def value(self):
    method parse (line 402) | def parse(fn):
  function wrap_lines (line 468) | def wrap_lines(text, cols=60):
Condensed preview — 110 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (905K chars).
[
  {
    "path": ".editorconfig",
    "chars": 435,
    "preview": "# EditorConfig helps developers define and maintain consistent\n# coding styles between different editors and IDEs\n# edit"
  },
  {
    "path": ".gitignore",
    "chars": 130,
    "preview": "*~\ntests/__pycache__/\nmanagement/__pycache__/\ntools/__pycache__/\nexternals/\n.env\n.vagrant\napi/docs/api-docs.html\n*.code-"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 65422,
    "preview": "CHANGELOG\n=========\n\nVersion 74 (January 4, 2026)\n----------------------------\n\n* Updated Roundcube to 1.6.12, fixing a "
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 4867,
    "preview": "# Mail-in-a-Box Code of Conduct\n\nMail-in-a-Box is an open source community project about working, as a group, to empower"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 2720,
    "preview": "# Contributing\n\nMail-in-a-Box is an open source project. Your contributions and pull requests are welcome.\n\n## Developme"
  },
  {
    "path": "LICENSE",
    "chars": 7048,
    "preview": "Creative Commons Legal Code\n\nCC0 1.0 Universal\n\n    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE\n"
  },
  {
    "path": "README.md",
    "chars": 6880,
    "preview": "Mail-in-a-Box\n=============\n\nBy [@JoshData](https://github.com/JoshData) and [contributors](https://github.com/mail-in-a"
  },
  {
    "path": "Vagrantfile",
    "chars": 912,
    "preview": "# -*- mode: ruby -*-\n# vi: set ft=ruby :\n\nVagrant.configure(\"2\") do |config|\n  config.vm.box = \"ubuntu/jammy64\"\n\n  # Net"
  },
  {
    "path": "api/docs/generate-docs.sh",
    "chars": 861,
    "preview": "#!/usr/bin/env sh\n\n# Requirements:\n# - Node.js\n# - redoc-cli (`npm install redoc-cli -g`)\n\nredoc-cli bundle ../mailinabo"
  },
  {
    "path": "api/docs/template.hbs",
    "chars": 885,
    "preview": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf8\" />\n  <title>{{title}}</title>\n  <meta name=\"viewport\" content=\"wid"
  },
  {
    "path": "api/mailinabox.yml",
    "chars": 82910,
    "preview": "openapi: 3.0.3\ninfo:\n  title: Mail-in-a-Box\n  description: |\n    Mail-in-a-Box API HTTP specification.\n\n    # Introducti"
  },
  {
    "path": "conf/dovecot-mailboxes.conf",
    "chars": 2195,
    "preview": "## NOTE: This file is automatically generated by Mail-in-a-Box.\n##       Do not edit this file. It is continually update"
  },
  {
    "path": "conf/fail2ban/filter.d/dovecotimap.conf",
    "chars": 831,
    "preview": "# Fail2Ban filter Dovecot authentication and pop3/imap/managesieve server\n# For Mail-in-a-Box\n\n[INCLUDES]\n\nbefore = comm"
  },
  {
    "path": "conf/fail2ban/filter.d/miab-management-daemon.conf",
    "chars": 231,
    "preview": "# Fail2Ban filter Mail-in-a-Box management daemon\n\n[INCLUDES]\n\nbefore = common.conf\n\n[Definition]\n\n_daemon = mailinabox\n"
  },
  {
    "path": "conf/fail2ban/filter.d/miab-munin.conf",
    "chars": 124,
    "preview": "[INCLUDES]\n\nbefore = common.conf\n\n[Definition]\nfailregex=<HOST> - .*GET /admin/munin/.* HTTP/\\d+\\.\\d+\\\" 401.*\nignorerege"
  },
  {
    "path": "conf/fail2ban/filter.d/miab-owncloud.conf",
    "chars": 365,
    "preview": "[INCLUDES]\n\nbefore = common.conf\n\n[Definition]\n_groupsre = (?:(?:,?\\s*\"\\w+\":(?:\"[^\"]+\"|\\w+))*)\nfailregex = ^\\{%(_groupsr"
  },
  {
    "path": "conf/fail2ban/filter.d/miab-postfix-submission.conf",
    "chars": 153,
    "preview": "[INCLUDES]\n\nbefore = common.conf\n\n[Definition]\nfailregex=postfix/submission/smtpd.*warning.*\\[<HOST>\\]: .* authenticatio"
  },
  {
    "path": "conf/fail2ban/filter.d/miab-roundcube.conf",
    "chars": 138,
    "preview": "[INCLUDES]\n\nbefore = common.conf\n\n[Definition]\n\nfailregex = IMAP Error: Login failed for .*? from <HOST>\\. AUTHENTICATE."
  },
  {
    "path": "conf/fail2ban/jails.conf",
    "chars": 2141,
    "preview": "# Fail2Ban configuration file for Mail-in-a-Box. Do not edit.\n# This file is re-generated on updates.\n\n[DEFAULT]\n# White"
  },
  {
    "path": "conf/ios-profile.xml",
    "chars": 4282,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "conf/mailinabox.service",
    "chars": 204,
    "preview": "[Unit]\nDescription=Mail-in-a-Box System Management Service\nAfter=multi-user.target\n\n[Service]\nType=idle\nIgnoreSIGPIPE=Fa"
  },
  {
    "path": "conf/mozilla-autoconfig.xml",
    "chars": 2490,
    "preview": "<?xml version=\"1.0\"?>\n<clientConfig version=\"1.1\">\n    <emailProvider id=\"PRIMARY_HOSTNAME\">\n      <domain>PRIMARY_HOSTN"
  },
  {
    "path": "conf/mta-sts.txt",
    "chars": 67,
    "preview": "version: STSv1\r\nmode: MODE\r\nmx: PRIMARY_HOSTNAME\r\nmax_age: 604800\r\n"
  },
  {
    "path": "conf/munin.service",
    "chars": 192,
    "preview": "[Unit]\nDescription=Munin System Monitoring Startup Script\nAfter=multi-user.target\n\n[Service]\nType=idle\nExecStart=/usr/lo"
  },
  {
    "path": "conf/nginx-alldomains.conf",
    "chars": 2785,
    "preview": "\t# Expose this directory as static files.\n\troot $ROOT;\n\tindex index.html index.htm;\n\n\tlocation = /robots.txt {\n\t\tlog_not"
  },
  {
    "path": "conf/nginx-primaryonly.conf",
    "chars": 3360,
    "preview": "\t# Control Panel\n\t# Proxy /admin to our Python based control panel daemon. It is\n\t# listening on IPv4 only so use an IP "
  },
  {
    "path": "conf/nginx-ssl.conf",
    "chars": 846,
    "preview": "# We track the Mozilla \"intermediate\" compatibility TLS recommendations.\n# Note that these settings are repeated in the "
  },
  {
    "path": "conf/nginx-top.conf",
    "chars": 427,
    "preview": "## NOTE: This file is automatically generated by Mail-in-a-Box.\n##       Do not edit this file. It is continually update"
  },
  {
    "path": "conf/nginx.conf",
    "chars": 1318,
    "preview": "## $HOSTNAME\n\n# Redirect all HTTP to HTTPS *except* the ACME challenges (Let's Encrypt TLS certificate\n# domain validati"
  },
  {
    "path": "conf/postfix_outgoing_mail_header_filters",
    "chars": 726,
    "preview": "# Remove the first line of the Received: header. Note that we cannot fully remove the Received: header\n# because OpenDKI"
  },
  {
    "path": "conf/sieve-spam.txt",
    "chars": 126,
    "preview": "require [\"regex\", \"fileinto\", \"imap4flags\"];\n\nif allof (header :regex \"X-Spam-Status\" \"^Yes\") {\n  fileinto \"Spam\";\n  sto"
  },
  {
    "path": "conf/www_default.html",
    "chars": 271,
    "preview": "<html>\n\t<head>\n\t\t<title>this is a mail-in-a-box</title>\n\t\t<meta name=\"robots\" content=\"noindex\">\n\t</head>\n\t<body>\n\t\t<h1>"
  },
  {
    "path": "conf/zpush/autodiscover_config.php",
    "chars": 752,
    "preview": "<?php\n/***********************************************\n* File      :   config.php\n* Project   :   Z-Push\n* Descr     :  "
  },
  {
    "path": "conf/zpush/backend_caldav.php",
    "chars": 487,
    "preview": "<?php\n/***********************************************\n* File      :   config.php\n* Project   :   Z-Push\n* Descr     :  "
  },
  {
    "path": "conf/zpush/backend_carddav.php",
    "chars": 1242,
    "preview": "<?php\n/***********************************************\n* File      :   config.php\n* Project   :   Z-Push\n* Descr     :  "
  },
  {
    "path": "conf/zpush/backend_combined.php",
    "chars": 1358,
    "preview": "<?php\n/***********************************************\n* File      :   backend/combined/config.php\n* Project   :   Z-Pus"
  },
  {
    "path": "conf/zpush/backend_imap.php",
    "chars": 2357,
    "preview": "<?php\n/***********************************************\n* File      :   config.php\n* Project   :   Z-Push\n* Descr     :  "
  },
  {
    "path": "management/auth.py",
    "chars": 6101,
    "preview": "import base64, hmac, json, secrets\nfrom datetime import timedelta\n\nfrom expiringdict import ExpiringDict\n\nimport utils\nf"
  },
  {
    "path": "management/backup.py",
    "chars": 23840,
    "preview": "#!/usr/local/lib/mailinabox/env/bin/python\n\n# This script performs a backup of all user data:\n# 1) System services are s"
  },
  {
    "path": "management/cli.py",
    "chars": 5842,
    "preview": "#!/usr/bin/python3\n#\n# This is a command-line script for calling management APIs\n# on the Mail-in-a-Box control panel ba"
  },
  {
    "path": "management/csr_country_codes.tsv",
    "chars": 4444,
    "preview": "# This list is derived from https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2.\n# The columns are ISO_3166-1_alpha-2 code,"
  },
  {
    "path": "management/daemon.py",
    "chars": 27240,
    "preview": "#!/usr/local/lib/mailinabox/env/bin/python3\n#\n# The API can be accessed on the command line, e.g. use `curl` like so:\n# "
  },
  {
    "path": "management/daily_tasks.sh",
    "chars": 1087,
    "preview": "#!/bin/bash\n# This script is run daily (at 3am each night).\n\n# Set character encoding flags to ensure that any non-ASCII"
  },
  {
    "path": "management/dns_update.py",
    "chars": 47275,
    "preview": "#!/usr/local/lib/mailinabox/env/bin/python\n\n# Creates DNS zone files for all of the domains of all of the mail users\n# a"
  },
  {
    "path": "management/email_administrator.py",
    "chars": 1417,
    "preview": "#!/usr/local/lib/mailinabox/env/bin/python\n\n# Reads in STDIN. If the stream is not empty, mail it to the system administ"
  },
  {
    "path": "management/mail_log.py",
    "chars": 29427,
    "preview": "#!/usr/local/lib/mailinabox/env/bin/python\nimport argparse\nimport datetime\nimport gzip\nimport os.path\nimport re\nimport s"
  },
  {
    "path": "management/mailconfig.py",
    "chars": 24819,
    "preview": "#!/usr/local/lib/mailinabox/env/bin/python\n\n# NOTE:\n# This script is run both using the system-wide Python 3\n# interpret"
  },
  {
    "path": "management/mfa.py",
    "chars": 4412,
    "preview": "import base64\nimport hmac\nimport io\nimport os\nimport pyotp\nimport qrcode\n\nfrom mailconfig import open_database\n\ndef get_"
  },
  {
    "path": "management/munin_start.sh",
    "chars": 66,
    "preview": "#!/bin/bash\nmkdir -p /var/run/munin && chown munin /var/run/munin\n"
  },
  {
    "path": "management/ssl_certificates.py",
    "chars": 26783,
    "preview": "#!/usr/local/lib/mailinabox/env/bin/python\n# Utilities for installing and selecting SSL certificates.\n\nimport os, os.pat"
  },
  {
    "path": "management/status_checks.py",
    "chars": 55042,
    "preview": "#!/usr/local/lib/mailinabox/env/bin/python\n#\n# Checks that the upstream DNS has been set correctly and that\n# TLS certif"
  },
  {
    "path": "management/templates/aliases.html",
    "chars": 13269,
    "preview": "<style>\n#alias_table .actions > * { padding-right: 3px; }\n#alias_table .alias-auto .actions > * { display: none }\n</styl"
  },
  {
    "path": "management/templates/custom-dns.html",
    "chars": 13939,
    "preview": "<style>\n#custom-dns-current td.long {\n  word-break: break-all;\n}\n</style>\n\n<h2>Custom DNS</h2>\n\n<p class=\"text-warning\">"
  },
  {
    "path": "management/templates/external-dns.html",
    "chars": 4138,
    "preview": "<style>\n#external_dns_settings .heading td {\n  font-weight: bold;\n  font-size: 120%;\n  padding-top: 1.5em;\n}\n#external_d"
  },
  {
    "path": "management/templates/index.html",
    "chars": 15678,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n    <head>\n        <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\">\n     "
  },
  {
    "path": "management/templates/login.html",
    "chars": 6870,
    "preview": "<style>\n  .title {\n    margin: 1em;\n    text-align: center;\n  }\n\n  .subtitle {\n    margin: 2em;\n    text-align: center;\n"
  },
  {
    "path": "management/templates/mail-guide.html",
    "chars": 4271,
    "preview": "<style>#panel_mail-guide table.table { width: auto; margin-left: .5em; }</style>\n\n<div>\n\t<h2 style=\"margin-bottom: 0\">Ch"
  },
  {
    "path": "management/templates/mfa.html",
    "chars": 8097,
    "preview": "<style>\n    .twofactor #totp-setup,\n    .twofactor #disable-2fa,\n    .twofactor #output-2fa {\n        display: none;\n   "
  },
  {
    "path": "management/templates/munin.html",
    "chars": 336,
    "preview": "<h2>Munin Monitoring</h2>\n\n<style>\n</style>\n\n<p>Opening munin in a new tab... You may need to allow pop-ups for this sit"
  },
  {
    "path": "management/templates/ssl.html",
    "chars": 9166,
    "preview": "<style>\n</style>\n\n<h2>TLS (SSL) Certificates</h2>\n\n<p>A TLS (formerly called SSL) certificate is a cryptographic file th"
  },
  {
    "path": "management/templates/sync-guide.html",
    "chars": 1798,
    "preview": "<div>\n\t<h2>Contacts &amp; Calendar Synchronization</h2>\n\n\t<p>This box can hold your contacts and calendar, just like it "
  },
  {
    "path": "management/templates/system-backup.html",
    "chars": 16946,
    "preview": "<style>\n#backup-status th { text-align: center; }\n#backup-status tr.full-backup td { font-weight: bold; }\n</style>\n\n<h2>"
  },
  {
    "path": "management/templates/system-status.html",
    "chars": 5503,
    "preview": "<h2>System Status Checks</h2>\n\n<style>\n#system-checks .heading td {\n  font-weight: bold;\n  font-size: 120%;\n  padding-to"
  },
  {
    "path": "management/templates/users.html",
    "chars": 14161,
    "preview": "<h2>Users</h2>\n\n<style>\n#user_table tr.account_inactive td.address { color: #888; text-decoration: line-through; }\n#user"
  },
  {
    "path": "management/templates/web.html",
    "chars": 3919,
    "preview": "<style>\n</style>\n\n<h2>Static Web Hosting</h2>\n\n<p>This machine is serving a simple, static website at <a href=\"https://{"
  },
  {
    "path": "management/templates/welcome.html",
    "chars": 241,
    "preview": "<style>\n  .title {\n    margin: 1em;\n    text-align: center;\n  }\n\n  .subtitle {\n    margin: 2em;\n    text-align: center;\n"
  },
  {
    "path": "management/utils.py",
    "chars": 6926,
    "preview": "import os.path\n\n# DO NOT import non-standard modules. This module is imported by\n# migrate.py which runs on fresh machin"
  },
  {
    "path": "management/web_update.py",
    "chars": 11545,
    "preview": "# Creates an nginx configuration file so we serve HTTP/HTTPS on all\n# domains for which a mail account has been set up.\n"
  },
  {
    "path": "management/wsgi.py",
    "chars": 142,
    "preview": "from daemon import app\nimport utils\n\napp.logger.addHandler(utils.create_syslog_handler())\n\nif __name__ == \"__main__\":\n  "
  },
  {
    "path": "pyproject.toml",
    "chars": 692,
    "preview": "[tool.ruff]\nline-length = 320 # https://github.com/astral-sh/ruff/issues/8106\nindent-width = 4\n\ntarget-version = \"py310\""
  },
  {
    "path": "security.md",
    "chars": 10434,
    "preview": "Mail-in-a-Box Security Guide\n============================\n\nMail-in-a-Box turns a fresh Ubuntu 22.04 LTS 64-bit machine i"
  },
  {
    "path": "setup/bootstrap.sh",
    "chars": 3019,
    "preview": "#!/bin/bash\n#########################################################\n# This script is intended to be run like this:\n#\n#"
  },
  {
    "path": "setup/dkim.sh",
    "chars": 4751,
    "preview": "#!/bin/bash\n# OpenDKIM\n# --------\n#\n# OpenDKIM provides a service that puts a DKIM signature on outbound mail.\n#\n# The D"
  },
  {
    "path": "setup/dns.sh",
    "chars": 5681,
    "preview": "#!/bin/bash\n# DNS\n# -----------------------------------------------\n\n# This script installs packages, but the DNS zone f"
  },
  {
    "path": "setup/firstuser.sh",
    "chars": 1844,
    "preview": "#!/bin/bash\n# If there aren't any mail users yet, create one.\nif [ -z \"$(management/cli.py user)\" ]; then\n\t# The output "
  },
  {
    "path": "setup/functions.sh",
    "chars": 7594,
    "preview": "#!/bin/bash\n# Turn on \"strict mode.\" See http://redsymbol.net/articles/unofficial-bash-strict-mode/.\n# -e: exit if any c"
  },
  {
    "path": "setup/mail-dovecot.sh",
    "chars": 10333,
    "preview": "#!/bin/bash\n#\n# Dovecot (IMAP/POP and LDA)\n# ----------------------\n#\n# Dovecot is *both* the IMAP/POP server (the proto"
  },
  {
    "path": "setup/mail-postfix.sh",
    "chars": 17554,
    "preview": "#!/bin/bash\n#\n# Postfix (SMTP)\n# --------------\n#\n# Postfix handles the transmission of email between servers\n# using th"
  },
  {
    "path": "setup/mail-users.sh",
    "chars": 7668,
    "preview": "#!/bin/bash\n#\n# User Authentication and Destination Validation\n# ----------------------------------------------\n#\n# This"
  },
  {
    "path": "setup/management.sh",
    "chars": 4449,
    "preview": "#!/bin/bash\n\nsource setup/functions.sh\nsource /etc/mailinabox.conf # load global vars\n\necho \"Installing Mail-in-a-Box sy"
  },
  {
    "path": "setup/migrate.py",
    "chars": 11114,
    "preview": "#!/usr/bin/python3\n\n# Migrates any file structures, database schemas, etc. between versions of Mail-in-a-Box.\n\n# We have"
  },
  {
    "path": "setup/munin.sh",
    "chars": 3232,
    "preview": "#!/bin/bash\n# Munin: resource monitoring tool\n#################################################\n\nsource setup/functions."
  },
  {
    "path": "setup/network-checks.sh",
    "chars": 2467,
    "preview": "#!/bin/bash\n# Install the 'host', 'sed', and and 'nc' tools. This script is run before\n# the rest of the system setup so"
  },
  {
    "path": "setup/nextcloud.sh",
    "chars": 20330,
    "preview": "#!/bin/bash\n# Nextcloud\n##########################\n\nsource setup/functions.sh # load our functions\nsource /etc/mailinabo"
  },
  {
    "path": "setup/preflight.sh",
    "chars": 2497,
    "preview": "#!/bin/bash\n# Are we running as root?\nif [[ $EUID -ne 0 ]]; then\n\techo \"This script must be run as root. Please re-run l"
  },
  {
    "path": "setup/questions.sh",
    "chars": 7826,
    "preview": "#!/bin/bash\nif [ -z \"${NONINTERACTIVE:-}\" ]; then\n\t# Install 'dialog' so we can ask the user questions. The original mot"
  },
  {
    "path": "setup/spamassassin.sh",
    "chars": 8113,
    "preview": "#!/bin/bash\n# Spam filtering with spamassassin via spampd\n# -------------------------------------------\n#\n# spampd sits "
  },
  {
    "path": "setup/ssl.sh",
    "chars": 4259,
    "preview": "#!/bin/bash\n#\n# RSA private key, SSL certificate, Diffie-Hellman bits files\n# ------------------------------------------"
  },
  {
    "path": "setup/start.sh",
    "chars": 6531,
    "preview": "#!/bin/bash\n# This is the entry point for configuring the system.\n#####################################################\n"
  },
  {
    "path": "setup/system.sh",
    "chars": 16242,
    "preview": "#!/bin/bash\nsource /etc/mailinabox.conf\nsource setup/functions.sh # load our functions\n\n# Basic System Configuration\n# -"
  },
  {
    "path": "setup/web.sh",
    "chars": 6364,
    "preview": "#!/bin/bash\n# HTTP: Turn on a web server serving static files\n#################################################\n\nsource "
  },
  {
    "path": "setup/webmail.sh",
    "chars": 9178,
    "preview": "#!/bin/bash\n# Webmail with Roundcube\n# ----------------------\n\nsource setup/functions.sh # load our functions\nsource /et"
  },
  {
    "path": "setup/zpush.sh",
    "chars": 3955,
    "preview": "#!/bin/bash\n#\n# Z-Push: The Microsoft Exchange protocol server\n# ----------------------------------------------\n#\n# Most"
  },
  {
    "path": "tests/fail2ban.py",
    "chars": 6353,
    "preview": "# Test that a box's fail2ban setting are working\n# correctly by attempting a bunch of failed logins.\n#\n# Specify a SSH l"
  },
  {
    "path": "tests/pip-requirements.txt",
    "chars": 11,
    "preview": "dnspython3\n"
  },
  {
    "path": "tests/test_dns.py",
    "chars": 3823,
    "preview": "#!/usr/bin/env python3\n#\n# Tests the DNS configuration of a Mail-in-a-Box.\n#\n# tests/dns.py ipaddr hostname\n#\n# where ip"
  },
  {
    "path": "tests/test_mail.py",
    "chars": 3246,
    "preview": "#!/usr/bin/env python3\n# Tests sending and receiving mail by sending a test message to yourself.\n\nimport sys, imaplib, s"
  },
  {
    "path": "tests/test_smtp_server.py",
    "chars": 404,
    "preview": "#!/usr/bin/env python3\nimport smtplib, sys\n\nif len(sys.argv) < 3:\n        print(\"Usage: tests/smtp_server.py host email."
  },
  {
    "path": "tests/tls.py",
    "chars": 7541,
    "preview": "#!/usr/bin/python3\n\n# Runs SSLyze on the TLS endpoints of a box and outputs\n# the results so we can inspect the settings"
  },
  {
    "path": "tests/tls_results.txt",
    "chars": 33401,
    "preview": "PORT 25\n-------\n\n  * Deflate Compression:\n      OK - Compression disabled          \n\n  * Session Renegotiation:\n      Cl"
  },
  {
    "path": "tools/archive_conf_files.sh",
    "chars": 416,
    "preview": "#!/bin/bash\n# Use this script to make an archive of the contents of all\n# of the configuration files we edit with editco"
  },
  {
    "path": "tools/dns_update",
    "chars": 176,
    "preview": "#!/bin/bash\nPOSTDATA=dummy\nif [ \"$1\" == \"--force\" ]; then\n\tPOSTDATA=force=1\nfi\ncurl -s -d $POSTDATA --user \"$(</var/lib/"
  },
  {
    "path": "tools/editconf.py",
    "chars": 4406,
    "preview": "#!/usr/bin/python3\n#\n# This is a helper tool for editing configuration files during the setup\n# process. The tool is giv"
  },
  {
    "path": "tools/mail.py",
    "chars": 60,
    "preview": "#!/bin/bash\n# This script has moved.\nmanagement/cli.py \"$@\"\n"
  },
  {
    "path": "tools/owncloud-restore.sh",
    "chars": 1528,
    "preview": "#!/bin/bash\n#\n# This script will restore the backup made during an installation\nsource /etc/mailinabox.conf # load globa"
  },
  {
    "path": "tools/owncloud-unlockadmin.sh",
    "chars": 784,
    "preview": "#!/bin/bash\n#\n# This script will give you administrative access to the Nextcloud\n# instance running here.\n#\n# Run this a"
  },
  {
    "path": "tools/parse-nginx-log-bootstrap-accesses.py",
    "chars": 2072,
    "preview": "#!/usr/bin/python3\n#\n# This is a tool Josh uses on his box serving mailinabox.email to parse the nginx\n# access log to s"
  },
  {
    "path": "tools/readable_bash.py",
    "chars": 14577,
    "preview": "#!/usr/bin/python3\n#\n# Generate documentation for how this machine works by\n# parsing our bash scripts!\n\nimport cgi, re\n"
  },
  {
    "path": "tools/ssl_cleanup",
    "chars": 540,
    "preview": "#!/bin/bash\n# Cleanup SSL certificates which expired more than 7 days ago from $STORAGE_ROOT/ssl and move them to $STORA"
  },
  {
    "path": "tools/web_update",
    "chars": 108,
    "preview": "#!/bin/bash\ncurl -s -d POSTDATA --user \"$(</var/lib/mailinabox/api.key):\" http://127.0.0.1:10222/web/update\n"
  }
]

About this extraction

This page contains the full source code of the mail-in-a-box/mailinabox GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 110 files (832.9 KB), approximately 223.4k tokens, and a symbol index with 318 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!