eGroupware with Virtualmin and CentOS 6


First, you will need the php-mcrypt module which is no longer provided in RHEL6 or Centos 6. For that, you must install the EPEL (Extra Packages for Enterprise Linux) library. As root, follow the instructions here as follows:

rpm -Uvh

(Do not use that command verbatim without double-checking the link above!). Then --

Following the instructions here

but changing the wget to:


then, noting capitazliation:

yum install eGroupware

which should say (amongst much other):

Installed: eGroupware.noarch 0:

The software installs into /usr/share/groupware

I had to edit two parameters in my php.ini file.

Note that with Virtualmin, it is not /etc/php.ini to modify but rather the one for the domain in which you are running eGroupware. If your domain is a Virtualmin subdomain, the actual file might be:


You can find the exact location by creating a file, let's say foo.php, within your public_html directory, and having the following contents:

<?php phpinfo(); ?>

Then look for the value of Loaded Configuration File... that is the php.ini file to edit.

Once you have located the right php.ini file, change:

upload_max_filesize = 16M

and un-commenting and modifying the line:

date.timezone = "America/Phoenix"

...and restart Apache:

sudo service httpd restart

Now, rather than write an Apache alias (which caused me problems with PHP files being returned as plaintext instead of executable code), I set a symlink:

ln -s /usr/share/egroupware public_html/egroupware

The RPM install of eGroupware sets a link from /usr/share/egroupware/ to ../../../var/lib/egroupware/ ... which should be actually in /var itself... thus, presumably, avoiding an RPM update from overwriting your config file. However this may cause problems with permissions. For me, I created the file but found that the directory /var/lib/egroupware was set to owner apache with no read or execute (i.e., list-directory) permissions for anyone else. This did the trick:

sudo chmod 755 /var/lib/egroupware sudo chmod 644 /var/lib/egroupware/

With that done, direct your web browser to and you should see the setup screen.

Note that you can have multiple domains with the single instance of egroupware. We could be fancy and call this a "multi-tenant" install. To do this, remove the "default" domain and add,, and so on.

