Meta-verify-raq3

From CobaltFAQs

Jump to: navigation, search

If your GUI won't allow you to add new sites or new users (unable to convert from vanilla group to shadow group), or if you are having issuesin general with the GUI (such as hanging or time-outs when creating a new site or new user), you might need this script.

1. Have you backed up?

2. Log in via SSH as 'admin' and perform the following commands:

 su -
 (enter root user password when prompted)
 cd /home/

3. Create the file meta-verify.pl and put the script below in it. Then make it executable:

 chmod +x meta-verify.pl

4. Follow the instructions displayed by these commands:

 ./meta-verify.pl -u -a
 ./meta-verify.pl -u -i
 ./meta-verify.pl -s -a
 ./meta-verify.pl -s -i

This should restore and repair your GUI.

In the event that you get the error message "Unable to convert from vanilla group to shadow group"

1. Check /etc/locks for old lock files

 ls -oA /etc/locks

Only monitor and services files should exist. Remove all others.

2. If you still have problems, the group shadow file needs to be rebuilt. Make a backup copy of your shadow group file, just in case:

 cp /etc/gshadow /etc/gshadow.bak

Then delete the "bad" file and remake it:

 rm gshadow
 grpconv

Your GUI should work now. You may want to perform a reboot, but it should not be necessary.


#!/usr/bin/perl

#######  meta-verify.pl
#
#  system & database verification and integrity check utility
#
#  Duncan Laurie (duncan@cobalt.com)
#  (c) 2000 Cobalt Networks, Inc
#
#
#######  OPERATION
#
#  perl meta-verify.pl -u|s -a|i|d [-v] [-h]
#
#    -u   users
#    -s   virtual sites
#
#    -i   repair invalid
#    -a   verify all valid
#    -d   drop table
#
#    -v   increase warning level
#    -h   usage help
#
#
#######  USES
#
#  - Verify and repair virtual sites.
#  - Verify and repair user accounts.
#  - Maintain intergrity of the RaQ3 system configuration files.
#  - Ensure coherency between System and Meta/postgreSQL backend.
#
#
#######  DESCRIPTION
#
#  The RaQ3 (all varieties pre Update 2.0) contains a bug w.r.t. handling
#  single quote characters in vacation messages and usernames that
#  begin with a decimal digit.  The result of this bug is that some or even
#  all of your users could become "detached" from the Web GUI -- they will
#  exist on the system but not show up in the Admin interface.
#
#  This script attempts to detect and repair coherency problems that may
#  result from this bug.  It will find invalid (detached) users and
#  present them as Meta object which can be saved to the postgreSQL backend.
#
#  UPDATE: as of version 2.0 this can now rebuild the virtual site table.
#
#
#######  REQUIREMENTS
#
#  RaQ3 / RaQ3i / RaQ3-ja
#
#
#######  CHANGELOG
#
# version 2.1 (Apr 18 2000)
#   - added 'Y' option to say yes to all
#
# version 2.0 (Mar 01 2000)
#   - now has support for rebuilding the virtual site list
#   - command line options have changed
#
# version 1.2 (Feb 16 2000)
#   - don't show conflict on self->self aliases
#   - vacationmsg field is now blank
#
# version 1.1 (Feb 04 2000)
#   - fix display of user info that is longer than page width
#   - fix alias handling
#   - verifies system is RaQ3 and user is root
#
# version 1.0 (Feb 03 2000)
#   - initial release
#   - fix incorrect reporting of frontpage state
#
# version 0.9 (Feb 02 2000)
#   - verify valid user accounts, prompt database update when changes detected
#   - prompts for and allows database deletion of sites that do not exist in
#     the system configuration
#   - fix int() conversion & mismatched parens
#
# version 0.8 (Feb 01 2000)
#   - @{ union,intersect,symmetric difference } for database/system user lists
#   - detect invalid system users correctly
#
# version 0.7 (Feb 01 2000)
#   - meta object containing all user fields, completed with information from
#     current system configuration and state information
#
# version 0.1 (Jan 31 2000)
#   - initial creation
#
#
#######  

# verify that this is a 3000R
my $build_file = "/etc/build";
if (-e $build_file) {
    if (system("/bin/grep", "-q", "3[01]00R", "$build_file")) {
        die("\nThis program is only for the RaQ3!\n\n");
    }
} else {
    die("\nThis program is only for the RaQ3!\n\n");
}

