#!/usr/bin/php
<? /*
// File: webcp.php
// Purpose: web://cp CGI Daemon
// Creation: 2002-02-28
// Author: Felix <hide@address.com>
*/
//explicitly set error level for people who have changed their php.ini defaults
error_reporting(E_ALL & ~E_NOTICE);
// Change working directory
chdir(dirname(__FILE__));
// Include configuration, system and interface functions
include("../web/config.inc.php");
include("../web/functions.inc.phps");
// server functions
include("functions.inc.phps");
// Make sure the php CGI has the needed modules
$i = 0;
unset($error);
if (!extension_loaded('mysql'))
$error[++$i] = "PHP Error: The MySQL module is required (configure PHP '--with-mysql=/usr')\n";
if (!extension_loaded('ftp'))
$error[++$i] = "PHP Error: The FTP module is required (configure PHP '--enable-ftp')\n";
if (!extension_loaded('zlib'))
$error[++$i] = "PHP Error: The Zlib module is required (configure PHP '--with-zlib-dir=/usr')\n";
if (!extension_loaded('posix'))
$error[++$i] = "PHP Error: The POSIX module is required (configure PHP '--enable-posix')\n";
if (!extension_loaded('pcntl'))
$error[++$i] = "PHP Error: The Process Control module is required (configure PHP '--enable-pcntl')\n";
if (!ini_get("register_argc_argv"))
$error[++$i] = "Variable register_argc_argv must be set to On inside php.ini\n";
// Connect to the database
@mysql_connect($cfg['dbhost'], $cfg['dbuser'], $cfg['dbpass']);
@mysql_select_db($cfg['dbname']);
if (isset($error)) {
webcp_log(0,0,'webcp.php',implode("\n",$error),0,1);
exit;
}
// Run from root only
if (posix_getuid() != 0) {
webcp_log(0,0,'webcp.php',"This program must be run as root.",0,1);
exit;
}
// Check if we are run interactively or with the -d switch; keep on going or become a daemon
$args = trim(next($HTTP_SERVER_VARS["argv"]));
if ($args == '-d' OR $args == '--daemon') {
// Fork webcp.php
$pid = pcntl_fork();
$echo = 0;
if ($pid == -1) {
webcp_log(0,0,'webcp.php',"Error: could not fork.",0,1);
exit;
} else if ($pid) {
return 0; // we are the parent, return '0' on success
}
// detach from the controlling terminal
if (!posix_setsid()) {
webcp_log(0,0,'webcp.php',"Could not detach from terminal",0,1);
exit;
} else
webcp_log(2,0,'webcp.php',"The web://cp daemon has been started.",0,1);
}
else {
$echo = 1;
webcp_log(3,0,'webcp.php',"web://cp ".$cfg['webcp']." daemon usage:
webcp.php (run interactively)
webcp.php -i (run interactively)
webcp.php -d, --daemon (run as daemon)
webcp.php -h, --help (display this help notice)
",0,1);
if ($args == '-h' OR $args == '--help')
exit;
else {
webcp_log(3,0,'webcp.php',"\n\n Running web://cp ".$cfg['webcp']." interactively\n use ./webcp.php -h for help, CTRL-C to exit\n==================================================\n\n",0,1);
}
}
unset($args);
// Log Process ID
$fp = fopen($cfg['pid'],"w+");
fwrite($fp,getmypid());
fflush($fp);
fclose($fp);
// Indicate the backend is up
touch($cfg['basedir'].$cfg['statustag']);
// Initialize vars
$remapmail = FALSE;
$restarthttp = FALSE;
$restartmail = FALSE;
$restartdns = FALSE;
$domainchange = FALSE;
$userchange = FALSE;
$endlessloop = 1;
// Set no timeout (unlimited)
set_time_limit(0);
// Start endless loop
while ($endlessloop++) {
// check cron.php zombies child process
if (file_exists($cfg['pid'].'-cron')) {
$tmp = (int)trim(implode("",file($cfg['pid'].'-cron')));
$f_pid = pcntl_waitpid ($tmp, $f_stat, WNOHANG);
$f_pid = pcntl_waitpid (0, $f_stat, WNOHANG);
}
// every 60 seconds, fork cron.php (webcp's scheduler)
if (!($endlessloop % 60)) {
webcp_log(3,0,'webcp.php',"Note: Forking cron.php",0,$echo);
$pid = pcntl_fork();
if ($pid == -1) {
webcp_log(0,0,"webcp.php","Fork Error: could not fork webcp.php for cron.php",0,$echo);
} elseif (!$pid) {
// Log Process ID
$fp = fopen($cfg['pid'].'-cron',"w+");
$mypid = getmypid();
fwrite($fp,$mypid);
fflush($fp);
fclose($fp);
// Set no timeout (unlimited)
set_time_limit(0);
// include cron script and stay there
include("cron.php");
exit;
}
}
// Check if there is a server tag set
$tag = commit('read');
// Remove flag right away to make room.
commit('remove');
if ($tag) {
// Scan database if flag is stop,start,restart,scan
switch($tag['action'][0]) {
// change configuration settings
case 'config':
parse_str(trim($tag['action'][1]), $config);
foreach($config AS $key => $val) {
// check and see if config is allowed to be changed
if (preg_match('/^('.$cfg['allowed_cfg'].')$/', $key)) {
// it is not already set to the same value.
if (!isset($cfg[$key]) || (isset($cfg[$key]) && $val != $cfg[$key])) {
// and don't quote number or boolian values
if (is_numeric($val) || $val == 'true' || $val == 'false') {
if (!file_edit('/\$cfg\[[\'"]'.$key.'["\']\].*?;/', '$cfg[\''.$key.'\'] = '.$val.';', $cfg['basedir'].'/config.php')) {
file_edit("/\n\?>/", '', $cfg['basedir'].'/config.php');
file_append("\$cfg['".$key."'] = ".$val.";\n?>", $cfg['basedir'].'/config.php');
}
} else {
if (!file_edit('/\$cfg\[[\'"]'.$key.'["\']\].*?;/', '$cfg[\''.$key.'\'] = \''.$val.'\';', $cfg['basedir'].'/config.php')) {
file_edit("/\n\?>/", '', $cfg['basedir'].'/config.php');
file_append("\$cfg['".$key."'] = '".$val."';\n?>", $cfg['basedir'].'/config.php');
}
}
}
}
}
// reload webcp http server
if ($cfg['httpd_mode'] = 'webcp') {
if (file_exists($cfg['httpd_pid']))
posix_kill(trim(implode('', file($cfg['httpd_pid']))), 1);
}
break;
// create initial config.php
case 'setup':
// don't overwrite someone's custom settings
if (!file_exists($cfg['basedir'].'/config.php')) {
// write small header
$fp = fopen($cfg['basedir'].'/config.php',"w");
fwrite($fp, '<'.'?'."\n");
fwrite($fp, "/**********************************************\n");
fwrite($fp, " * Check web/config.inc.php for all settings. *\n");
fwrite($fp, " **********************************************/\n\n");
// split up single config line and enter each setting into the config.php file
parse_str(trim($tag['action'][1]), $config);
foreach($config AS $key => $val) {
if (preg_match('/^('.$cfg['allowed_cfg'].')$/', $key)) {
if (is_numeric($val) || $val == 'true' || $val == 'false') {
// don't quote number or boolian values
fwrite($fp, "\$cfg['".$key."'] = ".$val.";\n");
} else {
fwrite($fp, "\$cfg['".$key."'] = '".$val."';\n");
}
}
}
fwrite($fp, '?'.'>');
fclose($fp);
// change config.php to be group readable by webcp-httpd
if ($user = posix_getpwnam($cfg['httpd_user'])) {
chgrp($cfg['basedir'].'/config.php', $user['gid']);
chmod($cfg['basedir'].'/config.php', 0640);
}
}
// load the newly created settings
include($cfg['basedir'].'/config.php');
// try to create and propagate the database
if (@mysql_connect($cfg['dbhost'], $cfg['dbuser'], $cfg['dbpass'])) {
if (!@mysql_select_db($cfg['dbname'])) {
// database is missing create it
mysql_query("CREATE DATABASE $cfg[dbname]");
}
$dbp = mysql_query("SHOW TABLES FROM $cfg[dbname]");
if (@mysql_num_rows($dbp) === 0) {
// database is empty, fill it. TODO: maybe we shouldn't rely on mysql
empty($cfg['dbpass']) ? $tmp = '' : $tmp = "-p$cfg[dbpass]";
exec($cfg['prog']['mysql']." -s -u $cfg[dbuser] $tmp $cfg[dbname] < $cfg[basedir]/webcp.sql");
exec($cfg['prog']['mysql']." -s -u $cfg[dbuser] $tmp $cfg[dbname] < $cfg[basedir]/help-$cfg[defaultlang].sql");
}
} else {
webcp_log(0,0,'webcp.php',"Error: Could not connect to database server",0,$echo);
}
// reload webcp http server
if ($cfg['httpd_mode'] = 'webcp') {
if (file_exists($cfg['httpd_pid']))
posix_kill(trim(implode('', file($cfg['httpd_pid']))), 1);
}
break;
// System Reboot
case 'reboot':
exec($cfg['prog']['shut']." -r now");
// Shut webcp.php gracefully: remove pid file and die
unlink($cfg['pid']);
exit;
// stop/start/restart program or network
case 'start':
case 'stop':
case 'restart':
// more sysv init stuff here.
webcp_log(3,0,'webcp.php',$tag['action'][0]." ".$tag['action'][1],0,$echo);
exec($cfg['init'][$tag['action'][1]]." ".$tag['action'][0]);
// Scan databases users and domains for changes to apply
case 'scan':
// Connect to database
if (!@mysql_connect($cfg['dbhost'], $cfg['dbuser'], $cfg['dbpass'])) {
webcp_log(0,0,'webcp.php',"Error: Could not connect to database server",0,$echo);
break;
}
else {
// Select correct database and go on.
if (!@mysql_select_db($cfg['dbname'])) {
webcp_log(0,0,'webcp.php',"Error: Could not select database",0,$echo);
exit;
}
// Begin loop on the db check to insure we don't get into a race situation
do {
// Check Domains Table
$dbp = mysql_query("SELECT id FROM domains WHERE action != ''");
while ($data = mysql_fetch_array($dbp)) {
$tmp = fetchdata("*","domain",$data['id']);
// seal check (data integrity)
$hash = $tmp;
$hash['hash'] = $hash['time'] = $hash['ucount'] = '';
$hash = crypt(md5(serialize($hash)), $cfg['key']);
if ($hash == $tmp['hash']) {
webcp("domain",$tmp);
// interactive dump
webcp_log(3,0,'webcp.php',"Domain ".$tmp['id']." ".$tmp['action'],0,$echo);
// Signal a domain change
$domainchange = TRUE;
}
else {
// suspend domain
// $tmp['action'] = 'suspend';
webcp("domain",$tmp);
webcp_log(1,$tmp['id'],"system","Invalid Domain Seal -- Domain $tmp[id] $tmp[action]",0, $echo);
// Signal a domain change
$domainchange = TRUE;
}
}
// Check Users Table
$dbp = mysql_query("SELECT username FROM users WHERE action != ''");
while ($data = mysql_fetch_array($dbp)) {
$tmp = fetchdata("*","user",$data['username']);
// seal check (data integrity)
$hash = $tmp;
$hash['hash'] = $hash['time'] = $hash['ucount'] = '';
$hash = crypt(md5(serialize($hash)), $cfg['key']);
if ($hash == $tmp['hash']) {
webcp("user",$tmp);
// interactive dump
webcp_log(3,0,'webcp.php',"User ".$tmp['username']." ".$tmp['action'],0,$echo);
// Signal a user change
$userchange = TRUE;
}
else {
// suspend user
// $tmp['action'] = 'suspend';
webcp("user",$tmp);
webcp_log(0,$tmp['id'],"system","Invalid User Seal -- User $tmp[username] $tmp[action]",0,$echo);
// Signal a user change
$userchange = TRUE;
}
}
// Check Mailman Table
if ($cfg['maillist'] == 'mailman') {
$dbp = mysql_query('SELECT id, domain, listname, admin, DECODE(password, "'.$cfg['key'].'") AS password, action FROM mailman WHERE action != ""');
while ($data = mysql_fetch_assoc($dbp)) {
if($data['action'] == 'c') {
if(strlen($data['password'])<3) {
mt_srand(time());
$data['password'] = mt_rand(111111, 999999999);
}
exec($cfg['prog']['mm_new'].' "'.$data['listname'].'" "'.$data['admin'].'" "'.$data['password'].'"');
mysql_query('UPDATE mailman SET action = "" WHERE id = "'.$data['id'].'"');
// interactive dump
webcp_log(3,0,'webcp.php',"Mailman list ".$data['listname']." created",0,$echo);
$userchange = TRUE;
}
elseif($data['action'] == 'r') {
exec($cfg['prog']['mm_rm'].' '.$data['listname']);
mysql_query('DELETE FROM mailman WHERE id = '.$data['id']);
// interactive dump
webcp_log(3,0,'webcp.php',"Mailman list ".$data['listname']." removed",0,$echo);
$userchange = TRUE;
}
}
}
// Fetch new db info and see if we're still looping
$dbp = mysql_query("SELECT id FROM domains WHERE action != ''");
$dbp1 = mysql_query("SELECT username FROM users WHERE action != ''");
if (mysql_num_rows($dbp) OR mysql_num_rows($dbp1)) $continueloop = true;
else $continueloop = false;
} while ($continueloop);
}
break;
case "backup":
$backupParms = explode("|", $tag['action'][1]);
$domains = explode("_", $backupParms[5]);
$pid = pcntl_fork();
webcp_log(3,0,'webcp.php',"Note: Forking backup.php",0,$echo);
if ($pid == -1) {
webcp_log(0,0,"webcp.php","Fork Error: could not fork webcp.php for backup.php",0,$echo);
} elseif (!$pid) {
// Log Process ID
$fp = fopen($cfg['pid'].'-backup',"w+");
$mypid = getmypid();
fwrite($fp,$mypid);
fflush($fp);
fclose($fp);
// Set no timeout (unlimited)
set_time_limit(0);
// include cron script and stay there
include("backup.php");
exit;
}
break;
case "restore":
$restoreFiles = explode("|", $tag['action'][1]);
$pid = pcntl_fork();
webcp_log(3,0,'webcp.php',"Note: Forking restore.php",0,$echo);
if ($pid == -1) {
webcp_log(0,0,"webcp.php","Fork Error: could not fork webcp.php for restore.php",0,$echo);
} elseif (!$pid) {
// Log Process ID
$fp = fopen($cfg['pid'].'-restore',"w+");
$mypid = getmypid();
fwrite($fp,$mypid);
fflush($fp);
fclose($fp);
// Set no timeout (unlimited)
set_time_limit(0);
// include cron script and stay there
include("restore.php");
exit;
}
break;
}
// See if we trapped any signal and update system configuration files
if ($domainchange) {
$domainchange = FALSE;
updatesys("domain");
}
if ($userchange) {
$userchange = FALSE;
updatesys("user");
}
// reload / restart services as appropriate
reload_services();
}
// Sleep for 1 second and loop!
sleep(1);
}
?>