plirof2

getsimple cms p01contacts mod01

Dec 4th, 2020 (edited)
1,357
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 17.40 KB | None | 0 0
  1. <?php
  2. /**
  3.  * p01-contact - A simple contact forms manager
  4.  *
  5.  * @link https://github.com/nliautaud/p01contact
  6.  * @author Nicolas Liautaud
  7.  * @package p01contact
  8.  */
  9. namespace P01C;
  10.  
  11. require 'P01contact_Field.php';
  12.  
  13. class P01contactForm
  14. {
  15.     private $manager;
  16.  
  17.     private $id;
  18.     private $status;
  19.     private $targets;
  20.     private $fields;
  21.     public $lang;
  22.     public $sent;
  23.  
  24.     /**
  25.      * @param P01contact $P01contact
  26.      * @param int $id the form id
  27.      */
  28.     public function __construct($P01contact)
  29.     {
  30.         static $id;
  31.         $id++;
  32.  
  33.         $this->manager = $P01contact;
  34.  
  35.         $this->id = $id;
  36.         $this->status = '';
  37.         $this->targets = array();
  38.         $this->fields = array();
  39.     }
  40.  
  41.     /**
  42.      * Find tag parameters, populate fields and targets.
  43.      *
  44.      * @param string $params the params
  45.      */
  46.     public function parseTag($params)
  47.     {
  48.         // assure formating
  49.         $params = str_replace('&nbsp;', ' ', $params);
  50.         $params = strip_tags(str_replace("\n", '', $params));
  51.         $params = html_entity_decode($params, ENT_QUOTES, 'UTF-8');
  52.  
  53.         // explode
  54.         $sep = $this->config('separator');
  55.         $params = array_filter(explode($sep, $params));
  56.  
  57.         // emails
  58.         foreach ($params as $id => $param) {
  59.             $param = trim($param);
  60.             if (filter_var($param, FILTER_VALIDATE_EMAIL)) {
  61.                 $this->addTarget($param);
  62.                 unset($params[$id]);
  63.             }
  64.         }
  65.         // default params
  66.         if (empty($params)) {
  67.             $default = $this->config('default_params');
  68.             $params = array_filter(explode($sep, $default));
  69.         }
  70.         // create fields
  71.         foreach (array_values($params) as $id => $param) {
  72.             $this->parseParam($id, trim($param));
  73.         }
  74.         // default email addresses
  75.         $default_emails = $this->getValidEmails($this->config('default_email'));
  76.         foreach ($default_emails as $email) {
  77.             $this->addTarget($email);
  78.         }
  79.     }
  80.     /**
  81.      * Create a field by parsing a tag parameter
  82.      *
  83.      * Find emails and parameters, create and setup form object.
  84.      * @param int $id the field id
  85.      * @param string $param the param to parse
  86.      */
  87.     private function parseParam($id, $param)
  88.     {
  89.         $param_pattern = '`\s*([^ ,"=!]+)';     // type
  90.         $param_pattern.= '\s*(!)?';             // required!
  91.         $param_pattern.= '\s*(?:"([^"]*)")?';   // "title"
  92.         $param_pattern.= '\s*(?:\(([^"]*)\))?'; // (description)
  93.         $param_pattern.= '\s*(?:(=[><]?)?';     // =value, =>locked, =<placeholder
  94.         $param_pattern.= '\s*(.*))?\s*`';       // value
  95.  
  96.         preg_match($param_pattern, $param, $param);
  97.         list(, $type, $required, $title, $desc, $assign, $values) = $param;
  98.  
  99.         $field = new P01contactField($this, $id, $type);
  100.  
  101.         // values
  102.         switch ($type) {
  103.             case 'select':
  104.             case 'radio':
  105.             case 'checkbox':
  106.                 $field->value = explode('|', $values);
  107.                 $field->resetSelectedValues();
  108.                 break;
  109.             case 'askcopy':
  110.                 // checkbox-like structure
  111.                 $field->value = array($this->lang('askcopy'));
  112.                 break;
  113.             case 'password':
  114.                 // password value is required value
  115.                 $field->required = $values;
  116.                 break;
  117.             default:
  118.                 if ($assign == '=<') {
  119.                     $field->placeholder = $values;
  120.                 } else {
  121.                     // simple value
  122.                     $field->value = $values;
  123.                 }
  124.         }
  125.         // required
  126.         if ($type != 'password') {
  127.             $field->required = $required == '!';
  128.         }
  129.         if ($type == 'captcha') {
  130.             $field->required = true;
  131.         }
  132.         $field->title = $title;
  133.         $field->description = $desc;
  134.         $field->locked = $assign == '=>';
  135.  
  136.         $this->addField($field);
  137.     }
  138.  
  139.     /**
  140.      * Update POSTed form and try to send mail
  141.      *
  142.      * Check posted data, update form data,
  143.      * define fields errors and form status.
  144.      * At least, if there is no errors, try to send mail.
  145.      */
  146.     public function post()
  147.     {
  148.         if (empty($_POST['p01-contact_form'])
  149.          || $_POST['p01-contact_form']['id'] != $this->id ) {
  150.             return;
  151.         }
  152.  
  153.         // check token
  154.         if (!$this->checkToken()) {
  155.             $this->setStatus('sent_already');
  156.             $this->setToken();
  157.             $this->reset();
  158.             return;
  159.         }
  160.  
  161.         $posted = $_POST['p01-contact_fields'];
  162.         //echo "<h1>jon DEBUG 162: ";print_r($_POST['p01-contact_fields']);echo "_____END DEBUG</h1>";
  163.        
  164.         // populate fields values and check errors
  165.         $hasFieldsErrors = false;
  166.         $fields = $this->getFields(); //echo "<h3>jon DEBUG 166:fields: ";print_r($fields,true);echo '$field["title"]'."___END DEBUG</h3>";
  167.         $jon_csv="";
  168.         foreach ($fields as $field) {
  169.             // echo "<h3>jon DEBUG 168:field: ";print_r($field,true);echo '$field["title"]'."___END DEBUG</h3>";
  170.             if (!isset($posted[$field->id])) {
  171.                 continue;
  172.             }
  173.             $posted_val = $posted[$field->id];
  174.             $field->setValue($posted_val);
  175.             $hasFieldsErrors = !$field->validate() || $hasFieldsErrors;
  176.             //echo "<h1>jon DEBUG 174: posted_val=$posted_val posted[field->id]=".$posted[$field->id]." _".$field->title."____END DEBUG</h1>";
  177.             $jon_csv=$jon_csv.$posted_val."|_|"; // jon 201204a
  178.         }
  179.        
  180.         $form_name="jonToDoGetFormName"; // jon 201204a
  181.         $this->jon_log($jon_csv,$form_name); // jon 201204a
  182.         //echo "<h1>jon DEBUG 179: jon_csv=".$jon_csv."____END DEBUG</h1>";
  183.         // check errors and set status
  184.         if ($this->config('disable')) {
  185.             $this->setStatus('disable');
  186.             return;
  187.         }
  188.         if (count($this->targets) == 0) {
  189.             $this->setStatus('error_notarget');
  190.             return;
  191.         }
  192.         if ($hasFieldsErrors || $this->checkSpam($posted) !== true) {
  193.             return;
  194.         }
  195.  
  196.         $this->sendMail();
  197.         $this->setToken();
  198.         $this->reset();
  199.     }
  200.  
  201.  
  202.     private function jon_log($log_line,$form_name="default") // jon 201204a
  203.     {
  204.         $log_name = 'P01contact_log_'.$form_name.'.log';
  205.         $log_path = GSDATAOTHERPATH.'logs/';
  206.         $log_file = $log_path . $log_name;
  207.  
  208.         $log_result = file_put_contents($log_file,$log_line.PHP_EOL , FILE_APPEND | LOCK_EX);
  209.         echo "<h1>jon DEBUG 204: INSIDE jon_log(log_line) log_line,jon_csv=".$log_line."___log_result=$log_result_________END DEBUG</h1>";
  210.          
  211.     }// jon 201204a
  212.  
  213.     /*
  214.      *  SECURITY
  215.      */
  216.  
  217.  
  218.     /**
  219.      * Check if the honeypot field is untouched and if the time between this post,
  220.      * the page load and previous posts and the hourly post count are valid
  221.      * according to the settings, and set the form status accordingly.
  222.      *
  223.      * @param P01contact_form $form The submitted form
  224.      * @param array $post Sanitized p01-contact data of $_POST
  225.      * @return bool the result status
  226.      */
  227.     private function checkSpam($post)
  228.     {
  229.         if (isset($post['totally_legit'])) {
  230.             $this->setStatus('error_honeypot');
  231.             return false;
  232.         }
  233.         $loads = Session::get('pageloads');
  234.         if (count($loads) > 1 && $loads[1] - $loads[0] < $this->config('min_sec_after_load')) {
  235.             $this->setStatus('error_pageload');
  236.             return false;
  237.         }
  238.         $lastpost = Session::get('lastpost', false);
  239.         if ($lastpost && time() - $lastpost < $this->config('min_sec_between_posts')) {
  240.             $this->setStatus('error_lastpost');
  241.             return false;
  242.         }
  243.         $postcount = Session::get('postcount', 0);
  244.         if (!$this->config('debug') && $postcount > $this->config('max_posts_by_hour')) {
  245.             $this->setStatus('error_postcount');
  246.             return false;
  247.         }
  248.  
  249.         Session::set('lastpost', time());
  250.         Session::set('postcount', $postcount + 1);
  251.  
  252.         return true;
  253.     }
  254.  
  255.     /**
  256.      * Create an unique hash in Session
  257.      */
  258.     private static function setToken()
  259.     {
  260.         Session::set('token', uniqid(md5(microtime()), true));
  261.     }
  262.     /**
  263.      * Get the token in Session (create it if not exists)
  264.      * @return string
  265.      */
  266.     public function getToken()
  267.     {
  268.         if (!Session::get('token', false)) {
  269.             $this->setToken();
  270.         }
  271.         return Session::get('token');
  272.     }
  273.     /**
  274.      * Compare the POSTed token to the Session one
  275.      * @return boolean
  276.      */
  277.     private function checkToken()
  278.     {
  279.         return $this->getToken() === $_POST['p01-contact_form']['token'];
  280.     }
  281.  
  282.  
  283.     /*
  284.      *  RENDER
  285.      */
  286.  
  287.  
  288.     /**
  289.      * Return the html display of the form
  290.      * @return string the <form>
  291.      */
  292.     public function html()
  293.     {
  294.         $html  = '<form action="'.PAGEURL.'#p01-contact'.$this->id.'" autocomplete="off" ';
  295.         $html .= 'id="p01-contact' . $this->id . '" class="p01-contact" method="post">';
  296.  
  297.         if ($this->status) {
  298.             $html .= $this->htmlStatus();
  299.         }
  300.         if (!$this->sent) {
  301.             foreach ($this->fields as $field) {
  302.                 $html .= $field->html();
  303.             }
  304.             if ($this->config('use_honeypot')) {
  305.                 $html .= '<input type="checkbox" name="p01-contact_fields[totally_legit]" value="1" style="display:none !important" tabindex="-1" autocomplete="false">';
  306.             }
  307.             $html .= '<div><input name="p01-contact_form[id]" type="hidden" value="' . $this->id . '" />';
  308.             $html .= '<input name="p01-contact_form[token]" type="hidden" value="' . $this->getToken() . '" />';
  309.             $html .= '<input class="submit" type="submit" value="' . $this->lang('send') . '" /></div>';
  310.         }
  311.         $html .= '</form>';
  312.  
  313.         if ($this->config('debug')) {
  314.             $html .= $this->debug(false);
  315.         }
  316.         return $html;
  317.     }
  318.  
  319.  
  320.     /**
  321.      * Return an html display of the form status
  322.      * @return string the <div>
  323.      */
  324.     private function htmlStatus()
  325.     {
  326.         $statusclass = $this->sent ? 'alert success' : 'alert failed';
  327.         return '<div class="' . $statusclass . '">' . $this->lang($this->status) . '</div>';
  328.     }
  329.  
  330.     /**
  331.      * Return P01contact_form infos.
  332.      * @return string
  333.      */
  334.     public function debug($set_infos)
  335.     {
  336.         $out = '<div class="debug debug_form">';
  337.         static $post;
  338.         if ($set_infos) {
  339.             $post = $set_infos;
  340.             return;
  341.         }
  342.         if ($post) {
  343.             list($headers, $targets, $subject, $text_content, $html_content) = $post;
  344.             $out.= '<h3>Virtually sent mail :</h3>';
  345.             $out.= '<pre>'.htmlspecialchars($headers).'</pre>';
  346.             $out.= "<pre>Targets: $targets\nSubject: $subject</pre>";
  347.             $out.= "Text content : <pre>$text_content</pre>";
  348.             $out.= "HTML content : <div style=\"border:1px solid #ccc;\">$html_content</div>";
  349.         }
  350.         $infos = $this;
  351.         unset($infos->manager);
  352.         $out .= "<h3>p01contact form $this->id :</h3>";
  353.         $out .= preint($infos, true);
  354.         $out .= '</div>';
  355.         return $out;
  356.     }
  357.  
  358.     /*
  359.      *  MAIL
  360.      */
  361.  
  362.  
  363.     /**
  364.      * Send a mail based on form
  365.      *
  366.      * Create the mail content and headers along to settings, form
  367.      * and fields datas; and update the form status (sent|error).
  368.      */
  369.     public function sendMail()
  370.     {
  371.         $email = $name = $subject = $askcopy = null;
  372.         $tpl_data = (object) null;
  373.         $tpl_data->date = date('r');
  374.         $tpl_data->ip = $_SERVER["REMOTE_ADDR"];
  375.         $tpl_data->contact = $this->targets[0];
  376.         // fields
  377.         $tpl_data->fields = '';
  378.         foreach ($this->fields as $field) {
  379.             $tpl_data->fields .= $field->htmlMail();
  380.             switch ($field->type) {
  381.                 case 'name':
  382.                     $name = $field->value;
  383.                     break;
  384.                 case 'email':
  385.                     $email = $field->value;
  386.                     break;
  387.                 case 'subject':
  388.                     $subject = $field->value;
  389.                     break;
  390.                 case 'askcopy':
  391.                     $askcopy = true;
  392.                     break;
  393.             }
  394.         }
  395.          print_r($tpl_data);//jon
  396.         $html = $this->manager->renderTemplate('mail_template', $tpl_data);
  397.         $text = strip_tags($html);
  398.  
  399.         if (empty($name)) {
  400.             $name = $this->lang('anonymous');
  401.         }
  402.         if (empty($subject)) {
  403.             $subject = $this->lang('nosubject');
  404.         }
  405.  
  406.         // targets, subject, headers and multipart content
  407.         $targets = implode(',', $this->targets);
  408.         $encoded_subject = $this->encodeHeader($subject);
  409.  
  410.         $mime_boundary = '----'.md5(time());
  411.         $headers = $this->mailHeaders($name, $email, $mime_boundary);
  412.  
  413.         $content = $this->mailContent($text, 'plain', $mime_boundary);
  414.         $content .= $this->mailContent($html, 'html', $mime_boundary);
  415.         $content .= "--$mime_boundary--\n\n";
  416.  
  417.  
  418.         echo "<hr size=7 >JON :<BR> $text   <hr> $html<hr size=7 >";
  419.  
  420.         // debug
  421.         if ($this->config('debug')) {
  422.             $this->debug(array($headers, $targets, $subject, $text, $html));
  423.             return $this->setStatus('sent_debug');
  424.         }
  425.  
  426.  
  427.  
  428.         // send mail
  429.         $success = mail($targets, $encoded_subject, $content, $headers);
  430.  
  431.         // log
  432.         $this->manager->log(array(
  433.             date('d/m/Y H:i:s'), $targets, $subject, $name, $success ? 'success':'error'
  434.         ));
  435.  
  436.         if (!$success) {
  437.             return $this->setStatus('error');
  438.         }
  439.         if (!$email || !$askcopy) {
  440.             return $this->setStatus('sent');
  441.         }
  442.  
  443.         // mail copy
  444.         $copy = mail($email, $encoded_subject, $content, $headers);
  445.         $this->setStatus($copy ? 'sent_copy' : 'sent_copy_error');
  446.     }
  447.  
  448.     /**
  449.      * Return the mail headers
  450.      * @param string $name
  451.      * @param string $email
  452.      * @param string $mime_boundary
  453.      * @return string
  454.      */
  455.     private function mailHeaders($name, $email, $mime_boundary)
  456.     {
  457.         $encoded_name = $this->encodeHeader($name);
  458.         $headers  = "From: $encoded_name <no-reply@" . SERVERNAME . ">\n";
  459.         if ($email) {
  460.             $headers .= "Reply-To: $encoded_name <$email>\n";
  461.             $headers .= "Return-Path: $encoded_name <$email>";
  462.         }
  463.         $headers .= "\n";
  464.         $headers .= "MIME-Version: 1.0\n";
  465.         $headers .= "Content-type: multipart/alternative; boundary=\"$mime_boundary\"\n";
  466.         $headers .= "X-Mailer: PHP/" . phpversion() . "\n";
  467.         return $headers;
  468.     }
  469.  
  470.     /**
  471.      * Return a multipart/alternative content part.
  472.      * @param string $content
  473.      * @param string $type the content type (plain, html)
  474.      * @param string $mime_boundary
  475.      * @return string
  476.      */
  477.     private function mailContent($content, $type, $mime_boundary)
  478.     {
  479.         $head = "--$mime_boundary\n";
  480.         $head .= "Content-Type: text/$type; charset=UTF-8\n";
  481.         $head .= "Content-Transfer-Encoding: 7bit\n\n";
  482.         return $head.$content."\n";
  483.     }
  484.  
  485.     /**
  486.      * Format a string for UTF-8 email headers.
  487.      * @param string $string
  488.      * @return string
  489.      */
  490.     private function encodeHeader($string)
  491.     {
  492.         $string = base64_encode(html_entity_decode($string, ENT_COMPAT, 'UTF-8'));
  493.         return "=?UTF-8?B?$string?=";
  494.     }
  495.  
  496.     /**
  497.      * Return array of valid emails from a comma separated string
  498.      * @param string $emails
  499.      * @return array
  500.      */
  501.     public static function getValidEmails($emails)
  502.     {
  503.         return array_filter(explode(',', $emails), function ($email) {
  504.             return filter_var($email, FILTER_VALIDATE_EMAIL);
  505.         });
  506.     }
  507.  
  508.     /**
  509.      * GETTERS / SETTERS
  510.      */
  511.  
  512.     /*
  513.      * Reset all fields values and errors
  514.      */
  515.     public function reset()
  516.     {
  517.         foreach ($this->fields as $field) {
  518.             $field->value = '';
  519.             $field->error = '';
  520.         }
  521.     }
  522.     public function getTargets()
  523.     {
  524.         return $this->targets;
  525.     }
  526.     public function addTarget($tget)
  527.     {
  528.         if (in_array($tget, $this->targets) === false) {
  529.             $this->targets[] = $tget;
  530.         }
  531.     }
  532.     public function getField($id)
  533.     {
  534.         return $this->fields[$id];
  535.     }
  536.     public function getFields()
  537.     {
  538.         return $this->fields;
  539.     }
  540.     public function addField($field)
  541.     {
  542.         $this->fields[] = $field;
  543.     }
  544.     public function getStatus()
  545.     {
  546.         return $this->status;
  547.     }
  548.     public function setStatus($status)
  549.     {
  550.         if (!is_string($status)) {
  551.             return;
  552.         }
  553.         $this->status = $status;
  554.         if (substr($status, 0, 4) == 'sent') {
  555.             $this->sent = true;
  556.         }
  557.     }
  558.     public function getId()
  559.     {
  560.         return $this->id;
  561.     }
  562.     public function config($key)
  563.     {
  564.         return $this->manager->config($key);
  565.     }
  566.     public function lang($key)
  567.     {
  568.         return $this->manager->lang($key, $this->lang);
  569.     }
  570. }
  571.  
Add Comment
Please, Sign In to add comment