# verify that we are root
if ($< != 0) {
    die("\nThis program must be run as root!\n\n");
}

require Cobalt::Meta;
require Cobalt::User;
require Cobalt::Vacation;
require Cobalt::Email;
require Cobalt::List;
require Cobalt::Fpx;
require Cobalt::Ftp;
require Cobalt::BandWidth;
require Cobalt::Quota;

use Getopt::Std;
use IO::File;

# program info
use vars qw($Title $Product $Version $Author $Company);
$Product = "meta-verify.pl";
$Title   = '"system & database verifier"';
$Author  = "Duncan Laurie (duncan\@cobalt.com)";
$Company = "(c) 2000 Cobalt Networks, Inc";
$Version = "2.2";

# program header
printf("\n");
printf("%s\n", $Product);
printf("%s\n", $Title);
printf("version %s\n", $Version);
printf("%s\n", $Author);
printf("%s\n", $Company);

# global variables
use vars qw($e %eCOUNT @eALL @eGOOD @eBAD);
use vars qw(@eFIELDS @eARRAYS);
use vars qw(@all_DB @all_SYS $entryX $entry $entryTYPE);
use vars qw($opt_h $opt_u $opt_s $opt_v $opt_a $opt_i $opt_d $opt_l $Verbose);
use vars qw($login $message $field $DB_field $SYS_field);

# format for output of messages
format PRINT_MSG =
                | ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                  $message
~                 ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<...
                  $message
.

# format for output of messages
format PRINT_ENTRY =
@>>>>>>>>>>>>>> | ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$login,           $message
~                 ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<...
                  $message
.

# format for output when handling entry errors
format PRINT_ENTRY_ERROR =
@>>>>>>>>>>>>>> | ERROR ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$login,                 $message
~                       ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<...
                        $message
.

# format for output when processing
format PRINT_FIELD =
@>>>>>>>>>>>>>> | @>>>>>>>>>>>>> = ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$login,           $field,          $message
~                                  ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                   $message
~                                  ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<...
                                   $message
.

# format for error output when comparing meta+system fields
format PRINT_FIELD_ERROR =
@>>>>>>>>>>>>>> | ERROR IN @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$login,                    $field
                | DATABASE ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                           $DB_field
~               |          ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                           $DB_field
~               |          ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<...
                           $DB_field
                |   SYSTEM ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                           $SYS_field
~               |          ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                           $SYS_field
~               |          ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<...
                           $SYS_field
.

use strict;

    
# command-line arguments, verbose level
getopts("suvaihdl");
    
# usage help
if ($opt_h)
{
    printf("\n");
    printf(" usage: %s -u|s -a|i|d [-v] [-h]\n", $Product);
    printf("\n");
    printf("   -l   reset /home/sites symlinks\n");
    printf("   -u   verify users\n");
    printf("   -s   verify virtual sites\n");
    printf("\n");
    printf("   -i   repair invalid\n");
    printf("   -a   verify valid\n");
    printf("   -d   drop table\n");
    printf("\n");
    printf("   -v   increase warning level\n");
    printf("   -h   usage help\n");
    printf("\n");
    exit 1;
}
$Verbose = ($opt_v) ? 1 : 0;

if ($opt_l) {
    # reset virtual site symlinks with
    # entries from /etc/httpd/conf/httpd.conf
    my $root = "/home/sites";
    my $httpd_conf = "/etc/httpd/conf/httpd.conf";
    my($vpath,$vhost,$host);

    unless (-s $httpd_conf) {
	printf("ERROR: $httpd_conf not found\n");
	exit 1;
    }

    my $htconf = new IO::File "$httpd_conf";
    unless (defined $htconf) {
	printf("ERROR: unable to open $httpd_conf: $!\n");
	exit 1;
    }

    print "\n";
    while (<$htconf>) {
	if (m{^<VirtualHost [0-9\.]+>} ... m{^</VirtualHost>}) {
	    if (m{^DocumentRoot $root/([^/]+)/web}) {
		$entry = $1;
		$vpath = "$root/$entry";
	    }
	    elsif (m{^ServerName (.+)}) {
		$host  = $1;
		$vhost = "$root/$host";
	    }

	    if (m{^</VirtualHost>}) {
		next unless (-d $vpath);
		unlink $vhost if (-e $vhost);
		print_entry($host);
		symlink $vpath, $vhost;
		$vpath=$vhost="";
	    }
	}
    }
    print "\n";

    undef $htconf;
    exit 0;
}

