Version 1.50.0

Released: 2016-02-21

Checkbox to restore SPF TXT from backup, or use local value (SKINS) new

Previously, all v=spf1 TXT/SPF records were ignored during the restore, and only the local dns_txt.conf / dns_spf.conf values were used for v=spf1, upon restore.

A new checkbox has been added to:

Admin Level -> Admin Backup/Transfer -> Backup/Restore settings
Reseller Level -> Manage User Backup -> Backup/Restore settings


"Restore with SPF values from backup. (unchecked: Use local spf values)"

which defaults to unchecked/off, to maintain backwards compatibility for old skins are not yet updated.

When checked, restores will not do the previous behavior, and instead will restore whatever v=spf1 values were stored in the backup .db file, for both TXT and SPF record types. Admin/Reseller backups new

New script called just before a backup job is saved, either to a cron or to the task.queue.

Applies for both creation and modification of backups, and backup crons.

Env data passed to the script will match exactly what is given to the task.queue upon backup creation, unless "when=cron" is set, then it will only be saved (no task.queue entry)

Too many options to list, just create sample task.queue entries for the data you're looking to check.

Some variables:

(all cron date items)
(append_to_path=%something)  optional
(ftp_username, ftp_path, ftp_port, ftp_secure, ftp_password)
(select0=george, select1=gary, etc..)
(all of the "what_select" checkboxes)

Similar to other script, if you exit with a non-zero value, the process will abort and an error message shown on screen.

To let the save continue, use:

exit 0;

Admin Level restores to override domain limits new

Related error:

"Cannot add, it would exceed your limit"

occurs if you have multiple domains in your User backup, but the user.conf has vdomains=1 (or lower the number of domains).

Or if the User already exists, new domains exist in the account and not in the backup, and the backup user.conf limit is low.

Solution is to simply skip the check if a restore is being done as an Admin.

The Admin Level restores will now throw this warning, instead of the abort/error:

"Creating domain exceeds the limit, but the Admin Level restore overrides the restriction."

so it will continue normally.

However, you'd probably want to fix the domain limits after the restore is done, so you don't have 2 created domains with a limit of 1. new

Similar to, this script will be called before a User is Modified.

However, unlike, the will pass the post variables given to it through the POST, and not the user.conf data.

So you'll be able to check what was passed to decide if you want to abort (with an echo and a non-zero exist status). and new

Similar to and user_modify_post.s, except it's for changes done to a Reseller. will be passed the contents of the POST.

Any non-zero exit value will display any echo-ed output to the screen, and the modification will be aborted.

Exit with a 0 value to not display anything.

Does not apply to "action=single" values. will be called after the Reseller modifications are done and saved.

The script will be passed the contents of the new reseller.conf file.

Exit value does not affect anything, but if you output a non-zero, then the output will be saved to the error.log (not displayed)

BFM: Speed improvement on reading new

For anyone who is using the Brute Force Monitor and has set their "unblock" time to a very high value, your blocked IP list may get quite large.

In that case, DA may have troubles reading this over-sized file in a timely manner, possibly causing a timeout on the BFM page.

In debug mode, you'd see this as the last output before it hangs:

ServerInfo::fill_brute_force_tokens : have blocked_ips

Fix was to change the sorting method of the container class.

Global custom include templates for apache/nginx (SKINS) new

This feature will let you insert global custom values into the CUSTOM and CUSTOM# variables in the apache/nginx templates.

You can think of this as a virtual_host2.conf subset template, that is only used if exists, and gets inserted into the CUSTOM tokens for all VirtualHosts.

It would be handy if you want add code to all VirtualHosts, but don't want to create new templates, and don't want to add it to each domain's Custom Httpd Config.

Add to new custom template token files to the data/templates/custom folder, matching the token name you want, with a .pre or .post suffix eg:


with the apache/nginx code that you want to be inserted to the given token.

The prefix is either cust_httpd. or cust_nginx. to determine which server type the token is for.

Where .pre means that your code would be added before any CUSTOM/CUSTOM.# values set in:

Admin Level -> Custom Httpd Config -> -> CUSTOM

