From: Gitolite Admin Date: Sat, 8 Feb 2014 06:56:18 +0000 (-0500) Subject: Initial push of current release X-Git-Url: https://git.gothamcode.com/?a=commitdiff_plain;p=pop-before-smtp-auth.git Initial push of current release --- 377ace2c34f607ea9341ffa04906aa62d9348587 diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..7959905 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,47 @@ +v1.4 December XX, 2012 +* New config file locating method. Attempts to detect Optware. +* Fix: Bug introduced in 1.3.1 caused HUP not to use command-line vars when + reloading config variables. +* Re-worked command-line/config parser in anticipation of 'bare options'. +* New command-line option '--config' to specify the config file. +* Finally added section on configuring Postfix to use POP-Before-SMTP-Auth. +* Logging to syslog now optional. +* Config parser now parses entire config file instead of terminating at the + first error. This allows you to see all config errors at once. +* HUP now parses config and only reloads if there are no errors. +* Includes a manpage + +v1.3.1 September 21, 2012 +* Internal code changes. Switched to single-quoting. +* Fixed some config variable bugs that probably made 3.0 unusable for some. + +v1.3 September 21, 2012 +* tailfile class and file (tailfile.phpc) have been eliminated, making + pop-before-smtp-auth a self-contained script with fewer total lines. +* Generates a message to syslog if the monitored log file disappears + for more than a configurable amount of seconds. +* Various internal changes that didn't change functionality available, + but cleaned up the code a bit. + +v1.2 September 15, 2012 +* Use #!/usr/bin/env php for better portability +* Use pcntl-signal-dispatch instead of ticks. +* Now requires PHP 5.3 as a result. +* New configuration setting 'logignores' (default TRUE) determines whether + messages about mail collectors get logged. +* Fixed some error messages that weren't being sent to syslog. +* Daemonization config setting (default TRUE) determines whether or not + pop-before-smtp-auth daemonizes. +* The regex to match log lines can now be changed by setting the + 'regex', 'regexstamp', 'regexuser', and 'regexip" configuration settings. + +v1.1 January 22, 2012 +* Fixed up init scripts a bit +* Changed to ignore Gmail mail collectors +* Log failure if tailfile class file can't be found +* Makes own PID file, +* Daemonized +* De-PHPified the config file. + +v1.0 January 4, 2012 +Initial Version diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..60b0137 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program 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. + + This program 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..d496370 --- /dev/null +++ b/INSTALL @@ -0,0 +1 @@ +See README for all instructions available at this time. diff --git a/README b/README new file mode 100644 index 0000000..3b3fade --- /dev/null +++ b/README @@ -0,0 +1,139 @@ +POP-Before-SMTP-Auth v.1.3.2 October 27, 2012 +(c) 2012 Ron Guerin +Licensed under GPL2 or later. +Requires PHP-CLI 5.3 or greater with pcntl, PCRE and POSIX. + +This code is unsupported, though you can try mailing the list and if I can, +and I have time, I may try to help. You have to join the list to post to it. + +Mailing List: http://lists.gothamcode.com/listinfo/gothamcode + +This script needs to be run as root to read log files and run postmap + +Installing +---------- +1. Copy the script pop-before-smtp-auth to /usr/local/sbin +2. Copy the manpage pop-before-smtp-auth.8.gz to /usr/local/man/man8 +3. Create the config file /etc/pop-before-smtp-auth.conf + Put your settings in this file rather than altering the script. + +Configuring Postfix +------------------- +Postfix's main.cf needs to be modified to include: + check_client_access hash:${posthashfile} + +where you'd substitute something like /var/lib/pop-before-smtp-auth/hosts +for ${posthashfile}. This needs to go under smtp_recipient_restrictions, +perhaps immediately follwing permit_sasl_authenticated . + + +Settings +-------- +Do not edit the script. Create a /etc/pop-before-smtp-auth.conf file +to change settings, or set them via the command-line. + +Setting Default +------------ ------------------------------------------------------------- +config = ./${scriptname}.conf, /etc/${scriptname}.conf, + or /etc/local/${scriptname}.conf (in that order) + If Optware is detected, these will be prefixed by /opt and + checked first. (see Config Search below) +pidpath = /var/run (where to put the pidfile) +hostname = (default: output of hostname --short) +maillog = /var/log/mail.log (log file to monitor) +authperiod = 30 (minutes to allow an IP address to relay) +checkdelay = 5 (seconds, length of time between checking for changes) +popserver = dovecot +popservice = pop3-login +postmap = postmap +postinstance = /etc/postfix (the instance of Postfix to work with) +posthashfile = /var/lib/${scriptname}/hosts +debug = FALSE +daemonize = TRUE (run as a daemon) +logignores = TRUE (log ignored POP mail collectors) +regex = (.*) $HOSTNAME $POPSERVER: $POPSERVICE: Login: user=<(.*)>, method=.*, rip=(.*), lip +regexstamp = 1 +regexuser = 2 +regexip = 3 +silentmax = 30 (seconds to wait for monitored file exist before warning) +stderr = /dev/null (where to point stderr) + +${scriptname} = whatever the name of the script is. Usually=pop-before-smtp-auth + +Command-line Options +-------------------- +All configuration settings are available as command-line options. Settings +made via the command-line override config file settings. + +For example, to use a config file with a non-standard name in a non-standard +location, use the option --config=/path/to/configfile.conf + +To override a config file setting with the default setting, set the option +on the command-line without a value. For example, if in your config file +you have set: authperiod=60 + +You can override it with its default like this: + --authperiod= +To override it with a specific value: + --authperiod=45 + +Options can be set to TRUE by either of the following: + --option=TRUE + --option +The form --option is an abbreviation for --option=TRUE + +Config Search +------------- +The config file is searched for in the following order: +1. If a config file is specified on the command-line, use that, + or terminate because it doesn't exist or it can't be read. +2. ./${scriptname}.conf +3. /opt/usr/local/etc/${scriptname}.conf +4. /opt/etc/${scriptname}.conf +5. /usr/local/etc/${scriptname}.conf +6. /etc/${scriptname}.conf + +Changing the log-line matching regular expression +------------------------------------------------- +You should be able to substitute any PCRE regex for your own, by defining +the configuration setting "regex". The following substitutions will be +made at runtime: + + * $HOSTNAME + * $POPSERVER + * $POPSERVICE + +These substitutions are made based on their corresponding configuation +variable values. (ie: $HOSTNAME = the hostname config value) + +The values 'regexstamp', 'regexuser', and 'regexip' indicate which sub-matches +in 'regex' contain the timestamp, user, and remote IP address, respectively. +These three values are required to be able to use a custom regex in 'regex'. + +Unless you change it, the following default regex will be used: +$HOSTNAME $POPSERVER: $POPSERVICE: Login: user=<(.*)>, method=.*, rip=(.*), lip + +With the default regex, the regexstamp is 1, regexuser is 2, +and regexip is 3. + +Specifying regex changes on the command line is possible as with any other +configuration setting, but you may find it difficult to properly escape +everything. + + +Init Scripts +------------ + Debian/Ubuntu/et al: + -------------------- + Copy: pop-before-smtp-auth.init-debian to /etc/init.d/pop-before-smtp-auth + Run: update-rc.d pop-before-smtp-auth start 20 2 3 4 5 . stop 20 0 1 6 . + + RedHat/Fedora/CentOS/et al: + --------------------------- + Copy: pop-before-smtp-auth.init-fedora to /etc/init.d/pop-before-smtp-auth + Run: ???? + +The RedHat/Fedora init script is untested, and I don't know how to properly +install it on a contemporary RedHat system. + +Good luck, Mr. Phelps. diff --git a/UPGRADING b/UPGRADING new file mode 100644 index 0000000..6c28882 --- /dev/null +++ b/UPGRADING @@ -0,0 +1,13 @@ +If upgrading from a version prior to 1.3, the configuration +setting 'postinstance' has changed. + +From version 1.0 through 1.2, this setting assumed the Postfix +directory was in /etc so that for /etc/postfix it was: + + postinstance = postfix + +From version 1.3 on, this configuration setting requires the +entire path to the Postfix instance: + + postinstance = /etc/postfix + diff --git a/cloneurl b/cloneurl new file mode 100644 index 0000000..1cdca03 --- /dev/null +++ b/cloneurl @@ -0,0 +1,3 @@ +git://git.gothamcode.com/pop-before-smtp-auth (git/read only) +http://git.gothamcode.com/pop-before-smtp-auth (http/read only) +ssh://git@git.gothamcode.com/pop-before-smtp-auth (ssh-public key/read-write) diff --git a/config b/config new file mode 100644 index 0000000..64d02a4 --- /dev/null +++ b/config @@ -0,0 +1,14 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + +[gitweb] + owner = Ron Guerin + description = POP-Before-SMTP-Auth, POP before SMTP daemon for *nix systems :: http://gothamcode.com/pop-before-smtp-auth + category = System daemons + +[remote "origin"] + url = ssh://git@git.gothamcode.com/pop-before-smtp-auth.git + fetch = +refs/heads/*:refs/remotes/origin/* diff --git a/git-daemon-export-ok b/git-daemon-export-ok new file mode 100644 index 0000000..e69de29 diff --git a/pop-before-smtp-auth b/pop-before-smtp-auth new file mode 100755 index 0000000..33958c9 --- /dev/null +++ b/pop-before-smtp-auth @@ -0,0 +1,601 @@ +#!/usr/bin/env php + + * + * 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 + * @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=, 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"); + } +} +?> diff --git a/pop-before-smtp-auth.8 b/pop-before-smtp-auth.8 new file mode 100644 index 0000000..8e2ee04 --- /dev/null +++ b/pop-before-smtp-auth.8 @@ -0,0 +1,51 @@ +.\" Manpage for pop-before-smtp-auth. +.\" Contact ron@vnetworx.net with corrections and improvements. +.TH POP-BEFORE-SMTP-AUTH 8 "2012-11-30" "1.3.2" "pop-before-smtp-auth man page" +.SH NAME +pop-before-smtp-auth \- POP Before SMTP Authorization Daemon for Postfix and Dovecot +.SH SYNOPSIS +pop-before-smtp-auth [--debug=TRUE|FALSE] [--config=/path/to/config] [--syslog=TRUE|FALSE] [--pidpath=/path/to/pidfile] +[--hostname=hostname] [--maillog=/path/to/mail.log] [--authperiod=seconds] [--checkdelay=seconds] [--popserver=popserver] +[--popservice=popservice] [--postmap=/path/to/postmap] [--postinstance=/path/to/postfix_config_dir] +[--posthashfile=/path/to/pop_users_hashfile] [--logignores=TRUE|FALSE] [--daemonize=TRUE|FALSE] [--regex='regex'] +[--regexstamp=''] [--regexuser=''] [--regexip=''] [--silentmax=seconds] +.SH DESCRIPTION +pop-before-smtp-auth (PBSA) is a POP before SMTP Authorization Daemon for use with Postfix and Dovecot. +PBSA monitors your mail server log file looking for successful POP3 logins via Dovecot, and authorizes +the corresponding IP addresses to use Postfix as a SMTP relay for a limited time. +.SH OPTIONS +pop-before-smtp-auth has many options which control its behavior. These may be set by default, through +a configuration file, or via the command-line. Options may be specified in the configuration file like this: + option + option=value + option='string value' + +Options may be specified on the command-line as follows: + --option + --option=value + --option='string value' + +Options: +--debug +.RE +Include debugging information in messages. +--config +.RE +Path to configuration file (can only be set on command-line). + +An option not set to anything is the same as setting it to TRUE. For example on the command-line, +.B --option +is the same as +.B --option=TRUE +, while in the configuration file, +.B option +is the same as +.B option=TRUE + +.SH FILES +/etc/pop-before-smtp-auth.conf +.SH NOTES +pop-before-smtp-auth (PBSA) recognizes the signals HUP and TERM. Send TERM to get PBSA to shut down, +send HUP to get PBSA to reload its configuration settings. +.SH AUTHOR +Ron Guerin diff --git a/pop-before-smtp-auth.conf b/pop-before-smtp-auth.conf new file mode 100644 index 0000000..0ab7590 --- /dev/null +++ b/pop-before-smtp-auth.conf @@ -0,0 +1,13 @@ + diff --git a/pop-before-smtp-auth.init-debian b/pop-before-smtp-auth.init-debian new file mode 100755 index 0000000..65818e2 --- /dev/null +++ b/pop-before-smtp-auth.init-debian @@ -0,0 +1,51 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: pop-before-smtp-auth +# Required-Start: $syslog +# Required-Stop: $syslog +# Should-Start: $local_fs +# Should-Stop: $local_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +### END INIT INFO + +NAME=pop-before-smtp-auth +DESC=pop-before-smtp-auth +PIDFILE=/var/run/$NAME.pid +DIR=/usr/local/sbin + +case "$1" in + start) + echo -n "Starting $DESC: " + start-stop-daemon -S -p $PIDFILE --exec $DIR/$NAME + ;; + restart) + $0 stop + $0 start + ;; + reload) + PID=`pidof php $DIR/$NAME` + if [ -f $PIDFILE ]; then + PIDFILEPID=`cat $PIDFILE` + if [ "X$PIDFILEPID" = "X$PID" ]; then + echo "Reloading $NAME." + kill -HUP $PID + exit 0 + fi + fi + echo "$NAME not running." + ;; + stop) + echo -n "Stopping $DESC: " + start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE + echo "$NAME." + rm -f $PIDFILE + ;; + *) + N=/etc/init.d/$NAME + echo "Usage: $N {start|stop|reload}" >&2 + ;; +esac + +exit 0 + diff --git a/pop-before-smtp-auth.init-fedora b/pop-before-smtp-auth.init-fedora new file mode 100755 index 0000000..e2e8960 --- /dev/null +++ b/pop-before-smtp-auth.init-fedora @@ -0,0 +1,107 @@ +#!/bin/sh +# +# pop-before-smtp-auth SMTP Auth for POP3 +# +# chkconfig: 2345 20 80 +# description: Provides SMTP Auth for POP3 clients + +### BEGIN INIT INFO +# Provides: +# Required-Start: +# Required-Stop: +# Should-Start: +# Should-Stop: +# Default-Start: +# Default-Stop: +# Short-Description: +# Description: +### END INIT INFO + +# Source function library. +. /etc/rc.d/init.d/functions + +exec="/usr/local/sbin/pop-before-smtp-auth" +prog="pop-before-smtp-auth" +config="/etc/pop-before-smtp-auth.conf" +pid=`pidof php $exec` + +[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog + +lockfile=/var/lock/subsys/$prog + +start() { + [ -x $exec ] || exit 5 + [ -f $config ] || exit 6 + echo -n $"Starting $prog: " + daemon $exec + retval=$? + echo + [ $retval -eq 0 ] && touch $lockfile + return $retval +} + +stop() { + echo -n $"Stopping $prog: " + killproc $prog + retval=$? + echo + [ $retval -eq 0 ] && rm -f $lockfile + return $retval +} + +restart() { + stop + start +} + +reload() { + if [ "X$pid" != "X" ]; then + term -HUP $pid + fi +} + +force_reload() { + restart +} + +rh_status() { + # run checks to determine if the service is running or use generic status + status $prog +} + +rh_status_q() { + rh_status >/dev/null 2>&1 +} + + +case "$1" in + start) + rh_status_q && exit 0 + $1 + ;; + stop) + rh_status_q || exit 0 + $1 + ;; + restart) + $1 + ;; + reload) + rh_status_q || exit 7 + $1 + ;; + force-reload) + force_reload + ;; + status) + rh_status + ;; + condrestart|try-restart) + rh_status_q || exit 0 + restart + ;; + *) + echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" + exit 2 +esac +exit $? diff --git a/pop-before-smtp-auth.php b/pop-before-smtp-auth.php new file mode 120000 index 0000000..2743ae2 --- /dev/null +++ b/pop-before-smtp-auth.php @@ -0,0 +1 @@ +pop-before-smtp-auth \ No newline at end of file