printf("\nchecking system+database coherency...\n");

if ($opt_u)
{
    &setup_users();
}
elsif ($opt_s)
{
    &setup_vsite();
}
else
{
    printf("error\n - must specify users (-u) or virtual sites (-s)\n\n");
    exit 1;
}

if ($opt_d)
{
    printf("dropping database table...\n");
    &Cobalt::Meta::drop($entryTYPE);
#    {
#	printf("ERROR - unable to drop database \"$entryTYPE\"\n\n");
#	exit 1;
#    }
    printf("OK\n\n");
    exit 0;
}

foreach $e (@all_DB, @all_SYS)
{
    $eCOUNT{$e}++;
}

foreach $e (keys %eCOUNT)
{
    # union -
    # all in database or system
    push(@eALL, $e);

    if ($eCOUNT{$e} == 2)
    {
	# intersection -
	# both in database and in system
	push(@eGOOD, $e);
    }
    else
    {
	# symmetric difference -
	# either not in database or not in system
	push(@eBAD, $e);
    }
}

# output NUMBERS
print "\n";
    printf("       database | %d\n", scalar(@all_DB));
    printf("         system | %d\n", scalar(@all_SYS));
    printf("          total | %d\n", scalar(@eALL));
    printf("          valid | %d\n", scalar(@eGOOD));
    printf("        invalid | %d\n", scalar(@eBAD));
print "\n";

if ($opt_a)
{
    printf("verifying %d VALID %s...\n\n", scalar(@eGOOD),
	   (($opt_s) ? "virtual site(s)" : "user(s)"));
    &examine_valid(@eGOOD);
}
elsif ($opt_i)
{
    printf("repairing %d INVALID %s...\n\n", scalar(@eBAD),
	   (($opt_s) ? "virtual site(s)" : "user(s)"));
    &examine_invalid(@eBAD) if (scalar(@eBAD));
}
else
{
    printf("error - must specify valid (-a) or invalid (-i)\n\n");
    exit 1;
}

print "\n" unless ($Verbose);
printf("OK\n\n");

exit 0;

1;

sub print_msg
{
    $message = "@_";
    $~ = 'PRINT_MSG';
    write();
}

sub print_entry
{
    $message = "@_";
    $login = "$entry";
    $~ = 'PRINT_ENTRY';
    write();
}

sub print_entry_error
{
    $message = "(@_)";
    $login = "$entry";
    $~ = 'PRINT_ENTRY_ERROR';
    write();
}

sub print_field
{
    ($field,$message) = (@_);
    $login = "$entry";
    $~ = 'PRINT_FIELD';
    write();
}

sub print_field_error
{
    ($field,$DB_field,$SYS_field) = (@_);
    $field = "($field)";
    $DB_field = "($DB_field)";
    $SYS_field = "($SYS_field)";
    $login = "$entry";
    $~ = 'PRINT_FIELD_ERROR';
    write();
}