Copy the text of the created and paste it into /usr/share/egroupware/ (which is actually /var/lib/egroupweare/

In your domain's root directory (the one above public_html), create a directory egw that will contain the files and backup directories:

mkdir egw mkdir egw/files mkdir egw/backup chown youruser:apache -R egw/

and configure eGroupware to use, for example, /home/maindomain/domains/ and /home/maindomain/domains/ as its directories.

At that point, you should be up and running!

SketchUp on Linux


Install Trimble [formerly Google] SketchUp in a few easy steps

For simple architectural CAD tasks, SketchUp can be a useful alternative. It is certainly simple to use, and unlike Blender it's pretty obvious how to enter dimensions by typing them into a box -- really handy if you are converting an existing paper plan. I had a client send me some files in SketchUp that he wanted moved onto his website, so this was a perfect excuse to try it out.

NOTE: Google has sold this product to a company called Trimble. If you experience problems, the latest info should be on the Wine wiki.

Step 1: Install a few missing utilities

I have shown you not long ago how to use winetricks to easily obtain missing programs and libraries required by some of the Windows programs installed using Wine. Winetricks is a very smart wrapper script that will facilitate the installation of extra components, letting you focus on getting your desired application to run.

Now, to get Google SketchUp to run, you will need some fonts and Visual C libraries, as follows:

winetricks corefonts vcrun6 vcrun2005

You can download the script by being geeky and using wget:


See also, this on SysAdmin World.

When using Wine in Fedora 16 you get an error message that “Wine could not find the Gecko package which is needed for applications embedding HTML to work correctly”. It offers you to automatically download and install the package but nothing seems to actually happen since the message does not go away.

To fix this issue you can use the the script here which will conveniently install the necessary packages to /usr/share/wine/gecko/.

  1. First install cabextract.

    sudo yum install cabextract

  2. Then download the script, make it executable and start it.

    curl -o chmod +x sudo ./

Step 2: Install SketchUp


wine <Google SketchUp windows executable>

And just follow the wizard. For example, on my system:

cd "/home/bill/.wine/drive_c/Program Files (x86)/Google/Google SketchUp 8"
wine ./SketchUp.exe

Step 2a: You may have to tweak this

I get an error saying OpenGL is not installed.

Resolution is here -- (From the Linux shell prompt) run regedit, open [HKEY_CURRENT_USERSoftwareGoogleSketchUp6GLConfigDisplay], and change "HW_OK" to 1.

Notes on installing ownCloud


Missing icons in Calendar dialog, and other places: Edit the .htaccess file and add following line at the end:

AddType image/svg+xml svg svgz AddEncoding gzip svgz

Listing email accounts hosted on a Webmin / Virtualmin server


Here's a little script that I found handy to scan Postfix's virtual address table, compare the domains with the ones actually hosted by the system, and tell me what's really going on.

This works great for servers setup with Webmin and Virtualmin, or with plain postfix installs.

See the comments about how the script determines who "we" really are.


# Looks at /etc/postfix/virtual and tells us which of those emails
# are _actually_ hosted by this system, based on whether DNS lookups of
# the domains seem to point to "us"... where "us" is defined as any
# of the IP addresses on any of localhost's interfaces.
# Naturally, this will fail if your system is behind a gateway/firewall,
# because we have no way of probing that gadget to see how connections
# are routed from "The Internet" to us.

# Copyright (c) 2012, William Lindley bill -at- saltriversystems -dot- com
# 2012-06-06

# This script is free software, you may distribute it and/or modify it
# under the same terms as Perl itself.

use Net::DNS;
use Socket qw/inet_aton/;

use IO::Socket;
use IO::Interface qw(:flags);

my $s = IO::Socket::INET->new(Proto => 'udp');
my @interfaces = $s->if_list;
my %local_interfaces;

for my $if (@interfaces) {
    my $flags = $s->if_flags($if);

    if ( ( $flags & IFF_RUNNING ) && 
     !( $flags & IFF_LOOPBACK ) &&
     !( $flags & IFF_NOARP )) {
    $local_interfaces{$if}{address} = $s->if_addr($if);
    $local_interfaces{$s->if_addr($if)}{interface} = $if;


my $r = Net::DNS::Resolver->new;

open VIRTUAL, '<', '/etc/postfix/virtual';

my %domains_hosted;

while (<VIRTUAL>) {
    s/#.*$//;  # Remove after comment
    my ($address, $alias) = split;
    if ($address) {
    if ($alias !~ /@/) { # Only for local addresses (not forwarded)
        my ($name, $domain) = ($address =~ /^([^@]+)@(.+)$/);
        next unless $name;
#        print "[$name]@[$domain] -> [$alias]n";
        $domains_hosted{$domain}{hosted} = 1;

use Data::Dumper;

foreach my $domain (keys %domains_hosted) {

# Liberally borrowed from David Landgren (grinder)'s code at
    my %res;
    my $rr = $r->query( $domain, 'MX' );
    if ($rr) {
    for my $mx( $rr->answer ) {
            if( $mx->type eq 'CNAME' ) {
                my $a_rr = $r->query( $mx->cname, 'A' );
                if( !$a_rr ) {
                    push @{$res{-1}}, { ip => $mx->cname, forw => $r->
                        errorstring, back => 'CNAME' };
                } else {
                    $_->type eq "A"
                        and push @{$res{-1}}, { ip => $mx->cname, forw => $_->address, back => 'CNAME' }
            for( $a_rr->answer );

            next unless $mx->type eq 'MX';

            my $a_rr = $r->query( $mx->exchange, 'A' );

            if( !$a_rr ) {
                push @{$res{$mx->preference ? $mx->preference : 0}}, {
                    ip   => $mx->exchange,
                    forw => $r->errorstring,
                    back => $r->errorstring,

            my @a;
            for my $a( $a_rr->answer ) {
                next unless $a->type eq "A";

                my $ptr_rr = $r->query( join( '.', reverse( split /./ , $a->address )) . '', 'PTR' );
        if ($local_interfaces{$a->address}{interface}) {
                if( !$ptr_rr ) {
                    push @{$res{$mx->preference}}, {
                        ip => $a->address,
                        forw => $mx->exchange,
                        back => $r->errorstring,
                } else {
                    foreach ( $ptr_rr->answer ) {
            if ( $_->type eq 'PTR' ) {
                push @{$res{$mx->preference}}, {
                ip => $a->address,
                forw => lc $mx->exchange,
                back => lc $_->ptrdname,

    $domains_hosted{$domain}{mx} = %res;

# This could be greatly expanded by doing more with the data herein:
# print Dumper(%domains_hosted);

print "These email accounts are actually hosted here:n";

foreach my $domain (sort keys %domains_hosted) {
    next unless $domains_hosted{$domain}{local};
    print $domain . "n";
    foreach my $email (sort keys %{$domains_hosted{$domain}{address}}) {
    print "   ${email}@${domain}n";


Printing an arbitrary PostScript document as a booklet


Use the ps2book script, documented here.

The above, a "non-trivial wrapper around" the psutils package, includes code that will insert PostScript "cookies" to force the correct double-sided printing on capable printers.

From is the following set of notes and an alternate method:

Using ps2book

This is a slight modification of Method A that uses ps2book, a wrapper around the psutils commands. It requires only 3 steps, but I am cheating ;-).

1) Change the document format to postscript:

pdftops -level3 file.pdf

This creates a postscript file named '' (pdftops [from the xpdf family] does a much better job than pdf2ps [from ghostscript] in my opinion).

If you want to print the resulting PS file, rather than converting it to PDF, you might need to use `-level2' if your printer only understands PostScript level 2.

2) Call ps2book:


This creates a file named ''.

ps2book has a number of options; a few useful ones are

ps2book -Pdisplay  # show result in gv
ps2book -f1.0      # set `fill factor' to 1, based on document bounding box
ps2book -F0.87     # set `fill factor' to 0.87, based on true bounding box
ps2book -m letter  # set output medium

The option `-f1.0' will do the right thing for 2xA5 -> A4 booklets, provided the document bounding box covers the whole paper size (which is the case with pdftops).

The `-F0.87' is what I normally use for printing almost anything as a booklet (leaves a 6.5% margin on each side).

ps2pdf tries to be clever about the output medium, but if it fails, use `-m' to set it explicitly.

3) Either print '' directly, or transform it back to pdf:


This creates the file 'file_book.pdf' (ps2pdf13 or ps2pdf14 often produce much more compact PDF than ps2pdf which by default writes PDF version 1.2; it is very hard to find somebody who cannot read PDF 1.3).

Alternate: Using psutils only

1) Change the document format to postscript:

pdf2ps file.pdf

This creates a postscript file named ''

2) Change the order of the pages:

psbook -s16

Where 16 is the number of pages that your document has. It should be a multiple of four.

3) Arrange the pages so two logical pages are printed on one physical sheet:

psnup -2 -w21cm -h16.3cm -W10.5cm -H16.3cm

Which arranges two logical pages of the size 105x163mm on a sheet of the size 210x163mm. If you have different page sizes, then simply change the dimensions. (Take a look to the man page of psnup) You can alternatively use the -p and -P flags to indicate page sizes:

psnup -2 -pa4 -Pa5

which will take two A5 pages and put them on A4 paper.

4) Transform it back to pdf:


This creates the file 'fileB.pdf'.

5) Print 'fileB.pdf' with the program of your choice.