/**
  * Returns true if and only if $value meets the validation requirements
  *
  * If $value fails validation, then this method returns false, and
  * getMessages() will return an array of messages that explain why the
  * validation failed.
  *
  * @param  mixed $value
  * @return boolean
  * @throws \Zend_Valid_Exception If validation of $value is impossible
  */
 public function isValid($value)
 {
     if ($throttleSettings = $this->project->getAskThrottleSettings()) {
         // Prune the database for (very) old attempts
         $where = $this->db->quoteInto('gta_datetime < DATE_SUB(NOW(), INTERVAL ? second)', $throttleSettings['period'] * 20);
         $this->db->delete('gems__token_attempts', $where);
         // Retrieve the number of failed attempts that occurred within the specified window
         $select = $this->db->select();
         $select->from('gems__token_attempts', array(new \Zend_Db_Expr('COUNT(*) AS attempts'), new \Zend_Db_Expr('UNIX_TIMESTAMP(MAX(gta_datetime)) - UNIX_TIMESTAMP() AS last')))->where('gta_datetime > DATE_SUB(NOW(), INTERVAL ? second)', $throttleSettings['period']);
         $attemptData = $this->db->fetchRow($select);
         $remainingDelay = $attemptData['last'] + $throttleSettings['delay'];
         // \MUtil_Echo::track($throttleSettings, $attemptData, $remainingDelay);
         if ($attemptData['attempts'] > $throttleSettings['threshold'] && $remainingDelay > 0) {
             $this->logger->log("Possible token brute force attack, throttling for {$remainingDelay} seconds", \Zend_Log::ERR);
             $this->_messages = $this->translate->_('The server is currently busy, please wait a while and try again.');
             return false;
         }
     }
     // The pure token check
     if ($this->isValidToken($value)) {
         return true;
     }
     $max_length = $this->tracker->getTokenLibrary()->getLength();
     $this->db->insert('gems__token_attempts', array('gta_id_token' => substr($value, 0, $max_length), 'gta_ip_address' => $this->getRequest()->getClientIp()));
     return false;
 }
 /**
  * Survey dependent calculations / answer changes that must occur after a survey is completed
  *
  * @param type $tokenId The tokend the answers are for
  * @param array $tokenAnswers Array with answers. May be changed in process
  * @return array The changed values
  */
 public function handleBeforeAnswering()
 {
     $survey = $this->getSurvey();
     $event = $survey->getSurveyBeforeAnsweringEvent();
     if ($event) {
         try {
             $changed = $event->processTokenInsertion($this);
             if ($changed && is_array($changed)) {
                 $this->setRawAnswers($changed);
                 if (\Gems_Tracker::$verbose) {
                     \MUtil_Echo::r($changed, 'Source values for ' . $this->_tokenId . ' changed by event.');
                 }
                 return $changed;
             }
         } catch (\Exception $e) {
             $this->logger->log(sprintf("Before answering event error for token %s on survey '%s' using event '%s': %s", $this->_tokenId, $this->getSurveyName(), $event->getEventName(), $e->getMessage()), \Zend_Log::ERR);
         }
     }
 }