# examine_invalid ()
#
#   check the LIST for invalid
#   correct the problem if possible
#
#   arg:  list to check
#
sub examine_invalid ()
{
    my (@eLIST) = (@_);
    my $yes = 0;
    
    foreach $entry (@eLIST)
    {
	my $inDB  = 0;
	my $inSYS = 0;

	printf("\n");

	# see if this entry exists in the database
	if (grep(/\b$entry\b/, @all_DB))
	{
	    $inDB = 1;
	}
	else
	{
	    $inDB = 0;
	    &print_entry_error("entry does not exist in database");
	}

	# see if this entry exists in the system
	&print_msg("retrieving information from system...") if ($Verbose);
	$entryX = &build_system("$entry");
	if ($entryX)
	{
	    $inSYS = 1;
	}
	else
	{
	    &print_entry_error("does not exist in system");
	    $inSYS = 0;
	}

	# in both system and database
	# this should never happen
	if ($inSYS && $inDB)
	{
	    &print_entry_error("entry is already valid");
	    $entryX->DESTROY();
	    next;
	}

	# in system but not database
	if ($inSYS)
	{
	    my $input;

	    # confirm add
	    while ($input !~ /^[Yyn]/ && !$yes)
	    {
		printf("                | <Y> - save ALL\n");
		printf("                | <y> - save entry to database\n");
		printf("                | <n> - do NOT save entry to database\n");
		printf("                | <v> - view info obtained from system\n");
		printf("                | save? [Yynv] ");

		$input = <STDIN>;
		$yes++ if ($input =~ /^Y/);

		# print info
		if ($input =~ /^v/)
		{
		    $Verbose++;
		    $entryX->DESTROY();
		    $entryX = &build_system("$entry");
		    $Verbose--;
		}
	    }

	    # always give the chance to bail
	    next if ($input =~ /^n/);

	    # save
	    unless ($entryX->save())
	    {
		&print_entry_error("unable to save");
		$entryX->DESTROY();
		next;
	    }

	    &print_entry("saved");
	    $entryX->DESTROY();
	    next;
	}

	# in database but not system
	if ($inDB)
	{
	    my $input = '';
	    my $entryY = Cobalt::Meta->new("type" => "$entryTYPE");

	    # confirm database entry delete
	    while ($input !~ /^[yn]/)
	    {
                printf("                | <y> - remove database entry\n");
                printf("                | <n> - do NOT remove database entry\n");
		printf("                | remove? [yn] ");
		$input = <STDIN>;
	    }

	    # always give the chance to bail
	    next if ($input =~ /^n/);

	    # delete entry
	    $entryY->retrieve("$entry");
	    unless ($entryY->delete())
	    {
		&print_entry_error("unable to remove");
		$entryY->DESTROY();
		next;
	    }

	    $entryY->DESTROY();
	    &print_entry("removed");
	    next;
	}

	# not in database or system
	&print_entry_error("entry not in database OR system");
	next;
    }

    return;
}

# examine_valid ()
#
#   check the LIST for valid and verify
#   coherency of fields between system and database
#
#   correct the problem if possible
#
#   arg:  list to check
#
sub examine_valid ()
{
    my (@eLIST) = (@_);

    foreach $entry (@eLIST)
    {
	# this entry from the database
	my $entry_DB = (Cobalt::Meta->new("type" => "$entryTYPE"));
	unless ($entry_DB->retrieve("$entry"))
	{
	    &print_entry_error("entry non-existant in database");
	    next;
	}

	# get info from the system
	my $entry_SYS = &build_system("$entry");
	unless ($entry_SYS)
	{
	    &print_entry_error("entry does not exist in system");
	    next;
	}

	# compare fields
	my $errcount = 0;
	foreach my $fld (@eFIELDS)
	{
	    if ($entry_DB->{$fld} ne $entry_SYS->{$fld})
	    {
		$errcount++;
		&print_field_error($fld, $entry_DB->{$fld}, $entry_SYS->{$fld});
	    }
	}

	foreach my $fld (@eARRAYS)
	{
	    my ($e,@aDB,@aSYS,%count,@diff);

	    # turn flat string into list
	    @aDB = split(' ', $entry_DB->{$fld});
	    @aSYS = split(' ', $entry_SYS->{$fld});

	    # compute difference of lists
	    foreach $e (@aDB, @aSYS) { $count{$e}++ }
	    foreach $e (keys %count) { push (@diff, $e) unless ($count{$e} == 2) }

	    # if difference then error
	    if (scalar(@diff))
	    {
		$errcount++;
		&print_field_error($fld, join(', ', @aDB), join(', ', @aSYS));
	    }
	}

	if ($errcount)
	{
	    my $input;

	    # confirm database update
	    while ($input !~ /^[yn]/)
	    {
		printf("                | <y> - update database info for entry\n");
		printf("                | <n> - do NOT update database\n");
		printf("                | update? [yn] ");
		$input = <STDIN>;
	    }

	    # always give the chance to bail
	    if ($input =~ /^n/)
	    {
		print "\n";
		next;
	    }

	    # save
	    unless ($entry_SYS->save())
	    {
		&print_entry_error("unable to save");
		print "\n";
	    }
	    else
	    {
		&print_entry("updated");
		print "\n";
	    }
	}
	else
	{
	    &print_entry("ok");
	    print "\n" if ($Verbose);
	}

	$entry_DB->DESTROY();
	$entry_SYS->DESTROY();
    }

    return;
}

