#!/usr/bin/perl -w # # ichatserver_init_tool : tool for initializing the iChat Server database and initializing jabberd config files # # Apple Inc. (c) 2007-2008 - All rights reserved. # use Getopt::Std; use File::Basename; $g_my_name = "ichatserver_init_tool"; $g_jabber_var_dir = "/private/var/jabberd"; $g_db_dir = "$g_jabber_var_dir/sqlite"; $g_default_db_file = "$g_db_dir/jabberd2.db"; $g_db_setup_file = "$g_jabber_var_dir/db-setup.sqlite"; $g_conf_dir = "/private/etc/jabberd"; $g_tmp_dir = "$g_jabber_var_dir/tmp"; $g_pid_dir = "/private/var/run/jabberd"; $g_spool_dir = "/private/var/spool"; $g_log_dir = "$g_jabber_var_dir/log"; $g_logfile = "$g_log_dir/$g_my_name.log"; $g_sqlite3_path = "/usr/bin/sqlite3"; $g_jabber_user = 'jabber'; $g_jabber_group = 'jabber'; $g_admin_group = 'wheel'; $g_jabber_uid = getpwnam($g_jabber_user); $g_jabber_gid = getgrnam($g_jabber_group); $g_admin_gid = getgrnam($g_admin_group); $DEBUG = 0; $VERBOSE = 1; $LOGGING = 1; sub _usage() { print "$g_my_name: tool for initializing the iChat Server sqlite database and initializing jabberd config files\n"; print "Usage:\n"; print "$g_my_name: [-d]\n"; print "Options:\n"; print " -d: debug mode\n"; print " -i: install db, create jabberd pid dir, and configure config files (set hostname and router random router passwords)\n"; print " -s: install sqlite database only\n"; print " -c: configure config files (set hostname and router random router passwords) only\n"; print " -p : specify the full path for the jabberd database file. (Default: $g_default_db_file)\n"; print "\n"; } sub _timestamp() { my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime(time); $year += 1900; $mon += 1; if ( $hour =~ /^\d$/ ) { $hour = "0" . $hour; } if ( $min =~ /^\d$/ ) { $min = "0" . $min; } if ( $sec =~ /^\d$/ ) { $sec = "0" . $sec; } my $ret = $year."-".$mon."-".$mday."-$hour:$min:$sec"; return $ret; } sub _log() { my $t = &_timestamp; if ($LOGGING) { open(LOG, ">>$g_logfile") || die "ERROR: could not open log file at $g_logfile: $!"; print LOG "$t: $_[0]\n"; } if ($VERBOSE) { print "$t: $_[0]\n"; } } sub _bail() { &_log($_[0]); exit 1; } # Generate a random password. # arguments: # 0: length of password to create [required] # # returns: # on failure: empty string # on success: the password string # sub random_password_gen() { if ($_[0] !~ /^\d+$/) { &_log("Invalid argument provided to random_password_gen(). Must be a valid non-negative integer."); return ""; } my $length = $_[0]; my $dev = "/dev/random"; my $password = ""; my $matchlist= q{ABCDEFGHIJKLMNOPQRSTUVWXYZJabcdefghijklmnopqrstuvwxyz0123456789`~!#$%^*()-=_+[]\\|;':",./?}.'{}'; my $random_bits; &_log("Generating random password..."); eval { $random_bits = sprintf("%0.0f", log(length($matchlist) ** $length) / log(2)); }; $matchlist =~ s|([^A-Za-z0-9])|\\$1|g; if (! open(DEV, "<$dev")) { &_log("Can't open random device $dev ($!)"); return ""; } until ($password =~ /^.{$length}$/) { $_ = getc DEV; $password .= $_ if (m/[$matchlist]/); } &_log("Generated random password..."); close(DEV); return $password; } # Create pid directory used by proxy and jabberd sub init_pid_dir() { if (! -e $g_pid_dir) { &_log("Creating pid directory $g_pid_dir..."); if (! mkdir($g_pid_dir, 0775)) { &_log("Unable to create directory $g_pid_dir ($!)"); return 1; } chown($g_jabber_uid, $g_jabber_gid, $g_pid_dir); } else { &_log("Directory already exists: $g_pid_dir"); } return 0; } # Create spool dir for mu-conference sub init_muc_spool_dir() { my $hostname = `hostname`; chomp($hostname); my $full_spool_path = $g_spool_dir."/conference.".$hostname; if (! -e $full_spool_path) { &_log("Creating MUC spool directory $full_spool_path..."); if (! mkdir($full_spool_path, 0770)) { &_log("Unable to create directory $full_spool_path ($!)"); return 1; } chown($g_jabber_uid, $g_jabber_gid, $full_spool_path); } else { &_log("Directory already exists: $full_spool_path"); } return 0; } # Initialize sqlite database file for jabberd. # returns: # 0: success # 1: error # 2: database file already exists # sub init_db() { my $db_file = shift; &_log("Creating SQLite database setup for jabberd..."); if (! defined($db_file)) { $db_file = $g_default_db_file; } if (-e "$db_file") { &_log("Already exists: $db_file"); return 2; } my $db_containing_dir = dirname($db_file); if (! -d $db_containing_dir) { &_log("$db_containing_dir does not exist. Creating..."); if (! mkdir($db_containing_dir, 0750)) { &_log("Cannot create directory: $db_containing_dir ... Aborting ($!)"); return 1; } } my $ret = chown($g_jabber_uid, $g_jabber_gid, $db_containing_dir); unless ($ret) { &_log("Failed to set correct ownership on $db_containing_dir ($!)"); return 1; } if (! -e $g_db_setup_file) { &_log("Cannot find: $g_db_setup_file"); return 1; } if (! -e $g_sqlite3_path) { &_log("$g_sqlite3_path does not exist. Aborting."); return 1; } $exec_string = "\"$g_sqlite3_path\" \"$db_file\" < \"$g_db_setup_file\""; my $mask = umask; umask(027); $ret = system($exec_string); umask($mask); if ($ret != 0) { &_log("Execution failed with status $ret: $exec_string ($!)"); return 1; } $ret = chown($g_jabber_uid, $g_jabber_gid, $db_file); unless ($ret) { &_log("Failed to set correct ownership on $db_file ($!)"); return 1; } return 0; } sub init_configs() { my @lines; my $line; my @files = ("c2s.xml", "sm.xml", "s2s.xml", "resolver.xml", "router.xml", "router-users.xml", "muc-jcr.xml" ); my $file; my $hostname = `hostname`; my $router_password = &random_password_gen(15); if ($router_password eq "") { &_log("random_password_gen failed, aborting"); return 1; } chomp($hostname); foreach $file (@files) { if (! open(FILE, "<$g_conf_dir/$file")) { &_log("Could not open file: $g_conf_dir/$file ($!)"); return 1; } @lines = ; close(FILE); my $mask = umask; umask(077); if (! open(NEW_FILE, ">$g_tmp_dir/$file.new")) { umask($mask); &_log("Could not open file: $g_tmp_dir/$file.new ($!)"); return 1; } umask($mask); foreach $line (@lines) { if ($line =~ /\@HOSTNAME\@/) { &_log("Found hostname tag in $g_conf_dir/$file, replacing string with $hostname..."); $line =~ s/\@HOSTNAME\@/$hostname/g; } if ($line =~ /\@ROUTERPASSWORD\@/) { &_log("Found password tag in $g_conf_dir/$file, replacing string..."); $line =~ s/\@ROUTERPASSWORD\@/$router_password/g; } print NEW_FILE "$line"; } close(NEW_FILE); $mask = umask; umask(077); my $ret = rename("$g_tmp_dir/$file.new", "$g_conf_dir/$file"); umask($mask); unless ($ret) { &_log("rename failed with status $ret: ($!)"); return 1; } # Set the ownership and permissions for the file $ret = chown($g_jabber_uid, $g_admin_gid, "$g_conf_dir/$file"); unless ($ret) { &_log("chown failed with status $ret: ($!)"); } $ret = chmod(0600, "$g_conf_dir/$file"); unless ($ret) { &_log("chmod failed with status $ret: ($!)"); } } return 0; } ####### MAIN getopts('discp:?h', \%opts); if (defined $opts{'d'}) { $DEBUG = 1; } if (defined $opts{'?'} || defined $opts{'h'}) { &_usage; exit 0; } my $db_file; if (defined $opts{'p'}) { $db_file = $opts{'p'}; } if (defined $opts{'i'}) { $ret = &init_pid_dir; if ($ret > 0) { &_bail("Exiting because pid dir could not be created"); } $ret = &init_muc_spool_dir; if ($ret > 0) { &_bail("Exiting because muc spool dir could not be created"); } $ret = &init_db($db_file); if ($ret == 2) { &_log("Exiting since database already exists. Use -c to init config files only."); exit 0; } elsif ($ret == 1) { &_bail("Aborting since init_db failed."); } $ret = &init_configs; if ($ret > 0) { &_bail("Aborting since init_configs failed."); } } elsif (defined $opts{'s'}) { $ret = &init_db($db_file); if ($ret == 1) { &_bail("Aborting since init_db failed."); } } elsif (defined $opts{'c'}) { $ret = &init_configs; if ($ret > 0) { &_bail("Aborting since init_configs failed."); } } else { &_log("Unrecognized options, exiting"); &_usage; exit 1; } &_log("Finished.");