or .post means it would be added after any Custom Httpd Config data.

You can use tokens as desired.

If you're not sure if you should use pre or post, keep in mind that tokens can be set in Custom Httpd Config, so you could then read them if they're set, and alter the behavior of your using if-then-else statements.



Old textarea for the CUSTOM token:

<textarea class="code_edit" cols=120 rows=10 wrap=off name=config>|CONFIG|</textarea>

Changed to add 2 more: before and after, also in divs:

<div><textarea class="code_edit" cols=120 rows=2 wrap=off readonly>|CUSTOM_PRE|</textarea></div>

<div><textarea class="code_edit" cols=120 rows=10 wrap=off name=config>|CONFIG|</textarea></div>

<div><textarea class="code_edit" cols=120 rows=2 wrap=off readonly>|CUSTOM_POST|</textarea></div>

same idea for the CUSTOM1-4 tokens, eg#1:

<div><textarea class="code_edit" cols=120 rows=2 wrap=off readonly>|CUSTOM1_PRE|</textarea></div>

<div><textarea class="code_edit" cols=120 rows=4 wrap=off name=custom1>|CUSTOM1|</textarea></div>

<div><textarea class="code_edit" cols=120 rows=2 wrap=off readonly>|CUSTOM1_POST|</textarea></div>

Database: Ability to restore with SUPER privileges at Admin Level new

Relating to this error:

Access denied; you need (at least one of) the SUPER privilege(s) for this operation

DA can now restore your databases with SUPER privileges via the Admin Level.

By default, this feature is disable and the mysql restore will run as the User with basic mysql privileges (but no SUPER):


However, if you enable this feature by adding:


to your directadmin.conf and restart DA, any restore done via:

Admin Level -> Admin Backup/Transfers

will then run as root using the da_admin user/pass, which has SUPER privileges.

Note that it's less safe to run as root, but because the .sql files are chowned to the User at restore time, and the --defaults-extra-file is not readable by the User, a single use with access to both is required. Since only Admin accounts can do this via their own tar.gz files in a safe place (which they must trust), it's less of an issue., new

Hook for autoresponder creation and modification.


username - DA username
domain -
user - name of the autoresponder
cc - 1 or 0 if the cc checkbox was set
email - cc value set
header - 1 or 0 relating to the html/utf-8 autoreplies
reply_encoding, reply_content_type, subject, reply_once_time: see id=1785


if you exist with a non-zero the autoreponder will not be created, and DA will output your echo.

Else, the autoresponder will be created and will still display your output.

The will display all output regardless.

If you exit with a non-zero, DA will return normally but will return an error status code to the calling function.

All output is displayed.

lsphp add php version to hander name (TEMPLATES) new

This should only affect servers with php#_mode=lsphp and where there are 2 php versions being used.. and the verions are swapped.

2 new tokens:


for example.

By default these tokens are empty: "" but if php#_mode=lsphp in the CB2 options.conf, then the respective LSPHP#_RELEASE token is set.






Old code:

|*if HAVE_PHP1_CLI="1"|
<FilesMatch "\\.php$">
AddHandler application/x-httpd-php .php
|*if HAVE_PHP2_CLI="1"|
<FilesMatch "\\.php|PHP2_RELEASE|$">
AddHandler application/x-httpd-php .php|PHP2_RELEASE|

New code:

|*if HAVE_PHP1_CLI="1"|
<FilesMatch "\\.php$">
AddHandler application/x-httpd-php|LSPHP1_RELEASE| .php
|*if HAVE_PHP2_CLI="1"|
<FilesMatch "\\.php|PHP2_RELEASE|$">
AddHandler application/x-httpd-php|LSPHP2_RELEASE| .php|PHP2_RELEASE|

New template for easier branding (TEMPLATES) new

New footer template:


allowing all other messages to simply include the 1 file.

This will let you very easily swap out that template, rather than changing all mentioned templates below, in order to alter the footer text.

  • block_cracking_notice_denied_path.txt
  • block_cracking_notice_script.txt
  • block_cracking_notice.txt
  • email_limit_message.txt
  • load_check_message.txt
  • message_tech.txt
  • message_user.txt
  • partition_check_message.txt
  • per_email_limit_email_message.txt
  • per_email_limit_message.txt
  • reseller_limit.txt
  • user_limit.txt
  • user_suspension.txt