sub setup_users ()
{
    $entryTYPE = "users";

    # some global variables
    # user fields to compare when verifying all users
    @eFIELDS = ("name", "fullname", "uid", "vsite", "quota",
		"vacation", "admin", "shell", "apop", "fpx", "suspend");
    # these fields are to be treated as arrays
    # where order does NOT matter
    @eARRAYS = ("forward", "aliases");
    
    # the global database views and Meta objects
    $entryX = Cobalt::Meta->new("type" => "users");
    @all_DB = $entryX->getall();
    @all_SYS = Cobalt::User::user_list();

    # remove admin from both sets
    @all_SYS = grep !/\bdefault\b/, @all_SYS;
    @all_SYS = grep !/\badmin\b/, @all_SYS;
    @all_DB = grep !/\bdefault\b/, @all_DB;
    @all_DB = grep !/\badmin\b/, @all_DB;

    if ($Verbose)
    {
	print "\n";
	foreach $e (@all_DB) { &print_field("db user", $e); }
	foreach $e (@all_SYS) { &print_field("sys user", $e); }
	print "\n";
    }
}

sub setup_vsite ()
{
    $entryTYPE = "vsite";

    # some global variables
    # user fields to compare when verifying all users
    @eFIELDS = ("emaildomain", "name", "suspend", "fqdn", "quota",
		"ftp", "fpx", "ssi", "ssl", "cgi", "ipaddr",
		"webdomain", "bwlimit", "domain", "hostname");

    # these fields are to be treated as arrays
    # where order does NOT matter
    @eARRAYS = ();
    
    # the global database views and Meta objects
    $entryX = Cobalt::Meta->new("type" => "vsite");
    @all_DB = $entryX->getall();
    map { push @all_SYS, @$_[2]; } (Cobalt::Vsite::vsite_list());

    # remove from both sets
    @all_SYS = grep !/\bdefault\b/, @all_SYS;
    @all_DB = grep !/\bdefault\b/, @all_DB;

    if ($Verbose)
    {
	print "\n";
	foreach $e (@all_DB) { &print_field("db vsite", $e); }
	foreach $e (@all_SYS) { &print_field("sys vsite", $e); }
	print "\n";
    }
}

sub build_system ()
{
    my $ebuild = shift;
    return unless ($ebuild);

    if ($entryTYPE eq "users")
    {
	return build_user_system($ebuild);
    }
    elsif ($entryTYPE eq "vsite")
    {
	return build_vsite_system($ebuild);
    }

    return;
}

