Bladeren bron

2017-12-07

Visman 7 jaren geleden
bovenliggende
commit
c49db50c23

+ 20 - 7
app/Core/Mail.php

@@ -92,21 +92,34 @@ class Mail
     public function valid($email, $strict = false, $idna = false)
     {
         if (! is_string($email)
-            || mb_strlen($email, 'UTF-8') > 80
-            || ! preg_match('%^([a-z0-9_!#$\%&\'*+-/=?^`{|}~]+(?:\.[a-z0-9_!#$\%&\'*+-/=?^`{|}~]+)*)@([^\x00-\x20]+)$%Di', $email, $matches)
+            || mb_strlen($email, 'UTF-8') > 80 //?????
+            || ! preg_match('%^([^\x00-\x1F\\\/\s@]+)@([^\x00-\x1F\s@]+)$%Du', $email, $matches)
         ) {
             return false;
         }
         $local = $matches[1];
         $domain = mb_strtolower($matches[2], 'UTF-8');
-        if (! preg_match('%^(?:[\p{L}\p{N}]+(?:\-[\p{L}\p{N}]+)*\.)*\p{L}+$%u', $domain)) {
-            return false;
-        }
 
-        $domainASCII = idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46);
+        if ($domain{0} === '[' && substr($domain, -1) === ']') {
+            $ip = substr($domain, 1, -1);
+            if (! filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
+                return $false;
+            }
+            $domainASCII = $domain;
+        } else {
+            $ip = null;
+            if (! preg_match('%^(?:[\p{L}\p{N}]+(?:\-[\p{L}\p{N}]+)*\.)*\p{L}+$%u', $domain)) {
+                return false;
+            }
+            $domainASCII = idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46);
+        }
 
         if ($strict) {
-            $mx = @dns_get_record($domainASCII, DNS_MX); //????
+            if ($ip) {
+                $mx = @checkdnsrr($ip, 'MX');
+            } else {
+                $mx = @dns_get_record($domainASCII, DNS_MX);
+            }
             if (empty($mx)) {
                 return false;
             }

+ 89 - 0
app/Core/Parser.php

@@ -3,6 +3,7 @@
 namespace ForkBB\Core;
 
 use Parserus;
+use ForkBB\Core\Container;
 
 class Parser extends Parserus
 {
@@ -65,4 +66,92 @@ class Parser extends Parserus
         }
         return parent::addBBCode($bb);
     }
+
+    /**
+     * Проверяет разметку сообщения с бб-кодами
+     * Пытается исправить неточности разметки
+     * Генерирует ошибки разметки
+     *
+     * @param string $text
+     * @param bool $isSignature
+     *
+     * @return string
+     */
+    public function prepare($text, $isSignature = false)
+    {
+        if ($isSignature) {
+            $whiteList = $this->c->config->p_sig_bbcode == '1' ? $this->c->BBCODE_INFO['forSign'] : [];
+            $blackList = $this->c->config->p_sig_img_tag == '1' ? [] : ['img'];
+        } else {
+            $whiteList = $this->c->config->p_message_bbcode == '1' ? null : [];
+            $blackList = $this->c->config->p_message_img_tag == '1' ? [] : ['img'];
+        }
+
+        $this->setAttr('isSign', $isSignature)
+             ->setWhiteList($whiteList)
+             ->setBlackList($blackList)
+             ->parse($text, ['strict' => true])
+             ->stripEmptyTags(" \n\t\r\v", true);
+
+        if ($this->c->config->o_make_links == '1') {
+            $this->detectUrls();
+        }
+
+        return preg_replace('%^(\x20*\n)+|(\n\x20*)+$%D', '', $this->getCode());
+    }
+
+    /**
+     * Преобразует бб-коды в html в сообщениях
+     *
+     * @param null|string $text
+     * @param bool $hideSmilies
+     *
+     * @return string
+     */
+    public function parseMessage($text = null, $hideSmilies = false)
+    {
+        // при null предполагается брать данные после prepare()
+        if (null !== $text) {
+            $whiteList = $this->c->config->p_message_bbcode == '1' ? null : [];
+            $blackList = $this->c->config->p_message_img_tag == '1' ? [] : ['img'];
+    
+            $this->setAttr('isSign', false)
+                 ->setWhiteList($whiteList)
+                 ->setBlackList($blackList)
+                 ->parse($text);
+        }
+
+        if (! $hideSmilies && $this->c->config->o_smilies == '1') {
+            $this->detectSmilies();
+        }
+
+        return $this->getHtml();
+    }
+
+    /**
+     * Преобразует бб-коды в html в подписях пользователей
+     *
+     * @param null|string $text
+     *
+     * @return string
+     */
+    public function parseSignature($text = null)
+    {
+        // при null предполагается брать данные после prepare()
+        if (null !== $text) {
+            $whiteList = $this->c->config->p_sig_bbcode == '1' ? $this->c->BBCODE_INFO['forSign'] : [];
+            $blackList = $this->c->config->p_sig_img_tag == '1' ? [] : ['img'];
+    
+            $this->setAttr('isSign', true)
+                 ->setWhiteList($whiteList)
+                 ->setBlackList($blackList)
+                 ->parse($text);
+        }
+
+        if ($this->c->config->o_smilies_sig == '1') {
+            $this->detectSmilies();
+        }
+
+        return $this->getHtml();
+    }
 }

