Version 1.40.0
Released: 2011-11-24
new
Log pop+imap bandwidth for dovecotRelating to:
http://help.directadmin.com/item.php?id=253
dovecot recently logs bytes for imap usage, so bandwidth can now be counted.
Example from maillog:
Sep 11 19:43:26 abc dovecot: imap(user@domain.com): Disconnected: Logged out bytes=241/451
Nov 15 02:28:12 abc dovecot: pop3(user@domain.com): Disconnected: Logged out top=1/30, retr=1/2210, del=0/110, size=1638002
For imap, DA will add the bytes=241/451 (in/out) together for the total.
For pop, DA will use retr=1/2210, where 2210 is the sent bytes with the retr command.
If the pop3 line contains a bytes= entry, that will be used instead (even though it doesn't by default, it will be more accurate when we you add it to your dovecot.conf. (new systems will have the new log format for pop, see below)
This will add 2 new columns to the bandwidth breakdown page.
And will add pop and imap values to the bandwidth.tally.cache file.
The file:
/usr/local/directadmin/scripts/rotate_email_usage.sh
has also been updated to rotate the dovecote.bytes files which will be created by the da-popb4smtp binary.
The dovecot.bytes files will exist in 2 locations, depending on the login format.
/etc/virtual/domain.com/dovecot.bytes for @domain email accounts.
/usr/local/directadmin/data/users/username/dovecot.bytes for system email accounts.
The contents of the dovecot.bytes files will be dumped into the bandwidth.tally, then deleted (via rotate_email_usage.sh)
New addition to the dovecot.conf for dovecot 2.0, into the section for protocol pop3:
pop3_logout_format = top=%t/%p, retr=%r/%b, del=%d/%m, size=%s, bytes=%i/%o
adding this line will give a more accurate bandwidth logging.
Not adding it will only rely on the sent bytes frm RETR commands, so will be somewhat short of the truth.
new
Log delete/rename/copy portions of the CMD_FILE_MANAGERLog delete/rename/copy portions of the CMD_FILE_MANAGER, to better track User actions.
Because the filemanager is chrooted, the log to the system.log will happen before the action takes place, so if there are any errors, the logs won't show them.
new
all_backups_post.shall_backups_post.sh to be called after all backups are finished.
Only applies to backups triggered from:
Admin Level -> Admin Backup/Transfer
Reseller Level -> Manage User Backups
Is not triggered from:
User Level -> Create/Restore Backups
This script is purposed to run after a large number of backups are created, hence it's not called for the User Level backups.
The parameters passed are the same values that are passed to DA via the task.queue for the creation of the backup in question.
This variable can vary, so to see what you'll get, type:
cat /usr/local/directadmin/data/task.queue
after triggering the run of the backup you want.
If you wish to do something after each User backup is called, use this instead:
user_backup_post.sh
new
Add Block IP button to IP/User tables for faster blocking (SKINS)With the Brute Force Monitor, add the Block IP button (if block_ip.sh exists) right in the tables, so multiple IPs can be blocked at once.
It will call the block_ip.sh once per IP.
SKINS:
admin/brute_force_monitor.html
added:
|*if HAVE_BLOCK_SH="1"|
<input type='button' value='Block IPs' onclick="if (confirm('Are you sure you want to block these IPs?')){document.tableform.action.value = 'block_ips';document.tableform.submit();}">
<b>|</b>
|*endif|
new
database_user_destroy_post.shCustom script called when deleting a Database User:
database_user_destroy_post.sh
Related to database_user_create_post.sh: custom scripts for databases
environmental variables for script:
username - DA username
database - name of the db
user - name of the user created
Note that database_destroy_user_post.sh will not be called when a database and it's users are being deleted.
It's only called when a single DB user is removed from a database.
This means that any code used in database_user_create_post.sh will have to be doubled in database_delete_post.sh.
EDIT: should really call this for each DB User being removed, when a DB is deleted as well.
new
IP ranges and reverse IPs in brute_skip.list (SKINS)Optional field/value on the Brute Force Monitor (BFM) page which will allow you to manually type in:
IPs, IPv4 and IPv6 (use full/expanded form for IPv6)
IP ranges: 1.2.3.4-5 and 1:2:3:4:5:6:7:8-9 (make sure to use the range type that the logs will see the IP as. If an IP shows up as IPv6 in the logs, use an IPv6 range)
domain.com (reverse IP lookup, exact match)
*.domain.com (reverse IP lookup, wildcard match, must start with *. Can also be *irectadmin.com, as it's just a string match)
which will give you more control over skip list with the BFM.
If you leave the field empty, the "Add to Skip List" button will block the check-boxed IPs listed in the table above.
If you add an IP/range/domain to the field, then that value will be added to the skip list as well as any check-boxed IPs.
When entering domain.com or *.domain.com, this will trigger a reverse IP lookup on the IP using a new script:
/usr/local/directadmin/scripts/reverse_ip_lookup.sh
If no domain.com or *.domain.com value is added, then the lookup is not done, which will speed up the process (reverse IP lookups can be slow if there are many IPs to check)
The timing of the lookup is after the logs are parsed for that minute.. so a lookup will only ever be done at most once per minute, per IP.
SKINS:
admin/brute_force_monitor.html
add;
<input type=text name="skip_value" placeholder="Optional IP/Range/Domain" title="Optional IP/Range/Domain" size=24><a target=_blank class=listtitle href="http://www.directadmin.com/features.php?id=1252">?</a>
new
option to allow underscores in db names and db usersNew directadmin.conf option to allow an override for anyone who needs to backup/restore databases or db users with underscores in their names.
Removed in DirectAdmin 1.666.
new
Disk Usage Breakdown - CMD_DU_BREAKDOWN (SKINS)New button on the stats page of Users, so they can click "Details" next to their total disk usage.
"Details" button also on the User info page viewed by Admins and Resellers.
CMD_API_DU_BREAKDOWN also exists. It will dump the contents of the user's du_breakdown.list file.
User Level -> Site Summary / Statistics / Logs -> Total Disk Usage (MB) -> [Details]
Reseller/Admin Level -> Show All Users -> Total Disk Usage (MB) -> [Details]
SKINS:
files_user.conf:
CMD_DU_BREAKDOWN=user/du_breakdown.html
user/du_breakdown.html - see skin
new
BFM: show_blocked_ips.sh and unblock_ip.sh (SKINS)Two new items for the brute force monitor:
show_blocked_ips.sh
If you create it, it's output should generate a list of IPs which are blocked.
The format will be 1 IP per line, but should also have an = at the end to allow for more data to be associated with the IPs in the future (eg: if we add blocked time or info, etc..)
So for now, make the output like:
havedata=1|0
1.2.3.4=
2.3.4.5=
etc...
or
1.2.3.4=dateblocked=12345678&info=he%20was%20being%20mean
2.3.4.5=dateblocked=12346789&info=definitely%20a%20meany
The havedata=1 value is optional.
If you include in the the output, then DA will expect dateblocked=123456 (unix timestamp) along with the IP, as well as info=.
Note that either "dateblocked" or "info" can be blank. DA will ensure there is something in the variable before chewing on it.
The havedata=1 value simply changes the table to show more cells, ie: the data you're giving it.
- unblock_ip.sh
Related for automated unblocking: BFM automated unblock (SKINS)
Called by DA if it exists and will pass the environmental variable:
ip=1.2.3.4
so that you can remove an IP from your block list.
SKINS
admin/brute_force_monitor.html
very top of file:
|*if HAVE_SHOW_BLOCKED_IPS_SH="1"|
|?BODY=onLoad="sizeTheDiv();"|
|*endif|
after the skip list:
|*if HAVE_SHOW_BLOCKED_IPS_SH="1"|
<br>
<form name=tableform5 action='CMD_BRUTE_FORCE_MONITOR' method=POST>
<input type=hidden name=action value="unblock">
<b>Blockd IPs</b>
<div id="blockedipsdiv" style="overflow: auto; height: 450px; width: 300px; border: 1px solid grey;">
|BLOCKED_IPS|
</div>
|*endif|
|*if HAVE_UNBLOCK_SH="1"|
<table id="blockbuttontable" class=list style='width: 50%' cellpadding=3 cellspacing=1>
<tr><td class=listtitle align='right' colspan='5'> <input type='submit' value='UnBlock' name=unblock></td ></tr>
</table>
</form>
|*endif|
|*if HAVE_SHOW_BLOCKED_IPS_SH="1"|
<script type="text/javascript">
<!--
function sizeTheDiv()
{
var tblwidth=document.getElementById('blockedipstable').offsetWidth;
if (tblwidth>0)
{
if (tblwidth < 300)
{
tblwidth = 300;
document.getElementById('blockedipstable').style.width=tblwidth;
}
document.getElementById('blockedipsdiv').style.width=tblwidth+19;
if (document.getElementById('blockbuttontable'))
document.getElementById('blockbuttontable').style.width=tblwidth+21;
}
}
sizeTheDiv();
// -->
</script>
|*endif|
new
Ability to hide and block "Domain Setup" page (SKINS)New global token:
|ALLOWED_CMD_DOMAIN|
will be set to yes or no, depending if the User is allowed to run:
CMD_DOMAIN
The way to control the running of that command is with the commands.allow and/or commands.deny.
For this case, generally, you'd just add:
CMD_DOMAIN
CMD_ADDITIONAL_DOMAINS
CMD_API_DOMAIN
CMD_API_ADDITIONAL_DOMAINS
to the file:
/usr/local/directadmin/data/users/username/commands.deny
Related feature:
commands.allow and commands.deny for per-user control
https://forum.directadmin.com/posts/211408
SKINS:
user/content_main.html
user/show_domain.html
Add this around the href to CMD_ADDITIONAL_DOMAINS to hide the link:
|*if ALLOWED_CMD_DOMAIN!="no"|
.... url ....
|*endif|
we use "not no" instead of "is yes", to be backwards compatible, in case they use a new skin for an old version of DA.
The old versions of DA would be blank, so "is yes" would fail, but "not no" will succeed if blank.
new
Suspend reason: user/domain (SKINS) (TEMPLATES)When a user/domain is suspended, feature which allows reasons that it was suspended.
Note that the reason is optional. You don't need to pass it, and you don't need to pick something, if you don't want to.
The variable suspended_reason will be stored in both the user.conf and domains/domain.com.conf if either is suspended.
When the user/domain is unsuspended (suspended=no), the variable is deleted.
Uses an environmental variable in the httpd.conf holding the key-word descriptor of why it was suspended. (probably cleaner/simpler), eg:
SetEnv reason bandwidth
which can then be accessed via the /home/reseller/domains/suspended/index.html, index.shtml, or index.php.. whatever works for you which has the ability to read ENV variables.
Variable name is "reason"
Available for Admins/Resellers to use on Users.
Also available for Users to use on Domains.
template:
/usr/local/directadmin/data/templates/suspension_reason.txt
user_bandwidth=id=12&text=User Bandwidth
user_quota=id=13&text=User Disk Quota
domain_bandwidth=id=14&text=Domain Bandwidth
domain_quota=id=15&text=Domain Quota
reseller_bandwidth=id=16&text=Reseller Bandwidth
reseller_quota=id=17&text=Reseller Quota
billing=id=18&text=Billing Issue
abuse=id=19&text=Abuse
spam=id=20&text=Spam
other=id=21&text=Other
where the id values for the key names are internal language pack id numbers set in:
/usr/local/directadmin/data/skins/enhanced/lang/en/internal/suspension.txt
Note, if you just want to set the text, and not make your entries translateable, then simply don't include an id value.
A sample reason without an id:
smelly=text=He Really Smells Bad
Both cases must have a "text" variable, as it will be the fallback for the event that the id cannot be found in the given language file.
If you wish to customize the suspension_reason.txt file, first copy it to the folder:
/usr/local/directadmin/data/templates/custom
and edit the custom copy.
TEMPLATES:
suspension_reason.txt : see above
virtual_host*.conf
added:
|*if SUSPENDED_REASON|
<IfModule mod_env.c>
SetEnv reason |SUSPENDED_REASON|
</IfModule>
|*endif|
SKINS:
Just a minor cosmetic change.
Since Show Admins and Show Resellers now have wider tables, we're changing the header page to be the full path instead of the path with the stats on the right, such that the table doesn't impede on the stats.
admin/show_admins.html
admin/show_resellers.html
Change:
|HTM_ADMIN_TOP|
and:
|HTM_ADMIN_BOTTOM|
to be:
|HTM_HEADER|
|HTM_HEADER_WIDE|
and:
|HTM_FOOTER_WIDE|
|HTM_FOOTER|
Thread containing examples on index.shtml and index.php usage:
https://forum.directadmin.com/posts/212649
new
Add a whois to reverse IP lookup, and ip_info.sh scriptThe IP information page under the Brute Force monitor will now be changed to call a new script:
/usr/local/directadmin/scripts/ip_info.sh 1.2.3.4
The script will call:
dig -x "1.2.3.4" +noshort
but will also include a whois output (if whois exist) for more information on the IP:
whois "1.2.3.4"
Related DA command:
/CMD_BRUTE_FORCE_MONITOR?ipinfo=1.2.3.4
Note that this does add a few seconds to a the lookup time, but IMO worth the few seconds to get more/accurate information on the attacking IP.
new
domain log rotation to have orderThe tar.gz files in:
/home/user/domains/domain.com/logs
will now be rotated in a similar fashion to all logs in /var/log (but still controlled by DA, and not logrotate)
The format will be:
Nov-2011.tar.gz
Nov-2011.tar.gz.1
Nov-2011.tar.gz.2
Nov-2011.tar.gz.3
Nov-2011.tar.gz.4
Nov-2011.subdomain.tar.gz
Nov-2011.subdomain.tar.gz.1
Nov-2011.subdomain.tar.gz.2
Nov-2011.subdomain.tar.gz.3
Nov-2011.subdomain.tar.gz.4
The old format had no pattern to the numbering. Only the timestamp could be used to determine the newest log.
The numbers would be used in a rotation, but they were not in order.
The new format keeps things much cleaner.
The file that does not end in a number is always going to be the newest.
new
remove clipboard file upon logoutWhen CMD_LOGOUT is called, the file:
/home/user/.clipboard
will be removed, if it exists.
This is done to prevent confusion in the FileManager, where files are still in the clipboard from previous sessions.
Default internal directadmin.conf option will be:
remove_clipboard_on_logout=1
If you do not want the clipboard to be removed on logout, set:
remove_clipboard_on_logout=0
in your directadmin.conf.
Note that this only applies if CMD_LOGOUT is called.
If the client just closes his or her browser, the event will not be triggered.
new
user_info_modify_post.shCustom script:
/usr/local/directadmin/scripts/custom/user_info_modify_post.sh
used for chaning of User info:
Name
language
skin
Note that the user.conf may or may not have already been written when this is called, so don't rely on it being written already.
Values passed:
username: DA user who's email was changed
only one of following:
email=button's text
name=button's text
language=button's text
skin=button's text
so you'll need to check which variable exists, and go from there.
All of these values will also be passed, where you'd figure out which button was pressed using the above values (email, name, etc) and use on of these values:
evalue=email@domain.com (email)
nvalue=John Doe (name)
lvalue=en (the language)
skinvalue=skinname
new
CMD_API_USER_EXISTSCalled by an Admin, CMD_API_USER_EXISTS will return exists=1 or exists=0 if the account exists (Any type: Admin, Reseller, User).
error=1|0 is also returned.
If error=1, then text= will be set to the reason.
new
Multi Server Setup - User CheckNew checkbox in the section:
Admin Level -> Multi Server Setup
which checks the remote boxes for duplicate Usernames.
This is beneficial if you want to prevent duplicate usernames for reasons such as:
transfers between boxes to prevent accidental merges
you use a single remote MySQL server for multiple boxes
long term prediction of full DA clustering, where unique usernames is required (don't ask, distant future)
Feature will use the new API for confirming if a User exists:
new
Table Row Highlighting (SKINS)BETA - disabled by default
only exists in "enhanced" skin at this time.
When your mouse hovers over a row, that entire row will change to a darker background, to more easily track which value you're about to select.
To enable, add the following to your directadmin.conf and restart DA:
table_highlighting=1
The internal default is:
table_highlighting=0
In a future version of DA, this will be enabled by default.
It's disabled for now, for testing purposes.
===================
SKINS:
style.css:
.listhighlight
{
BACKGROUND: #b8c0e2;
white-space: nowrap;
}
.list2highlight
{
BACKGROUND: #b8c0e2;
white-space: nowrap;
}
.listwraphighlight
{
BACKGROUND: #b8c0e2;
white-space: wrap;
}
.listwrap2highlight
{
BACKGROUND: #b8c0e2;
white-space: wrap;
}
header.html, in the <head>
section:
|*if TABLE_HIGHLIGHTING="1"|
<script type="text/javascript">
<!-- // start preload code
function tr_add_highlight()
{
add_highlight(this, 'list','listhighlight','list2','list2highlight','listwrap','listwraphighlight','listwrap2','listwrap2highlight');
}
function tr_remove_highlight()
{
add_highlight(this, 'listhighlight','list','list2highlight','list2','listwraphighlight','listwrap','listwrap2highlight','listwrap2');
}
function add_highlight(ob,h1,l1,h2,l2,h3,l3,h4,l4)
{
var tds = ob.getElementsByTagName('td');
for(var d=0; d<tds.length; d++)
{
switch (tds\[d\].className)
{
case h1:tds\[d\].className = l1; break;
case h2:tds\[d\].className = l2; break;
case h3:tds\[d\].className = l3; break;
case h4:tds\[d\].className = l4; break;
}
}
}
function make_tables_highlightable()
{
var tables = document.getElementsByTagName('table');
for (var tbl=0; tbl<tables.length; tbl++)
{
if (tables\[tbl\].className.indexOf('table-highlight') != -1)
{
var trs = tables\[tbl\].getElementsByTagName('tr');
for(var tr=0; tr<trs.length; tr++)
{
trs\[tr\].onmouseover = tr_add_highlight;
trs\[tr\].onmouseout = tr_remove_highlight;
}
}
}
}
// done with preload code -->
</script>
|*endif|
footer.html, just before </body>
:
|*if TABLE_HIGHLIGHTING="1"|
<script type="text/javascript">
make_tables_highlightable();
</script>
|*endif|
fixed
move_user_to_reseller.sh not updating IPsmove_user_to_reseller.sh
https://forum.directadmin.com/posts/196796
https://forum.directadmin.com/threads/41787
Updated the move_user_to_reseller.sh script to check if an IP is owned (for each ip in the user_ips.list file).
For any owned IP, owned by that User, the IP will be moved from the old Reseller to the new Reseller's ip.list file.
The data/admin/ips/1.2.3.4 is also updated for reserller=oldreseller to reseller=newreseller
fixed
Include multiple forwarder names in forwarder count for pre-check on limitForwarders can be used like:
user1,user2,user3 -> somewhere@email.com
Ensure that this 1 entry counts as 3 forwarders and check the limit so it's not created if it shouldn't be.
Related thread:
https://forum.directadmin.com/posts/208135
fixed
set_permisisons.sh sets exim to 755The script already did make the call correctly to set it to 4755:
set_file /usr/sbin/exim root $RT_GRP 4755
The issue was that the set_file function called the chmod first, then the chown.
The issue was that a 4755 file becomes reset to 755 when a chown is made on the file.
The simple solution was to put the chown first, then chmod.
In exim mainlog error you'd see would look like this if exim is 755 instead of 4755:
unable to set gid=12 or uid=2345 (euid=8): domain_filter router
If you see that error, type:
chmod 4755 /usr/sbin/exim
or run this fixed set_permission.sh:
cd /usr/local/directadmin/scripts
./set_permissions.sh email
but once done either, confirm it's set correctly:
[root@server scripts]# ls -la /usr/sbin/exim
-rwsr-xr-x 1 root root 856823 Mar 9 2011 /usr/sbin/exim
[root@server scripts]#
where you're looking for:
rwsr
and not:
rwxr
fixed
Update template filter_userspamfolder not to save to spambox if account doesn't existWhen the option:
"Send the spam to the appropriate users's spam folder."
is used with SpamAssassin, any spam will be saved to the spambox, eg:
/home/username/imap/$domain/$local_part/Maildir/.INBOX.spam/new
However, if spam is sent to a forwarder, where no local mailbox exists, the filter will create that path to save it to the spambox, when it shouldn't.
The fix was to add extra code to the filter template:
/usr/local/directadmin/data/templates/filter_userspamfolder
where it will check for the account to exist, and if not, it will save the spam to the system account's spambox.
Changed:
save |HOME|/imap/$domain/$local_part/Maildir/.INBOX.spam/new/ 660
to be:
if "${if exists{|HOME|/imap/${domain}/${local_part}}{yes}{no}}" is "yes"
then
save |HOME|/imap/$domain/$local_part/Maildir/.INBOX.spam/new/ 660
else
save |HOME|/Maildir/.INBOX.spam/new/ 660
endif
Note, if the catch-all is enabled (which I never recommend), the system account's spambox will grow quite rapidly.
Apart from not enabling catch-all accounts, I recommend to enable the spambox purge option in the Admin Settings.
Figuring out the syntax for the "exists" check took a while, as it uses double "if's" for string expansion to convert to a yes/no, then a string test for "yes" on that result.
Somewhat obfuscated, but that's why exim is so powerful.
To force a rewrite of all filter files, use this, as the filter files won't be rewritten with the update, but only after a User saves a change:
Also create a new template file:
/usr/local/directadmin/data/templates/filter_filterspamfolder
for when a basic "spam filter" (word filter) is used to redirect to the spambox.
The same rules apply, where the new double-if is used to ensure the folder exists.
If not, it's saved in the main spambox.
Related to the issue: (bottom part about not scanning forwarders)
http://help.directadmin.com/item.php?id=156
however, it wasn't a good solution because spam would then be forwarded to external systems (eg: gmail) without being scanned.
This could potentially cause your server to be blacklisted for sending spam, when it was only forwarding it.
The above fix is good because it allows forwarders to still be scanned, but doesn't save the local copy.
fixed
Restore adding duplicate A records if in short vs long formIf the backup stored a full format of the www record (for example):
www.domain.com. A 4.3.2.1
where 4.3.2.1 was a custom IP which did not match the old IP of the User (eg: an external server)
Then when the restore was done, since it doesn't match the dns_a.conf template which just has:
www A 1.2.3.4
this caused both to exist in the zone after the restore:
www.domain.com. A 4.3.2.1
www A 1.2.3.4
causing an undesired round-robin effect.
Fix was to always shrink the long www.domain.com. format to the short www format when doing comparisons.
Note that if it's decided the value should be added (can't find a duplicate) then the original format used will be added (in this case, the www.domain.com. is added, if that's what was in the backup)
fixed
Options +ExecCGI to .htaccess file in cgi-bin by defaultRelating to:
http://help.directadmin.com/item.php?id=7
If cgi-bin is enabled when a domain is created, the items from id=7 will be added to the .htaccess file.
fixed
Table quicksort was incorrect, plus sort optimizationsThe quicksort algorithm for tables (which is only used in the brute force monitor so far) was not sorting correctly.
A rewrite of the algorithm corrected this. Other tables may be changed to use quicksort as well for it's significant performance increase.
Other classes are already using it.
Note that with quicksort, the "subsort" option does not work, so if it's used, those tables will only be able to sorted by 1 column, so it's use will be selective.
(the subsort is the column choice that the data will be sorted by, when the main column sort values match)
Also, optimizations to all quicksorts (configfile, listfile and tables) have been added in the form of inline functions for all of the quicksort function calls (swap, quicksort, compares, etc..)
This has gained roughly a 15% performance improvment on a 100% inverted list. (where it has to flip the list completely to be sorted)
fixed
Additional owned IPs had VH in ips.confhttps://forum.directadmin.com/threads/42003
When adding an additional IP to a domain (User Level -> Domain Administration), I've added code to DA to issue a rewrite to the ips.conf.
This will get rid of the VH for the IP.. as it doesn't belong there if the IP is owned. (needs to go away in order to have SSL work correctly for the additional IP)
fixed
Allow tar exit code 1As of tar 1.16, if a file is being changed as it's being included into a tar.gz file, tar will return exit code 1.
DA previously only accepted error code 0 as valid.
This change will allow the exit code of 1 to be valid and to not stop the backup process.
The usual causes of this error could be things such as incoming, or deleting email as the backup is being created, or changing files mid-backup.
Related:
http://www.mail-archive.com/bug-tar@gnu.org/msg01215.html
http://www.gnu.org/software/tar/manual/html_section/Synopsis.html
Related error:
Error Compressing the backup file user.reseller.username.tar.gz : /bin/tar: some/file/name.txt: file changed as we read it
fixed
Bug with database names in change_database_username.phpResolved an issues with the change_database_username.php script.
The mysql.db and mysql.user tables were being corrected updated, however, the issue was that MySQL doesn't actually have a rename option.
(They used to have RENAME DATABASE dbname, but it was dropped due to potential loss of data)
Added a rename() call to change /var/lib/mysql/dbname to then new dbname.
The side-effect of this issue, was that if you change the username again, it wouldn't find the DB, since it was never renamed in the first place.
Also, this means that the change_username.sh script will not work if you've got databases on an external box, since a MySQL query won't be able to rename the directory.