sub build_vsite_system ()
{
    my $site = shift;
    return unless ($site);

    my(@tmpARRAY);
    my($newVSITE) = Cobalt::Meta->new("type" => "vsite");
    $newVSITE->put("name" => "$site",

		   # network info
		   "ipaddr" => "",
		   "hostname" => "",
		   "domain" => "",
		   "fqdn" => "",

		   # configuration values
		   "maxusers" => 100,
		   "quota" => 1000,
		   "ftpquota" => 20,
		   "ftpusers" => 10,
		   "gid" => 0,

		   # default options to off
		   "ftp" => "off",
		   "fpx" => "off",
		   "ssi" => "off",
		   "cgi" => "off",
		   "ssl" => "off",
		   "apop" => "off",
		   "shell" => "off",
		   "suspend" => "off",
		   "bwlimit" => "off",
		   "webdomain" => "off",
		   "emaildomain" => "off",

		   # user defaults
		   "userlist_range" => 15,
		   "userlist_sort" => "name",
		   "user_namegen" => 0,
		   "user_quota" => 10,
		   "user_shell" => "off",
		   "user_fpx" => "off",
		   "user_apop" => "off"
		   );

    &print_field("group", $newVSITE->get("name")) if ($Verbose);

    # verify the group existance
    unless (Cobalt::Group::group_exist("$site"))
    {
	&print_entry_error("group does not exist");
	return;
    }

    # retrieve network info from httpd.conf
    my($ipaddr,$fqdn) = (Cobalt::Vsite::vsite_get_bygroup("$site"))[0,1];
    unless ($ipaddr && $fqdn)
    {
	&print_entry_error("does not exist in apache configuration");
	return;
    }
    $newVSITE->put("ipaddr", "$ipaddr");
    &print_field("ipaddr", $newVSITE->get("ipaddr")) if ($Verbose);
    $newVSITE->put("fqdn", "$fqdn");
    &print_field("fqdn", $newVSITE->get("fqdn")) if ($Verbose);

    # parse fqdn for hostname+domain
    unless ($fqdn =~ /([-\w]+)\.([-\w\.]+)/)
    {
	&print_entry_error("unable to split \"$fqdn\" into hostname+domain");
	return;
    }
    my($hostname,$domain) = ($1,$2);
    $newVSITE->put("hostname", "$1");
    &print_field("hostname", $newVSITE->get("hostname")) if ($Verbose);
    $newVSITE->put("domain", "$2");
    &print_field("domain", $newVSITE->get("domain")) if ($Verbose);

    # get the GID
    $newVSITE->put("gid", (getgrnam("$site"))[2]);
    &print_field("GID", $newVSITE->get("gid")) if ($Verbose);

    # see if this has a serveralias for the domain (webdomain)
    my($found) = 0;
    if (Cobalt::Util::FILE_lock_r("/etc/httpd/conf/httpd.conf", *HTCONF))
    {
	while (<HTCONF>)
	{
	    if (m%^<VirtualHost [\d\.]+>$% ... m%^</VirtualHost>$%) {
		$found = 1 if (m%^DocumentRoot /home/sites/\Q$site\E/web$%);
		if ($found)
		{
		    last if (m%^</VirtualHost>$%);
		    if (m%^ServerAlias \Q$domain\E$%)
		    {
			$newVSITE->put("webdomain", "on");
			last;
		    }
		}
	    }
	}
	Cobalt::Util::FILE_unlock(*HTCONF);
    }
    else
    {
	&print_entry_error("cannot open apache config file") if ($Verbose);
    }
    &print_field("web \@domain", $newVSITE->get("webdomain")) if ($Verbose);

    # get emaildomain setting
    my(%routes) = Cobalt::Email::mail_virtuser_list_route();
    $newVSITE->put("emaildomain", exists($routes{"$domain"}) ? "on" : "off");
    &print_field("email \@domain", $newVSITE->get("emaildomain")) if ($Verbose);

    # get disk quota
    my($quota) = (Cobalt::Quota::repquota("$site", 1))[1];
    if ($quota > 0)
    {
	$newVSITE->put("quota", int(($quota * 1024) / 1048576));
    }
    &print_field("disk quota", $newVSITE->get("quota")) if ($Verbose);

    # get anon ftp settings from proftpd config file
    my($ftp,$ftpusers,$ftpquota) = Cobalt::Ftp::ftp_get_anonymous("$site");
    if ($ftp)
    {
	$newVSITE->put("ftp", "on");
	$newVSITE->put("ftpusers", $ftpusers);
	if ($ftpquota > 0)
	{
	    $newVSITE->put("ftpquota", int(($ftpquota * 1024) / 1048576));
	}
    }
    &print_field("anonymous FTP", $newVSITE->get("ftp")) if ($Verbose);

    # get suspend flag
    $newVSITE->put("suspend", (Cobalt::Vsite::vsite_issuspend("$site")) ? "on" : "off");
    &print_field("suspended", $newVSITE->get("suspend")) if ($Verbose);

    # get frontpage settings
    $newVSITE->put("fpx", (Cobalt::Fpx::fpx_get_web("$site")) ? "on" : "off");
    &print_field("frontpage", $newVSITE->get("fpx")) if ($Verbose);

    # get cgi settings
    $newVSITE->put("cgi", (Cobalt::Vsite::vsite_get_cgis("$site")) ? "on" : "off");
    &print_field("CGI scripts", $newVSITE->get("cgi")) if ($Verbose);

    # get ssi settings
    $newVSITE->put("ssi", (Cobalt::Vsite::vsite_get_ssi("$site")) ? "on" : "off");
    &print_field("SSI scripts", $newVSITE->get("ssi")) if ($Verbose);

    # get ssl settings
    $newVSITE->put("ssl", (-f "/etc/httpd/ssl/$site") ? "on" : "off");
    &print_field("SSL server", $newVSITE->get("ssl")) if ($Verbose);

    # attempt to get bandwidth limit
    $newVSITE->put("bwlimit", (Cobalt::BandWidth::bw_getip("$ipaddr") > 0) ? "on" : "off");
    &print_field("bandwidth", $newVSITE->get("bwlimit")) if ($Verbose);

    printf("\n") if ($Verbose);

    return $newVSITE;
}