+ 66 - 43
app/Core/Router.php

@@ -2,6 +2,8 @@
 
 namespace ForkBB\Core;
 
+use InvalidArgumentException;
+
 class Router
 {
     const OK = 200;
@@ -103,40 +105,43 @@ class Router
     public function link($marker = null, array $args = [])
     {
         $result = $this->baseUrl;
-        if (is_string($marker) && isset($this->links[$marker])) {
-            $s = $this->links[$marker];
-            foreach ($args as $key => $val) {
-                if ($key == '#') {
-                    $s .= '#' . rawurlencode($val);
-                    continue;
-                } elseif ($key == 'page' && $val === 1) {
+        $anchor = isset($args['#']) ? '#' . rawurlencode($args['#']) : '';
+
+        // маркер пустой
+        if (null === $marker) {
+            return $result . "/{$anchor}";
+        // такой ссылки нет
+        } elseif (! isset($this->links[$marker])) {
+            return $result . '/';
+        // ссылка статична
+        } elseif (is_string($data = $this->links[$marker])) {
+            return $result . $data . $anchor;
+        }
+
+        list($link, $names, $request) = $data;
+        $data = [];
+        // перечисление имен переменных для построения ссылки
+        foreach ($names as $name) {
+            // значение есть
+            if (isset($args[$name])) {
+                // кроме page = 1
+                if ($name != 'page' || $args[$name] !== 1) {
+                    $data['{' . $name . '}'] = rawurlencode(preg_replace('%[\s\\\/]+%u', '-', $args[$name]));
                     continue;
                 }
-                $s = preg_replace_callback( //????
-                    '%\{' . preg_quote($key, '%') . '(?::[^{}]+)?\}%', 
-                    function($match) use ($val) {
-                        if (is_string($val)) {
-                            $val = trim(preg_replace('%[^\p{L}\p{N}_]+%u', '-', $val), '_-');
-                        } elseif (is_numeric($val)) { //????
-                            $val = (string) $val;
-                        } else {
-                            $val = null;
-                        }
-                        return isset($val[0]) ? rawurlencode($val) : '-';
-                    },
-                    $s
-                );
             }
-            $s = preg_replace('%\[[^{}\[\]]*\{[^}]+\}[^{}\[\]]*\]%', '', $s);
-            if (strpos($s, '{') === false) {
-                $result .= str_replace(['[', ']'], '', $s);
+
+            // значения нет, но оно обязательно
+            if ($request[$name]) {
+                return $result . '/';
+            // значение не обязательно
             } else {
-                $result .= '/';
+                $link = preg_replace('%\[[^\[\]{}]*{' . preg_quote($name, '%') . '}[^\[\]{}]*\]%', '', $link);
             }
-        } else {
-            $result .= '/';
         }
-        return $result;
+        $link = str_replace(['[', ']'], '', $link);
+
+        return $result . strtr($link, $data) . $anchor;
     }
 
     /**
@@ -227,12 +232,15 @@ class Router
             $this->methods[$method] = 1;
         }
 
-        $link = $route;
-        if (($pos = strpos($route, '#')) !== false) {
-            $route = substr($route, 0, $pos);
+        $link   = $route;
+        $anchor = '';
+        if (false !== strpos($route, '#')) {
+            list($route, $anchor) = explode('#', $route, 2);
+            $anchor = '#' . $anchor;
         }
 
         if (false === strpbrk($route, '{}[]')) {
+            $data = null;
             if (is_array($method)) {
                 foreach ($method as $m) {
                     $this->statical[$route][$m] = $handler;
@@ -255,7 +263,11 @@ class Router
         }
 
         if ($marker) {
-            $this->links[$marker] = $link;
+            if ($data) {
+                $this->links[$marker] = [$data[3] . $anchor, $data[2], $data[4]];
+            } else {
+                $this->links[$marker] = $link;
+            }
         }
     }
 
@@ -281,11 +293,14 @@ class Router
         }
 
         $pattern = '%^';
-        $var = false;
-        $first = false;
-        $buffer = '';
-        $args = [];
-        $s = 0;
+        $var     = false;
+        $first   = false;
+        $buffer  = '';
+        $args    = [];
+        $s       = 0;
+        $req     = true;
+        $argReq  = [];
+        $temp    = '';
 
         foreach ($parts as $part) {
             if ($var) {
@@ -301,9 +316,11 @@ class Router
                             return false;
                         }
                         $pattern .= '(?P<' . $data[0] . '>' . $data[1] . ')';
-                        $args[] = $data[0];
-                        $var = false;
-                        $buffer = '';
+                        $args[]   = $data[0];
+                        $temp    .= '{' . $data[0] . '}';
+                        $var      = false;
+                        $buffer   = '';
+                        $argsReq[$data[0]] = $req;
                         break;
                     default:
                         $buffer .= $part;
@@ -311,8 +328,9 @@ class Router
             } elseif ($first) {
                 switch ($part) {
                     case '/':
-                        $first = false;
+                        $first    = false;
                         $pattern .= preg_quote($part, '%');
+                        $temp    .= $part;
                         break;
                     default:
                         return false;
@@ -322,7 +340,9 @@ class Router
                     case '[':
                         ++$s;
                         $pattern .= '(?:';
-                        $first = true;
+                        $first    = true;
+                        $req      = false;
+                        $temp    .= '[';
                         break;
                     case ']':
                         --$s;
@@ -330,6 +350,8 @@ class Router
                             return false;
                         }
                         $pattern .= ')?';
+                        $req      = true;
+                        $temp    .= ']';
                         break;
                     case '{':
                         $var = true;
@@ -338,6 +360,7 @@ class Router
                         return false;
                     default:
                         $pattern .= preg_quote($part, '%');
+                        $temp    .= $part;
                 }
             }
         }
@@ -345,6 +368,6 @@ class Router
             return false;
         }
         $pattern .= '$%D';
-        return [$base, $pattern, $args];
+        return [$base, $pattern, $args, $temp, $argsReq];
     }
 }

+ 1 - 1
app/Models/Pages/Auth.php

@@ -145,7 +145,7 @@ class Auth extends Page
                 $user->update();
 
                 $this->c->Online->delete($this->c->user);
-                $this->c->Cookie->setUser($user);
+                $this->c->Cookie->setUser($user, (bool) $v->save);
             }
         }
         return $password;

+ 34 - 24
app/Models/Pages/Post.php

@@ -67,7 +67,7 @@ class Post extends Page
         $args['_vars'] = $v->getData();
 
         if (null !== $v->preview && ! $v->getErrors()) {
-            $this->previewHtml = $this->c->Parser->getHtml();
+            $this->previewHtml = $this->c->Parser->parseMessage(null, (bool) $v->hide_smilies);
         }
 
         return $this->newTopic($args, $forum);
@@ -105,7 +105,7 @@ class Post extends Page
         
         $this->nameTpl   = 'post';
         $this->onlinePos = 'topic-' . $topic->id;
-        $this->canonical = $this->c->Router->link('NewReply', $args);
+        $this->canonical = $this->c->Router->link('NewReply', ['id' => $topic->id]);
         $this->robots    = 'noindex';
         $this->crumbs    = $this->crumbs(__('Post a reply'), $topic);
         $this->form      = $this->messageForm($topic, 'NewReply', $args);
@@ -142,7 +142,7 @@ class Post extends Page
         $args['_vars'] = $v->getData();
 
         if (null !== $v->preview && ! $v->getErrors()) {
-            $this->previewHtml = $this->c->Parser->getHtml();
+            $this->previewHtml = $this->c->Parser->parseMessage(null, (bool) $v->hide_smilies);
         }
 
         return $this->newReply($args, $topic);
@@ -206,9 +206,10 @@ class Post extends Page
 
         // попытка объеденить новое сообщение с крайним в теме
         if ($merge) {
-            $lastPost = $this->c->ModelPost->load($topic->last_post_id);
+            $lastPost  = $this->c->ModelPost->load($topic->last_post_id);
+            $newLength = mb_strlen($lastPost->message . $v->message, 'UTF-8');
 
-            if ($this->c->MAX_POST_SIZE > mb_strlen($lastPost->message . $v->message, 'UTF-8') + 100) { //????
+            if ($newLength < $this->c->MAX_POST_SIZE - 100) {
                 $lastPost->message = $lastPost->message . "\n[after=" . ($now - $topic->last_post) . "]\n" . $v->message; //????
                 $lastPost->posted  = $lastPost->posted + 1; //???? прибаляем 1 секунду для появления в новых //????
 
@@ -339,7 +340,7 @@ class Post extends Page
      * @param Validator $v
      * @param string $message
      * 
-     * @return array
+     * @return string
      */
     public function vCheckMessage(Validator $v, $message, $attr, $executive)
     {
@@ -355,23 +356,7 @@ class Post extends Page
             $v->addError('All caps message');
         // проверка парсером
         } else {
-            
-            $bbWList = $this->c->config->p_message_bbcode == '1' ? null : [];
-            $bbBList = $this->c->config->p_message_img_tag == '1' ? [] : ['img'];
-
-            $this->c->Parser->setAttr('isSign', false)
-                ->setWhiteList($bbWList)
-                ->setBlackList($bbBList)
-                ->parse($message, ['strict' => true])
-                ->stripEmptyTags(" \n\t\r\v", true);
-
-            if ($this->c->config->o_make_links == '1') {
-                $this->c->Parser->detectUrls();
-            }
-
-            if ($v->hide_smilies != '1' && $this->c->config->o_smilies == '1') {
-                $this->c->Parser->detectSmilies();
-            }
+            $message = $this->c->Parser->prepare($message); //????
 
             foreach($this->c->Parser->getErrors() as $error) {
                 $v->addError($error);
@@ -381,6 +366,30 @@ class Post extends Page
         return $message;
     }
 
+    /**
+     * Проверка времени ограничения флуда
+     * 
+     * @param Validator $v
+     * @param null|string $submit
+     * 
+     * @return null|string
+     */
+    public function vCheckTimeout(Validator $v, $submit)
+    {
+        if (null === $submit) {
+            return null;
+        }
+
+        $user = $this->c->user;
+        $time = time() - (int) $user->last_post;
+
+        if ($time < $user->g_post_flood) {
+            $v->addError(__('Flood start', $user->g_post_flood, $user->g_post_flood - $time), 'e');
+        }
+
+        return $submit;
+    }
+
     /**
      * Подготовка валидатора к проверке данных из формы создания темы/сообщения
      * 
@@ -436,6 +445,7 @@ class Post extends Page
             'check_username' => [$this, 'vCheckUsername'],
             'check_subject'  => [$this, 'vCheckSubject'],
             'check_message'  => [$this, 'vCheckMessage'],
+            'check_timeout'  => [$this, 'vCheckTimeout'],
         ])->setRules([
             'token'        => 'token:' . $marker,
             'email'        => [$ruleEmail, __('Email')],
@@ -445,8 +455,8 @@ class Post extends Page
             'stick_fp'     => $ruleStickFP,
             'merge_post'   => $ruleMergePost,
             'hide_smilies' => $ruleHideSmilies,
-            'submit'       => 'string', //????
             'preview'      => 'string', //????
+            'submit'       => 'string|check_timeout', //????
             'message'      => 'required|string:trim|max:' . $this->c->MAX_POST_SIZE . '|check_message',
         ])->setArguments([
             'token'                 => $args,

+ 1 - 13
app/Models/Post.php

@@ -115,18 +115,6 @@ class Post extends DataModel
      */
     public function html()
     {
-        $bbWList = $this->c->config->p_message_bbcode == '1' ? null : [];
-        $bbBList = $this->c->config->p_message_img_tag == '1' ? [] : ['img'];
-
-        $parser = $this->c->Parser->setAttr('isSign', false)
-            ->setWhiteList($bbWList)
-            ->setBlackList($bbBList)
-            ->parse($this->cens()->message);
-
-        if ($this->hide_smilies != '1' && $this->c->config->o_smilies == '1') {
-            $parser->detectSmilies();
-        }
-
-        return $parser->getHtml();
+        return $this->c->Parser->parseMessage($this->cens()->message, (bool) $this->hide_smilies); //????
     }
 }

+ 1 - 13
app/Models/User.php

@@ -199,18 +199,6 @@ class User extends DataModel
      */
     protected function gethtmlSign()
     {
-        $bbWList = $this->c->config->p_sig_bbcode == '1' ? $this->c->BBCODE_INFO['forSign'] : [];
-        $bbBList = $this->c->config->p_sig_img_tag == '1' ? [] : ['img'];
-
-        $parser = $this->c->Parser->setAttr('isSign', true)
-            ->setWhiteList($bbWList)
-            ->setBlackList($bbBList)
-            ->parse($this->cens()->signature);
-
-        if ($this->c->config->o_smilies_sig == '1') {
-            $parser->detectSmilies();
-        }
-
-        return $parser->getHtml();
+        return $this->c->Parser->parseSignature($this->cens()->signature); //????
     }
 }

+ 1 - 1
app/lang/English/post.po

@@ -73,7 +73,7 @@ msgid "Topic review"
 msgstr "Topic review (newest first)"
 
 msgid "Flood start"
-msgstr "At least %s seconds have to pass between posts. Please wait %s seconds and try posting again."
+msgstr "At least %1$s seconds have to pass between posts. Please wait %2$s seconds and try posting again."
 
 msgid "Preview"
 msgstr "Preview"

+ 1 - 1
app/lang/Russian/post.po

@@ -73,7 +73,7 @@ msgid "Topic review"
 msgstr "Обзор темы (новое вверху)"
 
 msgid "Flood start"
-msgstr "Хотя бы %s секунд должно пройти между отправкой сообщений. Пожалуйста, подождите %s секунд и попробуйте снова."
+msgstr "Хотя бы %1$s секунд должно пройти между отправкой сообщений. Пожалуйста, подождите %2$s секунд и попробуйте снова."
 
 msgid "Preview"
 msgstr "Предпросмотр"

+ 1 - 1
app/templates/post.tpl

@@ -20,7 +20,7 @@
       <h2>{!! __('Post preview') !!}</h2>
       <div class="f-post-body clearfix">
         <div class="f-post-right f-post-main">
-          {!! $p->previewHtml !!}
+          {!! $p->cens()->previewHtml !!}
         </div>
       </div>
     </section>