* @license GNU General Public License, version 2 (GPL-2.0) * * For full copyright and license information, please see * the docs/CREDITS.txt file. * */ /** * @ignore */ if (!defined('IN_PHPBB')) { exit; } class acp_bbcodes { var $u_action; function main($id, $mode) { global $db, $user, $auth, $template, $cache, $request, $phpbb_dispatcher; global $config, $phpbb_root_path, $phpbb_admin_path, $phpEx; $user->add_lang('acp/posting'); // Set up general vars $action = request_var('action', ''); $bbcode_id = request_var('bbcode', 0); $this->tpl_name = 'acp_bbcodes'; $this->page_title = 'ACP_BBCODES'; $form_key = 'acp_bbcodes'; add_form_key($form_key); // Set up mode-specific vars switch ($action) { case 'add': $bbcode_match = $bbcode_tpl = $bbcode_helpline = ''; $display_on_posting = 0; break; case 'edit': $sql = 'SELECT bbcode_match, bbcode_tpl, display_on_posting, bbcode_helpline FROM ' . BBCODES_TABLE . ' WHERE bbcode_id = ' . $bbcode_id; $result = $db->sql_query($sql); $row = $db->sql_fetchrow($result); $db->sql_freeresult($result); if (!$row) { trigger_error($user->lang['BBCODE_NOT_EXIST'] . adm_back_link($this->u_action), E_USER_WARNING); } $bbcode_match = $row['bbcode_match']; $bbcode_tpl = htmlspecialchars($row['bbcode_tpl']); $display_on_posting = $row['display_on_posting']; $bbcode_helpline = $row['bbcode_helpline']; break; case 'modify': $sql = 'SELECT bbcode_id, bbcode_tag FROM ' . BBCODES_TABLE . ' WHERE bbcode_id = ' . $bbcode_id; $result = $db->sql_query($sql); $row = $db->sql_fetchrow($result); $db->sql_freeresult($result); if (!$row) { trigger_error($user->lang['BBCODE_NOT_EXIST'] . adm_back_link($this->u_action), E_USER_WARNING); } // No break here case 'create': $display_on_posting = request_var('display_on_posting', 0); $bbcode_match = request_var('bbcode_match', ''); $bbcode_tpl = htmlspecialchars_decode(utf8_normalize_nfc(request_var('bbcode_tpl', '', true))); $bbcode_helpline = utf8_normalize_nfc(request_var('bbcode_helpline', '', true)); break; } // Do major work switch ($action) { case 'edit': case 'add': $tpl_ary = array( 'S_EDIT_BBCODE' => true, 'U_BACK' => $this->u_action, 'U_ACTION' => $this->u_action . '&action=' . (($action == 'add') ? 'create' : 'modify') . (($bbcode_id) ? "&bbcode=$bbcode_id" : ''), 'L_BBCODE_USAGE_EXPLAIN'=> sprintf($user->lang['BBCODE_USAGE_EXPLAIN'], '', ''), 'BBCODE_MATCH' => $bbcode_match, 'BBCODE_TPL' => $bbcode_tpl, 'BBCODE_HELPLINE' => $bbcode_helpline, 'DISPLAY_ON_POSTING' => $display_on_posting, ); $bbcode_tokens = array('TEXT', 'SIMPLETEXT', 'INTTEXT', 'IDENTIFIER', 'NUMBER', 'EMAIL', 'URL', 'LOCAL_URL', 'RELATIVE_URL', 'COLOR'); /** * Modify custom bbcode template data before we display the add/edit form * * @event core.acp_bbcodes_edit_add * @var string action Type of the action: add|edit * @var array tpl_ary Array with custom bbcode add/edit data * @var int bbcode_id When editing: the bbcode id, * when creating: 0 * @var array bbcode_tokens Array of bbcode tokens * @since 3.1.0-a3 */ $vars = array('action', 'tpl_ary', 'bbcode_id', 'bbcode_tokens'); extract($phpbb_dispatcher->trigger_event('core.acp_bbcodes_edit_add', compact($vars))); $template->assign_vars($tpl_ary); foreach ($bbcode_tokens as $token) { $template->assign_block_vars('token', array( 'TOKEN' => '{' . $token . '}', 'EXPLAIN' => ($token === 'LOCAL_URL') ? $user->lang(array('tokens', $token), generate_board_url() . '/') : $user->lang(array('tokens', $token)), )); } return; break; case 'modify': case 'create': $sql_ary = $hidden_fields = array(); /** * Modify custom bbcode data before the modify/create action * * @event core.acp_bbcodes_modify_create * @var string action Type of the action: modify|create * @var array sql_ary Array with new bbcode data * @var int bbcode_id When editing: the bbcode id, * when creating: 0 * @var bool display_on_posting Display bbcode on posting form * @var string bbcode_match The bbcode usage string to match * @var string bbcode_tpl The bbcode HTML replacement string * @var string bbcode_helpline The bbcode help line string * @var array hidden_fields Array of hidden fields for use when * submitting form when $warn_text is true * @since 3.1.0-a3 */ $vars = array( 'action', 'sql_ary', 'bbcode_id', 'display_on_posting', 'bbcode_match', 'bbcode_tpl', 'bbcode_helpline', 'hidden_fields', ); extract($phpbb_dispatcher->trigger_event('core.acp_bbcodes_modify_create', compact($vars))); $warn_text = preg_match('%<[^>]*\{text[\d]*\}[^>]*>%i', $bbcode_tpl); if (!$warn_text || confirm_box(true)) { $data = $this->build_regexp($bbcode_match, $bbcode_tpl); // Make sure the user didn't pick a "bad" name for the BBCode tag. $hard_coded = array('code', 'quote', 'quote=', 'attachment', 'attachment=', 'b', 'i', 'url', 'url=', 'img', 'size', 'size=', 'color', 'color=', 'u', 'list', 'list=', 'email', 'email=', 'flash', 'flash='); if (($action == 'modify' && strtolower($data['bbcode_tag']) !== strtolower($row['bbcode_tag'])) || ($action == 'create')) { $sql = 'SELECT 1 as test FROM ' . BBCODES_TABLE . " WHERE LOWER(bbcode_tag) = '" . $db->sql_escape(strtolower($data['bbcode_tag'])) . "'"; $result = $db->sql_query($sql); $info = $db->sql_fetchrow($result); $db->sql_freeresult($result); // Grab the end, interrogate the last closing tag if ($info['test'] === '1' || in_array(strtolower($data['bbcode_tag']), $hard_coded) || (preg_match('#\[/([^[]*)]$#', $bbcode_match, $regs) && in_array(strtolower($regs[1]), $hard_coded))) { trigger_error($user->lang['BBCODE_INVALID_TAG_NAME'] . adm_back_link($this->u_action), E_USER_WARNING); } } if (substr($data['bbcode_tag'], -1) === '=') { $test = substr($data['bbcode_tag'], 0, -1); } else { $test = $data['bbcode_tag']; } if (!preg_match('%\\[' . $test . '[^]]*].*?\\[/' . $test . ']%s', $bbcode_match)) { trigger_error($user->lang['BBCODE_OPEN_ENDED_TAG'] . adm_back_link($this->u_action), E_USER_WARNING); } if (strlen($data['bbcode_tag']) > 16) { trigger_error($user->lang['BBCODE_TAG_TOO_LONG'] . adm_back_link($this->u_action), E_USER_WARNING); } if (strlen($bbcode_match) > 4000) { trigger_error($user->lang['BBCODE_TAG_DEF_TOO_LONG'] . adm_back_link($this->u_action), E_USER_WARNING); } if (strlen($bbcode_helpline) > 255) { trigger_error($user->lang['BBCODE_HELPLINE_TOO_LONG'] . adm_back_link($this->u_action), E_USER_WARNING); } $sql_ary = array_merge($sql_ary, array( 'bbcode_tag' => $data['bbcode_tag'], 'bbcode_match' => $bbcode_match, 'bbcode_tpl' => $bbcode_tpl, 'display_on_posting' => $display_on_posting, 'bbcode_helpline' => $bbcode_helpline, 'first_pass_match' => $data['first_pass_match'], 'first_pass_replace' => $data['first_pass_replace'], 'second_pass_match' => $data['second_pass_match'], 'second_pass_replace' => $data['second_pass_replace'] )); if ($action == 'create') { $sql = 'SELECT MAX(bbcode_id) as max_bbcode_id FROM ' . BBCODES_TABLE; $result = $db->sql_query($sql); $row = $db->sql_fetchrow($result); $db->sql_freeresult($result); if ($row) { $bbcode_id = $row['max_bbcode_id'] + 1; // Make sure it is greater than the core bbcode ids... if ($bbcode_id <= NUM_CORE_BBCODES) { $bbcode_id = NUM_CORE_BBCODES + 1; } } else { $bbcode_id = NUM_CORE_BBCODES + 1; } if ($bbcode_id > BBCODE_LIMIT) { trigger_error($user->lang['TOO_MANY_BBCODES'] . adm_back_link($this->u_action), E_USER_WARNING); } $sql_ary['bbcode_id'] = (int) $bbcode_id; $db->sql_query('INSERT INTO ' . BBCODES_TABLE . $db->sql_build_array('INSERT', $sql_ary)); $cache->destroy('sql', BBCODES_TABLE); $lang = 'BBCODE_ADDED'; $log_action = 'LOG_BBCODE_ADD'; } else { $sql = 'UPDATE ' . BBCODES_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' WHERE bbcode_id = ' . $bbcode_id; $db->sql_query($sql); $cache->destroy('sql', BBCODES_TABLE); $lang = 'BBCODE_EDITED'; $log_action = 'LOG_BBCODE_EDIT'; } add_log('admin', $log_action, $data['bbcode_tag']); trigger_error($user->lang[$lang] . adm_back_link($this->u_action)); } else { confirm_box(false, $user->lang['BBCODE_DANGER'], build_hidden_fields(array_merge($hidden_fields, array( 'action' => $action, 'bbcode' => $bbcode_id, 'bbcode_match' => $bbcode_match, 'bbcode_tpl' => htmlspecialchars($bbcode_tpl), 'bbcode_helpline' => $bbcode_helpline, 'display_on_posting' => $display_on_posting, ))) , 'confirm_bbcode.html'); } break; case 'delete': $sql = 'SELECT bbcode_tag FROM ' . BBCODES_TABLE . " WHERE bbcode_id = $bbcode_id"; $result = $db->sql_query($sql); $row = $db->sql_fetchrow($result); $db->sql_freeresult($result); if ($row) { if (confirm_box(true)) { $db->sql_query('DELETE FROM ' . BBCODES_TABLE . " WHERE bbcode_id = $bbcode_id"); $cache->destroy('sql', BBCODES_TABLE); add_log('admin', 'LOG_BBCODE_DELETE', $row['bbcode_tag']); if ($request->is_ajax()) { $json_response = new \phpbb\json_response; $json_response->send(array( 'MESSAGE_TITLE' => $user->lang['INFORMATION'], 'MESSAGE_TEXT' => $user->lang['BBCODE_DELETED'], 'REFRESH_DATA' => array( 'time' => 3 ) )); } } else { confirm_box(false, $user->lang['CONFIRM_OPERATION'], build_hidden_fields(array( 'bbcode' => $bbcode_id, 'i' => $id, 'mode' => $mode, 'action' => $action)) ); } } break; } $u_action = $this->u_action; $template_data = array( 'U_ACTION' => $this->u_action . '&action=add', ); $sql_ary = array( 'SELECT' => 'b.*', 'FROM' => array(BBCODES_TABLE => 'b'), 'ORDER_BY' => 'b.bbcode_tag', ); /** * Modify custom bbcode template data before we display the form * * @event core.acp_bbcodes_display_form * @var string action Type of the action: modify|create * @var string sql_ary The SQL array to get custom bbcode data * @var array template_data Array with form template data * @var string u_action The u_action link * @since 3.1.0-a3 */ $vars = array('action', 'sql_ary', 'template_data', 'u_action'); extract($phpbb_dispatcher->trigger_event('core.acp_bbcodes_display_form', compact($vars))); $result = $db->sql_query($db->sql_build_query('SELECT', $sql_ary)); $template->assign_vars($template_data); while ($row = $db->sql_fetchrow($result)) { $bbcodes_array = array( 'BBCODE_TAG' => $row['bbcode_tag'], 'U_EDIT' => $u_action . '&action=edit&bbcode=' . $row['bbcode_id'], 'U_DELETE' => $u_action . '&action=delete&bbcode=' . $row['bbcode_id'], ); /** * Modify display of custom bbcodes in the form * * @event core.acp_bbcodes_display_bbcodes * @var array row Array with current bbcode data * @var array bbcodes_array Array of bbcodes template data * @var string u_action The u_action link * @since 3.1.0-a3 */ $vars = array('bbcodes_array', 'row', 'u_action'); extract($phpbb_dispatcher->trigger_event('core.acp_bbcodes_display_bbcodes', compact($vars))); $template->assign_block_vars('bbcodes', $bbcodes_array); } $db->sql_freeresult($result); } /* * Build regular expression for custom bbcode */ function build_regexp(&$bbcode_match, &$bbcode_tpl) { $bbcode_match = trim($bbcode_match); $bbcode_tpl = trim($bbcode_tpl); $utf8 = strpos($bbcode_match, 'INTTEXT') !== false; $utf8_pcre_properties = phpbb_pcre_utf8_support(); $fp_match = preg_quote($bbcode_match, '!'); $fp_replace = preg_replace('#^\[(.*?)\]#', '[$1:$uid]', $bbcode_match); $fp_replace = preg_replace('#\[/(.*?)\]$#', '[/$1:$uid]', $fp_replace); $sp_match = preg_quote($bbcode_match, '!'); $sp_match = preg_replace('#^\\\\\[(.*?)\\\\\]#', '\[$1:$uid\]', $sp_match); $sp_match = preg_replace('#\\\\\[/(.*?)\\\\\]$#', '\[/$1:$uid\]', $sp_match); $sp_replace = $bbcode_tpl; // @todo Make sure to change this too if something changed in message parsing $tokens = array( 'URL' => array( '!(?:(' . str_replace(array('!', '\#'), array('\!', '#'), get_preg_expression('url')) . ')|(' . str_replace(array('!', '\#'), array('\!', '#'), get_preg_expression('www_url')) . '))!ie' => "\$this->bbcode_specialchars(('\$1') ? '\$1' : 'http://\$2')" ), 'LOCAL_URL' => array( '!(' . str_replace(array('!', '\#'), array('\!', '#'), get_preg_expression('relative_url')) . ')!e' => "\$this->bbcode_specialchars('$1')" ), 'RELATIVE_URL' => array( '!(' . str_replace(array('!', '\#'), array('\!', '#'), get_preg_expression('relative_url')) . ')!e' => "\$this->bbcode_specialchars('$1')" ), 'EMAIL' => array( '!(' . get_preg_expression('email') . ')!ie' => "\$this->bbcode_specialchars('$1')" ), 'TEXT' => array( '!(.*?)!es' => "str_replace(array(\"\\r\\n\", '\\\"', '\\'', '(', ')'), array(\"\\n\", '\"', ''', '(', ')'), trim('\$1'))" ), 'SIMPLETEXT' => array( '!([a-zA-Z0-9-+.,_ ]+)!' => "$1" ), 'INTTEXT' => array( ($utf8_pcre_properties) ? '!([\p{L}\p{N}\-+,_. ]+)!u' : '!([a-zA-Z0-9\-+,_. ]+)!u' => "$1" ), 'IDENTIFIER' => array( '!([a-zA-Z0-9-_]+)!' => "$1" ), 'COLOR' => array( '!([a-z]+|#[0-9abcdef]+)!i' => '$1' ), 'NUMBER' => array( '!([0-9]+)!' => '$1' ) ); $sp_tokens = array( 'URL' => '(?i)((?:' . str_replace(array('!', '\#'), array('\!', '#'), get_preg_expression('url')) . ')|(?:' . str_replace(array('!', '\#'), array('\!', '#'), get_preg_expression('www_url')) . '))(?-i)', 'LOCAL_URL' => '(?i)(' . str_replace(array('!', '\#'), array('\!', '#'), get_preg_expression('relative_url')) . ')(?-i)', 'RELATIVE_URL' => '(?i)(' . str_replace(array('!', '\#'), array('\!', '#'), get_preg_expression('relative_url')) . ')(?-i)', 'EMAIL' => '(' . get_preg_expression('email') . ')', 'TEXT' => '(.*?)', 'SIMPLETEXT' => '([a-zA-Z0-9-+.,_ ]+)', 'INTTEXT' => ($utf8_pcre_properties) ? '([\p{L}\p{N}\-+,_. ]+)' : '([a-zA-Z0-9\-+,_. ]+)', 'IDENTIFIER' => '([a-zA-Z0-9-_]+)', 'COLOR' => '([a-zA-Z]+|#[0-9abcdefABCDEF]+)', 'NUMBER' => '([0-9]+)', ); $pad = 0; $modifiers = 'i'; $modifiers .= ($utf8 && $utf8_pcre_properties) ? 'u' : ''; if (preg_match_all('/\{(' . implode('|', array_keys($tokens)) . ')[0-9]*\}/i', $bbcode_match, $m)) { foreach ($m[0] as $n => $token) { $token_type = $m[1][$n]; reset($tokens[strtoupper($token_type)]); list($match, $replace) = each($tokens[strtoupper($token_type)]); // Pad backreference numbers from tokens if (preg_match_all('/(?lang['BBCODE_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING); } $fp_match = preg_replace('#\[/?' . $bbcode_search . '#ie', "strtolower('\$0')", $fp_match); $fp_replace = preg_replace('#\[/?' . $bbcode_search . '#ie', "strtolower('\$0')", $fp_replace); $sp_match = preg_replace('#\[/?' . $bbcode_search . '#ie', "strtolower('\$0')", $sp_match); $sp_replace = preg_replace('#\[/?' . $bbcode_search . '#ie', "strtolower('\$0')", $sp_replace); return array( 'bbcode_tag' => $bbcode_tag, 'first_pass_match' => $fp_match, 'first_pass_replace' => $fp_replace, 'second_pass_match' => $sp_match, 'second_pass_replace' => $sp_replace ); } } *ia8҇ |) 7ߓMJ+f <y/ؒR~|! kvsz_<]\.?~P{~ eWhf.-1bt`@/G+pU8R5]pk^(+jUޢ̧ U'_k/6uGL=rբ0e_Ը$vSTjϹX(u%D] s˥󍏺̇lkiS3P̜iPA ʍ 29ZLZӫ6"yt_ ~_֜!2 d |lH=8c>5x F?9:o@Uc)`(-h6q*1fq?0P1U䔲1k'd-҉ wWiyY죶D<pFr6kT$ؠ&9&ehy+WUl\_!,G䎖>a1Ea]ƣ&'XDZmM+F6&`~ KQ ꖀxL,?Lw#(Ֆ1PZֺ2fJp)V.lS@3fi$r.X[7*byu"/Ჶe٤2db65ܔd_)XEh]icC$Kwa"+ΐVbNF?("Z|H&[谚l _7ֳJ 1RS&tg%^[m6kGڜnEh1y Q0 bގ5PE ˘J]n0CzԹHq$Zg Y;ڥ zW'Fx?Y8n /nd"._M`.づ R{a)ӓ8qjPeiow2bۑ"x%T @b꩗GcJi#Mm׮]^qL $(!%}( Z*1*e{$tl`%t-Ԭ4ɖ85T+JPpFVߪXzZ}IJX3I2d ,[d󯉄NNU`$Ruf+eR  ^L=|`&k 9jT9!6-eCETwrYTßȼ;`X}hpNE cB_xrHlj [k>̉ +h1}2+aUV >(O!+S%pˤ.Squl^J/t$ep>GxԱޱFhM6;#Lr\{JX%&4u`qVup(1)% 0\ j3&ю)c`-&a%HmWymtq(a*?a 6CXLr!`,(b։BDd,{Ð @3?^DW&;#Kdŷ.SK,Co bQ}3w[9#p}Gh>O3OqAmln^+NcYBub|`ϧ2maJR gM<yuF| /ϪQ &}rƜtZɯEnާB“_豓Q0u.hjN-t3eU'wW)a~7ՎhQdxBĬ0'mƆQ oe7=h(xkfbfs&[e74[ ~ṄţOJ_ˑ$3UA cRǐjY3.#w/- =:~ gowF~xS&Ϫ:# #V[*r 1ئIe^9c8ɚ0pa:0ŠN|&yUL1TڥIkY7{s4]&miNɹM;LnUu h#P"y@%~>hg!کX&]*A3Vz 6۷pMꍳ]7P,ʻIgutXbj/O$o9ēZt, /ZC>HOOylNn%)֎36l[;59bLgIZ!}t Q_%sn[X*g'C;`ŗRB-)фSLT!ò& }p{4XՔ-Z&gf"6sWĦ+=R0rj20>(40.`]MZnMVɡe2tO7}~< 0T/3RxyBmQf3G zBߴ[z+"ek 7~k/R̬eR;L.6Dj۵?[ 4zпsDkv0&<3XE<u|Q[q"8%~8}}ڬ0_4\hMn.JׂF+,yZvCke>0`YK! 6 w^ ApvX$NwG:+ȎHA*&~<I׌QԧX \-_pF,['+lLr:Fċ;~ 0vAxwx ɵJJ}$?'֙!YuMXbjlZSh% V!C]i7D{Kc(d_nTR^][@^YWб{@lBkvnျ% s0yɇBy- ` \B5 ƟF<}|y&UP\z/(ZL+dV'PNEӣD+NE2u8~ߚw91_eoYpe*VWCo Ļx0.6y3 2WMjo Gw+7!՜w4Ajs%2FY*>.ŋ ӚXz(65eaț{^@)k$*x D֫ < DDDe0ҫR’s(c{ʹ:jRhy ڌ܃"`|zFKcЊI%Ow\z(8o?s5GkCƯꦆt*yI;NQ$ T 1|_`|J\%~=#ݎ* 8-$sI8XyXwmv[TӆtȭdK\iL?ti/N[AybS ռv*b|4a?aI<nUZ"ް2ɐ1L-9~\r6QNPXԤҰQ1\`Qy4DpPNz= `.901@YbB)# 4FFddO|6;û@x ƫ!-$} 1Z7vVB~zFg\v=|mvSMiNg7DoFBԑ}bۮg, #筂^B5p-VH$bLkO+  @&U ?Lۮ_ Ѥ6FpLy-w5nC Sir_02*D8LeS7W@bc q+@ܳǀ|.!rQ O17X;0 kNb<Üt2GZ+Aʈ .p :omEL;ѵIi" ښ͸ /^2 o꿟*! bM)@β2a 5LI/]І{;Gr޺xxG Є.i8j@wf`?4Un;ː^\#LmW96 ɑWj3XP1Qv:2>P'뭞J 9Y0s@ʛy[ȯVI5dgWcbBP#Yc3V=vZ5#+nYt/U0c+d x}df'vB+'NSR0Sjdp}0gCI6?lPVzVǴT)Ɠ mJ*z  AѮ92+BhvUܣŨki6*vYNXmDKSbg3 ﱭ@"f{Y݀NLCZat>i.zG,롵+L=9wqt-aP.Ϧy>|1v(rH*l;)>B4"o=Wrs~uזB66; /z.nk` u [ۖ>mp߄(Y1 nwJ_G~zw:1d[ۖ/KTBLGȞt[Ϋakۖ:e].xP6l(Xiy~*~qO[.ePi-ʣjgjxS֩3"={y;w$)gTR@ycǵVF25E[-yՃ"RH 7&@q>NɷAd Ard`Zep82^P~&qsX92/&=-Ўh16 ݯDR"wQ0(E aRCF$#"9d|]_&N{jg94 /C&8a'_/:nݏ>DF%ǻ2`Q#4rr4 r7'|:{-،hD)JFp("Ƽ?!bd' #>:4|jI\$C_9 I0"H_ %|pt IMx8Щ/9.I2o,Az  Mm,YD٪P}Pvh%9Y[vS·\+oHzVA㺘pd|~qcY0'vLmM)~Ȁthww );~;ރ@ҲxPO2΀өfZ[\dh1Q='ޘo5 踺u[X=<ZX6##⿶n$9);?V4it.|}/,G$wzZZlQI)M ʙk&(ދԉT=u4f'0C$=`MP2i4䨅ҩs6zqgLzym-$-߶rrg3Y[{pHOI#,4]驉)c$.xv!v kJ oZ_'(QwG6hx(PFB1hv!0Pb:`Ӯˍbh ]=!͍֔F(К |l@5=hN8#|T*M()s0tNC;mk.unpO: ^TP7 wM{1 y.ԄWWaԢj~g3۸,} H9"=wȞ=x]|BK߲SFNxhvm+!:y^B{fguAjDs1sҿ_$BvvszQ8eKg=k )H٬fʰ7Q'kbco{-\uQu_ŏ!f8.72\ka 1Ѷɩ*?P3ZA\hԌ~4䊦| տTG=TKud q2%s30W>% O\ и`tBfmeMHbP\Te t w{VqGq_ڛ/ЃG'Ԣ?&bUǀH~6̒б 2ҋqJ]줭%_!P=} р@'af 3_ۈ?~JdfKfHYϊur58S jrg> \ȷsJZ!qҵ\P!?,N&gjvUO Aݳ&qIE` ;52)3SPqOtƦ3!BLvFKs2#ٳfFFP┛-^/cty~ᡁإGfǻiUus@:DVX\nQ Wݲ7 `V& WRY8bY|˧O뵗0aA>fF?\(Nw9=RE@dTؗz=Sb:p# ߒL$48( l_&/rh2>6yV6Yh~aEk9CX5Ւ&R tqŠkςmNxsf ԋ].2HYDeY S8'T'X+$nh4wEk]=1ϫM)zi>dOjLnwn!*}[QDh&zƷĎk`>c-L QO+nV Y; FW p)n=і8)w0  ڛIX$VOވc=; 9 cPrb5hn~T{͉&qPQOEQS | cJ&=KHro*:oIlpwcg8{<-_œrٴȣ}r»>GRhqh7z4٘{Q(v( jOrجz+jEYsDD#Ɉ4?ۯ4 v^49صs iE! h \N>2l|=H"U*Fz;Ոhp;h)mx`y+YX4.Q;^FhWkzY޷+cyk.%pFޮYQ',tnF۶W(']%S'0ڊDp]\'6Q)bxH4k[D '-kWˊ#m'OY\! :%#}0nC۫ooS_¾Z jpo:k9b%:JO5ۖgoaU!j:ddҺqAbj3"Q+ >(Z-=י"nf鸝AY2+SzCresHBNI9~ ͖;`xg4HY+ 7)D&z1K.NKcݣx*1]АzMp<~rkY~T5ɾ5NƆ:]* 59@6DTXq[umEPv\2D "\ʹW?J= GoܿCf"^;I1U:SqW{8 =&+o|r"#-2?%I_M|(A ()wF;R&{}萺: ^8fGzW9"*}9G(h۰Dm(LPIUPa[!̌\md+ g*wE`bLC@qנs#o&Nx-8 ..83q!ӿw(,xN4( pGlɧD`Y;T=A^>OE4%Y7$H=W(c[b=3t4nW"{i#.7cӊƀrosE(kg)q!`/ypGLw'a  N0jpj~މv4\v I WFPɃȹr[ט>۝.6d,FJV m`AW|Jbf#՝3)Musߤš1}~4W:A"!Ӎֵ.l`C&A JHD""Zmh۝Vщ/5~r IC 5$n2u CäCP]ME$f૩WEYzmZ#_$`4JU\G1ԅ `qׇ~Y׹+t,o\xG*h<+p/ݻTxx;8[:} KoF+f&s۵2mnS봻sq\ܷdIMFY1*'fI]J?wbۄ nw |yBsI&޲,|"4PjS{ 9ɤ6s >)GR ^OgN:H?)v H _Aw:hP]U44} t&hAm}?Вjg'o!.\xǷ @@;b+;o1սtwglP @{ζBd~)s#Ed=afflo W5EӸ Wɭ]uɜ} ʤjKXYZQhhj'^]X:w@7YR$FvZlI Mwn?}PT# pwqz+3_>{gmt/Gk˚-dc;.k]ݳthGX"fo+dR͉N}Q!vk_dnE"^=DFlDeĹU荞X^zDzWE٣p2wR#eBg@Ǣ^32XT j\ɋ(.3<8@ _d uQX-QVX=کRo`Oѳ6Y %G?.Qd %.2VbRBib ǜDq\B3I3zVwj' '>ẇd%< ַ٠8*Bi}jYú>&MSz(HV?A?.)jGtҚ~ݼ)ױс.ܦc)o(,Tzbzŀ>``D^Yiv65|&vƫAby3J|+ۀ$:>DF$8'[G}&rx',z0LJTV @^)Wۯ)&^ f8r U ! Ƶ&Yxy1& _Ⳟ8jdcwwKe/yW/Bz"&7JNęڳSdvk4oѲm8>Z\6 )LK6$_JZ݈F%”6*odzpnr9kt/ȋ^VMcU?ċt칸e_k#&.qŒCTd]~QLaRE0kΐUumC0;sFH)} R1k_>ij_8)*$`|W^{wj/(h r>kF!|%v=i0c/ c_XOd>:R犼PèX-#A@78 Q\0b(XZgjN8  \h'Z}z)`]x*C3ӑI4):r*q}%l h~_ O7`qЉ4 vUŸ?!uxHl1,>ymT-ߘhbm=.jO'LI(Zw M bgudSW^ݵ/;F rOrM4f~<D2Ź1+Էm,b5@i@֩65&=RΕٌOQhjFub7JYkI 9! ݽmĩ'~C\1|_oՕsh['+۞g3R{Nr12%xrk_K@rоJ+K'G=p~ǔKs!ÜR?3-3c((Ư[ Z3=3hbjݩ8?ػ?OH(F[s&_ cҘD#G=+׏f>#'քi;Fb."#pl–Fػ8 ~n*XS:O-2=:OFC.r 6i t+فː N7)\oj)3k2pyHKW"úJ:Th5NWqgvL7}+[e  މoLN}x_;ϐ4pbKh04C-KHzfzj hLe!mw1 ^hDI! dk3M6'>lJ<RY>G}E{Ѡh]x, 4U:*t˹)& eDVG5&N &~ޘWq8Fy-nKjo~纆 Kax8 ǃ"GZWNb=d9)Byr;*vA5qXgb[ uSdD@W` kj)2]ʙ`5I I.et_%bK zx;T]ř_xf:REfPg)f\VMiwRK&Fj xHnȏEkJpu"[?Q>&@rXǾ`?qS'<5NXJ^WJg 0:"x$:~;t_=Kg{hBeeP)Ty97CDKQk!6ԁD؊CהXFvz*0/~y -0뛥jmZ;)_Ĩ 'gyxP ?'"aEOnBƶx"<3VTU$ ³BFbP,ȉ^%PzZ:c(`;r`RB3C]+mSAUmulz,J ˒v 5,W+U*!yFC҇k1xԺٙj3cwݱs)Ni ea/ 8( 8K06J]nPA#yJz*{o7MM.9@,sǁኡJEk͏m-]n"IttZcυi(Ps퇵!Φ/yŜxY?H!Az0FNCfW( /&Bv^T1Dṯ0-d_cƋʟwO-G;MwC^mȨۻ$nTgw^A)JlY~ K|#|Q@+H:6J֯MӁC`c 1IoI#+_xNEK 00?&w.aE ŚV R66X E2z+9dLJ Qoxz xtKEdm׍vI{ZuK ktqKOcuAۗj}pu4t>}ZH25FL@OաF-J,bҳL'ݏZiXejX_݅B6CPeq0 EV &>a8'(CqKR6ԭJn5E0Re:4&{m<+'=/(e|{s<)g6rlˉONTZ6tϽ9aL"8AWeg92z0͸;Os+ǟP8s*ޓp&\ ۻd8Fhŵ@U¨"Ƞ y/c o-PK #J"jTK/_mNast(>R5"#YfV69x.p^c02'ҲXlV } רKaKϚ}FFHNE ;εKZ~\+UFS.* x݃2KS_g{mW]-vÈ飆8B$@K%9 I^`Ԍ0Oʲw nQ}?L5]Pbᅏif߈EklYMK բ6oux- ^yC 6;+M)0Νq+:Vm ؏G0Ri2Gf)FT[3̭[qp4d'͢[CNuPQJ 6;ԉicKĿ9m]n fR]r392)>$đ3cL s~fQ5St4Q~L\oFt5Ŧg[=oⷵ]ƉLh Olq4*(ŽC҇J_6K;M٨QˆYm n5u/H\A:"*uzad[;Z&QP_?z+,/Ve+$ssk^$FWh %Ӟ WӋGiD4az-6=F70K2 ,eB^ZIiH)㖸9n4!ujPuQ%D!F. ,"Qֲ48kfl`ٿgrꔄ;NeSa Ë}:d al$ď4e]3ڰuCG- է5҆ym˰jd\xX?Z;9!`!\ cn%fj jL}*>500"(7aHGχ]DW1yKɃKT5儂YM꾦0C2+1_#u||U/ .*m82D(h0l3=ж}TJ3ϨޢAibC֙gݰ$ժ} VͽN\>ry8z|-=ǵAJ̽燬IQ'߅INB=Eɽ5'VGZHCLRkXk ,} ta%men+%+D+Zվ тDquwfZQmta:6mI*45DOGL8o ;0Q*ŠT 1vZ)}Bñ\p =N:<އ>^a.EX\G/iƾS~ Q>x rQ2c?Nz+j %SwpG9ꠙ@b@D=%rUNiq R_/I8biFN9hsՐ*zx㞡k:;wcDУbBhJsLzJ4cƂ`8U rյY # ŧ/rR+oQEF<,@8/~8yUyK;E1y|fjR=$z/H׾ihپ#usPcTgQO\ UDnjUn &Tfk5lXZkj "^էgv0:G8z^M멻R!L>\j{^hhPK~ zZ2{]pE9 ô(;+gDYa}OB3RJt O?g4pᖍC8ε&7rǘԁF 2/VdF&[w+Fxq#rgb.Ƶ=~Ns$/ $pC7 %,;Pm1{`Y8,-㽔8Iۃ,L]x|Q[v1}ZlS`PXJWES1~HX0չ ΋}6NIv!$ȫƨs+,s>x J f>D"u_0>V6xH'D; }b8RNF[[pXliJ7468 3sS|d>xHUM2Sbھ;TLiHh DxbI4P@S&ڔ ̇~aeriZ.j{ͽ&=oJ֯y5!Ld[ T 9f,~fjɒM$ !7*=@?CgPLC~`vkkk"|aqG>,; -2t55x^GZq;Qv<m<: e%c3[dqwZ]NN-.\TlgLex);BIXFO9 n3x j0+i~蓢SA-6{Oe8zm"J3m& ҽq1U`h7lB|#T3~O9cBBl:m:qZbf{;ފh?&P BmIVgt|=Z~:!fA >snyI;nYx7hM7||]tNbal%|չoqጏc0Z{{6ۅL=?i7%`{ˎa6@~bzئg3WƉ |&uL̄/c7s#]a|mR=}p1*5*o{A] r H-k{m/)h7+zf.LMCI;=ba;L44[Y|@G!0&Q!SJR2t yl,,j6n7:4^1'l@E \ӻ߳i~]eCۇB JkiFaUzmoyO%vCEDoM$ ]qʰPĘQ4t9>^Dk2`W`f #GE1@c{G>>~Izz{Mj/,L/5GZ(lJR1I$k*ĺz$Ęsk]A6EO!ÁIb/󳆊 j 3BtEZ!a-*,J镮>#]v_p0 Mj6b5aC&eV^~JCo_p-Ngң&lb/1ЫlWe%7Ve*.J%9&띀O#`{*o_﹒%0OР+ԑT҃ҬIMq{4+}p5 !T^. ÕAUᕏNJ_s`i^K i ̆gNܖwX`HhǪ) ?hIi[jY~NhhC/(pq9Ójʃ9ꝚAyeHG[݀e$b=:[)/xSCyF[`RkHd-ΑC$$]N\LȡТ-ߜ63GsBnD&r^qX{s > GL\[^z9Kgi8ݭ96ۜ!^WM{=I9 U 8y[ B?/va ҁ.q5X 1Lj7WAa`xPM#D b1dM:q ÒnUdJ'!E=2q=ܾuֱZ4җqAjt]ZAsͥMLVapPnCZ} bC5eʂ¬PWz4ǼrI73#UqJ"c ;qKEL=c% bhNwKҝu2I,ge֧Tq 0p$!~QPq+B&[н;EX$#ԧ]iDBM+*A0E+tG3k`)F:w'%fl΅2wxZܡ yWG ; [Zp7s2ڞȢkW\>yekp+SeO#'iea v/-ы*wJ[u=^j2j[oNrP[# bw#էjY4mC)k^n hyGEW.9"H%׼ L1VI؀bsRFP-[;MSāD#eYU7w d@.i"`1XڝP ȟb]'c7D2Yպ 5 A:]B?CY:_ܥ ?h^ҐG__0{'NEw3p[P\EP5̙a#P_R<#`GPJD㞅ĒN фdL鈴ldVy@uNt(hd'H@=2J2"^N>qc3Ha2#f4 yKeZԳ"^l.o<yQ],лKp|cZ FϘAf_vue.cX(־tv5)a;s-f?X ]z. O/(LQ\I:4(P9,w{R x- \Ȇx76E1q/ -+ ;c/r,rs~ڀnm2Z.(MRYu Dwݒ A'$ze^̞ эl!ٕh"G`QHgXG%)nT"Qv?ehS6ttQ5IOGuOOdԂvYAY<\Ĭ*%#7̛xGJУhDKf;J@GVi.Rr ,OyeEĺm@}v#W~JsA6c>ӨTFe9]6[o-\.沑.BeVoS״XAw'㸪;⧽ktM5sF6&GqE}YTA2NCV@F+2Uj| c{)Y-4inXsG $fDoT@-*.]+AϽ~M-(aF@qXvz7]X92-)Zv4slѴf۶`8ߐz$!Hh&$9HINa_sƦ H-%Kή*,ѫ^[,7wY:NV(mW-n߯ p j "> c #bkft Ge%A B@!5~cjw/#v6x . R `M{g o'cr)3i ւiԷ>zH)@b6f?/2cYRݥJP~`dY\"F,Ec kI+>lI^^pg']!fOy>U DZݘ:9d{kP&@t_\chH˾B TbGx'ECՌ}0e߫Ś0+l@dOisyH! 4t$࣪#V[>3pWp~ۮ{aFr5Bmﰾ3!-`IyѺ>$4&0LAF@q mu- ѫ\y"HE2}~C4lH J9ɘ@ܤd]1Ծv mYa-zrdGPOHm5^ REz6<U*9RKG&z,htSga觑w'hmvьsqKTƺpk n{Ye|Jm^8AT~`Oߑ "͗i+s/kJf_L]fF[%C:k\l]gyWGq3*vS;-{']q a~TI!l b஛&R)рH[)߅BΆ@5_ ׸;:Pkݘ0?ji||#6T+y/nDyչU,Agy0ߊ$5 kZvrm;f_N,;c|H^-"fHCXd=,I+0+kH0ޯ|]yw~} !OȿgsY[x2>l_8Ft7D5 VpA jM3ZS cP K&4 x@U7d. ?uDzxW]v mjI [^I</Q\sf_"r>bixŠ;@eqhWHߑ7E(3*vI5( :'&;ABpWהaxȩfL a$Km@P+0)<mߡUآZ>vcgJ*"Cwcog Ƨ|Xf#Vp!s WkEӔ<}蓞e7w͗ҍ0Ohv5;Ss^=v9?_z>Ǖ'.2NI5%Lv喳"I];{*:kn }F''m%T\pqmJz$z%^^ gEer~3u֨v>(s:hWձEU[TMfJ}\ڏTS֦PWJs8'w3xGG ,>[Rxv;@0P$,;Q.BZ3phf'۽-7i`lFF@+keYk>$Ƌ3|K`+R]؃'R13#Ƙ|iɓrk7Zzz:y}9ӝy˻-+Up⳹ӹ`3Q~ŝdj,2j}xlɊ*7'%9"R*rIo. g;g#߶@:*nP H.~^7Qʘbӛ"z#jعpso~ElkC8zlv swf5IXeV%Hщ03Uqpbq=JQoB/*i)c2PIu5>9өP3 \N9wg,nrk:UF\]e,M51-;DxMA; n[1^ӯ&QeL5l&1>uFJ xwHLwKz%6LQ^_*+XvxE>`, (n.&Y:_jl-~?\rm1ǀ ZK__oߊӬґ[RKa2؝D֦kcw/kdYJh] TYwY7-HE?"RS!Ֆ Gי{ńK(,ufƕlNڴQu!XHfDbѼ|MGKyXҕJl@ e˃oK*XuNl!#ޟƖ# .H!طGV<EХ%o=?8V,n'{e51 z:$X\ᣏh?ilmB2Y߯pXr )Ib"ԙyWy;I],Ju ۩;_uM^zLNg5&7ɫJA#@a&6ʒ,ծIX5F@(!B(т-F %95Aw1BHmӤmPCA1ϟTI&i69Sr0¿\)~-=tW#E_?yEe'Z͆J%9d/h8Ճd4.%_S|8m~j6ǟk٨\`"t~uN%ne +]?VyC?FFA?RZ;cbEX^Ku {Ja)C]pARžX~ƲU|p{: N+􃭙!HݓI ¤ee5 ;27+ز`TNTTxAoXJF1 83qӱ-3=<G0Pdhv2V?,ur/{ԙhlY Yl^i 3TɖDUf=˫06S;#꡽_T[ ]"4v CGc~)Tai-IXJԵuhQ-TGeQ)ᮠ˻5MRMɍ *3Rp9t\KoR[3SII2)V5 yڃ c)=_cٺZ`ww ?e\㎳%a \#W(iԷ#ɁX~ۺ9Q>}~w1mB93./u i՚1BI('Youf;_fE&{ G,0_6T8ء#|OH˵9Ծxokw H6"H>V/J錥l aJ\ts'~c#0+Ć~t9y Чn"0"sPA d<15ECYQ+%GDKjպeV(qT{0U<y[R F\وUrʱ`.S%~B@Fl %:l̎:7YHtVؒ\Nukxھ+lHs|yb]>1wGn"X!_Yͅ?BCO)YF %fמrM!qiOJ"\Qg`J>T\؎Pn܈GfiB6סI@TXa0VRpeuxPTHYJ}?3A#u*p@)p<7EN 0AZ~ hzl/yx:" `B P[ wJD|AlgmG)څ㭅\IBӶضAvV{ݵF`8KN;@z 1#8eUZs '<̿ӬC5ch]1FS̰~*>MTw4Vl(HP[<#6XXK5WIƖbQĪ6HzLۅdZfQ~^|?|7=̻\J%S{?(S?ƺ Cϑ{n7X6a t!faW!X~?|4{HjhB/ Ru[sut (Q'SfX*|!\c-s3֭cjw}cݘʙDZK ] m n?23gmE^Wۡ?`Vy1{j<]O+IL=~aڔUt/I Y_Hmcu1b1B(@=}ҨT)dvL2dMpwZ[ΡH$xF3Z͓.I6Uxӷ'kj$Yt~1 *qUQ[KD ?eR(j$Ocjx/3<[+I/"!<)J,E?H*;s]EEl_lt9NO8_RZ[pac>wXvP.XL d0IX0M;Zb -^wѮ l_⿁YܳF\[nOa4nCgs0 򗨠TkHOite [Zl,=[CK_Xqifta*,ȍh=F sA!0)LwmA9g +@ms>& dy[R&})Ohx&|:CODµ"7~[JpxeWgqP;QV?lPc Q9Ώ'|)Qh fQv*ΝK&ɜG$#}!WXㅥrۥf?m F@`=u"td>B隋FCHFWٕj0c_*l=&r/a֋[6%h`č-8WlsfĜR^3"[;'hCU9BIILn=;ܟ|uigz”|{l&+"gr-ec*Ӆ>U(($eqp{zc"A}^TeET{wwo1sB {8BE/r惣ۙ?Ӡh1fz3uai8}g߅4wdɈu~`N31RmJUs'U33h-GBZV@Ox>qRG}y^g ϱaPM5!Wb'rl9blՋ' + XJ))&&bmJt"gm9j3w(kU :ptWCMelc!4aT+#i.^_)B MzTҲVAM1N.7/k_,R;jW= A':R ^7,e+t:1&tD;9Bw%(,c>`%oҷ*&ω 6P0DNavan9M߉m^1#Zg9d iAjopFO!+ lu{w2VebVl|ǝ~Ju &vsDV_:;G݉_aBi[ΰֶ@CHgLҬW'I|ݼHZ$#&fUP^ߟdU\H-$D !Μ$@?p<%%ȖF>PduH`a?CO*+f6='tT6hy: 8vîxc|{sݺ:\3Qkx9XՉgWK0, kfͯ5|֟OMtw:uNy :!>GɷlpU~yZkgW*Z;-KfBɪ-r= =7f6'w3ʢk?ͺ7Q.ja߇ײOd-%?5&H#$b&о{Xq M=ʫ7o.'lN˜He0,ܢA[tNaG@ 64$[ē .k?a9_8ܒ=&4ˁq45!ɔTp005̛D- 8GKFNog;!,~.Ig`6ⅲxϻݤo<_EZR;XgkBܫu"&g^c ƍ'j 3 Co"ZKY)sMi$IdH:Vw:n#@tP 2A^xළ?Jɶ8BRg};~r T/9͓o~ r).L`~@@%ttsZ:dDb}~y,)?Z'ݝg`˧W<\?)0 K vmeA.K~aױ=,[kғAr#9f$3&Rn-'+G{'Nyoܢ.d?şW~8ڰKcaVg=!u6zN;z|b%