15417 lines
472 KiB
PHP
15417 lines
472 KiB
PHP
<?php
|
|
/** @noinspection PhpMissingParamTypeInspection */
|
|
/** @noinspection PhpUnused */
|
|
/** @noinspection PhpIncludeInspection */
|
|
$FM_VERS = "10.03"; // script version
|
|
|
|
/* ex:set ts=4 sw=4 et:
|
|
* FormMail PHP script from Tectite.com.
|
|
* FormMail versions <= 8 support PHP 4.
|
|
* FormMail version 9 supports PHP 5 through 7.
|
|
* FormMail versions >= 10 supports PHP 8.
|
|
*
|
|
* Copyright (c) 2001-2022 Open Concepts (Vic) Pty Ltd
|
|
* (ABN 12 130 429 248), Melbourne, Australia.
|
|
* This script is free for all use as described in the "Copying and Use" and
|
|
* "Warranty and Disclaimer" sections below.
|
|
*
|
|
* Visit us at http://www.tectite.com/ for updates and more information.
|
|
*
|
|
*** If you use Tectite FormMail, please support its development and other
|
|
*** freeware products by putting the following link on your website:
|
|
*** Visit www.tectite.com for free <a href="http://www.tectite.com/">FormMail</a>.
|
|
*
|
|
* Author: Russell Robinson
|
|
* First released: 2nd October 2001
|
|
*
|
|
* Read This First
|
|
* ~~~~~~~~~~~~~~~
|
|
* This script is very well documented and quite large! It looks daunting,
|
|
* but really isn't.
|
|
* If you have experience with PHP or other scripting languages,
|
|
* here's what you *need* to read:
|
|
* - Configuration (TARGET_EMAIL & DEF_ALERT)
|
|
* - Creating Forms
|
|
* That's it! (Alternatively, just read the Quick Start and/or
|
|
* Quicker Start section below).
|
|
* Full configuration documentation is here:
|
|
* http://www.tectite.com/fmdoc/index.php
|
|
*
|
|
* NOTE: do not read or modify this script or any PHP script
|
|
* with DreamWeaver or FrontPage!
|
|
* Many versions of those programs silently corrupt PHP scripts.
|
|
*
|
|
* Purpose:
|
|
* ~~~~~~~~
|
|
* To accept information from an HTML form via HTTP and mail it to recipients.
|
|
*
|
|
* What does this PHP script do?
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
* On your web site, you may have one or more HTML forms that accept
|
|
* information from people visiting your website. Your aim is for your
|
|
* website to email that information to you and/or add it to a database.
|
|
* FormMail performs those functions.
|
|
*
|
|
* Quick Start
|
|
* ~~~~~~~~~~~
|
|
* 1. Edit this file and set TARGET_EMAIL for your requirements (near
|
|
* line 512 in this file - replace "yourhost\.com" with your mail server's
|
|
* name). We also strongly recommend you set DEF_ALERT (the next
|
|
* configuration below TARGET_EMAIL).
|
|
* 2. Install this file as formmail.php (or other name ending in .php)
|
|
* on your web server.
|
|
* Test alerts by using your browser to open a URL to the script:
|
|
* http://www.yourhost.com/formmail.php?testalert=1
|
|
* Alerts are the only way FormMail can tell you the details of
|
|
* errors or faults.
|
|
* 3. Create an HTML form and:
|
|
* - specify a hidden field called "recipients" with the email address
|
|
* of the person to receive the form's results.
|
|
* - in the your form tag set the action attribute to
|
|
* the formmail.php you uploaded to your web server
|
|
*
|
|
* Once you have FormMail working, you may be interested in some advanced
|
|
* usage and features. We have HOW-TO guides at www.tectite.com which
|
|
* describe many of the advanced processing you can do with FormMail.
|
|
* http://www.tectite.com/fmhowto/guides.php
|
|
*
|
|
* Quicker Start
|
|
* ~~~~~~~~~~~~~
|
|
* Use the FormMail Configuration Wizard here:
|
|
* http://www.tectite.com/wizards/fmconf.php
|
|
* By answering a few questions you'll get a configured FormMail and
|
|
* a sample HTML form ready to upload and use on your server.
|
|
*
|
|
* Features
|
|
* ~~~~~~~~
|
|
* For a list of features go to: http://www.tectite.com/formmailpage.php
|
|
*
|
|
* Security
|
|
* ~~~~~~~~
|
|
* Security is the primary concern in accepting data from your website
|
|
* visitors.
|
|
* Tectite FormMail has several security features designed into it. Note,
|
|
* however, it requires configuration for your particular web site.
|
|
*
|
|
* Configuration
|
|
* ~~~~~~~~~~~~~
|
|
* To configure this script, go to the section titled "CONFIGURATION"
|
|
* (after reading the legal stuff below).
|
|
*
|
|
* There is only one mandatory setting: TARGET_EMAIL
|
|
* and one strongly recommended setting: DEF_ALERT
|
|
*
|
|
* Full configuration information is available here:
|
|
* http://www.tectite.com/fmdoc/index.php
|
|
*
|
|
* Creating Forms
|
|
* ~~~~~~~~~~~~~~
|
|
* Go to this URL to learn how to write HTML forms for use with
|
|
* Tectite FormMail: http://www.tectite.com/fmdoc/creating_forms.php
|
|
*
|
|
* Copying and Use (Software License)
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
* Tectite FormMail is provided free of charge and may be freely distributed
|
|
* and used provided that you:
|
|
* 1. keep this header, including copyright and comments,
|
|
* in place and unmodified; and,
|
|
* 2. do not charge a fee for distributing it, without an agreement
|
|
* in writing with Root Software allowing you to do so; and,
|
|
* 3. if you modify FormMail before distributing it, you clearly
|
|
* identify:
|
|
* a) who you are
|
|
* b) how to contact you
|
|
* c) what changes you have made
|
|
* d) why you have made those changes.
|
|
*
|
|
* By using any of our products, including this script, you are
|
|
* agreeing to our standard Terms and Conditions, available here:
|
|
* http://www.tectite.com/TermsAndConditions.pdf
|
|
*
|
|
* This is free software and the Software License shown above
|
|
* is to be read in conjunction with our standard Terms and Conditions.
|
|
*
|
|
* Warranty and Disclaimer
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~
|
|
* Tectite FormMail is provided free-of-charge and with ABSOLUTELY NO WARRANTY.
|
|
* It has not been verified for use in critical applications, including,
|
|
* but not limited to, medicine, defense, aircraft, space exploration,
|
|
* or any other potentially dangerous activity.
|
|
*
|
|
* By using Tectite FormMail you agree to indemnify Root Software and
|
|
* Open Concepts (Vic) Pty Ltd, their agents, employees, directors and
|
|
* associated companies and businesses from any liability whatsoever.
|
|
*
|
|
* We still care
|
|
* ~~~~~~~~~~~~~
|
|
* If you find a bug or fault in FormMail, please report it to us.
|
|
* We will respond to your report and make endeavours to rectify any
|
|
* faults you've detected as soon as possible.
|
|
*
|
|
* To contact us view our contact information:
|
|
* http://www.tectite.com/contacts.php
|
|
*
|
|
* Version History
|
|
* ~~~~~~~~~~~~~~~
|
|
* Near the top of this file, you'll find its version. The version
|
|
* line looks like this:
|
|
* $FM_VERS = "N.MM"; /* script version ...
|
|
*
|
|
* The version history used to be located within this file. However,
|
|
* starting with Version 8.00 we've moved it...
|
|
*
|
|
* You can read the complete version history of FormMail on our
|
|
* main website here:
|
|
* http://www.tectite.com/fmdoc/version_history.php
|
|
*/
|
|
|
|
/**
|
|
* An alternative function for sending email.
|
|
* You can override this variable in a hook script to use a different function.
|
|
*/
|
|
$ALT_MAIL_FUNCTION = '';
|
|
|
|
FMDebug('Submission to: ' . (isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : '') . ' from: ' .
|
|
(isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''));
|
|
|
|
if (isset($_SERVER['REQUEST_METHOD']) && strtoupper($_SERVER['REQUEST_METHOD']) === 'OPTIONS') {
|
|
FMDebug('CORS OPTIONS request');
|
|
CORS_Response();
|
|
FormMailExit();
|
|
}
|
|
|
|
//
|
|
// Capture the current date and time, for various purposes.
|
|
//
|
|
date_default_timezone_set('UTC'); /* prevent notice in PHP 5.1+ */
|
|
$lNow = time();
|
|
|
|
ini_set('track_errors',1); // enable $php_errormsg
|
|
|
|
$aAlertInfo = array();
|
|
|
|
$sLangID = ""; // the language ID
|
|
$aMessages = array(); // all FormMail messages in the appropriate language
|
|
|
|
function FormMailExit($s_mesg = '')
|
|
{
|
|
if (($incFile = hookFileExists("fmhookonexit.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
exit($s_mesg);
|
|
}
|
|
|
|
/**
|
|
* Interrogate and manage the execution environment.
|
|
*
|
|
* @package tectite_formmail
|
|
* @subpackage setup
|
|
*/
|
|
class ExecEnv
|
|
{
|
|
/**
|
|
* Value of phpversion().
|
|
*
|
|
* @var string
|
|
*/
|
|
private $_sPHPVersionString;
|
|
|
|
/**
|
|
* Array containing the elements of the PHP version
|
|
*
|
|
* @var array
|
|
*/
|
|
private $_aPHPVersion;
|
|
|
|
/**
|
|
* The URL for the script.
|
|
*
|
|
* @var string
|
|
*/
|
|
private $_sScript;
|
|
|
|
/**
|
|
* Construct the class, and check PHP version.
|
|
*/
|
|
function __construct()
|
|
{
|
|
$this->_Init();
|
|
$this->_CheckVersion();
|
|
}
|
|
|
|
/**
|
|
* Initialise the object.
|
|
* Sets {@link $_aPHPVersion} and {@link $_sPHPVersionString}
|
|
*/
|
|
private function _Init()
|
|
{
|
|
$this->_sPHPVersionString = phpversion();
|
|
$this->_aPHPVersion = explode(".",$this->_ZapHyphenPart($this->_sPHPVersionString));
|
|
}
|
|
|
|
/**
|
|
* Remove any hyphenated component of a version string.
|
|
*
|
|
* @param $str
|
|
*
|
|
* @return false|string
|
|
*/
|
|
private function _ZapHyphenPart($str)
|
|
{
|
|
if (($i_pos = strpos($str,'-')) !== false) {
|
|
return substr($str,0,$i_pos);
|
|
} else {
|
|
return $str;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function getPHPVersion()
|
|
{
|
|
return $this->_aPHPVersion;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getPHPVersionString()
|
|
{
|
|
return $this->_sPHPVersionString;
|
|
}
|
|
|
|
/**
|
|
* Check for old version of PHP - die with a message if too old.
|
|
*
|
|
* This is actually not required because PHP 4 won't even accept
|
|
* the syntax of a PHP 5 script. However, we might need some
|
|
* other version check in the future, so this is a useful method
|
|
* to have around in that case.
|
|
*/
|
|
private function _CheckVersion()
|
|
{
|
|
$s_req_string = "5.0.0"; // We only support PHP 5 onward.
|
|
$a_too_old = explode(".",$s_req_string);
|
|
|
|
$i_cannot_use = ($a_too_old[0] * 10000) + ($a_too_old[1] * 100) + $a_too_old[2];
|
|
|
|
$i_this_num = ($this->_aPHPVersion[0] * 10000) + ($this->_aPHPVersion[1] * 100) +
|
|
$this->_aPHPVersion[2];
|
|
|
|
if ($i_this_num <= $i_cannot_use) {
|
|
FormMailExit(GetMessage(MSG_SCRIPT_VERSION,
|
|
array("PHPREQ" => $s_req_string,"PHPVERS" => $this->_sPHPVersionString)));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test PHP version against a particular version string.
|
|
*
|
|
* @param $s_vers
|
|
*
|
|
* @return boolean true if the PHP version is at or later than the version
|
|
* specified
|
|
*/
|
|
public function IsPHPAtLeast($s_vers)
|
|
{
|
|
$a_test_version = explode(".",$s_vers);
|
|
if (count($a_test_version) < 3) {
|
|
return (false);
|
|
}
|
|
return ($this->_aPHPVersion[0] > $a_test_version[0] || ($this->_aPHPVersion[0] ==
|
|
$a_test_version[0] &&
|
|
($this->_aPHPVersion[1] >
|
|
$a_test_version[1] ||
|
|
$this->_aPHPVersion[1] ==
|
|
$a_test_version[1] &&
|
|
$this->_aPHPVersion[2] >=
|
|
$a_test_version[2])));
|
|
}
|
|
|
|
public function GetScript()
|
|
{
|
|
if (!isset($this->_sScript)) {
|
|
if (isset($_SERVER["PHP_SELF"]) &&
|
|
!empty($_SERVER["PHP_SELF"]) &&
|
|
isset($_SERVER["SERVER_NAME"]) &&
|
|
!empty($_SERVER["SERVER_NAME"])
|
|
) {
|
|
if (isset($_SERVER["SERVER_PORT"]) &&
|
|
$_SERVER["SERVER_PORT"] != 80
|
|
) {
|
|
if ($_SERVER["SERVER_PORT"] == 443) // SSL port
|
|
//
|
|
// just use https prefix
|
|
//
|
|
{
|
|
$this->_sScript = "https://" . $_SERVER["SERVER_NAME"] .
|
|
$_SERVER["PHP_SELF"];
|
|
} else
|
|
//
|
|
// use http with port number
|
|
//
|
|
{
|
|
$this->_sScript = "http://" . $_SERVER["SERVER_NAME"] .
|
|
":" . $_SERVER["SERVER_PORT"] .
|
|
$_SERVER["PHP_SELF"];
|
|
}
|
|
} else {
|
|
$this->_sScript = "http://" . $_SERVER["SERVER_NAME"] .
|
|
$_SERVER["PHP_SELF"];
|
|
}
|
|
} else {
|
|
Error("no_php_self",GetMessage(MSG_NO_PHP_SELF),false,false);
|
|
}
|
|
}
|
|
return ($this->_sScript);
|
|
}
|
|
|
|
/**
|
|
* Get a boolean PHP setting.
|
|
*
|
|
* @param string $s_name the name of the setting
|
|
*
|
|
* @return bool|null the value of the setting
|
|
*/
|
|
public function getINIBool($s_name)
|
|
{
|
|
$m_val = ini_get($s_name);
|
|
if ($m_val !== null) {
|
|
if (is_numeric($m_val)) {
|
|
$m_val = (int)$m_val;
|
|
} elseif (is_string($m_val)) {
|
|
$m_val = strtolower($m_val);
|
|
switch ($m_val) {
|
|
case "1":
|
|
case "on":
|
|
case "true":
|
|
$m_val = true;
|
|
break;
|
|
default:
|
|
$m_val = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return ($m_val);
|
|
}
|
|
|
|
/**
|
|
* Return whether the session can be passed in a URL.
|
|
*
|
|
* @return bool true if the session can be passed in a URL
|
|
*/
|
|
public function allowSessionURL()
|
|
{
|
|
$m_only_cookies = $this->getINIBool('session.use_only_cookies');
|
|
FMDebug('only_cookies=' . $m_only_cookies);
|
|
if ($m_only_cookies === null) {
|
|
$m_only_cookies = $this->IsPHPAtLeast('5.3.0');
|
|
FMDebug('php=' . $this->IsPHPAtLeast('5.3.0') . ',only_cookies=' . $m_only_cookies);
|
|
}
|
|
return (!$m_only_cookies);
|
|
}
|
|
|
|
public function getPostMaxSize()
|
|
{
|
|
$s_max_value = ini_get('post_max_size');
|
|
$number = (int)substr($s_max_value,0,-1);
|
|
switch (strtoupper(substr($s_max_value,-1))) {
|
|
case "K":
|
|
return $number * 1024;
|
|
case "M":
|
|
return $number * pow(1024,2);
|
|
case "G":
|
|
return $number * pow(1024,3);
|
|
default:
|
|
return (int)$s_max_value;
|
|
}
|
|
}
|
|
|
|
public function checkUploadSize()
|
|
{
|
|
if (isset($_SERVER['CONTENT_LENGTH'])) {
|
|
$n_size = (int)$_SERVER['CONTENT_LENGTH'];
|
|
$n_max = $this->getPostMaxSize();
|
|
// echo "Size = $n_size, max = $n_max\n";
|
|
if ($n_size > $n_max) {
|
|
UserError("post_size_limit",GetMessage(MSG_POST_SIZE_LIMIT,array(),false,false));
|
|
}
|
|
}
|
|
}
|
|
|
|
public function checkFileUploadSize($a_file_vars)
|
|
{
|
|
if (Settings::get('FILEUPLOADS')) {
|
|
$a_field_names = array_keys($a_file_vars);
|
|
foreach ($a_field_names as $s_name => $a_upload) {
|
|
if (isset($a_upload)) {
|
|
switch (isset($a_upload['error']) ? $a_upload['error'] : 0) {
|
|
case 0: // no error
|
|
case 4: // no file uploaded
|
|
break;
|
|
default:
|
|
UserError('file_upload_error',FieldManager::GetFileUploadErrorMesg($a_upload['error']));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$ExecEnv = new ExecEnv();
|
|
if (!$ExecEnv->IsPHPAtLeast("5.3.0")) {
|
|
//
|
|
// disable this silly setting (usually not enabled)
|
|
// it's also deprecated from PHP version 5.3.0
|
|
//
|
|
if (function_exists('set_magic_quotes_runtime')) {
|
|
/** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */
|
|
@set_magic_quotes_runtime(0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// We set references to the appropriate arrays to handle PHP version differences
|
|
// Session vars are selected after we start the session.
|
|
//
|
|
$aServerVars = &$_SERVER;
|
|
$aGetVars = &$_GET;
|
|
$aFormVars = &$_POST;
|
|
$aFileVars = &$_FILES;
|
|
$aEnvVars = &$_ENV;
|
|
|
|
$bIsGetMethod = false;
|
|
$bHasGetData = false;
|
|
|
|
if (!isset($REAL_DOCUMENT_ROOT)) {
|
|
SetRealDocumentRoot();
|
|
}
|
|
|
|
if (isset($aServerVars['SERVER_PORT'])) {
|
|
$SCHEME = ($aServerVars['SERVER_PORT'] == 80) ? "http://" : "https://";
|
|
} else {
|
|
$SCHEME = "";
|
|
}
|
|
if (isset($aServerVars['SERVER_NAME']) && $aServerVars['SERVER_NAME'] !== "") {
|
|
$SERVER = $aServerVars['SERVER_NAME'];
|
|
} elseif (isset($aServerVars['SERVER_ADDR']) && $aServerVars['SERVER_ADDR'] !== "") {
|
|
$SERVER = $aServerVars['SERVER_ADDR'];
|
|
} else {
|
|
$SERVER = "";
|
|
}
|
|
|
|
/*
|
|
* Load an optional include file before the configuration.
|
|
* You can use this to set variables that can be used in the
|
|
* configuration section.
|
|
*/
|
|
if (includeFileExists("formmail-preconfig.inc.php")) {
|
|
@include("formmail-preconfig.inc.php");
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* CONFIGURATION (do not alter this line in any way!!!) */
|
|
/*****************************************************************************
|
|
* This is the *only* place where you need to modify things to use formmail.php
|
|
* on your particular system. This section finishes at "END OF CONFIGURATION".
|
|
* Help for all settings can be found on our website:
|
|
* http://www.tectite.com/fmdoc/index.php
|
|
*
|
|
* Also, above each setting is a direct URL to the help information for the
|
|
* setting.
|
|
*****************************************************************************/
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/email_name.php */
|
|
$EMAIL_NAME = "^[-a-z0-9._]+"; /* the '^' is an important security feature! */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/target_email.php */
|
|
$TARGET_EMAIL = array("^info@iocloudsolutions\.net$");
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/email_addrs.php */
|
|
$EMAIL_ADDRS = array();
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/def_alert.php */
|
|
$DEF_ALERT = "admin@bugzbunny.net";
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/site_domain.php */
|
|
$SITE_DOMAIN = "https://www.iocloudsolutions.net"; /* your website domain name */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/set_real_document_root.php */
|
|
$SET_REAL_DOCUMENT_ROOT = ""; /* overrides the value set by SetRealDocumentRoot function */
|
|
|
|
//
|
|
// override $REAL_DOCUMENT_ROOT from the $SET_REAL_DOCUMENT_ROOT value (if any)
|
|
// Do not alter the following code (next 3 lines)!
|
|
//
|
|
if (isset($SET_REAL_DOCUMENT_ROOT) && $SET_REAL_DOCUMENT_ROOT !== "") {
|
|
$REAL_DOCUMENT_ROOT = $SET_REAL_DOCUMENT_ROOT;
|
|
}
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/config_check.php */
|
|
$CONFIG_CHECK = array("TARGET_EMAIL");
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/at_mangle.php */
|
|
$AT_MANGLE = "";
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/target_urls.php */
|
|
$TARGET_URLS = array(); /* default; no URLs allowed */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/head_crlf.php */
|
|
$HEAD_CRLF = "\r\n";
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/body_lf.php */
|
|
$BODY_LF = "\r\n"; /* the new default: use this for CR+LF */
|
|
//$BODY_LF = "\n"; /* the old default: just LF */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/from_user.php */
|
|
$FROM_USER = ""; /* the default - setting not used */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/sendmail_f_option.php */
|
|
$SENDMAIL_F_OPTION = false;
|
|
$SENDMAIL_F_OPTION_LINE = __LINE__ - 1; /* don't modify this line! */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/fixed_sender.php */
|
|
$FIXED_SENDER = "";
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/set_sender_from_email.php */
|
|
$SET_SENDER_FROM_EMAIL = false;
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/ini_set_from.php */
|
|
$INI_SET_FROM = false;
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/logdir.php */
|
|
$LOGDIR = "/var/www/logs"; /* directory for log files; empty string to disallow log files */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/autorespondlog.php */
|
|
$AUTORESPONDLOG = ""; /* file name in $LOGDIR for the auto responder log; empty string for no auto responder log */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/csv_file_settings.php */
|
|
$CSVDIR = ""; /* directory for csv files; empty string to disallow csv files */
|
|
$CSVSEP = ","; /* comma separator between fields (columns) */
|
|
$CSVINTSEP = ";"; /* semicolon is the separator for fields (columns) with multiple values (checkboxes, etc.) */
|
|
$CSVQUOTE = '"'; /* all fields in the CSV are quoted with this character; default is double quote. You can change it to single quote or leave it empty for no quotes. */
|
|
//$CSVQUOTE = "'"; /* use this if you want single quotes */
|
|
$CSVOPEN = ""; /* set to "b" to force line terminations to be kept as $CSVLINE setting below, regardless of operating
|
|
system. Keep as empty string and leave $CSVLINE unchanged, to get text file
|
|
terminations for your server's operating system. (Line feed on UNIX, carriage-return line feed on Windows). */
|
|
$CSVLINE = "\n"; /* line termination for CSV files. The default is a single line feed, which may be modified for your
|
|
server's operating system. If you want to change
|
|
this value, you *must* set $CSVOPEN = "b". */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/templatedir.php */
|
|
$TEMPLATEDIR = ""; /* directory for template files; empty string if you don't have any templates */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/templateurl.php */
|
|
$TEMPLATEURL = ""; /* default; no template URL */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/multiformdir.php */
|
|
$MULTIFORMDIR = ""; /* directory for multi-form template files; empty string if you're not using multi-forms */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/multiformurl.php */
|
|
$MULTIFORMURL = ""; /* default; no multi-forms templates URL */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/text_subs.php */
|
|
$TEXT_SUBS = array(
|
|
array("srch" => "/\\\\r\\\\n/","repl" => "\r\n",),
|
|
array("srch" => "/\\\\n/","repl" => "\n",),
|
|
array("srch" => "/\\\\t/","repl" => "\t",),
|
|
array("srch" => "/\\[NL\\]/","repl" => "\n",),
|
|
array("srch" => "/\\[TAB\\]/","repl" => "\t",),
|
|
array("srch" => "/\\[NBSP\\]/","repl" => " ",),
|
|
array("srch" => "/\\[DQUOT\\]/","repl" => '"',),
|
|
array("srch" => "/\\[SQUOT\\]/","repl" => "'",),
|
|
array("srch" => "/\\[COLON\\]/","repl" => ":",),
|
|
array("srch" => "/\\[SLOSH\\]/","repl" => "\\",),
|
|
array("srch" => "/\\[OPCURL\\]/","repl" => "{",),
|
|
array("srch" => "/\\[CLCURL\\]/","repl" => "}",),
|
|
array("srch" => "/(on[a-z]*|href|src)\\s*=\\s*/i","repl" => ""), /* strip html attributes that could be unsafe */
|
|
array("srch" => "/<\\s*(table|tr|td|th|p|ul|ol|li|b|i|u|strong|pre|h[1-6]|em|dl|dd|dt|hr|span|br)(\\b[^>]*?)>/i",
|
|
"repl" => "<\$1\$2>",
|
|
),
|
|
array("srch" => "#<\\s*/\\s*(table|tr|td|th|p|ul|ol|li|b|i|u|strong|pre|h[1-6]|em|dl|dd|dt|hr|span|br)\\s*>#i",
|
|
"repl" => "</\$1>",
|
|
),
|
|
);
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/authentication_settings.php */
|
|
$AUTHENTICATE = "";
|
|
//$AUTHENTICATE = "Basic cnVzc2VsbHI6dGVzdA=="; // example
|
|
$AUTH_USER = "";
|
|
$AUTH_PW = "";
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/form_ini_file.php */
|
|
$FORM_INI_FILE = "/var/www/formmail.ini";
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/moduledir.php */
|
|
$MODULEDIR = ".";
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/fmcompute.php */
|
|
$FMCOMPUTE = "fmcompute.php";
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/fmgeoip.php */
|
|
$FMGEOIP = "fmgeoip.php";
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/advanced_templates.php */
|
|
$ADVANCED_TEMPLATES = false; /* set to true for advanced templates */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/limited_import.php */
|
|
$LIMITED_IMPORT = true; /* set to true if your database cannot handle escaped quotes or newlines within imported data. Microsoft Access is one example. */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/valid_env.php */
|
|
$VALID_ENV = array('HTTP_REFERER','REMOTE_HOST','REMOTE_ADDR','REMOTE_USER',
|
|
'HTTP_USER_AGENT'
|
|
);
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/fileuploads.php */
|
|
$FILEUPLOADS = false; /* set to true to allow file attachments */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/max_file_upload_size.php */
|
|
$MAX_FILE_UPLOAD_SIZE = 0; /* default of 0 means that other software */
|
|
// controls the maximum file upload size
|
|
// (FormMail doesn't test the file size)
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/file_repository.php */
|
|
$FILE_REPOSITORY = "";
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/file_mode.php */
|
|
$FILE_MODE = 0664; /* always precede with 0 to specify octal! */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/file_overwrite.php */
|
|
$FILE_OVERWRITE = true;
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/next_num_file.php */
|
|
$NEXT_NUM_FILE = "";
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/put_data_in_url.php */
|
|
$PUT_DATA_IN_URL = true; /* set to true to place data in the URL */
|
|
// for bad_url redirects
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/allow_get_method.php */
|
|
$ALLOW_GET_METHOD = false;
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/db_see_input.php */
|
|
$DB_SEE_INPUT = false; /* set to true to just see the input values */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/db_see_ini.php */
|
|
$DB_SEE_INI = false; /* set to true to just see the ini file */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/maxstring.php */
|
|
$MAXSTRING = 1024; /* maximum string length for a value */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/require_captcha.php */
|
|
$REQUIRE_CAPTCHA = ""; /* set to a message string if your forms */
|
|
// must provide a CAPTCHA string
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/recaptcha_private_key.php */
|
|
$RECAPTCHA_PRIVATE_KEY = "";
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/bshowmesgnumbers.php */
|
|
$bShowMesgNumbers = false;
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/filters.php */
|
|
/* Note for Tectite personnel: the upgrade Wizard will merge new values
|
|
* but be careful of $var usage and quoting in new entries.
|
|
*/
|
|
/** @noinspection PhpUndefinedVariableInspection */
|
|
$FILTERS = array("encode" => "$REAL_DOCUMENT_ROOT/cgi-bin/fmencoder -kpubkey.txt",
|
|
"null" => "null",
|
|
"csv" => "csv"
|
|
);
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/socket_filters.php */
|
|
$SOCKET_FILTERS = array(
|
|
"httpencode" => array("site" => "YourSiteHere",
|
|
"port" => 80,
|
|
"path" => "/cgi-bin/fmencoder",
|
|
"params" => array(array("name" => "key",
|
|
"file" => "$REAL_DOCUMENT_ROOT/cgi-bin/pubkey.txt"
|
|
)
|
|
)
|
|
),
|
|
"sslencode" => array("site" => "ssl://YourSecureSiteHere",
|
|
"port" => 443,
|
|
"path" => "/cgi-bin/fmencoder",
|
|
"params" => array(array("name" => "key",
|
|
"file" => "$REAL_DOCUMENT_ROOT/cgi-bin/pubkey.txt"
|
|
)
|
|
)
|
|
),
|
|
);
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/filter_attribs.php */
|
|
$FILTER_ATTRIBS = array("encode" => "Strips,MIME=application/vnd.fmencoded,Encrypts",
|
|
"httpencode" => "Strips,MIME=application/vnd.fmencoded,Encrypts",
|
|
"sslencode" => "Strips,MIME=application/vnd.fmencoded,Encrypts",
|
|
"csv" => "Strips,MIME=text/csv",
|
|
);
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/check_for_new_version.php */
|
|
$CHECK_FOR_NEW_VERSION = true;
|
|
$CHECK_DAYS = 30;
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/scratch_pad.php */
|
|
$SCRATCH_PAD = "";
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/cleanup_time.php */
|
|
$CLEANUP_TIME = 60; /* cleanup time in minutes */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/cleanup_chance.php */
|
|
$CLEANUP_CHANCE = 20; /* percentage probability that cleanup will be performed */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/pear_settings.php */
|
|
$PEAR_SMTP_HOST = "";
|
|
$PEAR_SMTP_PORT = 25;
|
|
$PEAR_SMTP_USER = "";
|
|
$PEAR_SMTP_PWD = "";
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/alert_on_user_error.php */
|
|
$ALERT_ON_USER_ERROR = true;
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/enable_attack_detection.php */
|
|
$ENABLE_ATTACK_DETECTION = true;
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/attack_detection_url.php */
|
|
$ATTACK_DETECTION_URL = "";
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/alert_on_attack_detection.php */
|
|
$ALERT_ON_ATTACK_DETECTION = false;
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/attack_detection_mime.php */
|
|
$ATTACK_DETECTION_MIME = true;
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/attack_detection_junk.php */
|
|
$ATTACK_DETECTION_JUNK = true;
|
|
$ATTACK_DETECTION_JUNK_CONSONANTS = "bcdfghjklmnpqrstvwxz";
|
|
$ATTACK_DETECTION_JUNK_VOWELS = "aeiouy";
|
|
$ATTACK_DETECTION_JUNK_CONSEC_CONSONANTS = 5;
|
|
$ATTACK_DETECTION_JUNK_CONSEC_VOWELS = 4;
|
|
$ATTACK_DETECTION_JUNK_TRIGGER = 2;
|
|
$ATTACK_DETECTION_JUNK_LANG_STRIP = array(
|
|
"aiia", /* Hawaiian */
|
|
"aeoa", /* palaeoanthropic */
|
|
"aeoe", /* palaeoethnic */
|
|
"ayou", /* layout */
|
|
"ayee", /* payee */
|
|
"buyout", /* buyout */
|
|
"clayey", /* clayey */
|
|
"hooey", /* hooey */
|
|
"ioau", /* radioautograph */
|
|
"opoeia", /* pharmacopoeia, onomatopoeia */
|
|
"ooee", /* cooee */
|
|
"oyee", /* employee */
|
|
"ioau", /* radioautograph */
|
|
"uaia", /* guaiac */
|
|
"uaya", /* uruguayan */
|
|
"ueou", /* aqueous */
|
|
"uiou", /* obsequious */
|
|
"uoya", /* buoyant */
|
|
"queue", /* queue, queueing */
|
|
"earth", /* earthquake, earthslide */
|
|
"cks", /* jockstrap, backscratcher */
|
|
"ngth", /* strengths, length */
|
|
"ndths", /* thousandths */
|
|
"ght", /* nightclub, knightsbridge */
|
|
"phth", /* ophthalmology */
|
|
"sch", /* rothschild */
|
|
"shch", /* borshch */
|
|
"scr", /* corkscrew */
|
|
"spr", /* wingspread, offspring */
|
|
"str", /* armstrong, songstress */
|
|
"sts", /* bursts, postscript */
|
|
"tch", /* catchphrase, scratchproof */
|
|
"thst", /* northstar, birthstone */
|
|
"http", /* https, http */
|
|
"html", /* HTML, XHTML */
|
|
);
|
|
$ATTACK_DETECTION_JUNK_IGNORE_FIELDS = array();
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/attack_detection_dups.php */
|
|
$ATTACK_DETECTION_DUPS = array("realname","address1","address2","country","zip",
|
|
"phone","postcode","state","email"
|
|
);
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/attack_detection_specials.php */
|
|
$ATTACK_DETECTION_SPECIALS = true;
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/attack_detection_specials.php */
|
|
$ATTACK_DETECTION_SPECIALS_ONLY_EMAIL = array("derive_fields","required",
|
|
"mail_options","good_url","bad_url","good_template",
|
|
"bad_template"
|
|
);
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/attack_detection_specials.php */
|
|
$ATTACK_DETECTION_SPECIALS_ANY_EMAIL = array("subject");
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/attack_detection_many_urls.php */
|
|
$ATTACK_DETECTION_MANY_URLS = 3;
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/attack_detection_many_url_fields.php */
|
|
$ATTACK_DETECTION_MANY_URL_FIELDS = 0;
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/attack_detection_url_patterns.php */
|
|
$ATTACK_DETECTION_URL_PATTERNS = array(
|
|
'(^|[^-a-z_.0-9]+)(?<!\S@)([-a-z0-9]+\.)+(aaa|aarp|abarth|abb|abbott|abbvie|abc|able|abogado|abudhabi|ac|academy|accenture|accountant|accountants|aco|actor|ad|adac|ads|adult|ae|aeg|aero|aetna|af|afl|africa|ag|agakhan|agency|ai|aig|airbus|airforce|airtel|akdn|al|alfaromeo|alibaba|alipay|allfinanz|allstate|ally|alsace|alstom|am|amazon|americanexpress|americanfamily|amex|amfam|amica|amsterdam|analytics|android|anquan|anz|ao|aol|apartments|app|apple|aq|aquarelle|ar|arab|aramco|archi|army|arpa|art|arte|as|asda|asia|associates|at|athleta|attorney|au|auction|audi|audible|audio|auspost|author|auto|autos|avianca|aw|aws|ax|axa|az|azure|ba|baby|baidu|banamex|bananarepublic|band|bank|bar|barcelona|barclaycard|barclays|barefoot|bargains|baseball|basketball|bauhaus|bayern|bb|bbc|bbt|bbva|bcg|bcn|bd|be|beats|beauty|beer|bentley|berlin|best|bestbuy|bet|bf|bg|bh|bharti|bi|bible|bid|bike|bing|bingo|bio|biz|bj|black|blackfriday|blockbuster|blog|bloomberg|blue|bm|bms|bmw|bn|bnpparibas|bo|boats|boehringer|bofa|bom|bond|boo|book|booking|bosch|bostik|boston|bot|boutique|box|br|bradesco|bridgestone|broadway|broker|brother|brussels|bs|bt|budapest|bugatti|build|builders|business|buy|buzz|bv|bw|by|bz|bzh|ca|cab|cafe|cal|call|calvinklein|cam|camera|camp|cancerresearch|canon|capetown|capital|capitalone|car|caravan|cards|care|career|careers|cars|casa|case|cash|casino|cat|catering|catholic|cba|cbn|cbre|cbs|cc|cd|center|ceo|cern|cf|cfa|cfd|cg|ch|chanel|channel|charity|chase|chat|cheap|chintai|christmas|chrome|church|ci|cipriani|circle|cisco|citadel|citi|citic|city|cityeats|ck|cl|claims|cleaning|click|clinic|clinique|clothing|cloud|club|clubmed|cm|cn|co|coach|codes|coffee|college|cologne|com|comcast|commbank|community|company|compare|computer|comsec|condos|construction|consulting|contact|contractors|cooking|cookingchannel|cool|coop|corsica|country|coupon|coupons|courses|cpa|cr|credit|creditcard|creditunion|cricket|crown|crs|cruise|cruises|csc|cu|cuisinella|cv|cw|cx|cy|cymru|cyou|cz|dabur|dad|dance|data|date|dating|datsun|day|dclk|dds|de|deal|dealer|deals|degree|delivery|dell|deloitte|delta|democrat|dental|dentist|desi|design|dev|dhl|diamonds|diet|digital|direct|directory|discount|discover|dish|diy|dj|dk|dm|dnp|do| docs|doctor|dog|domains|dot|download|drive|dtv|dubai|dunlop|dupont|durban|dvag|dvr|dz|earth|eat|ec|eco|edeka|edu|education|ee|eg|email|emerck|energy|engineer|engineering|enterprises|epson|equipment|er|ericsson|erni|es|esq|estate|et|etisalat|eu|eurovision|eus|events|exchange|expert|exposed|express|extraspace|fage|fail|fairwinds|faith|family|fan|fans|farm|farmers|fashion|fast|fedex|feedback|ferrari|ferrero|fi|fiat|fidelity|fido|film|final|finance|financial|fire|firestone|firmdale|fish|fishing|fit|fitness|fj|fk|flickr|flights|flir|florist|flowers|fly|fm|fo|foo|food|foodnetwork|football|ford|forex|forsale|forum|foundation|fox|fr|free|fresenius|frl|frogans|frontdoor|frontier|ftr|fujitsu|fun|fund|furniture|futbol|fyi|ga|gal|gallery|gallo|gallup|game|games|gap|garden|gay|gb|gbiz|gd|gdn|ge|gea|gent|genting|george|gf|gg|ggee|gh|gi|gift|gifts|gives|giving|gl|glass|gle|global|globo|gm|gmail|gmbh|gmo|gmx|gn|godaddy|gold|goldpoint|golf|goo|goodyear|goog|google|gop|got|gov|gp|gq|gr|grainger|graphics|gratis|green|gripe|grocery|group|gs|gt|gu|guardian|gucci|guge|guide|guitars|guru|gw|gy|hair|hamburg|hangout|haus|hbo|hdfc|hdfcbank|health|healthcare|help|helsinki|here|hermes|hgtv|hiphop|hisamitsu|hitachi|hiv|hk|hkt|hm|hn|hockey|holdings|holiday|homedepot|homegoods|homes|homesense|honda|horse|hospital|host|hosting|hot|hoteles|hotels|hotmail|house|how|hr|hsbc|ht|hu|hughes|hyatt|hyundai|ibm|icbc|ice|icu|id|ie|ieee|ifm|ikano|il|im|imamat|imdb|immo|immobilien|in|inc|industries|infiniti|info|ing|ink|institute|insurance|insure|int|international|intuit|investments|io|ipiranga|iq|ir|irish|is|ismaili|ist|istanbul|it|itau|itv|jaguar|java|jcb|je|jeep|jetzt|jewelry|jio|jll|jm|jmp|jnj|jo|jobs|joburg|jot|joy|jp|jpmorgan|jprs|juegos|juniper|kaufen|kddi|ke|kerryhotels|kerrylogistics|kerryproperties|kfh|kg|kh|ki|kia|kim|kinder|kindle|kitchen|kiwi|km|kn|koeln|komatsu|kosher|kp|kpmg|kpn|kr|krd|kred|kuokgroup|kw|ky|kyoto|kz|la|lacaixa|lamborghini|lamer|lancaster|lancia|land|landrover|lanxess|lasalle|lat|latino|latrobe|law|lawyer|lb|lc|lds|lease|leclerc|lefrak|legal|lego|lexus|lgbt|li|lidl|life|lifeinsurance|lifestyle|lighting|like|lilly|limited|limo|lincoln|linde|link|lipsy|live|living|lk|llc|llp|loan|loans|locker|locus|loft|lol|london|lotte|lotto|love|lpl|lplfinancial|lr|ls|lt|ltd|ltda|lu|lundbeck|luxe|luxury|lv|ly|ma|macys|madrid|maif|maison|makeup|man|management|mango|map|market|marketing|markets|marriott|marshalls|maserati|mattel|mba|mc|mckinsey|md|me|med|media|meet|melbourne|meme|memorial|men|menu|merckmsd|mg|mh|miami|microsoft|mil|mini|mint|mit|mitsubishi|mk|ml|mlb|mls|mm|mma|mn|mo|mobi|mobile|moda|moe|moi|mom|monash|money|monster|mormon|mortgage|moscow|moto|motorcycles|mov|movie|mp|mq|mr|ms|msd|mt|mtn|mtr|mu|museum|music|mutual|mv|mw|mx|my|mz|na|nab|nagoya|name|natura|navy|nba|nc|ne|nec|net|netbank|netflix|network|neustar|new|news|next|nextdirect|nexus|nf|nfl|ng|ngo|nhk|ni|nico|nike|nikon|ninja|nissan|nissay|nl|no|nokia|northwesternmutual|norton|now|nowruz|nowtv|np|nr|nra|nrw|ntt|nu|nyc|nz|obi|observer|office|okinawa|olayan|olayangroup|oldnavy|ollo|om|omega|one|ong|onl|online|ooo|open|oracle|orange|org|organic|origins|osaka|otsuka|ott|ovh|pa|page|panasonic|paris|pars|partners|parts|party|passagens|pay|pccw|pe|pet|pf|pfizer|pg|ph|pharmacy|phd|philips|phone|photo|photography|photos|physio|pics|pictet|pictures|pid|pin|ping|pink|pioneer|pizza|pk|pl|place|play|playstation|plumbing|plus|pm|pn|pnc|pohl|poker|politie|porn|post|pr|pramerica|praxi|press|prime|pro|prod|productions|prof|progressive|promo|properties|property|protection|pru|prudential|ps|pt|pub|pw|pwc|py|qa|qpon|quebec|quest|racing|radio|re|read|realestate|realtor|realty|recipes|red|redstone|redumbrella|rehab|reise|reisen|reit|reliance|ren|rent|rentals|repair|report|republican|rest|restaurant|review|reviews|rexroth|rich|richardli|ricoh|ril|rio|rip|ro|rocher|rocks|rodeo|rogers|room|rs|rsvp|ru|rugby|ruhr|run|rw|rwe|ryukyu|sa|saarland|safe|safety|sakura|sale|salon|samsclub|samsung|sandvik|sandvikcoromant|sanofi|sap|sarl|sas|save|saxo|sb|sbi|sbs|sc|sca|scb|schaeffler|schmidt|scholarships|school|schule|schwarz|science|scot|sd|se|search|seat|secure|security|seek|select|sener|services|ses|seven|sew|sex|sexy|sfr|sg|sh|shangrila|sharp|shaw|shell|shia|shiksha|shoes|shop|shopping|shouji|show|showtime|si|silk|sina|singles|site|sj|sk|ski|skin|sky|skype|sl|sling|sm|smart|smile|sn|sncf|so|soccer|social|softbank|software|sohu|solar|solutions|song|sony|soy|spa|space|sport|spot|sr|srl|ss|st|stada|staples|star|statebank|statefarm|stc|stcgroup|stockholm|storage|store|stream|studio|study|style|su|sucks|supplies|supply|support|surf|surgery|suzuki|sv|swatch|swiss|sx|sy|sydney|systems|sz|tab|taipei|talk|taobao|target|tatamotors|tatar|tattoo|tax|taxi|tc|tci|td|tdk|team|tech|technology|tel|temasek|tennis|teva|tf|tg|th|thd|theater|theatre|tiaa|tickets|tienda|tiffany|tips|tires|tirol|tj|tjmaxx|tjx|tk|tkmaxx|tl|tm|tmall|tn|to|today|tokyo|tools|top|toray|toshiba|total|tours|town|toyota|toys|tr|trade|trading|training|travel|travelchannel|travelers|travelersinsurance|trust|trv|tt|tube|tui|tunes|tushu|tv|tvs|tw|tz|ua|ubank|ubs|ug|uk|unicom|university|uno|uol|ups|us|uy|uz|va|vacations|vana|vanguard|vc|ve|vegas|ventures|verisign|versicherung|vet|vg|vi|viajes|video|vig|viking|villas|vin|vip|virgin|visa|vision|viva|vivo|vlaanderen|vn|vodka|volkswagen|volvo|vote|voting|voto|voyage|vu|vuelos|wales|walmart|walter|wang|wanggou|watch|watches|weather|weatherchannel|webcam|weber|website|wed|wedding|weibo|weir|wf|whoswho|wien|wiki|williamhill|win|windows|wine|winners|wme|wolterskluwer|woodside|work|works|world|wow|ws|wtc|wtf|xbox|xerox|xfinity|xihuan|xin|xxx|xyz|yachts|yahoo|yamaxun|yandex|ye|yodobashi|yoga|yokohama|you|youtube|yt|yun|za|zappos|zara|zero|zip|zm|zone|zuerich|zw)\b',
|
|
'(^|[^-a-z_.0-9]+)(?<!\S@)([-a-z0-9]+\.)+(com{0,1}|org|net)\.[a-z][a-z]\b',
|
|
'(^|[^-a-z_.0-9]+)(?<!\S@)([-a-z0-9]+\.)+(xn--[a-z0-9]+)\b',
|
|
'\b(bit\.ly|goo\.gl|owl\.ly|deck\.ly|su\.pr|lnk\.co|fur\.ly)/'
|
|
);
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/attack_detection_ignore_errors.php */
|
|
$ATTACK_DETECTION_IGNORE_ERRORS = false;
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/attack_detection_reverse_captcha.php */
|
|
$ATTACK_DETECTION_REVERSE_CAPTCHA = array();
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/geoip_lic.php */
|
|
$GEOIP_LIC = ""; /* default - no GeoIP */
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/zero_is_empty.php */
|
|
$ZERO_IS_EMPTY = false;
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/validate_email_domain.php */
|
|
$VALIDATE_EMAIL_DOMAIN = true;
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/email_validation_max_monitoring_time_secs.php */
|
|
$EMAIL_VALIDATION_MAX_MONITORING_TIME_SECS = 0;
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/alert_on_email_validation_failed.php */
|
|
$ALERT_ON_EMAIL_VALIDATION_FAILED = true;
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/session_name.php */
|
|
$SESSION_NAME = "";
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/session_access.php */
|
|
$SESSION_ACCESS = array();
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/destroy_session.php */
|
|
$DESTROY_SESSION = true;
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/hook_dir.php */
|
|
$HOOK_DIR = "";
|
|
|
|
/* Help: http://www.tectite.com/fmdoc/test_password.php */
|
|
$TEST_PASSWORD = "1";
|
|
|
|
/* UPGRADE CONTROL
|
|
**
|
|
** ATTACK_DETECTION_URL_PATTERNS:lt:9.26:no_keep:The ATTACK_DETECTION_URL_PATTERNS
|
|
** configuration has been modified to recognise all current top level domains.:
|
|
**
|
|
** FILTERS:lt:8.04:merge:The FILTERS configuration has
|
|
** been modified to include some new standard filters.:
|
|
**
|
|
** FILTER_ATTRIBS:lt:8.04:no_keep:The FILTER_ATTRIBS configuration has
|
|
** been modified to include new information about the standard filters.:
|
|
**
|
|
** ATTACK_DETECTION_URL_PATTERNS:eq:8.02:no_keep:The ATTACK_DETECTION_URL_PATTERNS
|
|
** configuration has been modified to fix a bug.:
|
|
**
|
|
** FILTER_ATTRIBS:lt:4.00:no_keep:The FILTER_ATTRIBS configuration has
|
|
** been modified to include new information about the standard filters.:
|
|
**
|
|
** SET_REAL_DOCUMENT_ROOT:gt:4.07:copy_from=REAL_DOCUMENT_ROOT:The
|
|
** REAL_DOCUMENT_ROOT configuration has been renamed to SET_REAL_DOCUMENT_ROOT.:
|
|
**
|
|
** EMAIL_NAME:lt:6.01:no_keep:The EMAIL_NAME configuration has
|
|
** been modified to match hyphens ('-') in email addresses.:
|
|
**
|
|
** ZERO_IS_EMPTY:le:6.01:set_to=true:ZERO_IS_EMPTY has been
|
|
** set to a value that duplicates previous behaviour.:
|
|
**
|
|
** TEXT_SUBS:lt:8.30:no_keep:The TEXT_SUBS configuration has
|
|
** been modified to be secure with new features released in this version.:
|
|
**
|
|
** EMAIL_NAME:lt:9.08:no_keep:The EMAIL_NAME configuration has
|
|
** been modified to match underscores ('_') in email addresses.:
|
|
** END OF CONTROL
|
|
*/
|
|
|
|
/*****************************************************************************/
|
|
/* END OF CONFIGURATION (do not alter this line in any way!!!) */
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* Class to access and modify configuration settings.
|
|
*
|
|
* @author russellr
|
|
*/
|
|
class Settings
|
|
{
|
|
/**
|
|
* Check that the configuration setting exists.
|
|
* If it doesn't exist, then the script dies immediately.
|
|
*
|
|
* @param string $s_name the name of the config setting
|
|
*/
|
|
private static function _check($s_name)
|
|
{
|
|
if (!array_key_exists($s_name,$GLOBALS)) {
|
|
echo '<pre>';
|
|
debug_print_backtrace();
|
|
echo '</pre>';
|
|
FormMailExit("No FormMail setting called '$s_name' exists.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tests if the given config setting is empty.
|
|
*
|
|
* @param string $s_name the name of the config setting
|
|
*
|
|
* @return boolean true if the config setting is empty
|
|
*/
|
|
public static function isEmpty($s_name)
|
|
{
|
|
Settings::_check($s_name);
|
|
if (gettype($GLOBALS[$s_name]) == 'string') {
|
|
return ($GLOBALS[$s_name] === '');
|
|
} else {
|
|
return (empty($GLOBALS[$s_name]));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the value of the given config setting.
|
|
*
|
|
* @param string $s_name the name of the config setting
|
|
*
|
|
* @return mixed the value of the config setting
|
|
*/
|
|
public static function get($s_name)
|
|
{
|
|
Settings::_check($s_name);
|
|
return ($GLOBALS[$s_name]);
|
|
}
|
|
|
|
/**
|
|
* Set the value of a config setting.
|
|
*
|
|
* @param string $s_name the name of the config setting
|
|
* @param mixed $m_value the value to set for the config setting
|
|
*/
|
|
public static function set($s_name,$m_value)
|
|
{
|
|
Settings::_check($s_name);
|
|
if (($s_orig_type = gettype($GLOBALS[$s_name])) != ($s_new_type = gettype($m_value))) {
|
|
echo '<pre>';
|
|
debug_print_backtrace();
|
|
echo '</pre>';
|
|
FormMailExit("You cannot set FormMail setting '$s_name' to type '$s_new_type'. It should be type '$s_orig_type'.");
|
|
}
|
|
$GLOBALS[$s_name] = $m_value;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* For PHP v8 we need to check each include file for existence first.
|
|
*/
|
|
function includeFileExists($file_name)
|
|
{
|
|
return (stream_resolve_include_path($file_name) !== false);
|
|
}
|
|
|
|
function hookFileExists($file_name)
|
|
{
|
|
if (!Settings::isEmpty('HOOK_DIR')) {
|
|
$hook_file = Settings::get('HOOK_DIR') . DIRECTORY_SEPARATOR . $file_name . ".php";
|
|
if (includeFileExists($hook_file)) {
|
|
return ($hook_file);
|
|
}
|
|
$hook_file = Settings::get('HOOK_DIR') . DIRECTORY_SEPARATOR . $file_name;
|
|
if (includeFileExists($hook_file)) {
|
|
return ($hook_file);
|
|
}
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
/*
|
|
* Load an optional include file after the configuration.
|
|
* You can use this to set variables or make adjustments
|
|
* based on the results of the configuration section.
|
|
*/
|
|
if (includeFileExists("formmail-preconfigdefs.inc.php")) {
|
|
@include("formmail-preconfigdefs.inc.php");
|
|
}
|
|
|
|
/*
|
|
* Many configuration settings used to be constants. However, constants
|
|
* cannot be re-defined in PHP. This limitation reduced the value
|
|
* of "formmail - postconfig . inc . php" and other hook scripts.
|
|
* Therefore, all configuration settings have been changed to be global
|
|
* variables (no define's).
|
|
*
|
|
* The following defines are for backward-compatibility with any existing
|
|
* hook scripts that are expecting the old constants.
|
|
*/
|
|
define("EMAIL_NAME",Settings::get("EMAIL_NAME"));
|
|
define("DEF_ALERT",Settings::get("DEF_ALERT"));
|
|
define("AT_MANGLE",Settings::get("AT_MANGLE"));
|
|
define("HEAD_CRLF",Settings::get("HEAD_CRLF"));
|
|
define("BODY_LF",Settings::get("BODY_LF"));
|
|
define("SENDMAIL_F_OPTION",Settings::get("SENDMAIL_F_OPTION"));
|
|
define("SENDMAIL_F_OPTION_LINE",Settings::get("SENDMAIL_F_OPTION_LINE"));
|
|
define("SET_SENDER_FROM_EMAIL",Settings::get("SET_SENDER_FROM_EMAIL"));
|
|
define("INI_SET_FROM",Settings::get("INI_SET_FROM"));
|
|
define("ADVANCED_TEMPLATES",Settings::get("ADVANCED_TEMPLATES"));
|
|
define("LIMITED_IMPORT",Settings::get("LIMITED_IMPORT"));
|
|
define("FILEUPLOADS",Settings::get("FILEUPLOADS"));
|
|
define("MAX_FILE_UPLOAD_SIZE",Settings::get("MAX_FILE_UPLOAD_SIZE"));
|
|
define("FILE_MODE",Settings::get("FILE_MODE"));
|
|
define("FILE_OVERWRITE",Settings::get("FILE_OVERWRITE"));
|
|
define("PUT_DATA_IN_URL",Settings::get("PUT_DATA_IN_URL"));
|
|
define("DB_SEE_INPUT",Settings::get("DB_SEE_INPUT"));
|
|
define("DB_SEE_INI",Settings::get("DB_SEE_INI"));
|
|
define("MAXSTRING",Settings::get("MAXSTRING"));
|
|
define("CHECK_FOR_NEW_VERSION",Settings::get("CHECK_FOR_NEW_VERSION"));
|
|
define("CHECK_DAYS",Settings::get("CHECK_DAYS"));
|
|
define("ALERT_ON_USER_ERROR",Settings::get("ALERT_ON_USER_ERROR"));
|
|
define("ENABLE_ATTACK_DETECTION",Settings::get("ENABLE_ATTACK_DETECTION"));
|
|
define("ATTACK_DETECTION_URL",Settings::get("ATTACK_DETECTION_URL"));
|
|
define("ALERT_ON_ATTACK_DETECTION",Settings::get("ALERT_ON_ATTACK_DETECTION"));
|
|
define("ATTACK_DETECTION_MIME",Settings::get("ATTACK_DETECTION_MIME"));
|
|
define("ATTACK_DETECTION_JUNK",Settings::get("ATTACK_DETECTION_JUNK"));
|
|
define("ATTACK_DETECTION_JUNK_CONSONANTS",Settings::get("ATTACK_DETECTION_JUNK_CONSONANTS"));
|
|
define("ATTACK_DETECTION_JUNK_VOWELS",Settings::get("ATTACK_DETECTION_JUNK_VOWELS"));
|
|
define("ATTACK_DETECTION_JUNK_CONSEC_CONSONANTS",Settings::get("ATTACK_DETECTION_JUNK_CONSEC_CONSONANTS"));
|
|
define("ATTACK_DETECTION_JUNK_CONSEC_VOWELS",Settings::get("ATTACK_DETECTION_JUNK_CONSEC_VOWELS"));
|
|
define("ATTACK_DETECTION_JUNK_TRIGGER",Settings::get("ATTACK_DETECTION_JUNK_TRIGGER"));
|
|
define("ATTACK_DETECTION_SPECIALS",Settings::get("ATTACK_DETECTION_SPECIALS"));
|
|
define("ATTACK_DETECTION_MANY_URLS",Settings::get("ATTACK_DETECTION_MANY_URLS"));
|
|
define("ATTACK_DETECTION_MANY_URL_FIELDS",Settings::get("ATTACK_DETECTION_MANY_URL_FIELDS"));
|
|
define("ATTACK_DETECTION_IGNORE_ERRORS",Settings::get("ATTACK_DETECTION_IGNORE_ERRORS"));
|
|
define("ZERO_IS_EMPTY",Settings::get("ZERO_IS_EMPTY"));
|
|
define("VALIDATE_EMAIL_DOMAIN",Settings::get("VALIDATE_EMAIL_DOMAIN"));
|
|
define("DESTROY_SESSION",Settings::get("DESTROY_SESSION"));
|
|
|
|
//
|
|
// for Ajax allow GET method for cross site JSONP
|
|
//
|
|
if (IsAjax()) {
|
|
Settings::set('ALLOW_GET_METHOD',true);
|
|
}
|
|
|
|
/*
|
|
* Load an optional include file after the configuration.
|
|
* You can use this to set variables or make adjustments
|
|
* based on the results of the configuration section.
|
|
*/
|
|
if (includeFileExists("formmail-postconfig.inc.php")) {
|
|
@include("formmail-postconfig.inc.php");
|
|
}
|
|
|
|
// @formatter:off
|
|
|
|
//
|
|
// the following constants define all FormMail messages
|
|
//
|
|
// To re-align comments, use these search and replace patterns:
|
|
// Search Replace
|
|
// ^(\S{35,39})\s+// $1\t//
|
|
// ^(\S{31,35})\s+// $1\t\t//
|
|
// ^(\S{27,31})\s+// $1\t\t\t//
|
|
// ^(\S{23,27})\s+// $1\t\t\t\t//
|
|
// ^(\S{19,23})\s+// $1\t\t\t\t\t//
|
|
//
|
|
define('MSG_SCRIPT_VERSION',0); // This script requires at least PHP version...
|
|
define('MSG_END_VERS_CHK',1); // If you're happy...
|
|
define('MSG_VERS_CHK',2); // A later version of FormMail is available...
|
|
define('MSG_CHK_FILE_ERROR',3); // Unable to create check file...
|
|
define('MSG_UNK_VALUE_SPEC',4); // derive_fields: unknown value specification...
|
|
define('MSG_INV_VALUE_SPEC',5); // derive_fields: invalid value specification...
|
|
define('MSG_DERIVED_INVALID',6); // Some derive_fields specifications...
|
|
define('MSG_INT_FORM_ERROR',7); // Internal form error...
|
|
define('MSG_OPTIONS_INVALID',8); // Some mail_options settings...
|
|
define('MSG_PLSWAIT_REDIR',9); // Please wait while you are redirected...
|
|
define('MSG_IFNOT_REDIR',10); // If you are not redirected...
|
|
define('MSG_PEAR_OBJ',11); // Failed to create PEAR Mail object...
|
|
define('MSG_PEAR_ERROR',12); // PEAR Mail error...
|
|
define('MSG_NO_FOPT_ADDR',13); // You have specified "SendMailFOption"...
|
|
define('MSG_MORE_INFO',14); // More information...
|
|
define('MSG_INFO_STOPPED',15); // Extra alert information suppressed...
|
|
define('MSG_FM_ALERT',16); // FormMail alert
|
|
define('MSG_FM_ERROR',17); // FormMail script error
|
|
define('MSG_FM_ERROR_LINE',18); // The following error occurred...
|
|
define('MSG_USERDATA_STOPPED',19); // User data suppressed...
|
|
define('MSG_FILTERED',20); // This alert has been filtered...
|
|
define('MSG_TEMPLATES',21); // You must set either TEMPLATEDIR or TEMPLATEURL...
|
|
define('MSG_OPEN_TEMPLATE',22); // Failed to open template...
|
|
define('MSG_ERROR_PROC',23); // An error occurred while processing...
|
|
define('MSG_ALERT_DONE',24); // Our staff have been alerted...
|
|
define('MSG_PLS_CONTACT',25); // Please contact us directly...
|
|
define('MSG_APOLOGY',26); // We apologize for any inconvenience...
|
|
define('MSG_ABOUT_FORMMAIL',27); // Your form submission was processed by...
|
|
define('MSG_PREG_FAILED',28); // preg_match_all failed in FindCRMFields...
|
|
define('MSG_URL_INVALID',29); // CRM URL "$URL" is not valid...
|
|
define('MSG_URL_OPEN',30); // Failed to open Customer Relationship...
|
|
define('MSG_CRM_FAILED',31); // Failure report from CRM...
|
|
define('MSG_CRM_FORM_ERROR',32); // Your form submission was not...
|
|
define('MSG_OR',33); // "$ITEM1" or "$ITEM2"
|
|
define('MSG_NOT_BOTH',34); // not both "$ITEM1" and "$ITEM2"
|
|
define('MSG_XOR',35); // "$ITEM1" or "$ITEM2" (but not both)
|
|
define('MSG_IS_SAME_AS',36); // "$ITEM1" is the same as "$ITEM2"
|
|
define('MSG_IS_NOT_SAME_AS',37); // "$ITEM1" is not the same as "$ITEM2"
|
|
define('MSG_REQD_OPER',38); // Operator "$OPER" is not valid for "required"
|
|
define('MSG_PAT_FAILED',39); // Pattern operator "$OPER" failed: pattern...
|
|
define('MSG_COND_OPER',40); // Operator "$OPER" is not valid...
|
|
define('MSG_INV_COND',41); // Invalid "conditions" field...
|
|
define('MSG_COND_CHARS',42); // The conditions field "$FLD" is not valid...
|
|
define('MSG_COND_INVALID',43); // The conditions field "$FLD" is not valid...
|
|
define('MSG_COND_TEST_LONG',44); // Field "$FLD" has too many components...
|
|
define('MSG_COND_IF_SHORT',45); // Field "$FLD" has too few components for...
|
|
define('MSG_COND_IF_LONG',46); // Field "$FLD" has too many components for...
|
|
define('MSG_COND_UNK',47); // Field "$FLD" has an unknown command word...
|
|
define('MSG_MISSING',48); // Missing "$ITEM"...
|
|
define('MSG_NEED_ARRAY',49); // "$ITEM" must be an array...
|
|
define('MSG_SUBM_FAILED',50); // Your form submission has failed...
|
|
define('MSG_FILTER_WRONG',51); // Filter "$FILTER" is not properly...
|
|
define('MSG_FILTER_CONNECT',52); // Could not connect to site "$SITE"...
|
|
define('MSG_FILTER_PARAM',53); // Filter "$FILTER" has invalid parameter...
|
|
define('MSG_FILTER_OPEN_FILE',54); // Filter "$FILTER" cannot open file...
|
|
define('MSG_FILTER_FILE_ERROR',55); // Filter "$FILTER": read error on file...
|
|
define('MSG_FILTER_READ_ERROR',56); // Filter '$filter' failed: read error...
|
|
define('MSG_FILTER_NOT_OK',57); // Filter 'FILTER' failed...
|
|
define('MSG_FILTER_UNK',58); // Unknown filter...
|
|
define('MSG_FILTER_CHDIR',59); // Cannot chdir...
|
|
define('MSG_FILTER_NOTFOUND',60); // Cannot execute...
|
|
define('MSG_FILTER_ERROR',61); // Filter "$FILTER" failed...
|
|
define('MSG_SPARE',62); // this value is now spare
|
|
define('MSG_TEMPLATE_ERRORS',63); // Template "$NAME" caused the...
|
|
define('MSG_TEMPLATE_FAILED',64); // Failed to process template "$NAME"...
|
|
define('MSG_MIME_PREAMBLE',65); // (Your mail reader should not show this...
|
|
define('MSG_MIME_HTML',66); // This message has been generated by FormMail...
|
|
define('MSG_FILE_OPEN_ERROR',67); // Failed to open file "$NAME"...
|
|
define('MSG_ATTACH_DATA',68); // Internal error: AttachFile requires...
|
|
define('MSG_PHP_HTML_TEMPLATES',69); // HTMLTemplate option is only ...
|
|
define('MSG_PHP_FILE_UPLOADS',70); // For security reasons, file upload...
|
|
define('MSG_FILE_UPLOAD',71); // File upload attempt ignored...
|
|
define('MSG_FILE_UPLOAD_ATTACK',72); // Possible file upload attack...
|
|
define('MSG_PHP_PLAIN_TEMPLATES',73); // PlainTemplate option is only...
|
|
define('MSG_ATTACH_NAME',74); // filter_options: Attach must contain a name...
|
|
define('MSG_PHP_BCC',75); // Warning: BCC is probably not supported...
|
|
define('MSG_CSVCOLUMNS',76); // The "csvcolumns" setting is not...
|
|
define('MSG_CSVFILE',77); // The "csvfile" setting is not...
|
|
define('MSG_TARG_EMAIL_PAT_START',78); // Warning: Your TARGET_EMAIL pattern...
|
|
define('MSG_TARG_EMAIL_PAT_END',79); // Warning: Your TARGET_EMAIL pattern...
|
|
define('MSG_CONFIG_WARN',80); // The following potential problems...
|
|
define('MSG_PHP_AUTORESP',81); // Autorespond is only supported...
|
|
define('MSG_ALERT',82); // This is a test alert message...
|
|
define('MSG_NO_DEF_ALERT',83); // No DEF_ALERT value has been set....
|
|
define('MSG_TEST_SENT',84); // Test message sent. Check your email.....
|
|
define('MSG_TEST_FAILED',85); // FAILED to send alert message...
|
|
define('MSG_NO_DATA_PAGE',86); // This URL is a Form submission program...
|
|
define('MSG_REQD_ERROR',87); // The form required some values that you...
|
|
define('MSG_COND_ERROR',88); // Some of the values you provided...
|
|
define('MSG_CRM_FAILURE',89); // The form submission did not succeed...
|
|
define('MSG_FOPTION_WARN',90); // Warning: You've used SendMailFOption in...
|
|
define('MSG_NO_ACTIONS',91); // The form has an internal error...
|
|
define('MSG_NO_RECIP',92); // The form has an internal error...
|
|
define('MSG_INV_EMAIL',93); // Invalid email addresses...
|
|
define('MSG_FAILED_SEND',94); // Failed to send email...
|
|
define('MSG_ARESP_EMAIL',96); // No "email" field was found. Autorespond...
|
|
define('MSG_ARESP_SUBJ',97); // Your form submission...
|
|
define('MSG_LOG_NO_VERIMG',98); // No VerifyImgString in session...
|
|
define('MSG_ARESP_NO_AUTH',99); // Failed to obtain authorization...
|
|
define('MSG_LOG_NO_MATCH',100); // User did not match image...
|
|
define('MSG_ARESP_NO_MATCH',101); // Your entry did not match...
|
|
define('MSG_LOG_FAILED',102); // Failed
|
|
define('MSG_ARESP_FAILED',103); // Autoresponder failed
|
|
define('MSG_LOG_OK',104); // OK
|
|
define('MSG_THANKS_PAGE',105); // Thanks! We've received your....
|
|
define('MSG_LOAD_MODULE',106); // Cannot load module....
|
|
define('MSG_LOAD_FMCOMPUTE',107); // Cannot load FMCompute....
|
|
define('MSG_REGISTER_MODULE',108); // Cannot register module....
|
|
define('MSG_COMP_PARSE',109); // These parse errors occurred....
|
|
define('MSG_COMP_REG_DATA',110); // Failed to register data field....
|
|
define('MSG_COMP_ALERT',111); // The following alert messages....
|
|
define('MSG_COMP_DEBUG',112); // The following debug messages...
|
|
define('MSG_COMP_EXEC',113); // The following errors occurred....
|
|
define('MSG_REG_FMCOMPUTE',114); // Cannot register function...
|
|
define('MSG_USER_ERRORS',115); // A number of errors occurred...
|
|
define('MSG_CALL_PARAM_COUNT',116); // Invalid parameter count...
|
|
define('MSG_CALL_UNK_FUNC',117); // Unknown function...
|
|
define('MSG_SAVE_FILE',118); // Failed to save file....
|
|
define('MSG_CHMOD',119); // Failed to chmod file....
|
|
define('MSG_VERIFY_MISSING',120); // Image verification string missing...
|
|
define('MSG_VERIFY_MATCH',121); // Your entry did not match...
|
|
define('MSG_FILE_NAMES_INVALID',122); // Some file_names specifications...
|
|
define('MSG_FILE_NAMES_NOT_FILE',123); // Your file_names specification...
|
|
define('MSG_TEMPL_ALERT',124); // The following alert messages....
|
|
define('MSG_TEMPL_DEBUG',125); // The following debug messages...
|
|
define('MSG_TEMPL_PROC',126); // The following errors occurred....
|
|
define('MSG_SAVE_FILE_EXISTS',127); // Cannot save file....
|
|
define('MSG_EMPTY_ADDRESSES',128); // $COUNT empty addresses
|
|
define('MSG_CALL_INVALID_PARAM',129); // Invalid parameter....
|
|
define('MSG_INI_PARSE_WARN',130); // Warning: your INI
|
|
define('MSG_INI_PARSE_ERROR',131); // The FormMail INI...
|
|
define('MSG_RECAPTCHA_MATCH',132); // reCaptcha verification failed...
|
|
|
|
define('MSG_AND',133); // "$ITEM1" and "$ITEM2"
|
|
define('MSG_NEXT_PLUS_GOOD',134); // The form specifies both next_form and....
|
|
define('MSG_MULTIFORM',135); // You must set either MULTIFORMDIR or MULTIFORMURL...
|
|
define('MSG_MULTIFORM_FAILED',136); // Failed to process multi-page form template "$NAME"...
|
|
define('MSG_NEED_THIS_FORM',137); // Multi-page forms require "this_form" field...
|
|
define('MSG_NO_PHP_SELF',138); // PHP on the server is not providing "PHP_SELF"
|
|
define('MSG_RETURN_URL_INVALID',139); // Return "$URL" is not valid...
|
|
define('MSG_GO_BACK',140); // Cannot 'go back' if not a multi-page form...
|
|
define('MSG_OPEN_URL',141); // Cannot open URL...
|
|
define('MSG_CANNOT_RETURN',142); // Cannot return to page....
|
|
define('MSG_ATTACK_DETECTED',143); // Server attack detected....
|
|
define('MSG_ATTACK_PAGE',144); // Your form submission....
|
|
define('MSG_ATTACK_MIME_INFO',145); // The field "$FLD" contained...
|
|
define('MSG_ATTACK_DUP_INFO',146); // The fields "$FLD1" and...
|
|
define('MSG_ATTACK_SPEC_INFO',147); // Special field "$FLD"...
|
|
define('MSG_NEED_SCRATCH_PAD',148); // You need to set SCRATCH_PAD...
|
|
define('MSG_MULTI_UPLOAD',149); // File upload processing failed during multi-page form processing.
|
|
define('MSG_OPEN_SCRATCH_PAD',150); // Cannot open directory...
|
|
define('MSG_NO_NEXT_NUM_FILE',151); // You cannot use the %nextnum% feature...
|
|
define('MSG_NEXT_NUM_FILE',152); // Cannot process next number...
|
|
define('MSG_ATTACK_MANYURL_INFO',153); // Field "$FLD"...
|
|
define('MSG_ATTACK_MANYFIELDS_INFO',154); // $NUM fields have URLs....
|
|
define('MSG_REV_CAP',155); // ATTACK_DETECTION_REVERSE_CAPTCHA setting....
|
|
define('MSG_ATTACK_REV_CAP_INFO',156); // The field "$FLD" contained...
|
|
define('MSG_ATTACK_JUNK_INFO',157); // The field "$FLD" contained...
|
|
define('MSG_ARESP_EMPTY',158); // The autoresponse...
|
|
define('MSG_LOG_RECAPTCHA',159); // reCaptcha process failed...
|
|
|
|
define('MSG_URL_PARSE',160); // URL parse failed
|
|
define('MSG_URL_SCHEME',161); // Unsupported URL scheme...
|
|
define('MSG_SOCKET',162); // Socket error ...
|
|
define('MSG_GETURL_OPEN',163); // Open URL failed: ...
|
|
define('MSG_RESOLVE',164); // Cannot resolve...
|
|
|
|
define('MSG_FORM_OK',170); // Form Submission Succeeded
|
|
define('MSG_FORM_ERROR',171); // Form Submission Error
|
|
define('MSG_GET_DISALLOWED',172); // GET method has...
|
|
define('MSG_INVALID_SENDER',173); // The form has specified an invalid sender
|
|
define('MSG_SET_SENDER_FROM_EMAIL',174); // SET_SENDER_FROM_EMAIL is no longer supported
|
|
//
|
|
// The following are PHP's file upload error messages
|
|
//
|
|
define('MSG_FILE_UPLOAD_ERR_UNK',180); // Unknown error code.
|
|
define('MSG_FILE_UPLOAD_ERR1',181); // The uploaded file exceeds the upload_max_filesize directive in php.ini.
|
|
define('MSG_FILE_UPLOAD_ERR2',182); // The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the html form.
|
|
define('MSG_FILE_UPLOAD_ERR3',183); // The uploaded file was only partially uploaded.
|
|
define('MSG_FILE_UPLOAD_ERR4',184); // No file was uploaded.
|
|
define('MSG_FILE_UPLOAD_ERR6',186); // Missing a temporary folder.
|
|
define('MSG_FILE_UPLOAD_ERR7',187); // Failed to write file to disk.
|
|
define('MSG_FILE_UPLOAD_ERR8',188); // File upload stopped by extension.
|
|
define('MSG_FILE_UPLOAD_SIZE',189); // Uploaded file "$NAME" is too big... (not a PHP error code - internal maximum file size error)
|
|
define('MSG_POST_SIZE_LIMIT',190); // Your form submission exceeds the server's configured size limit.
|
|
//
|
|
// following are for derive_fields functions
|
|
//
|
|
define('MSG_DER_FUNC_ERROR',200); // derive_fields: invalid function....
|
|
define('MSG_DER_FUNC_SIZE_FMT',201); // function 'size' requires....
|
|
define('MSG_DER_FUNC_IF_FMT',202); // function 'if' requires....
|
|
define('MSG_DER_FUNC_NEXTNUM_FMT',203); // function 'nextnum' requires....
|
|
define('MSG_DER_FUNC_EXT_FMT',204); // function 'ext' requires....
|
|
define('MSG_DER_FUNC1_FMT',205); // function 'FUNC' requires....
|
|
define('MSG_DER_FUNC_SUBSTR_FMT',206); // function 'substr' requires....
|
|
|
|
define('MSG_USER_ATTACK_JUNK',220); // The following input ...
|
|
define('MSG_USER_ATTACK_REV_CAP',221); // Your input ...
|
|
define('MSG_USER_ATTACK_DUP',222); // You have ...
|
|
define('MSG_USER_ATTACK_MANY_URLS',223); // Your input ...
|
|
define('MSG_USER_ATTACK_MANY_URL_FIELDS',224); // Your input ...
|
|
|
|
define('MSG_INVALID_EMAIL',230); // The email address...is invalid
|
|
|
|
//
|
|
// The following messages are no longer used
|
|
//
|
|
define('MSG_FLD_NOTFOUND', 900);
|
|
|
|
|
|
// @formatter:on
|
|
// <A NAME="MessageNumbers"> Jump to: <A HREF="#BuiltinMessages">
|
|
|
|
//
|
|
// Return true if using the built-in language
|
|
//
|
|
/** @noinspection PhpUnused */
|
|
function IsBuiltInLanguage()
|
|
{
|
|
global $sLangID;
|
|
|
|
return (strpos($sLangID,"builtin") !== false);
|
|
}
|
|
|
|
$sSavePath = "";
|
|
$bPathSaved = false;
|
|
//
|
|
// Set include path to include the given directory.
|
|
//
|
|
function AddIncludePath($s_dir = ".")
|
|
{
|
|
global $sSavePath,$bPathSaved;
|
|
|
|
$s_path = ini_get('include_path');
|
|
$i_path_len = strlen($s_path);
|
|
$s_sep = IsServerWindows() ? ";" : ":"; // get path separator
|
|
//
|
|
// look for it in the include_path
|
|
//
|
|
$b_found = false;
|
|
$i_pos = 0;
|
|
$i_len = strlen($s_dir);
|
|
while (!$b_found && ($i_pos = strpos($s_path,$s_dir,$i_pos)) !== false) {
|
|
if ($i_pos == 0) {
|
|
if ($i_len == $i_path_len) {
|
|
$b_found = true;
|
|
} // the path only has $s_dir
|
|
elseif ($s_path[$i_len] == $s_sep) {
|
|
$b_found = true;
|
|
}
|
|
} elseif ($s_path[$i_pos - 1] == $s_sep &&
|
|
($i_pos + $i_len == $i_path_len ||
|
|
$s_path[$i_pos + $i_len] == $s_sep)
|
|
) {
|
|
$b_found = true;
|
|
}
|
|
if (!$b_found) {
|
|
$i_pos++;
|
|
}
|
|
}
|
|
if (!$b_found) {
|
|
//
|
|
// allow multiple calls, but only store the original path once
|
|
//
|
|
if (!$bPathSaved) {
|
|
$sSavePath = $s_path;
|
|
}
|
|
if (empty($s_path)) {
|
|
$s_path = $s_dir;
|
|
} else
|
|
//
|
|
// prepend the directory
|
|
//
|
|
{
|
|
$s_path = $s_dir . $s_sep . $s_path;
|
|
}
|
|
ini_set('include_path',$s_path);
|
|
$bPathSaved = true;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reset the include path after a call to AddIncludePath.
|
|
//
|
|
function ResetIncludePath()
|
|
{
|
|
global $sSavePath,$bPathSaved;
|
|
|
|
if ($bPathSaved) {
|
|
ini_set('include_path',$sSavePath);
|
|
$bPathSaved = false;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Load a language file
|
|
//
|
|
function LoadLanguageFile()
|
|
{
|
|
global $aMessages,$sLangID,$sHTMLCharSet;
|
|
|
|
AddIncludePath();
|
|
if (includeFileExists("language.inc.php")) {
|
|
include("language.inc.php");
|
|
} else {
|
|
if (includeFileExists("language.inc")) {
|
|
include("language.inc");
|
|
}
|
|
}
|
|
|
|
ResetIncludePath();
|
|
if (isset($sHTMLCharSet) && $sHTMLCharSet !== "") {
|
|
header("Content-Type: text/html; charset=$sHTMLCharSet");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Load the messages array from the default language, and then
|
|
// override with an optional language file.
|
|
// Note: all messages get the MNUM parameter sent which they can use.
|
|
// If they don't use it, the message number is appended.
|
|
//
|
|
function LoadBuiltinLanguage()
|
|
{
|
|
global $aMessages,$sLangID;
|
|
|
|
$sLangID = "English (builtin)";
|
|
// MSG_SCRIPT_VERSION is shown if the PHP version is too old to run
|
|
// FormMail
|
|
// Parameters:
|
|
// $PHPREQ is the minimum required PHP version
|
|
// $PHPVERS is the version the server currently has installed.
|
|
$aMessages[MSG_SCRIPT_VERSION] = 'This script requires at least PHP version ' .
|
|
'$PHPREQ. You have PHP version $PHPVERS.';
|
|
|
|
// MSG_END_VERS_CHK is sent at the end of an Alert message when
|
|
// FormMail detects that there's a newer version available
|
|
// Parameters: none
|
|
$aMessages[MSG_END_VERS_CHK] = '***************************************************\n' .
|
|
'If you are happy with your current version and want\n' .
|
|
'to stop these reminders, edit formmail.php and\n' .
|
|
'set CHECK_FOR_NEW_VERSION to false.\n' .
|
|
'***************************************************\n';
|
|
|
|
// MSG_VERS_CHK is sent in an Alert message when
|
|
// FormMail detects that there's a newer version available
|
|
// Parameters:
|
|
// $TECTITE the website to go to
|
|
// $FM_VERS the current FormMail version
|
|
// $NEWVERS the new FormMail version that's available
|
|
$aMessages[MSG_VERS_CHK] = 'A later version of FormMail is available from $TECTITE.\n' .
|
|
'You are currently using version $FM_VERS.\n' .
|
|
'The new version available is $NEWVERS.\n';
|
|
|
|
// MSG_CHK_FILE_ERROR is sent in an Alert message when
|
|
// FormMail cannot create a file to record the time of version check.
|
|
// Parameters:
|
|
// $FILE the file name that could not be created
|
|
// $ERROR the actual error message
|
|
$aMessages[MSG_CHK_FILE_ERROR] = 'Unable to create check file "$FILE": $ERROR';
|
|
|
|
// MSG_UNK_VALUE_SPEC is sent in an Alert message when
|
|
// a form uses an unknown value specification in derive_fields.
|
|
// Parameters:
|
|
// $SPEC the unknown value specification
|
|
// $MSG additional message
|
|
$aMessages[MSG_UNK_VALUE_SPEC] = 'derive_fields: unknown value specification ' .
|
|
'"$SPEC"$MSG';
|
|
|
|
// MSG_INV_VALUE_SPEC is sent in an Alert message when
|
|
// a form uses a value specification in derive_fields that's
|
|
// formatted incorrectly (missing terminating '%')
|
|
// Parameters:
|
|
// $SPEC the invalid value specification
|
|
$aMessages[MSG_INV_VALUE_SPEC] = 'derive_fields: invalid value specification ' .
|
|
'"$SPEC" (possibly missing a "%")';
|
|
|
|
// MSG_DERIVED_INVALID is sent in an Alert message when
|
|
// a form's derive_fields setting has errors
|
|
// Parameters: none
|
|
// A list of errors is appended on separate lines
|
|
$aMessages[MSG_DERIVED_INVALID] = 'Some derive_fields specifications are invalid $MNUM:\n';
|
|
|
|
// MSG_INT_FORM_ERROR is sent in an Alert message and displayed
|
|
// to the form user
|
|
// Parameters: none
|
|
$aMessages[MSG_INT_FORM_ERROR] = 'Internal form error';
|
|
|
|
// MSG_OPTIONS_INVALID is sent in an Alert message when
|
|
// a form's options settings are invalid. This applies to
|
|
// mail_options, filter_options, crm_options, and autorespond
|
|
// Parameters:
|
|
// $OPT the name of the options field
|
|
// A list of errors is appended on separate lines
|
|
$aMessages[MSG_OPTIONS_INVALID] = 'Some $OPT settings are undefined $MNUM:\n';
|
|
|
|
// MSG_PLSWAIT_REDIR is shown to the user for a redirect
|
|
// with JavaScript
|
|
// Parameters: none
|
|
$aMessages[MSG_PLSWAIT_REDIR] = 'Please wait while you are redirected...';
|
|
|
|
// MSG_IFNOT_REDIR is shown to the user for a redirect
|
|
// with JavaScript
|
|
// Parameters:
|
|
// $URL the URL to redirect to
|
|
$aMessages[MSG_IFNOT_REDIR] = 'If you are not automatically redirected, ' .
|
|
'please <a href="$URL">click here</a>.';
|
|
|
|
// MSG_PEAR_OBJ is shown to the user if the PEAR Mail object
|
|
// cannot be created
|
|
// Parameters: none
|
|
$aMessages[MSG_PEAR_OBJ] = 'Failed to create PEAR Mail object';
|
|
|
|
// MSG_PEAR_ERROR is sent in an Alert message if the PEAR Mail processing
|
|
// reports an error
|
|
// Parameters:
|
|
// $MSG the error message from PEAR
|
|
$aMessages[MSG_PEAR_ERROR] = 'PEAR Mail error: $MSG';
|
|
|
|
// MSG_NO_FOPT_ADDR is sent in an Alert message SendMailFOption is
|
|
// specified in the form and no email address has been provided
|
|
// Parameters: none
|
|
$aMessages[MSG_NO_FOPT_ADDR] = 'You have specified "SendMailFOption" in your ' .
|
|
'form, but there is no email address to use';
|
|
|
|
// MSG_MORE_INFO is sent in an Alert message on a line by itself, just
|
|
// before extra information about the FormMail processing that may have
|
|
// led to the alert message
|
|
// Parameters: none
|
|
$aMessages[MSG_MORE_INFO] = 'More information:';
|
|
|
|
// MSG_INFO_STOPPED is sent in an Alert message to say that extra
|
|
// alert information has been suppressed because of potential security
|
|
// problems with showing it.
|
|
// Parameters: none
|
|
$aMessages[MSG_INFO_STOPPED] = '(Extra alert information suppressed for ' .
|
|
'security purposes. $MNUM)';
|
|
|
|
// MSG_FM_ALERT is sent as the subject line of an Alert message
|
|
// Parameters: none
|
|
$aMessages[MSG_FM_ALERT] = 'FormMail alert';
|
|
|
|
// MSG_FM_ERROR is sent as the subject line of an Alert message
|
|
// Parameters: none
|
|
$aMessages[MSG_FM_ERROR] = 'FormMail script error';
|
|
|
|
// MSG_FM_ERROR_LINE is sent in an Alert message on a
|
|
// separate line to introduce the actual error message
|
|
// Parameters: none
|
|
$aMessages[MSG_FM_ERROR_LINE] = 'The following error occurred in FormMail $MNUM:';
|
|
|
|
// MSG_USERDATA_STOPPED is sent in an Alert message to say that the
|
|
// user's data has been suppressed because of potential security
|
|
// problems with showing it.
|
|
// Parameters: none
|
|
$aMessages[MSG_USERDATA_STOPPED] = '(User data suppressed for security ' .
|
|
'purposes. $MNUM)';
|
|
|
|
// MSG_FILTERED is sent in an Alert message to show what filter
|
|
// has been used on the message
|
|
// Parameters:
|
|
// $FILTER the name of the filter
|
|
$aMessages[MSG_FILTERED] = 'This alert has been filtered through "$FILTER" ' .
|
|
'for security purposes.';
|
|
|
|
// MSG_TEMPLATES is sent in an Alert message when a form tries
|
|
// to use a template, but templates have not been configured in
|
|
// formmail.php
|
|
// Parameters: none
|
|
$aMessages[MSG_TEMPLATES] = 'You must set either TEMPLATEDIR or TEMPLATEURL ' .
|
|
'in formmail.php before you can specify ' .
|
|
'templates in your forms.';
|
|
|
|
// MSG_OPEN_TEMPLATE is sent in an Alert message when FormMail cannot
|
|
// open a template file
|
|
// Parameters:
|
|
// $NAME the name of the template file
|
|
// $ERROR information about the error
|
|
$aMessages[MSG_OPEN_TEMPLATE] = 'Failed to open template "$NAME" $MNUM: $ERROR';
|
|
|
|
// MSG_ERROR_PROC is shown to the user as part of an error
|
|
// page. This message introduces the error.
|
|
// Parameters: none
|
|
$aMessages[MSG_ERROR_PROC] = 'An error occurred while processing the ' .
|
|
'form $MNUM.\n\n';
|
|
|
|
// MSG_ALERT_DONE is shown to the user as part of an error
|
|
// page if an Alert message has been sent to the website owner.
|
|
// Parameters:
|
|
// SERVER the name of the server (website)
|
|
$aMessages[MSG_ALERT_DONE] = 'The staff at $SERVER have been alerted to the error $MNUM.\n';
|
|
|
|
// MSG_PLS_CONTACT is shown to the user as part of an error
|
|
// page if an Alert message could *not* be sent to the website owner.
|
|
// Parameters:
|
|
// SERVER the name of the server (website)
|
|
$aMessages[MSG_PLS_CONTACT] = 'Please contact us ($SERVER) directly since this form ' .
|
|
'is not working $MNUM.\n';
|
|
|
|
// MSG_APOLOGY is shown to the user as part of an error
|
|
// page as an apology for a problem with the form.
|
|
// Parameters:
|
|
// SERVER the name of the server (website)
|
|
$aMessages[MSG_APOLOGY] = '$SERVER apologizes for any inconvenience this error ' .
|
|
'may have caused.';
|
|
|
|
// MSG_ABOUT_FORMMAIL is shown to the user at the foot of pages
|
|
// generated by FormMail (e.g. the default "Thanks" page and default
|
|
// error page).
|
|
// Parameters:
|
|
// $FM_VERS the FormMail version number
|
|
// $TECTITE www.tectite.com
|
|
$aMessages[MSG_ABOUT_FORMMAIL] = 'Your form submission was processed by ' .
|
|
'<a href="http://$TECTITE/">FormMail</a> ' .
|
|
'($FM_VERS), a PHP script available from ' .
|
|
'<a href="http://$TECTITE/">$TECTITE</a>.';
|
|
|
|
// MSG_PREG_FAILED is sent in an Alert message if the TectiteCRM
|
|
// system failed to return the expected result.
|
|
// Parameters: none
|
|
$aMessages[MSG_PREG_FAILED] = 'preg_match_all failed in FindCRMFields';
|
|
|
|
// MSG_URL_INVALID is sent in an Alert message if the specified
|
|
// URL for TectiteCRM is not valid according to the TARGET_URLS
|
|
// configuration setting
|
|
// Parameters:
|
|
// $URL the invalid URL
|
|
$aMessages[MSG_URL_INVALID] = 'The URL "$URL" to access the Customer ' .
|
|
'Relationship Management System is not valid ' .
|
|
'(see TARGET_URLS in formmail.php)';
|
|
|
|
// MSG_URL_OPEN is sent in an Alert message if the specified
|
|
// URL for TectiteCRM cannot be opened
|
|
// Parameters:
|
|
// $URL the invalid URL
|
|
// $ERROR information about the error
|
|
$aMessages[MSG_URL_OPEN] = 'Failed to open Customer Relationship ' .
|
|
'Management System URL "$URL" $MNUM: $ERROR';
|
|
|
|
// MSG_CRM_FAILED is sent in an Alert message if the TectiteCRM
|
|
// system doesn't return an OK message
|
|
// Parameters:
|
|
// $URL the invalid URL
|
|
// $MSG more information
|
|
$aMessages[MSG_CRM_FAILED] = 'Failure report from Customer Relationship ' .
|
|
'Management System (url="$URL") $MNUM: $MSG';
|
|
|
|
// MSG_CRM_FORM_ERROR is shown to the user if the information
|
|
// passed to TectiteCRM was not accepted
|
|
// Parameters: none
|
|
$aMessages[MSG_CRM_FORM_ERROR] = 'Your form submission was not accepted';
|
|
|
|
// MSG_AND is shown to the user; it shows two items separated
|
|
// by "and"
|
|
// Parameters:
|
|
// $ITEM1 the first item
|
|
// $ITEM2 the second item
|
|
$aMessages[MSG_AND] = '"$ITEM1" and "$ITEM2"';
|
|
|
|
// MSG_OR is shown to the user; it shows two items separated
|
|
// by "or"
|
|
// Parameters:
|
|
// $ITEM1 the first item
|
|
// $ITEM2 the second item
|
|
$aMessages[MSG_OR] = '"$ITEM1" or "$ITEM2"';
|
|
|
|
// MSG_NOT_BOTH is shown to the user; it shows two items that must
|
|
// be specified together
|
|
// Parameters:
|
|
// $ITEM1 the first item
|
|
// $ITEM2 the second item
|
|
$aMessages[MSG_NOT_BOTH] = 'not both "$ITEM1" and "$ITEM2"';
|
|
|
|
// MSG_XOR is shown to the user; it shows two items that must
|
|
// not be specified together
|
|
// Parameters:
|
|
// $ITEM1 the first item
|
|
// $ITEM2 the second item
|
|
$aMessages[MSG_XOR] = '"$ITEM1" or "$ITEM2" (but not both)';
|
|
|
|
// MSG_IS_SAME_AS is shown to the user; it shows two items that must
|
|
// not be the same value
|
|
// Parameters:
|
|
// $ITEM1 the first item
|
|
// $ITEM2 the second item
|
|
$aMessages[MSG_IS_SAME_AS] = '"$ITEM1" is the same as "$ITEM2"';
|
|
|
|
// MSG_IS_NOT_SAME_AS is shown to the user; it shows two items that must
|
|
// be the same value
|
|
// Parameters:
|
|
// $ITEM1 the first item
|
|
// $ITEM2 the second item
|
|
$aMessages[MSG_IS_NOT_SAME_AS] = '"$ITEM1" is not the same as "$ITEM2"';
|
|
|
|
// MSG_REQD_OPER is sent in an Alert message when an unknown
|
|
// operator has been used in a "required" specification
|
|
// Parameters:
|
|
// $OPER the unknown operator
|
|
$aMessages[MSG_REQD_OPER] = 'Operator "$OPER" is not valid for "required"';
|
|
|
|
// MSG_PAT_FAILED is sent in an Alert message when a "conditions" pattern
|
|
// match has not matched anything (this isn't necessarily an error)
|
|
// Parameters:
|
|
// $OPER the "conditions" operator
|
|
// $PAT the "conditions" pattern
|
|
// $VALUE the value that was searched
|
|
$aMessages[MSG_PAT_FAILED] = 'Pattern operator "$OPER" failed: pattern ' .
|
|
'"$PAT", value searched was "$VALUE".';
|
|
|
|
// MSG_COND_OPER is sent in an Alert message when a "conditions"
|
|
// operator is not value
|
|
// Parameters:
|
|
// $OPER the "conditions" operator
|
|
$aMessages[MSG_COND_OPER] = 'Operator "$OPER" is not valid for "conditions"';
|
|
|
|
// MSG_INV_COND is sent in an Alert message when a "conditions"
|
|
// field is not valid
|
|
// Parameters:
|
|
// FLD the field name
|
|
$aMessages[MSG_INV_COND] = 'Invalid "conditions" field "$FLD" - not a string or array.';
|
|
|
|
// MSG_COND_CHARS is sent in an Alert message when a "conditions"
|
|
// field is missing the mandatory first 2 characters (the separators)
|
|
// Parameters:
|
|
// FLD the field name
|
|
// COND the conditions field value
|
|
$aMessages[MSG_COND_CHARS] = 'The conditions field "$FLD" is not valid. ' .
|
|
'You must provide the two separator ' .
|
|
'characters at the beginning. You had "$COND".';
|
|
|
|
// MSG_COND_INVALID is sent in an Alert message when a "conditions"
|
|
// field has the wrong format
|
|
// Parameters:
|
|
// FLD the field name
|
|
// COND the conditions field value
|
|
// SEP the internal separator character for the field.
|
|
$aMessages[MSG_COND_INVALID] = 'The conditions field "$FLD" is not valid. ' .
|
|
'There must be at least 5 components ' .
|
|
'separated by "$SEP". Your value was "$COND".';
|
|
|
|
// MSG_COND_TEST_LONG is sent in an Alert message when a "conditions"
|
|
// TEST value has too many components
|
|
// Parameters:
|
|
// FLD the field name
|
|
// COND the conditions field value
|
|
// SEP the list separator character for the field.
|
|
$aMessages[MSG_COND_TEST_LONG] = 'Field "$FLD" has too many components for ' .
|
|
'a "TEST" command: "$COND".\nAre you missing ' .
|
|
'a "$SEP"?';
|
|
|
|
// MSG_COND_IF_SHORT is sent in an Alert message when a "conditions"
|
|
// IF value has too few components
|
|
// Parameters:
|
|
// FLD the field name
|
|
// COND the conditions field value
|
|
// SEP the internal separator character for the field.
|
|
$aMessages[MSG_COND_IF_SHORT] = 'Field "$FLD" has too few components for ' .
|
|
'an "IF" command: "$COND".\nThere must be ' .
|
|
'at least 6 components separated by "$SEP"';
|
|
|
|
// MSG_COND_IF_LONG is sent in an Alert message when a "conditions"
|
|
// IF value has too many components
|
|
// Parameters:
|
|
// FLD the field name
|
|
// COND the conditions field value
|
|
// SEP the list separator character for the field.
|
|
$aMessages[MSG_COND_IF_LONG] = 'Field "$FLD" has too many components for ' .
|
|
'an "IF" command: "$COND".\nAre you missing ' .
|
|
'a "$SEP"?';
|
|
|
|
// MSG_COND_UNK is sent in an Alert message when a "conditions"
|
|
// value has an unknown command
|
|
// Parameters:
|
|
// FLD the field name
|
|
// COND the conditions field value
|
|
// CMD the unknown command
|
|
$aMessages[MSG_COND_UNK] = 'Field "$FLD" has an unknown command word ' .
|
|
'"$CMD": "$COND".';
|
|
|
|
// MSG_MISSING is sent in an Alert message when
|
|
// a socket filter is incorrectly defined
|
|
// Parameters:
|
|
// ITEM the missing item
|
|
$aMessages[MSG_MISSING] = 'Missing "$ITEM"';
|
|
|
|
// MSG_NEED_ARRAY is sent in an Alert message when
|
|
// a socket filter is incorrectly defined
|
|
// Parameters:
|
|
// ITEM the item that should be an array
|
|
$aMessages[MSG_NEED_ARRAY] = '"$ITEM" must be an array';
|
|
|
|
// MSG_SUBM_FAILED is shown to the user when an internal error
|
|
// as occurred and that error is not to be shown
|
|
// Parameters: none
|
|
$aMessages[MSG_SUBM_FAILED] = 'Your form submission has failed due to ' .
|
|
'an error on our server.';
|
|
|
|
// MSG_FILTER_WRONG is sent in an Alert message when
|
|
// a socket filter is incorrectly defined
|
|
// Parameters:
|
|
// FILTER the filter name
|
|
// ERRORS a string containing a list of errors
|
|
$aMessages[MSG_FILTER_WRONG] = 'Filter "$FILTER" is not properly defined: ' .
|
|
'$ERRORS';
|
|
|
|
// MSG_FILTER_CONNECT is sent in an Alert message when FormMail
|
|
// cannot connect to a socket filter
|
|
// Parameters:
|
|
// FILTER the filter name
|
|
// SITE the site
|
|
// ERRNUM socket error number
|
|
// ERRSTR socket error message
|
|
$aMessages[MSG_FILTER_CONNECT] = 'Could not connect to site "$SITE" ' .
|
|
'for filter "$FILTER" ($ERRNUM): $ERRSTR';
|
|
|
|
// MSG_FILTER_PARAM is sent in an Alert message when a socket
|
|
// filter has an invalid parameter specification
|
|
// Parameters:
|
|
// FILTER the filter name
|
|
// NUM parameter number
|
|
// NAME parameter name
|
|
$aMessages[MSG_FILTER_PARAM] = 'Filter "$FILTER" has invalid parameter ' .
|
|
'#$NUM: no "$NAME"';
|
|
|
|
// MSG_FILTER_OPEN_FILE is sent in an Alert message when a socket
|
|
// filter cannot open the required file
|
|
// Parameters:
|
|
// FILTER the filter name
|
|
// FILE the file that could not be opened
|
|
// ERROR the error message
|
|
$aMessages[MSG_FILTER_OPEN_FILE] = 'Filter "$FILTER" cannot open file ' .
|
|
'"$FILE": $ERROR';
|
|
|
|
// MSG_FILTER_FILE_ERROR is sent in an Alert message when a socket
|
|
// filter gets an error message during reading a file
|
|
// Parameters:
|
|
// FILTER the filter name
|
|
// FILE the file that could not be opened
|
|
// ERROR the error message
|
|
// NLINES the number of lines that were read successfully
|
|
$aMessages[MSG_FILTER_FILE_ERROR] = 'Filter "$FILTER": read error on file ' .
|
|
'"$FILE" after $NLINES lines: $ERROR';
|
|
|
|
// MSG_FILTER_READ_ERROR is sent in an Alert message when a socket
|
|
// filter gets an error during reading from the socket
|
|
// Parameters:
|
|
// FILTER the filter name
|
|
// ERROR the error message
|
|
$aMessages[MSG_FILTER_READ_ERROR] = 'Filter "$FILTER" failed: read error: ' .
|
|
'$ERROR';
|
|
|
|
// MSG_FILTER_NOT_OK is sent in an Alert message when a socket
|
|
// filter fails to return the agreed __OK__ indicator
|
|
// Parameters:
|
|
// FILTER the filter name
|
|
// DATA the data returned from the filter
|
|
$aMessages[MSG_FILTER_NOT_OK] = 'Filter "$FILTER" failed (missing ' .
|
|
'__OK__ line): $DATA';
|
|
|
|
// MSG_FILTER_UNK is sent in an Alert message
|
|
// when an unknown filter is specified by a form
|
|
// Parameters:
|
|
// FILTER the filter name
|
|
$aMessages[MSG_FILTER_UNK] = 'Unknown filter "$FILTER"';
|
|
|
|
// MSG_FILTER_CHDIR is sent in an Alert message
|
|
// when FormMail cannot change to the filter's directory
|
|
// Parameters:
|
|
// FILTER the filter name
|
|
// DIR the directory name
|
|
// ERROR an error message from the system
|
|
$aMessages[MSG_FILTER_CHDIR] = 'Cannot chdir to "$DIR" to run filter ' .
|
|
'"$FILTER": $ERROR';
|
|
|
|
// MSG_FILTER_NOTFOUND is sent in an Alert message
|
|
// when FormMail cannot execute the filter
|
|
// Parameters:
|
|
// FILTER the filter name
|
|
// CMD the command line being executed
|
|
// ERROR an error message from the system
|
|
$aMessages[MSG_FILTER_NOTFOUND] = 'Cannot execute filter "$FILTER" with ' .
|
|
'command "$CMD": $ERROR';
|
|
|
|
// MSG_FILTER_ERROR is sent in an Alert message
|
|
// when a filter returns a non-zero status
|
|
// Parameters:
|
|
// FILTER the filter name
|
|
// ERROR an error message from the system
|
|
// STATUS the status return from the command
|
|
$aMessages[MSG_FILTER_ERROR] = 'Filter "$FILTER" failed (status $STATUS): ' .
|
|
'$ERROR';
|
|
|
|
// MSG_SPARE is a spare message
|
|
$aMessages[MSG_SPARE] = '';
|
|
|
|
// MSG_TEMPLATE_ERRORS is sent as part of an Alert message
|
|
// when a template has generated some errors. The message
|
|
// should end with a new line and the actual errors are
|
|
// output after it.
|
|
// Parameters:
|
|
// NAME the template name
|
|
$aMessages[MSG_TEMPLATE_ERRORS] = 'Template "$NAME" caused the ' .
|
|
'following errors $MNUM:\n';
|
|
|
|
// MSG_TEMPLATE_FAILED is sent in an Alert message
|
|
// when processing a template has failed.
|
|
// Parameters:
|
|
// NAME the template name
|
|
$aMessages[MSG_TEMPLATE_FAILED] = 'Failed to process template "$NAME"';
|
|
|
|
// MSG_MIME_PREAMBLE is sent in the preamble of MIME emails
|
|
// Parameters: none
|
|
$aMessages[MSG_MIME_PREAMBLE] = '(Your mail reader should not show this ' .
|
|
'text.\nIf it does you may need to ' .
|
|
'upgrade to more modern software.)';
|
|
|
|
// MSG_MIME_HTML is sent in the preamble of HTML emails
|
|
// Parameters:
|
|
// NAME the template name
|
|
$aMessages[MSG_MIME_HTML] = 'This message has been generated by FormMail ' .
|
|
'using an HTML template\ncalled "$NAME". The ' .
|
|
'raw text of the form results\nhas been ' .
|
|
'included below, but your mail reader should ' .
|
|
'display the HTML\nversion only (unless it\'s ' .
|
|
'not capable of doing so).';
|
|
|
|
// MSG_FILE_OPEN_ERROR is sent in an Alert message when FormMail
|
|
// cannot open a file
|
|
// Parameters:
|
|
// NAME the file name
|
|
// TYPE the type of file
|
|
// ERROR the system error message
|
|
$aMessages[MSG_FILE_OPEN_ERROR] = 'Failed to open $TYPE file "$NAME": $ERROR';
|
|
|
|
// MSG_ATTACH_DATA is sent in an Alert message when the file
|
|
// attachment through 'data' has gone wrong.
|
|
// Parameters: none
|
|
$aMessages[MSG_ATTACH_DATA] = 'Internal error: AttachFile requires ' .
|
|
'"tmp_name" or "data"';
|
|
|
|
// MSG_PHP_HTML_TEMPLATES is sent in an Alert message when an
|
|
// HTML template is used but the PHP version is too old. (deprecated)
|
|
// Parameters:
|
|
// $PHPVERS the current PHP version
|
|
$aMessages[MSG_PHP_HTML_TEMPLATES] = '';
|
|
|
|
// MSG_PHP_FILE_UPLOADS is sent in an Alert message when
|
|
// file upload is used but the PHP version is too old. (deprecated)
|
|
// Parameters:
|
|
// $PHPVERS the current PHP version
|
|
$aMessages[MSG_PHP_FILE_UPLOADS] = '';
|
|
|
|
// MSG_FILE_UPLOAD is sent in an Alert message when
|
|
// file upload is attempted but FormMail is not configured to allow
|
|
// it
|
|
// Parameters: none
|
|
$aMessages[MSG_FILE_UPLOAD] = 'File upload attempt ignored';
|
|
|
|
// MSG_FILE_UPLOAD_ATTACK is sent in an Alert message when
|
|
// possible file upload attack is detected
|
|
// Parameters:
|
|
// NAME file name
|
|
// TEMP temporary file name
|
|
// FLD name of the file upload field
|
|
$aMessages[MSG_FILE_UPLOAD_ATTACK] = 'Possible file upload attack ' .
|
|
'detected: field="$FLD", name="$NAME" ' .
|
|
'temp name="$TEMP"';
|
|
|
|
// MSG_PHP_PLAIN_TEMPLATES is sent in an Alert message when a
|
|
// Plain template is used but the PHP version is too old. (deprecated)
|
|
// Parameters:
|
|
// $PHPVERS the current PHP version
|
|
$aMessages[MSG_PHP_PLAIN_TEMPLATES] = '';
|
|
|
|
// MSG_ATTACH_NAME is sent in an Alert message when a
|
|
// the form uses the Attach feature without specifying a file name
|
|
// Parameters: none
|
|
$aMessages[MSG_ATTACH_NAME] = 'filter_options: Attach must contain a name ' .
|
|
'(e.g. Attach=data.txt)';
|
|
|
|
// MSG_PHP_BCC is sent in an Alert message when a
|
|
// the form uses the BCC feature and the PHP version may not support it
|
|
// (deprecated)
|
|
// Parameters:
|
|
// $PHPVERS the current PHP version
|
|
$aMessages[MSG_PHP_BCC] = '';
|
|
|
|
// MSG_CSVCOLUMNS is sent in an Alert message when a csvcolumns field
|
|
// is not correct
|
|
// Parameters:
|
|
// $VALUE the csvcolumns field value
|
|
$aMessages[MSG_CSVCOLUMNS] = 'The "csvcolumns" setting is not ' .
|
|
'valid: "$VALUE"';
|
|
|
|
// MSG_CSVFILE is sent in an Alert message when a csvfile field
|
|
// is not correct
|
|
// Parameters:
|
|
// $VALUE the csvfile field value
|
|
$aMessages[MSG_CSVFILE] = 'The "csvfile" setting is not valid: "$VALUE"';
|
|
|
|
// MSG_TARG_EMAIL_PAT_START is sent in an Alert message when a
|
|
// $TARGET_EMAIL pattern is insecure because of a missing '^'
|
|
// at the beginning
|
|
// Parameters:
|
|
// $PAT the pattern
|
|
$aMessages[MSG_TARG_EMAIL_PAT_START] = 'Warning: Your TARGET_EMAIL pattern ' .
|
|
'"$PAT" is missing a ^ at the ' .
|
|
'beginning.';
|
|
|
|
// MSG_TARG_EMAIL_PAT_END is sent in an Alert message when a
|
|
// $TARGET_EMAIL pattern is insecure because of a missing '$'
|
|
// at the end
|
|
// Parameters:
|
|
// $PAT the pattern
|
|
$aMessages[MSG_TARG_EMAIL_PAT_END] = 'Warning: Your TARGET_EMAIL pattern ' .
|
|
'"$PAT" is missing a $ at the end.';
|
|
|
|
// MSG_CONFIG_WARN is sent in an Alert message when the FormMail
|
|
// configuration may have some problems. The messages are
|
|
// passed on separate lines, so the line terminations below
|
|
// are important.
|
|
// Parameters:
|
|
// $MESGS lines of messages
|
|
$aMessages[MSG_CONFIG_WARN] = 'The following potential problems were found ' .
|
|
'in your configuration:\n$MESGS\n\n' .
|
|
'These are not necessarily errors, but you ' .
|
|
'should review the documentation\n' .
|
|
'inside formmail.php. If you are sure your ' .
|
|
'configuration is correct\n' .
|
|
'you can disable the above messages by ' .
|
|
'changing the CONFIG_CHECK settings.';
|
|
|
|
// MSG_PHP_AUTORESP is sent in an Alert message when the PHP version
|
|
// does not support autoresponding (deprecated)
|
|
// Parameters:
|
|
// $PHPVERS current PHP version
|
|
$aMessages[MSG_PHP_AUTORESP] = '';
|
|
|
|
// MSG_ALERT is the test alert message (formmail.php?testalert=1)
|
|
// Parameters:
|
|
// $LANG the language ID
|
|
// $PHPVERS PHP version
|
|
// $FM_VERS FormMail version
|
|
// $SERVER server type
|
|
// $DOCUMENT_ROOT PHP's DOCUMENT_ROOT value
|
|
// $SCRIPT_FILENAME PHP's SCRIPT_FILENAME value
|
|
// $PATH_TRANSLATED PHP's PATH_TRANSLATED value
|
|
// $REAL_DOCUMENT_ROOT the REAL_DOCUMENT_ROOT value
|
|
$aMessages[MSG_ALERT] = 'This is a test alert message $MNUM\n' .
|
|
'Loaded language is $LANG\n' .
|
|
'PHP version is $PHPVERS\n' .
|
|
'FormMail version is $FM_VERS\n' .
|
|
'Server type: $SERVER\n' .
|
|
'\n' .
|
|
'DOCUMENT_ROOT: $DOCUMENT_ROOT\n' .
|
|
'SCRIPT_FILENAME: $SCRIPT_FILENAME\n' .
|
|
'PATH_TRANSLATED: $PATH_TRANSLATED\n' .
|
|
'REAL_DOCUMENT_ROOT: $REAL_DOCUMENT_ROOT';
|
|
|
|
// MSG_NO_DEF_ALERT is displayed if you use the testalert feature
|
|
// and no DEF_ALERT setting has been provided.
|
|
// Parameters: none
|
|
$aMessages[MSG_NO_DEF_ALERT] = 'No DEF_ALERT value has been set.';
|
|
|
|
// MSG_TEST_SENT is displayed if when use the testalert feature
|
|
// Parameters: none
|
|
$aMessages[MSG_TEST_SENT] = 'Test message sent. Check your email.';
|
|
|
|
// MSG_TEST_FAILED is displayed if when use the testalert feature
|
|
// and the mail sending fails.
|
|
// Parameters: none
|
|
$aMessages[MSG_TEST_FAILED] = 'FAILED to send alert message. Check your ' .
|
|
'server error logs.';
|
|
|
|
// MSG_NO_DATA_PAGE is the page that's displayed if the user
|
|
// just opens the URL to FormMail directly.
|
|
// Parameters: none
|
|
$aMessages[MSG_NO_DATA_PAGE] = 'This URL is a Form submission program.\n' .
|
|
'It appears the form is not working ' .
|
|
'correctly as there was no data found.\n' .
|
|
'You\'re not supposed to browse to this ' .
|
|
'URL; it should be accessed from a form.';
|
|
|
|
// MSG_REQD_ERROR is displayed to the user as a default error
|
|
// message when they haven't supplied some required fields
|
|
// Parameters: none
|
|
$aMessages[MSG_REQD_ERROR] = 'The form required some values that you ' .
|
|
'did not seem to provide.';
|
|
|
|
// MSG_COND_ERROR is displayed to the user as a default error
|
|
// message when some form conditions have failed
|
|
// Parameters: none
|
|
$aMessages[MSG_COND_ERROR] = 'Some of the values you provided are not valid.';
|
|
|
|
// MSG_CRM_FAILURE is displayed to the user when submission
|
|
// to the CRM has failed.
|
|
// Parameters:
|
|
// $URL the URL that was used
|
|
// $DATA data returned from the CRM
|
|
$aMessages[MSG_CRM_FAILURE] = 'The form submission did not succeed due to ' .
|
|
'a CRM failure. URL was \'$URL\'. ' .
|
|
'Returned CRM data:\n$DATA';
|
|
|
|
// MSG_FOPTION_WARN is sent in an Alert message when the form
|
|
// uses the superseded SendMailFOption feature
|
|
// Parameters:
|
|
// $LINE line number for SENDMAIL_F_OPTION
|
|
$aMessages[MSG_FOPTION_WARN] = 'Warning: You\'ve used SendMailFOption in ' .
|
|
'"mail_options" in your form. This has been ' .
|
|
'superseded with a configuration setting ' .
|
|
'inside formmail.php. Please update your ' .
|
|
'formmail.php configuration (look for ' .
|
|
'SENDMAIL_F_OPTION on line $LINE) and set ' .
|
|
'it to "true", then remove SendMailFOption ' .
|
|
'from your form(s).';
|
|
|
|
// MSG_NO_ACTIONS is sent in an Alert message when there is no
|
|
// action to perform or email address to send to
|
|
// Parameters: none
|
|
$aMessages[MSG_NO_ACTIONS] = 'The form has an internal error - no actions ' .
|
|
'or recipients were specified.';
|
|
|
|
// MSG_NO_RECIP is sent in an Alert message when there are no
|
|
// valid recipients to send to
|
|
// Parameters: none
|
|
$aMessages[MSG_NO_RECIP] = 'The form has an internal error - no valid ' .
|
|
'recipients were specified.';
|
|
|
|
// MSG_INV_EMAIL is sent in an Alert message when there are errors
|
|
// in the email addresses specified in the form
|
|
// Parameters:
|
|
// $ERRORS list of errors
|
|
$aMessages[MSG_INV_EMAIL] = 'Invalid email addresses were specified ' .
|
|
'in the form $MNUM:\n$ERRORS';
|
|
|
|
// MSG_FAILED_SEND is sent in an Alert message when the mail sending fails.
|
|
// Parameters: none
|
|
$aMessages[MSG_FAILED_SEND] = 'Failed to send email';
|
|
|
|
// MSG_ARESP_EMAIL is sent in an Alert message when
|
|
// no email address has been specified for an autoreponse
|
|
// Parameters: none
|
|
$aMessages[MSG_ARESP_EMAIL] = 'No "email" field was found. Autorespond ' .
|
|
'requires the submitter\'s email address.';
|
|
|
|
// MSG_ARESP_SUBJ is the default subject for the auto response email
|
|
// Parameters: none
|
|
$aMessages[MSG_ARESP_SUBJ] = 'Your form submission';
|
|
|
|
// MSG_LOG_NO_VERIMG is written to the auto respond log file
|
|
// if no VerifyImgString session variable was found
|
|
// Parameters: none
|
|
$aMessages[MSG_LOG_NO_VERIMG] = 'No VerifyImgString or turing_string in session, ' .
|
|
'no reverse CAPTCHA, no reCaptcha';
|
|
|
|
// MSG_ARESP_NO_AUTH is shown to the user
|
|
// if no VerifyImgString session variable was found
|
|
// Parameters: none
|
|
$aMessages[MSG_ARESP_NO_AUTH] = 'Failed to obtain authorization to send ' .
|
|
'you email. This is probably a fault on ' .
|
|
'the server.';
|
|
|
|
// MSG_LOG_NO_MATCH is written to the auto respond log file
|
|
// if the user's entry did not match the image verification
|
|
// Parameters: none
|
|
$aMessages[MSG_LOG_NO_MATCH] = 'User did not match image';
|
|
|
|
// MSG_LOG_RECAPTCHA is written to the auto respond log file
|
|
// if the reCaptcha process fails
|
|
// Parameters:
|
|
// ERR the reCaptcha error code
|
|
$aMessages[MSG_LOG_RECAPTCHA] = 'reCaptcha process failed ($ERR)';
|
|
|
|
// MSG_ARESP_NO_MATCH is shown to the user
|
|
// if the user's entry did not match the image verification
|
|
// Parameters: none
|
|
$aMessages[MSG_ARESP_NO_MATCH] = 'Your entry did not match the image';
|
|
|
|
// MSG_LOG_FAILED is written to the auto respond log file
|
|
// if the autoresponding failed
|
|
// Parameters: none
|
|
$aMessages[MSG_LOG_FAILED] = 'Failed';
|
|
|
|
// MSG_ARESP_FAILED is sent in an Alert message
|
|
// if the autoresponding failed
|
|
// Parameters: none
|
|
$aMessages[MSG_ARESP_FAILED] = 'Autoresponder failed';
|
|
|
|
// MSG_LOG_OK is written to the auto respond log file
|
|
// if the autoresponding succeeded
|
|
// Parameters: none
|
|
$aMessages[MSG_LOG_OK] = 'OK';
|
|
|
|
// MSG_THANKS_PAGE is the default page that's displayed if the
|
|
// submission is successful
|
|
// Parameters: none
|
|
$aMessages[MSG_THANKS_PAGE] = 'Thanks! We\'ve received your information ' .
|
|
'and, if it\'s appropriate, we\'ll be in ' .
|
|
'contact with you soon.';
|
|
|
|
// MSG_LOAD_MODULE is sent in an alert message if a module
|
|
// could not be loaded.
|
|
// Parameters:
|
|
// $FILE the file name
|
|
// $ERROR the error message
|
|
$aMessages[MSG_LOAD_MODULE] = 'Cannot load module from file \'$FILE\': $ERROR';
|
|
|
|
// MSG_LOAD_FMCOMPUTE is sent in an alert message if the form
|
|
// specifies at least one "fmcompute" field and the FMCompute
|
|
// module cannot be loaded.
|
|
// Parameters:
|
|
// $FILE the file name
|
|
// $ERROR the error message
|
|
$aMessages[MSG_LOAD_FMCOMPUTE] = 'Cannot load FMCompute module from file ' .
|
|
'\'$FILE\': $ERROR';
|
|
|
|
// MSG_REGISTER_MODULE is sent in an alert message if a module
|
|
// could not register with FMCompute
|
|
// Parameters:
|
|
// $NAME the name of the module
|
|
// $ERROR the error message
|
|
$aMessages[MSG_REGISTER_MODULE] = 'Cannot register module $NAME with ' .
|
|
'FMCompute: $ERROR';
|
|
|
|
// MSG_COMP_PARSE is sent in an alert message if a parse error
|
|
// occurs in an fmcompute field
|
|
// Parameters:
|
|
// $CODE the code with an error
|
|
// $ERRORS the error messages
|
|
$aMessages[MSG_COMP_PARSE] = 'These parse errors occurred in the following ' .
|
|
'code:\n$ERRORS\n$CODE';
|
|
|
|
// MSG_COMP_REG_DATA is sent in an alert message if FormMail cannot
|
|
// register a data field with the FMCompute module
|
|
// Parameters:
|
|
// $NAME the field name
|
|
// $ERROR the error message
|
|
$aMessages[MSG_COMP_REG_DATA] = 'Failed to register data field \'$NAME\': ' .
|
|
'$ERROR';
|
|
|
|
// MSG_COMP_ALERT is sent in an alert message if the FMCompute
|
|
// module has generated some alert messages.
|
|
// Parameters:
|
|
// $ALERTS the alerts
|
|
$aMessages[MSG_COMP_ALERT] = 'The following alert messages were reported ' .
|
|
'from the FMCompute module: $ALERTS';
|
|
|
|
// MSG_COMP_DEBUG is sent in an alert message if the FMCompute
|
|
// module has generated some debug messages.
|
|
// Parameters:
|
|
// $DEBUG the alerts
|
|
$aMessages[MSG_COMP_DEBUG] = 'The following debug messages were reported ' .
|
|
'from the FMCompute module: $DEBUG';
|
|
|
|
// MSG_COMP_EXEC is sent in an alert message if the FMCompute
|
|
// module has generated some error messages during execution
|
|
// Parameters:
|
|
// $ERRORS the errors
|
|
$aMessages[MSG_COMP_EXEC] = 'The following error messages were reported ' .
|
|
'from the FMCompute module: $ERRORS';
|
|
|
|
// MSG_TEMPL_ALERT is sent in an alert message if Advanced Template
|
|
// Processing has generated some alert messages.
|
|
// Parameters:
|
|
// $ALERTS the alerts
|
|
$aMessages[MSG_TEMPL_ALERT] = 'The following alert messages were reported ' .
|
|
'from the Advanced Template Processor: $ALERTS';
|
|
|
|
// MSG_TEMPL_DEBUG is sent in an alert message if Advanced Template
|
|
// Processing has generated some debug messages.
|
|
// Parameters:
|
|
// $DEBUG the alerts
|
|
$aMessages[MSG_TEMPL_DEBUG] = 'The following debug messages were reported ' .
|
|
'from the Advanced Template Processor: $DEBUG';
|
|
|
|
// MSG_TEMPL_PROC is sent in an alert message if Advanced Template Processing
|
|
// has generated some error messages during processing
|
|
// Parameters:
|
|
// $ERRORS the errors
|
|
$aMessages[MSG_TEMPL_PROC] = 'The following error messages were reported ' .
|
|
'from the Advanced Template Processor: $ERRORS';
|
|
|
|
// MSG_REG_FMCOMPUTE is sent in an Alert message when FormMail
|
|
// cannot register an external function with FMCompute.
|
|
// Parameters:
|
|
// FUNC the function that could not be registered
|
|
// ERROR the error message
|
|
$aMessages[MSG_REG_FMCOMPUTE] = 'Cannot register function "$FUNC" with ' .
|
|
'FMCompute: $ERROR';
|
|
|
|
// MSG_USER_ERRORS is shown as part of a user error when an FMCompute
|
|
// has called the "UserError" function one or more times.
|
|
// Parameters:
|
|
// NONE
|
|
$aMessages[MSG_USER_ERRORS] = 'One or more errors occurred in your form submission';
|
|
|
|
// MSG_CALL_PARAM_COUNT is sent in an alert when a call to a FormMail
|
|
// function from FMCompute has the wrong number of parameters
|
|
// Parameters:
|
|
// FUNC the function name
|
|
// COUNT the actual number of parameters passed
|
|
$aMessages[MSG_CALL_PARAM_COUNT] = 'FMCompute called FormMail function ' .
|
|
'\'$FUNC\' with wrong number of ' .
|
|
'parameters: $COUNT';
|
|
|
|
// MSG_CALL_UNK_FUNC is sent in an alert when FMCompute calls an
|
|
// unknown FormMail function
|
|
// Parameters:
|
|
// FUNC the function name
|
|
$aMessages[MSG_CALL_UNK_FUNC] = 'FMCompute called unknown FormMail function ' .
|
|
'\'$FUNC\'';
|
|
|
|
// MSG_SAVE_FILE is sent in an alert when saving a file to
|
|
// the server has failed
|
|
// Parameters:
|
|
// FILE the source file name (usually a temporary file name)
|
|
// DEST the destination file name
|
|
// ERR the error message
|
|
$aMessages[MSG_SAVE_FILE] = 'Failed to save file \'$FILE\' to \'$DEST\': $ERR';
|
|
|
|
// MSG_SAVE_FILE_EXISTS is sent as part of an alert when saving a file to
|
|
// the repository ($FILE_REPOSITORY) has failed because the file
|
|
// already exists and FILE_OVERWRITE is set to false.
|
|
// Parameters:
|
|
// FILE the destination file name
|
|
$aMessages[MSG_SAVE_FILE_EXISTS] = 'Cannot save file to repository as this would ' .
|
|
'overwrite \'$FILE\' and you have ' .
|
|
'set FILE_OVERWRITE to false.';
|
|
|
|
// MSG_EMPTY_ADDRESSES is sent as part of an alert when a number of empty
|
|
// email addresses have been specified in recipients, cc, or bcc
|
|
// *and* there are no valid addresses provided
|
|
// in the list
|
|
// Parameters:
|
|
// COUNT the number of empty addresses
|
|
$aMessages[MSG_EMPTY_ADDRESSES] = '$COUNT empty addresses';
|
|
|
|
// MSG_CALL_INVALID_PARAM is sent in an alert when a call to a FormMail
|
|
// function from FMCompute has an invalid parameter
|
|
// Parameters:
|
|
// FUNC the function name
|
|
// PARAM the parameter number
|
|
// CORRECT information about correct values
|
|
$aMessages[MSG_CALL_INVALID_PARAM] = 'FMCompute called FormMail function ' .
|
|
'\'$FUNC\' with an invalid parameter ' .
|
|
'number $PARAM. Correct values are: $CORRECT';
|
|
|
|
// MSG_INI_PARSE_WARN is sent in an alert when the INI file
|
|
// may have a syntax error and cannot be parsed.
|
|
// Parameters:
|
|
// FILE the file name
|
|
$aMessages[MSG_INI_PARSE_WARN] = 'Warning: your INI file \'$FILE\' appears ' .
|
|
'to be empty. This may indicate a syntax error.';
|
|
|
|
// MSG_INI_PARSE_ERROR is shown as an error message when the INI file
|
|
// has a syntax error and cannot be parsed.
|
|
// Parameters:
|
|
// FILE the file name
|
|
$aMessages[MSG_INI_PARSE_ERROR] = 'The FormMail INI file \'$FILE\' has a syntax error';
|
|
|
|
// MSG_CHMOD is sent in an alert when changing the protection
|
|
// mode of a file to has failed
|
|
// Parameters:
|
|
// FILE the file name
|
|
// MODE the mode
|
|
// ERR the error message
|
|
$aMessages[MSG_CHMOD] = 'Failed to change protection mode of file \'$FILE\' ' .
|
|
'to $MODE: $ERR';
|
|
|
|
// MSG_VERIFY_MISSING is shown to the user image verification string
|
|
// was not found
|
|
// Parameters: none
|
|
$aMessages[MSG_VERIFY_MISSING] = 'Image verification string missing. This' .
|
|
' is probably a fault on the server.';
|
|
|
|
// MSG_VERIFY_MATCH is shown to the user
|
|
// if the user's entry did not match the image verification for the
|
|
// imgverify option
|
|
// Parameters: none
|
|
$aMessages[MSG_VERIFY_MATCH] = 'Your entry did not match the image';
|
|
|
|
// MSG_RECAPTCHA_MATCH is shown to the user
|
|
// if using the reCaptcha system and there was an error
|
|
// Parameters:
|
|
// ERR the error code from the reCaptcha API
|
|
$aMessages[MSG_RECAPTCHA_MATCH] = 'reCaptcha verification failed ($ERR)';
|
|
|
|
// MSG_FILE_NAMES_INVALID is sent in an Alert message when
|
|
// a form's file_names setting has errors
|
|
// Parameters: none
|
|
// A list of errors is appended on separate lines
|
|
$aMessages[MSG_FILE_NAMES_INVALID] = 'Some file_names specifications are invalid $MNUM:\n';
|
|
|
|
// MSG_FILE_NAMES_NOT_FILE is sent in an Alert message when
|
|
// a form's file_names setting refers to a file field that doesn't
|
|
// exist
|
|
// Parameters:
|
|
// NAME the name of the file field that doesn't exist
|
|
$aMessages[MSG_FILE_NAMES_NOT_FILE] = 'Your file_names specification has ' .
|
|
'an error. \'$NAME\' is not the name ' .
|
|
'of a file upload field\n';
|
|
|
|
// MSG_NEXT_PLUS_GOOD is sent in an alert message if the form is
|
|
// ambiguous and specifies both "next_form" and "good_url" or
|
|
// "good_template"
|
|
// Parameters:
|
|
// $WHICH the "good_" field that was specified
|
|
$aMessages[MSG_NEXT_PLUS_GOOD] = 'The form has specified both "next_form" ' .
|
|
'and "$WHICH" fields - the action to ' .
|
|
'to perform is ambiguous';
|
|
|
|
// MSG_MULTIFORM is sent in an Alert message when a form tries
|
|
// to use a multi-form template, but templates have not been configured in
|
|
// formmail.php
|
|
// Parameters: none
|
|
$aMessages[MSG_MULTIFORM] = 'You must set either MULTIFORMDIR or MULTIFORMURL ' .
|
|
'in formmail.php before you can use ' .
|
|
'multi-page forms.';
|
|
|
|
// MSG_MULTIFORM_FAILED is sent in an Alert message
|
|
// when processing a multi-page form template has failed.
|
|
// Parameters:
|
|
// NAME the template name
|
|
$aMessages[MSG_MULTIFORM_FAILED] = 'Failed to process multi-page form template "$NAME"';
|
|
|
|
// MSG_NEED_THIS_FORM is sent in an Alert message
|
|
// when a multi-page form does not specify the "this_form" field.
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_NEED_THIS_FORM] = 'Multi-page forms require "this_form" field';
|
|
|
|
// MSG_NO_PHP_SELF is sent in an Alert message
|
|
// when FormMail requires the "PHP_SELF" server variable and PHP is not
|
|
// providing it.
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_NO_PHP_SELF] = 'PHP on the server is not providing "PHP_SELF"';
|
|
|
|
// MSG_RETURN_URL_INVALID is sent in an Alert message
|
|
// when "this_form" is not a valid return URL. This occurs for
|
|
// multi-page forms.
|
|
// Parameters:
|
|
// URL the invalid URL
|
|
$aMessages[MSG_RETURN_URL_INVALID] = 'Return URL "$URL" is not valid';
|
|
|
|
// MSG_GO_BACK is sent in an Alert message
|
|
// when "multi_go_back" has been submitted but this isn't part of a
|
|
// multi-page form.
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_GO_BACK] = 'Cannot "go back" if not in a multi-page form ' .
|
|
'sequence or at the first page of the form ' .
|
|
'sequence';
|
|
|
|
// MSG_OPEN_URL is sent in an Alert message when a URL cannot
|
|
// be opened.
|
|
// Parameters:
|
|
// URL the invalid URL
|
|
// ERROR error message
|
|
$aMessages[MSG_OPEN_URL] = 'Cannot open URL "$URL": $ERROR';
|
|
|
|
// MSG_CANNOT_RETURN is sent in an Alert message when an invalid return
|
|
// request is made in a multi-page form sequence.
|
|
// Parameters:
|
|
// TO the requested page index
|
|
// TOPINDEX the top page index
|
|
$aMessages[MSG_CANNOT_RETURN] = 'Cannot return to page $TO. The top page ' .
|
|
'index is $TOPINDEX';
|
|
|
|
// MSG_ATTACK_DETECTED is sent in an Alert message when an attack on
|
|
// the server has been detected
|
|
// Parameters:
|
|
// ATTACK name or description of the attack
|
|
// INFO more information about the attack
|
|
$aMessages[MSG_ATTACK_DETECTED] = 'Server attack "$ATTACK" detected. ' .
|
|
'Your server is safe as FormMail is ' .
|
|
'invulnerable to this attack. You can ' .
|
|
'disable these messages by setting ' .
|
|
'ALERT_ON_ATTACK_DETECTION to false ' .
|
|
'in FormMail\'s configuration section.' .
|
|
'\nMore information:\n$INFO';
|
|
|
|
// MSG_ATTACK_PAGE is the contents of the browser page displayed to the
|
|
// user when an attack is detected
|
|
// Parameters:
|
|
// SERVER the name of the server (website)
|
|
// USERINFO details of the error
|
|
$aMessages[MSG_ATTACK_PAGE] = 'Your form submission has been rejected ' .
|
|
'as it appears to be an abuse of our server (' .
|
|
'$SERVER).<br />' .
|
|
'Our supplier of forms processing software has ' .
|
|
'provided <a href="http://www.tectite.com/serverabuse.php" ' .
|
|
' target="_blank">more information about this error</a>.<br /><br />' .
|
|
'$USERINFO';
|
|
|
|
// MSG_ATTACK_MIME_INFO is the contents of the INFO parameter
|
|
// to the MSG_ATTACK_DETECTED message for the MIME attack
|
|
// Parameters:
|
|
// FLD name of the field
|
|
// CONTENT the invalid content found in the field
|
|
$aMessages[MSG_ATTACK_MIME_INFO] = 'The field "$FLD" contained invalid ' .
|
|
'content "$CONTENT"';
|
|
|
|
// MSG_ATTACK_DUP_INFO is the contents of the INFO parameter
|
|
// to the MSG_ATTACK_DETECTED message for the Duplicate Data attack
|
|
// Parameters:
|
|
// FLD1 name of the first field
|
|
// FLD2 name of the second field
|
|
$aMessages[MSG_ATTACK_DUP_INFO] = 'The fields "$FLD1" and "$FLD2" contained ' .
|
|
'duplicate data';
|
|
|
|
// MSG_ATTACK_SPEC_INFO is the contents of the INFO parameter
|
|
// to the MSG_ATTACK_DETECTED message for the Special Field attack
|
|
// Parameters:
|
|
// FLD name of the special field
|
|
$aMessages[MSG_ATTACK_SPEC_INFO] = 'Special field "$FLD" contained an email address';
|
|
|
|
// MSG_ATTACK_MANYURL_INFO is the contents of the INFO parameter
|
|
// to the MSG_ATTACK_DETECTED message for the Many URLs attack
|
|
// Parameters:
|
|
// FLD name of the field
|
|
// NUM number of URLs detected
|
|
$aMessages[MSG_ATTACK_MANYURL_INFO] = 'Field "$FLD" contained $NUM URLs';
|
|
|
|
// MSG_ATTACK_MANYFIELDS_INFO is the contents of the INFO parameter
|
|
// to the MSG_ATTACK_DETECTED message for the Many Fields with URLs
|
|
// attack
|
|
// Parameters:
|
|
// NUM number of fields detected with URLs
|
|
// FLDS list of fields with URLs in them
|
|
$aMessages[MSG_ATTACK_MANYFIELDS_INFO] = '$NUM fields contained URLs: $FLDS';
|
|
|
|
// MSG_REV_CAP is an error regarding the setting of ATTACK_DETECTION_REVERSE_CAPTCHA
|
|
// Parameters: none
|
|
$aMessages[MSG_REV_CAP] = 'ATTACK_DETECTION_REVERSE_CAPTCHA is not set correctly, ' .
|
|
'and will be ignored. Please refer to the documentation ' .
|
|
'to make the correct setting.';
|
|
|
|
// MSG_ATTACK_REV_CAP_INFO is the contents of the INFO parameter
|
|
// to the MSG_ATTACK_DETECTED message for the Reverse Captcha attack
|
|
// detection
|
|
// Parameters:
|
|
// FLD name of the field
|
|
// CONTENT the invalid content found in the field
|
|
$aMessages[MSG_ATTACK_REV_CAP_INFO] = 'The field "$FLD" contained unexpected ' .
|
|
'content "$CONTENT".';
|
|
|
|
// MSG_ATTACK_JUNK_INFO is the contents of the INFO parameter
|
|
// to the MSG_ATTACK_DETECTED message for the JUNK attack
|
|
// Parameters:
|
|
// FLD name of the field
|
|
// JUNK the junk that was found
|
|
$aMessages[MSG_ATTACK_JUNK_INFO] = 'The field "$FLD" contained junk ' .
|
|
'data "$JUNK"';
|
|
|
|
// MSG_ARESP_EMPTY is an alert message sent when
|
|
// an autoresponse template or file is empty
|
|
// Parameters:
|
|
// TYPE the type of autoreponse requested
|
|
$aMessages[MSG_ARESP_EMPTY] = 'The autoresponse is empty. The form ' .
|
|
'requested $TYPE';
|
|
|
|
// MSG_NEED_SCRATCH_PAD is an alert message when processing requires
|
|
// SCRATCH_PAD configuration for file upload processing. This occurs
|
|
// when you upload files in pages of a multi page form sequence.
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_NEED_SCRATCH_PAD] = 'You need to set SCRATCH_PAD in the ' .
|
|
'configuration section to process ' .
|
|
'uploaded files.';
|
|
|
|
// MSG_OPEN_SCRATCH_PAD is an alert message when the SCRATCH_PAD
|
|
// directory cannot be opened for cleanup.
|
|
// Parameters:
|
|
// DIR name of the directory
|
|
// ERR more error information
|
|
$aMessages[MSG_OPEN_SCRATCH_PAD] = 'Cannot open SCRATCH_PAD directory ' .
|
|
'"$DIR". Open failed: $ERR';
|
|
|
|
// MSG_NO_NEXT_NUM_FILE is an alert message when a form tries to
|
|
// use the %nextnum% derivation feature but you haven't
|
|
// setup FormMail's configuration correctly.
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_NO_NEXT_NUM_FILE] = 'You cannot use the %nextnum% feature: ' .
|
|
'you have not configured NEXT_NUM_FILE';
|
|
|
|
// MSG_NEXT_NUM_FILE is an alert message when a form tries to
|
|
// use the %nextnum% derivation feature but the next number file cannot
|
|
// be processed.
|
|
// Parameters:
|
|
// FILE name of the file
|
|
// ACT action
|
|
// ERR more error information
|
|
$aMessages[MSG_NEXT_NUM_FILE] = 'Cannot $ACT next number file ' .
|
|
'\'$FILE\': $ERR';
|
|
|
|
// MSG_MULTI_UPLOAD is an alert message when processing of uploaded
|
|
// fails during a multi-page form sequence
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_MULTI_UPLOAD] = 'File upload processing failed during ' .
|
|
'multi-page form processing.';
|
|
|
|
// MSG_URL_PARSE is an error message when a URL to be opened
|
|
// cannot be parsed
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_URL_PARSE] = 'Failed to parse URL';
|
|
|
|
// MSG_URL_SCHEME is an error message when a URL to be opened
|
|
// has an unsupported "scheme" value
|
|
// Parameters:
|
|
// SCHEME the scheme that was seen
|
|
$aMessages[MSG_URL_SCHEME] = 'Unsupported URL scheme "$SCHEME"';
|
|
|
|
// MSG_SOCKET is an error message when opening a socket for a URL
|
|
// fails
|
|
// Parameters:
|
|
// ERRNO the error code
|
|
// ERRSTR the error string
|
|
// PHPERR the value of $php_errormsg
|
|
$aMessages[MSG_SOCKET] = 'Socket error $ERRNO: $ERRSTR: $PHPERR';
|
|
|
|
// MSG_GETURL_OPEN is an error message when the web server reports
|
|
// a failure on opening a URL
|
|
// Parameters:
|
|
// STATUS the HTTP status value (number + string)
|
|
$aMessages[MSG_GETURL_OPEN] = 'Open URL failed: $STATUS URL=$URL';
|
|
|
|
// MSG_RESOLVE is an error message when a host name cannot be
|
|
// resolved to an IP address
|
|
// Parameters:
|
|
// NAME the host name that could not be resolved
|
|
$aMessages[MSG_RESOLVE] = 'Cannot resolve host name "$NAME"';
|
|
|
|
// MSG_FORM_OK is the page title for the default
|
|
// "thank you" page
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_FORM_OK] = 'Form Submission Succeeded';
|
|
|
|
// MSG_FORM_ERROR is the page title for default
|
|
// error pages
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_FORM_ERROR] = 'Form Submission Error';
|
|
|
|
// MSG_GET_DISALLOWED is the message shown when GET method is used
|
|
// but has been disabled.
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_GET_DISALLOWED] = 'GET method has been disabled. Forms must use ' .
|
|
'the POST method. Alternatively, reconfigure ' .
|
|
'FormMail to allow the GET method.';
|
|
|
|
// MSG_INVALID_SENDER is the alert message sent when an sender email address
|
|
// has been specified that is not a known or valid email address
|
|
// Parameters:
|
|
// LOC the location of the error
|
|
// EMAIL the invalid email address
|
|
$aMessages[MSG_INVALID_SENDER] = 'The form has specified an invalid sender email address ($EMAIL) in $LOC.';
|
|
|
|
// MSG_SET_SENDER_FROM_EMAIL is the configuration check message when SET_SENDER_FROM_EMAIL
|
|
// has been set to true in the configuration section. This feature is no longer available
|
|
// as can lead to a security problem.
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_SET_SENDER_FROM_EMAIL] = 'SET_SENDER_FROM_EMAIL is no longer supported and will be ignored.';
|
|
|
|
// MSG_FILE_UPLOAD_ERRn are the error messages corresponding to the
|
|
// PHP file upload error code n.
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_FILE_UPLOAD_ERR1] = 'The uploaded file exceeds the upload_max_filesize directive in php.ini.';
|
|
$aMessages[MSG_FILE_UPLOAD_ERR2] = 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the html form.';
|
|
$aMessages[MSG_FILE_UPLOAD_ERR3] = 'The uploaded file was only partially uploaded.';
|
|
$aMessages[MSG_FILE_UPLOAD_ERR4] = 'No file was uploaded.';
|
|
$aMessages[MSG_FILE_UPLOAD_ERR6] = 'Missing a temporary folder.';
|
|
$aMessages[MSG_FILE_UPLOAD_ERR7] = 'Failed to write file to disk.';
|
|
$aMessages[MSG_FILE_UPLOAD_ERR8] = 'File upload stopped by extension.';
|
|
|
|
// MSG_FILE_UPLOAD_ERR_UNK is displayed when an unknown error code
|
|
// is provided by PHP for a file upload
|
|
// Parameters:
|
|
// ERRNO the error code
|
|
$aMessages[MSG_FILE_UPLOAD_ERR_UNK] = 'Unknown file upload error code $ERRNO';
|
|
|
|
// MSG_FILE_UPLOAD_SIZE is displayed when an uploaded file exceeds
|
|
// the configured maximum size
|
|
// Parameters:
|
|
// NAME the uploaded file's name
|
|
// SIZE the size of the uploaded file
|
|
// MAX the maximum size that was exceeded
|
|
$aMessages[MSG_FILE_UPLOAD_SIZE] = 'Uploaded file "$NAME" is too big (' .
|
|
'$SIZE bytes). The maximum permitted ' .
|
|
'size is $MAX kilobytes.';
|
|
|
|
// MSG_POST_SIZE_LIMIT is displayed when a form submission is larger
|
|
// than the configured maximum size in PHP
|
|
// Parameters:
|
|
// NAME the uploaded file's name
|
|
// SIZE the size of the uploaded file
|
|
// MAX the maximum size that was exceeded
|
|
$aMessages[MSG_POST_SIZE_LIMIT] = 'Your form submission exceeds the server\'s configured size limit.';
|
|
|
|
// MSG_DER_FUNC_ERROR is sent in an Alert message when
|
|
// a form uses a derive_fields function that's
|
|
// formatted incorrectly
|
|
// Parameters:
|
|
// $SPEC the invalid value specification
|
|
// $MSG a message describing the error or providing an example
|
|
$aMessages[MSG_DER_FUNC_ERROR] = 'derive_fields: invalid function specification ' .
|
|
'"$SPEC": $MSG';
|
|
|
|
// MSG_DER_FUNC_SIZE_FMT describes the right syntax for the "size" function
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_DER_FUNC_SIZE_FMT] = '"size" function requires this format: ' .
|
|
'size(file_field)';
|
|
|
|
// MSG_DER_FUNC_IF_FMT describes the right syntax for the "if" function
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_DER_FUNC_IF_FMT] = '"if" function requires this format: ' .
|
|
'if(field;spec;spec)';
|
|
|
|
// MSG_DER_FUNC_NEXTNUM_FMT describes the right syntax for the "nextnum" function
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_DER_FUNC_NEXTNUM_FMT] = '"nextnum" function requires this format: ' .
|
|
'nextnum(pad) or nextnum(pad;base). pad and base ' .
|
|
'must be numbers. base must be 2 to 36 inclusive';
|
|
|
|
// MSG_DER_FUNC_EXT_FMT describes the right syntax for the "ext" function
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_DER_FUNC_EXT_FMT] = '"ext" function requires this format: ' .
|
|
'ext(file_field)';
|
|
|
|
// MSG_DER_FUNC1_FMT describes the right syntax for a function
|
|
// requiring one parameter
|
|
// Parameters:
|
|
// FUNC name of the function
|
|
$aMessages[MSG_DER_FUNC1_FMT] = '"$FUNC" function requires this format: ' .
|
|
'$FUNC(fieldname)';
|
|
|
|
// MSG_DER_FUNC_SUBSTR_FMT describes the right syntax for the "substr" function
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_DER_FUNC_SUBSTR_FMT] = '"substr" function requires this format: ' .
|
|
'substr(fieldname;start) or ' .
|
|
'substr(fieldname;start;length) - ' .
|
|
'start and length must be numbers.';
|
|
|
|
// MSG_USER_ATTACK_JUNK is a message shown to the user when a junk
|
|
// attack has been detected
|
|
// Parameters:
|
|
// INPUT the data the user input
|
|
$aMessages[MSG_USER_ATTACK_JUNK] = 'The following input looks like a junk attack ' .
|
|
'on our server. Please avoid scientific ' .
|
|
'or technical terms with long sequences ' .
|
|
'of consonants or vowels: $INPUT';
|
|
|
|
// MSG_USER_ATTACK_REV_CAP is a message shown to the user when a reverse
|
|
// captcha attack has been detected
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_USER_ATTACK_REV_CAP] = 'Your input looks like an automated spambot ' .
|
|
'attacking our server. Some automatic form ' .
|
|
'fillers can trigger this detection. Try ' .
|
|
'filling in our form manually. If you use the ' .
|
|
'back button to go back, make sure you ' .
|
|
'refresh the page before trying again.';
|
|
|
|
// MSG_USER_ATTACK_DUP is a message shown to the user when a duplicate
|
|
// data attack has been detected
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_USER_ATTACK_DUP] = 'You have input the same information in ' .
|
|
'several fields in the form. Please ' .
|
|
're-submit the form without duplication';
|
|
|
|
// MSG_USER_ATTACK_MANY_URLS is a message shown to the user when a many urls
|
|
// attack has been detected
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_USER_ATTACK_MANY_URLS] = 'Your input includes a number of URLs. ' .
|
|
'This server has been configured to reject ' .
|
|
'form submissions with too many URLs. ' .
|
|
'Please re-submit the form without URLs or ' .
|
|
'with fewer URLs.';
|
|
|
|
// MSG_USER_ATTACK_MANY_URL_FIELDS is a message shown to the user when a many urls
|
|
// attack has been detected
|
|
// Parameters:
|
|
// none
|
|
$aMessages[MSG_USER_ATTACK_MANY_URL_FIELDS] = $aMessages[MSG_USER_ATTACK_MANY_URLS];
|
|
|
|
$aMessages[MSG_INVALID_EMAIL] = 'The email address "$EMAIL" is not valid: $REASON';
|
|
} // <A NAME="BuiltinMessages"> Jump to: <A HREF="#MessageNumbers">
|
|
|
|
//
|
|
// If the form submission was using the GET method, switch to the
|
|
// GET vars instead of the POST vars
|
|
//
|
|
if (isset($aServerVars["REQUEST_METHOD"]) && $aServerVars["REQUEST_METHOD"] === "GET") {
|
|
$bIsGetMethod = true;
|
|
if (Settings::get('ALLOW_GET_METHOD')) {
|
|
$aFormVars = &$_GET;
|
|
} elseif (count($_GET) > 0) {
|
|
$bHasGetData = true;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Load the default language, and then override with an optional language file.
|
|
//
|
|
function LoadLanguage()
|
|
{
|
|
LoadBuiltinLanguage();
|
|
LoadLanguageFile();
|
|
}
|
|
|
|
//
|
|
// To return the value of a string or empty string if not set.
|
|
//
|
|
function CheckString($ss)
|
|
{
|
|
return (isset($ss) ? $ss : "");
|
|
}
|
|
|
|
$aGetMessageSubstituteErrors = array();
|
|
$aGetMessageSubstituteFound = array();
|
|
$bGetMessageSubstituteNoErrors = false;
|
|
|
|
//
|
|
// Worker function for GetMessage's preg_replace_callback calls.
|
|
// Returns the value of the matched variable name.
|
|
// Variables are searched for in the global $aGetMessageValues.
|
|
// If no such variable exists, an empty string is returned and the
|
|
// global variable $aGetMessageSubstituteErrors lists the missing names.
|
|
//
|
|
function GetMessageSubstituteParam($a_matches)
|
|
{
|
|
global $aGetMessageValues,$aGetMessageSubstituteErrors;
|
|
global $aGetMessageSubstituteFound,$bGetMessageSubstituteNoErrors;
|
|
|
|
$s_name = $a_matches[1];
|
|
$aGetMessageSubstituteFound[] = $s_name;
|
|
$s_value = "";
|
|
if (isset($aGetMessageValues[$s_name])) {
|
|
$s_value = $aGetMessageValues[$s_name];
|
|
} elseif ($bGetMessageSubstituteNoErrors) {
|
|
$s_value = '$' . $s_name;
|
|
} else {
|
|
$aGetMessageSubstituteErrors[] = $s_name;
|
|
}
|
|
return ($s_value);
|
|
}
|
|
|
|
//
|
|
// Returns message text from a message number, with optional parameters.
|
|
//
|
|
function GetMessage($i_msg_num,$a_params = array(),
|
|
$b_show_mnum = true,$b_no_errors = false)
|
|
{
|
|
global $aMessages,$sLangID;
|
|
|
|
if (count($aMessages) == 0) {
|
|
LoadLanguage();
|
|
}
|
|
if (!isset($aMessages[$i_msg_num])) {
|
|
SendAlert("Unknown Message Number $i_msg_num was used",false,true);
|
|
$s_text = "<UNKNOWN MESSAGE NUMBER>";
|
|
} else {
|
|
$s_text = $aMessages[$i_msg_num];
|
|
}
|
|
$s_mno = Settings::get('bShowMesgNumbers') ? "[M$i_msg_num]" : "";
|
|
|
|
$s_orig_text = $s_text;
|
|
//
|
|
// substitute parameters; only works with PHP version 4.0.5 or later
|
|
//
|
|
if (strpos($s_text,'$') !== false) {
|
|
global $aGetMessageValues,$aGetMessageSubstituteErrors;
|
|
global $aGetMessageSubstituteFound,$bGetMessageSubstituteNoErrors;
|
|
|
|
$aGetMessageSubstituteErrors = array();
|
|
$aGetMessageSubstituteFound = array();
|
|
$aGetMessageValues = HTMLEntitiesArray($a_params,true);
|
|
$bGetMessageSubstituteNoErrors = $b_no_errors;
|
|
$aGetMessageValues["MNUM"] = $s_mno; // add the message number
|
|
//
|
|
// search for words in this form:
|
|
// $word
|
|
// where word begins with an alphabetic character and
|
|
// consists of alphanumeric and underscore
|
|
//
|
|
$s_text = preg_replace_callback('/\$([a-z][a-z0-9_]*)/i',
|
|
'GetMessageSubstituteParam',$s_text);
|
|
if (count($aGetMessageSubstituteErrors) > 0) {
|
|
SendAlert("Message Number $i_msg_num ('$s_orig_text') in language $sLangID " .
|
|
"specified the following unsupported parameters: " .
|
|
implode(',',$aGetMessageSubstituteErrors));
|
|
}
|
|
if (!in_array("MNUM",$aGetMessageSubstituteFound))
|
|
//
|
|
// append the message number
|
|
//
|
|
{
|
|
$s_text .= $b_show_mnum ? " $s_mno" : "";
|
|
}
|
|
} else
|
|
//
|
|
// append the message number
|
|
//
|
|
{
|
|
$s_text .= $b_show_mnum ? " $s_mno" : "";
|
|
}
|
|
//
|
|
// replace '\n' sequences with new lines
|
|
//
|
|
return (str_replace('\n',"\n",$s_text));
|
|
}
|
|
|
|
//
|
|
// Check if the server is Windows
|
|
//
|
|
function IsServerWindows()
|
|
{
|
|
static $bGotAnswer = false;
|
|
static $bAnswer;
|
|
|
|
if (!$bGotAnswer) {
|
|
if ((isset($_ENV["OS"]) && stristr($_ENV["OS"],"windows") !== false) ||
|
|
(isset($_SERVER["PATH"]) && stristr($_SERVER["PATH"],"winnt") !== false) ||
|
|
(isset($_SERVER["PATH"]) && stristr($_SERVER["PATH"],"windows") !== false) ||
|
|
(isset($_SERVER["SystemRoot"]) && stristr($_SERVER["SystemRoot"],"winnt") !== false) ||
|
|
(isset($_ENV["SystemRoot"]) && stristr($_ENV["SystemRoot"],"winnt") !== false) ||
|
|
(isset($_SERVER["SystemRoot"]) && stristr($_SERVER["SystemRoot"],"windows") !== false) ||
|
|
(isset($_ENV["SystemRoot"]) && stristr($_ENV["SystemRoot"],"windows") !== false) ||
|
|
(isset($_SERVER["Path"]) && stristr($_SERVER["Path"],"windows") !== false)
|
|
) {
|
|
$bAnswer = true;
|
|
} else {
|
|
$bAnswer = false;
|
|
}
|
|
$bGotAnswer = true;
|
|
}
|
|
return ($bAnswer);
|
|
}
|
|
|
|
//
|
|
// To return a temporary file name from $SCRATCH_PAD
|
|
//
|
|
function GetScratchPadFile($s_prefix)
|
|
{
|
|
|
|
switch (substr(Settings::get('SCRATCH_PAD'),-1)) {
|
|
case '/':
|
|
case '\\':
|
|
$s_dir = substr(Settings::get('SCRATCH_PAD'),0,-1);
|
|
break;
|
|
default:
|
|
$s_dir = Settings::get('SCRATCH_PAD');
|
|
break;
|
|
}
|
|
//
|
|
// Ideally, we could use tempnam. But,
|
|
// tempnam is system dependent and might not use the
|
|
// SCRATCH_PAD directory even if we tell it to.
|
|
// So, we'll force the file into SCRATCH_PAD.
|
|
//
|
|
// Note that we do *not* create the file, even though tempnam
|
|
// does create it in PHP version 4.0.3 and above. (The reason is
|
|
// we can't guarantee a non-race condition anyway.)
|
|
//
|
|
do {
|
|
$i_rand = mt_rand(0,16777215); // 16777215 is FFFFFF in hex
|
|
$s_name = $s_dir . "/" . $s_prefix . sprintf("%06X",$i_rand);
|
|
} while (file_exists($s_name));
|
|
return ($s_name);
|
|
}
|
|
|
|
//
|
|
// To return a temporary file name.
|
|
//
|
|
function GetTempName($s_prefix)
|
|
{
|
|
if (!Settings::isEmpty('SCRATCH_PAD')) {
|
|
$s_name = GetScratchPadFile($s_prefix);
|
|
} else {
|
|
$s_name = tempnam("/tmp",$s_prefix);
|
|
}
|
|
return ($s_name);
|
|
}
|
|
|
|
//
|
|
// To find a directory on the server for temporary files.
|
|
//
|
|
function GetTempDir()
|
|
{
|
|
$s_name = GetTempName("fm");
|
|
if (file_exists($s_name)) {
|
|
unlink($s_name);
|
|
}
|
|
$s_dir = dirname($s_name);
|
|
return ($s_dir);
|
|
}
|
|
|
|
define('DEBUG',false); // for production
|
|
//define('DEBUG',true); // for development and debugging
|
|
define('RFCLINELEN',76); // recommend maximum line length from RFC 2822
|
|
|
|
//
|
|
// The user agent string to use when opening URLs
|
|
//
|
|
$sUserAgent = "FormMail/$FM_VERS (from www.tectite.com)";
|
|
|
|
if (DEBUG) {
|
|
error_reporting(E_ALL); // trap everything!
|
|
ini_set("display_errors","stdout");
|
|
ini_set("display_startup_errors","1");
|
|
assert_options(ASSERT_ACTIVE,true);
|
|
assert_options(ASSERT_BAIL,true);
|
|
LoadLanguage();
|
|
} else {
|
|
$iOldLevel = error_reporting(E_ALL ^ E_WARNING);
|
|
LoadLanguage();
|
|
//
|
|
// report everything except warnings and notices
|
|
//
|
|
error_reporting(E_ALL ^ E_WARNING ^ E_NOTICE);
|
|
}
|
|
|
|
function SetRealDocumentRoot()
|
|
{
|
|
global $aServerVars,$REAL_DOCUMENT_ROOT;
|
|
|
|
if (isset($aServerVars['SCRIPT_FILENAME'])) {
|
|
$REAL_DOCUMENT_ROOT = $aServerVars['SCRIPT_FILENAME'];
|
|
} elseif (isset($aServerVars['PATH_TRANSLATED'])) {
|
|
$REAL_DOCUMENT_ROOT = $aServerVars['PATH_TRANSLATED'];
|
|
} else {
|
|
$REAL_DOCUMENT_ROOT = "";
|
|
}
|
|
//
|
|
// look for 'www' or 'public_html' and strip back to that if found,
|
|
// otherwise just get the directory name
|
|
//
|
|
if (($i_pos = strpos($REAL_DOCUMENT_ROOT,"/www/")) !== false) {
|
|
$REAL_DOCUMENT_ROOT = substr($REAL_DOCUMENT_ROOT,0,$i_pos + 4);
|
|
} elseif (($i_pos = strpos($REAL_DOCUMENT_ROOT,"/public_html/")) !== false) {
|
|
$REAL_DOCUMENT_ROOT = substr($REAL_DOCUMENT_ROOT,0,$i_pos + 12);
|
|
} elseif (!empty($REAL_DOCUMENT_ROOT)) {
|
|
$REAL_DOCUMENT_ROOT = dirname($REAL_DOCUMENT_ROOT);
|
|
} elseif (isset($aServerVars['DOCUMENT_ROOT']) &&
|
|
!empty($aServerVars['DOCUMENT_ROOT'])
|
|
) {
|
|
$REAL_DOCUMENT_ROOT = $aServerVars['DOCUMENT_ROOT'];
|
|
}
|
|
}
|
|
|
|
//
|
|
// Hook system: before initialization (but after configuration)
|
|
//
|
|
if (($incFile = hookFileExists("fmhookpreinit.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
|
|
if (!Settings::isEmpty('SESSION_NAME')) {
|
|
session_name(Settings::get('SESSION_NAME'));
|
|
}
|
|
|
|
/**
|
|
* Session data access
|
|
*
|
|
* @param $s_name
|
|
*
|
|
* @return mixed null session variable's value or null if not set
|
|
*/
|
|
function GetSession($s_name)
|
|
{
|
|
return (isset($_SESSION) ? $_SESSION[$s_name] : null);
|
|
}
|
|
|
|
/**
|
|
* Session data isset
|
|
*
|
|
* @param $s_name
|
|
*
|
|
* @return boolean true if the given session variable is set
|
|
*/
|
|
function IsSetSession($s_name)
|
|
{
|
|
return (isset($_SESSION) && isset($_SESSION[$s_name]));
|
|
}
|
|
|
|
/**
|
|
* Session data setting
|
|
*
|
|
* Sets the given session variable to the given value.
|
|
*
|
|
* @param $s_name string
|
|
* name of the session variable
|
|
* @param $m_value mixed
|
|
* value to set
|
|
*/
|
|
function SetSession($s_name,$m_value)
|
|
{
|
|
$_SESSION[$s_name] = $m_value;
|
|
}
|
|
|
|
/**
|
|
* Session data un-setting
|
|
*
|
|
* Unsets the given session variable.
|
|
*
|
|
* @param $s_name string
|
|
* name of the session variable
|
|
*/
|
|
function UnsetSession($s_name)
|
|
{
|
|
$_SESSION[$s_name] = null;
|
|
unset($_SESSION[$s_name]);
|
|
}
|
|
|
|
function ZapSession()
|
|
{
|
|
global $aSessionVarNames;
|
|
|
|
if (Settings::get('DESTROY_SESSION')) {
|
|
if (session_id() != '') {
|
|
session_destroy();
|
|
}
|
|
} else {
|
|
foreach ($aSessionVarNames as $s_var_name) {
|
|
UnsetSession($s_var_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
$bReverseCaptchaCompleted = false; // records whether ATTACK_DETECTION_REVERSE_CAPTCHA has been completed successfully
|
|
session_start();
|
|
|
|
//
|
|
// Hook system: after session initialization
|
|
//
|
|
if (($incFile = hookFileExists("fmhookpostsess.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
|
|
//
|
|
// This array lists the private variables used by FormMail.
|
|
// We use the names here to cleanup the session when FormMail has
|
|
// finished its processing.
|
|
//
|
|
$aSessionVarNames = array("FormError","FormErrorInfo","FormErrorCode",
|
|
"FormErrorItems","FormData","FormIsUserError",
|
|
"FormAlerted","FormSavedFiles","FormIndex",
|
|
"FormList","FormKeep","VerifyImgString",
|
|
"turing_string"
|
|
);
|
|
|
|
UnsetSession("FormError"); // start with no error
|
|
UnsetSession("FormErrorInfo"); // start with no error
|
|
UnsetSession("FormErrorCode"); // start with no error
|
|
UnsetSession("FormErrorItems"); // start with no error
|
|
UnsetSession("FormData"); // start with no data
|
|
UnsetSession("FormIsUserError"); // start with no data
|
|
UnsetSession("FormAlerted"); // start with no data
|
|
|
|
//
|
|
// Note that HTTP_REFERER is easily spoofed, so there's no point in
|
|
// using it for security.
|
|
//
|
|
|
|
//
|
|
// SPECIAL_FIELDS is the list of fields that formmail.php looks for to
|
|
// control its operation
|
|
//
|
|
$SPECIAL_FIELDS = array(
|
|
"email", // email address of the person who filled in the form
|
|
"realname", // the real name of the person who filled in the form
|
|
"recipients", // comma-separated list of email addresses to which we'll send the results
|
|
"cc", // comma-separated list of email addresses to which we'll CC the results
|
|
"bcc", // comma-separated list of email addresses to which we'll BCC the results
|
|
"replyto", // comma-separated list of email addresses to whom replies should be sent
|
|
"required", // comma-separated list of fields that must be found in the input
|
|
"conditions", // complex condition tests
|
|
"fmcompute", // computations
|
|
"fmmodules", // list of modules required
|
|
"fmmode", // mode of operation
|
|
"mail_options", // comma-separated list of options
|
|
"good_url", // URL to go to on success
|
|
"good_template", // template file to display on success
|
|
"bad_url", // URL to go to on error
|
|
"bad_template", // template file to display on error
|
|
"template_list_sep", // separator when expanding lists in templates
|
|
"this_form", // the URL of the form (can be used by bad_url)
|
|
"subject", // subject for the email
|
|
"env_report", // comma-separated list of environment variables to report
|
|
"filter", // a supported filter to use
|
|
"filter_options", // options for using the filter
|
|
"filter_fields", // list of fields to filter (default is to filter all fields)
|
|
"filter_files", // list of file fields to filter (default is to filter no file fields)
|
|
"logfile", // log file to write to
|
|
"csvfile", // file to write CSV records to
|
|
"csvcolumns", // columns to save in the csvfile
|
|
"crm_url", // URL for sending data to the CRM; note that the
|
|
// value must have a valid prefix specified in TARGET_URLS
|
|
"crm_spec", // CRM specification (field mapping)
|
|
"crm_options", // comma-separated list of options to control CRM processing
|
|
"derive_fields", // a list of fields to derive from other fields
|
|
"file_names", // specifies names for files being uploaded
|
|
"autorespond", // specification for auto-responding
|
|
"arverify", // verification field to allow auto-responding
|
|
"imgverify", // verification field to allow submission
|
|
"multi_start", // set this field on the first page of a multi-page form sequence
|
|
"multi_keep", // set this field on the pages of a multi-page form sequence
|
|
// to the list of fields that should be kept when moving
|
|
// forward after going backwards
|
|
"next_form", // next form name or empty for last form
|
|
"multi_go_back", // this field should be set when the user clicks the
|
|
// back button or link in a multi-page form sequence
|
|
"alert_to", // email address to send alerts (errors) to
|
|
//
|
|
// fields for reCaptcha implementation
|
|
//
|
|
"recaptcha_response_field", // verification field to allow submission
|
|
"recaptcha_challenge_field", // challenge field
|
|
//
|
|
// reCaptcha version 2
|
|
//
|
|
"g-recaptcha-response",
|
|
"email_validation_time_secs" // set via the VALIDATE_EMAIL_DOMAIN process
|
|
);
|
|
|
|
//
|
|
// $SPECIAL_MULTI is the list of fields from $SPECIAL_FIELDS that can
|
|
// have multiple values, for example:
|
|
// name="conditions1"
|
|
// name="conditions2"
|
|
//
|
|
$SPECIAL_MULTI = array(
|
|
"conditions",
|
|
"fmcompute",
|
|
);
|
|
|
|
//
|
|
// $SPECIAL_ARRAYS is the list of fields from $SPECIAL_FIELDS that can
|
|
// be submitted as arrays of values, for example:
|
|
// <select name="recipients[]">
|
|
// <option value="sales">Sales</option>
|
|
// <option value="service">Service</option>
|
|
// </select>
|
|
//
|
|
$SPECIAL_ARRAYS = array(
|
|
"recipients",
|
|
"cc",
|
|
"bcc",
|
|
"replyto",
|
|
);
|
|
|
|
//
|
|
// $SPECIAL_NOSTRIP is the list of fields from $SPECIAL_FIELDS that
|
|
// should not be stripped (other than for magic_quotes_gpc reasons).
|
|
//
|
|
$SPECIAL_NOSTRIP = array(
|
|
"conditions",
|
|
"fmcompute",
|
|
"recaptcha_response_field",
|
|
"recaptcha_challenge_field",
|
|
"g-recaptcha-response",
|
|
"arverify",
|
|
"imgverify",
|
|
);
|
|
|
|
//
|
|
// VALID_MAIL_OPTIONS lists the valid mail_options words
|
|
//
|
|
$VALID_MAIL_OPTIONS = array(
|
|
"AlwaysEmailFiles" => true,
|
|
"AlwaysList" => true,
|
|
"CharSet" => true,
|
|
"DupHeader" => true,
|
|
"Exclude" => true,
|
|
"FromAddr" => true,
|
|
"FromLineStyle" => true,
|
|
"HTMLTemplate" => true,
|
|
"KeepLines" => true,
|
|
"NoEmpty" => true,
|
|
"NoPlain" => true,
|
|
"PlainTemplate" => true,
|
|
"SendMailFOption" => true,
|
|
"StartLine" => true,
|
|
"TemplateMissing" => true,
|
|
);
|
|
|
|
//
|
|
// VALID_CRM_OPTIONS lists the valid crm_options words
|
|
//
|
|
$VALID_CRM_OPTIONS = array(
|
|
"ErrorOnFail" => true,
|
|
);
|
|
|
|
//
|
|
// VALID_AR_OPTIONS lists the valid autorespond words
|
|
//
|
|
$VALID_AR_OPTIONS = array(
|
|
"Subject" => true,
|
|
"HTMLTemplate" => true,
|
|
"PlainTemplate" => true,
|
|
"TemplateMissing" => true,
|
|
"PlainFile" => true,
|
|
"HTMLFile" => true,
|
|
"FromAddr" => true,
|
|
"FromName" => true
|
|
);
|
|
|
|
//
|
|
// VALID_FILTER_OPTIONS lists the valid filter_options words
|
|
//
|
|
$VALID_FILTER_OPTIONS = array(
|
|
"Attach" => true,
|
|
"KeepInLine" => true,
|
|
"CSVHeading" => true,
|
|
"CSVSep" => true,
|
|
"CSVIntSep" => true,
|
|
"CSVQuote" => true,
|
|
"CSVEscPolicy" => true,
|
|
"CSVRaw" => true,
|
|
);
|
|
|
|
//
|
|
// SPECIAL_VALUES is set to the value of the fields we've found
|
|
// usage: $SPECIAL_VALUES["email"] is the value of the email field
|
|
//
|
|
$SPECIAL_VALUES = array();
|
|
//
|
|
// Array of mail options; set by the function 'ProcessMailOptions'
|
|
//
|
|
$MAIL_OPTS = array();
|
|
//
|
|
// Array of crm options; set by the function 'ProcessCRMOptions'
|
|
//
|
|
$CRM_OPTS = array();
|
|
//
|
|
// Array of autorespond options; set by the function 'ProcessAROptions'
|
|
//
|
|
$AR_OPTS = array();
|
|
//
|
|
// Array of filter options; set by the function 'ProcessFilterOptions'
|
|
//
|
|
$FILTER_OPTS = array();
|
|
|
|
//
|
|
// initialise $SPECIAL_VALUES so that we don't fail on using unset values
|
|
//
|
|
foreach ($SPECIAL_FIELDS as $sFieldName) {
|
|
$SPECIAL_VALUES[$sFieldName] = "";
|
|
}
|
|
|
|
//
|
|
// Defaults for some special fields....
|
|
//
|
|
$SPECIAL_VALUES['template_list_sep'] = ",";
|
|
|
|
//
|
|
// FORMATTED_INPUT contains the input variables formatted nicely
|
|
// This is used for error reporting and debugging only.
|
|
//
|
|
$FORMATTED_INPUT = array();
|
|
|
|
//
|
|
// $FILTER_ATTRIBS_LOOKUP is the parsed $FILTER_ATTRIBS array
|
|
//
|
|
$FILTER_ATTRIBS_LOOKUP = array();
|
|
|
|
//
|
|
// BuiltinFunctions provides additional functions that can be called via derive_fields
|
|
//
|
|
class BuiltinFunctions
|
|
{
|
|
private $_aFunctions;
|
|
|
|
function __construct()
|
|
{
|
|
$this->_aFunctions = array();
|
|
}
|
|
|
|
/**
|
|
* Adds a builtin function.
|
|
*
|
|
* @param string $s_name name of the function
|
|
* @param string $n_params number of parameters the function expects
|
|
*/
|
|
public function Add($s_name,$n_params)
|
|
{
|
|
$this->_aFunctions[$s_name] = array('nparams' => $n_params);
|
|
}
|
|
|
|
/**
|
|
* Calls a builtin function.
|
|
*
|
|
* @param string $s_name name of the function
|
|
* @param array $a_params list of parameters
|
|
* @param string &$s_result result of the function or error message
|
|
*
|
|
* @return boolean true on success, otherwise false
|
|
*/
|
|
public function Call($s_name,$a_params,&$s_result)
|
|
{
|
|
if (!isset($this->_aFunctions[$s_name])) {
|
|
$s_result = "Function '$s_name' is not a builtin function";
|
|
return (false);
|
|
}
|
|
$a_func = $this->_aFunctions[$s_name];
|
|
if (count($a_params) != $a_func['nparams']) {
|
|
$s_result = "Function '$s_name' expects " . $a_func['nparams'] . " parameters, " . count($a_params) .
|
|
" given.";
|
|
return (false);
|
|
}
|
|
$s_result = call_user_func_array($s_name,$a_params);
|
|
return (true);
|
|
}
|
|
}
|
|
|
|
$BuiltinFunctions = new BuiltinFunctions();
|
|
|
|
$reCaptchaProcessor = null;
|
|
if (Settings::get('RECAPTCHA_PRIVATE_KEY') !== "") {
|
|
//
|
|
// Assume version 1 unless g-recaptcha-response is found in the form data.
|
|
// If we cannot load recaptchalib, then we assume it's version 2.
|
|
//
|
|
$bRecaptchaVersion = 1;
|
|
if (isset($aFormVars['g-recaptcha-response']) && $aFormVars['g-recaptcha-response'] != '') {
|
|
$bRecaptchaVersion = 2;
|
|
}
|
|
if ($bRecaptchaVersion == 1 && (!includeFileExists("recaptchalib.php") || !@include_once("recaptchalib.php"))) {
|
|
$bRecaptchaVersion = 2;
|
|
}
|
|
|
|
if ($bRecaptchaVersion == 2) {
|
|
if (!function_exists('json_decode')) {
|
|
SendAlert("reCaptcha version 2 requires PHP version 5.2.0 or later",false,false);
|
|
}
|
|
|
|
/*
|
|
* Class: reCaptchaWrapper
|
|
* Description:
|
|
* Wraps processing of reCaptcha version 2.
|
|
*/
|
|
|
|
class reCaptchaWrapperV2
|
|
{
|
|
var $_sPrivate; // the private key
|
|
var $_bDone; // true when done
|
|
var $_Resp; // the response from reCaptcha
|
|
|
|
/*
|
|
* Method: reCaptchaWrapperV2 ctor
|
|
* Parameters: $s_priv the private key
|
|
* Returns: n/a
|
|
* Description:
|
|
* Initializes the wrapper ready to process reCaptcha.
|
|
*/
|
|
function __construct($s_priv)
|
|
{
|
|
$this->_sPrivate = $s_priv;
|
|
$this->_bDone = false;
|
|
}
|
|
|
|
/**
|
|
* Try to contact Google reCaptcha.
|
|
* PHP version 5.6.2 has problems with sockets, so this may fail with PHP 5.6.2.
|
|
*
|
|
* @param string $s_response the reCaptcha response.
|
|
*/
|
|
function _askGoogle($s_response)
|
|
{
|
|
$s_url = 'https://www.google.com/recaptcha/api/siteverify';
|
|
|
|
$a_post_data = array(
|
|
'secret' => $this->_sPrivate,
|
|
'response' => $s_response
|
|
);
|
|
if (isset($_SERVER['REMOTE_ADDR'])) {
|
|
$a_post_data['remoteip'] = $_SERVER['REMOTE_ADDR'];
|
|
}
|
|
|
|
FMDebug('Posting to google reCaptcha');
|
|
$recaptcha = new HTTPPost($s_url);
|
|
$a_resp_lines = $recaptcha->Post($a_post_data);
|
|
|
|
if ($a_resp_lines === false) {
|
|
FMDebug('reCaptcha via HTTPPost socket failed');
|
|
$s_resp = '{"success":false,"error-codes":["reCaptcha failed"]}';
|
|
} else {
|
|
$s_resp = implode('',$a_resp_lines);
|
|
FMDebug('reCaptcha via HTTPPost socket succeeded: ' . $s_resp);
|
|
}
|
|
/** @noinspection PhpComposerExtensionStubsInspection */
|
|
$this->_Resp = json_decode($s_resp,true);
|
|
}
|
|
|
|
/*
|
|
* Method: reCaptchaWrapper::Check
|
|
* Parameters: $s_response the reCaptcha response value
|
|
* $a_values field values
|
|
* $s_error returns the reCaptcha error code
|
|
* Returns: bool true on success, otherwise false
|
|
* Description:
|
|
* Performs the reCaptcha check and caches the result so it's
|
|
* only done once.
|
|
*/
|
|
function Check($s_response,$a_values,&$s_error)
|
|
{
|
|
if (!$this->_bDone) {
|
|
$this->_askGoogle($s_response);
|
|
}
|
|
$this->_bDone = true;
|
|
$s_error = "";
|
|
if (!$this->_Resp['success']) {
|
|
if (!isset($this->_Resp['error-codes']) || count($this->_Resp['error-codes']) == 0 ||
|
|
!$this->_Resp['error-codes'][0]
|
|
) {
|
|
$s_error = 'verification failed (error not specified)';
|
|
} else {
|
|
$s_error = $this->_Resp['error-codes'][0];
|
|
}
|
|
}
|
|
return ($this->_Resp['success']);
|
|
}
|
|
}
|
|
|
|
$reCaptchaProcessor = new reCaptchaWrapperV2(Settings::get('RECAPTCHA_PRIVATE_KEY'));
|
|
} else {
|
|
|
|
/*
|
|
* Class: reCaptchaWrapper
|
|
* Description:
|
|
* Wraps processing of reCaptcha version 1.
|
|
*/
|
|
|
|
class reCaptchaWrapper
|
|
{
|
|
var $_sPrivate; // the private key
|
|
var $_bDone; // true when done
|
|
var $_Resp; // the response from reCaptcha
|
|
|
|
/*
|
|
* Method: reCaptchaWrapper ctor
|
|
* Parameters: $s_priv the private key
|
|
* Returns: n/a
|
|
* Description:
|
|
* Initializes the wrapper ready to process reCaptcha.
|
|
*/
|
|
function __construct($s_priv)
|
|
{
|
|
$this->_sPrivate = $s_priv;
|
|
$this->_bDone = false;
|
|
}
|
|
|
|
/*
|
|
* Method: reCaptchaWrapper::Check
|
|
* Parameters: $s_response the reCaptcha response value
|
|
* $a_values field values
|
|
* $s_error returns the reCaptcha error code
|
|
* Returns: bool true on success, otherwise false
|
|
* Description:
|
|
* Performs the reCaptcha check and caches the result so it's
|
|
* only done once.
|
|
*/
|
|
function Check($s_response,$a_values,&$s_error)
|
|
{
|
|
if (!$this->_bDone) {
|
|
/** @noinspection PhpUndefinedFunctionInspection */
|
|
$this->_Resp = recaptcha_check_answer($this->_sPrivate,
|
|
$_SERVER["REMOTE_ADDR"],
|
|
$a_values["recaptcha_challenge_field"],
|
|
$s_response);
|
|
}
|
|
$this->_bDone = true;
|
|
$s_error = "";
|
|
if (!$this->_Resp->is_valid) {
|
|
$s_error = $this->_Resp->error;
|
|
}
|
|
return ($this->_Resp->is_valid);
|
|
}
|
|
}
|
|
|
|
$reCaptchaProcessor = new reCaptchaWrapper(Settings::get('RECAPTCHA_PRIVATE_KEY'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Contains a list of valid email addresses and
|
|
* email address patterns. Provides methods for checking the validity of an
|
|
* email address.
|
|
*/
|
|
class EmailChecker
|
|
{
|
|
|
|
var $_aAddresses; // valid email addresses (as keys)
|
|
var $_aTargetPatterns; // valid email address patterns
|
|
|
|
/**
|
|
* Constructs the object.
|
|
*
|
|
* @param array $a_aliases
|
|
* @param array $a_patterns an array of email address patterns
|
|
*/
|
|
function __construct($a_aliases = array(),$a_patterns = array())
|
|
{
|
|
$this->_aAddresses = array();
|
|
$this->_aTargetPatterns = $a_patterns;
|
|
foreach ($a_aliases as $s_list) {
|
|
$this->AddAddresses($s_list);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds an email address to the list of valid * email addresses.
|
|
*
|
|
* @param string $s_addr an email address
|
|
*/
|
|
function AddAddress($s_addr)
|
|
{
|
|
$this->_aAddresses[$s_addr] = true;
|
|
}
|
|
|
|
/**
|
|
* Adds a comma-separated list of email addresses to the list of valid email addresses.
|
|
*
|
|
* @param string $s_list a list of email addresses
|
|
*/
|
|
function AddAddresses($s_list)
|
|
{
|
|
$a_addrs = TrimArray(explode(",",$s_list));
|
|
foreach ($a_addrs as $s_addr) {
|
|
$this->AddAddress($s_addr);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks an email address for validity.
|
|
*
|
|
* @param string $s_email an email address
|
|
*
|
|
* @return boolean true if the address is valid, otherwise false
|
|
*/
|
|
function CheckAddress($s_email)
|
|
{
|
|
$b_is_valid = false;
|
|
if (isset($this->_aAddresses[$s_email])) {
|
|
$b_is_valid = true;
|
|
} else {
|
|
for ($ii = 0 ; $ii < count($this->_aTargetPatterns) ; $ii++) {
|
|
//
|
|
// prepend / with \
|
|
//
|
|
$s_pat = "/" . str_replace('/','\\/',$this->_aTargetPatterns[$ii]) . "/i";
|
|
if (preg_match($s_pat,$s_email)) {
|
|
$b_is_valid = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return ($b_is_valid);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create the object for checking emails
|
|
//
|
|
$ValidEmails = new EmailChecker(Settings::get('EMAIL_ADDRS'),Settings::get('TARGET_EMAIL'));
|
|
|
|
/**
|
|
* Class SpecialFieldsManager
|
|
*
|
|
* Encapsulate storage and access to special fields.
|
|
*
|
|
* NOTE: this is initial code implemented in version 9.17 and is not complete.
|
|
* It's part of our transition to a more complete Object Oriented code base
|
|
* which is targeted for version 9.??.
|
|
*/
|
|
class SpecialFieldsManager
|
|
{
|
|
/**
|
|
* list of special field names
|
|
*
|
|
* @var array
|
|
*/
|
|
private $_aFieldNames;
|
|
|
|
/**
|
|
* list of special field names that can have multiple values by a numeric suffic
|
|
*
|
|
* @var array
|
|
*/
|
|
private $_aMultiFieldNames;
|
|
|
|
/**
|
|
* list of special field names that can be arrays
|
|
*
|
|
* @var array
|
|
*/
|
|
private $_aArrayFieldNames;
|
|
|
|
/**
|
|
* list of special field names that must be used in raw format (not cleaned/stripped)
|
|
*
|
|
* @var array
|
|
*/
|
|
private $_aRawFieldNames;
|
|
|
|
/**
|
|
* list of field values keyed by field name
|
|
*
|
|
* @var array
|
|
*/
|
|
private $_aValues;
|
|
|
|
function __construct($a_field_names,$a_multi_field_names,$a_array_field_names,$a_raw_field_names)
|
|
{
|
|
$this->_aFieldNames = $a_field_names;
|
|
$this->_aMultiFieldNames = $a_multi_field_names;
|
|
$this->_aArrayFieldNames = $a_array_field_names;
|
|
$this->_aRawFieldNames = $a_raw_field_names;
|
|
}
|
|
|
|
public static function getCleanEmail($s_email)
|
|
{
|
|
if (function_exists('filter_var')) {
|
|
return filter_var($s_email,FILTER_VALIDATE_EMAIL) === false ? '' : $s_email;
|
|
} else {
|
|
// basic email pattern if filter_var is not available
|
|
return preg_match('/^[a-z0-9]+@[a-z\.]+$/i',$s_email) ? $s_email : '';
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO implement this!
|
|
//$SpecialFields = new SpecialFieldsManager($SPECIAL_FIELDS,$SPECIAL_MULTI,$SPECIAL_ARRAYS,$SPECIAL_NOSTRIP);
|
|
|
|
/**
|
|
* Class FieldManager
|
|
*
|
|
* Encapsulates storage and lookup of field data.
|
|
*
|
|
* NOTE: this is initial code implemented in version 8.27 and is not complete.
|
|
* It's part of our transition to a more complete Object Oriented code base
|
|
* which is targeted for version 9.??.
|
|
*/
|
|
class FieldManager
|
|
{
|
|
|
|
/**
|
|
* list of fields keyed by field name
|
|
*
|
|
* @var array
|
|
*/
|
|
private $_aFields;
|
|
|
|
/**
|
|
* list of file fields keyed by field name (not currently used)
|
|
*
|
|
* @var array
|
|
*/
|
|
private $_aFileFields;
|
|
|
|
/**
|
|
* last array separator specified
|
|
*
|
|
* @var string
|
|
*/
|
|
private $_sArraySep;
|
|
|
|
/**
|
|
* array separator to use (after substitutions)
|
|
*
|
|
* @var string
|
|
*/
|
|
private $_sArraySepValue;
|
|
|
|
/**
|
|
* counter for unique generation
|
|
*
|
|
* @var int
|
|
*/
|
|
private $_nUnique;
|
|
|
|
/**
|
|
* Constructs the object.
|
|
*
|
|
* @param array $a_fields list of fields
|
|
* @param array $a_file_fields list of file fields
|
|
*/
|
|
public function __construct($a_fields = array(),$a_file_fields = array())
|
|
{
|
|
$this->_sArraySepValue = $this->_sArraySep = "";
|
|
$this->_aFields = $this->_aFileFields = array();
|
|
$this->_nUnique = 0;
|
|
$this->Init($a_fields,$a_file_fields);
|
|
}
|
|
|
|
public function GetFields()
|
|
{
|
|
return $this->_aFields;
|
|
}
|
|
|
|
/**
|
|
* Initializes the object with the field data.
|
|
*
|
|
* @param array $a_fields list of fields
|
|
* @param array $a_file_fields list of file fields
|
|
*
|
|
* @return void
|
|
*/
|
|
public function Init($a_fields = array(),$a_file_fields = array())
|
|
{
|
|
$this->_aFields = $a_fields;
|
|
$this->_aFileFields = $a_file_fields;
|
|
}
|
|
|
|
/**
|
|
* Return a field value.
|
|
* Empty string is returned if the field is
|
|
* not found. File fields return the original name of the uploaded file.
|
|
*
|
|
* @param string $s_fld name of the field
|
|
* @param string $s_array_sep string to use to separate array values
|
|
*
|
|
* @return string the field's value
|
|
*/
|
|
public function GetFieldValue($s_fld,$s_array_sep = ";")
|
|
{
|
|
if (!isset($this->_aFields[$s_fld])) {
|
|
if (($s_value = $this->GetFileName($s_fld)) === false) {
|
|
$s_value = "";
|
|
}
|
|
} else {
|
|
if (is_array($this->_aFields[$s_fld])) {
|
|
$s_value = implode($this->_GetArraySep($s_array_sep),$this->_aFields[$s_fld]);
|
|
} else {
|
|
$s_value = (string)$this->_aFields[$s_fld];
|
|
}
|
|
}
|
|
return ($s_value);
|
|
}
|
|
|
|
/**
|
|
* Return a field value.
|
|
* Empty string is returned if the field is
|
|
* not found. File fields return the original name of the uploaded file.
|
|
* The returned value is HTML-safe.
|
|
* b_text_subs performs text substitutions on the field value
|
|
* that are not affected by HTML-safety replacement. This means
|
|
*
|
|
* @param string $s_fld name of the field
|
|
* @param bool $b_text_subs perform text substitutions
|
|
* @param string $s_array_sep string to use to separate array values
|
|
*
|
|
* @return string the field's value
|
|
* @uses $TEXT_SUBS can be used to force allowance of particular HTML tags. Note
|
|
* that b_text_subs is not yet implemented for array * field
|
|
* values.
|
|
*
|
|
*/
|
|
public function GetSafeFieldValue($s_fld,$b_text_subs = false,$s_array_sep = ";")
|
|
{
|
|
//
|
|
// for array values, insert the array separator after making
|
|
// the individual values HTML-safe
|
|
// The equivalent logic up to and including version 8.24 used
|
|
// htmlspecialchars not htmlentities.
|
|
// The use of htmlentities broke UTF-8 template processing,
|
|
// and this was reported in version 8.28.
|
|
// By specifying the character set, we trigger the use of
|
|
// htmlspecialchars
|
|
// so the logic is equivalent to the old logic.
|
|
//
|
|
if (isset($this->_aFields[$s_fld]) && is_array($this->_aFields[$s_fld])) {
|
|
$s_value = implode($this->_GetArraySep($s_array_sep),
|
|
HTMLEntitiesArray($this->_aFields[$s_fld],false,
|
|
GetMailOption("CharSet")));
|
|
} else {
|
|
if (!isset($this->_aFields[$s_fld])) {
|
|
if (($s_name = $this->GetFileName($s_fld)) === false) {
|
|
$s_name = "";
|
|
}
|
|
$s_value = $s_name;
|
|
} else {
|
|
$s_value = (string)$this->_aFields[$s_fld];
|
|
}
|
|
if ($b_text_subs) {
|
|
list ($s_value,$a_subs_data) = $this->_PrepareTextSubstitute($s_value);
|
|
}
|
|
$s_value = FixedHTMLEntities($s_value,GetMailOption("CharSet"));
|
|
if ($b_text_subs) {
|
|
$s_value = $this->_CompleteTextSubstitute($s_value,$a_subs_data);
|
|
}
|
|
}
|
|
return ($s_value);
|
|
}
|
|
|
|
/**
|
|
* Prepares a value for text substitution using $TEXT_SUBS.
|
|
*
|
|
* @param string $s_value
|
|
* the value to perform substitutions on
|
|
*
|
|
* @return array [0]=>the processed value, [1]=>array of substitution data
|
|
*/
|
|
private function _PrepareTextSubstitute($s_value)
|
|
{
|
|
$a_subs_data = array();
|
|
$a_text_subs = Settings::get('TEXT_SUBS');
|
|
for ($ii = 0 ; $ii < count($a_text_subs) ; $ii++) {
|
|
$a_match_data = array();
|
|
if (($n_matches = preg_match_all($a_text_subs[$ii]["srch"],$s_value,$a_matches,
|
|
PREG_OFFSET_CAPTURE)) !== false && $n_matches > 0
|
|
) {
|
|
$a_match_data["srch"] = $a_text_subs[$ii]["srch"];
|
|
$a_match_data["repl"] = $a_text_subs[$ii]["repl"];
|
|
$s_value = $this->_HTMLSafeSubstitute($s_value,$a_matches,$a_match_data);
|
|
}
|
|
$a_subs_data[$ii] = $a_match_data;
|
|
}
|
|
return (array($s_value,$a_subs_data));
|
|
}
|
|
|
|
/**
|
|
* Completes text substitution started by _PrepareTextSubstitute.
|
|
*
|
|
* @param string $s_value
|
|
* the value to perform substitutions on
|
|
* @param array $a_subs_data
|
|
* data that describes the substitutions to perform
|
|
*
|
|
* @return string the new value
|
|
*/
|
|
private function _CompleteTextSubstitute($s_value,$a_subs_data)
|
|
{
|
|
//
|
|
// because later substitutions can capture earlier ones,
|
|
// we have to process them all in reverse order
|
|
//
|
|
for ($ii = count($a_subs_data) ; --$ii >= 0 ;) {
|
|
$a_subs_list = $a_subs_data[$ii];
|
|
for ($jj = count($a_subs_list) ; --$jj >= 0 ;) {
|
|
$s_code = $a_subs_list[$jj]["code"];
|
|
$s_subs = $a_subs_list[$jj]["subs"];
|
|
$s_value = str_replace($s_code,$s_subs,$s_value);
|
|
}
|
|
}
|
|
return ($s_value);
|
|
}
|
|
|
|
/**
|
|
* Generates a unique string from a base string
|
|
*
|
|
* @param string $s_base
|
|
* a base of the unique string
|
|
*
|
|
* @return string a unique string
|
|
*/
|
|
private function _MakeUniqueString($s_base)
|
|
{
|
|
$n_uniq = $this->_nUnique++;
|
|
return ($s_base . "_" . str_pad("$n_uniq",5,"0",STR_PAD_LEFT));
|
|
}
|
|
|
|
/**
|
|
* Performs a temporary substitution on a string of the given matches with
|
|
* the given replacement specification.
|
|
* This makes the replacement using
|
|
* a special indicator string that can be substituted for the real value
|
|
* later. This allows non-replaced parts of the string to be processed
|
|
* and made safe for HTML entities, without affecting our actual
|
|
* replacements.
|
|
*
|
|
* @param string $s_value
|
|
* the string to substitute
|
|
* @param array $a_matches
|
|
* list of matches and offsets from preg_match_all
|
|
* @param array $a_match_data
|
|
* contains some data, and returns replacement data for the
|
|
* temporary substitution
|
|
*
|
|
* @return string the temporarily substituted string
|
|
*/
|
|
private function _HTMLSafeSubstitute($s_value,$a_matches,&$a_match_data)
|
|
{
|
|
$a_matches = $a_matches[0]; // we're only interested in the full pattern
|
|
// matches
|
|
$s_srch = $a_match_data["srch"];
|
|
$s_repl = $a_match_data["repl"];
|
|
|
|
//
|
|
// to preserve offsets, we must process the string in reverse order
|
|
// of the matches; since we don't assume the array is ordered
|
|
// by ascending offset, we'll sort it now
|
|
//
|
|
usort($a_matches,function ($a,$b) {
|
|
return $b[1] - $a[1];
|
|
});
|
|
$a_match_data = array();
|
|
for ($ii = 0 ; $ii < count($a_matches) ; $ii++) {
|
|
$s_match = $a_matches[$ii][0];
|
|
$i_offset = $a_matches[$ii][1];
|
|
$i_len = strlen($s_match);
|
|
|
|
$s_subs = preg_replace($s_srch,$s_repl,$s_match);
|
|
//
|
|
// the code string must e HTML safe so it doesn't get altered
|
|
// before we can replace it; we use ! at the edges so that
|
|
// other patterns can successfully match word boundaries
|
|
// An improvement would be to determine the type of characters
|
|
// at the edges of the matched string, then choose the substitution
|
|
// edges accordingly.
|
|
//
|
|
$s_code = "!" . $this->_MakeUniqueString("SUBS") . "!";
|
|
$a_match_data[$ii] = array("subs" => $s_subs,"code" => $s_code);
|
|
|
|
$s_value = substr($s_value,0,$i_offset) . $s_code . substr($s_value,$i_offset + $i_len);
|
|
}
|
|
return ($s_value);
|
|
}
|
|
|
|
/**
|
|
* Test if a field is set in the $_aFields array or in the uploaded
|
|
* files.
|
|
*
|
|
* @param string $s_fld name of the field
|
|
*
|
|
* @return bool true if the field has a value
|
|
*/
|
|
public function IsFieldSet($s_fld)
|
|
{
|
|
if (isset($this->_aFields[$s_fld])) {
|
|
return (true);
|
|
}
|
|
if (Settings::get('FILEUPLOADS')) {
|
|
if (isset($this->_aFileFields[$s_fld])) {
|
|
return (true);
|
|
}
|
|
if (IsSetSession("FormSavedFiles")) {
|
|
$a_saved_files = GetSession("FormSavedFiles");
|
|
if (isset($a_saved_files[$s_fld])) {
|
|
return (true);
|
|
}
|
|
}
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
public static function GetFileUploadErrorMesg($n_error)
|
|
{
|
|
$s_mesg = '';
|
|
switch ($n_error) {
|
|
case 0: // no error
|
|
break;
|
|
case 1:
|
|
$s_mesg = GetMessage(MSG_FILE_UPLOAD_ERR1);
|
|
break;
|
|
case 2:
|
|
$s_mesg = GetMessage(MSG_FILE_UPLOAD_ERR2);
|
|
break;
|
|
case 3:
|
|
$s_mesg = GetMessage(MSG_FILE_UPLOAD_ERR3);
|
|
break;
|
|
case 4:
|
|
$s_mesg = GetMessage(MSG_FILE_UPLOAD_ERR4);
|
|
break;
|
|
case 6:
|
|
$s_mesg = GetMessage(MSG_FILE_UPLOAD_ERR6);
|
|
break;
|
|
case 7:
|
|
$s_mesg = GetMessage(MSG_FILE_UPLOAD_ERR7);
|
|
break;
|
|
case 8:
|
|
$s_mesg = GetMessage(MSG_FILE_UPLOAD_ERR8);
|
|
break;
|
|
default:
|
|
$s_mesg = GetMessage(MSG_FILE_UPLOAD_ERR_UNK,array("ERRNO" => $n_error));
|
|
break;
|
|
}
|
|
return $s_mesg;
|
|
}
|
|
|
|
/**
|
|
* Tests a field against the $_aFields array for emptiness.
|
|
* If the var isn't found there, then the POSTed files array is checked.
|
|
* Returns true if the field is empty (a specific error may
|
|
* be returned in the $s_mesg parameter).
|
|
*
|
|
* @param string $s_fld name of the field
|
|
* @param string $s_mesg returns an error message, where possible
|
|
*
|
|
* @return bool true if the field is empty
|
|
*/
|
|
public function TestFieldEmpty($s_fld,&$s_mesg)
|
|
{
|
|
$s_mesg = "";
|
|
$b_empty = TRUE;
|
|
if (!isset($this->_aFields[$s_fld])) {
|
|
//
|
|
// Each file var is an array with these elements:
|
|
// "name" => The original name of the file on the client machine.
|
|
// "type" => The mime type of the file, if the browser provided this
|
|
// information.
|
|
// "tmp_name" => The temporary filename of the file in which the
|
|
// uploaded file was stored on the server.
|
|
// "error" => The error code associated with this file upload.
|
|
// NOTE: "error" was added in PHP 4.2.0
|
|
// "size" => The size, in bytes, of the uploaded file.
|
|
//
|
|
// Error codes (the constants are only available from PHP 4.3.0 so
|
|
// we have to use the raw numbers):
|
|
// UPLOAD_ERR_OK
|
|
// Value: 0; There is no error, the file uploaded with success.
|
|
// UPLOAD_ERR_INI_SIZE
|
|
// Value: 1; The uploaded file exceeds the upload_max_filesize
|
|
// directive in php.ini.
|
|
// UPLOAD_ERR_FORM_SIZE
|
|
// Value: 2; The uploaded file exceeds the MAX_FILE_SIZE directive
|
|
// that was specified in the html form.
|
|
// UPLOAD_ERR_PARTIAL
|
|
// Value: 3; The uploaded file was only partially uploaded.
|
|
// UPLOAD_ERR_NO_FILE
|
|
// Value: 4; No file was uploaded.
|
|
//
|
|
if (Settings::get('FILEUPLOADS')) {
|
|
if (IsSetSession("FormSavedFiles")) {
|
|
$a_saved_files = GetSession("FormSavedFiles");
|
|
if (isset($a_saved_files[$s_fld])) {
|
|
$a_upload = $a_saved_files[$s_fld];
|
|
} elseif (isset($this->_aFileFields[$s_fld])) {
|
|
$a_upload = $this->_aFileFields[$s_fld];
|
|
}
|
|
} elseif (isset($this->_aFileFields[$s_fld])) {
|
|
$a_upload = $this->_aFileFields[$s_fld];
|
|
}
|
|
}
|
|
if (isset($a_upload)) {
|
|
if (isset($a_upload["tmp_name"]) && !empty($a_upload["tmp_name"]) &&
|
|
isset($a_upload["name"]) && !empty($a_upload["name"])
|
|
) {
|
|
if ($this->_IsUploadedFile($a_upload)) {
|
|
$b_empty = false;
|
|
}
|
|
}
|
|
if ($b_empty && isset($a_upload["error"])) {
|
|
$s_mesg = FieldManager::GetFileUploadErrorMesg($a_upload["error"]);
|
|
}
|
|
}
|
|
} else {
|
|
$b_empty = FieldManager::IsEmpty($this->_aFields[$s_fld]);
|
|
}
|
|
return ($b_empty);
|
|
}
|
|
|
|
/**
|
|
* Same as "empty" but checks for true emptiness if ZERO_IS_EMPTY is
|
|
* set to false.
|
|
*
|
|
* @param string $s_value the value to test
|
|
*
|
|
* @return bool true if the value is "empty"
|
|
*/
|
|
public static function IsEmpty($s_value)
|
|
{
|
|
if (Settings::get('ZERO_IS_EMPTY') || is_array($s_value)) {
|
|
return (empty($s_value));
|
|
} else {
|
|
return ($s_value === "");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Makes substitutions on strings as specified in the configuration setting $TEXT_SUBS.
|
|
*
|
|
* @param string $s_str a string on which to perform substitutions
|
|
*
|
|
* @return string the string, substituted
|
|
*/
|
|
public static function Substitute($s_str)
|
|
{
|
|
|
|
$a_srch = $a_repl = array();
|
|
foreach (Settings::get('TEXT_SUBS') as $a_sub) {
|
|
if (isset($a_sub["srch"]) && isset($a_sub["repl"]) && $a_sub["srch"] !== "") {
|
|
$a_srch[] = $a_sub["srch"];
|
|
$a_repl[] = $a_sub["repl"];
|
|
}
|
|
}
|
|
return (preg_replace($a_srch,$a_repl,$s_str));
|
|
}
|
|
|
|
/**
|
|
* Computes the array separation string.
|
|
* $s_sep is subject to
|
|
* substitutions specified in $TEXT_SUBS, and then returned.
|
|
*
|
|
* @param string $s_sep the string specified for array separations
|
|
*
|
|
* @return string the string to use for array separations
|
|
*/
|
|
private function _GetArraySep($s_sep)
|
|
{
|
|
//
|
|
// check for cached (previously calculated) value
|
|
//
|
|
if ($s_sep !== $this->_sArraySep) {
|
|
$this->_sArraySep = $s_sep;
|
|
$this->_sArraySepValue = FieldManager::Substitute($this->_sArraySep);
|
|
}
|
|
return ($this->_sArraySepValue);
|
|
}
|
|
|
|
/**
|
|
* Return the info for the uploaded file, or false on error.
|
|
*
|
|
* @param string $s_fld field name
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function GetFileInfo($s_fld)
|
|
{
|
|
if (Settings::get('FILEUPLOADS')) {
|
|
//
|
|
// Must look at new file uploads first.
|
|
//
|
|
if (isset($this->_aFileFields[$s_fld]) && !empty($this->_aFileFields[$s_fld])) {
|
|
$a_upload = $this->_aFileFields[$s_fld];
|
|
} elseif (IsSetSession("FormSavedFiles")) {
|
|
$a_saved_files = GetSession("FormSavedFiles");
|
|
if (isset($a_saved_files[$s_fld])) {
|
|
$a_upload = $a_saved_files[$s_fld];
|
|
}
|
|
}
|
|
}
|
|
if (isset($a_upload)) {
|
|
if (isset($a_upload["tmp_name"]) && !empty($a_upload["tmp_name"]) &&
|
|
isset($a_upload["name"]) && !empty($a_upload["name"]) &&
|
|
$this->_IsUploadedFile($a_upload)
|
|
) {
|
|
return ($a_upload);
|
|
}
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
/**
|
|
* Return the original name of the uploaded file or false on error.
|
|
*
|
|
* @param string $s_fld field name
|
|
*
|
|
* @return bool
|
|
* @see \FormMail\UnitTests\GetFileNameTest
|
|
*/
|
|
public function GetFileName($s_fld)
|
|
{
|
|
if (($a_upload = $this->GetFileInfo($s_fld)) !== false) {
|
|
return ($a_upload["name"]);
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
/**
|
|
* Check if a file is a valid uploaded file.
|
|
*
|
|
* @param array $a_file_spec file specification
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function IsUploadedFile($a_file_spec)
|
|
{
|
|
//
|
|
// $a_file_spec["moved"] is our own internal flag to say we've
|
|
// saved the file
|
|
//
|
|
if (isset($a_file_spec["moved"]) && $a_file_spec["moved"]) {
|
|
return (true);
|
|
}
|
|
return (is_uploaded_file($a_file_spec["tmp_name"]));
|
|
}
|
|
|
|
/**
|
|
* Internal access to static method. This allows us to mock the operation in unit tests.
|
|
*
|
|
* @param $a_file_spec
|
|
*
|
|
* @return bool
|
|
*/
|
|
protected function _IsUploadedFile($a_file_spec)
|
|
{
|
|
return self::IsUploadedFile($a_file_spec);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Perform general line folding.
|
|
// This function can be used for RFC 2822 line folding, as well
|
|
// Quoted Printable soft line breaks (RFC 2045).
|
|
// $s_before lists the characters before which we should fold the line.
|
|
// $s_after lists the characters after which we should fold the line.
|
|
// $s_fold is the string to insert to fold the line.
|
|
//
|
|
function LineFolding($s_str,$i_max_line,$s_before,$s_after,$s_fold)
|
|
{
|
|
$i_str_len = strlen($s_str);
|
|
$ii = $i_start = 0;
|
|
$i_line_len = 0;
|
|
while ($ii < $i_str_len) {
|
|
if ($i_line_len == $i_max_line) {
|
|
//
|
|
// fold this line:
|
|
// search backwards for a character at which we can
|
|
// fold the line
|
|
//
|
|
$b_done = false;
|
|
for ($jj = $ii ; !$b_done && $jj > $i_start ; $jj--) {
|
|
$b_found = false;
|
|
if (strpos($s_before,$s_str[$jj]) !== false) {
|
|
//
|
|
// fold before this character
|
|
//
|
|
$b_found = true;
|
|
} elseif (strpos($s_after,$s_str[$jj]) !== false) {
|
|
//
|
|
// fold after this character
|
|
//
|
|
$jj++;
|
|
$b_found = true;
|
|
}
|
|
if ($b_found) {
|
|
$s_str = substr($s_str,0,$jj) . $s_fold . substr($s_str,$jj);
|
|
$i_fold_len = strlen($s_fold);
|
|
$i_str_len += $i_fold_len; // the additional chars we inserted
|
|
$i_start = $jj + $i_fold_len; // start of the next line
|
|
$b_done = true;
|
|
}
|
|
}
|
|
//
|
|
// if we cannot fold and shorten the line,
|
|
// ignore this and try for the next line
|
|
//
|
|
if ($b_done) {
|
|
$ii = $i_start;
|
|
} else {
|
|
$i_start = $ii;
|
|
}
|
|
$i_line_len = 0;
|
|
} elseif (substr($s_str,$ii,2) == "\r\n") {
|
|
//
|
|
// end of line found - reset counters
|
|
//
|
|
$i_line_len = 0;
|
|
$ii += 2;
|
|
$i_start = $ii;
|
|
} else {
|
|
$ii++;
|
|
$i_line_len++;
|
|
}
|
|
}
|
|
return ($s_str);
|
|
}
|
|
|
|
//
|
|
// Quoted Printable Encoding with soft line breaks.
|
|
//
|
|
// Process a string to fit the requirements of RFC2045 section 6.7. Note that
|
|
// this works, but replaces more characters than the minimum set.
|
|
// Prior to version 8.34, for readability the spaces were not encoded, which was
|
|
// WRONG (see http://tools.ietf.org/html/rfc2047#section-2). Spaces must
|
|
// be encoded.
|
|
//
|
|
// Adapted from:
|
|
// http://www.php.net/manual/en/function.quoted-printable-decode.php
|
|
// Note that we *must* split long lines because a QP string might not
|
|
// contain any Folding White Space (FWS). In this case, it would
|
|
// not be possible to fold the line according to RFC 2822.
|
|
// Therefore, we need to use Soft Line Breaks as defined by the
|
|
// Quoted Printable definition in RFC 2045.
|
|
//
|
|
// Set $i_max_line to -ve to skip the line folding.
|
|
//
|
|
function QPEncode($s_str,$i_max_line)
|
|
{
|
|
//
|
|
// According to RFC2045 section 6.7 point (4), we need to keep any
|
|
// actual \r\n breaks encoded in the QP. The original code replaced
|
|
// them with CRLF pairs.
|
|
//
|
|
$s_str = str_replace('%','=',rawurlencode($s_str));
|
|
if ($i_max_line < 0) {
|
|
return ($s_str);
|
|
} else {
|
|
$s_before = "="; // characters before which we can fold the line
|
|
return (LineFolding($s_str,$i_max_line,$s_before,"","=\r\n"));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Peform header line folding according to RFC 2822.
|
|
// $s_before lists the characters before which we should fold the line.
|
|
// $s_after lists the characters after which we should fold the line.
|
|
// Characters left out of folding:
|
|
// [] are part of no-fold-literal
|
|
// () are part of comments, and should work, but don't
|
|
//
|
|
function HeaderFolding($s_str,$i_max_line = RFCLINELEN,$s_before = "<",$s_after = ">;, ")
|
|
{
|
|
return (LineFolding($s_str,$i_max_line,$s_before,$s_after,"\r\n "));
|
|
}
|
|
|
|
//
|
|
// Access the www.tectite.com website to get the current version.
|
|
//
|
|
function CheckVersion()
|
|
{
|
|
global $FM_VERS,$php_errormsg;
|
|
|
|
$http_get = new HTTPGet("http://www.tectite.com/fmversion.txt");
|
|
$php_errormsg = ""; // clear this out in case we get an error that doesn't set it
|
|
FMDebug("CheckVersion");
|
|
if (($a_lines = $http_get->Read()) !== false) {
|
|
//
|
|
// version file looks like this:
|
|
// Version=versionumber
|
|
// Message=a message to send in the alert
|
|
//
|
|
$s_version = "";
|
|
$s_message = "";
|
|
$s_line = "";
|
|
$b_in_mesg = false;
|
|
foreach ($a_lines as $s_line) {
|
|
if ($b_in_mesg) {
|
|
$s_message .= $s_line;
|
|
} else {
|
|
$s_prefix = substr($s_line,0,8);
|
|
if ($s_prefix == "Message=") {
|
|
$s_message .= substr($s_line,8);
|
|
$b_in_mesg = true;
|
|
} elseif ($s_prefix == "Version=") {
|
|
$s_version = substr($s_line,8);
|
|
}
|
|
}
|
|
}
|
|
$s_version = str_replace("\r","",$s_version);
|
|
$s_version = str_replace("\n","",$s_version);
|
|
$s_stop_mesg = GetMessage(MSG_END_VERS_CHK);
|
|
FMDebug("CheckVersion: vers=$s_version");
|
|
if ((float)$s_version > (float)$FM_VERS) {
|
|
SendAlert(GetMessage(MSG_VERS_CHK,array(
|
|
"TECTITE" => "www.tectite.com",
|
|
"FM_VERS" => "$FM_VERS",
|
|
"NEWVERS" => $s_version,
|
|
)) .
|
|
"\n$s_message\n$s_stop_mesg",true,true);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for new FormMail version
|
|
//
|
|
function Check4Update($s_chk_file,$s_id = "")
|
|
{
|
|
global $lNow,$php_errormsg;
|
|
|
|
@ $l_last_chk = filemtime($s_chk_file);
|
|
if ($l_last_chk === false || $lNow - $l_last_chk >= (Settings::get('CHECK_DAYS') * 24 * 60 * 60)) {
|
|
CheckVersion();
|
|
//
|
|
// update the check file's time stamp
|
|
//
|
|
@ $fp = fopen($s_chk_file,"w");
|
|
if ($fp !== false) {
|
|
fwrite($fp,"FormMail version check " .
|
|
(empty($s_id) ? "" : "for identifier '$s_id' ") .
|
|
"at " . date("H:i:s d-M-Y",$lNow) . "\n");
|
|
fclose($fp);
|
|
} else {
|
|
SendAlert(GetMessage(MSG_CHK_FILE_ERROR,array("FILE" => $s_chk_file,
|
|
"ERROR" => CheckString($php_errormsg)
|
|
)));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Perform various processing at the end of the script's execution.
|
|
//
|
|
function OnExit()
|
|
{
|
|
FMDebug("OnExit");
|
|
|
|
//
|
|
// Check the www.tectite.com website for a new version, but only
|
|
// do this check once every CHECK_DAYS days (or on server reboot).
|
|
//
|
|
if (Settings::get('CHECK_FOR_NEW_VERSION')) {
|
|
global $SERVER;
|
|
|
|
$a_targets = Settings::get('TARGET_EMAIL');
|
|
if (isset($a_targets[0])) {
|
|
//
|
|
// use the first few characters of the MD5 of first email
|
|
// address pattern from $TARGET_EMAIL to get a unique file
|
|
// for the server
|
|
//
|
|
$s_id = "";
|
|
if (isset($SERVER) && !empty($SERVER)) {
|
|
$s_id = $SERVER;
|
|
}
|
|
$s_dir = GetTempDir();
|
|
$s_md5 = md5($a_targets[0]);
|
|
$s_uniq = substr($s_md5,0,6);
|
|
$s_chk_file = "fm" . "$s_uniq" . ".txt";
|
|
Check4Update($s_dir . "/" . $s_chk_file,$s_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
register_shutdown_function('OnExit');
|
|
|
|
//
|
|
// Return the array with each string processed by htmlentities
|
|
//
|
|
function HTMLEntitiesArray($a_array,$b_equals_processing = false,$s_charset = NULL)
|
|
{
|
|
foreach ($a_array as $m_key => $s_str) {
|
|
//
|
|
// only encode the value after the '='
|
|
//
|
|
if ($b_equals_processing && ($i_pos = strpos($s_str,'=')) !== false) {
|
|
$a_array[$m_key] = substr($s_str,0,$i_pos + 1) .
|
|
FixedHTMLEntities(substr($s_str,$i_pos + 1),$s_charset);
|
|
} else {
|
|
$a_array[$m_key] = FixedHTMLEntities($s_str,$s_charset);
|
|
}
|
|
}
|
|
return ($a_array);
|
|
}
|
|
|
|
//
|
|
// Unfortunately, htmlentities (in some versions of PHP) gets
|
|
// some characters wrong and converts them even when the
|
|
// charset is provided.
|
|
// This function overcomes this problem.
|
|
//
|
|
function FixedHTMLEntities($s_str,$s_charset = NULL)
|
|
{
|
|
global $sHTMLCharSet;
|
|
|
|
if (isset($s_charset) && $s_charset != "") {
|
|
return (htmlspecialchars($s_str,ENT_COMPAT,$s_charset));
|
|
}
|
|
if (isset($sHTMLCharSet) && $sHTMLCharSet != "") {
|
|
return (htmlspecialchars($s_str,ENT_COMPAT,$sHTMLCharSet));
|
|
}
|
|
return (htmlentities($s_str));
|
|
}
|
|
|
|
//
|
|
// Return the array with each string urlencode'd.
|
|
//
|
|
function URLEncodeArray($a_array)
|
|
{
|
|
foreach ($a_array as $m_key => $s_str) {
|
|
//
|
|
// only encode the value after the '='
|
|
//
|
|
if (($i_pos = strpos($s_str,'=')) !== false) {
|
|
$a_array[$m_key] = substr($s_str,0,$i_pos + 1) .
|
|
urlencode(substr($s_str,$i_pos + 1));
|
|
} else {
|
|
$a_array[$m_key] = urlencode($s_str);
|
|
}
|
|
}
|
|
return ($a_array);
|
|
}
|
|
|
|
//
|
|
// Performs charset encoding for header line text.
|
|
// This operates according to RFC 2047, but without
|
|
// imposing the 75 character limit on an encoding.
|
|
// I haven't implemented that because of all the dramas
|
|
// with *trying* to obey the header line length rules that
|
|
// don't seem to work with PHP, the MTA, and email clients.
|
|
//
|
|
function EncodeHeaderText($s_text,$i_max_line = -1)
|
|
{
|
|
global $sHTMLCharSet;
|
|
|
|
//
|
|
// RFCLINELEN is the RFC recommended maximum line length, but we don't know
|
|
// what the front part of the line will be at this point.
|
|
// So, we'll be conservative and reduce it.
|
|
//
|
|
if ($i_max_line == 0) {
|
|
$i_max_line = RFCLINELEN - 15;
|
|
}
|
|
$s_charset = "";
|
|
if (isset($sHTMLCharSet) && $sHTMLCharSet != "") {
|
|
$s_charset = $sHTMLCharSet;
|
|
} else {
|
|
if (IsMailOptionSet("CharSet")) {
|
|
$s_charset = GetMailOption("CharSet");
|
|
}
|
|
}
|
|
if ($s_charset != "") {
|
|
//
|
|
// this is the (unused) code for base64 encoding.
|
|
// quoted printable is a better choice for human readability
|
|
//
|
|
//return ("=?".$s_charset."?B?".base64_encode($s_text)."?=");
|
|
$s_prefix = "=?" . $s_charset . "?Q?";
|
|
$s_suffix = "?=";
|
|
//
|
|
// pick a line length that allows a line split with the prefix or suffix added
|
|
// to be within the RFC maximum recommended line length
|
|
//
|
|
return ($s_prefix . QPEncode($s_text,$i_max_line - strlen($s_prefix)) . $s_suffix);
|
|
} else {
|
|
return ($s_text);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add a parameter or list of parameters to a URL.
|
|
//
|
|
function AddURLParams($s_url,$m_params,$b_encode = true)
|
|
{
|
|
if (!empty($m_params)) {
|
|
if (!is_array($m_params)) {
|
|
$m_params = array($m_params);
|
|
}
|
|
$s_anchor = "";
|
|
if (($i_pos = strpos($s_url,'#')) !== false) {
|
|
//
|
|
// extract the anchor
|
|
//
|
|
$s_anchor = substr($s_url,$i_pos);
|
|
$s_url = substr($s_url,0,$i_pos);
|
|
}
|
|
if (strpos($s_url,'?') === false) {
|
|
$s_url .= '?';
|
|
} else {
|
|
$s_url .= '&';
|
|
}
|
|
$s_url .= implode('&',($b_encode) ? URLEncodeArray($m_params) : $m_params);
|
|
if ($s_anchor !== "") {
|
|
$s_url .= "$s_anchor";
|
|
}
|
|
}
|
|
return ($s_url);
|
|
}
|
|
|
|
//
|
|
// Recursively trim an array of strings (non string values are converted
|
|
// to a string first).
|
|
//
|
|
function TrimArray($a_list)
|
|
{
|
|
foreach ($a_list as $m_key => $m_item) {
|
|
if (is_array($m_item)) {
|
|
$a_list[$m_key] = TrimArray($m_item);
|
|
} elseif (is_scalar($m_item)) {
|
|
$a_list[$m_key] = trim("$m_item");
|
|
} else {
|
|
$a_list[$m_key] = "";
|
|
}
|
|
}
|
|
return ($a_list);
|
|
}
|
|
|
|
//
|
|
// Parse a derivation specification and return an array of
|
|
// field names and operators.
|
|
//
|
|
function ParseDerivation($s_fld_spec,$s_name,&$a_errors)
|
|
{
|
|
$a_deriv = array();
|
|
while (($i_len = strlen($s_fld_spec)) > 0) {
|
|
//
|
|
// we support the following operators:
|
|
// + concatenate with a single space between, but skip the space
|
|
// if the next field is empty
|
|
// * concatenate with a single space between
|
|
// . concatenate with no space between
|
|
//
|
|
$i_span = strcspn($s_fld_spec,'+*.');
|
|
if ($i_span == 0) {
|
|
$a_errors[] = $s_name;
|
|
return (false);
|
|
}
|
|
$a_deriv[] = trim(substr($s_fld_spec,0,$i_span));
|
|
if ($i_span < $i_len) {
|
|
$a_deriv[] = substr($s_fld_spec,$i_span,1);
|
|
$s_fld_spec = substr($s_fld_spec,$i_span + 1);
|
|
} else {
|
|
$s_fld_spec = "";
|
|
}
|
|
}
|
|
return ($a_deriv);
|
|
}
|
|
|
|
//
|
|
// Test if a character is an alphabetic.
|
|
//
|
|
function IsAlpha($ch)
|
|
{
|
|
return ($ch !== "" && strpos("abcdefghijklmnopqrstuvwxyz",strtolower($ch)) !== false);
|
|
}
|
|
|
|
//
|
|
// Test if a character is a digit.
|
|
//
|
|
function IsNumeric($ch)
|
|
{
|
|
return ($ch !== "" && strpos("0123456789",$ch) !== false);
|
|
}
|
|
|
|
//
|
|
// Test if a character is an alphanumeric
|
|
//
|
|
function IsAlnum($ch)
|
|
{
|
|
return (IsAlpha($ch) || IsNumeric($ch));
|
|
}
|
|
|
|
/**
|
|
* Test if there is a number at the current position in a string.
|
|
* (I had to write this because Badoo SoftMocks was crashing on the original
|
|
* complex expression.)
|
|
*
|
|
* @param string $s_str the string
|
|
* @param int $i_pos the current position in the string
|
|
* @param int $i_len the length of the string
|
|
*
|
|
* @return bool
|
|
*/
|
|
function IsANumber($s_str,$i_pos,$i_len)
|
|
{
|
|
$ch = $s_str[$i_pos];
|
|
if (IsNumeric($ch)) {
|
|
return true;
|
|
}
|
|
if ($ch == ".") {
|
|
if (($i_pos + 1) <= ($i_len - 1)) {
|
|
$ch = $s_str[$i_pos + 1];
|
|
return IsNumeric($ch);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Return an array of tokens extracted from the given string.
|
|
// A token is:
|
|
// - a word (begins with alpha or _, and is followed by any number
|
|
// of alphanumerics or _ chars)
|
|
// - a number (any number of consecutive digits with up to one period)
|
|
// - a string enclosed in specified quotes (this can be disabled)
|
|
// - any punctuation character
|
|
//
|
|
// Note that no string escapes (e.g. backslashes) are supported
|
|
// Anything not matching the above is silently ignored!
|
|
//
|
|
function GetTokens($s_str,$s_quotes = "'\"")
|
|
{
|
|
$b_allow_strings = $s_quotes !== "";
|
|
$ii = 0;
|
|
$i_len = strlen($s_str);
|
|
$a_toks = array();
|
|
|
|
while ($ii < $i_len) {
|
|
switch ($ch = $s_str[$ii]) {
|
|
case " ":
|
|
case "\t":
|
|
case "\n":
|
|
case "\r":
|
|
$ii++;
|
|
continue 2;
|
|
}
|
|
//
|
|
// start of a token
|
|
//
|
|
$i_start = $ii;
|
|
if ($ch == "_" || IsAlpha($ch)) {
|
|
//
|
|
// a word
|
|
//
|
|
$i_count = 1;
|
|
while (++$ii < $i_len &&
|
|
(($ch = $s_str[$ii]) == "_" || IsAlnum($ch))) {
|
|
++$i_count;
|
|
}
|
|
$a_toks[] = substr($s_str,$i_start,$i_count);
|
|
} elseif (IsANumber($s_str,$ii,$i_len)) {
|
|
// note: $ch == $s_str[$ii]
|
|
//
|
|
// a number
|
|
//
|
|
$b_had_dot = ($ch == ".");
|
|
$i_count = 1;
|
|
while (++$ii < $i_len) {
|
|
$c_check = $s_str[$ii];
|
|
if (IsNumeric($c_check)) {
|
|
++$i_count;
|
|
} elseif ($c_check == "." && !$b_had_dot) {
|
|
++$i_count;
|
|
$b_had_dot = true;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
$a_toks[] = substr($s_str,$i_start,$i_count);
|
|
} elseif ($b_allow_strings && strpos($s_quotes,$ch) !== false) {
|
|
$c_quote = $ch;
|
|
//
|
|
// a quoted string
|
|
//
|
|
while (++$ii < $i_len) {
|
|
if ($s_str[$ii] == $c_quote) {
|
|
++$ii; // include the terminating quote
|
|
break;
|
|
}
|
|
}
|
|
$a_toks[] = substr($s_str,$i_start,$ii - $i_start);
|
|
} else {
|
|
$s_punct = "~!@#$%^&*()-+={}[]|:;<>,.?/`\\";
|
|
if (!$b_allow_strings) {
|
|
$s_punct .= "'\"";
|
|
}
|
|
if (strpos($s_punct,$ch) !== false) {
|
|
$a_toks[] = $ch;
|
|
}
|
|
++$ii;
|
|
}
|
|
}
|
|
return ($a_toks);
|
|
}
|
|
|
|
/**
|
|
* Return the value from a derive_fields specification.
|
|
* Specifications are in this format:
|
|
* %info%
|
|
* where info is a predefined word or a literal in quotes
|
|
* (e.g. 'the time is ')
|
|
*
|
|
* @param string $s_spec the specification
|
|
* @param FieldManager $field_mgr form fields manager
|
|
* @param array $a_errors returns the list of errors
|
|
*
|
|
* @return string
|
|
* @see \FormMail\UnitTests\ValueSpecTest
|
|
*/
|
|
function ValueSpec($s_spec,$field_mgr,&$a_errors)
|
|
{
|
|
global $lNow;
|
|
|
|
$s_value = "";
|
|
switch (trim($s_spec)) {
|
|
case 'date': // "standard" date format: DD-MMM-YYYY
|
|
$s_value = date('d-M-Y',$lNow);
|
|
break;
|
|
case 'time': // "standard" time format: HH:MM:SS
|
|
$s_value = date('H:i:s',$lNow);
|
|
break;
|
|
case 'ampm': // am or pm
|
|
$s_value = date('a',$lNow);
|
|
break;
|
|
case 'AMPM': // AM or PM
|
|
$s_value = date('A',$lNow);
|
|
break;
|
|
case 'dom0': // day of month with possible leading zero
|
|
$s_value = date('d',$lNow);
|
|
break;
|
|
case 'dom': // day of month with no leading zero
|
|
$s_value = date('j',$lNow);
|
|
break;
|
|
case 'day': // day name (abbreviated)
|
|
$s_value = date('D',$lNow);
|
|
break;
|
|
case 'dayname': // day name (full)
|
|
$s_value = date('l',$lNow);
|
|
break;
|
|
case 'daysuffix': // day number suffix for English (st for 1st, nd for 2nd, etc.)
|
|
$s_value = date('S',$lNow);
|
|
break;
|
|
case 'moy0': // month of year with possible leading zero
|
|
$s_value = date('m',$lNow);
|
|
break;
|
|
case 'moy': // month of year with no leading zero
|
|
$s_value = date('n',$lNow);
|
|
break;
|
|
case 'month': // month name (abbreviated)
|
|
$s_value = date('M',$lNow);
|
|
break;
|
|
case 'monthname': // month name (full)
|
|
$s_value = date('F',$lNow);
|
|
break;
|
|
case 'year': // year (two digits)
|
|
$s_value = date('y',$lNow);
|
|
break;
|
|
case 'fullyear': // year (full)
|
|
$s_value = date('Y',$lNow);
|
|
break;
|
|
case 'rfcdate': // date formatted according to RFC 822
|
|
$s_value = date('r',$lNow);
|
|
break;
|
|
case 'tzname': // timezone name
|
|
$s_value = date('T',$lNow);
|
|
break;
|
|
case 'tz': // timezone difference from Greenwich +NNNN or -NNNN
|
|
$s_value = date('O',$lNow);
|
|
break;
|
|
case 'hour120': // hour of day (01-12) with possible leading zero
|
|
$s_value = date('h',$lNow);
|
|
break;
|
|
case 'hour240': // hour of day (00-23) with possible leading zero
|
|
$s_value = date('H',$lNow);
|
|
break;
|
|
case 'hour12': // hour of day (1-12) with no leading zero
|
|
$s_value = date('g',$lNow);
|
|
break;
|
|
case 'hour24': // hour of day (0-23) with no leading zero
|
|
$s_value = date('G',$lNow);
|
|
break;
|
|
case 'min': // minute of hour (00-59)
|
|
$s_value = date('i',$lNow);
|
|
break;
|
|
case 'sec': // seconds of minute (00-59)
|
|
$s_value = date('s',$lNow);
|
|
break;
|
|
default:
|
|
if ($s_spec[0] == "'") {
|
|
//
|
|
// to get a quote, use 3 quotes:
|
|
// '''
|
|
//
|
|
if ($s_spec == "'''") {
|
|
$s_value = "'";
|
|
} elseif (substr($s_spec,-1,1) == "'") {
|
|
$s_value = substr($s_spec,1,-1);
|
|
} else
|
|
//
|
|
// missing final quote is OK
|
|
//
|
|
{
|
|
$s_value = substr($s_spec,1);
|
|
}
|
|
} elseif (strspn($s_spec,"0123456789ABCDEF") == 2) {
|
|
//
|
|
// insert the ASCII character corresponding to
|
|
// the hexadecimal value
|
|
//
|
|
$i_val = intval(substr($s_spec,0,2),16);
|
|
$s_value = chr($i_val);
|
|
} else {
|
|
//
|
|
// look for supported functions, start by getting all
|
|
// the tokens
|
|
//
|
|
$a_toks = GetTokens($s_spec);
|
|
if (count($a_toks) > 0) {
|
|
switch ($a_toks[0]) {
|
|
case "if":
|
|
//
|
|
// "if" function: test first field
|
|
// if not empty, then use second field
|
|
// else, use third field
|
|
//
|
|
// Example: if(fld1 ; fld2 ; fld3)
|
|
//
|
|
// tokens are:
|
|
// 1 (
|
|
// 2 the field name to test (first)
|
|
// 3 ;
|
|
// 4 the "then" spec (can be missing)
|
|
// 5 ;
|
|
// 6 the "else" spec (can be missing)
|
|
// 7 )
|
|
//
|
|
if (($n_tok = count($a_toks)) < 6 ||
|
|
$a_toks[1] != "(" ||
|
|
$a_toks[3] != ";" ||
|
|
$a_toks[$n_tok - 1] != ")"
|
|
) {
|
|
SendAlert(GetMessage(MSG_DER_FUNC_ERROR,
|
|
array("SPEC" => $s_spec,
|
|
"MSG" => GetMessage(MSG_DER_FUNC_IF_FMT)
|
|
)));
|
|
} else {
|
|
$b_ok = true;
|
|
$s_fld_name = $a_toks[2];
|
|
$s_then_spec = $s_else_spec = "";
|
|
for ($ii = 4 ; $ii < $n_tok && $a_toks[$ii] != ';' ; $ii++) {
|
|
$s_then_spec .= $a_toks[$ii];
|
|
}
|
|
if ($ii == $n_tok) {
|
|
$b_ok = false;
|
|
} else {
|
|
//
|
|
// Concatenate tokens until the ')'.
|
|
// This provides the "else" spec.
|
|
//
|
|
for (; ++$ii < $n_tok && $a_toks[$ii] != ')' ;) {
|
|
$s_else_spec .= $a_toks[$ii];
|
|
}
|
|
if ($ii == $n_tok) {
|
|
$b_ok = false;
|
|
}
|
|
}
|
|
$s_mesg = "";
|
|
if ($b_ok) {
|
|
if (!$field_mgr->TestFieldEmpty($s_fld_name,$s_mesg)) {
|
|
$s_fld_spec = $s_then_spec;
|
|
} else {
|
|
$s_fld_spec = $s_else_spec;
|
|
}
|
|
$s_value = GetDerivedValue($field_mgr,$s_fld_spec,$a_errors);
|
|
} else {
|
|
SendAlert(GetMessage(MSG_DER_FUNC_ERROR,
|
|
array("SPEC" => $s_spec,
|
|
"MSG" => GetMessage(MSG_DER_FUNC_IF_FMT)
|
|
)));
|
|
}
|
|
}
|
|
break;
|
|
case "size":
|
|
//
|
|
// "size" function: return size of uploaded file
|
|
//
|
|
// Example: size(fieldname)
|
|
//
|
|
// tokens are:
|
|
// 1 (
|
|
// 2 the field name for the file upload
|
|
// 3 )
|
|
//
|
|
if (count($a_toks) != 4 ||
|
|
$a_toks[1] != "(" ||
|
|
$a_toks[3] != ")"
|
|
) {
|
|
SendAlert(GetMessage(MSG_DER_FUNC_ERROR,
|
|
array("SPEC" => $s_spec,
|
|
"MSG" => GetMessage(MSG_DER_FUNC_SIZE_FMT)
|
|
)));
|
|
} elseif (($i_size = GetFileSize($a_toks[2])) !== false) {
|
|
$s_value = "$i_size";
|
|
}
|
|
break;
|
|
case "ext":
|
|
//
|
|
// "ext" function: return filename extension of uploaded file
|
|
//
|
|
// Example: ext(fieldname)
|
|
//
|
|
// tokens are:
|
|
// 1 (
|
|
// 2 the field name for the file upload
|
|
// 3 )
|
|
//
|
|
if (count($a_toks) != 4 ||
|
|
$a_toks[1] != "(" ||
|
|
$a_toks[3] != ")"
|
|
) {
|
|
SendAlert(GetMessage(MSG_DER_FUNC_ERROR,
|
|
array("SPEC" => $s_spec,
|
|
"MSG" => GetMessage(MSG_DER_FUNC_EXT_FMT)
|
|
)));
|
|
} elseif (($s_name = GetFileName($a_toks[2])) !== false) {
|
|
if (($i_pos = strrpos($s_name,".")) !== false) {
|
|
$s_value = substr($s_name,$i_pos + 1);
|
|
}
|
|
}
|
|
break;
|
|
case "ucase":
|
|
case "lcase":
|
|
//
|
|
// "ucase" and "lcase" functions: return field
|
|
// converted to upper or lower case
|
|
//
|
|
// Example: lcase(fieldname)
|
|
//
|
|
// tokens are:
|
|
// 1 (
|
|
// 2 the field name to convert
|
|
// 3 )
|
|
//
|
|
if (count($a_toks) != 4 ||
|
|
$a_toks[1] != "(" ||
|
|
$a_toks[3] != ")"
|
|
) {
|
|
SendAlert(GetMessage(MSG_DER_FUNC_ERROR,
|
|
array("SPEC" => $s_spec,
|
|
"MSG" => GetMessage(MSG_DER_FUNC1_FMT,
|
|
array("FUNC" => $a_toks[0]))
|
|
)));
|
|
} elseif ($a_toks[0] == "ucase") {
|
|
$s_value = strtoupper($field_mgr->GetFieldValue($a_toks[2]));
|
|
} else {
|
|
$s_value = strtolower($field_mgr->GetFieldValue($a_toks[2]));
|
|
}
|
|
break;
|
|
case "ltrim":
|
|
case "rtrim":
|
|
case "trim":
|
|
//
|
|
// trim functions: return field with whitespace removed
|
|
// from left (ltrim), right (rtrim), or both (trim)
|
|
// ends.
|
|
//
|
|
// Example: ltrim(fieldname)
|
|
//
|
|
// tokens are:
|
|
// 1 (
|
|
// 2 the field name to trim
|
|
// 3 )
|
|
//
|
|
if (count($a_toks) != 4 ||
|
|
$a_toks[1] != "(" ||
|
|
$a_toks[3] != ")"
|
|
) {
|
|
SendAlert(GetMessage(MSG_DER_FUNC_ERROR,
|
|
array("SPEC" => $s_spec,
|
|
"MSG" => GetMessage(MSG_DER_FUNC1_FMT,
|
|
array("FUNC" => $a_toks[0]))
|
|
)));
|
|
} else {
|
|
$s_value = $a_toks[0]($field_mgr->GetFieldValue($a_toks[2]));
|
|
}
|
|
break;
|
|
case "ltrim0":
|
|
//
|
|
// ltrim0 function: return field with blanks and
|
|
// leading 0's removed from the left.
|
|
//
|
|
// Example: ltrim0(fieldname)
|
|
//
|
|
// tokens are:
|
|
// 1 (
|
|
// 2 the field name to trim
|
|
// 3 )
|
|
//
|
|
if (count($a_toks) != 4 ||
|
|
$a_toks[1] != "(" ||
|
|
$a_toks[3] != ")"
|
|
) {
|
|
SendAlert(GetMessage(MSG_DER_FUNC_ERROR,
|
|
array("SPEC" => $s_spec,
|
|
"MSG" => GetMessage(MSG_DER_FUNC1_FMT,
|
|
array("FUNC" => $a_toks[0]))
|
|
)));
|
|
} else {
|
|
$s_value = $field_mgr->GetFieldValue($a_toks[2]);
|
|
$s_value = ltrim($s_value); // trim blanks on left
|
|
$i_len = strspn($s_value,"0");
|
|
//
|
|
// if the whole string is zeroes, make sure we leave
|
|
// one of them!
|
|
//
|
|
if ($i_len == strlen($s_value)) {
|
|
if (--$i_len < 0) {
|
|
$i_len = 0;
|
|
}
|
|
}
|
|
$s_value = substr($s_value,$i_len);
|
|
}
|
|
break;
|
|
case "nextnum":
|
|
//
|
|
// "nextnum" function: return a unique number (next number)
|
|
//
|
|
// Usage: nextnum[(pad[,base])]
|
|
//
|
|
// Examples:
|
|
// %nextnum%
|
|
// %nextnum(8)%
|
|
// %nextnum(5;16)%
|
|
//
|
|
// You can provide a padding amount. In this case, the
|
|
// number is padded on the left with zeroes to the number
|
|
// of digits specified.
|
|
//
|
|
// You can also provide a base for your numbers. Valid
|
|
// values for base are 2 to 36, inclusive.
|
|
//
|
|
// tokens are:
|
|
// 1 (
|
|
// 2 the padding amount
|
|
// 3 ;
|
|
// 4 the base
|
|
// 5 )
|
|
//
|
|
$i_pad = 0; // no padding
|
|
$i_base = 10; // base 10
|
|
if (($n_tok = count($a_toks)) > 1) {
|
|
if (($n_tok != 4 && $n_tok != 6) ||
|
|
$a_toks[1] != "(" ||
|
|
$a_toks[$n_tok - 1] != ")"
|
|
) {
|
|
SendAlert(GetMessage(MSG_DER_FUNC_ERROR,
|
|
array("SPEC" => $s_spec,
|
|
"MSG" => GetMessage(MSG_DER_FUNC_NEXTNUM_FMT) . " (T1)"
|
|
)));
|
|
}
|
|
if ($n_tok == 6 && $a_toks[3] != ";") {
|
|
SendAlert(GetMessage(MSG_DER_FUNC_ERROR,
|
|
array("SPEC" => $s_spec,
|
|
"MSG" => GetMessage(MSG_DER_FUNC_NEXTNUM_FMT) . " (T2)"
|
|
)));
|
|
}
|
|
if (!is_numeric($a_toks[2]) ||
|
|
($n_tok == 6 && !is_numeric($a_toks[4]))
|
|
) {
|
|
SendAlert(GetMessage(MSG_DER_FUNC_ERROR,
|
|
array("SPEC" => $s_spec,
|
|
"MSG" => GetMessage(MSG_DER_FUNC_NEXTNUM_FMT) . " (T3)"
|
|
)));
|
|
}
|
|
$i_pad = intval($a_toks[2]);
|
|
if ($n_tok == 6) {
|
|
$i_base = intval($a_toks[4]);
|
|
if ($i_base < 2 || $i_base > 36) {
|
|
SendAlert(GetMessage(MSG_DER_FUNC_ERROR,
|
|
array("SPEC" => $s_spec,
|
|
"MSG" =>
|
|
GetMessage(MSG_DER_FUNC_NEXTNUM_FMT) . " (T4)"
|
|
)));
|
|
$i_base = 10;
|
|
}
|
|
}
|
|
$s_value = GetNextNum($i_pad,$i_base);
|
|
} else {
|
|
$s_value = GetNextNum($i_pad,$i_base);
|
|
}
|
|
break;
|
|
case "substr":
|
|
//
|
|
// "substr" function: return part of a string
|
|
//
|
|
// Usage: substr(fieldname;start;length)
|
|
//
|
|
// Examples:
|
|
// %substr(field1;0;5)%
|
|
// %substr(field1;4)%
|
|
// %substr(field1;-4;-1)%
|
|
//
|
|
// The start is the number of characters to skip
|
|
// at the beginning of the string, if positive.
|
|
// If negative, the start is calculated from the
|
|
// end of the string, and says how many characters
|
|
// to go backward from the end.
|
|
//
|
|
// The length is the number of characters to return,
|
|
// if positive. If not specified, then the remainder
|
|
// of the string is returned. If negative, it
|
|
// specifies the number of characters to be omitted
|
|
// from the end of the string.
|
|
//
|
|
// Examples:
|
|
// Assume "f1" has the value "abcdef":
|
|
// %substr(f1;0;5)% returns "abcde"
|
|
// %substr(f1;4)% returns "ef"
|
|
// %substr(f1;1;-1)% returns "bcde"
|
|
// %substr(f1;-4;-1)% returns "cde"
|
|
//
|
|
// tokens are:
|
|
// 1 (
|
|
// 2 the field name
|
|
// 3 ;
|
|
// 4 the start
|
|
// 5 ;
|
|
// 6 the length
|
|
// 7 )
|
|
//
|
|
$i_start = 0; // default start
|
|
$i_len = null; // no length
|
|
if (($n_tok = count($a_toks)) > 1) {
|
|
if (($n_tok != 6 && $n_tok != 8) ||
|
|
$a_toks[1] != "(" ||
|
|
$a_toks[$n_tok - 1] != ")"
|
|
) {
|
|
SendAlert(GetMessage(MSG_DER_FUNC_ERROR,
|
|
array("SPEC" => $s_spec,
|
|
"MSG" => GetMessage(MSG_DER_FUNC_SUBSTR_FMT) . " (T1)"
|
|
)));
|
|
}
|
|
if ($n_tok == 8 &&
|
|
($a_toks[3] != ";" || $a_toks[5] != ";")
|
|
) {
|
|
SendAlert(GetMessage(MSG_DER_FUNC_ERROR,
|
|
array("SPEC" => $s_spec,
|
|
"MSG" => GetMessage(MSG_DER_FUNC_SUBSTR_FMT) . " (T2)"
|
|
)));
|
|
}
|
|
if ($n_tok == 6 && $a_toks[3] != ";") {
|
|
SendAlert(GetMessage(MSG_DER_FUNC_ERROR,
|
|
array("SPEC" => $s_spec,
|
|
"MSG" => GetMessage(MSG_DER_FUNC_SUBSTR_FMT) . " (T3)"
|
|
)));
|
|
}
|
|
if (!is_numeric($a_toks[4]) ||
|
|
($n_tok == 8 && !is_numeric($a_toks[6]))
|
|
) {
|
|
SendAlert(GetMessage(MSG_DER_FUNC_ERROR,
|
|
array("SPEC" => $s_spec,
|
|
"MSG" => GetMessage(MSG_DER_FUNC_SUBSTR_FMT) . " (T4)"
|
|
)));
|
|
}
|
|
$i_start = intval($a_toks[4]);
|
|
$s_value = $field_mgr->GetFieldValue($a_toks[2]);
|
|
if ($n_tok == 8) {
|
|
$i_len = intval($a_toks[6]);
|
|
}
|
|
if (isset($i_len)) {
|
|
$s_value = substr($s_value,$i_start,$i_len);
|
|
} else {
|
|
$s_value = substr($s_value,$i_start);
|
|
}
|
|
if ($s_value === FALSE) {
|
|
$s_value = "";
|
|
}
|
|
} else {
|
|
SendAlert(GetMessage(MSG_DER_FUNC_ERROR,
|
|
array("SPEC" => $s_spec,
|
|
"MSG" => GetMessage(MSG_DER_FUNC_SUBSTR_FMT) . " (T5)"
|
|
)));
|
|
}
|
|
break;
|
|
case "call":
|
|
//
|
|
// "call" function: calls a builtin function
|
|
//
|
|
// Example: call(name ; p1 ; p2 ; ...)
|
|
//
|
|
// tokens are:
|
|
// 1 (
|
|
// 2 the function name
|
|
// 3 ;
|
|
// 4 first parameter
|
|
// 5 ;
|
|
// 6 second parameter
|
|
// ... ...
|
|
// n )
|
|
//
|
|
if (($n_tok = count($a_toks)) < 3 ||
|
|
$a_toks[1] != "(" ||
|
|
$a_toks[$n_tok - 1] != ")"
|
|
) {
|
|
SendAlert(GetMessage(MSG_DER_FUNC_ERROR,
|
|
array("SPEC" => $s_spec,
|
|
"MSG" => 'Incorrect format for call function'
|
|
)));
|
|
} else {
|
|
$b_ok = true;
|
|
$s_func_name = $a_toks[2];
|
|
$a_params = array();
|
|
if (3 < $n_tok && $a_toks[3] == ';') {
|
|
$a_params = CollectParams($a_toks,$n_tok,3);
|
|
}
|
|
for ($ii = 0 ; $ii < count($a_params) ; $ii++) {
|
|
$a_params[$ii] = GetDerivedValue($field_mgr,$a_params[$ii],$a_errors);
|
|
}
|
|
global $BuiltinFunctions;
|
|
|
|
if (!$BuiltinFunctions->Call($s_func_name,$a_params,$s_result)) {
|
|
SendAlert($s_result);
|
|
} else {
|
|
$s_value = $s_result;
|
|
}
|
|
}
|
|
break;
|
|
case "urlencode":
|
|
// code supplied by Jeff Shields
|
|
if (count($a_toks) != 4 ||
|
|
$a_toks[1] != "(" ||
|
|
$a_toks[3] != ")"
|
|
) {
|
|
SendAlert(GetMessage(MSG_DER_FUNC_ERROR,
|
|
array("SPEC" => $s_spec,
|
|
"MSG" => GetMessage(MSG_DER_FUNC1_FMT,
|
|
array("FUNC" => $a_toks[0]))
|
|
)));
|
|
} else {
|
|
$s_value = urlencode($field_mgr->GetFieldValue($a_toks[2]));
|
|
}
|
|
break;
|
|
default:
|
|
SendAlertIgnoreSpam(GetMessage(MSG_UNK_VALUE_SPEC,
|
|
array("SPEC" => $s_spec,"MSG" => "")));
|
|
break;
|
|
}
|
|
} else {
|
|
SendAlertIgnoreSpam(GetMessage(MSG_UNK_VALUE_SPEC,array("SPEC" => $s_spec,
|
|
"MSG" => ""
|
|
)));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return ($s_value);
|
|
}
|
|
|
|
//
|
|
// Collect parameters for a builtin function call
|
|
//
|
|
function CollectParams($a_toks,$n_tok,$i_tok)
|
|
{
|
|
$a_params = array();
|
|
$i_param = 0;
|
|
do {
|
|
$i_tok++;
|
|
$a_params[$i_param] = '';
|
|
for (; $i_tok < $n_tok && $a_toks[$i_tok] != ';' && $a_toks[$i_tok] != ')' ; $i_tok++) {
|
|
$a_params[$i_param] .= $a_toks[$i_tok];
|
|
}
|
|
$i_param++;
|
|
} while ($a_toks[$i_tok] != ')');
|
|
return ($a_params);
|
|
}
|
|
|
|
//
|
|
// Return the next number or fail on error
|
|
//
|
|
function GetNextNum($i_pad,$i_base)
|
|
{
|
|
global $php_errormsg;
|
|
|
|
if (Settings::isEmpty('NEXT_NUM_FILE') || Settings::get('NEXT_NUM_FILE') === "") {
|
|
ErrorWithIgnore("next_num_config",GetMessage(MSG_NO_NEXT_NUM_FILE));
|
|
FormMailExit();
|
|
}
|
|
if (($fp = @fopen(Settings::get('NEXT_NUM_FILE'),"r+")) === false) {
|
|
Error("next_num_file",GetMessage(MSG_NEXT_NUM_FILE,
|
|
array("FILE" => Settings::get('NEXT_NUM_FILE'),
|
|
"ACT" => "open",
|
|
"ERR" => $php_errormsg
|
|
)));
|
|
FormMailExit();
|
|
}
|
|
if (!flock($fp,defined("LOCK_EX") ? LOCK_EX : 2)) {
|
|
Error("next_num_file",GetMessage(MSG_NEXT_NUM_FILE,
|
|
array("FILE" => Settings::get('NEXT_NUM_FILE'),
|
|
"ACT" => "flock",
|
|
"ERR" => $php_errormsg
|
|
)));
|
|
FormMailExit();
|
|
}
|
|
//
|
|
// read the first line only
|
|
//
|
|
if (!feof($fp)) {
|
|
if (($s_line = fread($fp,1024)) === false) {
|
|
$i_next = 1;
|
|
} elseif (($i_next = intval($s_line)) <= 0) {
|
|
$i_next = 1;
|
|
}
|
|
} else {
|
|
$i_next = 1;
|
|
}
|
|
if (rewind($fp) == 0) {
|
|
Error("next_num_file",GetMessage(MSG_NEXT_NUM_FILE,
|
|
array("FILE" => Settings::get('NEXT_NUM_FILE'),
|
|
"ACT" => "rewind",
|
|
"ERR" => $php_errormsg
|
|
)));
|
|
FormMailExit();
|
|
}
|
|
$s_ret = strval($i_next++);
|
|
if (fputs($fp,"$i_next\r\n") <= 0) {
|
|
Error("next_num_file",GetMessage(MSG_NEXT_NUM_FILE,
|
|
array("FILE" => Settings::get('NEXT_NUM_FILE'),
|
|
"ACT" => "fputs",
|
|
"ERR" => $php_errormsg
|
|
)));
|
|
FormMailExit();
|
|
}
|
|
fclose($fp);
|
|
if ($i_base != 10) {
|
|
$s_ret = base_convert($s_ret,10,$i_base);
|
|
$s_ret = strtoupper($s_ret); // always upper case if alphas are used
|
|
}
|
|
if ($i_pad != 0) {
|
|
$s_ret = str_pad($s_ret,$i_pad,"0",STR_PAD_LEFT);
|
|
}
|
|
return ($s_ret);
|
|
}
|
|
|
|
//
|
|
// Return the value of an object or array as a string.
|
|
//
|
|
function GetObjectAsString($m_value)
|
|
{
|
|
ob_start();
|
|
print_r($m_value);
|
|
$s_ret = ob_get_contents();
|
|
ob_end_clean();
|
|
return ($s_ret);
|
|
}
|
|
|
|
//
|
|
// Return a Server or Environment variable value. Returns false if
|
|
// not found, otherwise a string value.
|
|
//
|
|
function GetEnvValue($s_name)
|
|
{
|
|
global $aServerVars,$aEnvVars;
|
|
|
|
if (isset($aEnvVars[$s_name])) {
|
|
$m_value = $aEnvVars[$s_name];
|
|
} elseif (isset($aServerVars[$s_name])) {
|
|
$m_value = $aServerVars[$s_name];
|
|
}
|
|
//
|
|
// some values might not be strings - so convert
|
|
//
|
|
if (isset($m_value) && !is_scalar($m_value)) {
|
|
$m_value = GetObjectAsString($m_value);
|
|
}
|
|
return (isset($m_value) ? ((string)$m_value) : false);
|
|
}
|
|
|
|
/**
|
|
* Test if a field is set in the given vars array or in the uploaded
|
|
* files.
|
|
*
|
|
* This function is obsolete and is only kept for use by
|
|
* existing hook files. Do not use this function in new code.
|
|
*
|
|
* @param string $s_fld the field name
|
|
* @param array $a_main_vars the field list array
|
|
*
|
|
* @return bool
|
|
*/
|
|
function IsFieldSet($s_fld,$a_main_vars)
|
|
{
|
|
global $aFileVars;
|
|
|
|
$mgr = new FieldManager($a_main_vars,$aFileVars);
|
|
return ($mgr->IsFieldSet($s_fld));
|
|
}
|
|
|
|
/*
|
|
* Function: IsFileField
|
|
* Parameters: $s_fld the field name
|
|
* Returns: bool true if this is a file upload field
|
|
* Description:
|
|
* Checks if a field is a file upload field (regardless of whether
|
|
* file uploads are being allowed, or whether the actual upload
|
|
* is valid in any way).
|
|
*/
|
|
/** @noinspection PhpUnused */
|
|
function IsFileField($s_fld)
|
|
{
|
|
global $aFileVars;
|
|
|
|
return (isset($aFileVars[$s_fld]));
|
|
}
|
|
|
|
//
|
|
// Delete the info for an uploaded file
|
|
//
|
|
function DeleteFileInfo($s_fld)
|
|
{
|
|
global $aFileVars;
|
|
global $aCleanedValues,$aRawDataValues,$aAllRawValues;
|
|
|
|
if (Settings::get('FILEUPLOADS')) {
|
|
if (IsSetSession("FormSavedFiles")) {
|
|
$a_saved_files = GetSession("FormSavedFiles");
|
|
unset($a_saved_files[$s_fld]);
|
|
SetSession("FormSavedFiles",$a_saved_files);
|
|
}
|
|
if (isset($aFileVars[$s_fld])) {
|
|
unset($aFileVars[$s_fld]);
|
|
}
|
|
//
|
|
// zap any "name_of" field that has been created
|
|
//
|
|
$s_name = "name_of_$s_fld";
|
|
unset($aCleanedValues[$s_name]);
|
|
unset($aRawDataValues[$s_name]);
|
|
unset($aAllRawValues[$s_name]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the info for the uploaded file, or false on error.
|
|
*
|
|
* This function is obsolete and is only kept for use by
|
|
* existing hook files. Do not use this function in new code.
|
|
*
|
|
* @param string $s_fld field name
|
|
*
|
|
* @return bool
|
|
* @see \FormMail\UnitTests\GetFieldValueTest
|
|
*/
|
|
function GetFileInfo($s_fld)
|
|
{
|
|
global $aFileVars;
|
|
|
|
$mgr = new FieldManager(array(),$aFileVars);
|
|
return ($mgr->GetFileInfo($s_fld));
|
|
}
|
|
|
|
/**
|
|
* Return the original name of the uploaded file or false on error.
|
|
*
|
|
* This function is obsolete and is only kept for use by
|
|
* existing hook files. Do not use this function in new code.
|
|
*
|
|
* @param string $s_fld field name
|
|
*
|
|
* @return bool
|
|
* @see \FormMail\UnitTests\GetFileNameTest
|
|
*/
|
|
function GetFileName($s_fld)
|
|
{
|
|
global $aFileVars;
|
|
|
|
$mgr = new FieldManager(array(),$aFileVars);
|
|
return ($mgr->GetFileName($s_fld));
|
|
}
|
|
|
|
//
|
|
// Return the size of the uploaded file or false on error.
|
|
//
|
|
function GetFileSize($s_fld)
|
|
{
|
|
if (($a_upload = GetFileInfo($s_fld)) !== false) {
|
|
return ($a_upload["size"]);
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
/**
|
|
* Return a field value. Empty string is returned if the field is
|
|
* not found. File fields return the original name of the uploaded file.
|
|
*
|
|
* This function is obsolete and is only kept for use by
|
|
* existing hook files. Do not use this function in new code.
|
|
*
|
|
* @param string $s_fld field name
|
|
* @param array $a_main_vars the array of fields
|
|
* @param string $s_array_sep a separator for joining array values
|
|
*
|
|
* @return bool|string
|
|
* @see \FormMail\UnitTests\GetFieldValueTest
|
|
*/
|
|
function GetFieldValue($s_fld,$a_main_vars,$s_array_sep = ";")
|
|
{
|
|
global $aFileVars;
|
|
|
|
$mgr = new FieldManager($a_main_vars,$aFileVars);
|
|
return ($mgr->GetFieldValue($s_fld,$s_array_sep));
|
|
}
|
|
|
|
/**
|
|
* Tests a field against an array of vars for emptiness.
|
|
* If the var isn't found there, then the POSTed files array is checked.
|
|
* Returns true if the field is empty (a specific error may
|
|
* be returned in the $s_mesg parameter).
|
|
*
|
|
* This function is obsolete and is only kept for use by
|
|
* existing hook files. Do not use this function in new code.
|
|
*
|
|
* @param string $s_fld field name
|
|
* @param array $a_main_vars the array of fields
|
|
* @param string $s_mesg returned error message
|
|
*
|
|
* @return bool
|
|
* @see \FormMail\UnitTests\TestFieldEmptyTest
|
|
*/
|
|
function TestFieldEmpty($s_fld,$a_main_vars,&$s_mesg)
|
|
{
|
|
global $aFileVars;
|
|
|
|
$mgr = new FieldManager($a_main_vars,$aFileVars);
|
|
return ($mgr->TestFieldEmpty($s_fld,$s_mesg));
|
|
}
|
|
|
|
//
|
|
// Return a derived field value or value specification.
|
|
//
|
|
function GetDerivedValue($field_mgr,$s_word,&$a_errors)
|
|
{
|
|
$s_value = "";
|
|
//
|
|
// a field name or a value specification
|
|
// value specifications have the following format:
|
|
// %spec%
|
|
//
|
|
if (substr($s_word,0,1) == '%') {
|
|
if (substr($s_word,-1,1) != '%') {
|
|
SendAlert(GetMessage(MSG_INV_VALUE_SPEC,array("SPEC" => $s_word)));
|
|
$s_value = $s_word;
|
|
} else {
|
|
$s_spec = substr($s_word,1,-1);
|
|
$s_value = ValueSpec($s_spec,$field_mgr,$a_errors);
|
|
}
|
|
} else {
|
|
$s_fld_name = $s_word;
|
|
//
|
|
// try form data first, then the environment/server data
|
|
//
|
|
if ($field_mgr->IsFieldSet($s_fld_name)) {
|
|
$s_value = $field_mgr->GetFieldValue($s_fld_name);
|
|
} elseif (($s_value = GetEnvValue($s_fld_name)) === false) {
|
|
$s_value = "";
|
|
}
|
|
$s_value = trim($s_value);
|
|
}
|
|
return ($s_value);
|
|
}
|
|
|
|
//
|
|
// Derive a value from the form data using the specification returned
|
|
// from ParseDerivation.
|
|
//
|
|
function DeriveValue($field_mgr,$a_value_spec,$s_name,&$a_errors)
|
|
{
|
|
$s_value = "";
|
|
for ($ii = 0 ; $ii < count($a_value_spec) ; $ii++) {
|
|
switch ($a_value_spec[$ii]) {
|
|
case '+':
|
|
//
|
|
// concatenate with a single space between, but skip the space
|
|
// if the next field is empty
|
|
//
|
|
if ($ii < count($a_value_spec) - 1) {
|
|
$s_temp = GetDerivedValue($field_mgr,$a_value_spec[$ii + 1],$a_errors);
|
|
if (!FieldManager::IsEmpty($s_temp)) {
|
|
$s_value .= ' ';
|
|
}
|
|
}
|
|
break;
|
|
case '.':
|
|
//
|
|
// concatenate with no space between
|
|
//
|
|
break;
|
|
case '*':
|
|
//
|
|
// concatenate with a single space between
|
|
//
|
|
$s_value .= ' ';
|
|
break;
|
|
default:
|
|
//
|
|
// a field name or a value specification
|
|
// value specifications have the following format:
|
|
// %name%
|
|
//
|
|
$s_value .= GetDerivedValue($field_mgr,$a_value_spec[$ii],$a_errors);
|
|
break;
|
|
}
|
|
}
|
|
return ($s_value);
|
|
}
|
|
|
|
//
|
|
// Create derived fields specified by the "derive_fields" value.
|
|
//
|
|
function CreateDerived($a_form_data)
|
|
{
|
|
global $aFileVars;
|
|
|
|
if (isset($a_form_data["derive_fields"])) {
|
|
$a_errors = array();
|
|
//
|
|
// get the list of derived field specifications
|
|
//
|
|
$a_list = TrimArray(explode(",",$a_form_data["derive_fields"]));
|
|
$field_mgr = new FieldManager($a_form_data,$aFileVars);
|
|
foreach ($a_list as $s_fld_spec) {
|
|
if ($s_fld_spec === "")
|
|
//
|
|
// silently ignore empty derivations
|
|
//
|
|
{
|
|
continue;
|
|
}
|
|
if (($i_pos = strpos($s_fld_spec,"=")) === false) {
|
|
$a_errors[] = $s_fld_spec;
|
|
continue;
|
|
}
|
|
$s_name = trim(substr($s_fld_spec,0,$i_pos));
|
|
$s_fld_spec = substr($s_fld_spec,$i_pos + 1);
|
|
|
|
if (($a_value_spec = ParseDerivation($s_fld_spec,$s_name,$a_errors)) === false
|
|
) {
|
|
break;
|
|
}
|
|
$a_form_data[$s_name] = DeriveValue($field_mgr,$a_value_spec,$s_name,$a_errors);
|
|
// we've added a new field, so we need a new FieldManager object
|
|
$field_mgr = new FieldManager($a_form_data,$aFileVars);
|
|
}
|
|
if (count($a_errors) > 0) {
|
|
SendAlertIgnoreSpam(GetMessage(MSG_DERIVED_INVALID) . implode("\n",$a_errors));
|
|
ErrorWithIgnore("derivation_failure",GetMessage(MSG_INT_FORM_ERROR));
|
|
}
|
|
}
|
|
return ($a_form_data);
|
|
}
|
|
|
|
//
|
|
// To process the name specification for files and update the
|
|
// array of file variables accordingly.
|
|
//
|
|
function SetFileNames($s_name_spec,$a_order,$a_fields,$a_raw_fields,$a_all_raw_values,$a_file_vars)
|
|
{
|
|
$a_errors = array();
|
|
//
|
|
// get the list of file name derivations
|
|
//
|
|
$a_list = TrimArray(explode(",",$s_name_spec));
|
|
foreach ($a_list as $s_fld_spec) {
|
|
if ($s_fld_spec === "")
|
|
//
|
|
// silently ignore empty file name derivations
|
|
//
|
|
{
|
|
continue;
|
|
}
|
|
if (($i_pos = strpos($s_fld_spec,"=")) === false) {
|
|
$a_errors[] = $s_fld_spec;
|
|
continue;
|
|
}
|
|
$s_name = trim(substr($s_fld_spec,0,$i_pos));
|
|
$s_fld_spec = substr($s_fld_spec,$i_pos + 1);
|
|
|
|
if (($a_value_spec = ParseDerivation($s_fld_spec,$s_name,$a_errors)) === false
|
|
) {
|
|
break;
|
|
}
|
|
if (isset($a_file_vars[$s_name]) && FieldManager::IsUploadedFile($a_file_vars[$s_name])) {
|
|
//
|
|
// we create our own special entry in the file variable's data
|
|
//
|
|
$a_file_vars[$s_name]["new_name"] = DeriveValue(new FieldManager($a_raw_fields,$a_file_vars),
|
|
$a_value_spec,$s_name,$a_errors);
|
|
//
|
|
// we also create (derive) a new field called 'name_of_X'
|
|
// where X is the file fields's name
|
|
//
|
|
ProcessField("name_of_$s_name",$a_file_vars[$s_name]["new_name"],
|
|
$a_order,$a_fields,$a_raw_fields);
|
|
$a_all_raw_values["name_of_$s_name"] = $a_file_vars[$s_name]["new_name"];
|
|
}
|
|
/* This is annoying if a file upload is optional. Just ignore missing
|
|
file upload fields.
|
|
else
|
|
SendAlert(GetMessage(MSG_FILE_NAMES_NOT_FILE,
|
|
array("NAME"=>$s_name)));*/
|
|
}
|
|
if (count($a_errors) > 0) {
|
|
SendAlertIgnoreSpam(GetMessage(MSG_FILE_NAMES_INVALID) . implode("\n",$a_errors));
|
|
ErrorWithIgnore("file_names_derivation_failure",GetMessage(MSG_INT_FORM_ERROR));
|
|
}
|
|
return (array($a_order,$a_fields,$a_raw_fields,$a_all_raw_values,$a_file_vars));
|
|
}
|
|
|
|
$aProcessSpecsFormData = array();
|
|
$sProcessSpecsFieldName = "";
|
|
|
|
//
|
|
// Callback function for ProcessSpecs
|
|
//
|
|
function ProcessSpecsMatch($a_matches)
|
|
{
|
|
global $aProcessSpecsFormData,$sProcessSpecsFieldName,$aFileVars;
|
|
|
|
//
|
|
// strip % at either end
|
|
//
|
|
$s_spec = substr($a_matches[0],1,-1);
|
|
$a_errors = array();
|
|
$s_value = ValueSpec($s_spec,new FieldManager($aProcessSpecsFormData,$aFileVars),$a_errors);
|
|
return ($s_value);
|
|
}
|
|
|
|
//
|
|
// Process %...% specifications in a string.
|
|
// This function is used for processing options values, such as in "autorespond"
|
|
// and "mail_options".
|
|
//
|
|
function ProcessSpecs($s_fld_name,$a_form_data,$s_value)
|
|
{
|
|
global $aProcessSpecsFormData,$sProcessSpecsFieldName;
|
|
|
|
$aProcessSpecsFormData = $a_form_data;
|
|
$sProcessSpecsFieldName = $s_fld_name;
|
|
//
|
|
// un-greedy pattern match
|
|
// Note that this means we can't use %..% within a %if(..)%, for example.
|
|
//
|
|
$s_value = preg_replace_callback('/%.+?%/',"ProcessSpecsMatch",$s_value);
|
|
$aProcessSpecsFormData = array();
|
|
$sProcessSpecsFieldName = "";
|
|
return ($s_value);
|
|
}
|
|
|
|
//
|
|
// Process a list of attributes or options.
|
|
// Format for each attribute/option:
|
|
// name
|
|
// or
|
|
// name=value
|
|
//
|
|
// Values can be simple values or semicolon (;) separated lists:
|
|
// avalue
|
|
// value1;value2;value3;...
|
|
//
|
|
// Returns attribute/options in the associative array $a_attribs.
|
|
// Optionally, valid attributes can be provided in $a_valid_attribs
|
|
// (if empty, all attributes found are considered valid).
|
|
// Errors are returned in $a_errors.
|
|
//
|
|
function ProcessAttributeList($s_fld_name,$a_form_data,$a_list,&$a_attribs,&$a_errors,
|
|
$a_valid_attribs = array())
|
|
{
|
|
$b_got_valid_list = (count($a_valid_attribs) > 0);
|
|
foreach ($a_list as $s_attrib) {
|
|
//
|
|
// if the name begins with '.' then silently ignore it;
|
|
// this allows you to temporarily disable an option without
|
|
// getting an alert message
|
|
//
|
|
if (($i_pos = strpos($s_attrib,"=")) === false) {
|
|
$s_name = trim($s_attrib);
|
|
if (empty($s_name) || $s_name[0] == '.') {
|
|
continue;
|
|
}
|
|
//
|
|
// option is a simple "present" value
|
|
//
|
|
$a_attribs[$s_name] = true;
|
|
} else {
|
|
$s_name = trim(substr($s_attrib,0,$i_pos));
|
|
if (empty($s_name) || $s_name[0] == '.') {
|
|
continue;
|
|
}
|
|
$s_value_list = substr($s_attrib,$i_pos + 1);
|
|
if (($i_pos = strpos($s_value_list,";")) === false)
|
|
//
|
|
// single value
|
|
//
|
|
{
|
|
$a_attribs[$s_name] = ProcessSpecs($s_fld_name,$a_form_data,trim($s_value_list));
|
|
} else
|
|
//
|
|
// list of values
|
|
//
|
|
{
|
|
$a_attribs[$s_name] = TrimArray(explode(";",$s_value_list));
|
|
}
|
|
}
|
|
if ($b_got_valid_list && !isset($a_valid_attribs[$s_name])) {
|
|
$a_errors[] = $s_name;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Process the options specified in the form.
|
|
// Options can be specified in this format:
|
|
// option1,option2,option3,...
|
|
// Each option can be a simple word or a word and value:
|
|
// name
|
|
// name=value
|
|
// No name or value can contain a comma.
|
|
// Values can be simple values or semicolon (;) separated lists:
|
|
// avalue
|
|
// value1;value2;value3;...
|
|
// No value can contain a semicolon.
|
|
// Be careful of values beginning and ending with whitespace characters;
|
|
// they will be trimmed.
|
|
//
|
|
function ProcessOptions($s_name,$a_form_data,&$a_options,$a_valid_options)
|
|
{
|
|
$a_errors = array();
|
|
$a_options = array();
|
|
if (isset($a_form_data[$s_name])) {
|
|
//
|
|
// get the options list and trim each one
|
|
//
|
|
$a_list = TrimArray(explode(",",$a_form_data[$s_name]));
|
|
ProcessAttributeList($s_name,$a_form_data,$a_list,$a_options,$a_errors,$a_valid_options);
|
|
}
|
|
if (count($a_errors) > 0) {
|
|
SendAlertIgnoreSpam(GetMessage(MSG_OPTIONS_INVALID,array("OPT" => $s_name)) .
|
|
implode("\n",$a_errors));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Process the mail_options specified in the form.
|
|
//
|
|
function ProcessMailOptions($a_form_data)
|
|
{
|
|
global $MAIL_OPTS,$VALID_MAIL_OPTIONS;
|
|
|
|
ProcessOptions("mail_options",$a_form_data,$MAIL_OPTS,$VALID_MAIL_OPTIONS);
|
|
}
|
|
|
|
//
|
|
// Check if an option is set
|
|
//
|
|
function IsMailOptionSet($s_name)
|
|
{
|
|
global $MAIL_OPTS;
|
|
|
|
return (isset($MAIL_OPTS[$s_name]));
|
|
}
|
|
|
|
//
|
|
// Return an option's value or NULL if not set.
|
|
//
|
|
function GetMailOption($s_name)
|
|
{
|
|
global $MAIL_OPTS;
|
|
|
|
return (isset($MAIL_OPTS[$s_name]) ? $MAIL_OPTS[$s_name] : NULL);
|
|
}
|
|
|
|
//
|
|
// Process the crm_options specified in the form.
|
|
//
|
|
function ProcessCRMOptions($a_form_data)
|
|
{
|
|
global $CRM_OPTS,$VALID_CRM_OPTIONS;
|
|
|
|
ProcessOptions("crm_options",$a_form_data,$CRM_OPTS,$VALID_CRM_OPTIONS);
|
|
}
|
|
|
|
//
|
|
// Check if an option is set
|
|
//
|
|
function IsCRMOptionSet($s_name)
|
|
{
|
|
global $CRM_OPTS;
|
|
|
|
return (isset($CRM_OPTS[$s_name]));
|
|
}
|
|
|
|
//
|
|
// Return an option's value or NULL if not set.
|
|
//
|
|
/** @noinspection PhpUnused */
|
|
function GetCRMOption($s_name)
|
|
{
|
|
global $CRM_OPTS;
|
|
|
|
return (isset($CRM_OPTS[$s_name]) ? $CRM_OPTS[$s_name] : NULL);
|
|
}
|
|
|
|
//
|
|
// Check if a field is in the mail exclusion list.
|
|
//
|
|
function IsMailExcluded($s_name)
|
|
{
|
|
$a_list = GetMailOption("Exclude");
|
|
if (!isset($a_list)) {
|
|
return (false);
|
|
}
|
|
if (is_array($a_list)) {
|
|
return (in_array($s_name,$a_list));
|
|
} else {
|
|
return ($s_name === $a_list);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Process the autorespond specified in the form.
|
|
//
|
|
function ProcessAROptions($a_form_data)
|
|
{
|
|
global $AR_OPTS,$VALID_AR_OPTIONS;
|
|
|
|
ProcessOptions("autorespond",$a_form_data,$AR_OPTS,$VALID_AR_OPTIONS);
|
|
}
|
|
|
|
//
|
|
// Check if an option is set
|
|
//
|
|
function IsAROptionSet($s_name)
|
|
{
|
|
global $AR_OPTS;
|
|
|
|
return (isset($AR_OPTS[$s_name]));
|
|
}
|
|
|
|
//
|
|
// Return an option's value or NULL if not set.
|
|
//
|
|
function GetAROption($s_name)
|
|
{
|
|
global $AR_OPTS;
|
|
|
|
return (isset($AR_OPTS[$s_name]) ? $AR_OPTS[$s_name] : NULL);
|
|
}
|
|
|
|
//
|
|
// Process the mail_options specified in the form.
|
|
//
|
|
function ProcessFilterOptions($a_form_data)
|
|
{
|
|
global $FILTER_OPTS,$VALID_FILTER_OPTIONS;
|
|
|
|
ProcessOptions("filter_options",$a_form_data,$FILTER_OPTS,$VALID_FILTER_OPTIONS);
|
|
}
|
|
|
|
//
|
|
// Check if an option is set
|
|
//
|
|
function IsFilterOptionSet($s_name)
|
|
{
|
|
global $FILTER_OPTS;
|
|
|
|
return (isset($FILTER_OPTS[$s_name]));
|
|
}
|
|
|
|
//
|
|
// Return an option's value or NULL if not set.
|
|
//
|
|
function GetFilterOption($s_name)
|
|
{
|
|
global $FILTER_OPTS;
|
|
|
|
return (isset($FILTER_OPTS[$s_name]) ? $FILTER_OPTS[$s_name] : NULL);
|
|
}
|
|
|
|
//
|
|
// Lookup a filter attribute for the given filter.
|
|
// Return it's value or false if not set.
|
|
//
|
|
function GetFilterAttrib($s_filter,$s_attrib)
|
|
{
|
|
global $FILTER_ATTRIBS_LOOKUP;
|
|
|
|
$a_attribs = Settings::get('FILTER_ATTRIBS');
|
|
if (!isset($a_attribs[$s_filter]))
|
|
//
|
|
// no attributes for the filter
|
|
//
|
|
{
|
|
return (false);
|
|
}
|
|
if (!isset($FILTER_ATTRIBS_LOOKUP[$s_filter])) {
|
|
//
|
|
// the attributes have not yet been parsed - create the lookup table
|
|
//
|
|
$a_list = TrimArray(explode(",",$a_attribs[$s_filter]));
|
|
$FILTER_ATTRIBS_LOOKUP[$s_filter] = array();
|
|
$a_errors = array();
|
|
|
|
ProcessAttributeList('FILTER_ATTRIBS',array(),$a_list,$FILTER_ATTRIBS_LOOKUP[$s_filter],$a_errors);
|
|
}
|
|
//
|
|
// perform the lookup and return the value
|
|
//
|
|
if (!isset($FILTER_ATTRIBS_LOOKUP[$s_filter][$s_attrib])) {
|
|
return (false);
|
|
}
|
|
return ($FILTER_ATTRIBS_LOOKUP[$s_filter][$s_attrib]);
|
|
}
|
|
|
|
//
|
|
// Check the filter attributes for the given filter.
|
|
// Return true if the given attribute is set otherwise false.
|
|
//
|
|
function IsFilterAttribSet($s_filter,$s_attrib)
|
|
{
|
|
return (GetFilterAttrib($s_filter,$s_attrib));
|
|
}
|
|
|
|
//
|
|
// Process the given .ini file.
|
|
//
|
|
function ProcessFormIniFile($s_file)
|
|
{
|
|
global $EMAIL_ADDRS,$ValidEmails;
|
|
|
|
$a_sections = parse_ini_file($s_file,TRUE);
|
|
//
|
|
// from PHP 5.2.7, parse_ini_file returns false on syntax problems
|
|
// prior to that, an empty array. So, on previous versions of PHP
|
|
// we cannot detect an actual error (an empty array is perfectly valid).
|
|
//
|
|
if ($a_sections === false) {
|
|
Error("bad_ini",GetMessage(MSG_INI_PARSE_ERROR,array("FILE" => $s_file)));
|
|
} elseif (empty($a_sections)) {
|
|
SendAlert(GetMessage(MSG_INI_PARSE_WARN,array("FILE" => $s_file)),false,true);
|
|
}
|
|
if (Settings::get('DB_SEE_INI')) {
|
|
//
|
|
// just display the ini file
|
|
//
|
|
$s_text = "<p><b>The following settings were found in the file '$s_file':</b></p>";
|
|
foreach ($a_sections as $s_sect => $a_settings) {
|
|
$s_text .= "<p>[$s_sect]\n";
|
|
foreach ($a_settings as $s_name => $s_value) {
|
|
$s_text .= "$s_name = \"$s_value\"\n";
|
|
}
|
|
$s_text .= "</p>";
|
|
}
|
|
CreatePage($s_text,"Debug Output - INI File Display");
|
|
FormMailExit();
|
|
}
|
|
//
|
|
// Load the email_addresses section.
|
|
//
|
|
if (isset($a_sections["email_addresses"])) {
|
|
$a_addr_list = $a_sections["email_addresses"];
|
|
//
|
|
// make these addresses valid
|
|
//
|
|
foreach ($a_addr_list as $s_alias => $s_list) {
|
|
$EMAIL_ADDRS[$s_alias] = $s_list;
|
|
$ValidEmails->AddAddresses($s_list);
|
|
}
|
|
}
|
|
//
|
|
// Process special fields
|
|
//
|
|
if (isset($a_sections["special_fields"])) {
|
|
foreach ($a_sections["special_fields"] as $s_name => $m_value) {
|
|
if (IsSpecialField($s_name)) {
|
|
ValidateSpecialField($s_name,$m_value,false);
|
|
SetSpecialField($s_name,$m_value);
|
|
//
|
|
// if this is the recipients, cc, or bcc field,
|
|
// make the addresses valid
|
|
//
|
|
if ($s_name === "recipients" || $s_name === "cc" || $s_name === "bcc")
|
|
//
|
|
// coming from the INI file, the values can only be strings
|
|
//
|
|
{
|
|
if (is_string($m_value)) {
|
|
$ValidEmails->AddAddresses($m_value);
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// check for multiple valued special fields
|
|
//
|
|
if (($a_multi_fld = IsSpecialMultiField($s_name)) !== false) {
|
|
SetSpecialMultiField($a_multi_fld[0],$a_multi_fld[1],$m_value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* UnMangle an email address. This means replacing AT_MANGLE in the given
|
|
* string with the @ symbol.
|
|
* It can also lookup an email alias and return the corresponding email address.
|
|
* Email aliases are defined in the EMAIL_ADDRS configuration setting or in an INI file.
|
|
*
|
|
* @param string $s_email the email address to unmangle
|
|
*
|
|
* @return string the actual email address
|
|
*/
|
|
function UnMangle($s_email)
|
|
{
|
|
$email_addrs = Settings::get('EMAIL_ADDRS');
|
|
|
|
//
|
|
// map from a name to the real email address; if it exists
|
|
//
|
|
if (isset($email_addrs[$s_email])) {
|
|
$s_email = $email_addrs[$s_email];
|
|
}
|
|
//
|
|
// unmangle
|
|
//
|
|
if (Settings::get('AT_MANGLE') != "") {
|
|
$s_email = str_replace(Settings::get('AT_MANGLE'),"@",$s_email);
|
|
}
|
|
return ($s_email);
|
|
}
|
|
|
|
//
|
|
// Check a list of email addresses (comma separated); returns a list
|
|
// of valid email addresses (comma separated).
|
|
// The list can be an array of comma separated lists.
|
|
// The return value is true if there is at least one valid email address.
|
|
//
|
|
function CheckEmailAddress($m_addr,&$s_valid,&$s_invalid,$b_check = true)
|
|
{
|
|
global $ValidEmails;
|
|
|
|
$s_invalid = $s_valid = "";
|
|
if (is_array($m_addr)) {
|
|
$a_list = array();
|
|
foreach ($m_addr as $s_addr_list) {
|
|
$a_list = array_merge($a_list,TrimArray(explode(",",$s_addr_list)));
|
|
}
|
|
} else {
|
|
$a_list = TrimArray(explode(",",$m_addr));
|
|
}
|
|
$a_invalid = array();
|
|
$n_empty = 0;
|
|
for ($ii = 0 ; $ii < count($a_list) ; $ii++) {
|
|
if ($a_list[$ii] === "") {
|
|
//
|
|
// ignore, but count empty addresses
|
|
//
|
|
$n_empty++;
|
|
continue;
|
|
}
|
|
$s_email = UnMangle($a_list[$ii]);
|
|
//
|
|
// UnMangle works with INI files too, and a single
|
|
// word can expand to a list of email addresses.
|
|
//
|
|
$a_this_list = TrimArray(explode(",",$s_email));
|
|
foreach ($a_this_list as $s_email) {
|
|
if ($s_email === "") {
|
|
//
|
|
// ignore, but count empty addresses
|
|
//
|
|
$n_empty++;
|
|
continue;
|
|
}
|
|
if ($b_check) {
|
|
$b_is_valid = $ValidEmails->CheckAddress($s_email);
|
|
} else {
|
|
$b_is_valid = true;
|
|
}
|
|
if ($b_is_valid) {
|
|
if (empty($s_valid)) {
|
|
$s_valid = $s_email;
|
|
} else {
|
|
$s_valid .= "," . $s_email;
|
|
}
|
|
} else {
|
|
$a_invalid[] = $s_email;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// just ignore empty recipients unless there are *no* valid recipients
|
|
//
|
|
if (empty($s_valid) && $n_empty > 0) {
|
|
$a_invalid[] = GetMessage(MSG_EMPTY_ADDRESSES,array("COUNT" => $n_empty));
|
|
}
|
|
if (count($a_invalid) > 0) {
|
|
$s_invalid = implode(",",$a_invalid);
|
|
}
|
|
return (!empty($s_valid));
|
|
}
|
|
|
|
//
|
|
// PHP 8 replacement for
|
|
// filter_var($url,FILTER_SANITIZE_STRING)
|
|
// See: https://stackoverflow.com/questions/69207368/constant-filter-sanitize-string-is-deprecated
|
|
//
|
|
function filter_string_polyfill(string $string): string
|
|
{
|
|
$str = preg_replace('/\x00|<[^>]*>?/','',$string);
|
|
return str_replace(["'",'"'],[''','"'],$str);
|
|
}
|
|
|
|
//
|
|
// Redirect to another URL
|
|
//
|
|
function Redirect($url,$title)
|
|
{
|
|
global $ExecEnv;
|
|
|
|
//
|
|
// for browsers without cookies enabled, append the Session ID
|
|
//
|
|
if ($ExecEnv->allowSessionURL()) {
|
|
if (session_id() !== "") {
|
|
$url = AddURLParams($url,session_name() . "=" . urlencode(session_id()));
|
|
} elseif (defined("SID")) {
|
|
$url = AddURLParams($url,SID);
|
|
}
|
|
}
|
|
|
|
//FMDebug("Before redirecting, FormData = ".(isset($_SESSION["FormData"]) ? var_export($_SESSION["FormData"],true) : "NULL"));
|
|
|
|
//
|
|
// this is probably a good idea to ensure the session data
|
|
// is written away
|
|
//
|
|
if (function_exists('session_write_close')) {
|
|
session_write_close();
|
|
}
|
|
|
|
header("Location: $url");
|
|
|
|
//
|
|
// avoid XSS by sanitizing the URL
|
|
//
|
|
$url = stripJS(filter_string_polyfill($url));
|
|
|
|
//
|
|
// if the header doesn't work, try JavaScript.
|
|
// if that doesn't work, provide a manual link
|
|
//
|
|
$s_text = GetMessage(MSG_PLSWAIT_REDIR) . "\n\n";
|
|
$s_text .= "<script language=\"JavaScript\" type=\"text/javascript\">";
|
|
$s_text .= "window.location.href = '$url';";
|
|
$s_text .= "</script>";
|
|
$s_text .= "\n\n" . GetMessage(MSG_IFNOT_REDIR,array("URL" => $url));
|
|
CreatePage($s_text,$title);
|
|
FormMailExit();
|
|
}
|
|
|
|
/**
|
|
* Remove 'javascript:' from the given text.
|
|
*
|
|
* @param string $s_text the text to be modified
|
|
*
|
|
* @return string|string[]
|
|
*/
|
|
function stripJS($s_text)
|
|
{
|
|
return str_ireplace('javascript:','',$s_text);
|
|
}
|
|
|
|
class JSON
|
|
{
|
|
function _Format($m_val)
|
|
{
|
|
if (is_bool($m_val)) {
|
|
$s_value = ($m_val) ? "true" : "false";
|
|
} elseif (is_string($m_val)) {
|
|
//
|
|
// convert literal line breaks into JavaScript escape sequences
|
|
//
|
|
$s_value = '"' . str_replace(array("\r","\n"),array('\\r','\\n'),addslashes($m_val)) . '"';
|
|
} elseif (is_numeric($m_val)) {
|
|
$s_value = $m_val;
|
|
} elseif (is_array($m_val)) {
|
|
$s_value = $this->_FormatArray($m_val);
|
|
} else {
|
|
$s_value = "null";
|
|
}
|
|
return ($s_value);
|
|
}
|
|
|
|
function _FormatArray($a_array)
|
|
{
|
|
if ($this->_IsNumericArray($a_array)) {
|
|
$a_values = array();
|
|
foreach ($a_array as $m_val) {
|
|
$a_values[] = $this->_Format($m_val);
|
|
}
|
|
$s_value = "[" . implode(",",$a_values) . "]";
|
|
} else {
|
|
//
|
|
// associative arrays are objects
|
|
//
|
|
$s_value = $this->MakeObject($a_array);
|
|
}
|
|
return ($s_value);
|
|
}
|
|
|
|
//
|
|
// check if we have a numeric array or an associative array
|
|
// numeric arrays may have holes; numeric array indexes must
|
|
// be integers
|
|
//
|
|
function _IsNumericArray($a_data)
|
|
{
|
|
if (empty($a_data)) {
|
|
return (true);
|
|
} // empty array - treat as numeric
|
|
//
|
|
// check all the keys for numeric
|
|
//
|
|
$a_keys = array_keys($a_data);
|
|
foreach ($a_keys as $m_index) {
|
|
if (!is_int($m_index)) {
|
|
return (false);
|
|
}
|
|
}
|
|
return (true);
|
|
}
|
|
|
|
function MakeObject($a_data)
|
|
{
|
|
$a_members = array();
|
|
foreach ($a_data as $s_key => $m_val) {
|
|
$a_members[] = '"' . $s_key . '":' . $this->_Format($m_val);
|
|
}
|
|
return ("{" . implode(",",$a_members) . "}");
|
|
}
|
|
}
|
|
|
|
function CORS_Response()
|
|
{
|
|
header('Access-Control-Allow-Origin: *');
|
|
header('Access-Control-Max-Age: 36000');
|
|
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
|
header('Access-Control-Allow-Headers: X-Requested-With');
|
|
}
|
|
|
|
function JSON_Result($s_result,$a_data = array())
|
|
{
|
|
global $aGetVars;
|
|
|
|
FMDebug("Sending JSON_Result: $s_result");
|
|
$a_data["Result"] = $s_result;
|
|
$json = new JSON();
|
|
$s_ret = $json->MakeObject($a_data);
|
|
CORS_Response();
|
|
//
|
|
// handle JSONP request
|
|
//
|
|
if (isset($aGetVars['callback']) && $aGetVars['callback'] != '') {
|
|
header('Content-Type: text/javascript; charset=utf-8');
|
|
$s_ret = $aGetVars['callback'] . "($s_ret);";
|
|
FMDebug('JSONP request callback=' . $aGetVars['callback']);
|
|
} else {
|
|
header('Content-Encoding: utf-8');
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
}
|
|
FMDebug("JSON_Result output: " . $s_ret);
|
|
echo $s_ret;
|
|
}
|
|
|
|
//
|
|
// JoinLines is just like "implode" except that it checks
|
|
// the end of each array for the separator already being
|
|
// there. This allows us to join a mixture of mail
|
|
// header lines (already terminated) with body lines.
|
|
// This logic works if HEAD_CRLF, for example, is the same
|
|
// as BODY_LF (i.e. both "\r\n") or if BODY_LF is the
|
|
// same as the last character in HEAD_CRLF (i.e.
|
|
// HEAD_CRLF = "\r\n" and BODY_LF = "\n").
|
|
// Other value combinations may break things.
|
|
//
|
|
function JoinLines($s_sep,$a_lines)
|
|
{
|
|
$s_str = "";
|
|
if (($i_sep_len = strlen($s_sep)) == 0)
|
|
//
|
|
// no separator
|
|
//
|
|
{
|
|
return (implode("",$a_lines));
|
|
}
|
|
$n_lines = count($a_lines);
|
|
for ($ii = 0 ; $ii < $n_lines ; $ii++) {
|
|
$s_line = $a_lines[$ii];
|
|
if (substr($s_line,-$i_sep_len) == $s_sep) {
|
|
$s_str .= $s_line;
|
|
} else {
|
|
$s_str .= $s_line;
|
|
//
|
|
// don't append a separator to the last line
|
|
//
|
|
if ($ii < $n_lines - 1) {
|
|
$s_str .= $s_sep;
|
|
}
|
|
}
|
|
}
|
|
return ($s_str);
|
|
}
|
|
|
|
//
|
|
// Re-orders an array of email headers into the
|
|
// order recommended by RFC822, section 4.1:
|
|
// It is recommended that, if present,
|
|
// headers be sent in the order "Return-
|
|
// Path", "Received", "Date", "From", "Subject",
|
|
// "Sender", "To", "cc", etc.
|
|
//
|
|
// Note that RFC822 is obsoleted by RFC2822 and
|
|
// the latter states that field order doesn't
|
|
// matter (except for some tracing fields).
|
|
// However, a FormMail user reported that Yahoo doesn't like
|
|
// email where the CC header appears before the From
|
|
// header. So, as always, we try to work with broken
|
|
// servers too...
|
|
//
|
|
// Returns an array indexed by the require numerical
|
|
// order. Each element is an array containing the
|
|
// header value (name,value pair).
|
|
//
|
|
function OrderHeaders($a_headers)
|
|
{
|
|
//
|
|
// we list the headers we're responsible for
|
|
// in the order suggested
|
|
//
|
|
$a_ordering = array("From","Subject","To","Cc","Bcc","Reply-To");
|
|
$a_ordered_headers = array();
|
|
foreach ($a_ordering as $s_name) {
|
|
if (isset($a_headers[$s_name])) {
|
|
$a_ordered_headers[] = array($s_name => $a_headers[$s_name]);
|
|
unset($a_headers[$s_name]);
|
|
}
|
|
}
|
|
//
|
|
// now add in the remaining headers
|
|
//
|
|
foreach ($a_headers as $s_name => $s_value) {
|
|
$a_ordered_headers[] = array($s_name => $a_headers[$s_name]);
|
|
}
|
|
return ($a_ordered_headers);
|
|
}
|
|
|
|
//
|
|
// Makes a mail header field body "safe".
|
|
// This simply places a backslash in front of every double-quote.
|
|
// There's probably more we could do if required, but this
|
|
// attempts to provide the same protection that was in the first
|
|
// version of FormMail. In that version, every incoming
|
|
// field had double-quotes replaced with single quotes.
|
|
// That processing is no longer performed, and this
|
|
// function is used to protect against potential attacks in
|
|
// header fields - not by replacing double quotes with single quotes,
|
|
// but by using the backslash "quoting" feature of RFC2822.
|
|
//
|
|
// This code could be improved by parsing the header and rewriting
|
|
// it to be valid, possibly removing junk.
|
|
//
|
|
// That's a lot of code, though!
|
|
//
|
|
function SafeHeader($s_str)
|
|
{
|
|
return (str_replace('"','\\"',$s_str));
|
|
}
|
|
|
|
//
|
|
// makes a string safe to put as words in a header
|
|
//
|
|
function SafeHeaderWords($s_str)
|
|
{
|
|
//
|
|
// We zap various characters and replace them with a question mark.
|
|
// Also, we don't handle quoted strings, which are valid words.
|
|
//
|
|
$s_specials = '()<>@,;:\\".[]'; // special characters defined by RFC822
|
|
$s_str = preg_replace('/[[:cntrl:]]+/',"?",$s_str); // zap all control chars
|
|
$s_str = preg_replace("/[" . preg_quote($s_specials,"/") . "]/","?",$s_str); // zap all specials
|
|
return ($s_str);
|
|
}
|
|
|
|
//
|
|
// makes a string safe to put as a quoted string in a header
|
|
//
|
|
function SafeHeaderQString($s_str)
|
|
{
|
|
return (str_replace('"','\\"',
|
|
str_replace("\\","\\\\",
|
|
str_replace("\r"," ",
|
|
str_replace("\r\n"," ",$s_str)))));
|
|
}
|
|
|
|
//
|
|
// makes a string safe to put in a header comment
|
|
//
|
|
function SafeHeaderComment($s_str)
|
|
{
|
|
return (str_replace("(","\\(",
|
|
str_replace(")","\\)",
|
|
str_replace("\\","\\\\",
|
|
str_replace("\r"," ",
|
|
str_replace("\r\n"," ",$s_str))))));
|
|
}
|
|
|
|
//
|
|
// makes a string safe to put in a header as an email address
|
|
//
|
|
function SafeHeaderEmail($s_str)
|
|
{
|
|
//
|
|
// An email address is made up of local and domain parts
|
|
// each of these is made up of "words" separated by "."
|
|
// each "word" can be a sequence of characters excluding
|
|
// specials, space and control characters OR it can be
|
|
// a quoted string.
|
|
//
|
|
// The correct processing would be to completely
|
|
// parse the address, strip junk, double-quote
|
|
// words that need to be turned into quote strings,
|
|
// and return a well-formed email address.
|
|
//
|
|
// That's a lot of code!
|
|
//
|
|
// So, instead, we opt for stripping out control characters.
|
|
//
|
|
$s_str = preg_replace('/[[:cntrl:]]+/',"",$s_str); // zap all control chars
|
|
return ($s_str);
|
|
}
|
|
|
|
//
|
|
// Expands an array of mail headers into mail header lines.
|
|
//
|
|
function ExpandMailHeaders($a_headers,$b_fold = false)
|
|
{
|
|
$s_hdrs = "";
|
|
$a_ordered_headers = OrderHeaders($a_headers);
|
|
for ($ii = 0 ; $ii < count($a_ordered_headers) ; $ii++) {
|
|
foreach ($a_ordered_headers[$ii] as $s_name => $s_value) {
|
|
if ($s_name != "") {
|
|
if ($s_hdrs != "") {
|
|
$s_hdrs .= Settings::get('HEAD_CRLF');
|
|
}
|
|
if ($b_fold) {
|
|
$s_hdrs .= HeaderFolding($s_name . ": " . $s_value);
|
|
} else {
|
|
$s_hdrs .= $s_name . ": " . $s_value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//FMDebug("Headers are: $s_hdrs");
|
|
return ($s_hdrs);
|
|
}
|
|
|
|
//
|
|
// Expands an array of mail headers into an array containing header lines.
|
|
//
|
|
function ExpandMailHeadersArray($a_headers)
|
|
{
|
|
$a_hdrs = array();
|
|
$a_ordered_headers = OrderHeaders($a_headers);
|
|
for ($ii = 0 ; $ii < count($a_ordered_headers) ; $ii++) {
|
|
foreach ($a_ordered_headers[$ii] as $s_name => $s_value) {
|
|
if ($s_name != "") {
|
|
$a_hdrs[] = $s_name . ": " . $s_value . Settings::get('HEAD_CRLF');
|
|
}
|
|
}
|
|
}
|
|
return ($a_hdrs);
|
|
}
|
|
|
|
//
|
|
// Low-level email send function; either calls PHP's mail function
|
|
// or uses the PEAR Mail object.
|
|
// NOTE: for some errors, there's no point trying to email
|
|
// an alert message! So, in these cases, we just display the error to
|
|
// the user.
|
|
// $s_options are ignored for PEAR sending.
|
|
//
|
|
function DoMail($s_to,$s_subject,$s_mesg,$a_headers,$s_options)
|
|
{
|
|
global $ALT_MAIL_FUNCTION;
|
|
|
|
if ($ALT_MAIL_FUNCTION !== '') {
|
|
return ($ALT_MAIL_FUNCTION($s_to,$s_subject,$s_mesg,$a_headers,$s_options));
|
|
} else {
|
|
//
|
|
// Encode the subject line.
|
|
// Ideally, we want to encode the relevant parts of To, From, Cc,
|
|
// Reply-To, and this is the right place to do it.
|
|
// However, it's another 1000 lines of code!
|
|
// So, we must compromise the code quality because of this cost.
|
|
// We encode subject here, and we encode the From line where it's
|
|
// created. The rest remain for a future version where code size
|
|
// can be controlled.
|
|
//
|
|
$s_subject = EncodeHeaderText($s_subject);
|
|
if (!Settings::isEmpty('PEAR_SMTP_HOST')) {
|
|
//
|
|
// Note that PEAR Mail seems to take responsibility for header line folding
|
|
//
|
|
require_once("Mail.php");
|
|
|
|
$a_params = array("host" => Settings::get('PEAR_SMTP_HOST'),
|
|
"port" => Settings::get('PEAR_SMTP_PORT')
|
|
);
|
|
if (!Settings::isEmpty('PEAR_SMTP_USER')) {
|
|
$a_params["auth"] = TRUE;
|
|
$a_params["username"] = Settings::get('PEAR_SMTP_USER');
|
|
$a_params["password"] = Settings::get('PEAR_SMTP_PWD');
|
|
}
|
|
$mailer = Mail::factory("smtp",$a_params);
|
|
if (!is_object($mailer)) {
|
|
ShowError("pear_error",GetMessage(MSG_PEAR_OBJ),FALSE,FALSE);
|
|
FormMailExit();
|
|
}
|
|
if (strtolower(get_class($mailer)) === 'pear_error') {
|
|
ShowError("pear_error",$mailer->getMessage(),FALSE,FALSE);
|
|
FormMailExit();
|
|
}
|
|
if (!isset($a_headers['To']) && !isset($a_headers['to'])) {
|
|
$a_headers['To'] = SafeHeader($s_to);
|
|
}
|
|
if (!isset($a_headers['Subject']) && !isset($a_headers['subject'])) {
|
|
$a_headers['Subject'] = SafeHeader($s_subject);
|
|
}
|
|
$res = $mailer->send($s_to,$a_headers,$s_mesg);
|
|
if ($res === TRUE) {
|
|
return (TRUE);
|
|
}
|
|
|
|
global $aAlertInfo;
|
|
|
|
$aAlertInfo[] = GetMessage(MSG_PEAR_ERROR,array("MSG" => $res->getMessage()));
|
|
return (FALSE);
|
|
} else {
|
|
//$s_subject = HeaderFolding($s_subject,RFCLINELEN-10); // "Subject: " is about 10 chars
|
|
//
|
|
// Notes from Feb 2010....
|
|
//
|
|
// PHP's mail function (tested in version 5.2.6) does folding of the
|
|
// To line and the Subject line.
|
|
// If we do it, then things break.
|
|
//
|
|
// This area is quite confusing. It's not clear whether the script
|
|
// should be folding header lines or whether the MTA should do it.
|
|
// We *do know* (as stated above) that folding To and Subject breaks things.
|
|
//
|
|
// But folding other header lines properly, seems to be OK.
|
|
//
|
|
// However, for years FormMail never did header line folding (except for the
|
|
// soft line breaks inserted by the quoted_printable_encode function we had used),
|
|
// and we didn't seem to get any reports of breakage (except for problems
|
|
// with the quoted_printable_encode soft line breaks!).
|
|
//
|
|
// So, even though we've implemented all the code for header line folding,
|
|
// we'll not use it.
|
|
// No header line folding will be performed in version 8.22 onwards.
|
|
//
|
|
if ($s_options !== "") {
|
|
return (mail($s_to,$s_subject,$s_mesg,ExpandMailHeaders($a_headers),$s_options));
|
|
} else {
|
|
return (mail($s_to,$s_subject,$s_mesg,ExpandMailHeaders($a_headers)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Send an email
|
|
//
|
|
function SendCheckedMail($to,$subject,$mesg,$sender,$a_headers = array())
|
|
{
|
|
|
|
$b_f_option = false;
|
|
$b_form_option = IsMailOptionSet("SendMailFOption"); // this is superseded, but still supported
|
|
if (Settings::get('SENDMAIL_F_OPTION') || $b_form_option) {
|
|
if (empty($sender)) {
|
|
//
|
|
// SENDMAIL_F_OPTION with no sender is silently ignored
|
|
//
|
|
if ($b_form_option) {
|
|
//
|
|
// form has specified SendMailFOption, but there's no
|
|
// sender address
|
|
//
|
|
static $b_in_here = false;
|
|
global $SERVER;
|
|
|
|
if (!$b_in_here) // prevent infinite recursion
|
|
{
|
|
$b_in_here = true;
|
|
SendAlert(GetMessage(MSG_NO_FOPT_ADDR));
|
|
$b_in_here = false;
|
|
}
|
|
//
|
|
// if there's no from address, create a dummy one
|
|
//
|
|
$sender = "dummy@" . (isset($SERVER) ? $SERVER : "UnknownServer");
|
|
$a_headers['From'] = $sender;
|
|
$b_f_option = true;
|
|
}
|
|
} else {
|
|
$b_f_option = true;
|
|
}
|
|
}
|
|
if (Settings::get('INI_SET_FROM') && !empty($sender)) {
|
|
ini_set('sendmail_from',$sender);
|
|
}
|
|
|
|
return (DoMail($to,$subject,$mesg,$a_headers,($b_f_option ? "-f$sender" : "")));
|
|
}
|
|
|
|
//
|
|
// Send an alert email, but not if ATTACK_DETECTION_IGNORE_ERRORS is true.
|
|
//
|
|
function SendAlertIgnoreSpam($s_error,$b_filter = true,$b_non_error = false)
|
|
{
|
|
if (!Settings::get('ATTACK_DETECTION_IGNORE_ERRORS')) {
|
|
SendAlert($s_error,$b_filter,$b_non_error);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Send an alert email
|
|
//
|
|
function SendAlert($s_error,$b_filter = true,$b_non_error = false,$s_addl_subject = '')
|
|
{
|
|
global $SPECIAL_VALUES,$FORMATTED_INPUT,$aServerVars,$aStrippedFormVars;
|
|
global $aAlertInfo,$aCleanedValues,$aFieldOrder,$sHTMLCharSet;
|
|
|
|
//
|
|
// The following initialisations are so that ShowError can run from fmhookpreinit scripts
|
|
//
|
|
$aSpecialValues = isset($SPECIAL_VALUES) ? $SPECIAL_VALUES : array();
|
|
$aFormattedInputValues = isset($FORMATTED_INPUT) ? $FORMATTED_INPUT : array();
|
|
$aServerVarValues = isset($aServerVars) ? $aServerVars : array();
|
|
$aStrippedFormVarValues = isset($aStrippedFormVars) ? $aStrippedFormVars : array();
|
|
$aAlertInfoValues = isset($aAlertInfo) ? $aAlertInfo : array();
|
|
$aCleaned = isset($aCleanedValues) ? $aCleanedValues : array();
|
|
$aFieldOrderValues = isset($aFieldOrder) ? $aFieldOrder : array();
|
|
|
|
$s_error = str_replace("\n",Settings::get('BODY_LF'),$s_error);
|
|
$b_got_filter = GetFilterSpec($s_filter_name,$a_filter_list);
|
|
|
|
//
|
|
// if there is a filter specified and we're not sending the alert
|
|
// through the filter, don't show the user's data. This is
|
|
// on the assumption that the filter is an encryption program; so,
|
|
// we don't want to send the user's data in clear text inside the
|
|
// alerts.
|
|
//
|
|
$b_show_data = true;
|
|
if ($b_got_filter && !$b_filter) {
|
|
$b_show_data = false;
|
|
}
|
|
|
|
$s_form_subject = $s_alert_to = "";
|
|
$b_check = true;
|
|
//
|
|
// might be too early to have $aSpecialValues set, so
|
|
// look in the form vars too
|
|
//
|
|
if (isset($aSpecialValues["alert_to"])) {
|
|
$s_alert_to = trim($aSpecialValues["alert_to"]);
|
|
}
|
|
if (empty($s_alert_to) && isset($aStrippedFormVarValues["alert_to"])) {
|
|
$s_alert_to = trim($aStrippedFormVarValues["alert_to"]);
|
|
}
|
|
|
|
if (isset($aSpecialValues["subject"])) {
|
|
$s_form_subject = trim($aSpecialValues["subject"]);
|
|
}
|
|
if (empty($s_form_subject) && isset($aStrippedFormVarValues["subject"])) {
|
|
$s_form_subject = trim($aStrippedFormVarValues["subject"]);
|
|
}
|
|
|
|
if (empty($s_alert_to)) {
|
|
$s_alert_to = Settings::get('DEF_ALERT');
|
|
$b_check = false;
|
|
}
|
|
if (!empty($s_alert_to)) {
|
|
$s_from_addr = $s_from = "";
|
|
$a_headers = array();
|
|
if (!Settings::isEmpty('FROM_USER')) {
|
|
if (Settings::get('FROM_USER') != "NONE") {
|
|
$a_headers['From'] = Settings::get('FROM_USER');
|
|
$s_from_addr = Settings::get('FROM_USER');
|
|
$s_from = "From: $s_from_addr";
|
|
}
|
|
} else {
|
|
global $SERVER;
|
|
|
|
$s_from_addr = "FormMail@" . $SERVER;
|
|
$a_headers['From'] = $s_from_addr;
|
|
$s_from = "From: $s_from_addr";
|
|
}
|
|
$s_mesg = "To: " . UnMangle($s_alert_to) . Settings::get('BODY_LF');
|
|
//
|
|
// if a language pack has been included, a lot of error messages
|
|
// may need the character set to be provided.
|
|
// If that's available from the language pack, use it,
|
|
// otherwise, if it's a mail_option, use it from there.
|
|
//
|
|
$s_charset = "";
|
|
if (isset($sHTMLCharSet) && $sHTMLCharSet !== "") {
|
|
$s_charset = $sHTMLCharSet;
|
|
} else {
|
|
if (IsMailOptionSet("CharSet")) {
|
|
$s_charset = GetMailOption("CharSet");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Alerts are plain text emails, so convert any HTML entities
|
|
// back to their original characters. Note, this will only work on PHP
|
|
// version 4.3.0 and above.
|
|
//
|
|
if (function_exists("html_entity_decode")) {
|
|
$s_error = @html_entity_decode($s_error,ENT_COMPAT,$s_charset);
|
|
}
|
|
|
|
if ($s_charset !== "") {
|
|
$a_headers['Content-Type'] = SafeHeader("text/plain; charset=$s_charset");
|
|
}
|
|
|
|
if (!empty($s_from)) {
|
|
$s_mesg .= $s_from . Settings::get('BODY_LF');
|
|
}
|
|
$s_mesg .= Settings::get('BODY_LF');
|
|
if (count($aAlertInfoValues) > 0) {
|
|
if ($b_show_data) {
|
|
$s_error .= Settings::get('BODY_LF') . GetMessage(MSG_MORE_INFO) . Settings::get('BODY_LF');
|
|
$s_error .= implode(Settings::get('BODY_LF'),$aAlertInfoValues);
|
|
} else {
|
|
$s_error .= Settings::get('BODY_LF') . GetMessage(MSG_INFO_STOPPED) . Settings::get('BODY_LF');
|
|
}
|
|
}
|
|
//
|
|
// some fields aren't security issues - show those in the alert
|
|
//
|
|
$a_safe_fields = array(
|
|
"email: " . isset($aSpecialValues["email"]) ? $aSpecialValues["email"] : '',
|
|
"realname: " . isset($aSpecialValues["realname"]) ? $aSpecialValues["realname"] : '',
|
|
);
|
|
$s_safe_data = implode(Settings::get('BODY_LF'),$a_safe_fields);
|
|
|
|
if ($b_non_error) {
|
|
$s_preamble = $s_error . Settings::get('BODY_LF') . Settings::get('BODY_LF');
|
|
$s_mesg .= $s_preamble;
|
|
$s_subj = GetMessage(MSG_FM_ALERT) . ($s_addl_subject !== '' ? " - $s_addl_subject" : '');
|
|
if (!empty($s_form_subject)) {
|
|
$s_subj .= " ($s_form_subject)";
|
|
}
|
|
} else {
|
|
$s_preamble = GetMessage(MSG_FM_ERROR_LINE) . Settings::get('BODY_LF') .
|
|
$s_error . Settings::get('BODY_LF') . Settings::get('BODY_LF');
|
|
$s_mesg .= $s_preamble;
|
|
$s_subj = GetMessage(MSG_FM_ERROR) . ($s_addl_subject !== '' ? " - $s_addl_subject" : '');
|
|
if (!empty($s_form_subject)) {
|
|
$s_subj .= " ($s_form_subject)";
|
|
}
|
|
$s_mesg .= $s_safe_data;
|
|
$s_mesg .= Settings::get('BODY_LF') . Settings::get('BODY_LF');
|
|
if ($b_show_data) {
|
|
$s_mesg .= implode(Settings::get('BODY_LF'),$aFormattedInputValues);
|
|
} else {
|
|
$s_mesg .= GetMessage(MSG_USERDATA_STOPPED);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We only need to filter the form fields if the filter that
|
|
* is specified is an encrypting filter.
|
|
*/
|
|
if ($b_filter && $b_got_filter &&
|
|
IsFilterAttribSet($aSpecialValues["filter"],"Encrypts")
|
|
) {
|
|
$s_new_mesg = $s_preamble . $s_safe_data;
|
|
$s_new_mesg .= Settings::get('BODY_LF') . Settings::get('BODY_LF');
|
|
|
|
if ($a_filter_list !== false) {
|
|
//
|
|
// just filter the critical fields
|
|
//
|
|
list($s_unfiltered,$s_filtered_results) =
|
|
GetFilteredOutput($aFieldOrderValues,$aCleaned,
|
|
$s_filter_name,$a_filter_list);
|
|
$s_new_mesg .= $s_unfiltered;
|
|
} else {
|
|
//
|
|
// filter everything
|
|
//
|
|
$s_filtered_results = Filter($s_filter_name,$s_mesg);
|
|
}
|
|
$s_new_mesg .= GetMessage(MSG_FILTERED,array("FILTER" => $s_filter_name)) .
|
|
Settings::get('BODY_LF') . Settings::get('BODY_LF') .
|
|
$s_filtered_results;
|
|
$s_mesg = $s_new_mesg;
|
|
}
|
|
$s_mesg .= Settings::get('BODY_LF');
|
|
|
|
if (isset($aServerVarValues['HTTP_REFERER'])) {
|
|
$s_mesg .= "Referring page was " . $aServerVarValues['HTTP_REFERER'];
|
|
} elseif (isset($aSpecialValues['this_form']) && $aSpecialValues['this_form'] !== "") {
|
|
$s_mesg .= "Referring form was " . $aSpecialValues['this_form'];
|
|
}
|
|
|
|
$s_mesg .= Settings::get('BODY_LF');
|
|
|
|
if (isset($aServerVarValues['SERVER_NAME'])) {
|
|
$s_mesg .= "SERVER_NAME was " . $aServerVarValues['SERVER_NAME'] . Settings::get('BODY_LF');
|
|
}
|
|
if (isset($aServerVarValues['REQUEST_URI'])) {
|
|
$s_mesg .= "REQUEST_URI was " . $aServerVarValues['REQUEST_URI'] . Settings::get('BODY_LF');
|
|
}
|
|
|
|
$s_mesg .= Settings::get('BODY_LF');
|
|
|
|
if (isset($aServerVarValues['REMOTE_ADDR'])) {
|
|
$s_mesg .= "User IP address was " . $aServerVarValues['REMOTE_ADDR'] . Settings::get('BODY_LF');
|
|
}
|
|
if (isset($aServerVarValues['HTTP_USER_AGENT'])) {
|
|
$s_mesg .= "User agent was " . $aServerVarValues['HTTP_USER_AGENT'] . Settings::get('BODY_LF');
|
|
}
|
|
|
|
if ($b_check) {
|
|
if (CheckEmailAddress($s_alert_to,$s_valid,$s_invalid)) {
|
|
return (SendCheckedMail($s_valid,$s_subj,$s_mesg,$s_from_addr,$a_headers));
|
|
}
|
|
} else {
|
|
return (SendCheckedMail($s_alert_to,$s_subj,$s_mesg,$s_from_addr,$a_headers));
|
|
}
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// Read the lines in a file and return an array.
|
|
// Each line is stripped of line termination characters.
|
|
//
|
|
function ReadLines($fp)
|
|
{
|
|
$a_lines = array();
|
|
while (!feof($fp)) {
|
|
$s_line = fgets($fp,4096);
|
|
//
|
|
// strip carriage returns and line feeds
|
|
//
|
|
$s_line = str_replace("\r","",$s_line);
|
|
$s_line = str_replace("\n","",$s_line);
|
|
$a_lines[] = $s_line;
|
|
}
|
|
return ($a_lines);
|
|
}
|
|
|
|
//
|
|
// Open a URL and return the data from it as a string or array of lines.
|
|
// Returns false on failure ($s_error has the error string)
|
|
//
|
|
function GetURL($s_url,&$s_error,$b_ret_lines = false,$n_depth = 0)
|
|
{
|
|
global $php_errormsg,$aServerVars,$sUserAgent,$ExecEnv;
|
|
|
|
//
|
|
// open the URL with the same session as we have
|
|
//
|
|
if ($ExecEnv->allowSessionURL()) {
|
|
if (session_id() !== "") {
|
|
$s_url = AddURLParams($s_url,session_name() . "=" . urlencode(session_id()));
|
|
}
|
|
if (defined("SID")) {
|
|
$s_url = AddURLParams($s_url,SID);
|
|
}
|
|
}
|
|
|
|
$http_get = new HTTPGet($s_url);
|
|
|
|
//
|
|
// Determine authentication requirements
|
|
//
|
|
if (Settings::get('AUTHENTICATE') !== "" || Settings::get('AUTH_USER') !== "" || Settings::get('AUTH_PW') !== "") {
|
|
if (Settings::get('AUTHENTICATE') === "") {
|
|
$http_get->SetAuthentication("Basic",Settings::get('AUTH_USER'),Settings::get('AUTH_PW'));
|
|
} else {
|
|
$http_get->SetAuthenticationLine(Settings::get('AUTHENTICATE'));
|
|
}
|
|
} else {
|
|
$a_parts = $http_get->GetURLSplit();
|
|
if (isset($a_parts["user"]) || isset($a_parts["pass"])) {
|
|
$s_auth_user = isset($a_parts["user"]) ? $a_parts["user"] : "";
|
|
$s_auth_pass = isset($a_parts["pass"]) ? $a_parts["pass"] : "";
|
|
} else {
|
|
$s_auth_type = isset($aServerVars["PHP_AUTH_TYPE"]) ? $aServerVars["PHP_AUTH_TYPE"] : "";
|
|
$s_auth_user = isset($aServerVars["PHP_AUTH_USER"]) ? $aServerVars["PHP_AUTH_USER"] : "";
|
|
$s_auth_pass = isset($aServerVars["PHP_AUTH_PW"]) ? $aServerVars["PHP_AUTH_PW"] : "";
|
|
}
|
|
if (!isset($s_auth_type) || $s_auth_type === "") {
|
|
$s_auth_type = "Basic";
|
|
}
|
|
if ($s_auth_user !== "" || $s_auth_pass !== "") {
|
|
$http_get->SetAuthentication($s_auth_type,$s_auth_user,$s_auth_pass);
|
|
}
|
|
}
|
|
//
|
|
// set the user agent
|
|
//
|
|
$http_get->SetAgent($sUserAgent);
|
|
//
|
|
// resolve the name now so the DNS cache can be written to the session
|
|
//
|
|
$http_get->Resolve();
|
|
|
|
//
|
|
// Since we might be opening a URL within the same session, we can
|
|
// get locks. So, close the session for writing to prevent this.
|
|
//
|
|
$b_closed = false;
|
|
if (function_exists('session_write_close')) {
|
|
session_write_close();
|
|
$b_closed = true;
|
|
//ob_flush(); // this prevents automatic redirects if $TEMPLATEURL
|
|
// is in use and JavaScript is switched off
|
|
}
|
|
|
|
$m_buf = FALSE;
|
|
|
|
//FMDebug("Begin read");
|
|
if (($a_lines = $http_get->Read()) === FALSE) {
|
|
$http_get->Close();
|
|
//
|
|
// get the error code and send the appropriate alert
|
|
//
|
|
list($i_error,$i_sys_err,$s_sys_msg) = $http_get->GetError();
|
|
switch ($i_error) {
|
|
case $http_get->nErrParse:
|
|
$s_error = GetMessage(MSG_URL_PARSE);
|
|
break;
|
|
case $http_get->nErrScheme:
|
|
$a_parts = $http_get->GetURLSplit();
|
|
$s_error = GetMessage(MSG_URL_SCHEME,array("SCHEME" => $a_parts["scheme"]));
|
|
break;
|
|
default:
|
|
$s_error = GetMessage(MSG_SOCKET,
|
|
array("ERRNO" => $i_sys_err,
|
|
"ERRSTR" => $s_sys_msg,
|
|
"PHPERR" => isset($php_errormsg) ? $php_errormsg : ""
|
|
));
|
|
break;
|
|
}
|
|
} else {
|
|
$http_get->Close();
|
|
//
|
|
// check the HTTP response for actual status. Anything outside
|
|
// 200-299 is a failure, but we also handle redirects.
|
|
//
|
|
list($i_http_code,$s_http_status) = $http_get->GetHTTPStatus();
|
|
|
|
if ($i_http_code < 200 || $i_http_code > 299) {
|
|
switch ($i_http_code) {
|
|
case 300: // multiple choices (we'll take the first)
|
|
case 301: // moved permanently
|
|
case 302: // found
|
|
case 303: // see other
|
|
/** @noinspection PhpMissingBreakStatementInspection */
|
|
case 307: // temporary redirect
|
|
//
|
|
// a "location" header must be present for us to continue
|
|
// In the case of infinite redirects, we need to stop.
|
|
// So, we limit to a maximum of 10 redirects.
|
|
//
|
|
if ($n_depth < 10) {
|
|
if (($s_location = $http_get->FindHeader("location")) !== false) {
|
|
FMDebug("Redirect from '$s_url' to '$s_location'");
|
|
$m_buf = GetURL($s_location,$s_error,$b_ret_lines,$n_depth + 1);
|
|
$b_closed = false;
|
|
break;
|
|
}
|
|
FMDebug("Redirect FAILED - no location header");
|
|
} else {
|
|
FMDebug("Redirect FAILED depth=$n_depth");
|
|
}
|
|
// FALL THRU
|
|
default:
|
|
$s_error = GetMessage(MSG_GETURL_OPEN,array("STATUS" => $s_http_status,"URL" => $s_url));
|
|
break;
|
|
}
|
|
} elseif ($b_ret_lines) {
|
|
$m_buf = $a_lines;
|
|
} else
|
|
//
|
|
// return lines as one big string buffer
|
|
//
|
|
{
|
|
$m_buf = implode("",$a_lines);
|
|
}
|
|
}
|
|
//
|
|
// re-open our session
|
|
//
|
|
if ($b_closed) {
|
|
session_start();
|
|
}
|
|
|
|
return ($m_buf);
|
|
}
|
|
|
|
//
|
|
// Write to the debug log if it exists and is writable.
|
|
//
|
|
function FMDebug($s_mesg)
|
|
{
|
|
static $fDebug = NULL;
|
|
|
|
if (!isset($fDebug)) {
|
|
$fDebug = false; // only initialize once
|
|
$s_db_file = "fmdebug.log"; // look for debug file in current directory
|
|
//
|
|
// we only open an existing file - we don't create one
|
|
//
|
|
if (file_exists($s_db_file)) {
|
|
if (($fDebug = fopen($s_db_file,"a")) === false) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if ($fDebug !== false) {
|
|
fwrite($fDebug,date('r') . ": " . $s_mesg . "\n");
|
|
fflush($fDebug);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Class: NetIO
|
|
* Description:
|
|
* A class to provide internet input/output capabilities.
|
|
* Use as a base class for more specific functions.
|
|
*/
|
|
|
|
class NetIO
|
|
{
|
|
var $_sHost;
|
|
var $_iPort;
|
|
var $_sPrefix;
|
|
|
|
var $_iConnTimeout;
|
|
var $_fSock;
|
|
|
|
var $_aIPs;
|
|
|
|
var $_iError = 0;
|
|
var $_iSysErr;
|
|
var $_sSysMesg;
|
|
|
|
var $nErrInit = -1; // not initialized
|
|
var $nErrRead = -2; // read error
|
|
var $nErrWrite = -3; // write error
|
|
var $nErrWriteShort = -4; // failed to write all bytes
|
|
|
|
var $nErrSocket = -100; // error in socket open
|
|
|
|
function __construct($s_host = NULL,$i_port = NULL,$s_prefix = "")
|
|
{
|
|
if (isset($s_host)) {
|
|
$this->_sHost = $s_host;
|
|
}
|
|
if (isset($i_port)) {
|
|
$this->_iPort = $i_port;
|
|
}
|
|
$this->_sPrefix = $s_prefix;
|
|
$this->_iConnTimeout = 30;
|
|
$this->_iSysErr = 0;
|
|
$this->_sSysMesg = "";
|
|
}
|
|
|
|
function _SetError($i_error,$i_sys_err = 0,$s_sys_mesg = "")
|
|
{
|
|
$this->_iError = $i_error;
|
|
$this->_iSysErr = $i_sys_err;
|
|
$this->_sSysMesg = $s_sys_mesg;
|
|
return (FALSE);
|
|
}
|
|
|
|
function IsError()
|
|
{
|
|
return $this->_iError !== 0;
|
|
}
|
|
|
|
function ClearError()
|
|
{
|
|
$this->_SetError(0);
|
|
}
|
|
|
|
function GetError()
|
|
{
|
|
return (array($this->_iError,$this->_iSysErr,$this->_sSysMesg));
|
|
}
|
|
|
|
function SetHost($s_host)
|
|
{
|
|
$this->_sHost = $s_host;
|
|
}
|
|
|
|
function SetPort($i_port)
|
|
{
|
|
$this->_iPort = $i_port;
|
|
}
|
|
|
|
function SetConnectionTimeout($i_secs)
|
|
{
|
|
$this->_iConnTimeout = $i_secs;
|
|
}
|
|
|
|
function SetPrefix($s_prefix)
|
|
{
|
|
$this->_sPrefix = $s_prefix;
|
|
}
|
|
|
|
function GetHost()
|
|
{
|
|
return (isset($this->_sHost) ? $this->_sHost : "");
|
|
}
|
|
|
|
function GetPort()
|
|
{
|
|
return (isset($this->_iPort) ? $this->_iPort : 0);
|
|
}
|
|
|
|
function GetPrefix()
|
|
{
|
|
return ($this->_sPrefix);
|
|
}
|
|
|
|
function GetConnectionTimeout()
|
|
{
|
|
return ($this->_iConnTimeout);
|
|
}
|
|
|
|
function _CacheIt()
|
|
{
|
|
FMDebug("Caching " . implode(",",$this->_aIPs));
|
|
if (IsSetSession("FormNetIODNSCache")) {
|
|
$a_cache = GetSession("FormNetIODNSCache");
|
|
} else {
|
|
$a_cache = array();
|
|
}
|
|
$a_cache[$this->_sHost] = $this->_aIPs;
|
|
SetSession("FormNetIODNSCache",$a_cache);
|
|
}
|
|
|
|
/*
|
|
* Some versions of PHP seem to have a major slowdown when resolving
|
|
* names with gethostbyname (5 seconds with PHP 4.3.9).
|
|
* So, in the case of multi-page forms using MULTIFORMURL, we get a big speed up
|
|
* by caching the IP address of the server.
|
|
*/
|
|
function _CheckCache()
|
|
{
|
|
if (!IsSetSession("FormNetIODNSCache")) {
|
|
return (FALSE);
|
|
}
|
|
$a_cache = GetSession("FormNetIODNSCache");
|
|
if (!is_array($a_cache) || !isset($a_cache[$this->_sHost]) || !is_array($a_cache[$this->_sHost])) {
|
|
return (FALSE);
|
|
}
|
|
$this->_aIPs = $a_cache[$this->_sHost];
|
|
return (TRUE);
|
|
}
|
|
|
|
function Resolve()
|
|
{
|
|
$this->ClearError();
|
|
if (!isset($this->_sHost)) {
|
|
return ($this->_SetError($this->nErrInit));
|
|
}
|
|
if ($this->_CheckCache()) {
|
|
return (TRUE);
|
|
}
|
|
FMDebug("Start resolve of " . $this->_sHost);
|
|
//
|
|
// if host is an actual IP address, then it is returned unchanged, which is good!
|
|
//
|
|
if (($a_ip_list = gethostbynamel($this->_sHost)) === FALSE) {
|
|
FMDebug("Resolve failed");
|
|
return ($this->_SetError($this->nErrInit,0,
|
|
GetMessage(MSG_RESOLVE,array("NAME" => $this->_sHost))));
|
|
}
|
|
FMDebug("Done resolve: " . implode(",",$a_ip_list));
|
|
$this->_aIPs = $a_ip_list;
|
|
$this->_CacheIt();
|
|
return (TRUE);
|
|
}
|
|
|
|
function _SSLOpen($s_ip,&$errno,&$errstr,$i_timeout)
|
|
{
|
|
global $ExecEnv;
|
|
|
|
FMDebug("Using _SSLOpen (stream_socket_client), SNI, host=" . $this->GetHost());
|
|
$context = stream_context_create();
|
|
$result = stream_context_set_option($context,'ssl','verify_host',true);
|
|
$result = stream_context_set_option($context,'ssl','verify_peer',false);
|
|
$result = stream_context_set_option($context,'ssl','allow_self_signed',true);
|
|
$result = stream_context_set_option($context,'ssl','SNI_enabled',true);
|
|
if ($ExecEnv->IsPHPAtLeast("5.6.0")) {
|
|
$result = stream_context_set_option($context,'ssl','peer_name',$this->GetHost());
|
|
} else {
|
|
$result = stream_context_set_option($context,'ssl','SNI_server_name',$this->GetHost());
|
|
}
|
|
//
|
|
// Note that even if SNI fails, the socket will still open, but the
|
|
// web server should send a 400 error.
|
|
//
|
|
return (stream_socket_client($this->GetPrefix() . $s_ip . ":" . $this->GetPort(),
|
|
$errno,$errstr,$i_timeout,STREAM_CLIENT_CONNECT,$context));
|
|
}
|
|
|
|
/** @noinspection PhpUndefinedVariableInspection */
|
|
function Open()
|
|
{
|
|
$this->ClearError();
|
|
if (!isset($this->_sHost) || !isset($this->_iPort)) {
|
|
return ($this->_SetError($this->nErrInit));
|
|
}
|
|
if (!$this->Resolve()) {
|
|
return (FALSE);
|
|
}
|
|
FMDebug("Starting socket open");
|
|
$f_sock = FALSE;
|
|
//
|
|
// Now, run through the list of IPs until we find one that connects.
|
|
// However, this can cause problems with SNI in SSL/TLS connections.
|
|
// If there is only one IP address, use the host name.
|
|
// Otherwise, if we can specify SNI and it's an SSL connection
|
|
// use streams, otherwise try each IP individually.
|
|
//
|
|
if (count($this->_aIPs) == 1) {
|
|
FMDebug("Trying host " . $this->_sHost . ", timeout " . $this->GetConnectionTimeout());
|
|
$f_sock = @fsockopen($this->GetPrefix() . $this->_sHost,$this->GetPort(),
|
|
$errno,$errstr,$this->GetConnectionTimeout());
|
|
} else {
|
|
foreach ($this->_aIPs as $s_ip) {
|
|
global $ExecEnv;
|
|
|
|
FMDebug("Trying IP $s_ip, timeout " . $this->GetConnectionTimeout());
|
|
if ($ExecEnv->IsPHPAtLeast("5.3.2") && substr($this->GetPrefix(),0,3) == "ssl") {
|
|
if (($f_sock = $this->_SSLOpen($s_ip,$errno,$errstr,
|
|
$this->GetConnectionTimeout())) !== FALSE
|
|
) {
|
|
break;
|
|
}
|
|
} elseif (($f_sock = @fsockopen($this->GetPrefix() . $s_ip,$this->GetPort(),
|
|
$errno,$errstr,$this->GetConnectionTimeout())) !== FALSE
|
|
) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if ($f_sock === FALSE) {
|
|
FMDebug("open failed: $errno $errstr");
|
|
return ($this->_SetError($this->nErrSocket,$errno,$errstr));
|
|
}
|
|
$this->_fSock = $f_sock;
|
|
FMDebug("Done socket open");
|
|
return (TRUE);
|
|
}
|
|
|
|
function Read()
|
|
{
|
|
$this->ClearError();
|
|
$a_lines = array();
|
|
while (($s_line = fgets($this->_fSock)) !== FALSE) {
|
|
$a_lines[] = $s_line;
|
|
}
|
|
FMDebug("Read " . count($a_lines) . " lines");
|
|
return ($a_lines);
|
|
}
|
|
|
|
function Write($s_str,$b_flush = TRUE)
|
|
{
|
|
$this->ClearError();
|
|
if (!isset($this->_fSock)) {
|
|
return ($this->_SetError($this->nErrInit));
|
|
}
|
|
if (($n_write = fwrite($this->_fSock,$s_str)) === FALSE) {
|
|
return ($this->_SetError($this->nErrWrite));
|
|
}
|
|
if ($n_write != strlen($s_str)) {
|
|
return ($this->_SetError($this->nErrWriteShort));
|
|
}
|
|
if ($b_flush) {
|
|
if (fflush($this->_fSock) === FALSE) {
|
|
return ($this->_SetError($this->nErrWriteShort));
|
|
}
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
function Close()
|
|
{
|
|
if (isset($this->_fSock)) {
|
|
fclose($this->_fSock);
|
|
unset($this->_fSock);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Class: HTTPGet
|
|
* Description:
|
|
* A class that implements HTTP GET method.
|
|
*/
|
|
|
|
class HTTPGet extends NetIO
|
|
{
|
|
var $_sURL;
|
|
var $_aURLSplit;
|
|
|
|
var $_sRequest;
|
|
var $_aResponse;
|
|
var $_aRespHeaders;
|
|
|
|
var $_sAuthLine;
|
|
var $_sAuthType;
|
|
var $_sAuthUser;
|
|
var $_sAuthPass;
|
|
|
|
var $_sAgent;
|
|
|
|
var $nErrParse = -1000; // failed to parse URL
|
|
var $nErrScheme = -1001; // unsupported URL scheme
|
|
|
|
function __construct($s_url = "")
|
|
{
|
|
parent::__construct();
|
|
$this->_aURLSplit = array();
|
|
if (($this->_sURL = $s_url) !== "") {
|
|
$this->_SplitURL();
|
|
}
|
|
}
|
|
|
|
function _SplitURL()
|
|
{
|
|
FMDebug("URL: " . $this->_sURL);
|
|
if (($this->_aURLSplit = parse_url($this->_sURL)) === FALSE) {
|
|
$this->_aURLSplit = array();
|
|
return ($this->_SetError($this->nErrParse));
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
function GetURLSplit()
|
|
{
|
|
return ($this->_aURLSplit);
|
|
}
|
|
|
|
function SetURL($s_url)
|
|
{
|
|
$this->_aURLSplit = array();
|
|
$this->_sURL = $s_url;
|
|
return ($this->_SplitURL());
|
|
}
|
|
|
|
function _Init()
|
|
{
|
|
if (!isset($this->_aURLSplit["host"])) {
|
|
return ($this->_SetError($this->nErrInit));
|
|
}
|
|
$this->SetHost($this->_aURLSplit["host"]);
|
|
$i_port = 80;
|
|
$b_use_ssl = false;
|
|
if (isset($this->_aURLSplit["scheme"])) {
|
|
switch (strtolower($this->_aURLSplit["scheme"])) {
|
|
case "http":
|
|
break;
|
|
case "https":
|
|
$b_use_ssl = true;
|
|
$i_port = 443;
|
|
break;
|
|
default:
|
|
return ($this->_SetError($this->nErrScheme));
|
|
}
|
|
}
|
|
if (isset($this->_aURLSplit["port"])) {
|
|
$i_port = $this->_aURLSplit["port"];
|
|
}
|
|
if ($b_use_ssl)
|
|
//
|
|
// we require ssl:// for port 443
|
|
//
|
|
{
|
|
$this->SetPrefix("ssl://");
|
|
}
|
|
$this->SetPort($i_port);
|
|
return (TRUE);
|
|
}
|
|
|
|
function _SendRequest()
|
|
{
|
|
$this->_PrepareRequest();
|
|
return (parent::Write($this->_sRequest));
|
|
}
|
|
|
|
function _PrepareRequest($s_method = 'GET')
|
|
{
|
|
FMDebug("Path: " . $this->_aURLSplit["path"]);
|
|
if (!isset($this->_aURLSplit["path"]) || $this->_aURLSplit["path"] === "") {
|
|
$s_path = "/";
|
|
} // default path
|
|
else {
|
|
$s_path = $this->_aURLSplit["path"];
|
|
}
|
|
if (isset($this->_aURLSplit["query"])) {
|
|
//
|
|
// add the query to the path
|
|
// Note that parse_url decodes the query string (urldecode), so
|
|
// we need to split it into its component parameters
|
|
// are re-encode their values. Calling urlencode($this->_aURLSplit["query"])
|
|
// encodes the '=' between parameters and this breaks things.
|
|
//
|
|
$a_params = explode('&',$this->_aURLSplit["query"]);
|
|
foreach ($a_params as $i_idx => $s_param) {
|
|
if (($i_pos = strpos($s_param,"=")) === false) {
|
|
$a_params[$i_idx] = urlencode($s_param);
|
|
} else {
|
|
$a_params[$i_idx] = substr($s_param,0,$i_pos) . '=' .
|
|
urlencode(substr($s_param,$i_pos + 1));
|
|
}
|
|
}
|
|
$s_path .= "?" . implode('&',$a_params);
|
|
}
|
|
//
|
|
// add the fragment to the path.
|
|
//
|
|
if (isset($this->_aURLSplit["fragment"])) {
|
|
$s_path .= '#' . urlencode($this->_aURLSplit["fragment"]);
|
|
}
|
|
//
|
|
// build the request
|
|
//
|
|
$s_req = $s_method . " $s_path HTTP/1.0\r\n";
|
|
//
|
|
// Add authentication
|
|
//
|
|
if (isset($this->_sAuthLine)) {
|
|
$s_req .= "Authorization: $this->_sAuthLine\r\n";
|
|
} elseif (isset($this->_sAuthType)) {
|
|
$s_req .= "Authorization: " . $this->_sAuthType . " " .
|
|
base64_encode($this->_sAuthUser . ":" . $this->_sAuthPass) . "\r\n";
|
|
}
|
|
//
|
|
// Specify the host name
|
|
//
|
|
$s_req .= "Host: " . $this->GetHost() . "\r\n";
|
|
//
|
|
// Specify the user agent
|
|
//
|
|
if (isset($this->_sAgent)) {
|
|
$s_req .= "User-Agent: " . $this->_sAgent . "\r\n";
|
|
}
|
|
//
|
|
// Accept any output
|
|
// use of concatenation to avoid problems with IDE syntax highlighting.
|
|
//
|
|
$s_req .= "Accept: */" . "*\r\n";
|
|
$s_req .= $this->_AdditionalHeaders();
|
|
//
|
|
// End of request headers
|
|
//
|
|
$s_req .= "\r\n";
|
|
$this->_sRequest = $s_req;
|
|
}
|
|
|
|
function _AdditionalHeaders()
|
|
{
|
|
return ('');
|
|
}
|
|
|
|
function _GetResponse()
|
|
{
|
|
FMDebug("Reading");
|
|
if (($a_lines = parent::Read()) === FALSE) {
|
|
return (FALSE);
|
|
}
|
|
|
|
$this->_aRespHeaders = $this->_aResponse = array();
|
|
$b_body = FALSE;
|
|
for ($ii = 0 ; $ii < count($a_lines) ; $ii++) {
|
|
if ($b_body) {
|
|
//FMDebug("Body line: ".rtrim($a_lines[$ii]));
|
|
$this->_aResponse[] = $a_lines[$ii];
|
|
} elseif ($a_lines[$ii] == "\r\n" || $a_lines[$ii] == "\n") {
|
|
$b_body = TRUE;
|
|
} else {
|
|
//FMDebug("Header line: ".rtrim($a_lines[$ii]));
|
|
$this->_aRespHeaders[] = $a_lines[$ii];
|
|
}
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
function GetResponseHeaders()
|
|
{
|
|
return ($this->_aRespHeaders);
|
|
}
|
|
|
|
function FindHeader($s_name)
|
|
{
|
|
$s_name = strtolower($s_name);
|
|
$i_len = strlen($s_name);
|
|
for ($ii = 0 ; $ii < count($this->_aRespHeaders) ; $ii++) {
|
|
$s_line = $this->_aRespHeaders[$ii];
|
|
if (($s_hdr = substr($s_line,0,$i_len)) !== false) {
|
|
$s_hdr = strtolower($s_hdr);
|
|
if ($s_hdr === $s_name && substr($s_line,$i_len,1) === ":") {
|
|
return (trim(substr($s_line,$i_len + 1)));
|
|
}
|
|
}
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
function GetHTTPStatus()
|
|
{
|
|
$i_http_code = 0;
|
|
$s_status = "";
|
|
for ($ii = 0 ; $ii < count($this->_aRespHeaders) ; $ii++) {
|
|
$s_line = $this->_aRespHeaders[$ii];
|
|
if (substr($s_line,0,4) == "HTTP") {
|
|
$i_pos = strpos($s_line," ");
|
|
$s_status = substr($s_line,$i_pos + 1);
|
|
$i_end_pos = strpos($s_status," ");
|
|
if ($i_end_pos === false) {
|
|
$i_end_pos = strlen($s_status);
|
|
}
|
|
$i_http_code = (int)substr($s_status,0,$i_end_pos);
|
|
}
|
|
}
|
|
return (array($i_http_code,$s_status));
|
|
}
|
|
|
|
function Resolve()
|
|
{
|
|
if (!$this->_Init()) {
|
|
return (FALSE);
|
|
}
|
|
return (parent::Resolve());
|
|
}
|
|
|
|
function Read()
|
|
{
|
|
if (!$this->_Init()) {
|
|
return (FALSE);
|
|
}
|
|
FMDebug("Init done");
|
|
if (!$this->Open()) {
|
|
return (FALSE);
|
|
}
|
|
FMDebug("Open done");
|
|
if (!$this->_SendRequest()) {
|
|
return (FALSE);
|
|
}
|
|
FMDebug("Send done");
|
|
if (!$this->_GetResponse()) {
|
|
return (FALSE);
|
|
}
|
|
FMDebug("Get done");
|
|
$this->Close();
|
|
return ($this->_aResponse);
|
|
}
|
|
|
|
function SetAuthenticationLine($s_auth)
|
|
{
|
|
$this->_sAuthLine = $s_auth;
|
|
}
|
|
|
|
function SetAuthentication($s_type,$s_user,$s_pass)
|
|
{
|
|
$this->_sAuthType = $s_type;
|
|
$this->_sAuthUser = $s_user;
|
|
$this->_sAuthPass = $s_pass;
|
|
}
|
|
|
|
function SetAgent($s_agent)
|
|
{
|
|
$this->_sAgent = $s_agent;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Class: HTTPPost
|
|
* Description:
|
|
* A class that implements HTTP POST method.
|
|
*/
|
|
|
|
class HTTPPost extends HTTPGet
|
|
{
|
|
var $_sPostData; /* data to POST */
|
|
|
|
function __construct($s_url = "")
|
|
{
|
|
$this->_sPostData = '';
|
|
parent::__construct($s_url);
|
|
}
|
|
|
|
function _SendRequest()
|
|
{
|
|
$this->_PrepareRequest();
|
|
return (NetIO::Write($this->_sRequest));
|
|
}
|
|
|
|
function _PrepareRequest($s_method = 'POST')
|
|
{
|
|
parent::_PrepareRequest($s_method);
|
|
$this->_AddData();
|
|
}
|
|
|
|
function _AdditionalHeaders()
|
|
{
|
|
//
|
|
// we don't handle file uploads yet
|
|
//
|
|
$a_hdrs = array(
|
|
'Content-Type: application/x-www-form-urlencoded',
|
|
'Content-Length: ' . strlen($this->_sPostData),
|
|
);
|
|
return (implode("\r\n",$a_hdrs));
|
|
}
|
|
|
|
function _AddData()
|
|
{
|
|
$this->_sRequest .= "\r\n"; // blank line after headers
|
|
$this->_sRequest .= $this->_sPostData;
|
|
}
|
|
|
|
function _EncodeData($a_fields)
|
|
{
|
|
$s_data = '';
|
|
foreach ($a_fields as $s_name => $s_value) {
|
|
if ($s_data != '') {
|
|
$s_data .= '&';
|
|
}
|
|
if (is_string($s_value)) {
|
|
$s_data .= urlencode($s_name) . '=' . urlencode($s_value);
|
|
} else {
|
|
$s_data .= urlencode($s_name) . '=' . urlencode(serialize($s_value));
|
|
}
|
|
}
|
|
return ($s_data);
|
|
}
|
|
|
|
function Post($a_fields)
|
|
{
|
|
//
|
|
// we don't handle file uploads yet
|
|
//
|
|
$this->_sPostData = $this->_EncodeData($a_fields);
|
|
return ($this->Read());
|
|
}
|
|
}
|
|
|
|
//
|
|
// Load a template file into a string.
|
|
//
|
|
function LoadTemplate($s_name,$s_dir,$s_url,$b_ret_lines = false)
|
|
{
|
|
global $php_errormsg;
|
|
|
|
$s_buf = "";
|
|
$a_lines = array();
|
|
if (!empty($s_dir)) {
|
|
$s_name = "$s_dir/" . basename($s_name);
|
|
@ $fp = fopen($s_name,"r");
|
|
if ($fp === false) {
|
|
SendAlert(GetMessage(MSG_OPEN_TEMPLATE,array("NAME" => $s_name,
|
|
"ERROR" => CheckString($php_errormsg)
|
|
)));
|
|
return (false);
|
|
}
|
|
if ($b_ret_lines) {
|
|
$a_lines = ReadLines($fp);
|
|
} else
|
|
//
|
|
// load the whole template into a string
|
|
//
|
|
{
|
|
$s_buf = fread($fp,filesize($s_name));
|
|
}
|
|
fclose($fp);
|
|
} else {
|
|
if (substr($s_url,-1) == '/') {
|
|
$s_name = "$s_url" . basename($s_name);
|
|
} else {
|
|
$s_name = "$s_url/" . basename($s_name);
|
|
}
|
|
if (($m_data = GetURL($s_name,$s_error,$b_ret_lines)) === false) {
|
|
SendAlert($s_error);
|
|
return (false);
|
|
}
|
|
if ($b_ret_lines) {
|
|
$a_lines = $m_data;
|
|
//
|
|
// strip line terminations from each line
|
|
//
|
|
for ($ii = count($a_lines) ; --$ii >= 0 ;) {
|
|
$s_line = $a_lines[$ii];
|
|
$s_line = str_replace("\r","",$s_line);
|
|
$s_line = str_replace("\n","",$s_line);
|
|
$a_lines[$ii] = $s_line;
|
|
}
|
|
} else {
|
|
$s_buf = $m_data;
|
|
}
|
|
}
|
|
return ($b_ret_lines ? $a_lines : $s_buf);
|
|
}
|
|
|
|
//
|
|
// To show an error template. The template must be HTML and, for security
|
|
// reasons, must be a file on the server in the directory specified
|
|
// by $TEMPLATEDIR or $TEMPLATEURL.
|
|
// $a_specs is an array of substitutions to perform, as follows:
|
|
// tag-name replacement string
|
|
//
|
|
// For example:
|
|
// "fmerror"=>"An error message"
|
|
//
|
|
function ShowErrorTemplate($s_name,$a_specs,$b_user_error)
|
|
{
|
|
if (function_exists('FMHookShowErrorTemplate')) {
|
|
if (FMHookShowErrorTemplate($s_name,$a_specs,$b_user_error)) {
|
|
return (true);
|
|
}
|
|
}
|
|
if (Settings::isEmpty('TEMPLATEDIR') && Settings::isEmpty('TEMPLATEURL')) {
|
|
SendAlert(GetMessage(MSG_TEMPLATES));
|
|
return (false);
|
|
}
|
|
if (($s_buf = LoadTemplate($s_name,Settings::get('TEMPLATEDIR'),Settings::get('TEMPLATEURL'))) === false) {
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// now look for the tags to replace
|
|
//
|
|
foreach ($a_specs as $s_tag => $s_value)
|
|
//
|
|
// search for
|
|
// <tagname/>
|
|
// or
|
|
// [tagname/]
|
|
// with optional whitespace
|
|
//
|
|
{
|
|
$s_buf = preg_replace('/[<\[]\s*' . preg_quote($s_tag,"/") . '\s*\/\s*[>\]]/ims',
|
|
nl2br($s_value),$s_buf);
|
|
}
|
|
if ($b_user_error) {
|
|
// strip any <fmusererror> and </fmusererror> tags
|
|
// or [fmusererrors] and [/fmusererror] tags
|
|
//
|
|
// You can show information that's specific to user
|
|
// errors between these special tags.
|
|
//
|
|
$s_buf = preg_replace('/[<\[]\s*\/?\s*fmusererror\s*[>\]]/ims','',$s_buf);
|
|
//
|
|
// since this isn't a system error, strip anything between
|
|
// <fmsyserror> and </fmsyserror>
|
|
// or [fmsyserrors] and [/fmsyserror] tags
|
|
//
|
|
$s_buf = preg_replace('/[<\[]\s*fmsyserror\s*[>\]].*[<\[]\s*\/\s*fmsyserror\s*[>\]]/ims','',$s_buf);
|
|
} else {
|
|
// strip any <fmsyserror> and </fmsyserror> tags
|
|
// or [fmsyserrors] and [/fmsyserror] tags
|
|
//
|
|
// You can show information that's specific to system
|
|
// errors between these special tags.
|
|
//
|
|
$s_buf = preg_replace('/[<\[]\s*\/?\s*fmsyserror\s*[>\]]/ims','',$s_buf);
|
|
//
|
|
// since this isn't a user error, strip anything between
|
|
// <fmusererror> and </fmusererror>
|
|
// or [fmusererrors] and [/fmusererror] tags
|
|
//
|
|
$s_buf = preg_replace('/[<\[]\s*fmusererror\s*[>\]].*[<\[]\s*\/\s*fmusererror\s*[>\]]/ims','',$s_buf);
|
|
}
|
|
//
|
|
// just output the page
|
|
//
|
|
echo $s_buf;
|
|
return (true);
|
|
}
|
|
|
|
//
|
|
// To show an error to the user.
|
|
//
|
|
function ShowError($error_code,$error_mesg,$b_user_error,
|
|
$b_alerted = false,$a_item_list = array(),$s_extra_info = "")
|
|
{
|
|
global $SPECIAL_FIELDS,$SPECIAL_MULTI,$SPECIAL_VALUES;
|
|
global $aServerVars,$aStrippedFormVars;
|
|
|
|
//
|
|
// The following initialisations are so that ShowError can run from fmhookpreinit scripts
|
|
//
|
|
$aSpecialFieldValues = isset($SPECIAL_FIELDS) ? $SPECIAL_FIELDS : array();
|
|
$aSpecialValues = isset($SPECIAL_VALUES) ? $SPECIAL_VALUES : array();
|
|
$aSpecialMultiValues = isset($SPECIAL_MULTI) ? $SPECIAL_MULTI : array();
|
|
$aServerVarValues = isset($aServerVars) ? $aServerVars : array();
|
|
$aStrippedFormVarValues = isset($aStrippedFormVars) ? $aStrippedFormVars : array();
|
|
|
|
//
|
|
// Testing with PHP 4.0.6 indicates that sessions don't always work.
|
|
// So, we'll also add the error to the URL, unless
|
|
// PUT_DATA_IN_URL is false.
|
|
//
|
|
SetSession("FormError",$error_mesg);
|
|
SetSession("FormErrorInfo",$s_extra_info);
|
|
SetSession("FormErrorCode",$error_code);
|
|
SetSession("FormErrorItems",$a_item_list);
|
|
SetSession("FormIsUserError",$b_user_error);
|
|
SetSession("FormAlerted",$b_alerted);
|
|
SetSession("FormData",array());
|
|
|
|
$bad_url = isset($aSpecialValues["bad_url"]) ? $aSpecialValues["bad_url"] : '';
|
|
$bad_template = isset($aSpecialValues["bad_template"]) ? $aSpecialValues["bad_template"] : '';
|
|
$this_form = isset($aSpecialValues["this_form"]) ? $aSpecialValues["this_form"] : '';
|
|
if (IsAjax()) {
|
|
JSON_Result("ERROR",array("ErrorCode" => $error_code,
|
|
"UserError" => $b_user_error,
|
|
"ErrorMesg" => $error_mesg,
|
|
"Alerted" => $b_alerted,
|
|
"ErrorItems" => $a_item_list
|
|
));
|
|
ZapSession();
|
|
} elseif (!empty($bad_url)) {
|
|
$a_params = array();
|
|
if (Settings::get('PUT_DATA_IN_URL')) {
|
|
$a_params[] = "this_form=" . urlencode("$this_form");
|
|
$a_params[] = "bad_template=" . urlencode("$bad_template");
|
|
$a_params[] = "error=" . urlencode("$error_mesg");
|
|
$a_params[] = "extra=" . urlencode("$s_extra_info");
|
|
$a_params[] = "errcode=" . urlencode("$error_code");
|
|
$a_params[] = "isusererror=" . ($b_user_error ? "1" : "0");
|
|
$a_params[] = "alerted=" . ($b_alerted ? "1" : "0");
|
|
$i_count = 1;
|
|
foreach ($a_item_list as $s_item) {
|
|
$a_params[] = "erroritem$i_count=" . urlencode("$s_item");
|
|
$i_count++;
|
|
}
|
|
} else {
|
|
$a_sess_data = GetSession("FormData");
|
|
$a_sess_data["this_form"] = "$this_form";
|
|
$a_sess_data["bad_template"] = "$bad_template";
|
|
SetSession("FormData",$a_sess_data);
|
|
//
|
|
// tell the bad_url to look in the session only
|
|
//
|
|
$a_params[] = "insession=1";
|
|
}
|
|
//
|
|
// Add the posted data to the URL so that an intelligent
|
|
// $bad_url can call the form again
|
|
//
|
|
foreach ($aStrippedFormVarValues as $s_name => $m_value) {
|
|
//
|
|
// skip special fields
|
|
//
|
|
$b_special = false;
|
|
if (in_array($s_name,$aSpecialFieldValues)) {
|
|
$b_special = true;
|
|
} else {
|
|
foreach ($aSpecialMultiValues as $s_multi_fld) {
|
|
$i_len = strlen($s_multi_fld);
|
|
if (substr($s_name,0,$i_len) == $s_multi_fld) {
|
|
$i_index = (int)substr($s_name,$i_len);
|
|
if ($i_index > 0) {
|
|
$b_special = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!$b_special) {
|
|
if (Settings::get('PUT_DATA_IN_URL')) {
|
|
if (is_array($m_value)) {
|
|
foreach ($m_value as $s_value) {
|
|
$a_params[] = "$s_name" . '[]=' .
|
|
urlencode(substr($s_value,0,Settings::get('MAXSTRING')));
|
|
}
|
|
} else {
|
|
$a_params[] = "$s_name=" . urlencode(substr($m_value,0,Settings::get('MAXSTRING')));
|
|
}
|
|
} else {
|
|
$a_sess_data = GetSession("FormData");
|
|
if (is_array($m_value)) {
|
|
$a_sess_data["$s_name"] = $m_value;
|
|
} else {
|
|
$a_sess_data["$s_name"] = substr($m_value,0,Settings::get('MAXSTRING'));
|
|
}
|
|
SetSession("FormData",$a_sess_data);
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Now add the authentication data, if any
|
|
//
|
|
if ((isset($aServerVarValues["PHP_AUTH_USER"]) &&
|
|
$aServerVarValues["PHP_AUTH_USER"] !== "") ||
|
|
(isset($aServerVarValues["PHP_AUTH_PW"]) &&
|
|
$aServerVarValues["PHP_AUTH_PW"] !== "")
|
|
) {
|
|
if (Settings::get('PUT_DATA_IN_URL')) {
|
|
if (isset($aServerVarValues["PHP_AUTH_USER"])) {
|
|
$a_params[] = "PHP_AUTH_USER=" . urlencode($aServerVarValues["PHP_AUTH_USER"]);
|
|
}
|
|
|
|
if (isset($aServerVarValues["PHP_AUTH_PW"])) {
|
|
$a_params[] = "PHP_AUTH_PW=" . urlencode($aServerVarValues["PHP_AUTH_PW"]);
|
|
}
|
|
|
|
if (isset($aServerVarValues["PHP_AUTH_TYPE"])) {
|
|
$a_params[] = "PHP_AUTH_TYPE=" . urlencode($aServerVarValues["PHP_AUTH_TYPE"]);
|
|
}
|
|
} else {
|
|
$a_sess_data = GetSession("FormData");
|
|
if (isset($aServerVarValues["PHP_AUTH_USER"])) {
|
|
$a_sess_data["PHP_AUTH_USER"] = $aServerVarValues["PHP_AUTH_USER"];
|
|
}
|
|
|
|
if (isset($aServerVarValues["PHP_AUTH_PW"])) {
|
|
$a_sess_data["PHP_AUTH_PW"] = $aServerVarValues["PHP_AUTH_PW"];
|
|
}
|
|
|
|
if (isset($aServerVarValues["PHP_AUTH_TYPE"])) {
|
|
$a_sess_data["PHP_AUTH_TYPE"] = $aServerVarValues["PHP_AUTH_TYPE"];
|
|
}
|
|
SetSession("FormData",$a_sess_data);
|
|
}
|
|
}
|
|
$bad_url = AddURLParams($bad_url,$a_params,false);
|
|
Redirect($bad_url,GetMessage(MSG_FORM_ERROR));
|
|
} else {
|
|
if (!empty($bad_template)) {
|
|
$a_specs = array("fmerror" => htmlspecialchars("$error_mesg"),
|
|
"fmerrorcode" => htmlspecialchars("$error_code"),
|
|
"fmfullerror" => htmlspecialchars("$error_mesg") . "\n" .
|
|
htmlspecialchars("$s_extra_info"),
|
|
"fmerrorextra" => htmlspecialchars("$s_extra_info"),
|
|
);
|
|
for ($i_count = 1 ; $i_count <= 20 ; $i_count++) {
|
|
$a_specs["fmerroritem$i_count"] = "";
|
|
}
|
|
$i_count = 1;
|
|
foreach ($a_item_list as $s_item) {
|
|
$a_specs["fmerroritem$i_count"] = htmlspecialchars($s_item);
|
|
$i_count++;
|
|
}
|
|
$s_list = "";
|
|
foreach ($a_item_list as $s_item) {
|
|
$s_list .= "<li>" . htmlspecialchars($s_item) . "</li>";
|
|
}
|
|
$a_specs["fmerroritemlist"] = $s_list;
|
|
if (ShowErrorTemplate($bad_template,$a_specs,$b_user_error)) {
|
|
return;
|
|
}
|
|
}
|
|
$s_text = GetMessage(MSG_ERROR_PROC);
|
|
if ($b_user_error) {
|
|
$s_text .= $error_mesg . "\n" . FixedHTMLEntities($s_extra_info);
|
|
} else {
|
|
global $SERVER;
|
|
|
|
if ($b_alerted) {
|
|
$s_text .= GetMessage(MSG_ALERT_DONE,array("SERVER" => $SERVER));
|
|
} else {
|
|
$s_text .= GetMessage(MSG_PLS_CONTACT,array("SERVER" => $SERVER));
|
|
}
|
|
$s_text .= GetMessage(MSG_APOLOGY,array("SERVER" => $SERVER));
|
|
}
|
|
CreatePage($s_text,GetMessage(MSG_FORM_ERROR),false);
|
|
//
|
|
// the session data is not needed now
|
|
//
|
|
ZapSession();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Report an error. Same as Error, but implements
|
|
// ATTACK_DETECTION_IGNORE_ERRORS.
|
|
//
|
|
function ErrorWithIgnore($error_code,$error_mesg,$b_filter = true,$show = true,$int_mesg = "")
|
|
{
|
|
if (function_exists('FMHookErrorWithIgnore')) {
|
|
FMHookErrorWithIgnore($error_code,$error_mesg,$b_filter,$show,$int_mesg);
|
|
}
|
|
|
|
$b_alerted = false;
|
|
if (!Settings::get('ATTACK_DETECTION_IGNORE_ERRORS')) {
|
|
if (SendAlert("$error_code\n *****$int_mesg*****\nError=$error_mesg\n",$b_filter)) {
|
|
$b_alerted = true;
|
|
}
|
|
}
|
|
if ($show) {
|
|
ShowError($error_code,$error_mesg,false,$b_alerted);
|
|
} else
|
|
//
|
|
// show something to the user
|
|
//
|
|
{
|
|
ShowError($error_code,GetMessage(MSG_SUBM_FAILED),false,$b_alerted);
|
|
}
|
|
FormMailExit();
|
|
}
|
|
|
|
//
|
|
// Report an error
|
|
//
|
|
function Error($error_code,$error_mesg,$b_filter = true,$show = true,$int_mesg = "")
|
|
{
|
|
if (function_exists('FMHookError')) {
|
|
FMHookError($error_code,$error_mesg,$b_filter,$show,$int_mesg);
|
|
}
|
|
|
|
$b_alerted = false;
|
|
if (SendAlert("$error_code\n *****$int_mesg*****\nError=$error_mesg\n",$b_filter)) {
|
|
$b_alerted = true;
|
|
}
|
|
if ($show) {
|
|
ShowError($error_code,$error_mesg,false,$b_alerted);
|
|
} else
|
|
//
|
|
// show something to the user
|
|
//
|
|
{
|
|
ShowError($error_code,GetMessage(MSG_SUBM_FAILED),false,$b_alerted);
|
|
}
|
|
FormMailExit();
|
|
}
|
|
|
|
//
|
|
// Report a user error
|
|
//
|
|
function UserError($s_error_code,$s_error_mesg,
|
|
$s_extra_info = "",$a_item_list = array())
|
|
{
|
|
if (function_exists('FMHookUserError')) {
|
|
FMHookUserError($s_error_code,$s_error_mesg,$s_extra_info,$a_item_list);
|
|
}
|
|
$b_alerted = false;
|
|
if (Settings::get('ALERT_ON_USER_ERROR') ||
|
|
($s_error_code === "invalid_email" && Settings::get('ALERT_ON_EMAIL_VALIDATION_FAILED'))) {
|
|
$s_addl_subject = $s_error_code === "invalid_email" ? 'Invalid Email Domain' : '';
|
|
if (SendAlert("$s_error_code\nError=$s_error_mesg\n$s_extra_info\n",true,false,$s_addl_subject)) {
|
|
$b_alerted = true;
|
|
}
|
|
}
|
|
ShowError($s_error_code,$s_error_mesg,true,$b_alerted,$a_item_list,$s_extra_info);
|
|
FormMailExit();
|
|
}
|
|
|
|
//
|
|
// Create a simple page with the given text.
|
|
//
|
|
function CreatePage($text,$title = "",$b_show_about = true)
|
|
{
|
|
global $FM_VERS,$sHTMLCharSet;
|
|
|
|
if (IsAjax()) {
|
|
//
|
|
// CreatePage should not be called in Ajax mode.
|
|
// If it is, it must be an error or debugging state.
|
|
//
|
|
JSON_Result("ERROR",array("ErrorCode" => $title,
|
|
"ErrorMesg" => $text
|
|
));
|
|
} else {
|
|
echo
|
|
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' .
|
|
"\n";
|
|
echo '<html xmlns="http://www.w3.org/1999/xhtml">' . "\n";
|
|
echo "<head>\n";
|
|
if (isset($sHTMLCharSet) && $sHTMLCharSet !== "") {
|
|
echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=$sHTMLCharSet\" />\n";
|
|
}
|
|
if ($title != "") {
|
|
echo "<title>" . FixedHTMLEntities($title) . "</title>\n";
|
|
}
|
|
echo "</head>\n";
|
|
echo "<body>\n";
|
|
echo nl2br($text);
|
|
echo "<p />";
|
|
if ($b_show_about) {
|
|
echo "<p><small>\n";
|
|
echo GetMessage(MSG_ABOUT_FORMMAIL,array("FM_VERS" => $FM_VERS,
|
|
"TECTITE" => "www.tectite.com"
|
|
));
|
|
echo "</small></p>\n";
|
|
}
|
|
echo "</body>\n";
|
|
echo "</html>\n";
|
|
}
|
|
}
|
|
|
|
function GetMagicQuotesGPC()
|
|
{
|
|
global $ExecEnv;
|
|
$b_result = false;
|
|
|
|
// the magic_quotes_gpc setting was removed in PHP 5.4.0
|
|
// get_magic_quotes_gpc was deprecated in PHP 7.4.0 and removed in PHP 8.0.0
|
|
if (function_exists('get_magic_quotes_gpc')) {
|
|
if (!$ExecEnv->IsPHPAtLeast("5.4.0")) {
|
|
$b_result = (get_magic_quotes_gpc() != 0);
|
|
}
|
|
}
|
|
return $b_result;
|
|
}
|
|
|
|
//
|
|
// Strip slashes if magic_quotes_gpc is set.
|
|
//
|
|
function StripGPC($s_value)
|
|
{
|
|
global $ExecEnv;
|
|
|
|
if (GetMagicQuotesGPC()) {
|
|
$s_value = stripslashes($s_value);
|
|
}
|
|
return ($s_value);
|
|
}
|
|
|
|
//
|
|
// return an array, stripped of slashes if magic_quotes_gpc is set
|
|
//
|
|
function StripGPCArray($a_values)
|
|
{
|
|
global $ExecEnv;
|
|
|
|
if (GetMagicQuotesGPC()) {
|
|
foreach ($a_values as $m_key => $m_value) {
|
|
if (is_array($m_value))
|
|
//
|
|
// strip arrays recursively
|
|
//
|
|
{
|
|
$a_values[$m_key] = StripGPCArray($m_value);
|
|
} else
|
|
//
|
|
// convert scalar to string and strip
|
|
//
|
|
{
|
|
$a_values[$m_key] = stripslashes("$m_value");
|
|
}
|
|
}
|
|
}
|
|
|
|
return ($a_values);
|
|
}
|
|
|
|
//
|
|
// Strip a value of unwanted characters, which might be hacks.
|
|
// The stripping of \r and \n is a *critical* security feature.
|
|
//
|
|
function Strip($value)
|
|
{
|
|
//
|
|
// When working with character sets such as UTF-8, stripping
|
|
// control characters is a *really bad idea* and breaks things.
|
|
// From version 8.22, FormMail only strips \r and \n as these
|
|
// are really the only characters that can cause header hacks
|
|
// to be inserted. (Strip means replace with a single space).
|
|
// We also handle multiple spaces.
|
|
//
|
|
$value = preg_replace('/[ \r\n]+/'," ",$value); // zap all CRLF and multiple blanks
|
|
return ($value);
|
|
}
|
|
|
|
//
|
|
// Clean a value. This means:
|
|
// 1. convert to string
|
|
// 2. truncate to maximum length
|
|
// 3. strip the value of unwanted or dangerous characters (hacks)
|
|
// 4. trim both ends of whitespace
|
|
// Each element of an array is cleaned as above. This process occurs
|
|
// recursively, so arrays of arrays work OK too (though there's no
|
|
// need for that in this program).
|
|
//
|
|
// Non-scalar values are changed to the string "<X>" where X is the type.
|
|
// In general, FormMail won't receive non-scalar non-array values, so this
|
|
// is more a future-proofing measure.
|
|
//
|
|
function CleanValue($m_value)
|
|
{
|
|
if (is_array($m_value)) {
|
|
foreach ($m_value as $m_key => $m_item) {
|
|
$m_value[$m_key] = CleanValue($m_item);
|
|
}
|
|
} elseif (!is_scalar($m_value)) {
|
|
$m_value = "<" . gettype($m_value) . ">";
|
|
} else {
|
|
//
|
|
// convert to string and truncate
|
|
//
|
|
$m_value = substr("$m_value",0,Settings::get('MAXSTRING'));
|
|
//
|
|
// strip unwanted chars and trim
|
|
//
|
|
$m_value = trim(Strip($m_value));
|
|
}
|
|
return ($m_value);
|
|
}
|
|
|
|
//
|
|
// Clean a special value. Special values listed in $SPECIAL_NOSTRIP
|
|
// will not be cleaned.
|
|
//
|
|
function SpecialCleanValue($s_name,$m_value)
|
|
{
|
|
global $SPECIAL_NOSTRIP;
|
|
|
|
if (!in_array($s_name,$SPECIAL_NOSTRIP)) {
|
|
$m_value = CleanValue($m_value);
|
|
}
|
|
return ($m_value);
|
|
}
|
|
|
|
//
|
|
// Return the fields and their values in a string containing one
|
|
// field per line.
|
|
//
|
|
function MakeFieldOutput($a_order,$a_fields,$s_line_feed = null)
|
|
{
|
|
if ($s_line_feed === null) {
|
|
$s_line_feed = Settings::get('BODY_LF');
|
|
}
|
|
$n_order = count($a_order);
|
|
$s_output = "";
|
|
for ($ii = 0 ; $ii < $n_order ; $ii++) {
|
|
$s_name = $a_order[$ii];
|
|
if (isset($a_fields[$s_name])) {
|
|
$s_output .= "$s_name: " . $a_fields[$s_name] . $s_line_feed;
|
|
}
|
|
}
|
|
return ($s_output);
|
|
}
|
|
|
|
//
|
|
// Check if a field name is special. Returns true if it is.
|
|
//
|
|
function IsSpecialField($s_name)
|
|
{
|
|
global $SPECIAL_FIELDS;
|
|
|
|
return (in_array($s_name,$SPECIAL_FIELDS));
|
|
}
|
|
|
|
//
|
|
// Set a special field value.
|
|
//
|
|
function SetSpecialField($s_name,$m_value)
|
|
{
|
|
global $SPECIAL_VALUES;
|
|
|
|
//
|
|
// most special values cannot be arrays; ignore them if they are
|
|
//
|
|
if (is_array($m_value)) {
|
|
global $SPECIAL_ARRAYS;
|
|
|
|
if (!in_array($s_name,$SPECIAL_ARRAYS)) {
|
|
return;
|
|
}
|
|
}
|
|
$SPECIAL_VALUES[$s_name] = SpecialCleanValue($s_name,$m_value);
|
|
}
|
|
|
|
//
|
|
// Check if a field name is a special "multi" field.
|
|
// A multi field is the name plus a sequence number. For example,
|
|
// conditions1
|
|
// conditions2
|
|
// Returns a list (array) if it is, otherwise false if not.
|
|
// The list contains:
|
|
// 1. the name of the special multi field
|
|
// 2. the index number for multi field
|
|
//
|
|
function IsSpecialMultiField($s_name)
|
|
{
|
|
global $SPECIAL_MULTI;
|
|
|
|
foreach ($SPECIAL_MULTI as $s_multi_fld) {
|
|
$i_len = strlen($s_multi_fld);
|
|
//
|
|
// look for nameN where N is a number starting from 1
|
|
//
|
|
if (substr($s_name,0,$i_len) == $s_multi_fld) {
|
|
$i_index = (int)substr($s_name,$i_len);
|
|
if ($i_index > 0) {
|
|
//
|
|
// re-index to zero
|
|
//
|
|
--$i_index;
|
|
return (array($s_multi_fld,$i_index));
|
|
}
|
|
}
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// Set a multi field value.
|
|
//
|
|
function SetSpecialMultiField($s_name,$i_index,$m_value)
|
|
{
|
|
global $SPECIAL_VALUES;
|
|
|
|
//
|
|
// these special fields cannot be arrays - ignore if it is
|
|
//
|
|
if (!is_array($m_value)) {
|
|
//
|
|
// Convert the special field itself into an array so it can be correctly
|
|
// indexed.
|
|
//
|
|
if (!is_array($SPECIAL_VALUES[$s_name])) {
|
|
$SPECIAL_VALUES[$s_name] = array();
|
|
}
|
|
$SPECIAL_VALUES[$s_name][$i_index] = SpecialCleanValue($s_name,$m_value);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if a field is part of Reverse Captcha processing.
|
|
//
|
|
function IsReverseCaptchaField($s_name)
|
|
{
|
|
$a_rev_captcha = Settings::get('ATTACK_DETECTION_REVERSE_CAPTCHA');
|
|
return (isset($a_rev_captcha[$s_name]));
|
|
}
|
|
|
|
/**
|
|
* Validate an email address.
|
|
* Generate a UserError if $b_user_error is true.
|
|
*
|
|
* @param $s_name
|
|
* @param $s_value
|
|
* @param $b_user_error
|
|
*/
|
|
function ValidateEmailAddress($s_name,$s_value,$b_user_error)
|
|
{
|
|
$b_error_on_invalid = true;
|
|
$b_valid = true;
|
|
$s_reason = '';
|
|
$a_components = explode('@',$s_value,2);
|
|
if (count($a_components) !== 2) {
|
|
$b_valid = false;
|
|
$s_reason = "missing '@'";
|
|
} else {
|
|
if ($a_components[0] === '' || $a_components[1] === '') {
|
|
$b_valid = false;
|
|
$s_reason = "missing name or domain name";
|
|
} else {
|
|
if (Settings::get('VALIDATE_EMAIL_DOMAIN')) {
|
|
$n_max_monitoring_secs = Settings::get('EMAIL_VALIDATION_MAX_MONITORING_TIME_SECS');
|
|
if ($n_max_monitoring_secs > 0) {
|
|
$f_start_time = microtime(true);
|
|
}
|
|
if (!checkdnsrr($a_components[1])) {
|
|
$b_valid = false;
|
|
$s_reason = "invalid domain name '" . $a_components[1] . "'";
|
|
}
|
|
if ($n_max_monitoring_secs > 0) {
|
|
$f_end_time = microtime(true);
|
|
$f_diff = $f_end_time - $f_start_time;
|
|
SetSpecialField("email_validation_time_secs",number_format($f_diff,2));
|
|
if ($f_diff >= $n_max_monitoring_secs) {
|
|
//
|
|
// let the submission go through because the time is >= EMAIL_VALIDATION_MAX_MONITORING_TIME_SECS
|
|
//
|
|
$b_error_on_invalid = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!$b_valid) {
|
|
if ($b_error_on_invalid) {
|
|
$s_error_mesg = GetMessage(MSG_INVALID_EMAIL,
|
|
array("EMAIL" => $s_value,
|
|
"REASON" => $s_reason
|
|
));
|
|
if ($b_user_error) {
|
|
UserError("invalid_email",$s_error_mesg,'',array($s_name => $s_error_mesg));
|
|
} else {
|
|
SendAlert($s_error_mesg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform some basic validation on a special field before setting it.
|
|
* Generate a UserError if $b_user_error is true.
|
|
*
|
|
* @param $s_name
|
|
* @param $s_value
|
|
* @param $b_user_error
|
|
*/
|
|
function ValidateSpecialField($s_name,$s_value,$b_user_error)
|
|
{
|
|
switch ($s_name) {
|
|
case "email":
|
|
if ($s_value !== '') {
|
|
ValidateEmailAddress($s_name,$s_value,$b_user_error);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Process a field
|
|
//
|
|
function ProcessField($s_name,$raw_value,&$a_order,&$a_fields,&$a_raw_fields)
|
|
{
|
|
global $FORMATTED_INPUT;
|
|
|
|
//
|
|
// fields go into an array of special values or an array of other values
|
|
// or get completely ignored.
|
|
//
|
|
$b_ignore = $b_special = false;
|
|
if (IsSpecialField($s_name)) {
|
|
ValidateSpecialField($s_name,$raw_value,true);
|
|
SetSpecialField($s_name,$raw_value);
|
|
$b_special = true;
|
|
}
|
|
//
|
|
// check for multiple valued special fields
|
|
//
|
|
if (($a_multi_fld = IsSpecialMultiField($s_name)) !== false) {
|
|
SetSpecialMultiField($a_multi_fld[0],$a_multi_fld[1],$raw_value);
|
|
$b_special = true;
|
|
}
|
|
if (!$b_special) {
|
|
if (IsReverseCaptchaField($s_name)) {
|
|
$b_ignore = true;
|
|
}
|
|
}
|
|
if (!$b_special && !$b_ignore) {
|
|
//
|
|
// return the raw value unchanged in the $a_raw_fields array
|
|
//
|
|
$a_raw_fields[$s_name] = $raw_value;
|
|
//
|
|
// handle checkboxes and multiple-selection lists
|
|
// Thanks go to Theodore Boardman for the suggestion
|
|
// and initial working code.
|
|
//
|
|
if (is_array($raw_value)) {
|
|
if (empty($raw_value)) {
|
|
$s_cleaned_value = "";
|
|
} else {
|
|
$a_cleaned_values = CleanValue($raw_value);
|
|
//
|
|
// the output is a comma separated list of values for the
|
|
// checkbox. For example,
|
|
// events: Diving,Cycling,Running
|
|
//
|
|
// Set the clean value to the list of cleaned checkbox
|
|
// values.
|
|
// First, remove any commas in the values themselves.
|
|
//
|
|
$a_cleaned_values = str_replace(",","",$a_cleaned_values);
|
|
$s_cleaned_value = implode(",",$a_cleaned_values);
|
|
}
|
|
} else {
|
|
//
|
|
// NOTE: there is a minor bug here now that we support
|
|
// $FORM_INI_FILE. The INI file is processed at the end
|
|
// so if you set the mail_options below in the INI file they
|
|
// won't get processed here. This means you must set
|
|
// the following mail_options in the HTML form for now.
|
|
// (To be fixed at a later date. RJR 17-Feb-06).
|
|
//
|
|
|
|
//
|
|
// if the form specifies the "KeepLines" option,
|
|
// don't strip new lines
|
|
//
|
|
if (IsMailOptionSet("KeepLines") && strpos($raw_value,"\n") !== false) {
|
|
//
|
|
// truncate first
|
|
//
|
|
$s_truncated = substr("$raw_value",0,Settings::get('MAXSTRING'));
|
|
//
|
|
// split into lines, clean each individual line,
|
|
// then put it back together again
|
|
//
|
|
$a_lines = explode("\n",$s_truncated);
|
|
$a_lines = CleanValue($a_lines);
|
|
$s_cleaned_value = implode(Settings::get('BODY_LF'),$a_lines);
|
|
//
|
|
// and, for this special case, prepend a new line
|
|
// so that the value is shown on a fresh line
|
|
//
|
|
$s_cleaned_value = Settings::get('BODY_LF') . $s_cleaned_value;
|
|
} else {
|
|
$s_cleaned_value = CleanValue($raw_value);
|
|
}
|
|
}
|
|
//
|
|
// if the form specifies the "NoEmpty" option, skip
|
|
// empty values.
|
|
//
|
|
if (!IsMailOptionSet("NoEmpty") || !FieldManager::IsEmpty($s_cleaned_value)) {
|
|
if (!IsMailExcluded($s_name)) {
|
|
//
|
|
// by default, we maintain the order as passed in
|
|
// the HTTP request
|
|
//
|
|
$a_order[] = $s_name;
|
|
$a_fields[$s_name] = $s_cleaned_value;
|
|
}
|
|
}
|
|
|
|
//
|
|
// add to the $FORMATTED_INPUT array for debugging and
|
|
// error reporting
|
|
//
|
|
array_push($FORMATTED_INPUT,"$s_name: '$s_cleaned_value'");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Parse the input variables and return:
|
|
// 1. an array that specifies the required field order in the output
|
|
// 2. an array containing the non-special cleaned field values indexed
|
|
// by field name.
|
|
// 3. an array containing the non-special raw field values indexed by
|
|
// field name.
|
|
//
|
|
function ParseInput($a_vars)
|
|
{
|
|
$a_order = array();
|
|
$a_fields = array();
|
|
$a_raw_fields = array();
|
|
//
|
|
// scan the array of values passed in (name-value pairs) and
|
|
// produce slightly formatted (not HTML) textual output
|
|
// and extract any special values found.
|
|
//
|
|
foreach ($a_vars as $s_name => $raw_value) {
|
|
ProcessField($s_name,$raw_value,$a_order,$a_fields,$a_raw_fields);
|
|
}
|
|
|
|
return (array($a_order,$a_fields,$a_raw_fields));
|
|
}
|
|
|
|
//
|
|
// Get the URL for sending to the CRM.
|
|
//
|
|
function GetCRMURL($spec,$vars,$url)
|
|
{
|
|
$bad = false;
|
|
$list = TrimArray(explode(",",$spec));
|
|
$map = array();
|
|
for ($ii = 0 ; $ii < count($list) ; $ii++) {
|
|
$name = $list[$ii];
|
|
if ($name) {
|
|
//
|
|
// the specification must be in this format:
|
|
// form-field-name:CRM-field-name
|
|
//
|
|
if (($i_crm_name_pos = strpos($name,":")) > 0) {
|
|
$s_crm_name = substr($name,$i_crm_name_pos + 1);
|
|
$name = substr($name,0,$i_crm_name_pos);
|
|
if (isset($vars[$name])) {
|
|
$map[] = $s_crm_name . "=" . urlencode($vars[$name]);
|
|
$map[] = "Orig_" . $s_crm_name . "=" . urlencode($name);
|
|
}
|
|
} else {
|
|
//
|
|
// not the right format, so just include as a parameter
|
|
// check for name=value format to choose encoding
|
|
//
|
|
$a_values = explode("=",$name);
|
|
if (count($a_values) > 1) {
|
|
$map[] = urlencode($a_values[0]) . "=" . urlencode($a_values[1]);
|
|
} else {
|
|
$map[] = urlencode($a_values[0]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (count($map) == 0) {
|
|
return ("");
|
|
}
|
|
return (AddURLParams($url,$map,false));
|
|
}
|
|
|
|
//
|
|
// strip the HTML from a string or array of strings
|
|
//
|
|
function StripHTML($m_value,$s_line_feed = "\n")
|
|
{
|
|
if (is_array($m_value)) {
|
|
foreach ($m_value as $m_key => $s_str) {
|
|
$m_value[$m_key] = StripHTML($s_str);
|
|
}
|
|
return ($m_value);
|
|
}
|
|
$s_str = $m_value;
|
|
//
|
|
// strip HTML comments (s option means include new lines in matches)
|
|
//
|
|
$s_str = preg_replace('/<!--([^-]*([^-]|-([^-]|-[^>])))*-->/s','',$s_str);
|
|
//
|
|
// strip any scripts (i option means case-insensitive)
|
|
//
|
|
$s_str = preg_replace('/<script[^>]*?>.*?<\/script[^>]*?>/si','',$s_str);
|
|
//
|
|
// replace paragraphs with new lines (line feeds)
|
|
//
|
|
$s_str = preg_replace('/<p[^>]*?>/i',$s_line_feed,$s_str);
|
|
//
|
|
// replace breaks with new lines (line feeds)
|
|
//
|
|
$s_str = preg_replace('/<br[[:space:]]*\/?[[:space:]]*>/i',$s_line_feed,$s_str);
|
|
//
|
|
// overcome this bug: http://bugs.php.net/bug.php?id=21311
|
|
//
|
|
$s_str = preg_replace('/<![^>]*>/s','',$s_str);
|
|
//
|
|
// get rid of all HTML tags
|
|
//
|
|
$s_str = strip_tags($s_str);
|
|
return ($s_str);
|
|
}
|
|
|
|
//
|
|
// Check for valid URL in TARGET_URLS
|
|
//
|
|
function CheckValidURL($s_url)
|
|
{
|
|
|
|
foreach (Settings::get('TARGET_URLS') as $s_prefix) {
|
|
if (!empty($s_prefix) &&
|
|
strtolower(substr($s_url,0,strlen($s_prefix))) ==
|
|
strtolower($s_prefix)
|
|
) {
|
|
return (true);
|
|
}
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// Scan the given data for fields returned from the CRM.
|
|
// A field has this following format:
|
|
// __FIELDNAME__=value
|
|
// terminated by a line feed.
|
|
//
|
|
function FindCRMFields($s_data)
|
|
{
|
|
$a_ret = array();
|
|
if (preg_match_all('/^__([A-Za-z][A-Za-z0-9_]*)__=(.*)$/m',$s_data,$a_matches) === false) {
|
|
SendAlert(GetMessage(MSG_PREG_FAILED));
|
|
} else {
|
|
$n_matches = count($a_matches[0]);
|
|
// SendAlert("$n_matches on '$s_data'");
|
|
for ($ii = 0 ; $ii < $n_matches ; $ii++) {
|
|
if (isset($a_matches[1][$ii]) && isset($a_matches[2][$ii])) {
|
|
$a_ret[$a_matches[1][$ii]] = $a_matches[2][$ii];
|
|
}
|
|
}
|
|
}
|
|
return ($a_ret);
|
|
}
|
|
|
|
//
|
|
// open the given URL to send data to it, we expect the response
|
|
// to contain at least '__OK__=' followed by true or false
|
|
//
|
|
function SendToCRM($s_url,&$a_data)
|
|
{
|
|
global $php_errormsg;
|
|
|
|
if (!CheckValidURL($s_url)) {
|
|
SendAlert(GetMessage(MSG_URL_INVALID,array("URL" => $s_url)));
|
|
return (false);
|
|
}
|
|
@ $fp = fopen($s_url,"r"); // RJR: TO DO: re-implement using NetIO
|
|
if ($fp === false) {
|
|
SendAlert(GetMessage(MSG_URL_OPEN,array("URL" => $s_url,
|
|
"ERROR" => CheckString($php_errormsg)
|
|
)));
|
|
return (false);
|
|
}
|
|
$s_mesg = "";
|
|
while (!feof($fp)) {
|
|
$s_line = fgets($fp,4096);
|
|
$s_mesg .= $s_line;
|
|
}
|
|
fclose($fp);
|
|
$s_mesg = StripHTML($s_mesg);
|
|
$s_result = preg_match('/__OK__=(.*)/',$s_mesg,$a_matches);
|
|
if (count($a_matches) < 2 || $a_matches[1] === "") {
|
|
//
|
|
// no agreed __OK__ value returned - assume system error
|
|
//
|
|
SendAlert(GetMessage(MSG_CRM_FAILED,array("URL" => $s_url,
|
|
"MSG" => $s_mesg
|
|
)));
|
|
return (false);
|
|
}
|
|
//
|
|
// look for fields to return
|
|
//
|
|
$a_data = FindCRMFields($s_mesg);
|
|
//
|
|
// check for success or user error
|
|
//
|
|
switch (strtolower($a_matches[1])) {
|
|
case "true":
|
|
break;
|
|
case "false":
|
|
//
|
|
// check for user error
|
|
//
|
|
if (isset($a_data["USERERRORCODE"])) {
|
|
$s_error_code = "crm_error";
|
|
$s_error_mesg = GetMessage(MSG_CRM_FORM_ERROR);
|
|
$s_error_code .= $a_data["USERERRORCODE"];
|
|
if (isset($a_data["USERERRORMESG"])) {
|
|
$s_error_mesg = $a_data["USERERRORMESG"];
|
|
}
|
|
UserError($s_error_code,$s_error_mesg);
|
|
// no return
|
|
}
|
|
return (false);
|
|
}
|
|
return (true);
|
|
}
|
|
|
|
//
|
|
// Split into field name and friendly name; returns an array with
|
|
// two elements.
|
|
// Format is:
|
|
// fieldname:Nice printable name for displaying
|
|
//
|
|
function GetFriendlyName($s_name)
|
|
{
|
|
if (($i_pos = strpos($s_name,':')) === false) {
|
|
return (array(trim($s_name),trim($s_name)));
|
|
}
|
|
return (array(trim(substr($s_name,0,$i_pos)),trim(substr($s_name,$i_pos + 1))));
|
|
}
|
|
|
|
define("REQUIREDOPS","|^!="); // operand characters for advanced required processing
|
|
|
|
//
|
|
// Perform a field comparison test.
|
|
//
|
|
/** @noinspection PhpStatementHasEmptyBodyInspection */
|
|
function FieldTest($s_oper,$s_fld1,$s_fld2,$a_vars,&$s_error_mesg,
|
|
$s_friendly1 = "",$s_friendly2 = "")
|
|
{
|
|
$b_ok = true;
|
|
$s_empty1 = $s_empty2 = "";
|
|
//
|
|
// perform the test
|
|
//
|
|
switch ($s_oper) {
|
|
case '&': // both fields must be present
|
|
if (!TestFieldEmpty($s_fld1,$a_vars,$s_empty1) &&
|
|
!TestFieldEmpty($s_fld2,$a_vars,$s_empty2)
|
|
) {
|
|
} // OK
|
|
else {
|
|
//
|
|
// failed
|
|
//
|
|
$s_error_mesg = GetMessage(MSG_AND,array("ITEM1" => $s_friendly1,
|
|
"ITEM2" => $s_friendly2
|
|
));
|
|
$b_ok = false;
|
|
}
|
|
break;
|
|
case '|': // either field or both must be present
|
|
if (!TestFieldEmpty($s_fld1,$a_vars,$s_empty1) ||
|
|
!TestFieldEmpty($s_fld2,$a_vars,$s_empty2)
|
|
) {
|
|
} // OK
|
|
else {
|
|
//
|
|
// failed
|
|
//
|
|
$s_error_mesg = GetMessage(MSG_OR,array("ITEM1" => $s_friendly1,
|
|
"ITEM2" => $s_friendly2
|
|
));
|
|
$b_ok = false;
|
|
}
|
|
break;
|
|
case '^': // either field but not both must be present
|
|
$b_got1 = !TestFieldEmpty($s_fld1,$a_vars,$s_empty1);
|
|
$b_got2 = !TestFieldEmpty($s_fld2,$a_vars,$s_empty2);
|
|
if ($b_got1 || $b_got2) {
|
|
if ($b_got1 && $b_got2) {
|
|
//
|
|
// failed
|
|
//
|
|
$s_error_mesg = GetMessage(MSG_NOT_BOTH,
|
|
array("ITEM1" => $s_friendly1,
|
|
"ITEM2" => $s_friendly2
|
|
));
|
|
$b_ok = false;
|
|
}
|
|
} else {
|
|
//
|
|
// failed
|
|
//
|
|
$s_error_mesg = GetMessage(MSG_XOR,
|
|
array("ITEM1" => $s_friendly1,
|
|
"ITEM2" => $s_friendly2
|
|
));
|
|
$b_ok = false;
|
|
}
|
|
break;
|
|
case '!=':
|
|
case '=':
|
|
$b_got1 = !TestFieldEmpty($s_fld1,$a_vars,$s_empty1);
|
|
$b_got2 = !TestFieldEmpty($s_fld2,$a_vars,$s_empty2);
|
|
if ($b_got1 && $b_got2) {
|
|
$b_match = (GetFieldValue($s_fld1,$a_vars) ==
|
|
GetFieldValue($s_fld2,$a_vars));
|
|
} elseif (!$b_got1 && !$b_got2)
|
|
//
|
|
// haven't got either value - they match
|
|
//
|
|
{
|
|
$b_match = true;
|
|
} else
|
|
//
|
|
// got one value, but not the other - they don't match
|
|
//
|
|
{
|
|
$b_match = false;
|
|
}
|
|
if ($s_oper != '=') {
|
|
//
|
|
// != operator
|
|
//
|
|
$b_match = !$b_match;
|
|
$s_desc = GetMessage(MSG_IS_SAME_AS,
|
|
array("ITEM1" => $s_friendly1,
|
|
"ITEM2" => $s_friendly2
|
|
));
|
|
} else {
|
|
$s_desc = GetMessage(MSG_IS_NOT_SAME_AS,
|
|
array("ITEM1" => $s_friendly1,
|
|
"ITEM2" => $s_friendly2
|
|
));
|
|
}
|
|
if (!$b_match) {
|
|
//
|
|
// failed
|
|
//
|
|
$s_error_mesg = $s_desc;
|
|
$b_ok = false;
|
|
}
|
|
break;
|
|
}
|
|
return ($b_ok);
|
|
}
|
|
|
|
//
|
|
// Process advanced "required" conditionals
|
|
//
|
|
function AdvancedRequired($s_cond,$i_span,$a_vars,&$s_missing,&$a_missing_list)
|
|
{
|
|
$b_ok = true;
|
|
//
|
|
// get first field name
|
|
//
|
|
list($s_fld1,$s_friendly1) = GetFriendlyName(substr($s_cond,0,$i_span));
|
|
//
|
|
// get the operator
|
|
//
|
|
$s_rem = substr($s_cond,$i_span);
|
|
$i_span = strspn($s_rem,REQUIREDOPS);
|
|
$s_oper = substr($s_rem,0,$i_span);
|
|
switch ($s_oper) {
|
|
case '|':
|
|
case '^':
|
|
case '=':
|
|
case '!=':
|
|
//
|
|
// second component is a field name
|
|
//
|
|
list($s_fld2,$s_friendly2) = GetFriendlyName(substr($s_rem,$i_span));
|
|
if (!FieldTest($s_oper,$s_fld1,$s_fld2,$a_vars,$s_error_mesg,
|
|
$s_friendly1,$s_friendly2)
|
|
) {
|
|
//
|
|
// failed
|
|
//
|
|
$s_missing .= "$s_error_mesg\n";
|
|
$a_missing_list[$s_fld1] = "$s_error_mesg";
|
|
$b_ok = false;
|
|
}
|
|
break;
|
|
default:
|
|
SendAlert(GetMessage(MSG_REQD_OPER,array("OPER" => $s_oper)));
|
|
break;
|
|
}
|
|
return ($b_ok);
|
|
}
|
|
|
|
//
|
|
// Check the input for required values. The list of required fields
|
|
// is a comma-separated list of field names or conditionals
|
|
//
|
|
function CheckRequired($s_reqd,$a_vars,&$s_missing,&$a_missing_list)
|
|
{
|
|
global $reCaptchaProcessor;
|
|
|
|
$b_bad = false;
|
|
$a_list = TrimArray(explode(",",$s_reqd));
|
|
$s_missing = "";
|
|
$a_missing_list = array();
|
|
$s_mesg = "";
|
|
for ($ii = 0 ; $ii < count($a_list) ; $ii++) {
|
|
$s_cond = $a_list[$ii];
|
|
$i_len = strlen($s_cond);
|
|
if ($i_len <= 0) {
|
|
continue;
|
|
}
|
|
if (($i_span = strcspn($s_cond,REQUIREDOPS)) >= $i_len) {
|
|
//
|
|
// no advanced operator; just a field name
|
|
//
|
|
list($s_fld,$s_friendly) = GetFriendlyName($s_cond);
|
|
if (TestFieldEmpty($s_fld,$a_vars,$s_mesg)) {
|
|
if ($s_mesg === "") {
|
|
$s_mesg = "$s_friendly";
|
|
} else {
|
|
$s_mesg = "$s_friendly ($s_mesg)";
|
|
}
|
|
$b_bad = true;
|
|
$s_missing .= "$s_mesg\n";
|
|
$a_missing_list[$s_fld] = "$s_mesg";
|
|
}
|
|
} elseif (!AdvancedRequired($s_cond,$i_span,$a_vars,
|
|
$s_missing,$a_missing_list)
|
|
) {
|
|
$b_bad = true;
|
|
}
|
|
}
|
|
|
|
global $SPECIAL_VALUES;
|
|
|
|
//
|
|
// implement REQUIRE_CAPTCHA feature
|
|
//
|
|
if (!Settings::isEmpty('REQUIRE_CAPTCHA')) {
|
|
if ($SPECIAL_VALUES["imgverify"] === "") {
|
|
$s_missing .= Settings::get('REQUIRE_CAPTCHA') . "\n";
|
|
$a_missing_list['imgverify'] = Settings::get('REQUIRE_CAPTCHA');
|
|
$b_bad = true;
|
|
}
|
|
}
|
|
return (!$b_bad);
|
|
}
|
|
|
|
/**
|
|
* Class Conditions
|
|
* Implements "conditions" processing.
|
|
*/
|
|
class Conditions
|
|
{
|
|
/** @var array|string the conditions to process */
|
|
private $_mConditions; //
|
|
/** @var string the "conditions" field being processed */
|
|
private $_sField;
|
|
/** @var string */
|
|
private $_sMissing;
|
|
/** @var array */
|
|
private $_aMissingList;
|
|
/** @var FieldManager */
|
|
private $_FldManager;
|
|
|
|
/**
|
|
* @param array|string $m_conditions the conditions to process
|
|
* @param string $s_missing returns the message from the condition's failure
|
|
* @param array $a_missing_list appended with the field name(s) that failed
|
|
*/
|
|
function __construct($m_conditions,&$s_missing,&$a_missing_list)
|
|
{
|
|
$this->_mConditions = $m_conditions;
|
|
$this->_sMissing = &$s_missing;
|
|
$this->_aMissingList = &$a_missing_list;
|
|
$this->_FldManager = new FieldManager();
|
|
}
|
|
|
|
/**
|
|
* Run the given field logic.
|
|
*
|
|
* @param string $s_test a string containing the field logic to run
|
|
*
|
|
* @return string|bool true if the logic evaluates to true, otherwise name of a field if the logic evaluates to false
|
|
*/
|
|
private function _runLogic($s_test)
|
|
{
|
|
global $aAlertInfo;
|
|
|
|
$s_op_chars = "&|^!=~#<>"; // these are the characters for the operators
|
|
$i_len = strlen($s_test);
|
|
$b_ok = true;
|
|
$s_mesg = "";
|
|
$s_fld_name = "";
|
|
/** @noinspection PhpStatementHasEmptyBodyInspection */
|
|
if ($i_len <= 0)
|
|
//
|
|
// empty test - true
|
|
//
|
|
{
|
|
} elseif ($s_test == "!")
|
|
//
|
|
// test asserts false
|
|
//
|
|
{
|
|
$b_ok = false;
|
|
} elseif (($i_span = strcspn($s_test,$s_op_chars)) >= $i_len)
|
|
//
|
|
// no operator - just check field presence
|
|
//
|
|
{
|
|
$s_fld_name = $s_test;
|
|
$b_ok = !$this->_FldManager->TestFieldEmpty($s_test,$s_mesg);
|
|
} else {
|
|
//
|
|
// get first field name
|
|
//
|
|
$s_fld_name = $s_fld1 = trim(substr($s_test,0,$i_span));
|
|
//
|
|
// get the operator
|
|
//
|
|
$s_rem = substr($s_test,$i_span);
|
|
$i_span = strspn($s_rem,$s_op_chars);
|
|
$s_oper = substr($s_rem,0,$i_span);
|
|
switch ($s_oper) {
|
|
case '&':
|
|
case '|':
|
|
case '^':
|
|
case '=':
|
|
case '!=':
|
|
//
|
|
// get the second field name
|
|
//
|
|
$s_fld2 = trim(substr($s_rem,$i_span));
|
|
$b_ok = FieldTest($s_oper,$s_fld1,$s_fld2,$this->_FldManager->GetFields(),$s_error_mesg);
|
|
break;
|
|
case '~':
|
|
case '!~':
|
|
//
|
|
// get the regular expression
|
|
//
|
|
$s_pat = trim(substr($s_rem,$i_span));
|
|
if (!$this->_FldManager->TestFieldEmpty($s_fld1,$s_mesg)) {
|
|
$s_value = $this->_FldManager->GetFieldValue($s_fld1);
|
|
} else {
|
|
$s_value = "";
|
|
}
|
|
//echo "<p>Pattern: '".htmlspecialchars($s_pat)."': count=".preg_match($s_pat,$s_value)."<br /></p>";
|
|
//
|
|
// match the regular expression
|
|
//
|
|
if (preg_match($s_pat,$s_value) > 0) {
|
|
$b_ok = ($s_oper == '~');
|
|
} else {
|
|
$b_ok = ($s_oper == '!~');
|
|
}
|
|
if (!$b_ok) {
|
|
$aAlertInfo[] = GetMessage(MSG_PAT_FAILED,array("OPER" => $s_oper,
|
|
"PAT" => $s_pat,
|
|
"VALUE" => $s_value
|
|
));
|
|
}
|
|
break;
|
|
case '#=':
|
|
case '#!=':
|
|
case '#<':
|
|
case '#>':
|
|
case '#<=':
|
|
case '#>=':
|
|
//
|
|
// numeric tests
|
|
//
|
|
$s_num = trim(substr($s_rem,$i_span));
|
|
//
|
|
// if this is a file field, get the size of the file for
|
|
// numeric tests
|
|
//
|
|
if (($s_value = GetFileSize($s_fld1)) === false) {
|
|
$s_value = $this->_FldManager->GetFieldValue($s_fld1);
|
|
}
|
|
if (strpos($s_num,'.') === false) {
|
|
//
|
|
// treat as integer
|
|
//
|
|
$m_num = (int)$s_num;
|
|
$m_fld = (int)$s_value;
|
|
} else {
|
|
//
|
|
// treat as floating point
|
|
//
|
|
$m_num = (float)$s_num;
|
|
$m_fld = (float)$s_value;
|
|
}
|
|
switch ($s_oper) {
|
|
case '#=':
|
|
$b_ok = ($m_fld == $m_num);
|
|
break;
|
|
case '#!=':
|
|
$b_ok = ($m_fld != $m_num);
|
|
break;
|
|
case '#<':
|
|
$b_ok = ($m_fld < $m_num);
|
|
break;
|
|
case '#>':
|
|
$b_ok = ($m_fld > $m_num);
|
|
break;
|
|
case '#<=':
|
|
$b_ok = ($m_fld <= $m_num);
|
|
break;
|
|
case '#>=':
|
|
$b_ok = ($m_fld >= $m_num);
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
SendAlert(GetMessage(MSG_COND_OPER,array("OPER" => $s_oper)));
|
|
break;
|
|
}
|
|
}
|
|
return $b_ok ? true : $s_fld_name;
|
|
}
|
|
|
|
//
|
|
// Check the input for condition tests.
|
|
//
|
|
public function Check($a_vars,$a_file_vars = array())
|
|
{
|
|
$this->_FldManager->Init($a_vars,$a_file_vars);
|
|
//
|
|
// handle a list of conditions in an array
|
|
//
|
|
if (is_array($this->_mConditions)) {
|
|
//
|
|
// Sort the conditions by their numeric value. This ensures
|
|
// conditions are executed in the right order.
|
|
//
|
|
ksort($this->_mConditions,SORT_NUMERIC);
|
|
foreach ($this->_mConditions as $m_key => $s_cond) {
|
|
if (!$this->_checkString($s_cond,$m_key)) {
|
|
return (false);
|
|
}
|
|
}
|
|
return (true);
|
|
} else {
|
|
//
|
|
// handle one set of conditions in a string
|
|
//
|
|
return $this->_checkString($this->_mConditions);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test a condition represented in a string.
|
|
*
|
|
* @param string $s_cond the condition
|
|
* @param bool|int $m_id ID of the condition being processed
|
|
*
|
|
* @return bool true if the condition passed, otherwise false
|
|
*/
|
|
private function _checkString($s_cond,$m_id = false)
|
|
{
|
|
$this->_sField = "conditions" . ($m_id === false ? "" : ($m_id + 1));
|
|
if (!is_string($s_cond)) {
|
|
SendAlert(GetMessage(MSG_INV_COND,array("FLD" => $this->_sField)));
|
|
//
|
|
// invalid conditions are simple "true"
|
|
//
|
|
return (true);
|
|
}
|
|
//
|
|
// empty conditions are simply "true"
|
|
//
|
|
if ($s_cond == "") {
|
|
return (true);
|
|
}
|
|
//
|
|
// extract the separator characters
|
|
//
|
|
if (strlen($s_cond) < 2) {
|
|
SendAlert(GetMessage(MSG_COND_CHARS,
|
|
array("FLD" => $this->_sField,"COND" => $s_cond)));
|
|
//
|
|
// invalid conditions are simple "true"
|
|
//
|
|
return (true);
|
|
}
|
|
return $this->_checkString2($s_cond);
|
|
}
|
|
|
|
/**
|
|
* Test a condition represented in a string. This method is called by _checkString to do the real
|
|
* work after some initial sanity checks.
|
|
*
|
|
* @param string $s_cond the condition
|
|
*
|
|
* @return bool true if the condition passed, otherwise false
|
|
*/
|
|
private function _checkString2($s_cond)
|
|
{
|
|
$s_list_sep = $s_cond[0];
|
|
$s_int_sep = $s_cond[1];
|
|
$s_full_cond = $s_cond = substr($s_cond,2);
|
|
$b_bad = false;
|
|
$a_list = TrimArray(explode($s_list_sep,$s_cond));
|
|
for ($ii = 0 ; $ii < count($a_list) ; $ii++) {
|
|
$s_curr_cond = $a_list[$ii];
|
|
$i_len = strlen($s_curr_cond);
|
|
if ($i_len <= 0) {
|
|
continue;
|
|
}
|
|
//
|
|
// split the condition into its internal components
|
|
//
|
|
$a_components = TrimArray(explode($s_int_sep,$s_curr_cond));
|
|
if (count($a_components) < 5) {
|
|
SendAlert(GetMessage(MSG_COND_INVALID,
|
|
array("FLD" => $this->_sField,"COND" => $s_curr_cond,
|
|
"SEP" => $s_int_sep
|
|
)));
|
|
//
|
|
// the smallest condition has 5 components
|
|
//
|
|
continue;
|
|
}
|
|
//
|
|
// first component is ignored (it's blank)
|
|
//
|
|
$a_components = array_slice($a_components,1);
|
|
switch ($a_components[0]) {
|
|
case "TEST":
|
|
if (!$this->_doTest($s_curr_cond,$a_components,$s_list_sep)) {
|
|
$b_bad = true;
|
|
}
|
|
break;
|
|
case "IF":
|
|
if (!$this->_doIf($s_curr_cond,$a_components,$s_int_sep,$s_list_sep)) {
|
|
$b_bad = true;
|
|
}
|
|
break;
|
|
default:
|
|
SendAlert(GetMessage(MSG_COND_UNK,
|
|
array("FLD" => $this->_sField,"COND" => $s_curr_cond,
|
|
"CMD" => $a_components[0]
|
|
)));
|
|
break;
|
|
}
|
|
}
|
|
return (!$b_bad);
|
|
}
|
|
|
|
/**
|
|
* Process a TEST condition.
|
|
*
|
|
* @param string $s_cond the condition
|
|
* @param array $a_components components of the TEST condition
|
|
* @param string $s_list_sep the separator for lists in the condition
|
|
*
|
|
* @return bool true if the condition passed, otherwise false
|
|
*/
|
|
private function _doTest($s_cond,$a_components,$s_list_sep)
|
|
{
|
|
//
|
|
// sanity check - if failed, just pass the TEST
|
|
//
|
|
if (count($a_components) > 5) {
|
|
SendAlert(GetMessage(MSG_COND_TEST_LONG,
|
|
array("FLD" => $this->_sField,"COND" => $s_cond,
|
|
"SEP" => $s_list_sep
|
|
)));
|
|
return true;
|
|
} elseif (($m_test_result = $this->_runLogic($a_components[1])) === true) {
|
|
return true;
|
|
} else {
|
|
$this->_recordField($m_test_result,$a_components[2]);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process an IF condition.
|
|
*
|
|
* @param string $s_cond the condition
|
|
* @param array $a_components components of the TEST condition
|
|
* @param string $s_int_sep the internal separator for the condition
|
|
* @param string $s_list_sep the separator for lists in the condition
|
|
*
|
|
* @return bool true if the condition passed, otherwise false
|
|
*/
|
|
private function _doIf($s_cond,$a_components,$s_int_sep,$s_list_sep)
|
|
{
|
|
//
|
|
// sanity checks - if failed, just pass the IF
|
|
//
|
|
if (count($a_components) < 6) {
|
|
SendAlert(GetMessage(MSG_COND_IF_SHORT,
|
|
array("FLD" => $this->_sField,"COND" => $s_cond,
|
|
"SEP" => $s_int_sep
|
|
)));
|
|
return true;
|
|
}
|
|
if (count($a_components) > 7) {
|
|
SendAlert(GetMessage(MSG_COND_IF_LONG,
|
|
array("FLD" => $this->_sField,"COND" => $s_cond,
|
|
"SEP" => $s_list_sep
|
|
)));
|
|
return true;
|
|
}
|
|
if (($m_test_result = $this->_runLogic($a_components[1])) === true) {
|
|
$m_test_result = $this->_runLogic($a_components[2]);
|
|
} else {
|
|
$m_test_result = $this->_runLogic($a_components[3]);
|
|
}
|
|
if ($m_test_result !== true) {
|
|
$this->_recordField($m_test_result,$a_components[4]);
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private function _recordField($s_fld_name,$s_mesg)
|
|
{
|
|
$this->_sMissing .= $s_mesg . "\n";
|
|
$this->_aMissingList[$s_fld_name] = $s_mesg;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Original interface to processing conditions.
|
|
* This function is only kept for backwards compatibility with any existing hook code.
|
|
*
|
|
* @param mixed $m_conditions a single condition or an array of them
|
|
* @param array $a_vars field values
|
|
* @param string $s_missing returns the message from the condition's failure
|
|
* @param array $a_missing_list appended with the field name(s) that failed
|
|
* @param bool|int $m_id was never used - deprecated
|
|
*
|
|
* @return bool true if the condition passed, otherwise false
|
|
* @noinspection PhpUnused
|
|
*/
|
|
function CheckConditions($m_conditions,$a_vars,&$s_missing,&$a_missing_list,$m_id = false)
|
|
{
|
|
global $aFileVars;
|
|
|
|
$cond = new Conditions($m_conditions,$s_missing,$a_missing_list);
|
|
|
|
return $cond->Check($a_vars,$aFileVars);
|
|
}
|
|
|
|
//
|
|
// Return a formatted list of the given environment variables.
|
|
//
|
|
function GetEnvVars($list,$s_line_feed)
|
|
{
|
|
global $aServerVars;
|
|
|
|
$output = "";
|
|
for ($ii = 0 ; $ii < count($list) ; $ii++) {
|
|
$name = $list[$ii];
|
|
if ($name && array_search($name,Settings::get('VALID_ENV'),true) !== false) {
|
|
//
|
|
// if the environment variable is empty or non-existent, try
|
|
// looking for the value in the server vars.
|
|
//
|
|
if (($s_value = getenv($name)) === "" || $s_value === false) {
|
|
if (isset($aServerVars[$name])) {
|
|
$s_value = $aServerVars[$name];
|
|
} else {
|
|
$s_value = "";
|
|
}
|
|
}
|
|
$output .= $name . "=" . $s_value . $s_line_feed;
|
|
}
|
|
}
|
|
return ($output);
|
|
}
|
|
|
|
//
|
|
// open a socket connection to a filter and post the data there
|
|
// RJR: TO DO: re-implement using NetIO
|
|
//
|
|
function SocketFilter($filter,$a_filter_info,$m_data)
|
|
{
|
|
static $b_in_here = false;
|
|
global $php_errormsg;
|
|
|
|
//
|
|
// prevent recursive errors
|
|
//
|
|
if ($b_in_here) {
|
|
return ("<DATA DISCARDED>");
|
|
}
|
|
$b_in_here = true;
|
|
|
|
$a_errors = array();
|
|
if (!isset($a_filter_info["site"])) {
|
|
$a_errors[] = GetMessage(MSG_MISSING,array("ITEM" => "site"));
|
|
} else {
|
|
$s_site = $a_filter_info["site"];
|
|
}
|
|
|
|
if (!isset($a_filter_info["port"])) {
|
|
$a_errors[] = GetMessage(MSG_MISSING,array("ITEM" => "port"));
|
|
} else {
|
|
$i_port = (int)$a_filter_info["port"];
|
|
}
|
|
|
|
if (!isset($a_filter_info["path"])) {
|
|
$a_errors[] = GetMessage(MSG_MISSING,array("ITEM" => "path"));
|
|
} else {
|
|
$s_path = $a_filter_info["path"];
|
|
}
|
|
|
|
if (!isset($a_filter_info["params"])) {
|
|
$a_params = array();
|
|
} elseif (!is_array($a_filter_info["params"])) {
|
|
$a_errors[] = GetMessage(MSG_NEED_ARRAY,array("ITEM" => "params"));
|
|
} else {
|
|
$a_params = $a_filter_info["params"];
|
|
}
|
|
|
|
if (!empty($a_errors)) {
|
|
Error("bad_filter",GetMessage(MSG_FILTER_WRONG,array(
|
|
"FILTER" => $filter,
|
|
"ERRORS" => implode(', ',$a_errors)
|
|
)), false,false);
|
|
FormMailExit();
|
|
}
|
|
|
|
//
|
|
// ready to build the socket - we need a longer time limit for the
|
|
// script if we're doing this; we allow 30 seconds for the connection
|
|
// (should be instantaneous, especially if it's the same domain)
|
|
//
|
|
set_time_limit(60);
|
|
/** @noinspection PhpUndefinedVariableInspection */
|
|
/** @noinspection PhpUndefinedVariableInspection */
|
|
@ $f_sock = fsockopen($s_site,$i_port,$i_errno,$s_errstr,30);
|
|
if ($f_sock === false) {
|
|
Error("filter_connect",GetMessage(MSG_FILTER_CONNECT,array(
|
|
"FILTER" => $filter,
|
|
"SITE" => $s_site,
|
|
"ERRNUM" => $i_errno,
|
|
"ERRSTR" => "$s_errstr (" . CheckString($php_errormsg) . ")"
|
|
)),
|
|
false,false);
|
|
FormMailExit();
|
|
}
|
|
//
|
|
// build the data to send
|
|
//
|
|
$m_request_data = array();
|
|
$i_count = 0;
|
|
/** @noinspection PhpUndefinedVariableInspection */
|
|
foreach ($a_params as $m_var) {
|
|
$i_count++;
|
|
//
|
|
// if the parameter spec is an array, process it specially;
|
|
// it must have "name" and "file" elements
|
|
//
|
|
if (is_array($m_var)) {
|
|
if (!isset($m_var["name"])) {
|
|
Error("bad_filter",GetMessage(MSG_FILTER_PARAM,
|
|
array("FILTER" => $filter,
|
|
"NUM" => $i_count,
|
|
"NAME" => "name"
|
|
)),false,false);
|
|
fclose($f_sock);
|
|
FormMailExit();
|
|
}
|
|
$s_name = $m_var["name"];
|
|
if (!isset($m_var["file"])) {
|
|
Error("bad_filter",GetMessage(MSG_FILTER_PARAM,
|
|
array("FILTER" => $filter,
|
|
"NUM" => $i_count,
|
|
"NAME" => "file"
|
|
)),false,false);
|
|
fclose($f_sock);
|
|
FormMailExit();
|
|
}
|
|
//
|
|
// open the file and read its contents
|
|
//
|
|
@ $fp = fopen($m_var["file"],"r");
|
|
if ($fp === false) {
|
|
Error("filter_error",GetMessage(MSG_FILTER_OPEN_FILE,
|
|
array("FILTER" => $filter,
|
|
"FILE" => $m_var["file"],
|
|
"ERROR" => CheckString($php_errormsg)
|
|
)),false,false);
|
|
fclose($f_sock);
|
|
FormMailExit();
|
|
}
|
|
$s_data = "";
|
|
$n_lines = 0;
|
|
while (!feof($fp)) {
|
|
if (($s_line = fgets($fp,2048)) === false) {
|
|
if (feof($fp)) {
|
|
break;
|
|
} else {
|
|
Error("filter_error",GetMessage(MSG_FILTER_FILE_ERROR,
|
|
array("FILTER" => $filter,
|
|
"FILE" => $m_var["file"],
|
|
"ERROR" => CheckString($php_errormsg),
|
|
"NLINES" => $n_lines
|
|
)),false,false);
|
|
fclose($f_sock);
|
|
FormMailExit();
|
|
}
|
|
}
|
|
$s_data .= $s_line;
|
|
$n_lines++;
|
|
}
|
|
|
|
fclose($fp);
|
|
$m_request_data[] = "$s_name=" . urlencode($s_data);
|
|
} else {
|
|
$m_request_data[] = (string)$m_var;
|
|
}
|
|
}
|
|
//
|
|
// add the data
|
|
//
|
|
if (is_array($m_data)) {
|
|
$m_request_data[] = "data=" . urlencode(implode(Settings::get('BODY_LF'),$m_data));
|
|
} else {
|
|
$m_request_data[] = "data=" . urlencode($m_data);
|
|
}
|
|
$s_request = implode("&",$m_request_data);
|
|
|
|
if (($i_pos = strpos($s_site,"://")) !== false) {
|
|
$s_site_name = substr($s_site,$i_pos + 3);
|
|
} else {
|
|
$s_site_name = $s_site;
|
|
}
|
|
|
|
/** @noinspection PhpUndefinedVariableInspection */
|
|
fputs($f_sock,"POST $s_path HTTP/1.0\r\n");
|
|
fputs($f_sock,"Host: $s_site_name\r\n");
|
|
fputs($f_sock,"Content-Type: application/x-www-form-urlencoded\r\n");
|
|
fputs($f_sock,"Content-Length: " . strlen($s_request) . "\r\n");
|
|
fputs($f_sock,"\r\n");
|
|
fputs($f_sock,"$s_request\r\n");
|
|
|
|
//
|
|
// now read the response
|
|
//
|
|
$m_hdr = "";
|
|
$m_data = "";
|
|
$b_in_hdr = true;
|
|
$b_ok = false;
|
|
while (!feof($f_sock)) {
|
|
if (($s_line = fgets($f_sock,2048)) === false) {
|
|
if (feof($f_sock)) {
|
|
break;
|
|
} else {
|
|
Error("filter_failed",GetMessage(MSG_FILTER_READ_ERROR,
|
|
array("FILTER" => $filter,
|
|
"ERROR" => CheckString($php_errormsg)
|
|
)),false,false);
|
|
fclose($f_sock);
|
|
FormMailExit();
|
|
}
|
|
}
|
|
//
|
|
// look for an "__OK__" line
|
|
//
|
|
if (trim($s_line) == "__OK__") {
|
|
$b_ok = true;
|
|
} elseif ($b_in_hdr) {
|
|
//
|
|
// blank line signals end of header
|
|
//
|
|
if (trim($s_line) == "") {
|
|
$b_in_hdr = false;
|
|
} else {
|
|
$m_hdr .= $s_line;
|
|
}
|
|
} else {
|
|
$m_data .= $s_line;
|
|
}
|
|
}
|
|
//
|
|
// if not OK, then report error
|
|
//
|
|
if (!$b_ok) {
|
|
Error("filter_failed",GetMessage(MSG_FILTER_NOT_OK,
|
|
array("FILTER" => $filter,
|
|
"DATA" => $m_data
|
|
)),false,false);
|
|
fclose($f_sock);
|
|
FormMailExit();
|
|
}
|
|
fclose($f_sock);
|
|
$b_in_here = false;
|
|
return ($m_data);
|
|
}
|
|
|
|
//
|
|
// run data through a supported filter
|
|
//
|
|
/** @noinspection PhpStatementHasEmptyBodyInspection */
|
|
function Filter($filter,$m_data)
|
|
{
|
|
|
|
global $php_errormsg;
|
|
static $b_in_here = false;
|
|
|
|
//
|
|
// prevent recursive errors
|
|
//
|
|
if ($b_in_here) {
|
|
return ("<DATA DISCARDED>");
|
|
}
|
|
$b_in_here = true;
|
|
|
|
//
|
|
// Any errors sent in an alert are flagged to not run through the
|
|
// filter - this also means the user's data won't be included in the
|
|
// alert.
|
|
// The reason for this is that the Filter is typically an encryption
|
|
// program. If the filter fails, then sending the user's data in
|
|
// clear text in an alert breaks the security of having encryption
|
|
// in the first place!
|
|
//
|
|
|
|
//
|
|
// find the filter
|
|
//
|
|
$a_filters = Settings::get('FILTERS');
|
|
if (!isset($a_filters[$filter]) || $a_filters[$filter] == "") {
|
|
//
|
|
// check for SOCKET_FILTERS
|
|
//
|
|
$a_filters = Settings::get('SOCKET_FILTERS');
|
|
if (!isset($a_filters[$filter]) || $a_filters[$filter] == "") {
|
|
ErrorWithIgnore("bad_filter",GetMessage(MSG_FILTER_UNK,
|
|
array("FILTER" => $filter)),false,false);
|
|
FormMailExit();
|
|
}
|
|
$m_data = SocketFilter($filter,$a_filters[$filter],$m_data);
|
|
} elseif ($a_filters[$filter] == "null")
|
|
//
|
|
// do nothing - just return the original data unchanged
|
|
//
|
|
{
|
|
} elseif ($a_filters[$filter] == "csv") {
|
|
$m_data = BuiltinFilterCSV();
|
|
} else {
|
|
$cmd = $a_filters[$filter];
|
|
//
|
|
// get the program name - assumed to be the first blank-separated word
|
|
//
|
|
$a_words = preg_split('/\s+/',$cmd);
|
|
$prog = $a_words[0];
|
|
|
|
$s_cwd = getcwd();
|
|
//
|
|
// change to the directory that contains the filter program
|
|
//
|
|
$dirname = dirname($prog);
|
|
if ($dirname != "" && $dirname != "." && !chdir($dirname)) {
|
|
Error("chdir_filter",
|
|
GetMessage(MSG_FILTER_CHDIR,
|
|
array("DIR" => $dirname,
|
|
"FILTER" => $filter,
|
|
"ERROR" => CheckString($php_errormsg)
|
|
)),
|
|
false,false);
|
|
FormMailExit();
|
|
}
|
|
|
|
//
|
|
// the output of the filter goes to a temporary file; this works
|
|
// OK on Windows too, even with the Unix shell syntax.
|
|
//
|
|
$temp_file = GetTempName("FMF");
|
|
$temp_error_file = GetTempName("FME");
|
|
$cmd = "$cmd >$temp_file 2>$temp_error_file";
|
|
//
|
|
// start the filter
|
|
//
|
|
$pipe = popen($cmd,"w");
|
|
if ($pipe === false) {
|
|
$s_sv_err = CheckString($php_errormsg);
|
|
$err = join('',file($temp_error_file));
|
|
unlink($temp_file);
|
|
unlink($temp_error_file);
|
|
Error("filter_not_found",
|
|
GetMessage(MSG_FILTER_NOTFOUND,
|
|
array("CMD" => $cmd,
|
|
"FILTER" => $filter,
|
|
"ERROR" => $s_sv_err
|
|
)),
|
|
false,
|
|
false,
|
|
$err);
|
|
FormMailExit();
|
|
}
|
|
//
|
|
// write the data to the filter
|
|
//
|
|
if (is_array($m_data)) {
|
|
fwrite($pipe,implode(Settings::get('BODY_LF'),$m_data));
|
|
} else {
|
|
fwrite($pipe,$m_data);
|
|
}
|
|
if (($i_st = pclose($pipe)) != 0) {
|
|
$s_sv_err = CheckString($php_errormsg);
|
|
$err = join('',file($temp_error_file));
|
|
unlink($temp_file);
|
|
unlink($temp_error_file);
|
|
Error("filter_failed",GetMessage(MSG_FILTER_ERROR,
|
|
array("FILTER" => $filter,
|
|
"ERROR" => $s_sv_err,
|
|
"STATUS" => $i_st
|
|
)),false,false,$err);
|
|
FormMailExit();
|
|
}
|
|
//
|
|
// read in the filter's output and return as the data to be sent
|
|
//
|
|
$m_data = join('',file($temp_file));
|
|
unlink($temp_error_file);
|
|
unlink($temp_file);
|
|
|
|
//
|
|
// return to previous directory
|
|
//
|
|
chdir($s_cwd);
|
|
}
|
|
$b_in_here = false;
|
|
return ($m_data);
|
|
}
|
|
|
|
/*
|
|
* Function: FilterFiles
|
|
* Parameters: $a_files list of file uploads to filter
|
|
* Returns: void
|
|
* Description:
|
|
* Run the given files through any filter for which they are specified.
|
|
*/
|
|
function FilterFiles(&$a_files)
|
|
{
|
|
global $SPECIAL_VALUES;
|
|
|
|
FMDebug("FilterFiles " . count($a_files));
|
|
if (!GetFilterSpec($s_filter,$a_filter_list,true) || $a_filter_list === false)
|
|
//
|
|
// no filter or file fields to filter
|
|
//
|
|
{
|
|
return;
|
|
}
|
|
if (($s_mime = GetFilterAttrib($s_filter,"MIME")) === false) {
|
|
$s_mime = "";
|
|
}
|
|
//
|
|
// now, for each file, check if it is specified in the filter list
|
|
//
|
|
foreach ($a_files as $s_fld => $a_upload) {
|
|
FMDebug("Checking $s_fld");
|
|
if (!FieldManager::IsUploadedFile($a_upload)) {
|
|
FMDebug("Not uploaded");
|
|
//
|
|
// failed security check
|
|
//
|
|
continue;
|
|
}
|
|
if (!in_array($s_fld,$a_filter_list,true)) {
|
|
FMDebug("Not to be filtered");
|
|
continue;
|
|
}
|
|
/*** not sure what to do with this....
|
|
* if (isset($a_upload["error"]))
|
|
* //
|
|
* // there was an upload error
|
|
* //
|
|
* continue;
|
|
***/
|
|
//
|
|
// this file upload has been specified for filtering
|
|
//
|
|
$s_file_name = $a_upload["tmp_name"];
|
|
//
|
|
// check if the file has been saved elsewhere
|
|
//
|
|
if (isset($a_upload["saved_as"]) && !empty($a_upload["saved_as"])) {
|
|
$s_file_name = $a_upload["saved_as"];
|
|
}
|
|
FMDebug("File name is $s_file_name");
|
|
//
|
|
// read in the file
|
|
//
|
|
if (($s_data = ReadInFile($s_file_name,"upload")) === false) {
|
|
Error("filter_files",GetMessage(MSG_FILE_UPLOAD_ERR_UNK,array("ERRNO" => "reading $s_fld")),false,
|
|
false);
|
|
}
|
|
//
|
|
// filter and write it back out to the same file
|
|
//
|
|
$s_data = Filter($s_filter,$s_data);
|
|
if (!WriteOutFile($s_file_name,$s_data,"upload")) {
|
|
Error("filter_files",GetMessage(MSG_FILE_UPLOAD_ERR_UNK,array("ERRNO" => "writing $s_fld")),false,
|
|
false);
|
|
}
|
|
//
|
|
// update size and MIME type for this upload
|
|
//
|
|
$a_upload["size"] = strlen($s_data);
|
|
if ($s_mime !== "") {
|
|
$a_upload["type"] = $s_mime;
|
|
}
|
|
$a_files[$s_fld] = $a_upload;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: ReadInFile
|
|
* Parameters: $s_file_name the name of the file
|
|
* $s_file_error_type type of file for any error message
|
|
* $b_text if true, read file as text
|
|
* Returns: mixed the entire contents of the file
|
|
* as a string, or false on error
|
|
* Description:
|
|
* Reads the contents of a file into a string.
|
|
*/
|
|
function ReadInFile($s_file_name,$s_file_error_type,$b_text = false)
|
|
{
|
|
global $php_errormsg;
|
|
|
|
if (($fp = @fopen($s_file_name,"r" . ($b_text ? "t" : "b"))) === false) {
|
|
SendAlert(GetMessage(MSG_FILE_OPEN_ERROR,array("NAME" => $s_file_name,
|
|
"TYPE" => "read " . $s_file_error_type,
|
|
"ERROR" => CheckString($php_errormsg)
|
|
)));
|
|
return (false);
|
|
}
|
|
$s_data = "";
|
|
while (!feof($fp)) {
|
|
$s_data .= fread($fp,8192);
|
|
}
|
|
@fclose($fp);
|
|
return ($s_data);
|
|
}
|
|
|
|
/*
|
|
* Function: WriteOutFile
|
|
* Parameters: $s_file_name the name of the file
|
|
* $s_data the data to write
|
|
* $s_file_error_type type of file for any error message
|
|
* $b_text if true, read file as text
|
|
* Returns: bool true on success, otherwise false
|
|
* Description:
|
|
* Writes the contents of a file from a string.
|
|
*/
|
|
function WriteOutFile($s_file_name,$s_data,$s_file_error_type,$b_text = false)
|
|
{
|
|
global $php_errormsg;
|
|
|
|
if (($fp = @fopen($s_file_name,"w" . ($b_text ? "t" : "b"))) === false) {
|
|
SendAlert(GetMessage(MSG_FILE_OPEN_ERROR,array("NAME" => $s_file_name,
|
|
"TYPE" => "write " . $s_file_error_type,
|
|
"ERROR" => CheckString($php_errormsg)
|
|
)));
|
|
return (false);
|
|
}
|
|
if (fwrite($fp,$s_data) < strlen($s_data)) {
|
|
@fclose($fp);
|
|
return (false);
|
|
}
|
|
@fclose($fp);
|
|
return (true);
|
|
}
|
|
|
|
/*
|
|
* Class: CSVFormat
|
|
* Description:
|
|
* Manages formatting of CSV content.
|
|
*/
|
|
|
|
class CSVFormat
|
|
{
|
|
var $_cSep; /* field separator character */
|
|
var $_cQuote; /* field quote character */
|
|
var $_cIntSep; /* internal separator character (for lists) */
|
|
var $_sEscPolicy; /* escape processing policy */
|
|
var $_sCleanFunc; /* cleaning function for fields */
|
|
|
|
/*
|
|
* Method: CSVFormat ctor
|
|
* Parameters: $c_sep the field separator
|
|
* $c_quote the quote character to use
|
|
* $c_int_sep the internal field separator to use
|
|
* $s_esc_policy escape processing policy to use
|
|
* $s_clean_func a cleaning function
|
|
* Returns: n/a
|
|
* Description:
|
|
* Constructs the object.
|
|
*/
|
|
function __construct($c_sep = ',',$c_quote = '"',$c_int_sep = ';',
|
|
$s_esc_policy = "backslash",$s_clean_func = NULL)
|
|
{
|
|
$this->SetSep($c_sep);
|
|
$this->SetQuote($c_quote);
|
|
$this->SetIntSep($c_int_sep);
|
|
$this->SetEscPolicy($s_esc_policy);
|
|
$this->SetCleanFunc($s_clean_func);
|
|
}
|
|
|
|
/*
|
|
* Method: SetEscPolicy
|
|
* Parameters: $s_esc_policy a string specifying the escape processing
|
|
* policy to use
|
|
* Returns: void
|
|
* Description:
|
|
* Set the escape processing policy.
|
|
*/
|
|
function SetEscPolicy($s_esc_policy)
|
|
{
|
|
switch ($s_esc_policy) {
|
|
default: /* should generate a warning */
|
|
case "backslash":
|
|
$this->_sEscPolicy = "b";
|
|
break;
|
|
case "double":
|
|
$this->_sEscPolicy = "d";
|
|
break;
|
|
case "strip":
|
|
$this->_sEscPolicy = "s";
|
|
break;
|
|
case "conv":
|
|
$this->_sEscPolicy = "c";
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Method: SetSep
|
|
* Parameters: $c_sep the separator character to use
|
|
* Returns: void
|
|
* Description:
|
|
* Set the separator character for between fields.
|
|
*/
|
|
function SetSep($c_sep)
|
|
{
|
|
$this->_cSep = $c_sep;
|
|
}
|
|
|
|
/*
|
|
* Method: SetQuote
|
|
* Parameters: $c_quote the quote character to use
|
|
* Returns: void
|
|
* Description:
|
|
* Set the quote character for quoting fields.
|
|
*/
|
|
function SetQuote($c_quote)
|
|
{
|
|
$this->_cQuote = $c_quote;
|
|
}
|
|
|
|
/*
|
|
* Method: SetIntSep
|
|
* Parameters: $c_int_sep the internal separator character to use
|
|
* Returns: void
|
|
* Description:
|
|
* Set the internal separator character for inside fields.
|
|
*/
|
|
function SetIntSep($c_int_sep)
|
|
{
|
|
$this->_cIntSep = $c_int_sep;
|
|
}
|
|
|
|
/*
|
|
* Method: SetCleanFunc
|
|
* Parameters: $s_clean_func the name of a cleaning function (can be NULL)
|
|
* Returns: void
|
|
* Description:
|
|
* Set the cleaning function for fields.
|
|
*/
|
|
function SetCleanFunc($s_clean_func)
|
|
{
|
|
$this->_sCleanFunc = $s_clean_func;
|
|
}
|
|
|
|
/*
|
|
* Method: _Escape
|
|
* Parameters: $m_value the field value; string or array of strings
|
|
* Returns: mixed the field value escaped according to the
|
|
* escape processing policy
|
|
* Description:
|
|
* Escapes a field value according to the configured requirements.
|
|
*/
|
|
function _Escape($m_value)
|
|
{
|
|
switch ($this->_sEscPolicy) {
|
|
default: /* should generate an error */
|
|
case "b":
|
|
/*
|
|
* 'backslash' escape policy: replace \ with \\ and
|
|
* " with \"
|
|
*/
|
|
$m_value = str_replace("\\","\\\\",$m_value);
|
|
$m_value = str_replace($this->_cQuote,"\\" . $this->_cQuote,
|
|
$m_value);
|
|
break;
|
|
case "d":
|
|
/*
|
|
* 'double' escape policy: replace " with ""
|
|
* This is suitable for Microsoft apps such as Excel
|
|
* and Access. It also meets the specification of
|
|
* RFC4180, though this RFC only specified double
|
|
* quotes whereas we handle any quote character.
|
|
*/
|
|
$m_value = str_replace($this->_cQuote,
|
|
$this->_cQuote . $this->_cQuote,$m_value);
|
|
break;
|
|
case "s":
|
|
/*
|
|
* 'strip' escape policy: strip quotes
|
|
*/
|
|
$m_value = str_replace($this->_cQuote,"",$m_value);
|
|
break;
|
|
case "c":
|
|
/*
|
|
* 'conv' escape policy: convert quotes to the other quotes
|
|
*/
|
|
switch ($this->_cQuote) {
|
|
case '"':
|
|
/*
|
|
* convert double quotes in the data to single quotes
|
|
*/
|
|
$m_value = str_replace("\"","'",$m_value);
|
|
break;
|
|
case '\'':
|
|
/*
|
|
* convert single quotes in the data to double quotes
|
|
*/
|
|
$m_value = str_replace("'","\"",$m_value);
|
|
break;
|
|
default:
|
|
/*
|
|
* otherwise, leave the data unchanged
|
|
*/
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return ($m_value);
|
|
}
|
|
|
|
/*
|
|
* Method: _Format
|
|
* Parameters: $s_value the string value to format
|
|
* $s_format a format specification
|
|
* a list of characters:
|
|
* c CleanValue
|
|
* s force to be a string
|
|
* r remove carriage returns
|
|
* Returns: string the formatted value
|
|
* Description:
|
|
* Formats a value.
|
|
*/
|
|
function _Format($s_value,$s_format = "")
|
|
{
|
|
$s_value = $this->_Escape($s_value);
|
|
$s_prefix = "";
|
|
/*
|
|
* now implement any special formatting to overcome
|
|
* problems with importing
|
|
*/
|
|
$i_len = strlen($s_format);
|
|
for ($ii = 0 ; $ii < $i_len ; $ii++) {
|
|
switch ($s_format[$ii]) {
|
|
case "c":
|
|
/*
|
|
* implement "c" formatting - CleanValue
|
|
*/
|
|
$s_value = CleanValue($s_value);
|
|
break;
|
|
case "r":
|
|
/*
|
|
* implement "r" formatting - remove
|
|
* carriage returns. Useful for Microsoft Excel
|
|
*/
|
|
$s_value = str_replace("\r","",$s_value);
|
|
break;
|
|
case "s":
|
|
/*
|
|
* implement "s" formatting - force
|
|
* a value to be a string (by making it a string
|
|
* formula). Useful for Microsoft Excel and OpenOffice
|
|
* spreadsheet, which don't understand numeric phone
|
|
* numbers, for example.
|
|
*/
|
|
if (strlen($s_value) > 0) {
|
|
$s_prefix = "=";
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ($s_prefix . $this->_cQuote . $s_value . $this->_cQuote);
|
|
}
|
|
|
|
/*
|
|
* Method: _GetColumn
|
|
* Parameters: $s_col_spec a column specification
|
|
* Returns: array the column name and the format specifier, if
|
|
* any
|
|
* Description:
|
|
* Returns the column name and any format specifier.
|
|
*/
|
|
function _GetColumn($s_col_spec)
|
|
{
|
|
$s_format = "";
|
|
if (($i_pos = strpos($s_col_spec,":")) !== false) {
|
|
$s_col_name = trim(substr($s_col_spec,0,$i_pos));
|
|
$s_format = trim(substr($s_col_spec,$i_pos + 1));
|
|
} else {
|
|
$s_col_name = $s_col_spec;
|
|
}
|
|
return (array($s_col_name,$s_format));
|
|
}
|
|
|
|
/*
|
|
* Method: MakeCSVRecord
|
|
* Parameters: $a_column_list a list of column names (field names) to
|
|
* include; can include format specifiers
|
|
* $a_vars raw data array indexed by column name
|
|
* (field name).
|
|
* A data value can be a string or an array
|
|
* of strings.
|
|
* Returns: string the comma-separated value
|
|
* Description:
|
|
* Creates a single CSV record for a list of columns.
|
|
*/
|
|
function MakeCSVRecord($a_column_list,$a_vars)
|
|
{
|
|
$s_rec = "";
|
|
$n_columns = count($a_column_list);
|
|
for ($ii = 0 ; $ii < $n_columns ; $ii++) {
|
|
list($s_col_name,$s_format) = $this->_GetColumn($a_column_list[$ii]);
|
|
/*
|
|
* if a column is specified it must be included, even if there
|
|
* is no data for it.
|
|
*/
|
|
$s_value = GetFieldValue($s_col_name,$a_vars,$this->_cIntSep);
|
|
if (isset($this->_sCleanFunc)) {
|
|
$s_func = $this->_sCleanFunc;
|
|
$s_value = $s_func($s_value);
|
|
}
|
|
|
|
$s_value = $this->_Format($s_value,$s_format);
|
|
if ($ii > 0) /*
|
|
* prepend the separator from the second field onwards
|
|
*/ {
|
|
$s_rec .= $this->_cSep;
|
|
}
|
|
$s_rec .= $s_value;
|
|
}
|
|
return ($s_rec);
|
|
}
|
|
|
|
/*
|
|
* Method: MakeHeading
|
|
* Parameters: $a_column_list a list of column names (field names) to
|
|
* include
|
|
* Returns: string the comma-separated heading record
|
|
* Description:
|
|
* Creates a heading record for the CSV data.
|
|
*/
|
|
function MakeHeading($a_column_list)
|
|
{
|
|
$s_rec = "";
|
|
$n_columns = count($a_column_list);
|
|
for ($ii = 0 ; $ii < $n_columns ; $ii++) {
|
|
list($s_col_name,$s_format) = $this->_GetColumn($a_column_list[$ii]);
|
|
$s_value = $this->_Format($s_col_name);
|
|
if ($ii > 0) /*
|
|
* prepend the separator from the second field onwards
|
|
*/ {
|
|
$s_rec .= $this->_cSep;
|
|
}
|
|
$s_rec .= $s_value;
|
|
}
|
|
return ($s_rec);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Built-in filter. Generates CSV (comma separated values) content from
|
|
* the submitted fields. The special field "filter_fields" determines
|
|
* which fields to include in the CSV content.
|
|
* The following options are support in "filter_options":
|
|
* CSVHeading if set, includes a heading line first with the field names
|
|
* CSVSep specifies a separator character instead of comma
|
|
* CSVIntSep specifies an internal separator character for lists
|
|
* CSVQuote specifies the character to use to quote each column; default
|
|
* is double quotes
|
|
* CSVEscPolicy controls the way quotes are escaped in the data. Supported
|
|
* values are: backslash (the default),double,strip
|
|
* CSVRaw if set, then the fields are recorded as raw values and
|
|
* are *not* cleaned according to FormMail's normal field
|
|
* cleaning process.
|
|
* If the "filter_fields" field does not exist, then the "csvcolumns" field is
|
|
* used instead. If neither exist, then all fields are included along with
|
|
* a Heading line.
|
|
*/
|
|
function BuiltinFilterCSV()
|
|
{
|
|
global $aAllRawValues,$aRawDataValues,$SPECIAL_VALUES;
|
|
|
|
$b_heading = false;
|
|
$a_column_list = array();
|
|
$s_cols = $SPECIAL_VALUES["filter_fields"];
|
|
if (!isset($s_cols) || empty($s_cols) || !is_string($s_cols)) {
|
|
$s_cols = $SPECIAL_VALUES["csvcolumns"];
|
|
if (!isset($s_cols) || empty($s_cols) || !is_string($s_cols)) {
|
|
/*
|
|
* neither filter_fields nor csvcolumns defined - get all columns
|
|
*/
|
|
$s_cols = "";
|
|
/*
|
|
* special case - include these two special fields
|
|
*/
|
|
$a_column_list = array("email","realname");
|
|
/*
|
|
* now include all the data fields
|
|
*/
|
|
$a_column_list = array_merge($a_column_list,
|
|
array_keys($aRawDataValues));
|
|
$b_heading = true;
|
|
}
|
|
}
|
|
if (empty($a_column_list)) {
|
|
$a_column_list = TrimArray(explode(",",$s_cols));
|
|
}
|
|
|
|
$csv_format = new CSVFormat();
|
|
|
|
/*
|
|
* get the various options and set them
|
|
*/
|
|
$m_temp = GetFilterOption("CSVQuote");
|
|
if (isset($m_temp)) {
|
|
$csv_format->SetQuote($m_temp);
|
|
}
|
|
$m_temp = GetFilterOption("CSVSep");
|
|
if (isset($m_temp)) {
|
|
$csv_format->SetSep($m_temp);
|
|
}
|
|
$m_temp = GetFilterOption("CSVIntSep");
|
|
if (isset($m_temp)) {
|
|
$csv_format->SetIntSep($m_temp);
|
|
}
|
|
$m_temp = GetFilterOption("CSVEscPolicy");
|
|
if (isset($m_temp)) {
|
|
$csv_format->SetEscPolicy($m_temp);
|
|
}
|
|
$m_temp = GetFilterOption("CSVHeading");
|
|
if (isset($m_temp)) {
|
|
$b_heading = true;
|
|
}
|
|
|
|
/*
|
|
* clean fields unless CSVRaw is specified
|
|
*/
|
|
$m_temp = GetFilterOption("CSVRaw");
|
|
if (!isset($m_temp)) {
|
|
$csv_format->SetCleanFunc(function ($m_value) {
|
|
return CleanValue($m_value);
|
|
});
|
|
}
|
|
|
|
$s_csv = $csv_format->MakeCSVRecord($a_column_list,$aAllRawValues);
|
|
|
|
if ($b_heading) {
|
|
$s_head = $csv_format->MakeHeading($a_column_list);
|
|
/*
|
|
* return the heading and the record with $CSVLINE as record separator
|
|
*/
|
|
return ($s_head . Settings::get('CSVLINE') . $s_csv . Settings::get('CSVLINE'));
|
|
} else {
|
|
/*
|
|
* return this record with $CSVLINE appended
|
|
*/
|
|
return ($s_csv . Settings::get('CSVLINE'));
|
|
}
|
|
}
|
|
|
|
$aSubstituteErrors = array();
|
|
$SubstituteFields = NULL;
|
|
$sSubstituteMissing = NULL;
|
|
|
|
//
|
|
// Run htmlspecialchars on every value in an array.
|
|
//
|
|
function ArrayHTMLSpecialChars($a_list)
|
|
{
|
|
$a_new = array();
|
|
foreach ($a_list as $m_key => $m_value) {
|
|
if (is_array($m_value)) {
|
|
$a_new[$m_key] = ArrayHTMLSpecialChars($m_value);
|
|
} else {
|
|
$a_new[$m_key] = htmlspecialchars($m_value);
|
|
}
|
|
}
|
|
return ($a_new);
|
|
}
|
|
|
|
//
|
|
// Truncate a value based on the specified maximums.
|
|
//
|
|
function Truncate($s_value,$n_max_chars,$n_max_lines)
|
|
{
|
|
if ($n_max_lines > 0) {
|
|
$a_lines = explode("\n",$s_value);
|
|
if (count($a_lines) > $n_max_lines) {
|
|
$a_lines = array_slice($a_lines,0,$n_max_lines);
|
|
$s_value = implode("\n",$a_lines);
|
|
$s_value .= "...";
|
|
}
|
|
}
|
|
if ($n_max_chars > 0) {
|
|
$a_lines = explode("\n",$s_value);
|
|
for ($ii = 0 ; $ii < count($a_lines) ; $ii++) {
|
|
$n_len = strlen($a_lines[$ii]);
|
|
$s_eol = "";
|
|
if (substr($a_lines[$ii],-1) == "\n") {
|
|
$n_len--;
|
|
$s_eol = "\n";
|
|
}
|
|
if ($n_len > $n_max_chars) {
|
|
$a_lines[$ii] = substr($a_lines[$ii],0,$n_max_chars) . "..." . $s_eol;
|
|
}
|
|
}
|
|
$s_value = implode("\n",$a_lines);
|
|
}
|
|
return ($s_value);
|
|
}
|
|
|
|
//
|
|
// Worker function for SubstituteValue and SubstituteValueForPage.
|
|
// Returns the value of the matched variable name.
|
|
// Variables are searched for in the global $SubstituteFields.
|
|
// If no such variable exists, an error is reported or the given
|
|
// replacement string is used.
|
|
// Errors are stored in the global $aSubstituteErrors.
|
|
//
|
|
function SubstituteValueWorker($a_matches,$s_repl,$b_html = true)
|
|
{
|
|
/**
|
|
* @global FieldManager $SubstituteFields
|
|
*/
|
|
global $aSubstituteErrors,$SubstituteFields,$SPECIAL_VALUES;
|
|
|
|
$b_insert_br = true; // option to put "<br />" tags before newlines in HTML templates
|
|
$n_max_chars = 0;
|
|
$n_max_lines = 0;
|
|
$s_list_sep = $SPECIAL_VALUES['template_list_sep'];
|
|
$b_text_subs = false;
|
|
|
|
$s_name = $a_matches[0];
|
|
assert(strlen($s_name) > 1 && $s_name[0] == '$');
|
|
$s_name = substr($s_name,1);
|
|
if (($i_len = strlen($s_name)) > 0 && $s_name[0] == '{') {
|
|
assert($s_name[$i_len - 1] == '}');
|
|
$s_name = substr($s_name,1,-1);
|
|
//
|
|
// grab any processing options
|
|
//
|
|
$a_args = explode(":",$s_name);
|
|
$s_name = $a_args[0];
|
|
if (($n_args = count($a_args)) > 1) {
|
|
for ($ii = 1 ; $ii < $n_args ; $ii++) {
|
|
//
|
|
// some options are followed by =X
|
|
// where X is a value
|
|
//
|
|
$s_param = "";
|
|
if (($i_pos = strpos($a_args[$ii],'=')) !== false) {
|
|
$s_param = substr($a_args[$ii],$i_pos + 1);
|
|
$s_opt = substr($a_args[$ii],0,$i_pos);
|
|
} else {
|
|
$s_opt = $a_args[$ii];
|
|
}
|
|
switch ($s_opt) {
|
|
case "nobr":
|
|
$b_insert_br = false;
|
|
break;
|
|
case "chars":
|
|
if ($s_param !== "") {
|
|
$n_max_chars = (int)$s_param;
|
|
}
|
|
break;
|
|
case "lines":
|
|
if ($s_param !== "") {
|
|
$n_max_lines = (int)$s_param;
|
|
}
|
|
break;
|
|
case "sep":
|
|
if ($s_param !== "") {
|
|
$s_list_sep = $s_param;
|
|
}
|
|
break;
|
|
case "subs":
|
|
$b_text_subs = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$s_value = "";
|
|
$s_mesg = "";
|
|
if ($SubstituteFields->IsFieldSet($s_name) &&
|
|
!$SubstituteFields->TestFieldEmpty($s_name,$s_mesg)
|
|
) {
|
|
if ($b_html)
|
|
//
|
|
// Up to and including version 8.24, the code used
|
|
// htmlspecialchars. Version 8.28 caused UTF-8 template
|
|
// processing to break, because it started using htmlentities
|
|
// without specifying the charset.
|
|
//
|
|
{
|
|
$s_value = $SubstituteFields->GetSafeFieldValue($s_name,$b_text_subs,$s_list_sep);
|
|
} else {
|
|
$s_value = $SubstituteFields->GetFieldValue($s_name,$s_list_sep);
|
|
}
|
|
$s_value = Truncate($s_value,$n_max_chars,$n_max_lines);
|
|
if ($b_html && $b_insert_br)
|
|
//
|
|
// Insert HTML line breaks before newlines.
|
|
//
|
|
{
|
|
$s_value = nl2br($s_value);
|
|
}
|
|
} elseif (isset($SPECIAL_VALUES[$s_name])) {
|
|
$s_value = $b_html ?
|
|
htmlspecialchars((string)$SPECIAL_VALUES[$s_name]) :
|
|
(string)$SPECIAL_VALUES[$s_name];
|
|
$s_value = Truncate($s_value,$n_max_chars,$n_max_lines);
|
|
} elseif (isset($s_repl))
|
|
//
|
|
// If a replacement value has been specified use it, and
|
|
// don't call htmlspecialchars. This allows the use
|
|
// of HTML tags in a replacement string.
|
|
//
|
|
{
|
|
$s_value = $s_repl;
|
|
} else {
|
|
$s_value = "";
|
|
}
|
|
return ($s_value);
|
|
}
|
|
|
|
//
|
|
// Callback function for preg_replace_callback. Returns the value
|
|
// of the matched variable name.
|
|
// Variables are searched for in the global $SubstituteFields.
|
|
// If no such variable exists, an error is reported or an special
|
|
// replacement string is used.
|
|
// Errors are stored in the global $aSubstituteErrors.
|
|
//
|
|
/** @noinspection PhpUnused */
|
|
function SubstituteValue($a_matches)
|
|
{
|
|
global $sSubstituteMissing;
|
|
|
|
return (SubstituteValueWorker($a_matches,$sSubstituteMissing));
|
|
}
|
|
|
|
//
|
|
// Callback function for preg_replace_callback. Returns the value
|
|
// of the matched variable name.
|
|
// Variables are searched for in the global $SubstituteFields.
|
|
// If no such variable exists, an error is reported or an special
|
|
// replacement string is used.
|
|
// Errors are stored in the global $aSubstituteErrors.
|
|
//
|
|
/** @noinspection PhpUnused */
|
|
function SubstituteValuePlain($a_matches)
|
|
{
|
|
global $sSubstituteMissing;
|
|
|
|
return (SubstituteValueWorker($a_matches,$sSubstituteMissing,false));
|
|
}
|
|
|
|
//
|
|
// Callback function for preg_replace_callback. Returns the value
|
|
// of the matched variable name.
|
|
// Variables are searched for in the global $SubstituteFields.
|
|
// If no such variable exists, the empty string is substituted.
|
|
// Errors are stored in the global $aSubstituteErrors.
|
|
//
|
|
/** @noinspection PhpUnused */
|
|
function SubstituteValueForPage($a_matches)
|
|
{
|
|
return (SubstituteValueWorker($a_matches,""));
|
|
}
|
|
|
|
//
|
|
// Callback function for preg_replace_callback. Returns
|
|
// exactly what was matched.
|
|
//
|
|
/** @noinspection PhpUnused */
|
|
function SubstituteValueDummy($a_matches)
|
|
{
|
|
return ($a_matches[0]);
|
|
}
|
|
|
|
//
|
|
// Process the given HTML template and fill the fields.
|
|
//
|
|
function DoProcessTemplate($s_dir,$s_url,$s_template,&$a_lines,
|
|
$a_values,$s_missing,$s_subs_func)
|
|
{
|
|
global $aSubstituteErrors,$SubstituteFields,$sSubstituteMissing,$aFileVars;
|
|
|
|
if (($a_template_lines = LoadTemplate($s_template,$s_dir,
|
|
$s_url,true)) === false
|
|
) {
|
|
return (false);
|
|
}
|
|
FMDebug("Template '$s_template' contains " . count($a_template_lines) . " lines");
|
|
|
|
$b_ok = true;
|
|
//
|
|
// initialize the errors list
|
|
//
|
|
$aSubstituteErrors = array();
|
|
//
|
|
// initialize the values
|
|
//
|
|
$SubstituteFields = new FieldManager($a_values,$aFileVars);
|
|
$sSubstituteMissing = $s_missing;
|
|
|
|
foreach ($a_template_lines as $s_line) {
|
|
//
|
|
// search for words in these forms:
|
|
// $word
|
|
// ${word:options}
|
|
// where word begins with an alphabetic character and
|
|
// consists of alphanumeric and underscore
|
|
//
|
|
$a_lines[] = preg_replace_callback('/\$[a-z][a-z0-9_]*|\$\{[a-z][a-z0-9_]*(:[^\}]*)*\}/i',
|
|
$s_subs_func,$s_line);
|
|
}
|
|
|
|
FMDebug("DoProcessTemplate error count=" . count($aSubstituteErrors));
|
|
if (count($aSubstituteErrors) != 0) {
|
|
SendAlert(GetMessage(MSG_TEMPLATE_ERRORS,array("NAME" => $s_template)) .
|
|
implode("\n",$aSubstituteErrors));
|
|
$b_ok = false;
|
|
}
|
|
global $FMCTemplProc;
|
|
|
|
//
|
|
// note that it's possible for an old version of FormMail Computation
|
|
// module to get loaded which doesn't provide FMCTemplProc
|
|
//
|
|
if ($b_ok && Settings::get('ADVANCED_TEMPLATES') && isset($FMCTemplProc)) {
|
|
$s_buf = implode("\n",$a_lines);
|
|
/*
|
|
* Look for a string that means we can skip advanced template
|
|
* processing on this template. The string is "FormMail-Basic-Template".
|
|
*/
|
|
if (strpos($s_buf,"FormMail-Basic-Template") === FALSE) {
|
|
$a_mesgs = array();
|
|
/*foreach ($a_lines as $i_lno=>$s_line)
|
|
if (strpos($s_line,"\n") !== false)
|
|
SendAlert("Line $i_lno has a newline");*/
|
|
set_time_limit(60);
|
|
if (($m_result = $FMCTemplProc->Process($s_buf,$a_mesgs)) === false) {
|
|
$s_msgs = "\n";
|
|
foreach ($a_mesgs as $a_msg) {
|
|
$s_msgs .= "Line " . $a_msg["LINE"];
|
|
$s_msgs .= ", position " . $a_msg["CHAR"] . ": ";
|
|
$s_msgs .= $a_msg["MSG"] . "\n";
|
|
}
|
|
Error("fmadvtemplates",GetMessage(MSG_TEMPL_PROC,
|
|
array("ERRORS" => $s_msgs)),false,false);
|
|
$b_ok = false;
|
|
} else {
|
|
/*foreach ($m_result as $i_lno=>$s_line)
|
|
if (($nn = substr_count($s_line,"\n")) > 1)
|
|
SendAlert("Result line $i_lno has $nn newlines");*/
|
|
//
|
|
// strip the new lines
|
|
//
|
|
$a_lines = explode("\n",implode("",$m_result));
|
|
}
|
|
$a_alerts = $FMCTemplProc->GetAlerts();
|
|
if (count($a_alerts) > 0) {
|
|
SendAlert(GetMessage(MSG_TEMPL_ALERT,
|
|
array("ALERTS" => implode("\n",$a_alerts))));
|
|
}
|
|
$a_debug = $FMCTemplProc->GetDebug();
|
|
if (count($a_debug) > 0) {
|
|
SendAlert(GetMessage(MSG_TEMPL_DEBUG,
|
|
array("DEBUG" => implode("\n",$a_debug))));
|
|
}
|
|
}
|
|
}
|
|
|
|
return ($b_ok);
|
|
}
|
|
|
|
//
|
|
// Process the given HTML template and fill the fields.
|
|
//
|
|
function ProcessTemplate($s_template,&$a_lines,$a_values,$s_missing = NULL,
|
|
$s_subs_func = 'SubstituteValue')
|
|
{
|
|
|
|
if (Settings::isEmpty('TEMPLATEDIR') && Settings::isEmpty('TEMPLATEURL')) {
|
|
SendAlert(GetMessage(MSG_TEMPLATES));
|
|
return (false);
|
|
}
|
|
return (DoProcessTemplate(Settings::get('TEMPLATEDIR'),Settings::get('TEMPLATEURL'),$s_template,$a_lines,
|
|
$a_values,$s_missing,$s_subs_func));
|
|
}
|
|
|
|
//
|
|
// Output the given HTML template after filling in the fields.
|
|
//
|
|
function OutputTemplate($s_template,$a_values)
|
|
{
|
|
$a_lines = array();
|
|
if (!ProcessTemplate($s_template,$a_lines,$a_values,"",'SubstituteValueForPage')) {
|
|
Error("template_failed",GetMessage(MSG_TEMPLATE_FAILED,
|
|
array("NAME" => $s_template)),false,false);
|
|
} else {
|
|
for ($ii = 0 ; $ii < count($a_lines) ; $ii++) {
|
|
echo $a_lines[$ii] . "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// This function handles input type fields.
|
|
//
|
|
function RemoveFieldValue($s_name,$s_buf)
|
|
{
|
|
//
|
|
// we search for:
|
|
// <input ... name="thename" ... >
|
|
// and change it to:
|
|
// <!-- disabled by FormMail: input ... name="thename" ... -->
|
|
//
|
|
|
|
// handle name attribute first
|
|
$s_pat = '/<(\s*input[^>]*name="';
|
|
$s_pat .= preg_quote($s_name,"/");
|
|
$s_pat .= '"[^>]*)>';
|
|
$s_pat .= '/ims';
|
|
$s_buf = preg_replace($s_pat,'<!-- disabled by FormMail: $1 -->',$s_buf);
|
|
|
|
return ($s_buf);
|
|
}
|
|
|
|
//
|
|
// Quote special characters in a replacement expression
|
|
// for preg_replace.
|
|
//
|
|
function RegReplaceQuote($s_value)
|
|
{
|
|
return (str_replace('$','\\$',str_replace('\\','\\\\',$s_value)));
|
|
}
|
|
|
|
//
|
|
// This function handles input type "text" and "password"
|
|
//
|
|
function FixInputText($s_name,$s_value,$s_buf)
|
|
{
|
|
//
|
|
// we search for:
|
|
// <input type="text" name="thename"...
|
|
// and change it to:
|
|
// <input type="text" name="thename" value="thevalue" ...
|
|
//
|
|
// Note that the value attribute must appear *after* the
|
|
// type and name attributes.
|
|
//
|
|
|
|
//
|
|
// first strip any current value attribute for the field
|
|
//
|
|
|
|
//
|
|
// (?:) is a grouping subpattern that does no capturing
|
|
//
|
|
|
|
// handle type attribute first
|
|
$s_pat = '/(<\s*input[^>]*type="(?:text|password|email|tel|search|number|date|month|datetime|datetime-local|time|week|range|url|color)"[^>]*name="';
|
|
$s_pat .= preg_quote($s_name,"/");
|
|
$s_pat .= '"[^>]*)(value="[^"]*")([^>]*?)(\s*\/\s*)?>';
|
|
$s_pat .= '/ims';
|
|
$s_buf = preg_replace($s_pat,'$1$3$4>',$s_buf);
|
|
|
|
// handle name attribute first
|
|
$s_pat = '/(<\s*input[^>]*name="';
|
|
$s_pat .= preg_quote($s_name,"/");
|
|
$s_pat .= '"[^>]*type="(?:text|password|email|tel|search|number|date|month|datetime|datetime-local|time|week|range|url|color)"[^>]*)(value="[^"]*")([^>]*?)(\s*\/\s*)?>';
|
|
$s_pat .= '/ims';
|
|
$s_buf = preg_replace($s_pat,'$1$3$4>',$s_buf);
|
|
|
|
//
|
|
// now add in the new value
|
|
//
|
|
$s_repl = '$1 value="' . htmlspecialchars(RegReplaceQuote($s_value)) . '" $2>';
|
|
|
|
// handle type attribute first
|
|
$s_pat = '/(<\s*input[^>]*type="(?:text|password|email|tel|search|number|date|month|datetime|datetime-local|time|week|range|url|color)"[^>]*name="';
|
|
$s_pat .= preg_quote($s_name,"/");
|
|
$s_pat .= '"[^>]*?)(\s*\/\s*)?>';
|
|
$s_pat .= '/ims';
|
|
$s_buf = preg_replace($s_pat,$s_repl,$s_buf);
|
|
|
|
// handle name attribute first
|
|
$s_pat = '/(<\s*input[^>]*name="';
|
|
$s_pat .= preg_quote($s_name,"/");
|
|
$s_pat .= '"[^>]*type="(?:text|password|email|tel|search|number|date|month|datetime|datetime-local|time|week|range|url|color)"[^>]*?)(\s*\/\s*)?>';
|
|
$s_pat .= '/ims';
|
|
$s_buf = preg_replace($s_pat,$s_repl,$s_buf);
|
|
|
|
return ($s_buf);
|
|
}
|
|
|
|
//
|
|
// This function handles textareas.
|
|
//
|
|
function FixTextArea($s_name,$s_value,$s_buf)
|
|
{
|
|
//
|
|
// we search for:
|
|
// <textarea name="thename"...>value</textarea>
|
|
// and change it to:
|
|
// <textarea name="thename"...>new value</textarea>
|
|
//
|
|
|
|
$s_pat = '/(<\s*textarea[^>]*name="';
|
|
$s_pat .= preg_quote($s_name,"/");
|
|
$s_pat .= '"[^>]*)>.*?<\s*\/\s*textarea\s*>';
|
|
$s_pat .= '/ims';
|
|
//
|
|
// we exclude the closing '>' from the match above so that
|
|
// we can put it below. We need to do this so that the replacement
|
|
// string is not faulty if the value begins with a digit:
|
|
// $19 Some Street
|
|
//
|
|
$s_repl = '$1>' . htmlspecialchars(RegReplaceQuote($s_value)) . '</textarea>';
|
|
$s_buf = preg_replace($s_pat,$s_repl,$s_buf);
|
|
|
|
return ($s_buf);
|
|
}
|
|
|
|
//
|
|
// This function handles radio buttons and non-array checkboxes.
|
|
//
|
|
function FixButton($s_name,$s_value,$s_buf)
|
|
{
|
|
//
|
|
// we search for:
|
|
// <input type="radio" name="thename" value="thevalue" ...
|
|
// and change it to:
|
|
// <input type="radio" name="thename" value="thevalue" checked="checked"
|
|
//
|
|
// Note that the value attribute must appear *after* the
|
|
// type and name attributes.
|
|
//
|
|
|
|
//
|
|
// first strip any current checked attributes
|
|
//
|
|
|
|
//
|
|
// (?:) is a grouping subpattern that does no capturing
|
|
//
|
|
|
|
// handle type attribute first
|
|
// match: input tag with type 'radio' or 'checkbox' with attribute
|
|
// 'checked' or 'checked="checked"'
|
|
// <A NAME="PatternInfo">
|
|
// [^>]*?[^"\w] matches up to a word boundary starting with
|
|
// 'checked' but not '"checked'
|
|
// (="checked"|(?=[^"\w]))? this matches:
|
|
// nothing
|
|
// ="checked"
|
|
// any character except a word character or " (without
|
|
// consuming it)
|
|
//
|
|
$s_pat = '/(<\s*input[^>]*type="(?:radio|checkbox)"[^>]*name="';
|
|
$s_pat .= preg_quote($s_name,"/");
|
|
$s_pat .= '"[^>]*?[^"\w])checked(="checked"|(?=[^"\w]))?([^>]*?)(\s*\/\s*)?>';
|
|
$s_pat .= '/ims';
|
|
$s_buf = preg_replace($s_pat,'$1$3$4>',$s_buf);
|
|
|
|
// handle name attribute first
|
|
$s_pat = '/(<\s*input[^>]*name="';
|
|
$s_pat .= preg_quote($s_name,"/");
|
|
$s_pat .= '"[^>]*type="(?:radio|checkbox)"[^>]*?[^"\w])checked(="checked"|(?=[^"\w]))?([^>]*?)(\s*\/\s*)?>';
|
|
$s_pat .= '/ims';
|
|
$s_buf = preg_replace($s_pat,'$1$3$4>',$s_buf);
|
|
|
|
// handle type attribute first
|
|
$s_pat = '/(<\s*input[^>]*type="(?:radio|checkbox)"[^>]*name="';
|
|
$s_pat .= preg_quote($s_name,"/");
|
|
$s_pat .= '"[^>]*value="';
|
|
$s_pat .= preg_quote($s_value,"/");
|
|
$s_pat .= '")([^>]*?)(\s*\/\s*)?>';
|
|
$s_pat .= '/ims';
|
|
$s_buf = preg_replace($s_pat,'$1$2 checked="checked" $3>',$s_buf);
|
|
|
|
// handle name attribute first
|
|
$s_pat = '/(<\s*input[^>]*name="';
|
|
$s_pat .= preg_quote($s_name,"/");
|
|
$s_pat .= '"[^>]*type="(?:radio|checkbox)"[^>]*value="';
|
|
$s_pat .= preg_quote($s_value,"/");
|
|
$s_pat .= '")([^>]*?)(\s*\/\s*)?>';
|
|
$s_pat .= '/ims';
|
|
$s_buf = preg_replace($s_pat,'$1$2 checked="checked" $3>',$s_buf);
|
|
|
|
return ($s_buf);
|
|
}
|
|
|
|
//
|
|
// This function handles checkboxes as an array of values.
|
|
//
|
|
function FixCheckboxes($s_name,$a_values,$s_buf)
|
|
{
|
|
//global $aDebug;
|
|
|
|
//
|
|
// we search for:
|
|
// <input type="checkbox" name="thename" value="thevalue" ...
|
|
// and change it to:
|
|
// <input type="checkbox" name="thename" value="thevalue" checked
|
|
//
|
|
// Note that the value attribute must appear *after* the
|
|
// type and name attributes.
|
|
//
|
|
|
|
//
|
|
// first strip any current checked attributes
|
|
//
|
|
//$aDebug[] = "FixCheckboxes: Name='$s_name'";
|
|
|
|
// handle type attribute first
|
|
// see <A HREF="fmbadhandler.php#PatternInfo">
|
|
$s_pat = '/(<\s*input[^>]*type="checkbox"[^>]*name="';
|
|
$s_pat .= preg_quote($s_name,"/");
|
|
$s_pat .= '\[]"[^>]*?[^"\w])checked(="checked"|(?=[^"\w]))?([^>]*?)(\s*\/\s*)?>';
|
|
$s_pat .= '/ims';
|
|
$s_buf = preg_replace($s_pat,'$1$3$4>',$s_buf);
|
|
|
|
// handle name attribute first
|
|
$s_pat = '/(<\s*input[^>]*name="';
|
|
$s_pat .= preg_quote($s_name,"/");
|
|
$s_pat .= '\[]"[^>]*type="checkbox"[^>]*?[^"\w])checked(="checked"|(?=[^"\w]))?([^>]*?)(\s*\/\s*)?>';
|
|
$s_pat .= '/ims';
|
|
$s_buf = preg_replace($s_pat,'$1$3$4>',$s_buf);
|
|
|
|
foreach ($a_values as $s_value) {
|
|
// handle type attribute first
|
|
$s_pat = '/(<\s*input[^>]*type="checkbox"[^>]*name="';
|
|
$s_pat .= preg_quote($s_name,"/");
|
|
$s_pat .= '\[\]"[^>]*value="';
|
|
$s_pat .= preg_quote($s_value,"/");
|
|
$s_pat .= '")([^>]*?)(\s*\/\s*)?>';
|
|
$s_pat .= '/ims';
|
|
$s_buf = preg_replace($s_pat,'$1$2 checked="checked"$3>',$s_buf);
|
|
//$aDebug[] = "Name='$s_name', pat='$s_pat'";
|
|
|
|
// handle name attribute first
|
|
$s_pat = '/(<\s*input[^>]*name="';
|
|
$s_pat .= preg_quote($s_name,"/");
|
|
$s_pat .= '\[\]"[^>]*type="checkbox"[^>]*value="';
|
|
$s_pat .= preg_quote($s_value,"/");
|
|
$s_pat .= '")([^>]*?)(\s*\/\s*)?>';
|
|
$s_pat .= '/ims';
|
|
$s_buf = preg_replace($s_pat,'$1$2 checked="checked">',$s_buf);
|
|
}
|
|
return ($s_buf);
|
|
}
|
|
|
|
//
|
|
// This function handles selects.
|
|
//
|
|
function FixSelect($s_name,$s_value,$s_buf)
|
|
{
|
|
//
|
|
// we search for:
|
|
// <select name="thename"...>
|
|
// <option value="thevalue">...</option>
|
|
// </select>
|
|
//
|
|
|
|
$s_pat = '/(<\s*select[^>]*name="';
|
|
$s_pat .= preg_quote($s_name,"/");
|
|
$s_pat .= '".*?<\s*option[^>]*value="';
|
|
$s_pat .= preg_quote($s_value,"/");
|
|
$s_pat .= '"[^>]*)>';
|
|
$s_pat .= '/ims';
|
|
$s_repl = '$1 selected="selected">';
|
|
// echo "<p>pat: ".htmlspecialchars($s_pat);
|
|
$s_buf = preg_replace($s_pat,$s_repl,$s_buf);
|
|
|
|
return ($s_buf);
|
|
}
|
|
|
|
//
|
|
// This function handles multiple selects.
|
|
//
|
|
function FixMultiSelect($s_name,$a_values,$s_buf)
|
|
{
|
|
//
|
|
// we search for:
|
|
// <select name="thename"...>
|
|
// <option value="thevalue">...</option>
|
|
// </select>
|
|
//
|
|
|
|
foreach ($a_values as $s_value) {
|
|
$s_pat = '/(<\s*select[^>]*name="';
|
|
$s_pat .= preg_quote($s_name,"/");
|
|
$s_pat .= '\[\]".*?<\s*option[^>]*value="';
|
|
$s_pat .= preg_quote($s_value,"/");
|
|
$s_pat .= '"[^>]*)>';
|
|
$s_pat .= '/ims';
|
|
$s_repl = '$1 selected="selected">';
|
|
// echo "<p>pat: ".htmlspecialchars($s_pat);
|
|
$s_buf = preg_replace($s_pat,$s_repl,$s_buf);
|
|
}
|
|
return ($s_buf);
|
|
}
|
|
|
|
//
|
|
// This function unchecks all checkboxes and select options.
|
|
//
|
|
function UnCheckStuff($s_buf)
|
|
{
|
|
global $php_errormsg;
|
|
|
|
//
|
|
// we search for:
|
|
// <input type="checkbox" ... checked
|
|
// and remove "checked" (checked="checked" is OK too)
|
|
//
|
|
// Note that the check attribute must appear *after* the
|
|
// type attribute.
|
|
// see <A HREF="fmbadhandler.php#PatternInfo">
|
|
//
|
|
|
|
$s_pat = '/(<\s*input[^>]*type="checkbox"[^>]*?[^"\w])checked(="checked"|(?=[^"\w]))?([^>]*?)(\s*\/\s*)?>';
|
|
$s_pat .= '/ims';
|
|
$s_buf = preg_replace($s_pat,'$1$3$4>',$s_buf);
|
|
|
|
//
|
|
// we search for:
|
|
// <option... selected
|
|
// and remove "selected" (selected="selected" is OK too)
|
|
// see <A HREF="fmbadhandler.php#PatternInfo">
|
|
//
|
|
|
|
$s_pat = '/(<\s*option[^>]*?[^"\w])selected(="selected"|(?=[^"\w]))?([^>]*)>';
|
|
$s_pat .= '/ims';
|
|
$s_buf = preg_replace($s_pat,'$1$3>',$s_buf);
|
|
|
|
return ($s_buf);
|
|
}
|
|
|
|
//
|
|
// Add the user agent to the url as a parameter called USER_AGENT.
|
|
// This allows dynamic web sites to know what the user's browser is.
|
|
//
|
|
function AddUserAgent($s_url)
|
|
{
|
|
global $aServerVars,$aGetVars;
|
|
|
|
//
|
|
// check if the USER_AGENT has been passed as a URL parameter,
|
|
// if so, use it
|
|
//
|
|
$s_agent = "";
|
|
if (isset($aGetVars['USER_AGENT']) && $aGetVars['USER_AGENT'] !== "") {
|
|
$s_agent = $aGetVars['USER_AGENT'];
|
|
//
|
|
// check for URL encoding, and if not, then encode it
|
|
//
|
|
if (!IsURLEncoded($s_agent)) {
|
|
$s_agent = urlencode($s_agent);
|
|
}
|
|
} elseif (isset($aServerVars['HTTP_USER_AGENT'])) {
|
|
$s_agent = urlencode($aServerVars['HTTP_USER_AGENT']);
|
|
}
|
|
|
|
if ($s_agent !== "") {
|
|
return (AddURLParams($s_url,"USER_AGENT=$s_agent",false));
|
|
} else {
|
|
return ($s_url);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Try to determine if the given string is already URL-encoded
|
|
//
|
|
function IsURLEncoded($s_str)
|
|
{
|
|
//
|
|
// the only non-alphanumeric characters we'd expect
|
|
// to see are defined as safe or extra in RFC 1738:
|
|
// safe = "$" | "-" | "_" | "." | "+"
|
|
// extra = "!" | "*" | "'" | "(" | ")" | ","
|
|
// plus the encoding character %
|
|
//
|
|
if (preg_match('/[^a-z0-9$_.+!*\'(),%-]/i',$s_str,$a_matches)) {
|
|
FMDebug("IsURLEncoded: '$s_str' matched '" . $a_matches[0] . "' and is therefore not URL-encoded");
|
|
return (false);
|
|
}
|
|
return (true);
|
|
}
|
|
|
|
//
|
|
// Sets previous values in a form.
|
|
//
|
|
function SetPreviousValues($s_form_buf,$a_values,$a_strip = array())
|
|
{
|
|
//
|
|
// Uncheck any checkboxes and select options
|
|
//
|
|
$s_form_buf = UnCheckStuff($s_form_buf);
|
|
foreach ($a_values as $s_name => $m_value) {
|
|
if (is_array($m_value)) {
|
|
//
|
|
// note that if no values are selected for a field,
|
|
// then we will never get here for that field
|
|
//
|
|
$s_form_buf = FixCheckboxes($s_name,$m_value,$s_form_buf);
|
|
$s_form_buf = FixMultiSelect($s_name,$m_value,$s_form_buf);
|
|
} else {
|
|
//
|
|
// Fix the field if it's an input type "text" or "password".
|
|
//
|
|
$s_form_buf = FixInputText($s_name,$m_value,$s_form_buf);
|
|
//
|
|
// Fix the field if it's radio button.
|
|
//
|
|
$s_form_buf = FixButton($s_name,$m_value,$s_form_buf);
|
|
//
|
|
// Fix the field if it's a "textarea".
|
|
//
|
|
$s_form_buf = FixTextArea($s_name,$m_value,$s_form_buf);
|
|
//
|
|
// Fix the field if it's a "select".
|
|
//
|
|
$s_form_buf = FixSelect($s_name,$m_value,$s_form_buf);
|
|
}
|
|
}
|
|
//
|
|
// Now strip particular field values.
|
|
//
|
|
foreach ($a_strip as $s_name) {
|
|
$s_form_buf = RemoveFieldValue($s_name,$s_form_buf);
|
|
}
|
|
return ($s_form_buf);
|
|
}
|
|
|
|
//
|
|
// Open a URL, do value substitutions, and send to browser.
|
|
// The a_strip array provides a list of fields (usually
|
|
// hidden fields) to remove from the form (their values are
|
|
// set to empty).
|
|
//
|
|
function ProcessReturnToForm($s_url,$a_values,$a_strip = array())
|
|
{
|
|
global $php_errormsg;
|
|
|
|
//
|
|
// read the original form, and modify it to provide values
|
|
// for the fields
|
|
//
|
|
if (!CheckValidURL($s_url)) {
|
|
Error("invalid_url",GetMessage(MSG_RETURN_URL_INVALID,
|
|
array("URL" => $s_url)),false,false);
|
|
}
|
|
|
|
$s_form_url = AddUserAgent($s_url);
|
|
$s_error = "";
|
|
$s_form_buf = GetURL($s_form_url,$s_error);
|
|
if ($s_form_buf === false) {
|
|
Error("invalid_url",
|
|
GetMessage(MSG_OPEN_URL,
|
|
array("URL" => $s_form_url,
|
|
"ERROR" => $s_error .
|
|
": " .
|
|
(isset($php_errormsg) ?
|
|
$php_errormsg : "")
|
|
)),
|
|
false,
|
|
false);
|
|
}
|
|
|
|
//
|
|
// Next, we replace or set actual field values.
|
|
//
|
|
echo SetPreviousValues($s_form_buf,$a_values,$a_strip);
|
|
}
|
|
|
|
//
|
|
// To return the URL for returning to a particular multi-page form URL.
|
|
//
|
|
function GetReturnLink($s_this_script,$i_form_index)
|
|
{
|
|
global $aServerVars;
|
|
|
|
if (!CheckValidURL($s_this_script)) {
|
|
Error("not_valid_url",GetMessage(MSG_RETURN_URL_INVALID,
|
|
array("URL" => $s_this_script)),false,false);
|
|
}
|
|
|
|
$a_params = array();
|
|
$a_params[] = "return=$i_form_index";
|
|
if (isset($aServerVars["QUERY_STRING"])) {
|
|
$a_params[] = $aServerVars["QUERY_STRING"];
|
|
}
|
|
$a_params[] = session_name() . "=" . session_id();
|
|
return (AddURLParams($s_this_script,$a_params));
|
|
}
|
|
|
|
//
|
|
// Process a multi-page form template.
|
|
//
|
|
function ProcessMultiFormTemplate($s_template,$a_values,&$a_lines)
|
|
{
|
|
global $SPECIAL_VALUES;
|
|
|
|
if (Settings::isEmpty('MULTIFORMDIR') && Settings::isEmpty('MULTIFORMURL')) {
|
|
SendAlert(GetMessage(MSG_MULTIFORM));
|
|
return (false);
|
|
}
|
|
//
|
|
// create the "this_form_url" field
|
|
//
|
|
|
|
$i_index = GetSession("FormIndex");
|
|
$a_list = GetSession("FormList");
|
|
$a_values["this_form_url"] = $a_list[$i_index]["URL"];
|
|
//
|
|
// get the persistent file fields
|
|
//
|
|
$a_values = GetSavedFileNames($a_values);
|
|
//$a_values["prev_form"] = GetReturnLink($SPECIAL_VALUES["this_form"]);
|
|
return (DoProcessTemplate(Settings::get('MULTIFORMDIR'),Settings::get('MULTIFORMURL'),$s_template,$a_lines,
|
|
$a_values,"",'SubstituteValueForPage'));
|
|
}
|
|
|
|
//
|
|
// Output the multi-form template after filling in the fields.
|
|
//
|
|
function OutputMultiFormTemplate($s_template,$a_values)
|
|
{
|
|
$a_lines = array();
|
|
if (!ProcessMultiFormTemplate($s_template,$a_values,$a_lines)) {
|
|
Error("multi_form_failed",GetMessage(MSG_MULTIFORM_FAILED,
|
|
array("NAME" => $s_template)),false,false);
|
|
} else {
|
|
$n_lines = count($a_lines);
|
|
$s_buf = "";
|
|
for ($ii = 0 ; $ii < $n_lines ; $ii++) {
|
|
$s_buf .= $a_lines[$ii] . "\n";
|
|
unset($a_lines[$ii]); // free memory (hopefully)
|
|
}
|
|
unset($a_lines); // free memory (hopefully)
|
|
|
|
if (IsSetSession("FormKeep"))
|
|
//
|
|
// put in any values that are being forward-remembered
|
|
//
|
|
{
|
|
echo SetPreviousValues($s_buf,GetSession("FormKeep"));
|
|
} else {
|
|
echo $s_buf;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Insert a preamble into a MIME message.
|
|
//
|
|
function MimePreamble(&$a_lines,$a_mesg = array())
|
|
{
|
|
$a_preamble = explode("\n",GetMessage(MSG_MIME_PREAMBLE));
|
|
foreach ($a_preamble as $s_line) {
|
|
$a_lines[] = $s_line . Settings::get('HEAD_CRLF');
|
|
}
|
|
|
|
$a_lines[] = Settings::get('HEAD_CRLF'); // blank line
|
|
$b_need_blank = false;
|
|
foreach ($a_mesg as $s_line) {
|
|
$a_lines[] = $s_line . Settings::get('HEAD_CRLF');
|
|
if (!empty($s_line)) {
|
|
$b_need_blank = true;
|
|
}
|
|
}
|
|
if ($b_need_blank) {
|
|
$a_lines[] = Settings::get('HEAD_CRLF');
|
|
} // blank line
|
|
}
|
|
|
|
//
|
|
// Create the HTML mail
|
|
//
|
|
function HTMLMail(&$a_lines,&$a_headers,$s_body,$s_template,$s_missing,$s_filter,
|
|
$s_boundary,$a_raw_fields,$b_no_plain,$b_process_template)
|
|
{
|
|
$s_charset = GetMailOption("CharSet");
|
|
if (!isset($s_charset)) {
|
|
$s_charset = "ISO-8859-1";
|
|
}
|
|
if ($b_no_plain) {
|
|
$b_multi = false;
|
|
//
|
|
// don't provide a plain text version - just the HTML
|
|
//
|
|
$a_headers['Content-Type'] = SafeHeader("text/html; charset=$s_charset");
|
|
} else {
|
|
$b_multi = true;
|
|
$a_headers['Content-Type'] = "multipart/alternative; boundary=\"$s_boundary\"";
|
|
|
|
$a_pre_lines = explode("\n",GetMessage(MSG_MIME_HTML,
|
|
array("NAME" => $s_template)));
|
|
|
|
MimePreamble($a_lines,$a_pre_lines);
|
|
|
|
//
|
|
// first part - the text version only
|
|
//
|
|
$a_lines[] = "--$s_boundary" . Settings::get('HEAD_CRLF');
|
|
$a_lines[] = "Content-Type: text/plain; charset=$s_charset" . Settings::get('HEAD_CRLF');
|
|
$a_lines[] = Settings::get('HEAD_CRLF'); // blank line
|
|
//
|
|
// treat the body like one line, even though it isn't
|
|
//
|
|
$a_lines[] = $s_body;
|
|
$a_lines[] = Settings::get('HEAD_CRLF'); // blank line
|
|
//
|
|
// second part - the HTML version
|
|
//
|
|
$a_lines[] = "--$s_boundary" . Settings::get('HEAD_CRLF');
|
|
$a_lines[] = "Content-Type: text/html; charset=$s_charset" . Settings::get('HEAD_CRLF');
|
|
$a_lines[] = Settings::get('HEAD_CRLF'); // blank line
|
|
}
|
|
|
|
$a_html_lines = array();
|
|
if (!$b_process_template) {
|
|
if (!ProcessTemplate($s_template,$a_html_lines,$a_raw_fields,NULL,'SubstituteValueDummy')) {
|
|
return (false);
|
|
}
|
|
} elseif (!ProcessTemplate($s_template,$a_html_lines,$a_raw_fields,$s_missing)) {
|
|
return (false);
|
|
}
|
|
|
|
if (!empty($s_filter))
|
|
//
|
|
// treat the data like one line, even though it isn't
|
|
//
|
|
{
|
|
$a_lines[] = Filter($s_filter,$a_html_lines);
|
|
} else {
|
|
foreach ($a_html_lines as $s_line) {
|
|
$a_lines[] = $s_line;
|
|
}
|
|
}
|
|
|
|
if ($b_multi) {
|
|
//
|
|
// end
|
|
//
|
|
$a_lines[] = "--$s_boundary--" . Settings::get('HEAD_CRLF');
|
|
$a_lines[] = Settings::get('HEAD_CRLF'); // blank line
|
|
}
|
|
return (true);
|
|
}
|
|
|
|
//
|
|
// Add the contents of a file in base64 encoding.
|
|
//
|
|
function AddFile(&$a_lines,$s_file_name,$i_file_size,$b_remove = true)
|
|
{
|
|
global $php_errormsg;
|
|
|
|
@ $fp = fopen($s_file_name,"rb");
|
|
if ($fp === false) {
|
|
SendAlert(GetMessage(MSG_FILE_OPEN_ERROR,array("NAME" => $s_file_name,
|
|
"TYPE" => "attachment",
|
|
"ERROR" => CheckString($php_errormsg)
|
|
)));
|
|
return (false);
|
|
}
|
|
//
|
|
// PHP under IIS has problems with the filesize function when
|
|
// the file is on another drive. So, we replaced a call
|
|
// to filesize with the $i_file_size parameter (this occurred
|
|
// in version 3.01).
|
|
//
|
|
$s_contents = fread($fp,$i_file_size);
|
|
//
|
|
// treat as a single line, even though it isn't
|
|
//
|
|
$a_lines[] = chunk_split(base64_encode($s_contents));
|
|
fclose($fp);
|
|
if ($b_remove) {
|
|
@unlink($s_file_name);
|
|
}
|
|
return (true);
|
|
}
|
|
|
|
//
|
|
// Add the contents of a string in base64 encoding.
|
|
//
|
|
function AddData(&$a_lines,$s_data)
|
|
{
|
|
//
|
|
// treat as a single line, even though it isn't
|
|
//
|
|
$a_lines[] = chunk_split(base64_encode($s_data));
|
|
return (true);
|
|
}
|
|
|
|
/**
|
|
* Check if a file is a valid uploaded file.
|
|
*
|
|
* This function is obsolete and is only kept for use by
|
|
* existing hook files. Do not use this function in new code.
|
|
*
|
|
* @param array $a_file_spec file specification
|
|
*
|
|
* @return bool
|
|
*/
|
|
function IsUploadedFile($a_file_spec)
|
|
{
|
|
return FieldManager::IsUploadedFile($a_file_spec);
|
|
}
|
|
|
|
//
|
|
// Save an uploaded file to the repository directory.
|
|
//
|
|
function SaveFileInRepository(&$a_file_spec)
|
|
{
|
|
global $php_errormsg;
|
|
|
|
//
|
|
// if a replacement name has been specified, use that, otherwise
|
|
// use the original name
|
|
//
|
|
if (isset($a_file_spec["new_name"])) {
|
|
$s_file_name = basename($a_file_spec["new_name"]);
|
|
} else {
|
|
$s_file_name = basename($a_file_spec["name"]);
|
|
}
|
|
$s_dest = Settings::get('FILE_REPOSITORY') . "/" . $s_file_name;
|
|
|
|
$b_ok = true;
|
|
$s_error = "";
|
|
|
|
if (isset($a_file_spec["saved_as"]) && !empty($a_file_spec["saved_as"])) {
|
|
FMDebug("SaveFileInRepository: saved_as");
|
|
$s_srce = $a_file_spec["saved_as"];
|
|
} else {
|
|
$s_srce = $a_file_spec["tmp_name"];
|
|
}
|
|
|
|
FMDebug("SaveFileInRepository: $s_srce");
|
|
if (!Settings::get('FILE_OVERWRITE')) {
|
|
clearstatcache();
|
|
if (@file_exists($s_dest)) {
|
|
$b_ok = false;
|
|
$s_error = GetMessage(MSG_SAVE_FILE_EXISTS,array("FILE" => $s_dest));
|
|
}
|
|
}
|
|
if (Settings::get('MAX_FILE_UPLOAD_SIZE') != 0 &&
|
|
$a_file_spec["size"] > Settings::get('MAX_FILE_UPLOAD_SIZE') * 1024
|
|
)
|
|
//
|
|
// this exits
|
|
//
|
|
{
|
|
UserError("upload_size",GetMessage(MSG_FILE_UPLOAD_SIZE,
|
|
array("NAME" => $a_file_spec["name"],
|
|
"SIZE" => $a_file_spec["size"],
|
|
"MAX" => Settings::get('MAX_FILE_UPLOAD_SIZE')
|
|
)));
|
|
}
|
|
if ($b_ok) {
|
|
if (isset($a_file_spec["saved_as"]) && !empty($a_file_spec["saved_as"])) {
|
|
if (!copy($s_srce,$s_dest) || !@unlink($s_srce)) {
|
|
$b_ok = false;
|
|
}
|
|
} else {
|
|
if (!move_uploaded_file($s_srce,$s_dest)) {
|
|
$b_ok = false;
|
|
}
|
|
}
|
|
if ($b_ok) {
|
|
//
|
|
// Flag to say it's been put in the repository.
|
|
//
|
|
$a_file_spec["in_repository"] = true;
|
|
//
|
|
// Its new location
|
|
//
|
|
$a_file_spec["saved_as"] = $s_dest;
|
|
//
|
|
// Now that the file has been saved, "is_uploaded_file"
|
|
// will return false. So, we create a flag to say it was
|
|
// valid.
|
|
//
|
|
$a_file_spec["moved"] = true;
|
|
} else {
|
|
$s_error = $php_errormsg;
|
|
}
|
|
}
|
|
if (!$b_ok) {
|
|
SendAlert(GetMessage(MSG_SAVE_FILE,array(
|
|
"FILE" => $s_srce,
|
|
"DEST" => $s_dest,
|
|
"ERR" => $s_error
|
|
)));
|
|
return (false);
|
|
}
|
|
//
|
|
// ignore chmod fails (other than reporting them)
|
|
//
|
|
if (Settings::get('FILE_MODE') != 0 && !chmod($s_dest,Settings::get('FILE_MODE'))) {
|
|
SendAlert(GetMessage(MSG_CHMOD,array(
|
|
"FILE" => $s_dest,
|
|
"MODE" => Settings::get('FILE_MODE'),
|
|
"ERR" => $s_error
|
|
)));
|
|
}
|
|
return (true);
|
|
}
|
|
|
|
//
|
|
// Save all uploaded files to the repository directory.
|
|
//
|
|
function SaveAllFilesToRepository()
|
|
{
|
|
global $aFileVars;
|
|
|
|
if (!Settings::get('FILEUPLOADS') || Settings::get('FILE_REPOSITORY') === "")
|
|
//
|
|
// nothing to do
|
|
//
|
|
{
|
|
return (true);
|
|
}
|
|
|
|
foreach ($aFileVars as $m_file_key => $a_upload) {
|
|
//
|
|
// One customer reported:
|
|
// Possible file upload attack detected: name='' temp name='none'
|
|
// on PHP 4.1.2 on RAQ4.
|
|
// So, we now also test for "name".
|
|
//
|
|
if (!isset($a_upload["tmp_name"]) || empty($a_upload["tmp_name"]) ||
|
|
!isset($a_upload["name"]) || empty($a_upload["name"])
|
|
) {
|
|
continue;
|
|
}
|
|
if (isset($a_upload["in_repository"]) && $a_upload["in_repository"])
|
|
//
|
|
// already saved
|
|
//
|
|
{
|
|
continue;
|
|
}
|
|
if (!FieldManager::IsUploadedFile($a_upload)) {
|
|
SendAlert(GetMessage(MSG_FILE_UPLOAD_ATTACK,
|
|
array("NAME" => $a_upload["name"],
|
|
"TEMP" => $a_upload["tmp_name"],
|
|
"FLD" => $m_file_key
|
|
)));
|
|
continue;
|
|
}
|
|
if (!SaveFileInRepository($aFileVars[$m_file_key])) {
|
|
return (false);
|
|
}
|
|
//
|
|
// Now the file has been saved in the repository, make
|
|
// the field persistent through all further processing
|
|
// (e.g. all movements in a multi-page form)
|
|
//
|
|
if (IsSetSession("FormSavedFiles")) {
|
|
$a_saved_files = GetSession("FormSavedFiles");
|
|
} else {
|
|
$a_saved_files = array();
|
|
}
|
|
$a_saved_files["repository_" . $m_file_key] = $aFileVars[$m_file_key];
|
|
SetSession("FormSavedFiles",$a_saved_files);
|
|
}
|
|
return (true);
|
|
}
|
|
|
|
//
|
|
// Delete an uploaded file from the repository directory.
|
|
// For security reasons, only the field name can be used. This
|
|
// uniquely identifies an uploaded file by this form process.
|
|
//
|
|
function DeleteFileFromRepository($s_fld)
|
|
{
|
|
global $aFileVars;
|
|
|
|
if (!Settings::get('FILEUPLOADS') || Settings::get('FILE_REPOSITORY') === "")
|
|
//
|
|
// nothing to do
|
|
//
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
if (($a_upload = GetFileInfo($s_fld)) === false) {
|
|
return (false);
|
|
}
|
|
|
|
if (isset($a_upload["in_repository"]) && $a_upload["in_repository"]) {
|
|
if (isset($a_upload["saved_as"]) && !empty($a_upload["saved_as"])) {
|
|
@unlink($a_upload["saved_as"]);
|
|
}
|
|
}
|
|
DeleteFileInfo($s_fld);
|
|
return (true);
|
|
}
|
|
|
|
//
|
|
// Save an uploaded file for later processing.
|
|
//
|
|
function SaveUploadedFile(&$a_file_spec,$s_prefix)
|
|
{
|
|
global $php_errormsg;
|
|
|
|
FMDebug("SaveUploadedFile");
|
|
$s_dest = GetScratchPadFile($s_prefix);
|
|
if (!move_uploaded_file($a_file_spec["tmp_name"],$s_dest)) {
|
|
SendAlert(GetMessage(MSG_SAVE_FILE,array(
|
|
"FILE" => $a_file_spec["tmp_name"],
|
|
"DEST" => $s_dest,
|
|
"ERR" => $php_errormsg
|
|
)));
|
|
return (false);
|
|
}
|
|
$a_file_spec["saved_as"] = $s_dest;
|
|
$a_file_spec["moved"] = true;
|
|
return (true);
|
|
}
|
|
|
|
//
|
|
// Remove old files from the scratchpad directory.
|
|
//
|
|
function CleanScratchPad($s_prefix = "")
|
|
{
|
|
global $lNow;
|
|
global $php_errormsg;
|
|
|
|
if (Settings::isEmpty('SCRATCH_PAD'))
|
|
//
|
|
// no scratchpad to cleanup!
|
|
//
|
|
{
|
|
return;
|
|
}
|
|
if (Settings::get('CLEANUP_TIME') <= 0)
|
|
//
|
|
// cleanup disabled
|
|
//
|
|
{
|
|
return;
|
|
}
|
|
//
|
|
// compute chance of cleanup
|
|
//
|
|
if (Settings::get('CLEANUP_CHANCE') < 100) {
|
|
$i_rand = mt_rand(1,100);
|
|
if ($i_rand > Settings::get('CLEANUP_CHANCE')) {
|
|
return;
|
|
}
|
|
}
|
|
if (($f_dir = @opendir(Settings::get('SCRATCH_PAD'))) === false) {
|
|
Error("open_scratch_pad",GetMessage(MSG_OPEN_SCRATCH_PAD,array(
|
|
"DIR" => Settings::get('SCRATCH_PAD'),
|
|
"ERR" => $php_errormsg
|
|
)), false,false);
|
|
return;
|
|
}
|
|
$i_len = strlen($s_prefix);
|
|
while (($s_file = readdir($f_dir)) !== false) {
|
|
$s_path = Settings::get('SCRATCH_PAD') . "/" . $s_file;
|
|
if (is_file($s_path) && ($i_len == 0 || substr($s_file,0,$i_len) == $s_prefix)) {
|
|
if (($a_stat = @stat($s_path)) !== false) {
|
|
if (isset($a_stat['mtime'])) {
|
|
$l_time = $a_stat['mtime'];
|
|
} else {
|
|
$l_time = $a_stat[9];
|
|
}
|
|
if (($lNow - $l_time) / 60 >= Settings::get('CLEANUP_TIME')) {
|
|
@unlink($s_path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
closedir($f_dir);
|
|
}
|
|
|
|
//
|
|
// Save all uploaded files for later processing.
|
|
//
|
|
function SaveAllUploadedFiles(&$a_file_vars)
|
|
{
|
|
global $php_errormsg;
|
|
|
|
$s_prefix = "UPLD";
|
|
if (Settings::isEmpty('SCRATCH_PAD')) {
|
|
Error("need_scratch_pad",GetMessage(MSG_NEED_SCRATCH_PAD),false,false);
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// remove old uploaded files that have not been moved out.
|
|
//
|
|
CleanScratchPad($s_prefix);
|
|
|
|
foreach (array_keys($a_file_vars) as $m_file_key) {
|
|
$a_upload = &$a_file_vars[$m_file_key];
|
|
//
|
|
// One customer reported:
|
|
// Possible file upload attack detected: name='' temp name='none'
|
|
// on PHP 4.1.2 on RAQ4.
|
|
// So, we now also test for "name".
|
|
//
|
|
if (!isset($a_upload["tmp_name"]) || empty($a_upload["tmp_name"]) ||
|
|
!isset($a_upload["name"]) || empty($a_upload["name"])
|
|
) {
|
|
continue;
|
|
}
|
|
//
|
|
// ensure we don't move the file more than once
|
|
//
|
|
if (!isset($a_upload["saved_as"]) || empty($a_upload["saved_as"])) {
|
|
if (!FieldManager::IsUploadedFile($a_upload)) {
|
|
SendAlert(GetMessage(MSG_FILE_UPLOAD_ATTACK,
|
|
array("NAME" => $a_upload["name"],
|
|
"TEMP" => $a_upload["tmp_name"],
|
|
"FLD" => $m_file_key
|
|
)));
|
|
} elseif (!SaveUploadedFile($a_upload,$s_prefix)) {
|
|
return (false);
|
|
}
|
|
}
|
|
}
|
|
return (true);
|
|
}
|
|
|
|
//
|
|
// Attach a file to the body of a MIME formatted email. $a_lines is the
|
|
// current body, and is modified to include the file.
|
|
// $a_file_spec must have the following values (just like an uploaded
|
|
// file specification):
|
|
// name the name of the file
|
|
// type the mime type
|
|
// tmp_name the name of the temporary file
|
|
// size the size of the temporary file
|
|
//
|
|
// Alternatively, you supply the following instead of tmp_name and size:
|
|
// data the data to attach
|
|
//
|
|
function AttachFile(&$a_lines,$s_att_boundary,$a_file_spec,$s_charset,$b_remove = true)
|
|
{
|
|
$a_lines[] = "--$s_att_boundary" . Settings::get('HEAD_CRLF');
|
|
//
|
|
// if a replacement name has been specified, use that, otherwise
|
|
// use the original name
|
|
//
|
|
if (isset($a_file_spec["new_name"])) {
|
|
$s_file_name = $a_file_spec["new_name"];
|
|
} else {
|
|
$s_file_name = $a_file_spec["name"];
|
|
}
|
|
$s_file_name = str_replace('"','',$s_file_name);
|
|
$s_mime_type = $a_file_spec["type"];
|
|
//
|
|
// The following says that the data is encoded in
|
|
// base64 and is an attachment and that once decoded the
|
|
// character set of the decoded data is $s_charset.
|
|
// (See RFC 1521 Section 5.)
|
|
//
|
|
$a_lines[] = "Content-Type: $s_mime_type; name=\"$s_file_name\"; charset=$s_charset" . Settings::get('HEAD_CRLF');
|
|
$a_lines[] = "Content-Transfer-Encoding: base64" . Settings::get('HEAD_CRLF');
|
|
$a_lines[] = "Content-Disposition: attachment; filename=\"$s_file_name\"" . Settings::get('HEAD_CRLF');
|
|
$a_lines[] = Settings::get('HEAD_CRLF'); // blank line
|
|
if (isset($a_file_spec["tmp_name"]) && isset($a_file_spec["size"])) {
|
|
$s_srce = $a_file_spec["tmp_name"];
|
|
//
|
|
// check if the file has been saved elsewhere
|
|
//
|
|
if (isset($a_file_spec["saved_as"]) && !empty($a_file_spec["saved_as"])) {
|
|
$s_srce = $a_file_spec["saved_as"];
|
|
}
|
|
FMDebug("AttachFile: $s_srce");
|
|
return (AddFile($a_lines,$s_srce,$a_file_spec["size"],$b_remove));
|
|
}
|
|
if (!isset($a_file_spec["data"])) {
|
|
SendAlert(GetMessage(MSG_ATTACH_DATA));
|
|
return (false);
|
|
}
|
|
return (AddData($a_lines,$a_file_spec["data"]));
|
|
}
|
|
|
|
//
|
|
// Reformat the email to be in MIME format.
|
|
// Process file attachments and and fill out any
|
|
// specified HTML template.
|
|
//
|
|
function MakeMimeMail(&$s_body,&$a_headers,$a_raw_fields,$s_template = "",
|
|
$s_missing = NULL,$b_no_plain = false,
|
|
$s_filter = "",$a_file_vars = array(),
|
|
$a_attach_spec = array(),$b_process_template = true)
|
|
{
|
|
global $FM_VERS;
|
|
global $SPECIAL_VALUES;
|
|
|
|
$s_charset = GetMailOption("CharSet");
|
|
if (!isset($s_charset)) {
|
|
$s_charset = "ISO-8859-1";
|
|
}
|
|
$b_att = $b_html = false;
|
|
$b_got_filter = (isset($s_filter) && !empty($s_filter));
|
|
if (isset($s_template) && !empty($s_template)) {
|
|
$b_html = true;
|
|
}
|
|
if (count($a_file_vars) > 0) {
|
|
if (!Settings::get('FILEUPLOADS')) {
|
|
SendAlert(GetMessage(MSG_FILE_UPLOAD));
|
|
//
|
|
// if storing files in the server repository, don't attach
|
|
// unless the mail_options insist
|
|
//
|
|
} elseif (Settings::get('FILE_REPOSITORY') === "" || IsMailOptionSet("AlwaysEmailFiles")) {
|
|
foreach ($a_file_vars as $a_upload) {
|
|
//
|
|
// One customer reported:
|
|
// Possible file upload attack detected: name='' temp name='none'
|
|
// on PHP 4.1.2 on RAQ4.
|
|
// So, we now also test for "name".
|
|
//
|
|
if (isset($a_upload["tmp_name"]) && !empty($a_upload["tmp_name"]) &&
|
|
isset($a_upload["name"]) && !empty($a_upload["name"])
|
|
) {
|
|
$b_att = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// check for an internally-generated attachment
|
|
//
|
|
if (isset($a_attach_spec["Data"])) {
|
|
$b_att = true;
|
|
}
|
|
|
|
$s_uniq = md5($s_body);
|
|
$s_body_boundary = "BODY$s_uniq";
|
|
$s_att_boundary = "PART$s_uniq";
|
|
$a_headers['MIME-Version'] = "1.0 (produced by FormMail $FM_VERS from www.tectite.com)";
|
|
|
|
//
|
|
// if the filter strips formatting, then we'll only have plain text
|
|
// to send, even after the template has been used
|
|
//
|
|
if ($b_got_filter && IsFilterAttribSet($s_filter,"Strips"))
|
|
//
|
|
// no HTML if the filter strips the formatting
|
|
//
|
|
{
|
|
$b_html = false;
|
|
}
|
|
$a_new = array();
|
|
if ($b_att) {
|
|
$a_headers['Content-Type'] = "multipart/mixed; boundary=\"$s_att_boundary\"";
|
|
|
|
MimePreamble($a_new);
|
|
//
|
|
// add the body of the email
|
|
//
|
|
$a_new[] = "--$s_att_boundary" . Settings::get('HEAD_CRLF');
|
|
if ($b_html) {
|
|
$a_lines = $a_local_headers = array();
|
|
if (!HTMLMail($a_lines,$a_local_headers,$s_body,$s_template,
|
|
$s_missing,($b_got_filter) ? $s_filter : "",
|
|
$s_body_boundary,$a_raw_fields,$b_no_plain,
|
|
$b_process_template)
|
|
) {
|
|
return (false);
|
|
}
|
|
$a_new = array_merge($a_new,ExpandMailHeadersArray($a_local_headers));
|
|
$a_new[] = Settings::get('HEAD_CRLF'); // blank line after header
|
|
$a_new = array_merge($a_new,$a_lines);
|
|
} else {
|
|
$a_new[] = "Content-Type: text/plain; charset=$s_charset" . Settings::get('HEAD_CRLF');
|
|
$a_new[] = Settings::get('HEAD_CRLF'); // blank line
|
|
//
|
|
// treat the body like one line, even though it isn't
|
|
//
|
|
$a_new[] = $s_body;
|
|
}
|
|
//
|
|
// now add the attachments or save to the $FILE_REPOSITORY
|
|
//
|
|
if (Settings::get('FILEUPLOADS') &&
|
|
(Settings::get('FILE_REPOSITORY') === "" || IsMailOptionSet("AlwaysEmailFiles"))
|
|
) {
|
|
foreach ($a_file_vars as $m_file_key => $a_upload) {
|
|
//
|
|
// One customer reported:
|
|
// Possible file upload attack detected: name='' temp name='none'
|
|
// on PHP 4.1.2 on RAQ4.
|
|
// So, we now also test for "name".
|
|
//
|
|
if (!isset($a_upload["tmp_name"]) || empty($a_upload["tmp_name"]) ||
|
|
!isset($a_upload["name"]) || empty($a_upload["name"])
|
|
) {
|
|
continue;
|
|
}
|
|
if (!FieldManager::IsUploadedFile($a_upload)) {
|
|
SendAlert(GetMessage(MSG_FILE_UPLOAD_ATTACK,
|
|
array("NAME" => $a_upload["name"],
|
|
"TEMP" => $a_upload["tmp_name"],
|
|
"FLD" => $m_file_key
|
|
)));
|
|
continue;
|
|
}
|
|
if (Settings::get('MAX_FILE_UPLOAD_SIZE') != 0 &&
|
|
$a_upload["size"] > Settings::get('MAX_FILE_UPLOAD_SIZE') * 1024
|
|
) {
|
|
UserError("upload_size",GetMessage(MSG_FILE_UPLOAD_SIZE,
|
|
array("NAME" => $a_upload["name"],
|
|
"SIZE" => $a_upload["size"],
|
|
"MAX" => Settings::get('MAX_FILE_UPLOAD_SIZE')
|
|
)));
|
|
}
|
|
if (!AttachFile($a_new,$s_att_boundary,$a_upload,$s_charset,
|
|
Settings::get('FILE_REPOSITORY') === "")
|
|
) {
|
|
return (false);
|
|
}
|
|
}
|
|
}
|
|
if (isset($a_attach_spec["Data"])) {
|
|
//
|
|
// build a specification similar to a file upload
|
|
//
|
|
$a_file_spec["name"] = isset($a_attach_spec["Name"]) ?
|
|
$a_attach_spec["Name"] :
|
|
"attachment.dat";
|
|
$a_file_spec["type"] = isset($a_attach_spec["MIME"]) ?
|
|
$a_attach_spec["MIME"] :
|
|
"text/plain";
|
|
$a_file_spec["data"] = $a_attach_spec["Data"];
|
|
if (!AttachFile($a_new,$s_att_boundary,$a_file_spec,
|
|
isset($a_attach_spec["CharSet"]) ?
|
|
$a_attach_spec["CharSet"] :
|
|
$s_charset)
|
|
) {
|
|
return (false);
|
|
}
|
|
}
|
|
$a_new[] = "--$s_att_boundary--" . Settings::get('HEAD_CRLF'); // the end
|
|
$a_new[] = Settings::get('HEAD_CRLF'); // blank line
|
|
} elseif ($b_html) {
|
|
if (!HTMLMail($a_new,$a_headers,$s_body,$s_template,
|
|
$s_missing,($b_got_filter) ? $s_filter : "",
|
|
$s_body_boundary,$a_raw_fields,$b_no_plain,
|
|
$b_process_template)
|
|
) {
|
|
return (false);
|
|
}
|
|
} else {
|
|
$a_headers['Content-Type'] = SafeHeader("text/plain; charset=$s_charset");
|
|
//
|
|
// treat the body like one line, even though it isn't
|
|
//
|
|
$a_new[] = $s_body;
|
|
}
|
|
|
|
$s_body = JoinLines(Settings::get('BODY_LF'),$a_new);
|
|
return (true);
|
|
}
|
|
|
|
//
|
|
// to make a From line for the email
|
|
//
|
|
function MakeFromLine($s_email,$s_name)
|
|
{
|
|
$s_style = GetMailOption("FromLineStyle");
|
|
$s_line = "";
|
|
if (!isset($s_style)) {
|
|
$s_style = "";
|
|
}
|
|
//
|
|
// the following From line styles are in accordance with RFC 822
|
|
//
|
|
switch ($s_style) {
|
|
default:
|
|
case "":
|
|
case "default":
|
|
case "AddrSpecName":
|
|
//
|
|
// this is the original From line style that FormMail produced
|
|
// e.g.
|
|
// jack@nowhere.com (Jack Smith)
|
|
// this is an addr-spec with a trailing comment with the name
|
|
//
|
|
if (!empty($s_email)) {
|
|
$s_line .= SafeHeaderEmail($s_email) . " ";
|
|
}
|
|
if (!empty($s_name)) {
|
|
$s_line .= "(" . SafeHeaderComment(EncodeHeaderText($s_name)) . ")";
|
|
}
|
|
break;
|
|
case "NameAddrSpec":
|
|
//
|
|
// email address as an addr-spec preceded by a comment with the name
|
|
// e.g.
|
|
// (Jack Smith) jack@nowhere.com
|
|
//
|
|
if (!empty($s_name)) {
|
|
$s_line .= "(" . SafeHeaderComment(EncodeHeaderText($s_name)) . ") ";
|
|
}
|
|
if (!empty($s_email)) {
|
|
$s_line .= SafeHeaderEmail($s_email);
|
|
}
|
|
break;
|
|
case "RouteAddr":
|
|
//
|
|
// just the email address as a route-addr
|
|
// e.g.
|
|
// <jack@nowhere.com>
|
|
//
|
|
if (!empty($s_email)) {
|
|
$s_line .= "<" . SafeHeaderEmail($s_email) . ">";
|
|
}
|
|
break;
|
|
case "QuotedNameRouteAddr":
|
|
//
|
|
// email address as a route-addr preceded
|
|
// by the name of the user as a quoted string
|
|
// e.g.
|
|
// "Jack Smith" <jack@nowhere.com>
|
|
//
|
|
if (!empty($s_name)) {
|
|
$s_line .= '"' . SafeHeaderQString(EncodeHeaderText($s_name)) . '" ';
|
|
}
|
|
if (!empty($s_email)) {
|
|
$s_line .= "<" . SafeHeaderEmail($s_email) . ">";
|
|
}
|
|
break;
|
|
case "NameRouteAddr":
|
|
//
|
|
// email address as a route-addr preceded
|
|
// by the name of the user as words
|
|
// e.g.
|
|
// Jack Smith <jack@nowhere.com>
|
|
//
|
|
if (!empty($s_name)) {
|
|
$s_line .= SafeHeaderWords(EncodeHeaderText($s_name)) . ' ';
|
|
}
|
|
if (!empty($s_email)) {
|
|
$s_line .= "<" . SafeHeaderEmail($s_email) . ">";
|
|
}
|
|
break;
|
|
}
|
|
return ($s_line);
|
|
}
|
|
|
|
//
|
|
// Return two sets of plain text output: the filtered fields and the
|
|
// non-filtered fields.
|
|
//
|
|
function GetFilteredOutput($a_fld_order,$a_clean_fields,$s_filter,$a_filter_list)
|
|
{
|
|
//
|
|
// find the non-filtered fields and make unfiltered text from them
|
|
//
|
|
$a_unfiltered_list = array();
|
|
$n_flds = count($a_fld_order);
|
|
for ($ii = 0 ; $ii < $n_flds ; $ii++) {
|
|
if (!in_array($a_fld_order[$ii],$a_filter_list)) {
|
|
$a_unfiltered_list[] = $a_fld_order[$ii];
|
|
}
|
|
}
|
|
$s_unfiltered_results = MakeFieldOutput($a_unfiltered_list,$a_clean_fields);
|
|
//
|
|
// filter the specified fields only
|
|
//
|
|
$s_filtered_results = MakeFieldOutput($a_filter_list,$a_clean_fields);
|
|
$s_filtered_results = Filter($s_filter,$s_filtered_results);
|
|
return (array($s_unfiltered_results,$s_filtered_results));
|
|
}
|
|
|
|
//
|
|
// Make a plain text email body
|
|
//
|
|
function MakePlainEmail($a_fld_order,$a_clean_fields,
|
|
$s_to,$s_cc,$s_bcc,$a_raw_fields,$s_filter,$a_filter_list)
|
|
{
|
|
global $SPECIAL_VALUES;
|
|
|
|
$s_unfiltered_results = $s_filtered_results = "";
|
|
$b_got_filter = (isset($s_filter) && !empty($s_filter));
|
|
if ($b_got_filter) {
|
|
if (isset($a_filter_list) && count($a_filter_list) > 0) {
|
|
$b_limited_filter = true;
|
|
} else {
|
|
$b_limited_filter = false;
|
|
}
|
|
}
|
|
$b_used_template = false;
|
|
if (IsMailOptionSet("PlainTemplate")) {
|
|
$s_template = GetMailOption("PlainTemplate");
|
|
if (ProcessTemplate($s_template,$a_lines,$a_raw_fields,GetMailOption('TemplateMissing'),
|
|
'SubstituteValuePlain')
|
|
) {
|
|
$b_used_template = true;
|
|
$s_unfiltered_results = implode(Settings::get('BODY_LF'),$a_lines);
|
|
if ($b_got_filter) {
|
|
//
|
|
// with a limited filter, the template goes unfiltered
|
|
// and the named fields get filtered
|
|
//
|
|
if ($b_limited_filter) {
|
|
list ($s_discard,$s_filtered_results) = GetFilteredOutput($a_fld_order,
|
|
$a_clean_fields,
|
|
$s_filter,$a_filter_list);
|
|
} else {
|
|
$s_filtered_results = Filter($s_filter,$s_unfiltered_results);
|
|
$s_unfiltered_results = "";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!$b_used_template) {
|
|
$res_hdr = "";
|
|
|
|
if (IsMailOptionSet("DupHeader")) {
|
|
//
|
|
// write some standard mail headers
|
|
//
|
|
$res_hdr = "To: $s_to" . Settings::get('BODY_LF');
|
|
if (!empty($s_cc)) {
|
|
$res_hdr .= "Cc: $s_cc" . Settings::get('BODY_LF');
|
|
}
|
|
if (!empty($SPECIAL_VALUES["email"])) {
|
|
$res_hdr .= "From: " . MakeFromLine($SPECIAL_VALUES["email"],
|
|
$SPECIAL_VALUES["realname"]) . Settings::get('BODY_LF');
|
|
}
|
|
$res_hdr .= Settings::get('BODY_LF');
|
|
if (IsMailOptionSet("StartLine")) {
|
|
$res_hdr .= "--START--" . Settings::get('BODY_LF');
|
|
} // signals the beginning of the text to filter
|
|
}
|
|
|
|
//
|
|
// put the realname and the email address at the top of the results
|
|
// (if not excluded)
|
|
//
|
|
if (!IsMailExcluded("realname")) {
|
|
array_unshift($a_fld_order,"realname");
|
|
$a_clean_fields["realname"] = $SPECIAL_VALUES["realname"];
|
|
}
|
|
if (!IsMailExcluded("email")) {
|
|
array_unshift($a_fld_order,"email");
|
|
$a_clean_fields["email"] = $SPECIAL_VALUES["email"];
|
|
}
|
|
if ($b_got_filter) {
|
|
if ($b_limited_filter) {
|
|
list($s_unfiltered_results,$s_filtered_results) =
|
|
GetFilteredOutput($a_fld_order,$a_clean_fields,
|
|
$s_filter,$a_filter_list);
|
|
} else {
|
|
//
|
|
// make text output and filter it (filter all fields)
|
|
//
|
|
$s_filtered_results = MakeFieldOutput($a_fld_order,$a_clean_fields);
|
|
$s_filtered_results = Filter($s_filter,$s_filtered_results);
|
|
}
|
|
} else {
|
|
//SendAlert("There are ".count($a_fld_order)." fields in the order array");
|
|
//SendAlert("Here is the clean fields array:\r\n".var_export($a_clean_fields,true));
|
|
$s_unfiltered_results = MakeFieldOutput($a_fld_order,$a_clean_fields);
|
|
}
|
|
$s_unfiltered_results = $res_hdr . $s_unfiltered_results;
|
|
}
|
|
$s_results = $s_unfiltered_results;
|
|
if ($b_got_filter && !empty($s_filtered_results)) {
|
|
if (!empty($s_results)) {
|
|
$s_results .= Settings::get('BODY_LF');
|
|
}
|
|
$s_results .= $s_filtered_results;
|
|
}
|
|
//
|
|
// append the environment variables report
|
|
//
|
|
if (isset($SPECIAL_VALUES["env_report"]) && !empty($SPECIAL_VALUES["env_report"])) {
|
|
$s_results .= Settings::get('BODY_LF') . "==================================" . Settings::get('BODY_LF');
|
|
$s_results .= Settings::get('BODY_LF') . GetEnvVars(TrimArray(explode(",",$SPECIAL_VALUES["env_report"])),
|
|
Settings::get('BODY_LF'));
|
|
}
|
|
return (array($s_results,$s_unfiltered_results,$s_filtered_results));
|
|
}
|
|
|
|
//
|
|
// Return the list of fields to be filtered, FALSE if no list provided.
|
|
//
|
|
function GetFilterList($b_file_fields)
|
|
{
|
|
global $SPECIAL_VALUES;
|
|
|
|
//
|
|
// no filter means no list of fields
|
|
//
|
|
if (!empty($SPECIAL_VALUES["filter"])) {
|
|
if ($b_file_fields) {
|
|
if (isset($SPECIAL_VALUES["filter_files"]) && !empty($SPECIAL_VALUES["filter_files"])) {
|
|
return (TrimArray(explode(",",$SPECIAL_VALUES["filter_files"])));
|
|
}
|
|
} else {
|
|
if (isset($SPECIAL_VALUES["filter_fields"]) && !empty($SPECIAL_VALUES["filter_fields"])) {
|
|
return (TrimArray(explode(",",$SPECIAL_VALUES["filter_fields"])));
|
|
}
|
|
}
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
/*
|
|
* Function: GetFilterSpec
|
|
* Parameters: $s_filter returns the filter name
|
|
* $m_filter_list returns the list of fields to filter (an array)
|
|
* or is set to false if there is no filter list
|
|
* $b_file_fields if true, return file fields, otherwise return non-file fields
|
|
* Returns: bool true if filtering a list of fields of the specified type
|
|
* Description:
|
|
* Checks whether the form has specified to filter a list of
|
|
* fields of the specified type (file fields or non-file fields).
|
|
*/
|
|
function GetFilterSpec(&$s_filter,&$m_filter_list,$b_file_fields = false)
|
|
{
|
|
global $SPECIAL_VALUES;
|
|
|
|
if (isset($SPECIAL_VALUES["filter"]) && !empty($SPECIAL_VALUES["filter"])) {
|
|
$s_filter = $SPECIAL_VALUES["filter"];
|
|
$m_filter_list = GetFilterList($b_file_fields);
|
|
return (true);
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// send the given results to the given email addresses
|
|
//
|
|
function SendResults($a_fld_order,$a_clean_fields,$s_to,$s_cc,$s_bcc,$a_raw_fields)
|
|
{
|
|
global $SPECIAL_VALUES,$aFileVars,$ValidEmails;
|
|
|
|
//
|
|
// check for a filter and how to use it
|
|
//
|
|
$b_filter_attach = false;
|
|
$a_attach_spec = array();
|
|
$s_filter = "";
|
|
$a_filter_list = array();
|
|
if ($b_got_filter = GetFilterSpec($s_filter,$a_filter_list)) {
|
|
if ($a_filter_list === false) {
|
|
//
|
|
// not a limited filter, so filter all fields
|
|
//
|
|
$b_limited_filter = false;
|
|
$a_filter_list = array();
|
|
} else {
|
|
$b_limited_filter = true;
|
|
}
|
|
FMDebug("SendResults: got filter '$s_filter', limited=$b_limited_filter");
|
|
$s_filter_attach_name = GetFilterOption("Attach");
|
|
if (isset($s_filter_attach_name)) {
|
|
if (!is_string($s_filter_attach_name) || empty($s_filter_attach_name)) {
|
|
SendAlert(GetMessage(MSG_ATTACH_NAME));
|
|
} else {
|
|
$b_filter_attach = true;
|
|
$a_attach_spec = array("Name" => $s_filter_attach_name);
|
|
if (($s_mime = GetFilterAttrib($s_filter,"MIME")) !== false) {
|
|
$a_attach_spec["MIME"] = $s_mime;
|
|
}
|
|
//
|
|
// Regarding the character set...
|
|
// A filter will not generally change the character set
|
|
// of the message, however, if it does, then we
|
|
// provide that information to the MIME encoder.
|
|
// Remember: this character set specification refers
|
|
// to the data *after* the effect of the filter
|
|
// has been reversed (e.g. an encrypted message
|
|
// in UTF-8 is in UTF-8 when it is decrypted).
|
|
//
|
|
if (($s_cset = GetFilterAttrib($s_filter,"CharSet")) !== false) {
|
|
$a_attach_spec["CharSet"] = $s_cset;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// check the need for MIME formatted mail
|
|
//
|
|
$b_mime_mail = (IsMailOptionSet("HTMLTemplate") || count($aFileVars) > 0 ||
|
|
$b_filter_attach);
|
|
|
|
//
|
|
// create the email header lines - CC, BCC, From, and Reply-To
|
|
//
|
|
$a_headers = array();
|
|
if (!empty($s_cc)) {
|
|
$a_headers['Cc'] = SafeHeader($s_cc);
|
|
}
|
|
if (!empty($SPECIAL_VALUES["replyto"])) {
|
|
//
|
|
// expand replyto list
|
|
//
|
|
CheckEmailAddress($SPECIAL_VALUES["replyto"],$s_list,$s_invalid,false);
|
|
if (!empty($s_list)) {
|
|
$a_headers['Reply-To'] = SafeHeader($s_list);
|
|
}
|
|
}
|
|
if (!empty($s_bcc)) {
|
|
$a_headers['Bcc'] = SafeHeader($s_bcc);
|
|
}
|
|
//
|
|
// create the From address
|
|
//
|
|
// Some servers won't let you set the email address to the
|
|
// submitter of the form. Therefore, use FromAddr if it's been
|
|
// specified to set the sender and the "From" address.
|
|
//
|
|
$s_sender = GetMailOption("FromAddr");
|
|
if (!isset($s_sender)) {
|
|
$s_sender = "";
|
|
if (!empty($SPECIAL_VALUES["email"])) {
|
|
$a_headers['From'] = MakeFromLine($SPECIAL_VALUES["email"],
|
|
$SPECIAL_VALUES["realname"]);
|
|
}
|
|
} elseif ($s_sender !== "") {
|
|
$s_sender = UnMangle($s_sender);
|
|
if ($ValidEmails->CheckAddress($s_sender)) {
|
|
$s_sender = $a_headers['From'] = SafeHeader($s_sender);
|
|
} else {
|
|
SendAlert(GetMessage(MSG_INVALID_SENDER,array("LOC" => "FromAddr in mail_options","EMAIL" => $s_sender)));
|
|
$s_sender = "";
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Override sender if $FIXED_SENDER is set.
|
|
*/
|
|
if (Settings::get('FIXED_SENDER') !== "") {
|
|
$s_sender = Settings::get('FIXED_SENDER');
|
|
}
|
|
|
|
//
|
|
// special case: if there is only one non-special string value, then
|
|
// format it as an email (unless an option says not to)
|
|
//
|
|
$a_keys = array_keys($a_raw_fields);
|
|
if (count($a_keys) == 1 && is_string($a_raw_fields[$a_keys[0]]) &&
|
|
!IsMailOptionSet("AlwaysList") && !IsMailOptionSet("DupHeader")
|
|
) {
|
|
if (IsMailExcluded($a_keys[0])) {
|
|
SendAlert("Exclusion of single field '" . $a_keys[0] . "' ignored");
|
|
}
|
|
$s_value = $a_raw_fields[$a_keys[0]];
|
|
//
|
|
// replace carriage return/linefeeds with <br>
|
|
//
|
|
$s_value = str_replace("\r\n",'<br />',$s_value);
|
|
//
|
|
// replace lone linefeeds with <br>
|
|
//
|
|
$s_value = str_replace("\n",'<br />',$s_value);
|
|
//
|
|
// remove lone carriage returns
|
|
//
|
|
$s_value = str_replace("\r","",$s_value);
|
|
//
|
|
// replace all control chars with <br />
|
|
//
|
|
$s_value = preg_replace('/[[:cntrl:]]+/','<br />',$s_value);
|
|
//
|
|
// strip HTML (note that all the <br> above will now be
|
|
// replaced with BODY_LF)
|
|
//
|
|
$s_value = StripHTML($s_value,Settings::get('BODY_LF'));
|
|
|
|
if ($b_mime_mail) {
|
|
if ($b_got_filter) {
|
|
//
|
|
// filter the whole value (ignore filter_fields for this
|
|
// special case) if a filter has been specified
|
|
//
|
|
$s_results = Filter($s_filter,$s_value);
|
|
if ($b_filter_attach) {
|
|
$a_attach_spec["Data"] = $s_results;
|
|
//
|
|
// KeepInLine keeps the filtered version inline as well
|
|
// as an attachment
|
|
//
|
|
if (!IsFilterOptionSet("KeepInLine")) {
|
|
$s_results = "";
|
|
}
|
|
$s_filter = ""; // no more filtering
|
|
}
|
|
} else {
|
|
$s_results = $s_value;
|
|
}
|
|
|
|
//
|
|
// send this single value off to get formatted in a MIME
|
|
// email
|
|
//
|
|
if (!MakeMimeMail($s_results,$a_headers,$a_raw_fields,
|
|
GetMailOption('HTMLTemplate'),
|
|
GetMailOption('TemplateMissing'),
|
|
IsMailOptionSet("NoPlain"),
|
|
$s_filter,$aFileVars,$a_attach_spec)
|
|
) {
|
|
return (false);
|
|
}
|
|
} elseif ($b_got_filter)
|
|
//
|
|
// filter the whole value (ignore filter_fields for this special case)
|
|
// if a filter has been specified
|
|
//
|
|
{
|
|
$s_results = Filter($s_filter,$s_value);
|
|
} else {
|
|
$s_results = $s_value;
|
|
if (IsMailOptionSet("CharSet"))
|
|
//
|
|
// sending plain text email, and the CharSet has been
|
|
// specified; include a header
|
|
//
|
|
{
|
|
$a_headers['Content-Type'] = "text/plain; charset=" . SafeHeader(GetMailOption("CharSet"));
|
|
}
|
|
}
|
|
} else {
|
|
if ($b_mime_mail) {
|
|
//
|
|
// get the plain text version of the email then send it
|
|
// to get MIME formatted
|
|
//
|
|
list($s_results,$s_unfiltered_results,$s_filtered_results) =
|
|
MakePlainEmail($a_fld_order,$a_clean_fields,
|
|
$s_to,$s_cc,$s_bcc,$a_raw_fields,$s_filter,
|
|
$a_filter_list);
|
|
if ($b_filter_attach) {
|
|
//
|
|
// attached the filtered results
|
|
//
|
|
$a_attach_spec["Data"] = $s_filtered_results;
|
|
//
|
|
// KeepInLine keeps the filtered version inline as well
|
|
// as an attachment
|
|
//
|
|
if (!IsFilterOptionSet("KeepInLine"))
|
|
//
|
|
// put the unfiltered results in the body of the message
|
|
//
|
|
{
|
|
$s_results = $s_unfiltered_results;
|
|
}
|
|
$s_filter = ""; // no more filtering
|
|
}
|
|
if (!MakeMimeMail($s_results,$a_headers,$a_raw_fields,
|
|
GetMailOption('HTMLTemplate'),
|
|
GetMailOption('TemplateMissing'),
|
|
IsMailOptionSet("NoPlain"),
|
|
$s_filter,$aFileVars,$a_attach_spec)
|
|
) {
|
|
return (false);
|
|
}
|
|
} else {
|
|
list($s_results,$s_unfiltered_results,$s_filtered_results) =
|
|
MakePlainEmail($a_fld_order,$a_clean_fields,
|
|
$s_to,$s_cc,$s_bcc,$a_raw_fields,$s_filter,
|
|
$a_filter_list);
|
|
if (!$b_got_filter && IsMailOptionSet("CharSet"))
|
|
//
|
|
// sending plain text email, and the CharSet has been
|
|
// specified; include a header
|
|
//
|
|
{
|
|
$a_headers['Content-Type'] = "text/plain; charset=" . SafeHeader(GetMailOption("CharSet"));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// now save uploaded files to the repository
|
|
//
|
|
if (Settings::get('FILEUPLOADS') && Settings::get('FILE_REPOSITORY') !== "") {
|
|
if (!SaveAllFilesToRepository()) {
|
|
return (false);
|
|
}
|
|
}
|
|
|
|
//
|
|
// send the mail - assumes the email addresses have already been checked
|
|
//
|
|
return (SendCheckedMail($s_to,$SPECIAL_VALUES["subject"],$s_results,
|
|
$s_sender,$a_headers));
|
|
}
|
|
|
|
//
|
|
// append an entry to a log file
|
|
//
|
|
function WriteLog($log_file)
|
|
{
|
|
global $SPECIAL_VALUES,$php_errormsg;
|
|
|
|
@ $log_fp = fopen($log_file,"a");
|
|
if ($log_fp === false) {
|
|
SendAlert(GetMessage(MSG_FILE_OPEN_ERROR,array("NAME" => $log_file,
|
|
"TYPE" => "log",
|
|
"ERROR" => CheckString($php_errormsg)
|
|
)));
|
|
return;
|
|
}
|
|
$date = gmdate("H:i:s d-M-y T");
|
|
$entry = $date . ":" . $SPECIAL_VALUES["email"] . "," .
|
|
$SPECIAL_VALUES["realname"] . "," . $SPECIAL_VALUES["subject"] . "\n";
|
|
fwrite($log_fp,$entry);
|
|
fclose($log_fp);
|
|
}
|
|
|
|
//
|
|
// write the data to a comma-separated-values file
|
|
//
|
|
function WriteCSVFile($s_csv_file,$a_vars)
|
|
{
|
|
global $SPECIAL_VALUES;
|
|
|
|
//
|
|
// create an array of column values in the order specified
|
|
// in $SPECIAL_VALUES["csvcolumns"]
|
|
//
|
|
$a_column_list = $SPECIAL_VALUES["csvcolumns"];
|
|
if (!isset($a_column_list) || empty($a_column_list) || !is_string($a_column_list)) {
|
|
SendAlert(GetMessage(MSG_CSVCOLUMNS,array("VALUE" => $a_column_list)));
|
|
return;
|
|
}
|
|
if (!isset($s_csv_file) || empty($s_csv_file) || !is_string($s_csv_file)) {
|
|
SendAlert(GetMessage(MSG_CSVFILE,array("VALUE" => $s_csv_file)));
|
|
return;
|
|
}
|
|
|
|
@ $fp = fopen($s_csv_file,"a" . Settings::get('CSVOPEN'));
|
|
if ($fp === false) {
|
|
SendAlert(GetMessage(MSG_FILE_OPEN_ERROR,array("NAME" => $s_csv_file,
|
|
"TYPE" => "CSV",
|
|
"ERROR" => CheckString($php_errormsg)
|
|
)));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// convert the column list to an array, trim the names too
|
|
//
|
|
$a_column_list = TrimArray(explode(",",$a_column_list));
|
|
$n_columns = count($a_column_list);
|
|
|
|
//
|
|
// if the file is currently empty, put the column names in the first line
|
|
//
|
|
$b_heading = false;
|
|
if (filesize($s_csv_file) == 0) {
|
|
$b_heading = true;
|
|
}
|
|
|
|
$csv_format = new CSVFormat();
|
|
|
|
//
|
|
// now configure the CSVFormat object
|
|
// according to FormMail's configuration settings
|
|
//
|
|
$csv_format->SetQuote(Settings::get('CSVQUOTE'));
|
|
$csv_format->SetEscPolicy("conv");
|
|
$csv_format->SetSep(Settings::get('CSVSEP'));
|
|
$csv_format->SetIntSep(Settings::get('CSVINTSEP'));
|
|
if (Settings::get('LIMITED_IMPORT')) {
|
|
$csv_format->SetCleanFunc(function ($m_value) {
|
|
return CleanValue($m_value);
|
|
});
|
|
}
|
|
|
|
$s_csv = $csv_format->MakeCSVRecord($a_column_list,$a_vars);
|
|
|
|
if ($b_heading) {
|
|
fwrite($fp,$csv_format->MakeHeading($a_column_list) . Settings::get('CSVLINE'));
|
|
}
|
|
|
|
fwrite($fp,$s_csv . Settings::get('CSVLINE'));
|
|
fclose($fp);
|
|
// CreatePage($debug);
|
|
// FormMailExit();
|
|
}
|
|
|
|
function CheckConfig()
|
|
{
|
|
$a_mesgs = array();
|
|
if (in_array("TARGET_EMAIL",Settings::get('CONFIG_CHECK'))) {
|
|
//
|
|
// $TARGET_EMAIL values should begin with ^ and end with $
|
|
//
|
|
$a_target_email = Settings::get('TARGET_EMAIL');
|
|
for ($ii = 0 ; $ii < count($a_target_email) ; $ii++) {
|
|
$s_pattern = $a_target_email[$ii];
|
|
if (substr($s_pattern,0,1) != '^') {
|
|
$a_mesgs[] = GetMessage(MSG_TARG_EMAIL_PAT_START,
|
|
array("PAT" => $s_pattern));
|
|
}
|
|
if (substr($s_pattern,-1) != '$') {
|
|
$a_mesgs[] = GetMessage(MSG_TARG_EMAIL_PAT_END,
|
|
array("PAT" => $s_pattern));
|
|
}
|
|
}
|
|
}
|
|
if (Settings::get('SET_SENDER_FROM_EMAIL')) {
|
|
$a_mesgs[] = GetMessage(MSG_SET_SENDER_FROM_EMAIL);
|
|
}
|
|
if (count($a_mesgs) > 0) {
|
|
SendAlert(GetMessage(MSG_CONFIG_WARN,
|
|
array("MESGS" => implode("\n",$a_mesgs))),false,true);
|
|
}
|
|
}
|
|
|
|
//
|
|
// append an entry to the Auto Responder log file
|
|
//
|
|
function WriteARLog($s_to,$s_subj,$s_info)
|
|
{
|
|
global $aServerVars,$php_errormsg;
|
|
|
|
if (Settings::isEmpty('LOGDIR') || Settings::isEmpty('AUTORESPONDLOG')) {
|
|
return;
|
|
}
|
|
|
|
$log_file = Settings::get('LOGDIR') . "/" . Settings::get('AUTORESPONDLOG');
|
|
@ $log_fp = fopen($log_file,"a");
|
|
if ($log_fp === false) {
|
|
SendAlert(GetMessage(MSG_FILE_OPEN_ERROR,array("NAME" => $log_file,
|
|
"TYPE" => "log",
|
|
"ERROR" => CheckString($php_errormsg)
|
|
)));
|
|
return;
|
|
}
|
|
$a_entry = array();
|
|
$a_entry[] = gmdate("H:i:s d-M-y T"); // date/time in GMT
|
|
$a_entry[] = $aServerVars['REMOTE_ADDR']; // remote IP address
|
|
$a_entry[] = $s_to; // target email address
|
|
$a_entry[] = $s_subj; // subject line
|
|
$a_entry[] = $s_info; // information
|
|
|
|
$s_log_entry = implode(",",$a_entry) . "\n";
|
|
fwrite($log_fp,$s_log_entry);
|
|
fclose($log_fp);
|
|
}
|
|
|
|
/*
|
|
* The main logic starts here....
|
|
*/
|
|
|
|
//
|
|
// First, a special case; if formmail.php is called like this:
|
|
// http://.../formmail.php?testalert=1
|
|
// it sends a test message to the default alert address with some
|
|
// information about your PHP version and the DOCUMENT_ROOT.
|
|
//
|
|
if (isset($aGetVars["testalert"])) {
|
|
if ($aGetVars["testalert"] == Settings::get('TEST_PASSWORD')) {
|
|
function ShowServerVar($s_name)
|
|
{
|
|
global $aServerVars;
|
|
|
|
return (isset($aServerVars[$s_name]) ? $aServerVars[$s_name] : "-not set-");
|
|
}
|
|
|
|
$sAlert = GetMessage(MSG_ALERT,
|
|
array("LANG" => $sLangID,
|
|
"PHPVERS" => $ExecEnv->getPHPVersionString(),
|
|
"FM_VERS" => $FM_VERS,
|
|
"SERVER" => (IsServerWindows() ? "Windows" : "non-Windows"),
|
|
"DOCUMENT_ROOT" => ShowServerVar('DOCUMENT_ROOT'),
|
|
"SCRIPT_FILENAME" => ShowServerVar('SCRIPT_FILENAME'),
|
|
"PATH_TRANSLATED" => ShowServerVar('PATH_TRANSLATED'),
|
|
"REAL_DOCUMENT_ROOT" => CheckString($REAL_DOCUMENT_ROOT),
|
|
));
|
|
|
|
if (Settings::get('DEF_ALERT') == "") {
|
|
echo "<p>" . GetMessage(MSG_NO_DEF_ALERT) . "</p>";
|
|
} elseif (SendAlert($sAlert,false,true)) {
|
|
echo "<p>" . GetMessage(MSG_TEST_SENT) . "</p>";
|
|
} else {
|
|
echo "<p>" . GetMessage(MSG_TEST_FAILED) . "</p>";
|
|
}
|
|
FormMailExit();
|
|
} else {
|
|
echo "<p>Wrong password for testalert!</p>";
|
|
FormMailExit();
|
|
}
|
|
}
|
|
|
|
if (isset($aGetVars["testlang"]) && $aGetVars["testlang"] == 1) {
|
|
|
|
function ShowMessages()
|
|
{
|
|
global $aMessages,$sLangID,$aGetVars,$sHTMLCharSet;
|
|
|
|
//
|
|
// force message numbers on unless "mnums=no"
|
|
//
|
|
if (isset($aGetVars["mnums"]) && $aGetVars["mnums"] == "no") {
|
|
Settings::set('bShowMesgNumbers',false);
|
|
} else {
|
|
Settings::set('bShowMesgNumbers',true);
|
|
}
|
|
LoadBuiltinLanguage();
|
|
|
|
$s_def_lang = $sLangID;
|
|
$a_def_mesgs = $aMessages;
|
|
|
|
LoadLanguageFile();
|
|
|
|
$s_active_lang = $sLangID;
|
|
$a_active_mesgs = $aMessages;
|
|
|
|
$a_list = get_defined_constants();
|
|
|
|
echo "<html>\n";
|
|
echo "<head>\n";
|
|
if (isset($sHTMLCharSet) && $sHTMLCharSet !== "") {
|
|
echo "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=$sHTMLCharSet\">\n";
|
|
}
|
|
echo "</head>\n";
|
|
echo "<body>\n";
|
|
|
|
echo "<table border=\"1\" cellpadding=\"10\" width=\"95%\">\n";
|
|
echo "<tr>\n";
|
|
echo "<th>\n";
|
|
echo "Message Number";
|
|
echo "</th>\n";
|
|
echo "<th>\n";
|
|
echo "$s_def_lang";
|
|
echo "</th>\n";
|
|
echo "<th>\n";
|
|
echo "$s_active_lang";
|
|
echo "</th>\n";
|
|
echo "</tr>\n";
|
|
foreach ($a_list as $s_name => $i_value) {
|
|
if (substr($s_name,0,4) == "MSG_") {
|
|
//
|
|
// some PHP constants begin with MSG_, so we try to skip them
|
|
// too
|
|
//
|
|
switch ($s_name) {
|
|
case "MSG_IPC_NOWAIT":
|
|
case "MSG_EAGAIN":
|
|
case "MSG_ENOMSG":
|
|
case "MSG_NOERROR":
|
|
case "MSG_EXCEPT":
|
|
case "MSG_OOB":
|
|
case "MSG_PEEK":
|
|
case "MSG_DONTROUTE":
|
|
case "MSG_EOR":
|
|
continue 2;
|
|
}
|
|
if ($i_value >= 256) {
|
|
continue;
|
|
}
|
|
echo "<tr>\n";
|
|
echo "<td valign=\"top\">\n";
|
|
echo "$s_name ($i_value)";
|
|
echo "</td>\n";
|
|
echo "<td valign=\"top\">\n";
|
|
$aMessages = $a_def_mesgs;
|
|
$s_def_msg = GetMessage((int)$i_value,array(),true,true);
|
|
echo nl2br(htmlentities($s_def_msg)); // English - don't need
|
|
// FixedHTMLEntities
|
|
echo "</td>\n";
|
|
echo "<td valign=\"top\">\n";
|
|
$aMessages = $a_active_mesgs;
|
|
$s_act_msg = GetMessage((int)$i_value,array(),true,true);
|
|
if ($s_def_msg == $s_act_msg) {
|
|
echo "<i>identical</i>\n";
|
|
} else {
|
|
echo nl2br(FixedHTMLEntities($s_act_msg));
|
|
}
|
|
echo "</td>\n";
|
|
echo "</tr>\n";
|
|
}
|
|
}
|
|
echo "</table>\n";
|
|
|
|
echo "</body>\n";
|
|
echo "</html>\n";
|
|
}
|
|
|
|
ShowMessages();
|
|
FormMailExit();
|
|
}
|
|
|
|
//
|
|
// For saved files, add in the "new_name" values to the given
|
|
// array.
|
|
//
|
|
function GetSavedFileNames($a_values)
|
|
{
|
|
if (IsSetSession("FormSavedFiles")) {
|
|
$a_saved_files = GetSession("FormSavedFiles");
|
|
foreach ($a_saved_files as $s_fld => $a_upload) {
|
|
if (isset($a_upload["name"])) {
|
|
$a_values[$s_fld] = $a_upload["name"];
|
|
}
|
|
if (isset($a_upload["new_name"])) {
|
|
$a_values["name_of_$s_fld"] = $a_upload["new_name"];
|
|
}
|
|
}
|
|
}
|
|
return ($a_values);
|
|
}
|
|
|
|
//
|
|
// Scan the Multi form sequence values and build up the values that
|
|
// were submitted to the given form index.
|
|
//
|
|
function GetMultiValues($a_form_list,$i_form_index,$a_order = array(),
|
|
$a_clean = array(),
|
|
$a_raw_data = array(),
|
|
$a_all_data = array(),
|
|
$a_file_data = array())
|
|
{
|
|
$a_ret_clean = $a_ret_raw = $a_ret_all = $a_ret_files = array();
|
|
for ($ii = 0 ; $ii < $i_form_index ; $ii++) {
|
|
//
|
|
// only add a field to the order if it's not already there
|
|
//
|
|
$a_form_order = $a_form_list[$ii]["ORDER"];
|
|
$n_order = count($a_form_order);
|
|
for ($jj = 0 ; $jj < $n_order ; $jj++) {
|
|
if (array_search($a_form_order[$jj],$a_order) === false) {
|
|
$a_order[] = $a_form_order[$jj];
|
|
}
|
|
}
|
|
$a_ret_clean = array_merge($a_ret_clean,$a_form_list[$ii]["CLEAN"]);
|
|
$a_ret_raw = array_merge($a_ret_raw,$a_form_list[$ii]["RAWDATA"]);
|
|
$a_ret_all = array_merge($a_ret_all,$a_form_list[$ii]["ALLDATA"]);
|
|
$a_ret_files = array_merge($a_ret_files,$a_form_list[$ii]["FILES"]);
|
|
}
|
|
//
|
|
// later values must take precedence to earlier values,
|
|
// so merge in the passed-in data last
|
|
//
|
|
$a_ret_clean = array_merge($a_ret_clean,$a_clean);
|
|
$a_ret_raw = array_merge($a_ret_raw,$a_raw_data);
|
|
$a_ret_all = array_merge($a_ret_all,$a_all_data);
|
|
$a_ret_files = array_merge($a_ret_files,$a_file_data);
|
|
return (array($a_order,$a_ret_clean,$a_ret_raw,$a_ret_all,$a_ret_files));
|
|
}
|
|
|
|
$bMultiForm = false;
|
|
|
|
//
|
|
// Multi-page form sequencing is complicated....
|
|
// Requirements:
|
|
// 1. The first page in a multi-page form sequence must provide:
|
|
// - multi_start field set to 1
|
|
// - this_form field (it's own URL)
|
|
// - next_form field (multi-page form template name)
|
|
// 2. Subsequent pages must provide either:
|
|
// next_form (the next template name in the sequence)
|
|
// OR
|
|
// good_url or good_template or neither; this terminates
|
|
// the multi-page processing and provides the final URL.
|
|
// Logic:
|
|
// 1. We create session variables to contain information about
|
|
// the sequence.
|
|
// 2. On the first submission from the starting form, we record
|
|
// its "this_form" URL and the data submitted.
|
|
// We also create a URL (to FormMail) that will allow return
|
|
// to the *next* form in the sequence.
|
|
// 3. On submission from other forms in the sequence, we record
|
|
// the data that was submitted so we can return to that
|
|
// form with the user's data re-filled into the form.
|
|
// We also create a URL (to FormMail) that will allow return
|
|
// to the *next* form in the sequence.
|
|
// 4. A return URL contains "return=index" where index is the
|
|
// form sequence index number. This is a URL to FormMail.
|
|
// FormMail gets the template name or URL (URL only for the
|
|
// starting form) and outputs the requested HTML form. It also
|
|
// truncates the session data for forms after the returned-to
|
|
// one.
|
|
//
|
|
|
|
//
|
|
// Return to a previous form in a sequence.
|
|
//
|
|
function MultiFormReturn($i_return_to)
|
|
{
|
|
global $iFormIndex;
|
|
global $SessionAccessor;
|
|
|
|
if (!IsSetSession("FormList") ||
|
|
!IsSetSession("FormIndex") ||
|
|
$i_return_to < 0 ||
|
|
$i_return_to > GetSession("FormIndex")
|
|
) {
|
|
Error("cannot_return",GetMessage(MSG_CANNOT_RETURN,
|
|
array("TO" => $i_return_to,
|
|
"TOPINDEX" => (
|
|
IsSetSession("FormIndex") ?
|
|
GetSession("FormIndex") :
|
|
"<undefined>")
|
|
)),
|
|
false,false);
|
|
}
|
|
$a_list = GetSession("FormList");
|
|
assert($i_return_to < count($a_list));
|
|
$a_form_def = $a_list[$i_return_to];
|
|
SetSession("FormList",$a_list = array_slice($a_list,0,$i_return_to + 1));
|
|
SetSession("FormIndex",$iFormIndex = $i_return_to);
|
|
if (isset($a_form_def["FORM"])) {
|
|
//
|
|
// get the values that were originally submitted to this form
|
|
//
|
|
list(,,$a_values,,) = GetMultiValues($a_list,$i_return_to);
|
|
//
|
|
// get the session values
|
|
//
|
|
$SessionAccessor->CopyIn($a_values,true);
|
|
$a_lines = array();
|
|
//
|
|
// process the page as a template
|
|
//
|
|
if (ProcessMultiFormTemplate($a_form_def["FORM"],$a_values,$a_lines)) {
|
|
$n_lines = count($a_lines);
|
|
$s_buf = "";
|
|
for ($ii = 0 ; $ii < $n_lines ; $ii++) {
|
|
$s_buf .= $a_lines[$ii] . "\n";
|
|
unset($a_lines[$ii]); // free memory (hopefully)
|
|
}
|
|
unset($a_lines); // free memory (hopefully)
|
|
//
|
|
// put in the values that the user previously submitted
|
|
// to this form
|
|
//
|
|
echo SetPreviousValues($s_buf,$a_form_def["RAWDATA"]);
|
|
} else {
|
|
Error("multi_form_failed",GetMessage(MSG_MULTIFORM_FAILED,
|
|
array("NAME" => $a_form_def["FORM"])),false,false);
|
|
}
|
|
} else
|
|
//
|
|
// we probably should include
|
|
// $SessionAccessor->CopyIn(...,true);
|
|
// at some stage in the future to get the session values....need to think about this
|
|
// and run some case studies.
|
|
//
|
|
{
|
|
ProcessReturnToForm($a_form_def["URL"],$a_form_def["RAWDATA"],array("multi_start"));
|
|
}
|
|
//echo "Returned to $i_return_to";
|
|
}
|
|
|
|
//
|
|
// Store any data just submitted and specified as "multi_keep".
|
|
//
|
|
function MultiKeep()
|
|
{
|
|
global $SPECIAL_VALUES,$aRawDataValues;
|
|
|
|
if (isset($SPECIAL_VALUES["multi_keep"]) &&
|
|
!empty($SPECIAL_VALUES["multi_keep"])
|
|
) {
|
|
$a_list = TrimArray(explode(",",$SPECIAL_VALUES["multi_keep"]));
|
|
if (IsSetSession("FormKeep")) {
|
|
$a_keep = GetSession("FormKeep");
|
|
} else {
|
|
$a_keep = array();
|
|
}
|
|
//
|
|
// For each data field specified in "multi_keep" store its
|
|
// value in the FormKeep session variable.
|
|
// If a field is specified and does not exist in the
|
|
// recent submission, its value is discarded.
|
|
//
|
|
foreach ($a_list as $s_fld_name) {
|
|
if (!empty($s_fld_name)) {
|
|
if (isset($aRawDataValues[$s_fld_name])) {
|
|
$a_keep[$s_fld_name] = $aRawDataValues[$s_fld_name];
|
|
} else {
|
|
unset($a_keep[$s_fld_name]);
|
|
}
|
|
}
|
|
}
|
|
SetSession("FormKeep",$a_keep);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Perform Logic for Multi-Page form sequences
|
|
//
|
|
function MultiFormLogic()
|
|
{
|
|
global $bMultiForm,$SPECIAL_VALUES,$aServerVars,$aFileVars,$ExecEnv;
|
|
global $sFormMailScript,$bGotGoBack,$bGotNextForm,$iFormIndex;
|
|
global $aFieldOrder,$aCleanedValues,$aRawDataValues,$aAllRawValues;
|
|
|
|
if ($SPECIAL_VALUES["multi_start"] == 1) {
|
|
if (empty($SPECIAL_VALUES["this_form"])) {
|
|
ErrorWithIgnore("need_this_form",GetMessage(MSG_NEED_THIS_FORM),false,false);
|
|
}
|
|
|
|
$bMultiForm = true;
|
|
//
|
|
// Start of multi-page form sequence
|
|
//
|
|
$a_list = array();
|
|
$a_list[0] = array("URL" => $SPECIAL_VALUES["this_form"],
|
|
"ORDER" => $aFieldOrder,
|
|
"CLEAN" => $aCleanedValues,
|
|
"RAWDATA" => $aRawDataValues,
|
|
"ALLDATA" => $aAllRawValues,
|
|
"FILES" => $aFileVars
|
|
);
|
|
$iFormIndex = 0; // zero is the first form, which was just submitted
|
|
SetSession("FormList",$a_list);
|
|
SetSession("FormIndex",$iFormIndex);
|
|
//
|
|
// this is a fresh session, so remove any remembered values
|
|
//
|
|
UnsetSession("FormSavedFiles");
|
|
UnsetSession("FormKeep");
|
|
} elseif (IsSetSession("FormList")) {
|
|
$bMultiForm = true;
|
|
}
|
|
|
|
if ($bMultiForm) {
|
|
$sFormMailScript = $ExecEnv->GetScript();
|
|
$iFormIndex = GetSession("FormIndex");
|
|
}
|
|
|
|
//
|
|
// If we're going forward in a multi-page form sequence,
|
|
// compute a URL to return to the form we're about to display.
|
|
//
|
|
if ($bMultiForm && !$bGotGoBack) {
|
|
//
|
|
// record the data that was just submitted by the previous form
|
|
//
|
|
$iFormIndex = GetSession("FormIndex");
|
|
$a_list = GetSession("FormList");
|
|
$a_list[$iFormIndex]["ORDER"] = $aFieldOrder;
|
|
$a_list[$iFormIndex]["CLEAN"] = $aCleanedValues;
|
|
$a_list[$iFormIndex]["RAWDATA"] = $aRawDataValues;
|
|
$a_list[$iFormIndex]["ALLDATA"] = $aAllRawValues;
|
|
if (count($aFileVars) > 0 && !Settings::get('FILEUPLOADS')) {
|
|
SendAlert(GetMessage(MSG_FILE_UPLOAD));
|
|
} elseif (count($aFileVars) > 0 && !SaveAllUploadedFiles($aFileVars)) {
|
|
Error("upload_save_failed",GetMessage(MSG_MULTI_UPLOAD),false,false);
|
|
}
|
|
$a_list[$iFormIndex]["FILES"] = $aFileVars;
|
|
$iFormIndex++;
|
|
$s_url = GetReturnLink($sFormMailScript,$iFormIndex);
|
|
$a_list[$iFormIndex] = array("URL" => $s_url,
|
|
"FORM" => $SPECIAL_VALUES["next_form"],
|
|
"ORDER" => $aFieldOrder,
|
|
"CLEAN" => $aCleanedValues,
|
|
"RAWDATA" => $aRawDataValues,
|
|
"ALLDATA" => $aAllRawValues,
|
|
"FILES" => $aFileVars
|
|
);
|
|
SetSession("FormList",$a_list);
|
|
SetSession("FormIndex",$iFormIndex);
|
|
MultiKeep();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for the MIME Attack
|
|
//
|
|
function DetectMimeAttack($a_fields,&$s_attack,&$s_info,&$s_user_info)
|
|
{
|
|
//
|
|
// if any of the recipient fields contain "MIME-Version" or
|
|
// "Content-Type" then this is the MIME attack
|
|
// Now also checks the subject field.
|
|
//
|
|
$a_rec_flds = array("recipients","cc","bcc","replyto","subject");
|
|
foreach ($a_rec_flds as $s_fld) {
|
|
if (isset($a_fields[$s_fld])) {
|
|
//
|
|
// some of the fields can be arrays
|
|
//
|
|
if (is_array($a_fields[$s_fld])) {
|
|
$s_data = implode(",",$a_fields[$s_fld]);
|
|
} else {
|
|
$s_data = $a_fields[$s_fld];
|
|
}
|
|
$s_data = strtolower($s_data);
|
|
if (($i_mime = strpos($s_data,"mime-version")) !== false ||
|
|
($i_cont = strpos($s_data,"content-type")) !== false
|
|
) {
|
|
$s_attack = "MIME";
|
|
$s_info = GetMessage(MSG_ATTACK_MIME_INFO,
|
|
array("FLD" => $s_fld,
|
|
"CONTENT" => ($i_mime !==
|
|
false) ?
|
|
"mime-version" :
|
|
"content-type"
|
|
),
|
|
false);
|
|
return (true);
|
|
}
|
|
}
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// Strip common language sequences from the data
|
|
// that might otherwise look like a junk attack.
|
|
// We replace them with a space so that the stripping
|
|
// cannot create more junk accidentally.
|
|
//
|
|
function AttackDetectionStripLang($s_data)
|
|
{
|
|
|
|
foreach (Settings::get('ATTACK_DETECTION_JUNK_LANG_STRIP') as $s_seq) {
|
|
$s_data = str_replace($s_seq," ",$s_data);
|
|
}
|
|
return ($s_data);
|
|
}
|
|
|
|
//
|
|
// Find strings of $n_consec characters from the alphabet of $s_alpha
|
|
// in $s_data. Return the count of instances found.
|
|
//
|
|
function AttackDetectionFindJunk($s_data,$s_alpha,$n_consec,&$a_matches)
|
|
{
|
|
$s_pat = "/[" . preg_quote($s_alpha,"/") . "]{" . "$n_consec,$n_consec" . "}/";
|
|
if (($n_count = preg_match_all($s_pat,$s_data,$a_matches)) === false) {
|
|
$n_count = 0;
|
|
$a_matches = array();
|
|
} else {
|
|
$a_matches = $a_matches[0];
|
|
}
|
|
return ($n_count);
|
|
}
|
|
|
|
//
|
|
// Test if a field name is a technical field.
|
|
// This means a field that is likely to contain junk values.
|
|
// Any special field and certain other field names are counted as technical.
|
|
//
|
|
function IsTechnicalField($s_fld)
|
|
{
|
|
$b_is_technical = false;
|
|
if (IsSpecialField($s_fld) || IsSpecialMultiField($s_fld)) {
|
|
$b_is_technical = true;
|
|
} else {
|
|
// case doesn't matter
|
|
$s_lower_fld = strtolower($s_fld);
|
|
|
|
$a_prefix_list = array('email');
|
|
$a_prefix_found = array_filter($a_prefix_list,function ($s_prefix) use ($s_lower_fld) {
|
|
return substr($s_lower_fld,strlen($s_prefix)) === $s_prefix;
|
|
});
|
|
$a_suffix_list = array('check','url','email');
|
|
$a_suffix_found = array_filter($a_suffix_list,function ($s_suffix) use ($s_lower_fld) {
|
|
return substr($s_lower_fld,-strlen($s_suffix)) === $s_suffix;
|
|
});
|
|
$a_fullname_list = array('tectiteformid');
|
|
$b_fullname_match = array_search($s_lower_fld,$a_fullname_list) !== false;
|
|
$b_is_technical = $b_fullname_match || count($a_prefix_found) > 0 || count($a_suffix_found) > 0;
|
|
}
|
|
return $b_is_technical;
|
|
}
|
|
|
|
//
|
|
// Check for the Junk Attack
|
|
//
|
|
function DetectJunkAttack($a_fields,&$s_attack,&$s_info,&$s_user_info)
|
|
{
|
|
//
|
|
// If any field contains junk data, trigger detection.
|
|
//
|
|
$n_count = 0;
|
|
$a_matches = array();
|
|
$a_user_matches = array();
|
|
foreach ($a_fields as $s_fld => $m_value) {
|
|
if (IsTechnicalField($s_fld)) {
|
|
//
|
|
// Skip special fields because they don't contain
|
|
// normal user input.
|
|
// But, some special fields do contain normal user
|
|
// input, so we don't skip them.
|
|
//
|
|
$b_skip = true;
|
|
switch ($s_fld) {
|
|
case "realname":
|
|
case "subject":
|
|
$b_skip = false;
|
|
break;
|
|
}
|
|
if ($b_skip) {
|
|
continue;
|
|
}
|
|
}
|
|
//
|
|
// Ignore fields that are specified to contain
|
|
// technical information.
|
|
//
|
|
if (in_array($s_fld,Settings::get('ATTACK_DETECTION_JUNK_IGNORE_FIELDS'),true)) {
|
|
continue;
|
|
}
|
|
if (isset($m_value) && !FieldManager::IsEmpty($m_value)) {
|
|
if (!is_array($m_value)) {
|
|
$m_value = array($m_value);
|
|
}
|
|
foreach ($m_value as $s_data) {
|
|
$s_orig_data = $s_data = strtolower($s_data);
|
|
//
|
|
// Strip out sequences that might be common to
|
|
// the user's language.
|
|
// For example, in English, there are a lot of common
|
|
// words that can easily be protected from our
|
|
// tests. For example, "thousandths" has 5 consecutive consonants
|
|
// but is a reasonable word.
|
|
// Similarly, "queue" has 4 consecutive vowels.
|
|
//
|
|
$s_data = AttackDetectionStripLang($s_data);
|
|
//
|
|
// Look for strings of too many vowels or too many consonants.
|
|
// For safety, we must detect more than one instance, because there
|
|
// are a lot of normal words that may be caught by this test.
|
|
// The number of instances is controlled by configuration.
|
|
//
|
|
$n_match = AttackDetectionFindJunk($s_data,Settings::get('ATTACK_DETECTION_JUNK_CONSONANTS'),
|
|
Settings::get('ATTACK_DETECTION_JUNK_CONSEC_CONSONANTS'),
|
|
$a_match_cons);
|
|
if ($n_match > 0) {
|
|
$a_user_matches[] = $s_orig_data;
|
|
}
|
|
$n_count += $n_match;
|
|
$n_match = AttackDetectionFindJunk($s_data,Settings::get('ATTACK_DETECTION_JUNK_VOWELS'),
|
|
Settings::get('ATTACK_DETECTION_JUNK_CONSEC_VOWELS'),
|
|
$a_match_vow);
|
|
if ($n_match > 0) {
|
|
$a_user_matches[] = $s_orig_data;
|
|
}
|
|
$n_count += $n_match;
|
|
if ($n_count >= Settings::get('ATTACK_DETECTION_JUNK_TRIGGER')) {
|
|
$a_matches = array_merge($a_matches,$a_match_cons,$a_match_vow);
|
|
$s_user_info = GetMessage(MSG_USER_ATTACK_JUNK,
|
|
array("INPUT" => implode(", ",$a_user_matches)),false);
|
|
$s_attack = "JUNK";
|
|
$s_info = GetMessage(MSG_ATTACK_JUNK_INFO,
|
|
array("FLD" => $s_fld,
|
|
"JUNK" => implode(" ",
|
|
$a_matches)
|
|
),
|
|
false);
|
|
return (true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// Check for the duplicate data attack
|
|
//
|
|
function DetectDupAttack($a_fields,&$s_attack,&$s_info,&$s_user_info)
|
|
{
|
|
//
|
|
// if any of the configured fields contain duplicate data,
|
|
// then this lame attack has been detected
|
|
//
|
|
|
|
$a_data_map = array();
|
|
foreach (Settings::get('ATTACK_DETECTION_DUPS') as $s_fld) {
|
|
if (isset($a_fields[$s_fld]) &&
|
|
is_scalar($a_fields[$s_fld]) && // can only work with string data
|
|
!empty($a_fields[$s_fld])
|
|
) {
|
|
$s_data = (string)$a_fields[$s_fld];
|
|
if (isset($a_data_map[$s_data])) {
|
|
//
|
|
// duplicate found!
|
|
//
|
|
$s_attack = "Duplicate Fields";
|
|
$s_info = GetMessage(MSG_ATTACK_DUP_INFO,
|
|
array("FLD1" => $a_data_map[$s_data],
|
|
"FLD2" => $s_fld
|
|
),false);
|
|
$s_user_info = GetMessage(MSG_USER_ATTACK_DUP,array(),false);
|
|
return (true);
|
|
}
|
|
$a_data_map[$s_data] = $s_fld;
|
|
}
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// Check for the email addresses in specials attack
|
|
//
|
|
function DetectSpecialsAttack($a_fields,&$s_attack,&$s_info,&$s_user_info)
|
|
{
|
|
//
|
|
// look for email addresses in whole fields
|
|
//
|
|
foreach (Settings::get('ATTACK_DETECTION_SPECIALS_ONLY_EMAIL') as $s_fld) {
|
|
if (isset($a_fields[$s_fld]) &&
|
|
is_scalar($a_fields[$s_fld]) && // can only work with string data
|
|
!empty($a_fields[$s_fld])
|
|
) {
|
|
$s_data = $a_fields[$s_fld];
|
|
if (preg_match("/^\b[-a-z0-9._%]+@[-.%_a-z0-9]+\.[a-z]{2,9}\b$/i",
|
|
$s_data) === 1
|
|
) {
|
|
//
|
|
// email address found in wrong field
|
|
//
|
|
$s_attack = "Special Fields Only";
|
|
$s_info = GetMessage(MSG_ATTACK_SPEC_INFO,
|
|
array("FLD" => $s_fld),false);
|
|
return (true);
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// look for email addresses in any part of fields
|
|
//
|
|
foreach (Settings::get('ATTACK_DETECTION_SPECIALS_ANY_EMAIL') as $s_fld) {
|
|
if (isset($a_fields[$s_fld]) &&
|
|
is_scalar($a_fields[$s_fld]) && // can only work with string data
|
|
!empty($a_fields[$s_fld])
|
|
) {
|
|
$s_data = $a_fields[$s_fld];
|
|
if (preg_match("/\b[-a-z0-9._%]+@[-.%_a-z0-9]+\.[a-z]{2,9}\b/i",
|
|
$s_data) > 0
|
|
) {
|
|
//
|
|
// email address found in wrong field
|
|
//
|
|
$s_attack = "Special Fields Any";
|
|
$s_info = GetMessage(MSG_ATTACK_SPEC_INFO,
|
|
array("FLD" => $s_fld),false);
|
|
return (true);
|
|
}
|
|
}
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// Check for "many URLs in a field" and "many fields with URLs" attacks
|
|
//
|
|
function DetectManyURLsAttack($a_fields,&$s_attack,&$s_info,&$s_user_info)
|
|
{
|
|
//
|
|
// This suffix to patterns ensure that a domain name is not actually the name component
|
|
// of an email address. For example, 'ss.game@bigpond.com' would get incorrectly recognised as a URL
|
|
// without this additional pattern.
|
|
//
|
|
$s_not_email_name = '(?!@[a-z]+\.)';
|
|
|
|
$a_fld_names = array();
|
|
//
|
|
// actual URL link patterns
|
|
// 21-Jul-18: We've removed looking for alphanumerics here because it doesn't
|
|
// detect non-English chars. The UTF-8 options for preg_match did not help.
|
|
//
|
|
$s_srch = '(\bhttps{0,1}:\/\/|<\s*a\s+href=)';
|
|
|
|
//
|
|
// now add configurable patterns
|
|
//
|
|
if (!Settings::isEmpty('ATTACK_DETECTION_URL_PATTERNS') &&
|
|
is_array(Settings::get('ATTACK_DETECTION_URL_PATTERNS'))
|
|
) {
|
|
//
|
|
// escape / characters and build up a string
|
|
// of alternate patterns
|
|
//
|
|
foreach (Settings::get('ATTACK_DETECTION_URL_PATTERNS') as $s_pat) {
|
|
if ($s_pat == "") {
|
|
continue;
|
|
}
|
|
$s_srch .= "|" . str_replace('/','\/',$s_pat) . $s_not_email_name;
|
|
}
|
|
}
|
|
if (Settings::get('SITE_DOMAIN')) {
|
|
if (strpos(Settings::get('SITE_DOMAIN'),'://') !== false) {
|
|
$a_url_parts = parse_url(Settings::get('SITE_DOMAIN'));
|
|
$own_domain = $a_url_parts['host'];
|
|
} else {
|
|
$own_domain = Settings::get('SITE_DOMAIN');
|
|
}
|
|
$own_domain = str_ireplace('www.','',$own_domain);
|
|
} else {
|
|
$own_domain = '';
|
|
}
|
|
foreach ($a_fields as $s_fld => $s_data) {
|
|
if (IsSpecialField($s_fld) || IsSpecialMultiField($s_fld))
|
|
//
|
|
// skip special fields because some are supposed to
|
|
// have URLs in them
|
|
//
|
|
{
|
|
continue;
|
|
}
|
|
if (isset($s_data) &&
|
|
is_scalar($s_data) && // can only work with string data
|
|
!empty($s_data)
|
|
) {
|
|
$n_match = preg_match_all("/$s_srch/msi",$s_data,$a_matches);
|
|
if (!is_int($n_match)) {
|
|
$n_match = 0;
|
|
}
|
|
// allow links to SITE_DOMAIN
|
|
if ($own_domain) {
|
|
$n_site_match = preg_match('/\b' . $own_domain . '\b/msi',$s_data);
|
|
if (is_int($n_site_match) && $n_site_match > 0) {
|
|
$n_match -= $n_site_match;
|
|
}
|
|
}
|
|
/*
|
|
* debugging code....
|
|
if ($n_match > 0)
|
|
echo "Pattern is '".htmlentities($s_srch)."' with '".
|
|
htmlentities($s_data)."' field '$s_fld', matched=$n_match<br />";
|
|
*/
|
|
if (Settings::get('ATTACK_DETECTION_MANY_URLS') > 0) {
|
|
if ($n_match >= Settings::get('ATTACK_DETECTION_MANY_URLS')) {
|
|
$s_attack = "Many URLS in a field";
|
|
$s_info = GetMessage(MSG_ATTACK_MANYURL_INFO,
|
|
array("FLD" => $s_fld,"NUM" => ($n_match)),false);
|
|
$s_user_info = GetMessage(MSG_USER_ATTACK_MANY_URLS,array(),false);
|
|
return (true);
|
|
}
|
|
}
|
|
if ($n_match > 0) {
|
|
$a_fld_names[] = $s_fld;
|
|
}
|
|
}
|
|
}
|
|
if (Settings::get('ATTACK_DETECTION_MANY_URL_FIELDS') > 0) {
|
|
if (count($a_fld_names) >= Settings::get('ATTACK_DETECTION_MANY_URL_FIELDS')) {
|
|
$s_attack = "Many fields with URLs";
|
|
$s_info = GetMessage(MSG_ATTACK_MANYFIELDS_INFO,
|
|
array("FLDS" => implode(",",$a_fld_names),
|
|
"NUM" => (count($a_fld_names))
|
|
),false);
|
|
$s_user_info = GetMessage(MSG_USER_ATTACK_MANY_URL_FIELDS,array(),false);
|
|
return (true);
|
|
}
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
function IsAjax()
|
|
{
|
|
global $SPECIAL_VALUES,$aFormVars,$aGetVars;
|
|
|
|
//
|
|
// this may be called too early to have SPECIAL_VALUES loaded,
|
|
// so we check the submitted form vars too.
|
|
//
|
|
if (isset($SPECIAL_VALUES) && $SPECIAL_VALUES["fmmode"] == "ajax") {
|
|
return (true);
|
|
}
|
|
if (isset($aFormVars["fmmode"])) {
|
|
return ($aFormVars["fmmode"] == "ajax");
|
|
}
|
|
if (isset($aGetVars["fmmode"])) {
|
|
return ($aGetVars["fmmode"] == "ajax");
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// Detect annoying attacks and prevent them from sending spurious
|
|
// alert messages.
|
|
//
|
|
function DetectAttacks($a_fields)
|
|
{
|
|
|
|
$s_info = $s_attack = "";
|
|
$b_attacked = false;
|
|
$s_user_info = "";
|
|
if (Settings::get('ATTACK_DETECTION_MIME')) {
|
|
if (DetectMimeAttack($a_fields,$s_attack,$s_info,$s_user_info)) {
|
|
$b_attacked = true;
|
|
}
|
|
}
|
|
if (!$b_attacked && !Settings::isEmpty('ATTACK_DETECTION_DUPS')) {
|
|
if (DetectDupAttack($a_fields,$s_attack,$s_info,$s_user_info)) {
|
|
$b_attacked = true;
|
|
}
|
|
}
|
|
if (!$b_attacked && Settings::get('ATTACK_DETECTION_SPECIALS')) {
|
|
if (DetectSpecialsAttack($a_fields,$s_attack,$s_info,$s_user_info)) {
|
|
$b_attacked = true;
|
|
}
|
|
}
|
|
if (!$b_attacked && (Settings::get('ATTACK_DETECTION_MANY_URLS') ||
|
|
Settings::get('ATTACK_DETECTION_MANY_URL_FIELDS'))
|
|
) {
|
|
if (DetectManyURLsAttack($a_fields,$s_attack,$s_info,$s_user_info)) {
|
|
$b_attacked = true;
|
|
}
|
|
}
|
|
if (Settings::get('ATTACK_DETECTION_JUNK')) {
|
|
if (DetectJunkAttack($a_fields,$s_attack,$s_info,$s_user_info)) {
|
|
$b_attacked = true;
|
|
}
|
|
}
|
|
|
|
if (!$b_attacked && !Settings::isEmpty('ATTACK_DETECTION_REVERSE_CAPTCHA')) {
|
|
if (DetectRevCaptchaAttack(Settings::get('ATTACK_DETECTION_REVERSE_CAPTCHA'),$a_fields,$s_attack,$s_info,
|
|
$s_user_info)
|
|
) {
|
|
$b_attacked = true;
|
|
}
|
|
}
|
|
if (function_exists('FMHookDetectAttacks')) {
|
|
if (FMHookDetectAttacks($a_fields,$s_attack,$s_info,$s_user_info)) {
|
|
$b_attacked = true;
|
|
}
|
|
}
|
|
|
|
if ($b_attacked) {
|
|
if (function_exists('FMHookAttacked')) {
|
|
FMHookAttacked($s_attack);
|
|
}
|
|
if (Settings::get('ALERT_ON_ATTACK_DETECTION')) {
|
|
SendAlert(GetMessage(MSG_ATTACK_DETECTED,
|
|
array("ATTACK" => $s_attack,
|
|
"INFO" => $s_info,
|
|
)),
|
|
false);
|
|
}
|
|
if (!IsAjax() && Settings::get('ATTACK_DETECTION_URL') !== "") {
|
|
Redirect(Settings::get('ATTACK_DETECTION_URL'),GetMessage(MSG_FORM_ERROR));
|
|
} else {
|
|
global $SERVER;
|
|
|
|
CreatePage(GetMessage(MSG_ATTACK_PAGE,array("SERVER" => $SERVER,"USERINFO" => $s_user_info)),
|
|
GetMessage(MSG_FORM_ERROR));
|
|
}
|
|
FormMailExit();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Implement reverse captcha. At least two fields must be provided.
|
|
// At least one of the fields should have a non-empty value.
|
|
//
|
|
function DetectRevCaptchaAttack($a_revcap_spec,$a_form_data,&$s_attack,&$s_info,&$s_user_info)
|
|
{
|
|
global $bReverseCaptchaCompleted;
|
|
|
|
if (count($a_revcap_spec) < 2) {
|
|
SendAlert(GetMessage(MSG_REV_CAP));
|
|
return (false);
|
|
}
|
|
//
|
|
// check the reverse captcha fields
|
|
//
|
|
$n_empty = $n_non_empty = 0;
|
|
$b_attacked = false;
|
|
$s_info = "";
|
|
foreach ($a_revcap_spec as $s_fld_name => $s_value) {
|
|
if ($s_value === "") {
|
|
$n_empty++;
|
|
if (isset($a_form_data[$s_fld_name]) &&
|
|
$a_form_data[$s_fld_name] !== ""
|
|
) {
|
|
$b_attacked = true;
|
|
$s_info .= "\n" . GetMessage(MSG_ATTACK_REV_CAP_INFO,
|
|
array("FLD" => $s_fld_name,
|
|
"CONTENT" => $a_form_data[$s_fld_name]
|
|
),
|
|
false);
|
|
}
|
|
} else {
|
|
$n_non_empty++;
|
|
if (!isset($a_form_data[$s_fld_name]) ||
|
|
$a_form_data[$s_fld_name] !== $s_value
|
|
) {
|
|
$b_attacked = true;
|
|
$s_info .= "\n" . GetMessage(MSG_ATTACK_REV_CAP_INFO,
|
|
array("FLD" => $s_fld_name,
|
|
"CONTENT" =>
|
|
isset($a_form_data[$s_fld_name]) ?
|
|
$a_form_data[$s_fld_name] :
|
|
""
|
|
),
|
|
false);
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// check that the rev captcha specification is correct:
|
|
// at least two fields, at least one empty
|
|
// and at least one non-empty
|
|
//
|
|
if ($n_empty + $n_non_empty < 2 ||
|
|
$n_empty == 0 || $n_non_empty == 0
|
|
) {
|
|
SendAlert(GetMessage(MSG_REV_CAP));
|
|
return (false);
|
|
}
|
|
if ($b_attacked) {
|
|
$s_attack = "Reverse Captcha";
|
|
$s_user_info = GetMessage(MSG_USER_ATTACK_REV_CAP,array(),false);
|
|
}
|
|
$bReverseCaptchaCompleted = !$b_attacked;
|
|
FMDebug('RevCaptcha: ' . $s_info);
|
|
//FMDebug("RevCaptcha done: ".($bReverseCaptchaCompleted ? "success" : "failure"));
|
|
return ($b_attacked);
|
|
}
|
|
|
|
//
|
|
// Check whether a form submission is allowed based on any Captcha provided
|
|
//
|
|
function CheckCaptchaSubmit()
|
|
{
|
|
global $SPECIAL_VALUES,$reCaptchaProcessor;
|
|
|
|
if ($SPECIAL_VALUES["imgverify"] !== "") {
|
|
//
|
|
// implement reCaptcha
|
|
//
|
|
if (isset($reCaptchaProcessor)) {
|
|
$s_error = '';
|
|
if (!$reCaptchaProcessor->Check($SPECIAL_VALUES["imgverify"],$SPECIAL_VALUES,$s_error)) {
|
|
$s_error_mesg = GetMessage(MSG_RECAPTCHA_MATCH,array("ERR" => $s_error));
|
|
UserError("recaptcha",$s_error_mesg,'',array('imgverify' => $s_error_mesg));
|
|
}
|
|
}
|
|
//
|
|
// implement other CAPTCHA
|
|
//
|
|
else {
|
|
//
|
|
// VerifyImgString is from Tectite's simple verifyimg.php CAPTCHA
|
|
// turing_string is from Captcha Creator
|
|
//
|
|
if (!IsSetSession("VerifyImgString") &&
|
|
!IsSetSession("turing_string")
|
|
) {
|
|
ErrorWithIgnore("verify_failed",GetMessage(MSG_VERIFY_MISSING),false);
|
|
}
|
|
//
|
|
// the user's entry must match the value in the session; allow
|
|
// spaces in the user's input
|
|
//
|
|
if (IsSetSession("VerifyImgString")) {
|
|
if (strtoupper(str_replace(" ","",$SPECIAL_VALUES["imgverify"])) !==
|
|
strtoupper(GetSession("VerifyImgString"))
|
|
) {
|
|
$s_error_mesg = GetMessage(MSG_VERIFY_MATCH);
|
|
UserError("img_verify",$s_error_mesg,'',array('imgverify' => $s_error_mesg));
|
|
}
|
|
} else {
|
|
if (strtoupper(str_replace(" ","",$SPECIAL_VALUES["imgverify"])) !==
|
|
strtoupper(GetSession("turing_string"))
|
|
) {
|
|
$s_error_mesg = GetMessage(MSG_VERIFY_MATCH);
|
|
UserError("img_verify",$s_error_mesg,'',array('imgverify' => $s_error_mesg));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Class: AutoResponder
|
|
* Description:
|
|
* Implements the auto responding feature of FormMail.
|
|
* The object must only be created after special fields have been
|
|
* processed.
|
|
*/
|
|
|
|
class AutoResponder
|
|
{
|
|
var $_bRequested; // true if requested by the form
|
|
var $_sTo; // to-address for auto response
|
|
var $_sSubject; // subject for auto response
|
|
|
|
var $_iNone = 0; // must be zero - initializes iType and iCaptchaType
|
|
|
|
var $_iCaptchaType; // type of CAPTCHA that's been successfully processed
|
|
var $_bCaptchaOK; // true if CAPTCHA processing is OK, otherwise false
|
|
|
|
//
|
|
// values of _iCaptchaType
|
|
//
|
|
var $_iFull = 1; // full captcha
|
|
var $_iRev = 2; // reverse captcha
|
|
|
|
var $_iType; // type of autoresponse (template or plain)
|
|
|
|
//
|
|
// values of _iType
|
|
//
|
|
var $_iSendTemplate = 1; // send a template
|
|
var $_iSendPlain = 2; // send a plain file
|
|
|
|
/*
|
|
* Method: AutoResponder ctor
|
|
* Parameters: void
|
|
* Returns: n/a
|
|
* Description:
|
|
* Constructs the object.
|
|
*/
|
|
function __construct()
|
|
{
|
|
global $SPECIAL_VALUES;
|
|
|
|
$this->_bCaptchaOK = $this->_bRequested = false;
|
|
$this->_sTo = "";
|
|
$this->_sSubject = "";
|
|
$this->_iType = $this->_iNone;
|
|
$this->_iCaptchaType = $this->_iNone;
|
|
|
|
//
|
|
// An autoreponse is sometimes optional in the sense the user
|
|
// can choose whether they want it.
|
|
// It can also be mandatory, and enforced by the form owner.
|
|
// Here are the rules:
|
|
// 1. If there is no return email address (email field), no autoresponse.
|
|
// In this case, if autoresponse has been requested by the form, send
|
|
// an alert message to the form owner, but otherwise ignore the problem
|
|
// (it's optional).
|
|
// 2. If no autoresponse has been requested by the form, skip.
|
|
// 3. If HTMLTemplate or PlainTemplate is set:
|
|
// 3a. If full CAPTCHA has been performed, send autoresponse.
|
|
// 3b If no full CAPTCHA has been performed, no autoresponse.
|
|
// 4. If HTMLTemplate and PlainTemplate are *not* set and either
|
|
// PlainFile or HTMLFile is set:
|
|
// 4a. If full CAPTCHA has been performed, send autoresponse.
|
|
// 4b. If Reverse CAPTCHA has been performed, send autoresponse.
|
|
// 4c. Otherwise, no autoresponse.
|
|
// 5. If full CAPTCHA is attempted but fails (e.g. wrong input), display
|
|
// error.
|
|
//
|
|
// The form owner can enforce autoresponse by making "email" required,
|
|
// and making "arverify" or "recaptcha_response_field" required (for templates)
|
|
// or by ensuring Reverse CAPTCHA is performed (for PlainFile or HTMLFile).
|
|
//
|
|
if (IsAROptionSet('HTMLTemplate') ||
|
|
IsAROptionSet('PlainTemplate')
|
|
) {
|
|
$this->_iType = $this->_iSendTemplate;
|
|
}
|
|
if (IsAROptionSet('PlainFile') ||
|
|
IsAROptionSet('HTMLFile')
|
|
) {
|
|
$this->_iType = $this->_iSendPlain;
|
|
}
|
|
if ($this->_iType) {
|
|
//
|
|
// the form has requested autoresponse....
|
|
// we need an email address to send to
|
|
//
|
|
if (!isset($SPECIAL_VALUES["email"]) || empty($SPECIAL_VALUES["email"])) {
|
|
SendAlert(GetMessage(MSG_ARESP_EMAIL));
|
|
} else {
|
|
$this->_bRequested = true;
|
|
$this->_sTo = $SPECIAL_VALUES["email"];
|
|
if (IsAROptionSet('Subject')) {
|
|
$this->_sSubject = GetAROption('Subject');
|
|
} else {
|
|
$this->_sSubject = GetMessage(MSG_ARESP_SUBJ,array(),false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Method: AutoResponder::IsRequested
|
|
* Parameters: void
|
|
* Returns: bool true if autoresponding has been requested
|
|
* Description:
|
|
* Determines if autoresponding has been requested by the HTML.
|
|
*/
|
|
function IsRequested()
|
|
{
|
|
return ($this->_bRequested);
|
|
}
|
|
|
|
/*
|
|
* Method: AutoResponder::Process
|
|
* Parameters: $b_check_only if true, perform checks but do not send
|
|
* Returns: void
|
|
* Description:
|
|
* Processes the autorespond.
|
|
*/
|
|
function Process($b_check_only = false)
|
|
{
|
|
global $SPECIAL_VALUES;
|
|
|
|
FMDebug("AutoResponder::Process: check=" . ($b_check_only ? "Y" : "N"));
|
|
if ($this->IsRequested()) {
|
|
FMDebug("AutoResponder::Process: requested");
|
|
$b_done = false;
|
|
//
|
|
// You can create a function called SpecialAutoResponderProcess in a hook script.
|
|
// It gets passed the AutoResponder object and the $b_check_only flag.
|
|
// If the function returns true, then the standard process is not performed.
|
|
// Otherwise the standard process continues as-is.
|
|
// Your special function can call any of the public methods of this class including:
|
|
// StandarProcess
|
|
// Send
|
|
//
|
|
if (function_exists('SpecialAutoResponderProcess')) {
|
|
$b_done = SpecialAutoResponderProcess($this,$b_check_only);
|
|
}
|
|
if (!$b_done) {
|
|
$this->StandardProcess($b_check_only);
|
|
}
|
|
}
|
|
}
|
|
|
|
function StandardProcess($b_check_only)
|
|
{
|
|
//
|
|
// verify CAPTCHA or that Reverse CAPTCHA has been completed
|
|
// (unless we've already done that)
|
|
//
|
|
$this->_CheckCaptcha();
|
|
if (!$b_check_only && $this->_bCaptchaOK) {
|
|
FMDebug("AutoResponder::Process: proceeding, type=" . $this->_iType);
|
|
//
|
|
// for a template, full CAPTCHA must have been processed
|
|
//
|
|
if ($this->_iType == $this->_iSendTemplate) {
|
|
if ($this->_iCaptchaType == $this->_iFull) {
|
|
$this->Send(true);
|
|
}
|
|
}
|
|
//
|
|
// for a plain file, reverse CAPTCHA is sufficient, any CAPTCHA is OK
|
|
//
|
|
elseif ($this->_iType == $this->_iSendPlain) {
|
|
if ($this->_iCaptchaType) {
|
|
$this->Send(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Method: AutoResponder::_CheckCaptcha
|
|
* Parameters: void
|
|
* Returns: void
|
|
* Description:
|
|
* Checks the type of CAPTCHA that has been processed.
|
|
* This method should only be called if autoresponse has been requested
|
|
* by the form (i.e. IsRequested returns true).
|
|
*/
|
|
function _CheckCaptcha()
|
|
{
|
|
global $SPECIAL_VALUES,$bReverseCaptchaCompleted;
|
|
global $reCaptchaProcessor;
|
|
|
|
//
|
|
// only check for CAPTCHA once
|
|
//
|
|
if (!$this->_iCaptchaType) {
|
|
//
|
|
// check for full CAPTCHA attempt
|
|
// first, check for reCaptcha
|
|
//
|
|
if (isset($reCaptchaProcessor) && $SPECIAL_VALUES["arverify"] !== "") {
|
|
$this->_iCaptchaType = $this->_iFull;
|
|
$s_error = '';
|
|
if ($reCaptchaProcessor->Check($SPECIAL_VALUES["arverify"],$SPECIAL_VALUES,$s_error)) {
|
|
$this->_bCaptchaOK = true;
|
|
} else {
|
|
$this->_bCaptchaOK = false;
|
|
//
|
|
// report the error
|
|
//
|
|
WriteARLog($this->_sTo,$this->_sSubject,
|
|
GetMessage(MSG_LOG_RECAPTCHA,array("ERR" => $s_error),false));
|
|
UserError("recaptcha",GetMessage(MSG_RECAPTCHA_MATCH,array("ERR" => $s_error)));
|
|
}
|
|
}
|
|
//
|
|
// now check for our verifyimg or Captcha Creator
|
|
//
|
|
elseif ($SPECIAL_VALUES["arverify"] !== "") {
|
|
//
|
|
// allow spaces in the user's input, except for reCaptcha
|
|
//
|
|
$s_arverify = str_replace(" ","",$SPECIAL_VALUES["arverify"]);
|
|
$this->_iCaptchaType = $this->_iFull;
|
|
//
|
|
// full CAPTCHA has been attempted
|
|
// VerifyImgString is from Tectite's simple verifyimg.php CAPTCHA.
|
|
// turing_string is from Captcha Creator
|
|
//
|
|
if (IsSetSession("VerifyImgString") || IsSetSession("turing_string")) {
|
|
$b_match = false;
|
|
//
|
|
// the user's entry must match the value in the session
|
|
//
|
|
if (IsSetSession("VerifyImgString")) {
|
|
if (strtoupper($s_arverify) === strtoupper(GetSession("VerifyImgString"))) {
|
|
$b_match = true;
|
|
}
|
|
} else {
|
|
if (strtoupper($s_arverify) === strtoupper(GetSession("turing_string"))) {
|
|
$b_match = true;
|
|
}
|
|
}
|
|
if ($b_match) {
|
|
$this->_bCaptchaOK = true;
|
|
} else {
|
|
WriteARLog($this->_sTo,$this->_sSubject,
|
|
GetMessage(MSG_LOG_NO_MATCH,array(),false));
|
|
UserError("ar_verify",GetMessage(MSG_ARESP_NO_MATCH));
|
|
}
|
|
} else {
|
|
//
|
|
// ...and it has failed because there's no session data
|
|
//
|
|
WriteARLog($this->_sTo,$this->_sSubject,
|
|
GetMessage(MSG_LOG_NO_VERIMG,array(),false));
|
|
ErrorWithIgnore("verify_failed",GetMessage(MSG_ARESP_NO_AUTH),true);
|
|
}
|
|
} elseif (Settings::get('ENABLE_ATTACK_DETECTION') &&
|
|
!Settings::isEmpty('ATTACK_DETECTION_REVERSE_CAPTCHA')
|
|
) {
|
|
//
|
|
// Reverse CAPTCHA has been configured
|
|
//
|
|
$this->_iCaptchaType = $this->_iRev;
|
|
$this->_bCaptchaOK = $bReverseCaptchaCompleted;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Method: AutoResponder::_Send
|
|
* Parameters: $b_use_template if true, allow template, otherwise file
|
|
* Returns: void
|
|
* Description:
|
|
* Sends an autoreponse using a template.
|
|
*/
|
|
function Send($b_use_template)
|
|
{
|
|
global $SPECIAL_VALUES;
|
|
//
|
|
// declare some global vars for the hook system calls below
|
|
//
|
|
global $aFieldOrder,$aCleanedValues,$aRawDataValues,$aAllRawValues,$aFileVars;
|
|
|
|
FMDebug("Sending auto response: " . ($b_use_template ? "template" : "plain"));
|
|
|
|
//
|
|
// Hook system: before sending auto response
|
|
//
|
|
if (($incFile = hookFileExists("fmhookprearesp.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
if (!$this->_SendEmail($this->_sTo,$this->_sSubject,$aRawDataValues,$b_use_template)) {
|
|
WriteARLog($this->_sTo,$this->_sSubject,
|
|
GetMessage(MSG_LOG_FAILED,array(),false));
|
|
SendAlert(GetMessage(MSG_ARESP_FAILED));
|
|
} else {
|
|
WriteARLog($this->_sTo,$this->_sSubject,
|
|
GetMessage(MSG_LOG_OK,array(),false));
|
|
//
|
|
// Hook system: after sending auto response
|
|
//
|
|
if (($incFile = hookFileExists("fmhookpostaresp.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Method: AutoResponder::_SendEmail
|
|
* Parameters: $s_to the email address to send to
|
|
* $s_subj the subject line
|
|
* $a_values field values to access
|
|
* $b_use_template if true, use a template, otherwise plain file
|
|
* Returns: bool true on success, otherwise false
|
|
* Description:
|
|
* Sends an autoresponse email to the user.
|
|
*/
|
|
function _SendEmail($s_to,$s_subj,$a_values,$b_use_template)
|
|
{
|
|
global $ValidEmails;
|
|
|
|
$a_headers = array();
|
|
$s_mail_text = "";
|
|
$s_from_addr = GetAROption("FromAddr");
|
|
|
|
if (isset($s_from_addr)) {
|
|
$s_from_addr = UnMangle($s_from_addr);
|
|
if (!$ValidEmails->CheckAddress($s_from_addr)) {
|
|
SendAlert(GetMessage(MSG_INVALID_SENDER,
|
|
array("LOC" => "FromAddr in autorespond","EMAIL" => $s_from_addr)));
|
|
unset($s_from_addr);
|
|
}
|
|
}
|
|
if (!isset($s_from_addr)) {
|
|
$s_from_addr = "";
|
|
if (!Settings::isEmpty('FROM_USER')) {
|
|
if (Settings::get('FROM_USER') != "NONE") {
|
|
$s_from_addr = Settings::get('FROM_USER');
|
|
}
|
|
} else {
|
|
global $SERVER;
|
|
|
|
$s_from_addr = "FormMail@" . $SERVER;
|
|
}
|
|
}
|
|
|
|
if (!empty($s_from_addr)) {
|
|
$s_from_name = GetAROption('FromName');
|
|
$a_headers['From'] = MakeFromLine($s_from_addr,$s_from_name);
|
|
}
|
|
if (Settings::get('FIXED_SENDER') !== "") {
|
|
$s_sender = Settings::get('FIXED_SENDER');
|
|
} else {
|
|
$s_sender = $s_from_addr;
|
|
}
|
|
$s_type = "";
|
|
if ($b_use_template) {
|
|
if (IsAROptionSet('PlainTemplate')) {
|
|
$s_type .= "PlainTemplate ";
|
|
$s_template = GetAROption("PlainTemplate");
|
|
if (!ProcessTemplate($s_template,$a_lines,$a_values,
|
|
GetAROption('TemplateMissing'),
|
|
'SubstituteValuePlain')
|
|
) {
|
|
return (false);
|
|
}
|
|
FMDebug("AutoRespond: PlainTemplate " . count($a_lines) . " lines");
|
|
$s_mail_text = implode(Settings::get('BODY_LF'),$a_lines);
|
|
}
|
|
if (IsAROptionSet("HTMLTemplate")) {
|
|
$s_type .= "HTMLTemplate ";
|
|
if (!MakeMimeMail($s_mail_text,$a_headers,$a_values,
|
|
GetAROption("HTMLTemplate"),
|
|
GetAROption('TemplateMissing'))
|
|
) {
|
|
return (false);
|
|
}
|
|
FMDebug("AutoRespond: HTMLTemplate " . strlen($s_mail_text) . " bytes");
|
|
}
|
|
} else {
|
|
|
|
if (IsAROptionSet('PlainFile')) {
|
|
$s_type .= "PlainFile ";
|
|
//
|
|
// load the plain text file from the templates area
|
|
//
|
|
if (Settings::isEmpty('TEMPLATEDIR') && Settings::isEmpty('TEMPLATEURL')) {
|
|
SendAlert(GetMessage(MSG_TEMPLATES));
|
|
return (false);
|
|
}
|
|
$s_file = GetAROption("PlainFile");
|
|
if (($a_lines = LoadTemplate($s_file,Settings::get('TEMPLATEDIR'),Settings::get('TEMPLATEURL'),
|
|
true)) === false
|
|
) {
|
|
return (false);
|
|
}
|
|
$s_mail_text = implode(Settings::get('BODY_LF'),$a_lines);
|
|
FMDebug("AutoRespond: PlainFile " . count($a_lines) . " lines");
|
|
}
|
|
if (IsAROptionSet("HTMLFile")) {
|
|
$s_type .= "HTMLFile ";
|
|
if (!MakeMimeMail($s_mail_text,$a_headers,$a_values,
|
|
GetAROption("HTMLFile"),"",
|
|
false,"",array(),array(),false)
|
|
) {
|
|
return (false);
|
|
}
|
|
FMDebug("AutoRespond: HTMLTemplate " . strlen($s_mail_text) . " bytes");
|
|
}
|
|
}
|
|
if (strlen($s_mail_text) == 0) {
|
|
SendAlert(GetMessage(MSG_ARESP_EMPTY),array("TYPE" => $s_type));
|
|
}
|
|
FMDebug("AutoRespond: message is " . strlen($s_mail_text) . " bytes");
|
|
return (SendCheckedMail($s_to,$s_subj,$s_mail_text,$s_sender,$a_headers));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Class: SessionAccess
|
|
* Description:
|
|
* Implements access to the general PHP session.
|
|
* This provides a secure way of copy data to and from the
|
|
* user's PHP session.
|
|
*/
|
|
|
|
class SessionAccess
|
|
{
|
|
var $_aAccessList;
|
|
|
|
/*
|
|
* Method: SessionAccess ctor
|
|
* Parameters: $a_access_list list of variables that can be accessed
|
|
* Returns: n/a
|
|
* Description:
|
|
* Constructs the object.
|
|
*/
|
|
function __construct($a_access_list)
|
|
{
|
|
$this->_aAccessList = $a_access_list;
|
|
}
|
|
|
|
/*
|
|
* Method: SessionAccess::CopyIn
|
|
* Parameters: $a_vars reference to an array of values (keyed on name)
|
|
* $b_overwrite_empty if true, the session value will overwrite
|
|
* an empty array variable
|
|
* Returns: int number of values copied
|
|
* Description:
|
|
* Copies in the list of variables from the session to the given array.
|
|
*/
|
|
function CopyIn(&$a_vars,$b_overwrite_empty)
|
|
{
|
|
//$s_db = "Session CopyIn:\n";
|
|
$n_copied = 0;
|
|
foreach ($this->_aAccessList as $s_var_name) {
|
|
if (IsSetSession($s_var_name)) {
|
|
if (!isset($a_vars[$s_var_name]) ||
|
|
($b_overwrite_empty &&
|
|
FieldManager::IsEmpty($a_vars[$s_var_name]))
|
|
) {
|
|
$a_vars[$s_var_name] = GetSession($s_var_name);
|
|
//$s_db .= "$s_var_name='".$a_vars[$s_var_name]."'\n";
|
|
$n_copied++;
|
|
}
|
|
}
|
|
}
|
|
//SendAlert($s_db);
|
|
return ($n_copied);
|
|
}
|
|
|
|
/*
|
|
* Method: SessionAccess::CopyOut
|
|
* Parameters: $a_vars reference to an array of values (keyed on name)
|
|
* $a_fields an array of fields to copy
|
|
* Returns: int number of values copied
|
|
* Description:
|
|
* Copies the variables from the given array into the session.
|
|
* The list of fields to copy is specified in _aAccessList.
|
|
* If $a_fields is provided, it contains a list of fields to copy, which
|
|
* limits the fields selected from _aAccessList, otherwise all fields
|
|
* listed in _aAccessList are copied.
|
|
*/
|
|
function CopyOut($a_vars,$a_fields = array())
|
|
{
|
|
//$s_db = "Session CopyOut:\n";
|
|
|
|
$n_copied = 0;
|
|
foreach ($this->_aAccessList as $s_var_name) {
|
|
if (isset($a_vars[$s_var_name])) {
|
|
if (empty($a_fields) || in_array($s_var_name,$a_fields)) {
|
|
SetSession($s_var_name,$a_vars[$s_var_name]);
|
|
//$s_db .= "$s_var_name='".GetSession($s_var_name)."'\n";
|
|
$n_copied++;
|
|
}
|
|
}
|
|
}
|
|
//SendAlert($s_db);
|
|
return ($n_copied);
|
|
}
|
|
}
|
|
|
|
$SessionAccessor = new SessionAccess(Settings::get('SESSION_ACCESS'));
|
|
|
|
$bAdvTemplates = false;
|
|
if (Settings::get('ADVANCED_TEMPLATES') && (
|
|
!Settings::isEmpty('TEMPLATEDIR')
|
|
|| !Settings::isEmpty('TEMPLATEURL')
|
|
|| !Settings::isEmpty('MULTIFORMDIR')
|
|
|| !Settings::isEmpty('MULTIFORMURL')
|
|
)) {
|
|
$bAdvTemplates = true;
|
|
}
|
|
|
|
//
|
|
// Hook system: after configuring Advanced Templates
|
|
//
|
|
if (($incFile = hookFileExists("fmhookpostadvtemplates.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
|
|
//
|
|
// Hook system: before return check and processing
|
|
//
|
|
if (($incFile = hookFileExists("fmhookprereturnform.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
|
|
if (isset($aGetVars["return"]) && is_numeric($aGetVars["return"])) {
|
|
//
|
|
// if advanced templates are in use, we will need them for
|
|
// performing a multi-page form return
|
|
//
|
|
if ($bAdvTemplates) {
|
|
$FMCTEMPLATE_PROC = true;
|
|
if (!include_once("$MODULEDIR/$FMCOMPUTE")) {
|
|
Error("load_fmcompute",
|
|
GetMessage(MSG_LOAD_FMCOMPUTE,
|
|
array("FILE" => "$MODULEDIR/$FMCOMPUTE",
|
|
"ERROR" => $php_errormsg
|
|
)),
|
|
false,
|
|
false);
|
|
}
|
|
}
|
|
MultiFormReturn($aGetVars["return"]);
|
|
//
|
|
// Hook system: after multi-page return to form output
|
|
//
|
|
if (($incFile = hookFileExists("fmhookpostreturnform.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
FormMailExit();
|
|
}
|
|
|
|
//
|
|
// Hook system: after initialization
|
|
//
|
|
if (($incFile = hookFileExists("fmhookpostinit.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
|
|
$ExecEnv->checkUploadSize();
|
|
$ExecEnv->checkFileUploadSize($aFileVars);
|
|
//
|
|
// check configuration values for potential security problems
|
|
//
|
|
CheckConfig();
|
|
|
|
//
|
|
// otherwise, do the real processing of FormMail
|
|
//
|
|
$aStrippedFormVars = $aAllRawValues = StripGPCArray($aFormVars);
|
|
|
|
if (Settings::get('ENABLE_ATTACK_DETECTION')) {
|
|
DetectAttacks($aAllRawValues);
|
|
}
|
|
|
|
//
|
|
// Copy in configured session variables, overwriting any
|
|
// fields that are empty.
|
|
//
|
|
$SessionAccessor->CopyIn($aAllRawValues,true);
|
|
$SessionAccessor->CopyIn($aStrippedFormVars,true);
|
|
|
|
//
|
|
// process the options
|
|
//
|
|
ProcessMailOptions($aAllRawValues);
|
|
ProcessCRMOptions($aAllRawValues);
|
|
ProcessAROptions($aAllRawValues);
|
|
ProcessFilterOptions($aAllRawValues);
|
|
|
|
//
|
|
// create any derived fields
|
|
// NOTE: it's important that derivation occurs before cleaned values
|
|
// are created. If derivation occurred after cleaning or it created
|
|
// fields that were assumed to be clean, then an attack could create
|
|
// forged email headers using derive_fields. The code is safe with
|
|
// the logic sequence below.
|
|
//
|
|
$aAllRawValues = CreateDerived($aAllRawValues);
|
|
|
|
list($aFieldOrder,$aCleanedValues,$aRawDataValues) = ParseInput($aAllRawValues);
|
|
|
|
FilterFiles($aFileVars);
|
|
|
|
//
|
|
// if we're processing multi-forms, then merge in the raw data from previous
|
|
// forms unless this is the first page of the form sequence
|
|
//
|
|
if (IsSetSession("FormList") && $SPECIAL_VALUES["multi_start"] != 1) {
|
|
list($aFieldOrder,$aCleanedValues,
|
|
$aRawDataValues,$aAllRawValues,$aFileVars) = GetMultiValues(
|
|
GetSession("FormList"),
|
|
GetSession("FormIndex"),
|
|
$aFieldOrder,$aCleanedValues,
|
|
$aRawDataValues,$aAllRawValues,
|
|
$aFileVars);
|
|
}
|
|
|
|
if ($SPECIAL_VALUES["file_names"] !== "") {
|
|
list($aFieldOrder,$aCleanedValues,$aRawDataValues,$aAllRawValues,$aFileVars) =
|
|
SetFileNames($SPECIAL_VALUES["file_names"],$aFieldOrder,
|
|
$aCleanedValues,$aRawDataValues,$aAllRawValues,$aFileVars);
|
|
}
|
|
|
|
//
|
|
// Hook system: after loading and processing data
|
|
//
|
|
if (($incFile = hookFileExists("fmhookload.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
|
|
if (Settings::get('FORM_INI_FILE') !== "") {
|
|
ProcessFormIniFile(Settings::get('FORM_INI_FILE'));
|
|
//
|
|
// Hook system: after processing INI file
|
|
//
|
|
if (($incFile = hookFileExists("fmhookinifile.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
}
|
|
|
|
$bDoneSomething = false;
|
|
if (Settings::get('DB_SEE_INPUT')) {
|
|
/***
|
|
* echo "<pre>";
|
|
* print_r($aFormVars);
|
|
* print_r($aServerVars);
|
|
* echo "</pre>";
|
|
* exit;
|
|
****/
|
|
CreatePage(implode("\n",$FORMATTED_INPUT),"Debug Output - Fields Submitted");
|
|
ZapSession();
|
|
FormMailExit();
|
|
}
|
|
|
|
if (!empty($SPECIAL_VALUES["fmcompute"]) || $bAdvTemplates) {
|
|
$FM_UserErrors = array();
|
|
//
|
|
// Generalized interface between FMCompute and FormMail functions
|
|
//
|
|
/** @noinspection PhpUnused */
|
|
function FM_CallFunction($s_func,$a_params,&$m_return,
|
|
&$s_mesg,&$a_debug,&$a_alerts)
|
|
{
|
|
switch ($s_func) {
|
|
case "FMFatalError":
|
|
SendComputeAlerts();
|
|
if (count($a_params) < 3) {
|
|
Error("fmcompute_call",GetMessage(MSG_CALL_PARAM_COUNT,
|
|
array("FUNC" => $s_func,
|
|
"COUNT" => count($a_params)
|
|
)),
|
|
false,false);
|
|
} else {
|
|
Error("fmcompute_error",$a_params[0],$a_params[1],$a_params[2]);
|
|
}
|
|
break;
|
|
|
|
case "FMFatalUserError":
|
|
SendComputeAlerts();
|
|
if (count($a_params) < 1) {
|
|
Error("fmcompute_call",GetMessage(MSG_CALL_PARAM_COUNT,
|
|
array("FUNC" => $s_func,
|
|
"COUNT" => count($a_params)
|
|
)),
|
|
false,false);
|
|
} else {
|
|
UserError("fmcompute_usererror",$a_params[0]);
|
|
}
|
|
break;
|
|
|
|
case "FMUserError":
|
|
if (count($a_params) < 1) {
|
|
SendComputeAlerts();
|
|
Error("fmcompute_call",GetMessage(MSG_CALL_PARAM_COUNT,
|
|
array("FUNC" => $s_func,
|
|
"COUNT" => count($a_params)
|
|
)),
|
|
false,false);
|
|
} else {
|
|
global $FM_UserErrors;
|
|
|
|
$FM_UserErrors[] = $a_params[0];
|
|
}
|
|
break;
|
|
|
|
case "FMSaveAllFilesToRepository":
|
|
if (count($a_params) != 0) {
|
|
SendComputeAlerts();
|
|
Error("fmcompute_call",GetMessage(MSG_CALL_PARAM_COUNT,
|
|
array("FUNC" => $s_func,
|
|
"COUNT" => count($a_params)
|
|
)),
|
|
false,false);
|
|
} else {
|
|
$m_return = SaveAllFilesToRepository();
|
|
}
|
|
break;
|
|
|
|
case "FMDeleteFileFromRepository":
|
|
if (count($a_params) != 1) {
|
|
SendComputeAlerts();
|
|
Error("fmcompute_call",GetMessage(MSG_CALL_PARAM_COUNT,
|
|
array("FUNC" => $s_func,
|
|
"COUNT" => count($a_params)
|
|
)),
|
|
false,false);
|
|
} else {
|
|
$m_return = DeleteFileFromRepository($a_params[0]);
|
|
}
|
|
break;
|
|
|
|
case "FMNextNum":
|
|
if (count($a_params) != 2) {
|
|
SendComputeAlerts();
|
|
Error("fmcompute_call",GetMessage(MSG_CALL_PARAM_COUNT,
|
|
array("FUNC" => $s_func,
|
|
"COUNT" => count($a_params)
|
|
)),
|
|
false,false);
|
|
} else {
|
|
$i_pad = $a_params[0];
|
|
$i_base = $a_params[1];
|
|
if ($i_base < 2 || $i_base > 36) {
|
|
Error("fmcompute_call",GetMessage(MSG_CALL_INVALID_PARAM,
|
|
array("FUNC" => $s_func,
|
|
"PARAM" => 2,
|
|
"CORRECT" => 2. . .36
|
|
)),
|
|
false,false);
|
|
}
|
|
$m_return = GetNextNum($i_pad,$i_base);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
$s_mesg = GetMessage(MSG_CALL_UNK_FUNC,array("FUNC" => $s_func));
|
|
return (false);
|
|
}
|
|
return (true);
|
|
}
|
|
|
|
//
|
|
// register useful FormMail functions with FMCompute module
|
|
//
|
|
function RegisterFormMailFunctions($fmc)
|
|
{
|
|
//
|
|
// Allows the user to call "Error" from within a computation
|
|
//
|
|
if (($s_msg = $fmc->RegisterExternalFunction("PHP","void",
|
|
"FMFatalError",
|
|
array("string","bool","bool"),
|
|
"FM_CallFunction")) !== true
|
|
) {
|
|
Error("fmcompute_reg",
|
|
GetMessage(MSG_REG_FMCOMPUTE,
|
|
array("FUNC" => "FMFatalError",
|
|
"ERROR" => $s_msg
|
|
)),
|
|
false,false);
|
|
}
|
|
//
|
|
// Allows the user to call "UserError" from within a computation
|
|
//
|
|
if (($s_msg = $fmc->RegisterExternalFunction("PHP","void",
|
|
"FMFatalUserError",
|
|
array("string"),
|
|
"FM_CallFunction")) !== true
|
|
) {
|
|
Error("fmcompute_reg",
|
|
GetMessage(MSG_REG_FMCOMPUTE,
|
|
array("FUNC" => "FMFatalUserError",
|
|
"ERROR" => $s_msg
|
|
)),
|
|
false,false);
|
|
}
|
|
|
|
//
|
|
// Allows the user to record an error to be displayed
|
|
// from within a computation.
|
|
//
|
|
if (($s_msg = $fmc->RegisterExternalFunction("PHP","void",
|
|
"FMUserError",
|
|
array("string"),
|
|
"FM_CallFunction")) !== true
|
|
) {
|
|
Error("fmcompute_reg",
|
|
GetMessage(MSG_REG_FMCOMPUTE,
|
|
array("FUNC" => "FMUserError",
|
|
"ERROR" => $s_msg
|
|
)),
|
|
false,false);
|
|
}
|
|
|
|
//
|
|
// Saves files to the repository.
|
|
//
|
|
if (($s_msg = $fmc->RegisterExternalFunction("PHP","bool",
|
|
"FMSaveAllFilesToRepository",
|
|
array(),
|
|
"FM_CallFunction")) !== true
|
|
) {
|
|
Error("fmcompute_reg",
|
|
GetMessage(MSG_REG_FMCOMPUTE,
|
|
array("FUNC" => "FMSaveAllFilesToRepository",
|
|
"ERROR" => $s_msg
|
|
)),
|
|
false,false);
|
|
}
|
|
|
|
//
|
|
// Delete a file from the repository.
|
|
//
|
|
if (($s_msg = $fmc->RegisterExternalFunction("PHP","bool",
|
|
"FMDeleteFileFromRepository",
|
|
array("string"),
|
|
"FM_CallFunction")) !== true
|
|
) {
|
|
Error("fmcompute_reg",
|
|
GetMessage(MSG_REG_FMCOMPUTE,
|
|
array("FUNC" => "FMDeleteFileFromRepository",
|
|
"ERROR" => $s_msg
|
|
)),
|
|
false,false);
|
|
}
|
|
|
|
//
|
|
// Generate a Next Number
|
|
//
|
|
if (($s_msg = $fmc->RegisterExternalFunction("PHP","string",
|
|
"FMNextNum",
|
|
array("int","int"),
|
|
"FM_CallFunction")) !== true
|
|
) {
|
|
Error("fmcompute_reg",
|
|
GetMessage(MSG_REG_FMCOMPUTE,
|
|
array("FUNC" => "FMNextNum",
|
|
"ERROR" => $s_msg
|
|
)),
|
|
false,false);
|
|
}
|
|
}
|
|
|
|
//
|
|
// load the fmcompute module
|
|
//
|
|
if (!empty($SPECIAL_VALUES["fmcompute"])) {
|
|
$FMCOMPUTE_CLASS = true;
|
|
$FMCOMPUTE_NODEBUG = true;
|
|
}
|
|
if ($bAdvTemplates) {
|
|
$FMCTEMPLATE_PROC = true;
|
|
}
|
|
if (!include_once("$MODULEDIR/$FMCOMPUTE")) {
|
|
Error("load_fmcompute",
|
|
GetMessage(MSG_LOAD_FMCOMPUTE,
|
|
array("FILE" => "$MODULEDIR/$FMCOMPUTE",
|
|
"ERROR" => $php_errormsg
|
|
)),
|
|
false,false);
|
|
}
|
|
if (!empty($SPECIAL_VALUES["fmcompute"])) {
|
|
/** @noinspection PhpUndefinedVariableInspection */
|
|
RegisterFormMailFunctions($FMCalc);
|
|
|
|
//
|
|
// if GeoIP support is specified, load that module now
|
|
//
|
|
if (!Settings::isEmpty('GEOIP_LIC')) {
|
|
$FMMODULE_LOAD = true; // signal module load
|
|
if (!include_once("$MODULEDIR/$FMGEOIP")) {
|
|
Error("load_module",
|
|
GetMessage(MSG_LOAD_MODULE,
|
|
array("FILE" => "$MODULEDIR/$FMGEOIP",
|
|
"ERROR" => $php_errormsg
|
|
)),
|
|
false,false);
|
|
}
|
|
//
|
|
// load the license and register the module
|
|
//
|
|
/** @noinspection PhpUndefinedClassInspection */
|
|
$GeoIP = new FMGeoIP(Settings::get('GEOIP_LIC'));
|
|
/** @noinspection PhpUndefinedMethodInspection */
|
|
if (!$GeoIP->RegisterModule($FMCalc)) {
|
|
/** @noinspection PhpUndefinedMethodInspection */
|
|
Error("reg_module",
|
|
GetMessage(MSG_REGISTER_MODULE,
|
|
array("NAME" => "FMGeoIP",
|
|
"ERROR" => $GeoIP->GetError()
|
|
)),
|
|
false,false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isset($SPECIAL_VALUES["multi_go_back"]) && !empty($SPECIAL_VALUES["multi_go_back"])) {
|
|
if (!IsSetSession("FormList") || GetSession("FormIndex") == 0) {
|
|
ErrorWithIgnore("go_back",GetMessage(MSG_GO_BACK),false,false);
|
|
}
|
|
MultiKeep(); // store any "multi_keep" data just submitted
|
|
//
|
|
// save back to the session any variables that have been specified in
|
|
// multi_keep
|
|
//
|
|
if (isset($SPECIAL_VALUES["multi_keep"]) && !empty($SPECIAL_VALUES["multi_keep"])) {
|
|
$SessionAccessor->CopyOut($aAllRawValues,$SPECIAL_VALUES["multi_keep"]);
|
|
}
|
|
MultiFormReturn(GetSession("FormIndex") - 1);
|
|
// echo "Form index = ".GetSession("FormIndex");
|
|
//
|
|
// Hook system: after multi-page return to form output
|
|
//
|
|
if (($incFile = hookFileExists("fmhookpostreturnform.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
FormMailExit();
|
|
}
|
|
|
|
//
|
|
// This is the check for spiders; I can't imagine a spider will
|
|
// ever use the POST method.
|
|
//
|
|
if ($bIsGetMethod && count($aFormVars) == 0) {
|
|
if (!Settings::get('ALLOW_GET_METHOD') && $bHasGetData) {
|
|
CreatePage(GetMessage(MSG_GET_DISALLOWED),GetMessage(MSG_FORM_ERROR));
|
|
} else {
|
|
CreatePage(GetMessage(MSG_NO_DATA_PAGE),GetMessage(MSG_FORM_ERROR));
|
|
}
|
|
ZapSession();
|
|
FormMailExit();
|
|
}
|
|
|
|
//
|
|
// Hook system: before performing required and conditions etc.
|
|
//
|
|
if (($incFile = hookFileExists("fmhookprechecks.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
|
|
//
|
|
// check for required fields
|
|
//
|
|
if (!CheckRequired($SPECIAL_VALUES["required"],$aAllRawValues,$sMissing,$aMissingList)) {
|
|
UserError("missing_fields",GetMessage(MSG_REQD_ERROR),$sMissing,$aMissingList);
|
|
}
|
|
|
|
//
|
|
// check complex conditions
|
|
//
|
|
$fmConditions = new Conditions($SPECIAL_VALUES["conditions"],$sMissing,$aMissingList);
|
|
if (!$fmConditions->Check($aAllRawValues,$aFileVars)) {
|
|
UserError("failed_conditions",GetMessage(MSG_COND_ERROR),$sMissing,$aMissingList);
|
|
}
|
|
|
|
//
|
|
// check CAPTCHA
|
|
//
|
|
CheckCaptchaSubmit();
|
|
|
|
//
|
|
// Hook system: after performing required and conditions etc.
|
|
//
|
|
if (($incFile = hookFileExists("fmhookchecks.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
|
|
if (!empty($SPECIAL_VALUES["fmmodules"])) {
|
|
$aModuleList = TrimArray(explode(",",$SPECIAL_VALUES["fmmodules"]));
|
|
$FMMODULE_LOAD = true; // signal module load
|
|
foreach ($aModuleList as $sModule) {
|
|
if (!include_once("$MODULEDIR/$sModule")) {
|
|
Error("load_module",
|
|
GetMessage(MSG_LOAD_MODULE,
|
|
array("FILE" => "$MODULEDIR/$sModule",
|
|
"ERROR" => $php_errormsg
|
|
)),
|
|
false,false);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!empty($SPECIAL_VALUES["fmcompute"])) {
|
|
//
|
|
// Callback function for preg_replace_callback to add line
|
|
// numbers on each match
|
|
//
|
|
function AddLineNumbersCallback($a_matches)
|
|
{
|
|
global $iAddLineNumbersCounter;
|
|
|
|
return (sprintf("%d:",++$iAddLineNumbersCounter) . $a_matches[0]);
|
|
}
|
|
|
|
//
|
|
// Add line numbers to some code
|
|
//
|
|
function AddLineNumbers($s_code)
|
|
{
|
|
global $iAddLineNumbersCounter;
|
|
|
|
$iAddLineNumbersCounter = 0;
|
|
return (preg_replace_callback('/^/m','AddLineNumbersCallback',$s_code));
|
|
}
|
|
|
|
//
|
|
// Load some more code into FMCalc
|
|
//
|
|
function Load($s_code)
|
|
{
|
|
global $FMCalc;
|
|
|
|
$a_mesgs = array();
|
|
// echo "Loading '$s_code'";
|
|
if ($FMCalc->Parse($s_code,$a_mesgs) === false) {
|
|
$s_msgs = "";
|
|
foreach ($a_mesgs as $a_msg) {
|
|
$s_msgs .= "Line " . $a_msg["LINE"];
|
|
$s_msgs .= ", position " . $a_msg["CHAR"] . ": ";
|
|
$s_msgs .= $a_msg["MSG"] . "\n";
|
|
}
|
|
Error("fmcompute_parse",
|
|
GetMessage(MSG_COMP_PARSE,
|
|
array("CODE" => AddLineNumbers($s_code),
|
|
"ERRORS" => $s_msgs
|
|
)),
|
|
false,
|
|
false);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Send any alerts found in FMCalc
|
|
//
|
|
function SendComputeAlerts()
|
|
{
|
|
global $FMCalc;
|
|
|
|
$a_alerts = $FMCalc->GetAlerts();
|
|
if (count($a_alerts) > 0) {
|
|
SendAlert(GetMessage(MSG_COMP_ALERT,
|
|
array("ALERTS" => implode("\n",StripHTML($a_alerts)))));
|
|
}
|
|
$a_debug = $FMCalc->GetDebug();
|
|
if (count($a_debug) > 0) {
|
|
SendAlert(GetMessage(MSG_COMP_DEBUG,
|
|
array("DEBUG" => implode("\n",StripHTML($a_debug)))));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Perform the computations in FMCalc
|
|
//
|
|
function Compute(&$a_field_order,&$a_cleaned_values,&$a_raw_data_values,
|
|
&$a_values)
|
|
{
|
|
global $FMCalc,$FM_UserErrors;
|
|
|
|
$a_mesgs = array();
|
|
$FM_UserErrors = array();
|
|
if (($a_flds = $FMCalc->Execute($a_mesgs)) !== false) {
|
|
SendComputeAlerts();
|
|
foreach ($a_flds as $s_name => $s_value) {
|
|
$a_values[$s_name] = $s_value;
|
|
ProcessField($s_name,$s_value,$a_field_order,
|
|
$a_cleaned_values,$a_raw_data_values);
|
|
}
|
|
if (count($FM_UserErrors) > 0) {
|
|
UserError("fmcompute_usererrors",GetMessage(MSG_USER_ERRORS),
|
|
"",$FM_UserErrors);
|
|
}
|
|
} else {
|
|
SendComputeAlerts();
|
|
Error("fmcompute_exec",GetMessage(MSG_COMP_EXEC,
|
|
array("ERRORS" => implode("\n",$a_mesgs))),false,false);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Register all fields; a future improvement should use a call-back
|
|
// function so as not to copy all the data into the FMCompute object
|
|
//
|
|
function RegisterData($a_form_data,$a_file_vars)
|
|
{
|
|
global $FMCalc;
|
|
|
|
foreach ($a_form_data as $s_name => $s_value) {
|
|
if (isset($s_name) && isset($s_value)) {
|
|
if (($s_msg = $FMCalc->RegisterExternalData("PHP","string",
|
|
$s_name,"c",$s_value)) !== true
|
|
) {
|
|
Error("fmcompute_regdata",GetMessage(MSG_COMP_REG_DATA,
|
|
array("NAME" => $s_name,"ERROR" => $s_msg)),false,false);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($a_file_vars as $s_fld_name => $a_file_spec) {
|
|
if (FieldManager::IsUploadedFile($a_file_spec)) {
|
|
if (isset($a_file_spec["new_name"])) {
|
|
//
|
|
// we ignore errors here, because name_of_ field often already
|
|
// exists from the above loop
|
|
//
|
|
$FMCalc->RegisterExternalData("PHP","string",
|
|
"name_of_" . $s_fld_name,"c",$a_file_spec["new_name"]);
|
|
}
|
|
$s_value = $a_file_spec["name"];
|
|
} else {
|
|
$s_value = "";
|
|
}
|
|
if (($s_msg = $FMCalc->RegisterExternalData("PHP","string",
|
|
$s_fld_name,"c",$s_value)) !== true
|
|
) {
|
|
Error("fmcompute_regdata",GetMessage(MSG_COMP_REG_DATA,
|
|
array("NAME" => $s_fld_name,"ERROR" => $s_msg)),false,false);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: MergeFileArrays
|
|
* Parameters: $a_new_files the list of files just submitted by the form
|
|
* $a_saved_files the list of files that have been previously saved (can be NULL)
|
|
* Returns: array a merged array
|
|
* Description:
|
|
* Intelligently merges two arrays of file definitions.
|
|
* If a file has been newly uploaded, its definition takes precedence.
|
|
*/
|
|
function MergeFileArrays($a_new_files,$a_saved_files)
|
|
{
|
|
if (isset($a_saved_files)) {
|
|
foreach ($a_saved_files as $s_key => $a_def) {
|
|
if (isset($a_new_files[$s_key])) {
|
|
if (!FieldManager::IsUploadedFile($a_new_files[$s_key])) {
|
|
$a_new_files[$s_key] = $a_def;
|
|
}
|
|
} else {
|
|
$a_new_files[$s_key] = $a_def;
|
|
}
|
|
}
|
|
}
|
|
return ($a_new_files);
|
|
}
|
|
|
|
RegisterData($aAllRawValues,MergeFileArrays($aFileVars,IsSetSession("FormSavedFiles") ?
|
|
GetSession("FormSavedFiles") : array()));
|
|
//
|
|
// parse all fmcompute fields
|
|
//
|
|
if (is_array($SPECIAL_VALUES["fmcompute"])) {
|
|
$nCompute = count($SPECIAL_VALUES["fmcompute"]);
|
|
for ($iCompute = 0 ; $iCompute < $nCompute ; $iCompute++) {
|
|
Load($SPECIAL_VALUES["fmcompute"][$iCompute]);
|
|
}
|
|
} else {
|
|
Load($SPECIAL_VALUES["fmcompute"]);
|
|
}
|
|
|
|
//
|
|
// run computations
|
|
//
|
|
Compute($aFieldOrder,$aCleanedValues,$aRawDataValues,$aAllRawValues);
|
|
//
|
|
// Hook system: after computations
|
|
//
|
|
if (($incFile = hookFileExists("fmhookcompute.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
}
|
|
|
|
$bGotGoBack = $bGotNextForm = $bGotGoodTemplate = $bGotGoodUrl = false;
|
|
if (isset($SPECIAL_VALUES["good_url"]) &&
|
|
!empty($SPECIAL_VALUES["good_url"])
|
|
) {
|
|
$bGotGoodUrl = true;
|
|
}
|
|
|
|
if (isset($SPECIAL_VALUES["good_template"]) &&
|
|
!empty($SPECIAL_VALUES["good_template"])
|
|
) {
|
|
$bGotGoodTemplate = true;
|
|
}
|
|
|
|
if (isset($SPECIAL_VALUES["next_form"]) &&
|
|
!empty($SPECIAL_VALUES["next_form"])
|
|
) {
|
|
$bGotNextForm = true;
|
|
}
|
|
|
|
if (isset($SPECIAL_VALUES["multi_go_back"]) &&
|
|
!empty($SPECIAL_VALUES["multi_go_back"])
|
|
) {
|
|
$bGotGoBack = true;
|
|
}
|
|
|
|
//
|
|
// it's not valid to specify "next_form" as well as "good_url" or
|
|
// "good_template"; this is because it's ambiguous - do we go to the
|
|
// next form or the final 'thank you' page?
|
|
//
|
|
if ($bGotNextForm && ($bGotGoodTemplate || $bGotGoodUrl)) {
|
|
ErrorWithIgnore("next_plus_good",GetMessage(MSG_NEXT_PLUS_GOOD,array("WHICH" =>
|
|
($bGotGoodUrl ? "good_url" :
|
|
"good_template")
|
|
)), false,false);
|
|
}
|
|
|
|
MultiFormLogic();
|
|
|
|
//
|
|
// Hook system: after multi-page form logic
|
|
//
|
|
if (($incFile = hookFileExists("fmhookmulti.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
|
|
//
|
|
// write to the CSV database
|
|
//
|
|
if (!Settings::isEmpty('CSVDIR') && isset($SPECIAL_VALUES["csvfile"]) &&
|
|
!empty($SPECIAL_VALUES["csvfile"])
|
|
) {
|
|
//
|
|
// Hook system: before writing CSV file
|
|
//
|
|
if (($incFile = hookFileExists("fmhookprecsv.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
WriteCSVFile(Settings::get('CSVDIR') . "/" . basename($SPECIAL_VALUES["csvfile"]),$aAllRawValues);
|
|
//
|
|
// Hook system: after writing CSV file
|
|
//
|
|
if (($incFile = hookFileExists("fmhookpostcsv.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
$bDoneSomething = true;
|
|
}
|
|
|
|
//
|
|
// write to the log file
|
|
//
|
|
if (!Settings::isEmpty('LOGDIR') && isset($SPECIAL_VALUES["logfile"]) && !empty($SPECIAL_VALUES["logfile"])) {
|
|
//
|
|
// Hook system: before writing log file
|
|
//
|
|
if (($incFile = hookFileExists("fmhookprelog.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
WriteLog(Settings::get('LOGDIR') . "/" . basename($SPECIAL_VALUES["logfile"]));
|
|
//
|
|
// Hook system: after writing log file
|
|
//
|
|
if (($incFile = hookFileExists("fmhookpostlog.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
$bDoneSomething = true;
|
|
}
|
|
|
|
//
|
|
// send to the CRM
|
|
//
|
|
if (isset($SPECIAL_VALUES["crm_url"]) && isset($SPECIAL_VALUES["crm_spec"]) &&
|
|
!empty($SPECIAL_VALUES["crm_url"]) && !empty($SPECIAL_VALUES["crm_spec"])
|
|
) {
|
|
$sCRM = GetCRMURL($SPECIAL_VALUES["crm_spec"],$aAllRawValues,$SPECIAL_VALUES["crm_url"]);
|
|
if (!empty($sCRM)) {
|
|
$aCRMReturnData = array();
|
|
//
|
|
// Hook system: before sending to CRM
|
|
//
|
|
if (($incFile = hookFileExists("fmhookprecrm.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
if (!SendToCRM($sCRM,$aCRMReturnData)) {
|
|
//
|
|
// CRM interface failed, check if the form wants an error
|
|
// displayed
|
|
//
|
|
if (IsCRMOptionSet("ErrorOnFail")) {
|
|
Error("crm_failed",GetMessage(MSG_CRM_FAILURE,
|
|
array("URL" => $sCRM,
|
|
"DATA" => GetObjectAsString($aCRMReturnData)
|
|
)),
|
|
false,false);
|
|
}
|
|
} else
|
|
//
|
|
// append the returned data to the raw data values of the form
|
|
//
|
|
{
|
|
$aRawDataValues = array_merge($aRawDataValues,$aCRMReturnData);
|
|
}
|
|
//
|
|
// Hook system: after sending to CRM
|
|
//
|
|
if (($incFile = hookFileExists("fmhookpostcrm.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
$bDoneSomething = true;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check obsolete SendMailFOption
|
|
//
|
|
if (IsMailOptionSet("SendMailFOption")) {
|
|
SendAlert(GetMessage(MSG_FOPTION_WARN,array("LINE" => Settings::get('SENDMAIL_F_OPTION_LINE'))),
|
|
false,true);
|
|
}
|
|
|
|
$AutoResp = new AutoResponder();
|
|
//
|
|
// check for autoresponse problems
|
|
//
|
|
$AutoResp->Process(true);
|
|
|
|
//
|
|
// Hook system: before completion
|
|
//
|
|
if (($incFile = hookFileExists("fmhookprecomplete.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
|
|
//
|
|
// send email
|
|
//
|
|
if (!isset($SPECIAL_VALUES["recipients"]) || empty($SPECIAL_VALUES["recipients"])) {
|
|
//
|
|
// No recipients - don't email anyone...
|
|
// If nothing has been done above (CSV, logging, or CRM),
|
|
// then report an error.
|
|
//
|
|
if (!$bDoneSomething) {
|
|
if (!$bGotGoBack && !$bGotNextForm) {
|
|
ErrorWithIgnore("no_recipients",GetMessage(MSG_NO_ACTIONS));
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// Hook system: before sending emails
|
|
//
|
|
if (($incFile = hookFileExists("fmhookpreemail.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
$s_invalid = $s_invalid_cc = $s_invalid_bcc = "";
|
|
if (!CheckEmailAddress($SPECIAL_VALUES["recipients"],$s_valid_recipients,$s_invalid)) {
|
|
ErrorWithIgnore("no_valid_recipients",GetMessage(MSG_NO_RECIP));
|
|
} else {
|
|
$s_valid_cc = $s_valid_bcc = "";
|
|
|
|
//
|
|
// check CC and BCC addresses
|
|
//
|
|
if (isset($SPECIAL_VALUES["cc"]) && !empty($SPECIAL_VALUES["cc"])) {
|
|
CheckEmailAddress($SPECIAL_VALUES["cc"],$s_valid_cc,$s_invalid_cc);
|
|
}
|
|
if (isset($SPECIAL_VALUES["bcc"]) && !empty($SPECIAL_VALUES["bcc"])) {
|
|
CheckEmailAddress($SPECIAL_VALUES["bcc"],$s_valid_bcc,$s_invalid_bcc);
|
|
}
|
|
|
|
//
|
|
// send an alert for invalid addresses
|
|
//
|
|
$s_error = "";
|
|
if (!empty($s_invalid)) {
|
|
$s_error .= "recipients: $s_invalid\r\n";
|
|
}
|
|
if (!empty($s_invalid_cc)) {
|
|
$s_error .= "cc: $s_invalid_cc\r\n";
|
|
}
|
|
if (!empty($s_invalid_bcc)) {
|
|
$s_error .= "bcc: $s_invalid_bcc\r\n";
|
|
}
|
|
if (!empty($s_error)) {
|
|
SendAlert(GetMessage(MSG_INV_EMAIL,array("ERRORS" => $s_error)));
|
|
}
|
|
|
|
//
|
|
// send the actual results
|
|
//
|
|
if (!SendResults($aFieldOrder,$aCleanedValues,$s_valid_recipients,$s_valid_cc,
|
|
$s_valid_bcc,$aRawDataValues)
|
|
) {
|
|
Error("mail_failed",GetMessage(MSG_FAILED_SEND));
|
|
}
|
|
//
|
|
// Hook system: after sending emails
|
|
//
|
|
if (($incFile = hookFileExists("fmhookpostemail.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Process autoresponse
|
|
//
|
|
$AutoResp->Process();
|
|
|
|
$SessionAccessor->CopyOut($aAllRawValues);
|
|
//
|
|
// multi-form processing
|
|
//
|
|
if ($bGotNextForm) {
|
|
OutputMultiFormTemplate($SPECIAL_VALUES["next_form"],$aRawDataValues);
|
|
// echo "Form index = ".GetSession("FormIndex");
|
|
//
|
|
// Hook system: after multi-page next form output
|
|
//
|
|
if (($incFile = hookFileExists("fmhookpostnextform.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
} else {
|
|
//
|
|
// Hook system: before finishing
|
|
//
|
|
if (($incFile = hookFileExists("fmhookprefinish.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
//
|
|
// redirect to the good URL page, or create a default page;
|
|
// we're no longer processing a multi-page form sequence
|
|
//
|
|
UnsetSession("FormList");
|
|
UnsetSession("FormIndex");
|
|
UnsetSession("FormKeep");
|
|
if (!$bGotGoodUrl) {
|
|
if (IsAjax()) {
|
|
JSON_Result("OK");
|
|
} else {
|
|
if ($bGotGoodTemplate) {
|
|
OutputTemplate($SPECIAL_VALUES["good_template"],$aRawDataValues);
|
|
} else {
|
|
CreatePage(GetMessage(MSG_THANKS_PAGE),GetMessage(MSG_FORM_OK));
|
|
}
|
|
}
|
|
//
|
|
// Hook system: after finishing (before session is cleared)
|
|
//
|
|
if (($incFile = hookFileExists("fmhookpostfinish.inc")) !== false) {
|
|
@include($incFile);
|
|
}
|
|
//
|
|
// everything's good, so we don't need the session any more
|
|
//
|
|
ZapSession();
|
|
} elseif (IsAjax()) {
|
|
JSON_Result("OK");
|
|
} else
|
|
|
|
//
|
|
// Note that a redirect terminates the script and prevents
|
|
// the processing of fmhookpostfinish.inc. Also, the session
|
|
// is left intact (which the good_url can use).
|
|
//
|
|
{
|
|
Redirect($SPECIAL_VALUES["good_url"],GetMessage(MSG_FORM_OK));
|
|
}
|
|
}
|