+#!/usr/bin/env php
+<?php
+/*
+ * POPBeforeSMTPAuth
+ *
+ * Implements POP-before-SMTP authorization for Dovecot POP3 with Postfix.
+ *
+ * Version 1.3.2, October 27, 2012
+ * Copyright (c) 2012, Ron Guerin <ron@vnetworx.net>
+ *
+ * This script implements POP-before-SMTP authorization for Dovecot POP3
+ * with Postfix by reading the syslog file.
+ *
+ * Requires: PHP_PCRE, PHP_pcntl, POSIX
+ *
+ * Do not edit this script!  Edit /etc/${scriptname}.conf to change settings.
+ * where ${scriptname} is the name of this file.  Changes made to the script
+ * will get overwritten on upgrades.
+ *
+ * POP-Before-SMTP-Auth is Free Software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * POP-Before-SMTP-Auth is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * If you are not able to view the file COPYING, please write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * to get a copy of the GNU General Public License or to report a
+ * possible license violation.
+ *
+ * @package POPBeforeSMTPAuth
+ * @author Ron Guerin <ron@vnetworx.net>
+ * @license http://www.fsf.org/licenses/gpl.html GNU Public License
+ * @copyright Copyright © 2012 Ron Guerin
+ * @filesourc
+ * @link http://gothamcode.com/pop-before-smtp-auth POPBeforeSMTPAuth
+ * @version 1.3.2
+ *
+ */
+
+# Configuration variables:
+# pidpath, hostname, maillog, authperiod, checkdelay, logignores,
+# popserver, popservice, postmap, postinstance, posthashfile, debug,
+# daemonize, regex, regexstamp, regexuser, regexip, silentmax
+#
+# To change, edit /etc/${scriptname}.conf:
+# instance = postfix-instance
+# maillog = /var/log/mymaillog
+
+define('VERSION', '1.3.2');
+
+// No, PHP Maintainers, it *is* safe to rely on the system timezone setting.
+@date_default_timezone_set(@date_default_timezone_get());
+
+// Settings
+$confvars = array('debug' => NULL, 'myname' => NULL, 'args' => array(),
+       'myconfigname' => NULL, 'mydirs' => NULL, 'config' => NULL,
+       'error' => NULL, 'syslog' => TRUE,
+       'pidpath' => NULL, 'stdin' => NULL, 'stdout' => NULL, 'stderr' => NULL,
+       'hostname' => NULL, 'maillog' => NULL, 'authperiod' => NULL,
+       'checkdelay' => NULL, 'popserver' => NULL, 'popservice' => NULL,
+       'postmap' => NULL, 'postinstance' => NULL, 'posthashfile' => NULL,
+       'logignores' => NULL, 'daemonize' => NULL, 'regex' => NULL,
+       'regexstamp' => NULL, 'regexuser' => NULL, 'regexip' => NULL,
+       'silentmax' => NULL);
+$confvars['myname'] = preg_replace(chr(7).'.php$'.chr(7), '', basename($argv[0])); // Get my name, minus any .php extension
+
+$init = init_settings($confvars, $argv); // Get settings from defaults, config file, and commandline
+
+if ($confvars['syslog']) openlog($confvars['myname'], LOG_PID, LOG_MAIL); // Open syslog
+// Enable garbage collection (this means PHP 5.3+ now)
+gc_enable(); // Collect garbage every 60 cycles below
+
+$msg = 'Using '.$confvars['config'].' and watching log for '.$confvars['postinstance'];
+if ($confvars['syslog']) syslog(LOG_INFO, $msg);
+if ($init === FALSE) {
+       if ($confvars['error'][0] == CANT_OPEN_CONF) {
+               $retcode = CANT_OPEN_CONF;
+               log_msg($confvars, 'Error: '.((array_key_exists('myconfigname', $confvars) && $confvars['myconfigname'])
+                       ? $confvars['myconfigname'] : $confvars['myname']).'.conf not found. Can\'t continue. Terminating', TRUE);
+       }
+       else {
+               foreach ($confvars['error'] as $msg) log_msg($confvars, $msg, TRUE);
+               $retcode = BAD_CONFIG;
+       }
+       if ($confvars['syslog']) closelog();
+       die((int) $retcode);
+}
+
+// Daemonize, handle TERM and HUP signals
+if ($confvars['daemonize']) $lock = daemonize($confvars['myname'], $confvars['syslog'], $confvars['pidpath'], $confvars['stdin'], $confvars['stdout'], $confvars['stderr']);
+pcntl_signal(SIGTERM, 'sig_handler');
+pcntl_signal(SIGHUP,  'sig_handler');
+
+// Now get about the point of all this...
+
+$iptable=array();
+$update = FALSE;
+
+$msg = 'POP-Before-SMTP-Auth Version '.VERSION.' starting.';
+if ($confvars['debug']) $msg .= ' postmap: '.$confvars['postmap'].' '.$confvars['posthashfile'].' mem:'.memory_get_usage().' max:'.memory_get_peak_usage();
+if ($confvars['syslog']) syslog(LOG_MAIL|LOG_INFO, $msg);
+
+init_posthashfile($confvars['postmap'], $confvars['postinstance'], $confvars['posthashfile']);
+
+$cycle = 0; $lastsize = 0; $waiting = 0;
+
+while (TRUE) {
+       pcntl_signal_dispatch();
+       if (! file_exists($confvars['maillog'])) {
+               $waiting = $waiting + $confvars['checkdelay'];
+               if ($waiting > $confvars['silentmax']) {
+                       $msg = 'WARNING: Operation suspended. '.$confvars['maillog'].' hasn\'t existed for at least '.$confvars['silentmax'].' seconds.';
+                       if ($confvars['syslog']) syslog(LOG_MAIL|LOG_INFO, $msg);
+               }
+               sleep($confvars['checkdelay']);  // wait for it
+               continue;
+       }
+       if ($waiting > $confvars['silentmax']) {
+                       $msg = 'NOTICE: '.$confvars['maillog'].' finally exists.  Resuming normal operation.';
+                       if ($confvars['syslog']) syslog(LOG_MAIL|LOG_INFO, $msg);
+       }
+       $waiting = 0;
+
+       $data = '';
+       clearstatcache();
+       $cursize = filesize($confvars['maillog']);
+       if ($cursize != $lastsize) {
+               $fd = fopen($confvars['maillog'], 'r');
+               // Better, I think, to seek forward than back, for accuracy sake
+               //fseek($fd, ($lastsize - $cursize) - 1, SEEK_END);
+               fseek($fd, $lastsize, SEEK_SET);
+               while((! feof($fd)) && ($lastsize > 0)) $data .= fgets($fd, 1024);
+               fclose($fd);
+               $lastsize = $cursize;
+       }
+
+       // Look for all matches in this chunk of data
+       if (preg_match_all(chr(7).$confvars['regex'].chr(7), $data, $matches) != FALSE) {
+               // loop through matches
+               foreach ($matches[$confvars['regexip']] as $key => $ip) {
+                       // Ignore POP3 mail pickups from Gmail, these don't need relay access
+                       $dnsname = dnsname($ip);
+                       if (! preg_match('/mail-.*\.google\.com/', $dnsname)) {
+                               if (! isset($iptable[$ip])) {
+                                       $update = TRUE;
+                                       $msg = 'Relaying for: ['.$ip.'] '.$dnsname;
+                                       //echo date('M j h:i:s ').'Relaying for: ['.$ip.'] '.$dnsname.' on '.date('r', strtotime($matches[$confvars['regexstamp']][$key]))."\n";
+                                       if ($confvars['debug']) $msg .= ' mem:'.memory_get_usage().' max:'.memory_get_peak_usage();
+                                       if ($confvars['syslog']) syslog(LOG_MAIL|LOG_INFO, $msg);
+                               }
+                               $iptable[$ip]=strtotime($matches[1][$key]) + $confvars['authperiod']; // last auth + authperiod = time auth expires
+                       }
+                       else {
+                               if ($confvars['logignores']) {
+                                       $msg = 'Ignoring mail collector: '.$dnsname;
+                                       if ($confvars['debug']) $msg .= ' mem:'.memory_get_usage().' max:'.memory_get_peak_usage();
+                                       if ($confvars['syslog']) syslog(LOG_MAIL|LOG_INFO, $msg);
+                               }
+                       }
+               }
+       }
+
+       // If something expired, set update flag
+       foreach ($iptable as $ip => $authtime) {
+               if ($authtime < time()) {
+                       $msg = 'Ending relay for: ['.$ip.']';
+                       if ($confvars['debug']) $msg .= ' mem:'.memory_get_usage().' max:'.memory_get_peak_usage();
+                       if ($confvars['syslog']) syslog(LOG_MAIL|LOG_INFO, $msg);
+                       //echo date('M j h:i:s ').'Ending relay for: '.$ip.' on '.date('r', $authtime)."\n";
+                       unset($iptable[$ip]);
+                       $update = TRUE;
+               }
+       }
+
+       if ($update) {
+               if (! $handle = fopen($confvars['posthashfile'].'.new', 'w')) {
+                       $msg = 'Cannot open file: '.$confvars['posthashfile'].'.new';
+                       if ($confvars['syslog']) {
+                               syslog(LOG_MAIL|LOG_INFO, $msg);
+                               closelog();
+                       }
+                       die(date('M j h:i:s ').$msg."\n");
+               }
+
+               foreach ($iptable as $ip => $authtime) {
+                       if (fwrite($handle, $ip." OK\n") === FALSE) {
+                               $msg = 'Cannot write to file: '.$confvars['posthashfile'].'.new';
+                               if ($confvars['syslog']) {
+                                       syslog(LOG_MAIL|LOG_INFO, $msg);
+                                       closelog();
+                               }
+                               die(date('M j h:i:s ').$msg."\n");
+                       }
+               }
+               fclose($handle);
+               postmap($confvars['postmap'], $confvars['postinstance'], $confvars['posthashfile']);
+               $update = FALSE;
+       }
+
+       l
+
+       if (++$cycle == 60) {
+               $cycle = 0;
+               gc_collect_cycles();
+       }
+
+       sleep ($confvars['checkdelay']);
+}
+// We can never get here because the while loop never ends
+
+/* *********************************************************************** */
+
+function sig_handler($signo){
+       global $confvars, $argv;
+
+       switch ($signo) {
+       case SIGTERM:
+               // handle shutdown tasks
+               $msg = 'Clearing IPs and shutting down.';
+               if ($confvars['debug']) $msg .= ' mem:'.memory_get_usage().' max:'.memory_get_peak_usage();
+               if ($confvars['syslog']) syslog(LOG_MAIL|LOG_INFO, $msg);
+               init_posthashfile($confvars['postmap'], $confvars['postinstance'], $confvars['posthashfile']);
+               if ($confvars['syslog']) closelog();
+               exit;
+               break;
+       case SIGHUP:
+               // handle reload tasks
+               $msg = 'Reloading configuration settings from config file and command-line.';
+               if ($confvars['debug']) $msg .= ' mem:'.memory_get_usage().' max:'.memory_get_peak_usage();
+               if ($confvars['syslog']) syslog(LOG_MAIL|LOG_INFO, $msg);
+               $tempvars = $confvars;
+               $init = init_settings($tempvars, $argv);
+               $msg = 'Reloading from '.$tempvars['config'].'.';
+               if ($confvars['syslog']) syslog(LOG_INFO, $msg);
+               if ($init === FALSE) {
+                       $msg = 'Errors reloading from '.$tempvars['config'];
+                       if ($confvars['syslog']) syslog(LOG_MAIL|LOG_INFO, $msg);
+                       fwrite($confvars['stderr'], date('M j h:i:s ').$msg."\n");
+                       foreach ($tempvars['error'] as $msg) {
+                               if ($confvars['syslog']) syslog(LOG_MAIL|LOG_INFO, $msg);
+                               fwrite($confvars['stderr'], date('M j h:i:s ').$msg."\n");
+                               #echo date('M j h:i:s ').$msg."\n";
+                       }
+               }
+               else $confvars = $tempvars;
+               break;
+       }
+}
+
+function init_posthashfile($postmap, $postinstance, $posthashfile) {
+       if (file_exists($posthashfile.'.new')) {
+               if (! unlink($posthashfile.'.new')) if ($confvars['syslog']) syslog(LOG_MAIL|LOG_INFO, 'ERROR: Problem unlinking '.$posthashfile.'.new');
+       }
+       if (! touch($posthashfile.'.new')) if ($confvars['syslog']) syslog(LOG_MAIL|LOG_INFO, 'ERROR: Problem touching new '.$posthashfile);
+       postmap($postmap, $postinstance, $posthashfile);
+}
+
+function postmap($postmap, $postinstance, $posthashfile) {
+       if (file_exists($posthashfile.'.new')) {
+               exec($postmap.' -c '.$postinstance.' '.$posthashfile.'.new', $out);
+               if ($out[0]) if ($confvars['syslog']) syslog(LOG_MAIL|LOG_INFO, 'ERROR: Problem ('.explode($out).') running postmap on '.$posthashfile.'.new for '.$postinstance);
+               if (! rename($posthashfile.'.new', $posthashfile)) if ($confvars['syslog']) syslog(LOG_MAIL|LOG_INFO, 'ERROR: Problem renaming '.$posthashfile.'.new');
+               if (! rename($posthashfile.'.new.db', $posthashfile.'.db')) if ($confvars['syslog']) syslog(LOG_MAIL|LOG_INFO, 'ERROR: Problem renaming '.$posthashfile.'.new.db');
+       }
+}
+
+function dnsname($ip){
+       $ptr= implode('.',array_reverse(explode('.',$ip))).'.in-addr.arpa';
+       $host = dns_get_record($ptr,DNS_PTR);
+       if ($host == null) return FALSE;
+       else return $host[0]['target'];
+}
+
+function init_settings(&$confvars, $argv) {
+       // This function is generic and passes data via $confvars
+       $confvars['error'] = NULL; // We turn this into an array to pass data back
+       $cmdline = TRUE;
+
+       // Parse out any command-line args first, skipping argv[0]
+       $cmdlinevars = array();
+       $cmdlineargs = array();
+       $optsdone = FALSE;
+       $skip = TRUE;
+       foreach ($argv as $argnum => $arg) {
+               if ($skip) $skip = FALSE;
+               else {
+                       if ($arg == '--') $optsdone = TRUE;
+                       if ((substr($arg, 0, 2) == '--') && (! $optsdone)) {
+                               $sep = strpos($arg,'=');
+                               if ($sep !== FALSE) { // This is set to a value
+                                       $var = trim(substr($arg, 2, $sep - 2));
+                                       $val = trim(substr($arg, $sep + 1));
+                               }
+                               else { // This is an implicit setting of TRUE
+                                       $var = trim(substr($arg, 2));
+                                       $val = TRUE;
+                               }
+
+                               if (array_key_exists($var, $confvars)) {
+                                       if ($val == '') $val = NULL;
+                                       if (strtoupper($val) == 'NULL') $val = NULL;
+                                       if (strtoupper($val) == 'TRUE') $val = TRUE;
+                                       if (strtoupper($val) == 'FALSE') $val = FALSE;
+                                       if (is_array($confvars[$var])) {
+                                               $cmdlinevars["$var"][] = $val;
+                                       }
+                                       else {
+                                               $cmdlinevars["$var"] = $val;
+                                       }
+                               }
+                               else {
+                                       $confvars['error'][] = "Unknown command-line option --$var";
+                                       $cmdline = FALSE;
+                               }
+                       }
+                       elseif ((substr($arg, 0, 1) == '-') && (strlen($arg) == 2) && (! $optsdone)) {
+                               // Try to parse single letter, single dash options
+                               if (in_array(substr($arg, 1), $confvars)) { // Does not accept value
+                                       $cmdlinevars["$var"] = TRUE;
+                               }
+                               elseif (array_key_exists(substr($arg, 1).':', $confvars)) { // Required value
+                                       if (($argnum + 1 >= $argc) || (substr($argv[$argnum + 1], 0, 1) == '-')) {
+                                               $confvars['error'][] = "Required value for $arg not given.";
+                                               $cmdline = FALSE;
+                                       }
+                                       else {
+                                               $var = $confvars[substr($arg, 1).':'];
+                                               $val = trim($argv[$argnum + 1]);
+                                               $skip = TRUE;
+                                               $cmdlinevars["$var"] = $val;
+                                       }
+                               }
+                               elseif (array_key_exists(substr($arg, 1).'::', $confvars)) { // Optional value
+                                       if (($argnum + 1 < $argc) && (substr($argv[$argnum + 1], 0, 1) != '-')) {
+                                               $var = $confvars[substr($arg, 1).'::'];
+                                               $val = trim($argv[$argnum + 1]);
+                                               $skip = TRUE;
+                                               $cmdlinevars["$var"] = $val;
+                                       }
+                                       else {
+                                               $var = $confvars[substr($arg, 1).'::'];
+                                               $cmdlinevars["$var"] = TRUE;
+                                       }
+                               }
+                               else {
+                                       $confvars['error'][] = "Unknown command-line option -$var";
+                                       $cmdline = FALSE;
+                               }
+                       }
+                       else { // Else it's a command argument
+                               $cmdlineargs[]=$arg;
+                       }
+               }
+       }
+       $confvars['arguments'] = $cmdlineargs;
+
+       // Set configuration settings with settings from config file.
+       // Get config file name from command line or look for it in the file system.
+       if (array_key_exists('config', $cmdlinevars)) {
+               if (file_exists($cmdlinevars('config'))) $config = $cmdlinevars('config');
+               else $config = FALSE;
+       }
+       else {
+               $config = config_location($confvars); // Returns config file pathname or FALSE
+       }
+       if ($config) {
+               $confvars['config'] = $config;
+               $lines = file($config);
+               foreach ($lines as $key => $line) {
+                       $line=trim($line);
+                       // Ignore blank lines and lines beginning with #, //, or ;
+                       if (($line != '') && (substr($line, 0, 1) != '#') && (substr($line, 0, 2) != '//') && (substr($line, 0, 1) != ';')) {
+                               $sep = strpos($line, '=');
+                               if ($sep === FALSE) { // This is an implicit setting of TRUE
+                                       $var = $line;
+                                       $val = TRUE;
+                               }
+                               else { // This is set to a value
+                                       $var = trim(substr($line, 0, $sep));
+                                       $val = trim(substr($line, $sep + 1));
+                               }
+                               if (array_key_exists($var, $confvars)) {
+                                       if ($val == '') $val = NULL;
+                                       if (strtoupper($val) == 'NULL') $val = NULL;
+                                       if (strtoupper($val) == 'TRUE') $val = TRUE;
+                                       if (strtoupper($val) == 'FALSE') $val = FALSE;
+
+                                       if (is_array($confvars[$var])) {
+                                               $confvars["$var"][] = $val;
+                                       }
+                                       else {
+                                               $confvars["$var"] = $val;
+                                       }
+                               }
+                               else {
+                                       $confvars['error'][] = "Unknown configuration file setting: $var";
+                                       $config = FALSE;
+                               }
+                       }
+               }
+       }
+       elseif ($confvars['mustconf']) { // If must have config file, return FALSE now
+               $confvars['error'] = array(CANT_OPEN_CONF);
+               return FALSE;
+       }
+
+       // Override all previous settings via any command-line args
+       $confvars = array_merge($confvars, $cmdlinevars);
+
+       // Certain things *have* to be set for this to work.  If they're not set by now,
+       // attempt to set them to reasonable defaults.  Return config file pathname
+       // or FALSE if anything goes wrong here or in set_defaults.
+       $defaults = init_settings_defaults($confvars);
+       // Resolve variables, ie: variable=$someothervariable
+       do {
+               $unresolved = FALSE;
+               foreach ($confvars as $key => $value) {
+                       if ((! is_array($value)) && (! is_resource($value))) {
+                               if (substr($value, 0, 1) == '$') {
+                                       $var = substr($value, 1);
+                                       if (array_key_exists($var, $confvars)) {
+                                               $confvars[$key] = $confvars[$var];
+                                               if (substr($confvars[$var], 0, 1) == '$') $unresolved = TRUE;
+                                       }
+                               }
+                       }
+               }
+       } while ($unresolved);
+       return (($defaults && $config && $cmdline) ? TRUE : FALSE);
+}
+
+function init_settings_defaults(&$confvars) {
+       // Application-specific code, returns FALSE on error and message in $confvars['error']
+       $return = TRUE;
+       if ((($confvars['debug'] == NULL) || (trim($confvars['debug']) == '')) && ($confvars['debug'] !== TRUE)) $confvars['debug'] = FALSE; // Don't print debug messages
+       if (($confvars['myconfigname'] == NULL) || (trim($confvars['myconfigname']) == '')) $confvars['myconfigname'] = FALSE; // Use default config file name
+       if (is_resource(STDIN)){ // STDIN will not be a resource if we've already closed it
+               // These can't be changed after daemonization.  Restart to change standard file handles.
+               $confvars['stdin'] = '/dev/null'; // Don't allow stdin to be pointed away from /dev/null
+               if (($confvars['stdout'] == NULL) || (trim($confvars['stdout']) == '')) $confvars['stdout'] = '/dev/null';
+               if (($confvars['stderr'] == NULL) || (trim($confvars['stderr']) == '')) $confvars['stderr'] = '/dev/null';
+       }
+       if ((($confvars['syslog'] == NULL) || (trim($confvars['syslog']) == '')) && ($confvars['syslog'] !== FALSE)) $confvars['syslog'] = TRUE; // Log to syslog
+       if (($confvars['pidpath'] == NULL) || (trim($confvars['pidpath']) == '')) $confvars['pidpath'] = '/var/run';
+       if (($confvars['hostname'] == NULL) || (trim($confvars['hostname']) == '')) $confvars['hostname'] = substr(php_uname('n'), 0, strpos(php_uname('n'), '.'));
+       if ((($confvars['daemonize'] == NULL) || (trim($confvars['daemonize']) == '')) && ($confvars['daemonize'] !== FALSE)) $confvars['daemonize'] = TRUE; // Daemonize when run
+       if (($confvars['popserver'] == NULL) || (trim($confvars['popserver']) == '')) $confvars['popserver'] = 'dovecot';
+       if (($confvars['popservice'] == NULL) || (trim($confvars['popservice']) == '')) $confvars['popservice'] = 'pop3-login';
+       if (($confvars['postinstance'] == NULL) || (trim($confvars['postinstance']) == '')) $confvars['postinstance'] = '/etc/postfix';
+       if (($confvars['postmap'] == NULL) || (trim($confvars['postmap']) == '')) $confvars['postmap'] = '/usr/sbin/postmap';
+       if (($confvars['maillog'] == NULL) || (trim($confvars['maillog']) == '')) $confvars['maillog'] = '/var/log/mail.log';
+       if (($confvars['posthashfile'] == NULL) || (trim($confvars['posthashfile']) == '')) $confvars['posthashfile'] = '/var/lib/pop-before-smtp-auth/hosts';
+       if (($confvars['authperiod'] == NULL) || (! is_numeric($confvars['authperiod']))) $confvars['authperiod'] = 1800; // 1800 = 30 minutes
+       if (($confvars['checkdelay'] == NULL) || (! is_numeric($confvars['checkdelay']))) $confvars['checkdelay'] = 5; // 5 seconds
+       if ((($confvars['logignores'] == NULL) || (trim($confvars['logignores']) == '')) && ($confvars['logignores'] !== FALSE)) $confvars['logignores'] = TRUE; // Log ignored mail collectors
+       if (($confvars['regex'] == NULL) || (trim($confvars['regex']) == '')) {
+               // Default regex
+               //Jan  3 16:08:23 hostname dovecot: pop3-login: Login: user=<user@example.com>, method=PLAIN, rip=192.168.1.201, lip=4.2.2.2, mpid=18943
+               $confvars['regex'] = '(.*) '.$confvars['hostname'].' '.$confvars['popserver'].': '.$confvars['popservice'].': Login: user=<(.*)>, method=.*, rip=(.*), lip';
+               $confvars['regexstamp'] = 1; $confvars['regexuser'] = 2; $confvars['regexip'] = 3;
+       }
+       else {
+               // User-provided regex
+               if ((! isset($confvars['regexstamp'])) || (trim($confvars['regexstamp']) == '') ||
+                       (! isset($confvars['regexuser'])) || (trim($confvars['regexuser']) == '') ||
+                       (! isset($confvars['regexip'])) || (trim($confvars['regexip']) == '')) {
+                       $confvars['error'][] = 'Custom regex specified, but required \'regexstamp\', or \'regexuser\', or \'regexip\' was not specified.';
+                       $return = FALSE;
+               }
+               // \\\$ = $ needs to be escaped with \, and so does the escape or PCRE won't see it
+               $confvars['regex'] = preg_replace('/\\\$HOSTNAME/', $confvars['hostname'], $confvars['regex']);
+               $confvars['regex'] = preg_replace('/\\\$POPSERVER/', $confvars['popserver'], $confvars['regex']);
+               $confvars['regex'] = preg_replace('/\\\$POPSERVICE/', $confvars['popservice'], $confvars['regex']);
+       }
+       if (($confvars['silentmax'] == NULL) || (! is_numeric($confvars['silentmax']))) $confvars['silentmax'] = 30; // 30 seconds
+       return $return;
+}
+
+function daemonize($myname, $syslog, $pidpath, &$stdin='/dev/null', &$stdout='/dev/null', &$stderr='/dev/null') {
+       // Returns file descriptors in place of file paths for $stdin, $stdout, $stderr
+
+       // Fork, become session leader, fork again, and close open handles so there's no zombie.
+       // Open lock file
+       $lock = fopen($pidpath.'/'.$myname.'.pid', 'c+');
+       if (! flock($lock, LOCK_EX | LOCK_NB)) {
+               $msg = $myname.' already running.';
+               if ($syslog) {
+                       syslog(LOG_MAIL|LOG_INFO, $msg);
+                       closelog();
+               }
+               die(date('M j h:i:s ').$msg."\n");
+       }
+
+       // Fork. If we get a PID, exit.  If we get 0, we're the child, continue
+       if (pcntl_fork()) exit();
+
+       // Dissociate from controlling terminal, become session leader
+       if (posix_setsid() === -1) {
+               $msg = $myname.' could not setsid.';
+               if ($syslog) {
+                       syslog(LOG_MAIL|LOG_INFO, $msg);
+                       closelog();
+               }
+               die(date('M j h:i:s ').$msg."\n");
+       }
+       usleep(100000); // sleep 1/10th of a second
+
+       // Fork again as session leader to be free of other processes. If pcntl_fork
+       // returns 0 we're the child, else we're the parent getting the PID of the child
+       $childpid = pcntl_fork();
+       if($childpid) {
+               // If we get here, we're the parent, write the child's PID to pidfile.
+               $msg = $myname.' daemonizing.';
+               if ($syslog) {
+                       syslog(LOG_MAIL|LOG_INFO, $msg);
+                       closelog();
+               }
+               echo date('M j h:i:s ').$msg."\n";
+               fseek($lock, 0);
+               ftruncate($lock, 0);
+               fwrite($lock, $childpid);
+               fflush($lock);
+               exit();
+       }
+    else {
+               // If we get here, we're the child, finally independent. Grab lockfile.
+               usleep(100000); // sleep 1/10th of a second
+               flock($lock, LOCK_EX | LOCK_NB);
+       }
+
+    // As we are a daemon, close standard file descriptors.
+       fclose(STDIN); fclose(STDOUT); fclose(STDERR);
+
+       // http://andytson.com/blog/2010/05/daemonising-a-php-cli-script-on-a-posix-system/
+       // When a standard file descriptor is closed, it can be replaced.
+       // Create new standard file descriptors in case anything tries to use them.
+       // Variable names are not important, but do not re-order the fopens
+       if (($stdout = '/dev/null') && ($stderr = '/dev/null')) {
+               $stderr = 'php://stdout'; // hack to duplicate fd1 to fd2
+       }
+       $stdin = fopen('/dev/null', 'r'); // set fd0
+       $stdout = fopen($stdout, 'w'); // set fd1
+       $stderr = fopen($stderr, 'w'); // set fd2 or set fd2 to fd1
+
+       // Ignore some signals we don't care about
+       pcntl_signal(SIGCHLD, SIG_IGN);
+       pcntl_signal(SIGTSTP, SIG_IGN);
+       pcntl_signal(SIGTTOU, SIG_IGN);
+       pcntl_signal(SIGTTIN, SIG_IGN);
+
+       return $lock;
+}
+
+function config_location($confvars) {
+       // Find config file, and return it.  Looks first in current directory,
+       // then iterates through array of standard directories.  For each
+       // standard directory, it first looks in the standard directory, then in
+       // whatever subdirectories are specified in array $confvars['mydirs'].
+       // Tries to detect Optware, searches /opt first when detected.
+
+       if ($confvars['myconfigname']) $myname = $confvars['myconfigname']; else $myname = $confvars['myname'];
+       $mydirs = $confvars['mydirs'];
+       $conf = './'.$myname.'.conf';
+
+       if (file_exists($conf)) return $conf;
+       $prefixes = file_exists('/opt/bin/ipkg-opt') ? array('/opt', '') : array(''); # This is a crude test for Optware
+       if ((! is_array($mydirs)) && (($mydirs == NULL) || (trim($mydirs) == ''))) $mydirs = array();
+       $confdirs = array_merge((array)'', $mydirs); // '' == current directory
+       $stddirs = array('/usr/local/etc', '/etc');
+
+       foreach ($prefixes as $prefix) {
+               foreach ($stddirs as $base) {
+                       foreach ($confdirs as $dir) {
+                               if ($dir != '') $dir = $dir.'/';
+                               $conf = $prefix.$base.'/'.$dir.$myname.'.conf';
+                               if (file_exists($conf)) return $conf;
+                       }
+               }
+       }
+       return FALSE;
+}
+
+function log_msgs($vars, $msgs, $stderr=FALSE) {
+       foreach ($msgs as $msg) log_msg($vars, $msg, $stderr);
+}
+
+function log_msg($vars, $msg, $stderr=FALSE) {
+       if ($vars['debug']) $msg .= ' mem: '.memory_get_usage().' max: '.memory_get_peak_usage();
+       if ($vars['syslog']) syslog($vars['sl_prio'], $msg);
+       if (array_key_exists('daemonize', $vars) && (! $vars['daemonize']))echo date('M j h:i:s ').$msg."\n";
+       elseif ($stderr === TRUE) {
+               fwrite($vars['stderr'], date('M j h:i:s ').$msg."\n");
+       }
+}
+?>