# build_user_system ()
#
#   tries to build a Meta object of type "users"
#   from information gathered by the system
#
#   arg:  login name
#   ret:  Meta object (type => users)
#
sub build_user_system ()
{
    my $user = shift;
    return unless ($user);

    my (@tmpARRAY);
    my $newUSER = Cobalt::Meta->new("type" => "users");

    # get basic user info
    my ($name,$uid,$fullname,$dir,$sh) = (getpwnam("$user"))[0,2,6,7,8];
    unless ($name)
    {
	&print_entry_error("does not exist in password file");
	return;
    }

    # determine virtual site from home directory path
    my $vsite;
    if ($dir =~ m|^/home/sites/home/users/|)
    {
	$vsite = "home";
    }
    elsif ($dir =~ m|^/home/sites/(site[0-9]+)/users/|)
    {
	$vsite = "$1";
    }
    else
    {
	&print_entry_error("invalid home directory");
	return;
    }

    printf("\n") if ($Verbose);

    # user name
    $newUSER->put("name", "$name");
    &print_field("user name", $name) if ($Verbose);

    # full name
    $newUSER->put("fullname", "$fullname");
    $newUSER->put("altname", "");
    &print_field("full name", $fullname) if ($Verbose);

    # UID
    $newUSER->put("uid", "$uid");
    &print_field("UID", $uid) if ($Verbose);

    # virtual site membership
    $newUSER->put("vsite", "$vsite");
    &print_field("virtual site", $vsite) if ($Verbose);

    # disk quota
    my($quota) = (Cobalt::Quota::repquota("$user"))[1];
    $newUSER->put("quota", (($quota > 0) ? int(($quota * 1024) / 1048576) : 0));
    &print_field("disk quota", $newUSER->get("quota")) if ($Verbose);

    # email aliases
    my $aliases = "";
    @tmpARRAY = (Cobalt::Email::mail_virtuser_get_byuser("$user"));
    if (scalar(@tmpARRAY))
    {
	# remove domains
	my @oARRAY;
	foreach (@tmpARRAY) {
	    if (/^([^@]+)\@.*/) {
                push(@oARRAY, $1) unless($1 eq $user);
            } else {
		push(@oARRAY, $_);
            }
        }
	$aliases = join(" ", @oARRAY);
    }
    $newUSER->put("aliases", "$aliases");
    &print_field("email aliases", $aliases) if ($Verbose);

    # email forwarding
    @tmpARRAY = (Cobalt::List::alias_get_vacationless("$user"));
    $newUSER->put("forward", ((scalar(@tmpARRAY) > 0) ? join(" ", @tmpARRAY) : "off"));
    &print_field("email forward", $newUSER->get("forward")) if ($Verbose);

    # virtual site administrator
    @tmpARRAY = (Cobalt::User::user_list_groups("$user"));
    $newUSER->put("admin", ((scalar(grep(/\b$vsite\b/, @tmpARRAY))) ? "on" : "off"));
    &print_field("site admin", $newUSER->get("admin")) if ($Verbose);

    # suspended user
    $newUSER->put("suspend", ((Cobalt::User::user_issuspend("$user")) ? "on" : "off"));
    &print_field("suspended", $newUSER->get("suspend")) if ($Verbose);

    # telnet/shell access
    $newUSER->put("shell", (($sh eq "/bin/bash") ? "on" : "off"));
    &print_field("telnet/shell", $newUSER->get("shell")) if ($Verbose);

    # authenticated pop3 (APOP)
    $newUSER->put("apop", ((Cobalt::Email::mail_apop_isuser("$user")) ? "on" : "off"));
    &print_field("secure POP3", $newUSER->get("apop")) if ($Verbose);

    # frontpage extensions
    @tmpARRAY = (Cobalt::Vsite::vsite_get_fpx("$vsite"));
    $newUSER->put("fpx", ((scalar(grep(/\b$user\b/, @tmpARRAY))) ? "on" : "off"));
    &print_field("frontpage", $newUSER->get("fpx")) if ($Verbose);

    # email vacation responder
    $newUSER->put("vacation", ((Cobalt::Vacation::vacation_get_on("$user")) ? "on" : "off"));
    &print_field("vacation", $newUSER->get("vacation")) if ($Verbose);
    $newUSER->put("vacationmsg", "");

    printf("\n") if ($Verbose);

    return $newUSER;
}
Personal tools