and each of these templates will have their footer sections replaced with this token:


The message_footer.txt has the following variables available:

HTTP=http or https

Also, new directadmin.conf options:


The "name" was actually already internally set, but this allows you to override the value if you want better branding.

The da_website value can be changed, will will affect the link at the bottom of the page, in case you want clients to see your own website rather than ours.

Note, there are some areas we still force "DirectAdmin" to be shown, such as in the http header output, as the Multi-Server Setup requires to see that exact text, or it won't work.

There are also many instances of the text DirectAdmin throughout which have not been touched, as they would need language pack variables that would break if we add %s to the string (as the var count would fail)

Allow header X-Forwarded-For header for proxy or load balancers new

Option to internally set the client IP to the value set in the header:


which is useful if you're using a proxy or load balancer in front of DirectAdmin.

New directadmin.conf option:


which is interally NULL by default (feature is off)

If you use an apache proxy to DA, eg:

then you might want to enable this feature, but setting the above option to the IP or list of IPs thare are allowed to set the headers:


The value should be an IP or list of IPs separated by colons, eg:




where they should be any IP that you trust that is going to set the X-Forwarded-From header.

If the incoming IP is not one of these x_forwarded_from_ip IPs, then DA will not check for the header.

If the incoming IP matches one of them, and there is such a header, then DA will internally set the incoming client IP to be that value (X-Forwarded-For)

After some testing, DA will not swap out the X-Forwarded-Host value, because it confuses the proxy, and doesn't remove :2222 in our redirects, causing the User to be sent to 2222, even if they're using a proxy on 80 or 443.

Keep in mind that for some things, this feature will not work in every case.

For example, the DA IP blacklist is checked prior to the headers being read in, so won't work for the block itself.

