/application/controllers/IndexController.php
PHP | 368 lines | 201 code | 52 blank | 115 comment | 25 complexity | 0ebcae1d8919584999cc5fb0b342b16b MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0
- <?php
- /**
- * Simple PHP Contact Form
- *
- * This file is part of the Simple PHP Contact Form which is subject to the New
- * BSD License {@see LICENSE} which you would be advised to read before using,
- * modifying or redistributing this software.
- *
- * PHP version 5.2
- *
- * @category Simple-PHP-Contact-Form
- * @package Application_Controllers
- * @author jah <jah@jahboite.co.uk>
- * @copyright 2010 jah <jah@jahboite.co.uk>
- * @license New BSD License {@see LICENSE}
- * @version SVN: $Id$
- * @link http://code.google.com/p/simple-php-contact-form/
- */
- /**
- * IndexController.
- *
- * @category Simple-PHP-Contact-Form
- * @package Application_Controllers
- * @author jah <jah@jahboite.co.uk>
- * @copyright 2010 jah <jah@jahboite.co.uk>
- * @license New BSD License {@see LICENSE}
- * @version Release: @package_version@
- * @link http://code.google.com/p/simple-php-contact-form/
- */
- class IndexController extends Zend_Controller_Action
- {
- private $_recipientsConfig = null;
- private $_formConfig = null;
- private $_form = null;
- private $_auditor = null;
- /**
- * Initialises the form at each request of this controller.
- * TODO error action doesn't need init - so either move the error action
- * or move the init stuff elsewhere.
- *
- * @return null
- */
- public function init()
- {
- // Get the form configuration data.
- $bs = $this->getInvokeArg('bootstrap');
- $formConfigLoc = $bs->getOption('formconfigloc');
- if ($formConfigLoc === null) {
- throw new EnterpriseSecurityException(
- 'Your request could not be fulfilled.',
- 'Contact Application cannot start, cannot find form configuration data!'
- );
- }
- $this->_formConfig = new Zend_Config_Ini(
- $formConfigLoc, APPLICATION_ENV, false
- );
- if (! $this->_formConfig instanceof Zend_Config) {
- throw new EnterpriseSecurityException(
- 'Your request could not be fulfilled.',
- 'Contact Application cannot start, cannot find form configuration data!'
- );
- }
- // Get the recipients configuration data.
- $rcptConfigLoc = $bs->getOption('recipientsconfigloc');
- if (! empty($rcptConfigLoc)) {
- $this->_recipientsConfig = new Zend_Config_Ini(
- $rcptConfigLoc, APPLICATION_ENV, false
- );
- }
- if (! $this->_recipientsConfig instanceof Zend_Config) {
- $this->_auditor = ESAPI::getAuditor('IndexController');
- $this->_auditor->warning(
- Auditor::SECURITY,
- false,
- 'Contact Application cannot find contact recipients data. Form submissions will be logged to file!'
- );
- }
- // get a new form and pass recipient config data to it.
- $this->_form = $this->_helper->formLoader('contact', $this->_formConfig);
- $this->_form->setRecipientConfiguration($this->_recipientsConfig);
- $this->_form->setCaptcha();
- }
- /**
- * The index action will add a form which will be rendered by the view.
- * If the form has been previously submitted and the form contained
- * validation errors then client will be redirected here and the form
- * will be retrieved from the session storage.
- *
- * @return null
- */
- public function indexAction()
- {
- // see if the form has already been submitted. if it was and it failed
- // validation then the form will be persisted in the session and can be
- // represented to the client. if it passed validation the client can be
- // shown a thank you message.
- if (Zend_Session::sessionExists()) {
- $ns = new Zend_Session_Namespace('Contact');
- if ($ns->submission === false) {
- $this->view->contactForm = $ns->form;
- unset($ns->form);
- unset($ns->submission);
- return;
- } else if ($ns->submission === true) {
- unset($ns->submission);
- $this->render('thanks');
- return;
- }
- }
- // new form
- $this->_form->setCSRFToken();
- $this->view->contactForm = $this->_form;
- }
- /**
- * The send action is the target for form submission. If the request does
- * not contain POST data then the response will be a redirect to the index
- * action where the form will be displayed.
- * POST data is validated and if successful an email will be sent before
- * redirecting the client away from the send action (to a success message).
- * If validation fails, the form is persisted in the session and the client
- * is redirected away from the send action to display the form with error
- * messages.
- * Whether or not the form is successfully validated, a user session will be
- * started so that the client can be tracked and certain thresholds enforced.
- *
- * @return null
- */
- public function sendAction()
- {
- if (! $this->_request->isPost()) {
- $this->_helper->getHelper('redirector')
- ->setCode(303)
- ->gotoSimple(
- 'index', null, null, $this->_request->getParams()
- );
- }
- $ns = new Zend_Session_Namespace('Contact');
- $ids = ESAPI::getIntrusionDetector();
- // Is the form submission a valid one?
- $valid = $this->_form->isValid($this->_request->getPost());
- // Check whether a csrf token is being re-used. This should not happen
- // often due to the POST-Redirect-GET design of this application.
- // If the token is being reused then throw an Intrusion Exception.
- if ( isset($ns->history->token)
- && in_array($this->_form->token->getValue(), $ns->history->token)
- ) {
- throw new IntrusionException(
- 'Form resubmission is not permitted.',
- 'Submitted token is one contained in the history of the current session.'
- );
- }
- if ($valid !== true) {
- // Add a formValidationErrors event to the client session.
- $ids->addEvent(
- 'ValidationErrorEvent',
- 'Form submission contained inputs that caused Validation errors.'
- );
- // There are certain validation errors that require an exception to
- // be thrown becuase errors for these elements is assumed to be
- // evidence of tampering. Throw IntrusionException if there are
- // errors in CSRFToken or recipientsMap.
- if ($this->_form->detectedTamper()) {
- throw new IntrusionException(
- 'The submitted form contained invalid information.',
- 'Form submission contained evidence of tampering!'
- );
- }
- // Log a warning about failed validation and include the error
- // messages.
- $validationErrMsgs = $this->_form->getMessages(null, true);
- $vmsgs = '';
- foreach ($validationErrMsgs as $elem => $messageArray) {
- $vmsgs .= "[{$elem}:";
- foreach ($messageArray as $validator => $message) {
- $vmsgs .= "{$validator}={$message};";
- }
- $vmsgs .= "] ";
- }
- ESAPI::getAuditor('IndexController')->warning(
- Auditor::SECURITY,
- false,
- 'Validation failure messages: ' . $vmsgs
- );
- // Store the form in the session so that we can perform a redirect
- // away from the send action. // TODO expire it soon!
- $ns->submission = false;
- $ns->form = $this->_form;
- $this->_helper->getHelper('redirector')
- ->setCode(303)
- ->gotoSimple(
- 'index', null, null, $this->_request->getParams()
- );
- return;
- }
- // We have a valid form!!
- // Kill the CSRF cookie.
- setcookie(Form_Contact::CSRFCOOKIE, 'expired', 1, '/');
- // Add token to map of submitted tokens
- if (is_array($ns->history->token)) {
- array_unshift($ns->history->token, $this->_form->token->getValue());
- } else {
- $ns->history->token = array($this->_form->token->getValue());
- }
- // extract the valid values from the form and send a mail
- $validValues = array();
- $elements = $this->_form->getElements();
- foreach ($elements as $key => $elem) {
- $validValues[$key] = $elem->getValue();
- }
- // attempt to send a mail
- $successfulDelivery = $this->_helper->sendMail(
- $validValues, $this->_recipientsConfig
- );
- // If mail delivery was not successful show a
- if ($successfulDelivery !== true) {
- ESAPI::getAuditor('IndexController')->warning(
- Auditor::SECURITY, false,
- 'Sending of mail failed - ' . $this->_helper->sendMail->getResponse()
- );
- $ids->addEvent(
- 'MailNotDeliveredEvent',
- 'successfully submitted valid contact form but mail was NOT sent.'
- );
- // TODO an encrypted logfile with failed email messages?
- // View helpful error message.
- throw new Exception('Successful form submission failed to be sent.');
- } else {
- $ns->submission = true;
- $this->_helper->getHelper('redirector')
- ->setCode(303)
- ->gotoSimple(
- 'index', null, null, $this->_request->getParams()
- );
- return;
- }
- }
- /**
- * The router is set to /action and this segment of the url is overloaded
- * so that /someRecipient is also valid. This __call method is invoked
- * whenever this segment of the url is not a defined action and in these
- * cases, the $actionMethod may be a valid recipient. These sre trapped
- * here and, if valid, the request is re-dispatched to the index action to
- * which we pass a recipient parameter.
- * If a valid recipient is not found then execution is passed to the parent
- * __call method.
- *
- * @param string $actionMethod Url segment.
- * @param array $args Request arguments.
- *
- * @return null
- */
- public function __call($actionMethod, $args)
- {
- $logger = ESAPI::getAuditor('IndexController');
- // I do not anticipate this happening often...
- if (! is_string($actionMethod)) {
- return parent::__call($actionMethod, $args);
- }
- // If there's less than two recipients defined, we don't need to trap
- // usernames. ignore them.
- if ($this->_recipientsConfig->count() < 2) {
- return parent::__call($actionMethod, $args);
- }
- // Strip the trailing 'Action' from the method name.
- $method = null;
- $detectedCharacterEncoding = mb_detect_encoding($actionMethod);
- $len = mb_strlen($actionMethod, $detectedCharacterEncoding);
- if (mb_substr($actionMethod, $len-6, 6, $detectedCharacterEncoding) == 'Action' ) {
- $method = mb_substr(
- $actionMethod, 0, $len-6, $detectedCharacterEncoding
- );
- } else {
- $method = $actionMethod;
- }
- // Validate the possible recipient and, if valid, add a 'recipient'
- // request param and continue on to the indexAction of this controller.
- $recipientValidator = new Custom_Validate_ValidRecipient(
- $this->_recipientsConfig
- );
- if ($recipientValidator->isValid($method)) {
- $this->_request->setActionName('index');
- $this->_request->setParams(
- array(
- 'recipient' => $method,
- 'action' => 'index'
- )
- );
- $this->_request->setDispatched(false);
- return;
- }
- return parent::__call($actionMethod, $args);
- }
- /**
- * The error controller will redirect the client here and a generic message will
- * be displayed. Direct requests for this page will result in a redirect to
- * index action.
- *
- * @return null
- */
- public function errorAction()
- {
- if (Zend_Session::sessionExists()) {
- $ns = new Zend_Session_Namespace('Contact');
- if ($ns->error === true) {
- // Just show this view. Simples!
- unset($ns->error);
- return;
- }
- }
- $this->_helper->getHelper('redirector')
- ->setCode(303)
- ->gotoSimple(
- 'index', null, null, $this->_request->getParams()
- );
- }
- }