Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

qa.php

Zuletzt modifiziert: 02.04.2025, 15:02 - Dateigröße: 24.21 KiB


0001  <?php
0002  /**
0003  *
0004  * This file is part of the phpBB Forum Software package.
0005  *
0006  * @copyright (c) phpBB Limited <https://www.phpbb.com>
0007  * @license GNU General Public License, version 2 (GPL-2.0)
0008  *
0009  * For full copyright and license information, please see
0010  * the docs/CREDITS.txt file.
0011  *
0012  */
0013   
0014  namespace phpbb\captcha\plugins;
0015   
0016  /**
0017  * And now to something completely different. Let's make a captcha without extending the abstract class.
0018  * QA CAPTCHA sample implementation
0019  */
0020  class qa
0021  {
0022      var $confirm_id;
0023      var $answer;
0024      var $question_ids = [];
0025      var $question_text;
0026      var $question_lang;
0027      var $question_strict;
0028      var $attempts = 0;
0029      var $type;
0030      // dirty trick: 0 is false, but can still encode that the captcha is not yet validated
0031      var $solved = 0;
0032   
0033      protected $table_captcha_questions;
0034      protected $table_captcha_answers;
0035      protected $table_qa_confirm;
0036   
0037      /**
0038      * @var string name of the service.
0039      */
0040      protected $service_name;
0041   
0042      /**
0043      * Constructor
0044      *
0045      * @param string $table_captcha_questions
0046      * @param string $table_captcha_answers
0047      * @param string $table_qa_confirm
0048      */
0049      function __construct($table_captcha_questions, $table_captcha_answers, $table_qa_confirm)
0050      {
0051          $this->table_captcha_questions = $table_captcha_questions;
0052          $this->table_captcha_answers = $table_captcha_answers;
0053          $this->table_qa_confirm = $table_qa_confirm;
0054      }
0055   
0056      /**
0057      * @param int $type  as per the CAPTCHA API docs, the type
0058      */
0059      function init($type)
0060      {
0061          global $config, $db, $user, $request;
0062   
0063          // load our language file
0064          $user->add_lang('captcha_qa');
0065   
0066          // read input
0067          $this->confirm_id = $request->variable('qa_confirm_id', '');
0068          $this->answer = $request->variable('qa_answer', '', true);
0069   
0070          $this->type = (int) $type;
0071          $this->question_lang = $user->lang_name;
0072   
0073          // we need all defined questions - shouldn't be too many, so we can just grab them
0074          // try the user's lang first
0075          $sql = 'SELECT question_id
0076              FROM ' . $this->table_captcha_questions . "
0077              WHERE lang_iso = '" . $db->sql_escape($user->lang_name) . "'";
0078          $result = $db->sql_query($sql, 3600);
0079   
0080          while ($row = $db->sql_fetchrow($result))
0081          {
0082              $this->question_ids[$row['question_id']] = $row['question_id'];
0083          }
0084          $db->sql_freeresult($result);
0085   
0086          // fallback to the board default lang
0087          if (!count($this->question_ids))
0088          {
0089              $this->question_lang = $config['default_lang'];
0090   
0091              $sql = 'SELECT question_id
0092                  FROM ' . $this->table_captcha_questions . "
0093                  WHERE lang_iso = '" . $db->sql_escape($config['default_lang']) . "'";
0094              $result = $db->sql_query($sql, 7200);
0095   
0096              while ($row = $db->sql_fetchrow($result))
0097              {
0098                  $this->question_ids[$row['question_id']] = $row['question_id'];
0099              }
0100              $db->sql_freeresult($result);
0101          }
0102   
0103          // final fallback to any language
0104          if (!count($this->question_ids))
0105          {
0106              $this->question_lang = '';
0107   
0108              $sql = 'SELECT q.question_id, q.lang_iso
0109                  FROM ' . $this->table_captcha_questions . ' q, ' . $this->table_captcha_answers . ' a
0110                  WHERE q.question_id = a.question_id';
0111              $result = $db->sql_query($sql, 7200);
0112   
0113              while ($row = $db->sql_fetchrow($result))
0114              {
0115                  if (empty($this->question_lang))
0116                  {
0117                      $this->question_lang = $row['lang_iso'];
0118                  }
0119                  $this->question_ids[$row['question_id']] = $row['question_id'];
0120              }
0121              $db->sql_freeresult($result);
0122          }
0123   
0124          // okay, if there is a confirm_id, we try to load that confirm's state. If not, we try to find one
0125          if (!$this->load_answer() && (!$this->load_confirm_id() || !$this->load_answer()))
0126          {
0127              // we have no valid confirm ID, better get ready to ask something
0128              $this->select_question();
0129          }
0130      }
0131   
0132      /**
0133      * See if the captcha has created its tables.
0134      */
0135      public function is_installed()
0136      {
0137          global $phpbb_container;
0138   
0139          $db_tool = $phpbb_container->get('dbal.tools');
0140   
0141          return $db_tool->sql_table_exists($this->table_captcha_questions);
0142      }
0143   
0144      /**
0145      *  API function - for the captcha to be available, it must have installed itself and there has to be at least one question in the board's default lang
0146      */
0147      public function is_available()
0148      {
0149          global $config, $db, $user;
0150   
0151          // load language file for pretty display in the ACP dropdown
0152          $user->add_lang('captcha_qa');
0153   
0154          if (!$this->is_installed())
0155          {
0156              return false;
0157          }
0158   
0159          $sql = 'SELECT COUNT(question_id) AS question_count
0160              FROM ' . $this->table_captcha_questions . "
0161              WHERE lang_iso = '" . $db->sql_escape($config['default_lang']) . "'";
0162          $result = $db->sql_query($sql);
0163          $row = $db->sql_fetchrow($result);
0164          $db->sql_freeresult($result);
0165   
0166          return ((bool) $row['question_count']);
0167      }
0168   
0169      /**
0170      *  API function
0171      */
0172      function has_config()
0173      {
0174          return true;
0175      }
0176   
0177      /**
0178      *  API function
0179      */
0180      static public function get_name()
0181      {
0182          return 'CAPTCHA_QA';
0183      }
0184   
0185      /**
0186      * @return string the name of the service corresponding to the plugin
0187      */
0188      function get_service_name()
0189      {
0190          return $this->service_name;
0191      }
0192   
0193      /**
0194      * Set the name of the plugin
0195      *
0196      * @param string $name
0197      */
0198      public function set_name($name)
0199      {
0200          $this->service_name = $name;
0201      }
0202   
0203      /**
0204      *  API function - not needed as we don't display an image
0205      */
0206      function execute_demo()
0207      {
0208      }
0209   
0210      /**
0211      *  API function - not needed as we don't display an image
0212      */
0213      function execute()
0214      {
0215      }
0216   
0217      /**
0218      *  API function - send the question to the template
0219      */
0220      function get_template()
0221      {
0222          global $phpbb_log, $template, $user;
0223   
0224          if ($this->is_solved())
0225          {
0226              return false;
0227          }
0228          else if (empty($this->question_text) || !count($this->question_ids))
0229          {
0230              /** @var \phpbb\log\log_interface $phpbb_log */
0231              $phpbb_log->add('critical', $user->data['user_id'], $user->ip, 'LOG_ERROR_CAPTCHA', time(), array($user->lang('CONFIRM_QUESTION_MISSING')));
0232              return false;
0233          }
0234          else
0235          {
0236              $template->assign_vars(array(
0237                  'QA_CONFIRM_QUESTION'    => $this->question_text,
0238                  'QA_CONFIRM_ID'            => $this->confirm_id,
0239                  'S_CONFIRM_CODE'        => true,
0240                  'S_TYPE'                => $this->type,
0241              ));
0242   
0243              return 'captcha_qa.html';
0244          }
0245      }
0246   
0247      /**
0248      *  API function - we just display a mockup so that the captcha doesn't need to be installed
0249      */
0250      function get_demo_template()
0251      {
0252          global $config, $db, $template;
0253   
0254          if ($this->is_available())
0255          {
0256              $sql = 'SELECT question_text
0257                  FROM ' . $this->table_captcha_questions . "
0258                  WHERE lang_iso = '" . $db->sql_escape($config['default_lang']) . "'";
0259              $result = $db->sql_query_limit($sql, 1);
0260              if ($row = $db->sql_fetchrow($result))
0261              {
0262                  $template->assign_vars(array(
0263                      'QA_CONFIRM_QUESTION'        => $row['question_text'],
0264                  ));
0265              }
0266              $db->sql_freeresult($result);
0267          }
0268          return 'captcha_qa_acp_demo.html';
0269      }
0270   
0271      /**
0272      *  API function
0273      */
0274      function get_hidden_fields()
0275      {
0276          $hidden_fields = array();
0277   
0278          // this is required - otherwise we would forget about the captcha being already solved
0279          if ($this->solved)
0280          {
0281              $hidden_fields['qa_answer'] = $this->answer;
0282          }
0283          $hidden_fields['qa_confirm_id'] = $this->confirm_id;
0284   
0285          return $hidden_fields;
0286      }
0287   
0288      /**
0289      *  API function
0290      */
0291      function garbage_collect($type = 0)
0292      {
0293          global $db;
0294   
0295          $sql = 'SELECT c.confirm_id
0296              FROM ' . $this->table_qa_confirm . ' c
0297              LEFT JOIN ' . SESSIONS_TABLE . ' s
0298                  ON (c.session_id = s.session_id)
0299              WHERE s.session_id IS NULL' .
0300                  ((empty($type)) ? '' : ' AND c.confirm_type = ' . (int) $type);
0301          $result = $db->sql_query($sql);
0302   
0303          if ($row = $db->sql_fetchrow($result))
0304          {
0305              $sql_in = array();
0306   
0307              do
0308              {
0309                  $sql_in[] = (string) $row['confirm_id'];
0310              }
0311              while ($row = $db->sql_fetchrow($result));
0312   
0313              if (count($sql_in))
0314              {
0315                  $sql = 'DELETE FROM ' . $this->table_qa_confirm . '
0316                      WHERE ' . $db->sql_in_set('confirm_id', $sql_in);
0317                  $db->sql_query($sql);
0318              }
0319          }
0320          $db->sql_freeresult($result);
0321      }
0322   
0323      /**
0324      *  API function - we don't drop the tables here, as that would cause the loss of all entered questions.
0325      */
0326      function uninstall()
0327      {
0328          $this->garbage_collect(0);
0329      }
0330   
0331      /**
0332      *  API function - set up shop
0333      */
0334      function install()
0335      {
0336          global $phpbb_container;
0337   
0338          $db_tool = $phpbb_container->get('dbal.tools');
0339          $schemas = array(
0340                  $this->table_captcha_questions        => array (
0341                      'COLUMNS' => array(
0342                          'question_id'    => array('UINT', null, 'auto_increment'),
0343                          'strict'        => array('BOOL', 0),
0344                          'lang_id'        => array('UINT', 0),
0345                          'lang_iso'        => array('VCHAR:30', ''),
0346                          'question_text'    => array('TEXT_UNI', ''),
0347                      ),
0348                      'PRIMARY_KEY'        => 'question_id',
0349                      'KEYS'                => array(
0350                          'lang'            => array('INDEX', 'lang_iso'),
0351                      ),
0352                  ),
0353                  $this->table_captcha_answers        => array (
0354                      'COLUMNS' => array(
0355                          'question_id'    => array('UINT', 0),
0356                          'answer_text'    => array('STEXT_UNI', ''),
0357                      ),
0358                      'KEYS'                => array(
0359                          'qid'            => array('INDEX', 'question_id'),
0360                      ),
0361                  ),
0362                  $this->table_qa_confirm        => array (
0363                      'COLUMNS' => array(
0364                          'session_id'    => array('CHAR:32', ''),
0365                          'confirm_id'    => array('CHAR:32', ''),
0366                          'lang_iso'        => array('VCHAR:30', ''),
0367                          'question_id'    => array('UINT', 0),
0368                          'attempts'        => array('UINT', 0),
0369                          'confirm_type'    => array('USINT', 0),
0370                      ),
0371                      'KEYS'                => array(
0372                          'session_id'            => array('INDEX', 'session_id'),
0373                          'lookup'                => array('INDEX', array('confirm_id', 'session_id', 'lang_iso')),
0374                      ),
0375                      'PRIMARY_KEY'        => 'confirm_id',
0376                  ),
0377          );
0378   
0379          foreach ($schemas as $table => $schema)
0380          {
0381              if (!$db_tool->sql_table_exists($table))
0382              {
0383                  $db_tool->sql_create_table($table, $schema);
0384              }
0385          }
0386      }
0387   
0388      /**
0389      *  API function - see what has to be done to validate
0390      */
0391      function validate()
0392      {
0393          global $phpbb_log, $user;
0394   
0395          $error = '';
0396   
0397          if (!count($this->question_ids))
0398          {
0399              /** @var \phpbb\log\log_interface $phpbb_log */
0400              $phpbb_log->add('critical', $user->data['user_id'], $user->ip, 'LOG_ERROR_CAPTCHA', time(), array($user->lang('CONFIRM_QUESTION_MISSING')));
0401              return $user->lang('CONFIRM_QUESTION_MISSING');
0402          }
0403   
0404          if (!$this->confirm_id)
0405          {
0406              $error = $user->lang['CONFIRM_QUESTION_WRONG'];
0407          }
0408          else
0409          {
0410              if ($this->check_answer())
0411              {
0412                  $this->solved = true;
0413              }
0414              else
0415              {
0416                  $error = $user->lang['CONFIRM_QUESTION_WRONG'];
0417              }
0418          }
0419   
0420          if (strlen($error))
0421          {
0422              // okay, incorrect answer. Let's ask a new question.
0423              $this->new_attempt();
0424              $this->solved = false;
0425   
0426              return $error;
0427          }
0428          else
0429          {
0430              return false;
0431          }
0432      }
0433   
0434      /**
0435      *  Select a question
0436      */
0437      function select_question()
0438      {
0439          global $db, $user;
0440   
0441          if (!count($this->question_ids))
0442          {
0443              return;
0444          }
0445          $this->confirm_id = md5(unique_id($user->ip));
0446          $this->question = (int) array_rand($this->question_ids);
0447   
0448          $sql = 'INSERT INTO ' . $this->table_qa_confirm . ' ' . $db->sql_build_array('INSERT', array(
0449              'confirm_id'    => (string) $this->confirm_id,
0450              'session_id'    => (string) $user->session_id,
0451              'lang_iso'        => (string) $this->question_lang,
0452              'confirm_type'    => (int) $this->type,
0453              'question_id'    => (int) $this->question,
0454          ));
0455          $db->sql_query($sql);
0456   
0457          $this->load_answer();
0458      }
0459   
0460      /**
0461      * New Question, if desired.
0462      */
0463      function reselect_question()
0464      {
0465          global $db, $user;
0466   
0467          if (!count($this->question_ids))
0468          {
0469              return;
0470          }
0471   
0472          $this->question = (int) array_rand($this->question_ids);
0473          $this->solved = 0;
0474   
0475          $sql = 'UPDATE ' . $this->table_qa_confirm . '
0476              SET question_id = ' . (int) $this->question . "
0477              WHERE confirm_id = '" . $db->sql_escape($this->confirm_id) . "'
0478                  AND session_id = '" . $db->sql_escape($user->session_id) . "'";
0479          $db->sql_query($sql);
0480   
0481          $this->load_answer();
0482      }
0483   
0484      /**
0485      * Wrong answer, so we increase the attempts and use a different question.
0486      */
0487      function new_attempt()
0488      {
0489          global $db, $user;
0490   
0491          // yah, I would prefer a stronger rand, but this should work
0492          $this->question = (int) array_rand($this->question_ids);
0493          $this->solved = 0;
0494   
0495          $sql = 'UPDATE ' . $this->table_qa_confirm . '
0496              SET question_id = ' . (int) $this->question . ",
0497                  attempts = attempts + 1
0498              WHERE confirm_id = '" . $db->sql_escape($this->confirm_id) . "'
0499                  AND session_id = '" . $db->sql_escape($user->session_id) . "'";
0500          $db->sql_query($sql);
0501   
0502          $this->load_answer();
0503      }
0504   
0505   
0506      /**
0507      * See if there is already an entry for the current session.
0508      */
0509      function load_confirm_id()
0510      {
0511          global $db, $user;
0512   
0513          $sql = 'SELECT confirm_id
0514              FROM ' . $this->table_qa_confirm . "
0515              WHERE
0516                  session_id = '" . $db->sql_escape($user->session_id) . "'
0517                  AND lang_iso = '" . $db->sql_escape($this->question_lang) . "'
0518                  AND confirm_type = " . $this->type;
0519          $result = $db->sql_query_limit($sql, 1);
0520          $row = $db->sql_fetchrow($result);
0521          $db->sql_freeresult($result);
0522   
0523          if ($row)
0524          {
0525              $this->confirm_id = $row['confirm_id'];
0526              return true;
0527          }
0528          return false;
0529      }
0530   
0531      /**
0532      * Look up everything we need and populate the instance variables.
0533      */
0534      function load_answer()
0535      {
0536          global $db, $user;
0537   
0538          if (!strlen($this->confirm_id) || !count($this->question_ids))
0539          {
0540              return false;
0541          }
0542   
0543          $sql = 'SELECT con.question_id, attempts, question_text, strict
0544              FROM ' . $this->table_qa_confirm . ' con, ' . $this->table_captcha_questions . " qes
0545              WHERE con.question_id = qes.question_id
0546                  AND confirm_id = '" . $db->sql_escape($this->confirm_id) . "'
0547                  AND session_id = '" . $db->sql_escape($user->session_id) . "'
0548                  AND qes.lang_iso = '" . $db->sql_escape($this->question_lang) . "'
0549                  AND confirm_type = " . $this->type;
0550          $result = $db->sql_query($sql);
0551          $row = $db->sql_fetchrow($result);
0552          $db->sql_freeresult($result);
0553   
0554          if ($row)
0555          {
0556              $this->question = $row['question_id'];
0557   
0558              $this->attempts = $row['attempts'];
0559              $this->question_strict = $row['strict'];
0560              $this->question_text = $row['question_text'];
0561   
0562              return true;
0563          }
0564   
0565          return false;
0566      }
0567   
0568      /**
0569      *  The actual validation
0570      */
0571      function check_answer()
0572      {
0573          global $db, $request;
0574   
0575          $answer = ($this->question_strict) ? $request->variable('qa_answer', '', true) : utf8_clean_string($request->variable('qa_answer', '', true));
0576   
0577          $sql = 'SELECT answer_text
0578              FROM ' . $this->table_captcha_answers . '
0579              WHERE question_id = ' . (int) $this->question;
0580          $result = $db->sql_query($sql);
0581   
0582          while ($row = $db->sql_fetchrow($result))
0583          {
0584              $solution = ($this->question_strict) ? $row['answer_text'] : utf8_clean_string($row['answer_text']);
0585   
0586              if ($solution === $answer)
0587              {
0588                  $this->solved = true;
0589   
0590                  break;
0591              }
0592          }
0593          $db->sql_freeresult($result);
0594   
0595          return $this->solved;
0596      }
0597   
0598      /**
0599      *  API function
0600      */
0601      function get_attempt_count()
0602      {
0603          return $this->attempts;
0604      }
0605   
0606      /**
0607      *  API function
0608      */
0609      function reset()
0610      {
0611          global $db, $user;
0612   
0613          $sql = 'DELETE FROM ' . $this->table_qa_confirm . "
0614              WHERE session_id = '" . $db->sql_escape($user->session_id) . "'
0615                  AND confirm_type = " . (int) $this->type;
0616          $db->sql_query($sql);
0617   
0618          // we leave the class usable by generating a new question
0619          $this->select_question();
0620      }
0621   
0622      /**
0623      *  API function
0624      */
0625      function is_solved()
0626      {
0627          global $request;
0628   
0629          if ($request->variable('qa_answer', false) && $this->solved === 0)
0630          {
0631              $this->validate();
0632          }
0633   
0634          return (bool) $this->solved;
0635      }
0636   
0637      /**
0638      *  API function - The ACP backend, this marks the end of the easy methods
0639      */
0640      function acp_page($id, $module)
0641      {
0642          global $config, $request, $phpbb_log, $template, $user;
0643   
0644          $user->add_lang('acp/board');
0645          $user->add_lang('captcha_qa');
0646   
0647          if (!self::is_installed())
0648          {
0649              $this->install();
0650          }
0651   
0652          $module->tpl_name = 'captcha_qa_acp';
0653          $module->page_title = 'ACP_VC_SETTINGS';
0654          $form_key = 'acp_captcha';
0655          add_form_key($form_key);
0656   
0657          $submit = $request->variable('submit', false);
0658          $question_id = $request->variable('question_id', 0);
0659          $action = $request->variable('action', '');
0660   
0661          // we have two pages, so users might want to navigate from one to the other
0662          $list_url = $module->u_action . "&amp;configure=1&amp;select_captcha=" . $this->get_service_name();
0663   
0664          $template->assign_vars(array(
0665              'U_ACTION'        => $module->u_action,
0666              'QUESTION_ID'    => $question_id ,
0667              'CLASS'            => $this->get_service_name(),
0668          ));
0669   
0670          // show the list?
0671          if (!$question_id && $action != 'add')
0672          {
0673              $this->acp_question_list($module);
0674          }
0675          else if ($question_id && $action == 'delete')
0676          {
0677              if ($this->get_service_name() !== $config['captcha_plugin'] || !$this->acp_is_last($question_id))
0678              {
0679                  if (confirm_box(true))
0680                  {
0681                      $this->acp_delete_question($question_id);
0682   
0683                      trigger_error($user->lang['QUESTION_DELETED'] . adm_back_link($list_url));
0684                  }
0685                  else
0686                  {
0687                      confirm_box(false, $user->lang['CONFIRM_OPERATION'], build_hidden_fields(array(
0688                          'question_id'        => $question_id,
0689                          'action'            => $action,
0690                          'configure'            => 1,
0691                          'select_captcha'    => $this->get_service_name(),
0692                          ))
0693                      );
0694                  }
0695              }
0696              else
0697              {
0698                  trigger_error($user->lang['QA_LAST_QUESTION'] . adm_back_link($list_url), E_USER_WARNING);
0699              }
0700          }
0701          else
0702          {
0703              // okay, show the editor
0704              $question_input = $this->acp_get_question_input();
0705              $langs = $this->get_languages();
0706   
0707              foreach ($langs as $lang => $entry)
0708              {
0709                  $template->assign_block_vars('langs', array(
0710                      'ISO' => $lang,
0711                      'NAME' => $entry['name'],
0712                  ));
0713              }
0714   
0715              $template->assign_vars(array(
0716                  'U_LIST' => $list_url,
0717              ));
0718   
0719              if ($question_id)
0720              {
0721                  if ($question = $this->acp_get_question_data($question_id))
0722                  {
0723                      $template->assign_vars(array(
0724                          'QUESTION_TEXT'        => ($question_input['question_text']) ? $question_input['question_text'] : $question['question_text'],
0725                          'LANG_ISO'            => ($question_input['lang_iso']) ? $question_input['lang_iso'] : $question['lang_iso'],
0726                          'STRICT'            => (isset($_REQUEST['strict'])) ? $question_input['strict'] : $question['strict'],
0727                          'ANSWERS'            => implode("\n", $question['answers']),
0728                      ));
0729                  }
0730                  else
0731                  {
0732                      trigger_error($user->lang['FORM_INVALID'] . adm_back_link($list_url));
0733                  }
0734              }
0735              else
0736              {
0737                  $template->assign_vars(array(
0738                      'QUESTION_TEXT'        => $question_input['question_text'],
0739                      'LANG_ISO'            => $question_input['lang_iso'],
0740                      'STRICT'            => $question_input['strict'],
0741                      'ANSWERS'            => (is_array($question_input['answers'])) ? implode("\n", $question_input['answers']) : '',
0742                  ));
0743              }
0744   
0745              if ($submit && check_form_key($form_key))
0746              {
0747                  if (!$this->validate_input($question_input))
0748                  {
0749                      $template->assign_vars(array(
0750                          'S_ERROR'            => true,
0751                      ));
0752                  }
0753                  else
0754                  {
0755                      if ($question_id)
0756                      {
0757                          $this->acp_update_question($question_input, $question_id);
0758                      }
0759                      else
0760                      {
0761                          $this->acp_add_question($question_input);
0762                      }
0763   
0764                      $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_CONFIG_VISUAL');
0765                      trigger_error($user->lang['CONFIG_UPDATED'] . adm_back_link($list_url));
0766                  }
0767              }
0768              else if ($submit)
0769              {
0770                  trigger_error($user->lang['FORM_INVALID'] . adm_back_link($list_url), E_USER_WARNING);
0771              }
0772          }
0773      }
0774   
0775      /**
0776      *  This handles the list overview
0777      */
0778      function acp_question_list($module)
0779      {
0780          global $db, $template;
0781   
0782          $sql = 'SELECT *
0783              FROM ' . $this->table_captcha_questions;
0784          $result = $db->sql_query($sql);
0785   
0786          $template->assign_vars(array(
0787              'S_LIST'            => true,
0788          ));
0789   
0790          while ($row = $db->sql_fetchrow($result))
0791          {
0792              $url = $module->u_action . "&amp;question_id={$row['question_id']}&amp;configure=1&amp;select_captcha=" . $this->get_service_name() . '&amp;';
0793   
0794              $template->assign_block_vars('questions', array(
0795                  'QUESTION_TEXT'        => $row['question_text'],
0796                  'QUESTION_ID'        => $row['question_id'],
0797                  'QUESTION_LANG'        => $row['lang_iso'],
0798                  'U_DELETE'            => "{$url}action=delete",
0799                  'U_EDIT'            => "{$url}action=edit",
0800              ));
0801          }
0802          $db->sql_freeresult($result);
0803      }
0804   
0805      /**
0806      *  Grab a question and bring it into a format the editor understands
0807      */
0808      function acp_get_question_data($question_id)
0809      {
0810          global $db;
0811   
0812          if ($question_id)
0813          {
0814              $sql = 'SELECT *
0815                  FROM ' . $this->table_captcha_questions . '
0816                  WHERE question_id = ' . $question_id;
0817              $result = $db->sql_query($sql);
0818              $question = $db->sql_fetchrow($result);
0819              $db->sql_freeresult($result);
0820   
0821              if (!$question)
0822              {
0823                  return false;
0824              }
0825   
0826              $question['answers'] = array();
0827   
0828              $sql = 'SELECT *
0829                  FROM ' . $this->table_captcha_answers . '
0830                  WHERE question_id = ' . $question_id;
0831              $result = $db->sql_query($sql);
0832   
0833              while ($row = $db->sql_fetchrow($result))
0834              {
0835                  $question['answers'][] = $row['answer_text'];
0836              }
0837              $db->sql_freeresult($result);
0838   
0839              return $question;
0840          }
0841   
0842          return false;
0843      }
0844   
0845      /**
0846      *  Grab a question from input and bring it into a format the editor understands
0847      */
0848      function acp_get_question_input()
0849      {
0850          global $request;
0851   
0852          $answers = $request->variable('answers', '', true);
0853   
0854          // Convert answers into array and filter if answers are set
0855          if (strlen($answers))
0856          {
0857              $answers = array_filter(array_map('trim', explode("\n", $answers)), function ($value) {
0858                  return $value !== '';
0859              });
0860          }
0861   
0862          $question = array(
0863              'question_text'    => $request->variable('question_text', '', true),
0864              'strict'        => $request->variable('strict', false),
0865              'lang_iso'        => $request->variable('lang_iso', ''),
0866              'answers'        => $answers,
0867          );
0868          return $question;
0869      }
0870   
0871      /**
0872      *  Update a question.
0873      * param mixed $data : an array as created from acp_get_question_input or acp_get_question_data
0874      */
0875      function acp_update_question($data, $question_id)
0876      {
0877          global $db, $cache;
0878   
0879          // easier to delete all answers than to figure out which to update
0880          $sql = 'DELETE FROM ' . $this->table_captcha_answers . " WHERE question_id = $question_id";
0881          $db->sql_query($sql);
0882   
0883          $langs = $this->get_languages();
0884          $question_ary = $data;
0885          $question_ary['lang_id'] = $langs[$question_ary['lang_iso']]['id'];
0886          unset($question_ary['answers']);
0887   
0888          $sql = 'UPDATE ' . $this->table_captcha_questions . '
0889              SET ' . $db->sql_build_array('UPDATE', $question_ary) . "
0890              WHERE question_id = $question_id";
0891          $db->sql_query($sql);
0892   
0893          $this->acp_insert_answers($data, $question_id);
0894   
0895          $cache->destroy('sql', $this->table_captcha_questions);
0896      }
0897   
0898      /**
0899      *  Insert a question.
0900      * param mixed $data : an array as created from acp_get_question_input or acp_get_question_data
0901      */
0902      function acp_add_question($data)
0903      {
0904          global $db, $cache;
0905   
0906          $langs = $this->get_languages();
0907          $question_ary = $data;
0908   
0909          $question_ary['lang_id'] = $langs[$data['lang_iso']]['id'];
0910          unset($question_ary['answers']);
0911   
0912          $sql = 'INSERT INTO ' . $this->table_captcha_questions . ' ' . $db->sql_build_array('INSERT', $question_ary);
0913          $db->sql_query($sql);
0914   
0915          $question_id = $db->sql_nextid();
0916   
0917          $this->acp_insert_answers($data, $question_id);
0918   
0919          $cache->destroy('sql', $this->table_captcha_questions);
0920      }
0921   
0922      /**
0923      *  Insert the answers.
0924      * param mixed $data : an array as created from acp_get_question_input or acp_get_question_data
0925      */
0926      function acp_insert_answers($data, $question_id)
0927      {
0928          global $db, $cache;
0929   
0930          foreach ($data['answers'] as $answer)
0931          {
0932              $answer_ary = array(
0933                  'question_id'    => $question_id,
0934                  'answer_text'    => $answer,
0935              );
0936   
0937              $sql = 'INSERT INTO ' . $this->table_captcha_answers . ' ' . $db->sql_build_array('INSERT', $answer_ary);
0938              $db->sql_query($sql);
0939          }
0940   
0941          $cache->destroy('sql', $this->table_captcha_answers);
0942      }
0943   
0944      /**
0945      *  Delete a question.
0946      */
0947      function acp_delete_question($question_id)
0948      {
0949          global $db, $cache;
0950   
0951          $tables = array($this->table_captcha_questions, $this->table_captcha_answers);
0952   
0953          foreach ($tables as $table)
0954          {
0955              $sql = "DELETE FROM $table
0956                  WHERE question_id = $question_id";
0957              $db->sql_query($sql);
0958          }
0959   
0960          $cache->destroy('sql', $tables);
0961      }
0962   
0963      /**
0964      *  Check if the entered data can be inserted/used
0965      * param mixed $data : an array as created from acp_get_question_input or acp_get_question_data
0966      */
0967      function validate_input($question_data)
0968      {
0969          $langs = $this->get_languages();
0970   
0971          if (!isset($question_data['lang_iso']) ||
0972              !isset($question_data['question_text']) ||
0973              !isset($question_data['strict']) ||
0974              !isset($question_data['answers']))
0975          {
0976              return false;
0977          }
0978   
0979          if (!isset($langs[$question_data['lang_iso']]) ||
0980              !strlen($question_data['question_text']) ||
0981              !count($question_data['answers']) ||
0982              !is_array($question_data['answers']))
0983          {
0984              return false;
0985          }
0986   
0987          return true;
0988      }
0989   
0990      /**
0991      * List the installed language packs
0992      */
0993      function get_languages()
0994      {
0995          global $db;
0996   
0997          $sql = 'SELECT *
0998              FROM ' . LANG_TABLE;
0999          $result = $db->sql_query($sql);
1000   
1001          $langs = array();
1002          while ($row = $db->sql_fetchrow($result))
1003          {
1004              $langs[$row['lang_iso']] = array(
1005                  'name'    => $row['lang_local_name'],
1006                  'id'    => (int) $row['lang_id'],
1007              );
1008          }
1009          $db->sql_freeresult($result);
1010   
1011          return $langs;
1012      }
1013   
1014   
1015   
1016      /**
1017      *  See if there is a question other than the one we have
1018      */
1019      function acp_is_last($question_id)
1020      {
1021          global $config, $db;
1022   
1023          if ($question_id)
1024          {
1025              $sql = 'SELECT question_id
1026                  FROM ' . $this->table_captcha_questions . "
1027                  WHERE lang_iso = '" . $db->sql_escape($config['default_lang']) . "'
1028                      AND  question_id <> " .  (int) $question_id;
1029              $result = $db->sql_query_limit($sql, 1);
1030              $question = $db->sql_fetchrow($result);
1031              $db->sql_freeresult($result);
1032   
1033              if (!$question)
1034              {
1035                  return true;
1036              }
1037              return false;
1038          }
1039      }
1040  }
1041