But for most other areas, like the actual count of failed logins (and adding the true client IP to the blacklist) will still work (but in the case of the IP blacklist, they won't actually be blocked due to the IP mismatch)

Every thing else should show the correct X-Forwarded-For IP address, such as logs, tokens, plugins, scripts, etc..

Let's Encrypt automated free SSL certificate installs (SKINS)(LANG) new


Functionality for the new Free Certificate Authority:

directadmin.conf option:


where 0 is the internal default.

To enable this feature, use:


followed by the "ACTION REQUIRED" below to add the .well-known Alias to the httpd-alias.conf file.

Users can then access it from:

User Level -> SSL Certificates

where a 3rd certification creation radio box will be created:

"Free & automatic certificate from Let's Encrypt"

in addition to the other 2 'self-signed' and 'create request' options.

There is an internal variable, set by default to:


you shouldn't need to adjust this.

The certificates are only valid for 90 days, so DA starts trying to renew 85 days in.

It's triggered at the end of a full tally, every night. Looks at any files, and sees if it's old enough.


You must have the .well-known Alias pointing to /var/www/html/.well-known, so update your CustomBuild configs:

cd /usr/local/directadmin/custombuild
./build update
./build rewrite_confs

LetsEncrypt does have a rate limit, so you won't likely be able to secure hundreds of domains at the same time.

As of Jan 31st, 2016 the rates are:

Rate limit on registrations per IP is now 10 per 3 hours

Rate limit on certificates per Domain is now 5 per 7 days

but once the project becomes "stable", they'll likely increase the allowed rates.


as mentioned above, the full tally does handle the automated renewals, if they're about to expire.

If you want to run a renewal check manually, you can use:

echo "action=rewrite&value=letsencrypt" >> /usr/local/directadmin/data/task.queue

which will call the same function as the full tally calls.

Only domains who's certificates are about to expire will be updated.




./ request|renew|revoke 4096 (/path/to/csr-request-config-file) (document_root)

but you shouldn't need to run it manually, as DA will call it automatically when the User triggers it through DA.

Note, when you run it through DA, the will have more details, than if you run it from ssh (.san_config will be created, but with less info)


/usr/local/directadmin/data/users/username/letsencrypt.key - L.E. account ID for this User. Only created once.

/usr/local/directadmin/data/users/username/domains/ - contains time L.E. cert was created (to be automatically renewed every 90 days)

/usr/local/directadmin/data/users/username/domains/ - csr -config request info, used for creation and renewal.

/var/www/html/.well-known/acme-challenge - directory created by DA for the random challenge key file: letsencrypt=1

.htaccess - added to document root to disable mod_rewrite, in case it's enable higher up.



added a 3rd radiobox:

<br><input type=radio name=request value="letsencrypt" disabled>Free & automatic certificate from <a class='listtitle' target=_blank href="">Let's Encrypt</a></td>

Show if LE is in use:



LANG_FREE_LETS_ENCRYPT=Free & automatic certificate from
LANG_LETSENCRYPT_IN_USE_AUTO_RENEWAL=Let's Encrypt in use.  Auto-renewal in


28=Let's Encrypt is not enabled. Â You cannot use this option.
29=Invalid keysize. Valid values: %s
30=Unable to create %s: %s


method: POST

Reported issue with curl's ca-certificates where this command was required:


if you see something like this in the output:

Getting challenge for from acme-server...
/usr/local/directadmin/scripts/ 279: [: Illegal number: 
/usr/local/directadmin/scripts/ 295: [: Illegal number: 

caused by an empty HTTD_STATUS variable, because of the broken ca-certificates for curl.

Reported issue of DNSSEC possibly causing this error:

"Challenge is invalid. Details: DNS problem: SERVFAIL looking up A for Exiting...'. "

but only after confirming that the A record for works correctly.

Ability to script/send your own custom notifications to the Admin Message System new

Very simple new task.queue command to send Admin notices.

subject=Your Subject
message=Content of your Message

It should be URL encoded (mainly if you want newline characters: %0A) so that it can be posted to the task.queue.

Here's an example:

echo "action=notify&value=admin&subject=hello this is my subject&message=this is the notification%0Awith a new line." >> data/task.queue.cb; ./dataskq d10 --custombuild

Note the above sends to the task.queue.cb file, and calls the dataskq with the --custombuild option.

This skips all other typical dataskq commands, so you don't accidentally process a backup or the tally.


There is no form checking on the subject and message. As you must have root access to do this, we trust you to pass clean values.

Per-User user.conf override for show_db_usage new

Relating to this feature:

option to not show the realtime database disk usage

you can now set the same show_db_usage values in the user.conf for a given user.

This allows you do keep a global value, but override it for any Users that need it.

Must be added manually (no interface for it)

RoundCube is the new default /webmail client new

As development of SquirrelMail has essentially stopped (no new release in about 5 years), it's clear the project should no longer be the primary email client.

We'll still include SM with installs, as many people use it, but without php 5.6 or php 7 support, it won't be able to be used much longer anyway.

The change simply adds:


to the directadmin.conf templates for new installs.

The internal default is still squirrelmail, so existing installs won't be affected.

Backup: direct_imap_backup for direct inclusion of imap folder into tar.gz new


New option, disabled by default:


but can be enabled by adding:


into your directadmin.conf.

When enabled, this feature will store the "imap" folder directly into the tar.gz backup file, saving the need to first copy all emails to an assembly area.

This should greatly speed up the backup process.

Previously, the /home/user/imap/* data was stored in:


but when this feature is enabled, a file:


will be created, telling DA to extract:


to /home/user/imap/*

For security reasons, the extraction is done as user:user, and not user:mail.

Then a secure recursive chown is done whereby it temporarily renames each file to a random value, checks for hardlinks, and checks for user:user ownership.

If all passes, it's then lchown, and renamed back.

The reason for the excessive checks is anytime a recrsive chown is done on files that can be controlled by a User, there is a risk of hardlinks being added, where DA might end up doing a chown on /etc/shadow, for example,

but there can also be a race condition where the client creates the hardlink after the check in DA is done, but before the chown happens, hence we need to rename the file to prevent that, as the client won't be able to predict the filename.

Currently, only /home/user/imap is done this way.

The /home/user/Maildir folder is still stored in the home.tar.gz (for now)

per-user commands.allow & commands.deny in backup/restore new

Include the per-User commands.allow and commands.deny files in all backups.

Restore the files from backups, but only via Admin Level -> Admin Backup/Transfer (as they're root created files, Users/Resellers are not allowed to touch them, only Admins can)

FTPS curl to create any missing directories with upload fixed

Added --ftp-create-dirs to the curl command for ftps uploads, similar to the -m option with ncftpput.

bugfix fixed

The was not being called for forwarder deletion.

It's now called for each forwarder deleted, after the aliases file is already written.

You can exit 1 if you want, but the action would have already been done, so wouldn't affect anything, other than having DA report an error.

Else, exit 0 for no issues.

Timing issue on cert/key verification fixed

To verify certificate/key validity, DA makes calls to openssl.

As the openssl calls used stdin, DA would call them and then pipe the info across.

The issue was that on some slower systems, openssl would provide some output, pause, and then produce more output, confusing DA's listener, so DA closed the pipe too soon, breaking the call, causing openssl to return a non-zero result.

I've changed the behavior to create temp files with mkstemp for the cert/key files, and then just pipe the data over using the shell < character.

This lets DA to simply wait for the process to exit normally.

Allow DH & EC ciphers fixed

The "modern" SSL ciphers require extra SSL setup steps to work correctly.

Used SSL_CTX_set_tmp_dh_callback and created a callback function.

Also use the ecdh calls to EC ciphers (Nov 10th, 2015)

MariaDB 10.1 needs CREATE USER before GRANT on restore fixed

When a DB is restored, previously the GRANT command was sufficient to create a new User, but with MariaDB 10.1, we noticed a behavior where this was not the case, eg:

GRANT ALL PRIVILEGES ON `mysqlrest_test`.* TO 'mysqlrest'@localhost

resulted in:

#1133 - Can't find any matching row in the user table

Solution was simply to call:

CREATE USER 'mysqlrest'@localhost

immediately prior to the GRANT.

The password is set very shortly after that.

Found to be caused by change in MariaDB 10.1 where they changed the default to use:



ftp_upload if missing bc, use expr fixed

A few reports of:

ftp_upload.php exit code: 0 ftp_upload.php output: Cannot find /usr/bin/bc for ftp upload timeout change on large file: 3 Gig.

during backups, which wouldn't break the backup, but doesn't increase the timeout accordingly.

Changed the ftp_upload.php to fallback and use /usr/bin/expr if /usr/bin/bc is missing.

If /usr/bin/expr doesnt exist, but /bin/expr exists, that's used instead. better removal of old hostname fixed

In some areas, the script did not remove the old hostname, and instead just added the new host, leaving the old value.

This fixes that using better regex's.

Some old locations:

/etc/virtual/  (now does a rename)

DNSSEC: Auto-resign wasn't updating serial of master zone fixed

During a re-sign DA reads /var/named/, updates the loaded serial and writes the +1 to /var/named/

The original zone didn't get an updated serial, thus the +1 keeps being set to the same value over and over.

So it "is" updating, but just being set to the same "new" value repeatedly.

Fix is to issue a serial updates on the zone first, before doing the re-sign.

SECURITY(low): removed localhost A and AAAA records fixed

Removed the localhost A and AAAA records from dns_a.conf and dns_aaaa.conf, so it's not added with new records.

Report that it could contribute towards a bypass of RFC2109 on a system where multiple Users can browse physically from this box.

Security issue only applies where you use a browser that is physically running on the same box as apache.

Basically it won't affect you unless your own desktop is also your server, and and you don't trust someone who is physically using the desktop login (almost 0% chance)

This change does not affect existing records.

If you wish to remove all localhost records from all domain db files, you can use this regex.

Be sure to backup the zones first:

cd /var/named
tar cvzf /root/zones.tar.gz *.db

Then remove the values:

perl -pi -e 's#^localhosts.*n##' *.db

And lastly, bump up the serial, and trigger any MSS rewrites:

echo "action=rewrite&value=named" >> /usr/local/directadmin/data/task.queue

Note this shouldn't affect your file, even if it ends in a .db because it should be "localhost." with a trailing dot, and the regex doesn't remove that.

Not restoring sub-domains if Domains Directory excluded from backup fixed

As many Admins are using rsync for /home, they might be excluding the "Domains Directory" option from their backups.

During the restore, DA would try and re-create the subdomains, but because /home/user/domains won't exist, the creation of the subdomain fails, so isn't added to the subdomains list.

Fix is to check if "domains" exists in the backup/backup_options.list.

If it's not in the list, then we set subdomain creation to skip creation of the path/template, etc..

The subdomain can then be correctly added to the data/users/domains/, so it's added to the apache VirtualHosts.

Note, you must still restore /home/user/domains via your manual method, or else you'll obviously have a website that doesn't work, and Apache might not be too happy about it.

Ensure permission/ownership on blocked_script_paths and blocked_authenticated_users fixed


When a path or authentication is blocked by BlockCracking, BC will use the mail_task.queue to notify DA of the block that has been done.

This change will cause DA to check the ownership and permissions of those 2 files to ensure they're set to mail:mail and chmod 600.

If not, they'll be changed, and a note will be added to your errortask.log, eg:

2015:11:23-22:39:45: Ownership of /var/spool/exim/blocked_authenticated_users is root but should be owned by mail.  Changed ownershp
2015:11:23-22:39:45: Permission of /var/spool/exim/blocked_authenticated_users are -rwxr-xr-x but should be set to -rw-------.  Changed permissions.
2015:11:23-22:39:45: Ownership of /var/spool/exim/blocked_script_paths is root but should be owned by mail.  Changed ownershp
2015:11:23-22:39:45: Permission of /var/spool/exim/blocked_script_paths are -rwxr-xr-x but should be set to -rw-------.  Changed permissions.

Debian 8 64-bit: Chaning DA binaries to dynamic fixed

Due to a report of compatibility issues with a release of Ubuntu (Debian 8 64-bit), I've changed the directadmin, dataskq, and da-popb4smtp binaries to be dynamic.

However, I've kept as static, so there are no issues if you are swapping around MySQL versions.

Related gdb segfault and backtrace:


Warning: couldn't activate thread debugging using libthread_db: Cannot find new threads: generic error
warning: File "/lib/x86_64-linux-gnu/" auto-loading has been declined by your \`auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
        add-auto-load-safe-path /lib/x86_64-linux-gnu/
line to your configuration file "/root/.gdbinit".
To completely disable this security protection add
        set auto-load safe-path /
line to your configuration file "/root/.gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual.  E.g., run from the shell:
        info "(gdb)Auto-loading safe path"
warning: Unable to find libthread_db matching inferior's thread library, thread debugging will not be available.

Program received signal SIGSEGV, Segmentation fault.

(gdb) bt full
#0  0x0000000000000000 in ?? ()
No symbol table info available.
#1  0x00007ffff6c9bfaa in __pthread_initialize_minimal_internal () at nptl-init.c:464
        pd = <optimized out>
        sa = {__sigaction_handler = {sa_handler = 0x7ffff6c9bc40 <sighandler_setxid>, sa_sigaction = 0x7ffff6c9bc40 <sighandler_setxid>}, sa_mask = {__val = {6442450944,
              0 <repeats 15 times>}}, sa_flags = 268435460, sa_restorer = 0x1}
        static_tls_align = 16
        limit = {rlim_cur = 8388608, rlim_max = 18446744073709551615}
        pagesz = <optimized out>
        minstack = <optimized out>
        rtld_lock_count = <optimized out>
#2  0x00007ffff6c9b5b9 in _init () at ../sysdeps/x86_64/crti.S:72
No locals.
#3  0x000000770000006e in ?? ()
No symbol table info available.
#4  0x00000000007fc89d in call_init.part ()
No symbol table info available.
#5  0x00000000007fc9e5 in _dl_init ()
No symbol table info available.
#6  0x00000000007daab7 in dl_open_worker ()
No symbol table info available.
#7  0x00000000007d8dd4 in _dl_catch_error ()
No symbol table info available.
#8  0x00000000007da463 in _dl_open ()
No symbol table info available.
#9  0x00000000007dd3f7 in do_dlopen ()
No symbol table info available.
#10 0x00000000007d8dd4 in _dl_catch_error ()
No symbol table info available.
#11 0x00000000007dd437 in dlerror_run ()
No symbol table info available.
#12 0x00000000007dd623 in __libc_dlopen_mode ()
No symbol table info available.
#13 0x00000000007cd6cb in __nss_lookup_function ()
No symbol table info available.
#14 0x00000000007cd8d3 in __nss_next2 ()
No symbol table info available.
#15 0x00000000007bcd8f in getpwnam_r ()
No symbol table info available.
#16 0x00000000007bc74f in getpwnam ()
No symbol table info available.
#17 0x000000000046c5b5 in getHomeDir(char const*) ()


where the getHomeDir is DA's call, but everything beyond that belong to the system libraries.

Leftover files in /home/tmp/admin.1234/user on failed backups fixed

If the backup is aborted prematurely, the previously post backup cleanup of admin.1234 (which only runs as "diradmin") didn't have enough permissions to remove admin.1234/user, because that's chowned to the user.

Fix is to trigger a folder removal of admin.1234/user as the User.

Done in the same function call, right before the script is called.

That will remove "user/*", so that removal of admin.1234 as "diradmin" can proceed normally.

The remove of admin.1234/user itself, although owned by "user" doesn't work because admin.1234 doesn't allow write privilges to user.

So the "fix" folder removal will do everything, but always generate an error in the debug 100 output. It's not sent to the errortaskq.log because know it's going to happen.

(saves having to code a new function to read all folders within user, and loop, etc..). Then the "diradmin" removal of admin.1234 has enough privilege to remove the rest.

user_virtual_host.conf wasn't being added for !mod_ruid2 + mod_php fixed

If CustomBuild 2 was set to not use mod_ruid2, and was mod_php (cli), then the User httpd.conf rewrite would have skipped the <Directory /home/username> section at the top of the User's httpd.conf.

The debug 1000 output would have generated:

Apache::write_user_directory: not use any releated php's, so skipping the user_virtual_host.conf

Completely removed this check, so it will user the user_virtual_host.conf for all cases, when CB2 is used.

Nginx cert/ca .combined files to ensure a newline between certs fixed

Mentioned here:

It's possible to paste a certificate without a newline character at the end.

DA would then combine that cert and ca bundle into a .combined file, but it could cause one line that looks like this:


Fix was to always add a newline in between them. It may add an extra space, but that should be fine.

display of email sends re-using old value fixed

On the Show All Users page, the total number of sends displayed is calculated from the User's email_deliveries_outgoing plus their real-time /etc/virtual/usage/username file.

The bug was from a static variable, where using a static variable, the math was done at the same time that the variable was instantiated, so the value never changed for subsequent calls.

The actual numbers and limits were not affected, only an incorrect display of the numbers.

The (bracketed) value for today's sends were correct.

better checking to skip bandwidth for locally uploaded ftp backup files fixed

Local IP list:




  1. The DAdminUsage.bytes now excludes any local upload IPs for calls to the ftp_upload.php

  2. pureftpd log parsing was already fine, but for proftpd, the :ffff: wasn't being skipped.

Centralized the ip check to one central "is_local_ip(ip)" function.

CMD_MX_CONTROL better MX value filtering fixed

The named-checkzone doesn't always seem to catch all entries, and reports that values that make it past caused named to fail.

The CMD_DNS_CONTROL was correctly filtering the MX values before moving forward, but CMD_DNS_MX was not.

Ability to type in Username or Email for BFM skip list fixed

Previously, you would only be able to use the top right User table to select which Users you want to add to the skip list.

This change allows you to type in the Username or Email value to the skip_value field.

DA will check if it's a User or E-Mail, and then add it to the User/Email "Select" list, rather than to the IP list on the left (As if it were in the list, and checkbox selected)

Then normal validation occurs, and ends up in the combined skip list.

Last Updated: