bbcodes) { $this->bbcode_init(); } global $user; $this->bbcode_bitfield = ''; $bitfield = new bitfield(); foreach ($this->bbcodes as $bbcode_name => $bbcode_data) { if (isset($bbcode_data['disabled']) && $bbcode_data['disabled']) { foreach ($bbcode_data['regexp'] as $regexp => $replacement) { if (preg_match($regexp, $this->message)) { $this->warn_msg[] = sprintf($user->lang['UNAUTHORISED_BBCODE'] , '[' . $bbcode_name . ']'); continue; } } } else { foreach ($bbcode_data['regexp'] as $regexp => $replacement) { // The pattern gets compiled and cached by the PCRE extension, // it should not demand recompilation if (preg_match($regexp, $this->message)) { $this->message = preg_replace($regexp, $replacement, $this->message); $bitfield->set($bbcode_data['bbcode_id']); } } } } $this->bbcode_bitfield = $bitfield->get_base64(); } /** * Prepare some bbcodes for better parsing */ function prepare_bbcodes() { // Ok, seems like users instead want the no-parsing of urls, smilies, etc. after and before and within quote tags being tagged as "not a bug". // Fine by me ;) Will ease our live... but do not come back and cry at us, we won't hear you. /* Add newline at the end and in front of each quote block to prevent parsing errors (urls, smilies, etc.) if (strpos($this->message, '[quote') !== false && strpos($this->message, '[/quote]') !== false) { $this->message = str_replace("\r\n", "\n", $this->message); // We strip newlines and spaces after and before quotes in quotes (trimming) and then add exactly one newline $this->message = preg_replace('#\[quote(=".*?")?\]\s*(.*?)\s*\[/quote\]#siu', '[quote\1]' . "\n" . '\2' ."\n[/quote]", $this->message); } */ // Add other checks which needs to be placed before actually parsing anything (be it bbcodes, smilies, urls...) } /** * Init bbcode data for later parsing */ function bbcode_init($allow_custom_bbcode = true) { static $rowset; // This array holds all bbcode data. BBCodes will be processed in this // order, so it is important to keep [code] in first position and // [quote] in second position. // To parse multiline URL we enable dotall option setting only for URL text // but not for link itself, thus [url][/url] is not affected. $this->bbcodes = array( 'code' => array('bbcode_id' => 8, 'regexp' => array('#\[code(?:=([a-z]+))?\](.+\[/code\])#uise' => "\$this->bbcode_code('\$1', '\$2')")), 'quote' => array('bbcode_id' => 0, 'regexp' => array('#\[quote(?:="(.*?)")?\](.+)\[/quote\]#uise' => "\$this->bbcode_quote('\$0')")), 'attachment' => array('bbcode_id' => 12, 'regexp' => array('#\[attachment=([0-9]+)\](.*?)\[/attachment\]#uise' => "\$this->bbcode_attachment('\$1', '\$2')")), 'b' => array('bbcode_id' => 1, 'regexp' => array('#\[b\](.*?)\[/b\]#uise' => "\$this->bbcode_strong('\$1')")), 'i' => array('bbcode_id' => 2, 'regexp' => array('#\[i\](.*?)\[/i\]#uise' => "\$this->bbcode_italic('\$1')")), 'url' => array('bbcode_id' => 3, 'regexp' => array('#\[url(=(.*))?\](?(1)((?s).*(?-s))|(.*))\[/url\]#uiUe' => "\$this->validate_url('\$2', ('\$3') ? '\$3' : '\$4')")), 'img' => array('bbcode_id' => 4, 'regexp' => array('#\[img\](.*)\[/img\]#uiUe' => "\$this->bbcode_img('\$1')")), 'size' => array('bbcode_id' => 5, 'regexp' => array('#\[size=([\-\+]?\d+)\](.*?)\[/size\]#uise' => "\$this->bbcode_size('\$1', '\$2')")), 'color' => array('bbcode_id' => 6, 'regexp' => array('!\[color=(#[0-9a-f]{3}|#[0-9a-f]{6}|[a-z\-]+)\](.*?)\[/color\]!uise' => "\$this->bbcode_color('\$1', '\$2')")), 'u' => array('bbcode_id' => 7, 'regexp' => array('#\[u\](.*?)\[/u\]#uise' => "\$this->bbcode_underline('\$1')")), 'list' => array('bbcode_id' => 9, 'regexp' => array('#\[list(?:=(?:[a-z0-9]|disc|circle|square))?].*\[/list]#uise' => "\$this->bbcode_parse_list('\$0')")), 'email' => array('bbcode_id' => 10, 'regexp' => array('#\[email=?(.*?)?\](.*?)\[/email\]#uise' => "\$this->validate_email('\$1', '\$2')")), 'flash' => array('bbcode_id' => 11, 'regexp' => array('#\[flash=([0-9]+),([0-9]+)\](.*?)\[/flash\]#uie' => "\$this->bbcode_flash('\$1', '\$2', '\$3')")) ); // Zero the parsed items array $this->parsed_items = array(); foreach ($this->bbcodes as $tag => $bbcode_data) { $this->parsed_items[$tag] = 0; } if (!$allow_custom_bbcode) { return; } if (!is_array($rowset)) { global $db; $rowset = array(); $sql = 'SELECT * FROM ' . BBCODES_TABLE; $result = $db->sql_query($sql); while ($row = $db->sql_fetchrow($result)) { $rowset[] = $row; } $db->sql_freeresult($result); } foreach ($rowset as $row) { $this->bbcodes[$row['bbcode_tag']] = array( 'bbcode_id' => (int) $row['bbcode_id'], 'regexp' => array($row['first_pass_match'] => str_replace('$uid', $this->bbcode_uid, $row['first_pass_replace'])) ); } } /** * Making some pre-checks for bbcodes as well as increasing the number of parsed items */ function check_bbcode($bbcode, &$in) { // when using the /e modifier, preg_replace slashes double-quotes but does not // seem to slash anything else $in = str_replace("\r\n", "\n", str_replace('\"', '"', $in)); // Trimming here to make sure no empty bbcodes are parsed accidently if (trim($in) == '') { return false; } $this->parsed_items[$bbcode]++; return true; } /** * Transform some characters in valid bbcodes */ function bbcode_specialchars($text) { $str_from = array('<', '>', '[', ']', '.', ':'); $str_to = array('<', '>', '[', ']', '.', ':'); return str_replace($str_from, $str_to, $text); } /** * Parse size tag */ function bbcode_size($stx, $in) { global $user, $config; if (!$this->check_bbcode('size', $in)) { return $in; } if ($config['max_' . $this->mode . '_font_size'] && $config['max_' . $this->mode . '_font_size'] < $stx) { $this->warn_msg[] = $user->lang('MAX_FONT_SIZE_EXCEEDED', (int) $config['max_' . $this->mode . '_font_size']); return '[size=' . $stx . ']' . $in . '[/size]'; } // Do not allow size=0 if ($stx <= 0) { return '[size=' . $stx . ']' . $in . '[/size]'; } return '[size=' . $stx . ':' . $this->bbcode_uid . ']' . $in . '[/size:' . $this->bbcode_uid . ']'; } /** * Parse color tag */ function bbcode_color($stx, $in) { if (!$this->check_bbcode('color', $in)) { return $in; } return '[color=' . $stx . ':' . $this->bbcode_uid . ']' . $in . '[/color:' . $this->bbcode_uid . ']'; } /** * Parse u tag */ function bbcode_underline($in) { if (!$this->check_bbcode('u', $in)) { return $in; } return '[u:' . $this->bbcode_uid . ']' . $in . '[/u:' . $this->bbcode_uid . ']'; } /** * Parse b tag */ function bbcode_strong($in) { if (!$this->check_bbcode('b', $in)) { return $in; } return '[b:' . $this->bbcode_uid . ']' . $in . '[/b:' . $this->bbcode_uid . ']'; } /** * Parse i tag */ function bbcode_italic($in) { if (!$this->check_bbcode('i', $in)) { return $in; } return '[i:' . $this->bbcode_uid . ']' . $in . '[/i:' . $this->bbcode_uid . ']'; } /** * Parse img tag */ function bbcode_img($in) { global $user, $config; if (!$this->check_bbcode('img', $in)) { return $in; } $in = trim($in); $error = false; $in = str_replace(' ', '%20', $in); // Checking urls if (!preg_match('#^' . get_preg_expression('url') . '$#i', $in) && !preg_match('#^' . get_preg_expression('www_url') . '$#i', $in)) { return '[img]' . $in . '[/img]'; } // Try to cope with a common user error... not specifying a protocol but only a subdomain if (!preg_match('#^[a-z0-9]+://#i', $in)) { $in = 'http://' . $in; } if ($config['max_' . $this->mode . '_img_height'] || $config['max_' . $this->mode . '_img_width']) { $stats = @getimagesize(htmlspecialchars_decode($in)); if ($stats === false) { $error = true; $this->warn_msg[] = $user->lang['UNABLE_GET_IMAGE_SIZE']; } else { if ($config['max_' . $this->mode . '_img_height'] && $config['max_' . $this->mode . '_img_height'] < $stats[1]) { $error = true; $this->warn_msg[] = $user->lang('MAX_IMG_HEIGHT_EXCEEDED', (int) $config['max_' . $this->mode . '_img_height']); } if ($config['max_' . $this->mode . '_img_width'] && $config['max_' . $this->mode . '_img_width'] < $stats[0]) { $error = true; $this->warn_msg[] = $user->lang('MAX_IMG_WIDTH_EXCEEDED', (int) $config['max_' . $this->mode . '_img_width']); } } } if ($error || $this->path_in_domain($in)) { return '[img]' . $in . '[/img]'; } return '[img:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($in) . '[/img:' . $this->bbcode_uid . ']'; } /** * Parse flash tag */ function bbcode_flash($width, $height, $in) { global $user, $config; if (!$this->check_bbcode('flash', $in)) { return $in; } $in = trim($in); $error = false; // Do not allow 0-sizes generally being entered if ($width <= 0 || $height <= 0) { return '[flash=' . $width . ',' . $height . ']' . $in . '[/flash]'; } $in = str_replace(' ', '%20', $in); // Make sure $in is a URL. if (!preg_match('#^' . get_preg_expression('url') . '$#i', $in) && !preg_match('#^' . get_preg_expression('www_url') . '$#i', $in)) { return '[flash=' . $width . ',' . $height . ']' . $in . '[/flash]'; } // Apply the same size checks on flash files as on images if ($config['max_' . $this->mode . '_img_height'] || $config['max_' . $this->mode . '_img_width']) { if ($config['max_' . $this->mode . '_img_height'] && $config['max_' . $this->mode . '_img_height'] < $height) { $error = true; $this->warn_msg[] = $user->lang('MAX_FLASH_HEIGHT_EXCEEDED', (int) $config['max_' . $this->mode . '_img_height']); } if ($config['max_' . $this->mode . '_img_width'] && $config['max_' . $this->mode . '_img_width'] < $width) { $error = true; $this->warn_msg[] = $user->lang('MAX_FLASH_WIDTH_EXCEEDED', (int) $config['max_' . $this->mode . '_img_width']); } } if ($error || $this->path_in_domain($in)) { return '[flash=' . $width . ',' . $height . ']' . $in . '[/flash]'; } return '[flash=' . $width . ',' . $height . ':' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($in) . '[/flash:' . $this->bbcode_uid . ']'; } /** * Parse inline attachments [ia] */ function bbcode_attachment($stx, $in) { if (!$this->check_bbcode('attachment', $in)) { return $in; } return '[attachment=' . $stx . ':' . $this->bbcode_uid . ']' . trim($in) . '[/attachment:' . $this->bbcode_uid . ']'; } /** * Parse code text from code tag * @access private */ function bbcode_parse_code($stx, &$code) { switch (strtolower($stx)) { case 'php': $remove_tags = false; $str_from = array('<', '>', '[', ']', '.', ':', ':'); $str_to = array('<', '>', '[', ']', '.', ':', ':'); $code = str_replace($str_from, $str_to, $code); if (!preg_match('/\<\?.*?\?\>/is', $code)) { $remove_tags = true; $code = ""; } $conf = array('highlight.bg', 'highlight.comment', 'highlight.default', 'highlight.html', 'highlight.keyword', 'highlight.string'); foreach ($conf as $ini_var) { @ini_set($ini_var, str_replace('highlight.', 'syntax', $ini_var)); } // Because highlight_string is specialcharing the text (but we already did this before), we have to reverse this in order to get correct results $code = htmlspecialchars_decode($code); $code = highlight_string($code, true); $str_from = array('', '', '','[', ']', '.', ':'); $str_to = array('', '', '', '[', ']', '.', ':'); if ($remove_tags) { $str_from[] = '<?php '; $str_to[] = ''; $str_from[] = '<?php '; $str_to[] = ''; } $code = str_replace($str_from, $str_to, $code); $code = preg_replace('#^()\n?(.*?)\n?()$#is', '$1$2$3', $code); if ($remove_tags) { $code = preg_replace('#()?\?>()#', '$1 $2', $code); } $code = preg_replace('#^(.*)#s', '$2', $code); $code = preg_replace('#(?:\s++| )*+$#u', '', $code); // remove newline at the end if (!empty($code) && substr($code, -1) == "\n") { $code = substr($code, 0, -1); } return "[code=$stx:" . $this->bbcode_uid . ']' . $code . '[/code:' . $this->bbcode_uid . ']'; break; default: return '[code:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($code) . '[/code:' . $this->bbcode_uid . ']'; break; } } /** * Parse code tag * Expects the argument to start right after the opening [code] tag and to end with [/code] */ function bbcode_code($stx, $in) { if (!$this->check_bbcode('code', $in)) { return $in; } // We remove the hardcoded elements from the code block here because it is not used in code blocks // Having it here saves us one preg_replace per message containing [code] blocks // Additionally, magic url parsing should go after parsing bbcodes, but for safety those are stripped out too... $htm_match = get_preg_expression('bbcode_htm'); unset($htm_match[4], $htm_match[5]); $htm_replace = array('\1', '\1', '\2', '\1'); $out = $code_block = ''; $open = 1; while ($in) { // Determine position and tag length of next code block preg_match('#(.*?)(\[code(?:=([a-z]+))?\])(.+)#is', $in, $buffer); $pos = (isset($buffer[1])) ? strlen($buffer[1]) : false; $tag_length = (isset($buffer[2])) ? strlen($buffer[2]) : false; // Determine position of ending code tag $pos2 = stripos($in, '[/code]'); // Which is the next block, ending code or code block if ($pos !== false && $pos < $pos2) { // Open new block if (!$open) { $out .= substr($in, 0, $pos); $in = substr($in, $pos); $stx = (isset($buffer[3])) ? $buffer[3] : ''; $code_block = ''; } else { // Already opened block, just append to the current block $code_block .= substr($in, 0, $pos) . ((isset($buffer[2])) ? $buffer[2] : ''); $in = substr($in, $pos); } $in = substr($in, $tag_length); $open++; } else { // Close the block if ($open == 1) { $code_block .= substr($in, 0, $pos2); $code_block = preg_replace($htm_match, $htm_replace, $code_block); // Parse this code block $out .= $this->bbcode_parse_code($stx, $code_block); $code_block = ''; $open--; } else if ($open) { // Close one open tag... add to the current code block $code_block .= substr($in, 0, $pos2 + 7); $open--; } else { // end code without opening code... will be always outside code block $out .= substr($in, 0, $pos2 + 7); } $in = substr($in, $pos2 + 7); } } // if now $code_block has contents we need to parse the remaining code while removing the last closing tag to match up. if ($code_block) { $code_block = substr($code_block, 0, -7); $code_block = preg_replace($htm_match, $htm_replace, $code_block); $out .= $this->bbcode_parse_code($stx, $code_block); } return $out; } /** * Parse list bbcode * Expects the argument to start with a tag */ function bbcode_parse_list($in) { if (!$this->check_bbcode('list', $in)) { return $in; } // $tok holds characters to stop at. Since the string starts with a '[' we'll get everything up to the first ']' which should be the opening [list] tag $tok = ']'; $out = '['; // First character is [ $in = substr($in, 1); $list_end_tags = $item_end_tags = array(); do { $pos = strlen($in); for ($i = 0, $tok_len = strlen($tok); $i < $tok_len; ++$i) { $tmp_pos = strpos($in, $tok[$i]); if ($tmp_pos !== false && $tmp_pos < $pos) { $pos = $tmp_pos; } } $buffer = substr($in, 0, $pos); $tok = $in[$pos]; $in = substr($in, $pos + 1); if ($tok == ']') { // if $tok is ']' the buffer holds a tag if (strtolower($buffer) == '/list' && sizeof($list_end_tags)) { // valid [/list] tag, check nesting so that we don't hit false positives if (sizeof($item_end_tags) && sizeof($item_end_tags) >= sizeof($list_end_tags)) { // current li tag has not been closed $out = preg_replace('/\n?\[$/', '[', $out) . array_pop($item_end_tags) . ']['; } $out .= array_pop($list_end_tags) . ']'; $tok = '['; } else if (preg_match('#^list(=[0-9a-z]+)?$#i', $buffer, $m)) { // sub-list, add a closing tag if (empty($m[1]) || preg_match('/^=(?:disc|square|circle)$/i', $m[1])) { array_push($list_end_tags, '/list:u:' . $this->bbcode_uid); } else { array_push($list_end_tags, '/list:o:' . $this->bbcode_uid); } $out .= 'list' . substr($buffer, 4) . ':' . $this->bbcode_uid . ']'; $tok = '['; } else { if (($buffer == '*' || substr($buffer, -2) == '[*') && sizeof($list_end_tags)) { // the buffer holds a bullet tag and we have a [list] tag open if (sizeof($item_end_tags) >= sizeof($list_end_tags)) { if (substr($buffer, -2) == '[*') { $out .= substr($buffer, 0, -2) . '['; } // current li tag has not been closed if (preg_match('/\n\[$/', $out, $m)) { $out = preg_replace('/\n\[$/', '[', $out); $buffer = array_pop($item_end_tags) . "]\n[*:" . $this->bbcode_uid; } else { $buffer = array_pop($item_end_tags) . '][*:' . $this->bbcode_uid; } } else { $buffer = '*:' . $this->bbcode_uid; } $item_end_tags[] = '/*:m:' . $this->bbcode_uid; } else if ($buffer == '/*') { array_pop($item_end_tags); $buffer = '/*:' . $this->bbcode_uid; } $out .= $buffer . $tok; $tok = '[]'; } } else { // Not within a tag, just add buffer to the return string $out .= $buffer . $tok; $tok = ($tok == '[') ? ']' : '[]'; } } while ($in); // do we have some tags open? close them now if (sizeof($item_end_tags)) { $out .= '[' . implode('][', $item_end_tags) . ']'; } if (sizeof($list_end_tags)) { $out .= '[' . implode('][', $list_end_tags) . ']'; } return $out; } /** * Parse quote bbcode * Expects the argument to start with a tag */ function bbcode_quote($in) { global $config, $user; $in = str_replace("\r\n", "\n", str_replace('\"', '"', trim($in))); if (!$in) { return ''; } // To let the parser not catch tokens within quote_username quotes we encode them before we start this... $in = preg_replace('#quote="(.*?)"\]#ie', "'quote="' . str_replace(array('[', ']', '\\\"'), array('[', ']', '\"'), '\$1') . '"]'", $in); $tok = ']'; $out = '['; $in = substr($in, 1); $close_tags = $error_ary = array(); $buffer = ''; do { $pos = strlen($in); for ($i = 0, $tok_len = strlen($tok); $i < $tok_len; ++$i) { $tmp_pos = strpos($in, $tok[$i]); if ($tmp_pos !== false && $tmp_pos < $pos) { $pos = $tmp_pos; } } $buffer .= substr($in, 0, $pos); $tok = $in[$pos]; $in = substr($in, $pos + 1); if ($tok == ']') { if (strtolower($buffer) == '/quote' && sizeof($close_tags) && substr($out, -1, 1) == '[') { // we have found a closing tag $out .= array_pop($close_tags) . ']'; $tok = '['; $buffer = ''; /* Add space at the end of the closing tag if not happened before to allow following urls/smilies to be parsed correctly * Do not try to think for the user. :/ Do not parse urls/smilies if there is no space - is the same as with other bbcodes too. * Also, we won't have any spaces within $in anyway, only adding up spaces -> #10982 if (!$in || $in[0] !== ' ') { $out .= ' '; }*/ } else if (preg_match('#^quote(?:="(.*?)")?$#is', $buffer, $m) && substr($out, -1, 1) == '[') { $this->parsed_items['quote']++; // the buffer holds a valid opening tag if ($config['max_quote_depth'] && sizeof($close_tags) >= $config['max_quote_depth']) { // there are too many nested quotes $error_ary['quote_depth'] = $user->lang('QUOTE_DEPTH_EXCEEDED', (int) $config['max_quote_depth']); $out .= $buffer . $tok; $tok = '[]'; $buffer = ''; continue; } array_push($close_tags, '/quote:' . $this->bbcode_uid); if (isset($m[1]) && $m[1]) { $username = str_replace(array('[', ']'), array('[', ']'), $m[1]); $username = preg_replace('#\[(?!b|i|u|color|url|email|/b|/i|/u|/color|/url|/email)#iU', '[$1', $username); $end_tags = array(); $error = false; preg_match_all('#\[((?:/)?(?:[a-z]+))#i', $username, $tags); foreach ($tags[1] as $tag) { if ($tag[0] != '/') { $end_tags[] = '/' . $tag; } else { $end_tag = array_pop($end_tags); $error = ($end_tag != $tag) ? true : false; } } if ($error) { $username = $m[1]; } $out .= 'quote="' . $username . '":' . $this->bbcode_uid . ']'; } else { $out .= 'quote:' . $this->bbcode_uid . ']'; } $tok = '['; $buffer = ''; } else if (preg_match('#^quote="(.*?)#is', $buffer, $m)) { // the buffer holds an invalid opening tag $buffer .= ']'; } else { $out .= $buffer . $tok; $tok = '[]'; $buffer = ''; } } else { /** * Old quote code working fine, but having errors listed in bug #3572 * * $out .= $buffer . $tok; * $tok = ($tok == '[') ? ']' : '[]'; * $buffer = ''; */ $out .= $buffer . $tok; if ($tok == '[') { // Search the text for the next tok... if an ending quote comes first, then change tok to [] $pos1 = stripos($in, '[/quote'); // If the token ] comes first, we change it to ] $pos2 = strpos($in, ']'); // If the token [ comes first, we change it to [ $pos3 = strpos($in, '['); if ($pos1 !== false && ($pos2 === false || $pos1 < $pos2) && ($pos3 === false || $pos1 < $pos3)) { $tok = '[]'; } else if ($pos3 !== false && ($pos2 === false || $pos3 < $pos2)) { $tok = '['; } else { $tok = ']'; } } else { $tok = '[]'; } $buffer = ''; } } while ($in); $out .= $buffer; if (sizeof($close_tags)) { $out .= '[' . implode('][', $close_tags) . ']'; } foreach ($error_ary as $error_msg) { $this->warn_msg[] = $error_msg; } return $out; } /** * Validate email */ function validate_email($var1, $var2) { $var1 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var1))); $var2 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var2))); $txt = $var2; $email = ($var1) ? $var1 : $var2; $validated = true; if (!preg_match('/^' . get_preg_expression('email') . '$/i', $email)) { $validated = false; } if (!$validated) { return '[email' . (($var1) ? "=$var1" : '') . ']' . $var2 . '[/email]'; } $this->parsed_items['email']++; if ($var1) { $retval = '[email=' . $this->bbcode_specialchars($email) . ':' . $this->bbcode_uid . ']' . $txt . '[/email:' . $this->bbcode_uid . ']'; } else { $retval = '[email:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($email) . '[/email:' . $this->bbcode_uid . ']'; } return $retval; } /** * Validate url * * @param string $var1 optional url parameter for url bbcode: [url(=$var1)]$var2[/url] * @param string $var2 url bbcode content: [url(=$var1)]$var2[/url] */ function validate_url($var1, $var2) { global $config; $var1 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var1))); $var2 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var2))); $url = ($var1) ? $var1 : $var2; if ($var1 && !$var2) { $var2 = $var1; } if (!$url) { return '[url' . (($var1) ? '=' . $var1 : '') . ']' . $var2 . '[/url]'; } $valid = false; $url = str_replace(' ', '%20', $url); // Checking urls if (preg_match('#^' . get_preg_expression('url') . '$#i', $url) || preg_match('#^' . get_preg_expression('www_url') . '$#i', $url) || preg_match('#^' . preg_quote(generate_board_url(), '#') . get_preg_expression('relative_url') . '$#i', $url)) { $valid = true; } if ($valid) { $this->parsed_items['url']++; // if there is no scheme, then add http schema if (!preg_match('#^[a-z][a-z\d+\-.]*:/{2}#i', $url)) { $url = 'http://' . $url; } // Is this a link to somewhere inside this board? If so then remove the session id from the url if (strpos($url, generate_board_url()) !== false && strpos($url, 'sid=') !== false) { $url = preg_replace('/(&|\?)sid=[0-9a-f]{32}&/', '\1', $url); $url = preg_replace('/(&|\?)sid=[0-9a-f]{32}$/', '', $url); $url = append_sid($url); } return ($var1) ? '[url=' . $this->bbcode_specialchars($url) . ':' . $this->bbcode_uid . ']' . $var2 . '[/url:' . $this->bbcode_uid . ']' : '[url:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($url) . '[/url:' . $this->bbcode_uid . ']'; } return '[url' . (($var1) ? '=' . $var1 : '') . ']' . $var2 . '[/url]'; } /** * Check if url is pointing to this domain/script_path/php-file * * @param string $url the url to check * @return true if the url is pointing to this domain/script_path/php-file, false if not * * @access private */ function path_in_domain($url) { global $config, $phpEx, $user; if ($config['force_server_vars']) { $check_path = $config['script_path']; } else { $check_path = ($user->page['root_script_path'] != '/') ? substr($user->page['root_script_path'], 0, -1) : '/'; } // Is the user trying to link to a php file in this domain and script path? if (strpos($url, ".{$phpEx}") !== false && strpos($url, $check_path) !== false) { $server_name = $user->host; // Forcing server vars is the only way to specify/override the protocol if ($config['force_server_vars'] || !$server_name) { $server_name = $config['server_name']; } // Check again in correct order... $pos_ext = strpos($url, ".{$phpEx}"); $pos_path = strpos($url, $check_path); $pos_domain = strpos($url, $server_name); if ($pos_domain !== false && $pos_path >= $pos_domain && $pos_ext >= $pos_path) { // Ok, actually we allow linking to some files (this may be able to be extended in some way later...) if (strpos($url, '/' . $check_path . '/download/file.' . $phpEx) !== 0) { return false; } return true; } } return false; } } /** * Main message parser for posting, pm, etc. takes raw message * and parses it for attachments, bbcode and smilies * @package phpBB3 */ class parse_message extends bbcode_firstpass { var $attachment_data = array(); var $filename_data = array(); // Helps ironing out user error var $message_status = ''; var $allow_img_bbcode = true; var $allow_flash_bbcode = true; var $allow_quote_bbcode = true; var $allow_url_bbcode = true; var $mode; /** * Init - give message here or manually */ function parse_message($message = '') { // Init BBCode UID $this->bbcode_uid = substr(base_convert(unique_id(), 16, 36), 0, BBCODE_UID_LEN); $this->message = $message; } /** * Parse Message */ function parse($allow_bbcode, $allow_magic_url, $allow_smilies, $allow_img_bbcode = true, $allow_flash_bbcode = true, $allow_quote_bbcode = true, $allow_url_bbcode = true, $update_this_message = true, $mode = 'post') { global $config, $db, $user; $this->mode = $mode; foreach (array('chars', 'smilies', 'urls', 'font_size', 'img_height', 'img_width') as $key) { if (!isset($config['max_' . $mode . '_' . $key])) { $config['max_' . $mode . '_' . $key] = 0; } } $this->allow_img_bbcode = $allow_img_bbcode; $this->allow_flash_bbcode = $allow_flash_bbcode; $this->allow_quote_bbcode = $allow_quote_bbcode; $this->allow_url_bbcode = $allow_url_bbcode; // If false, then $this->message won't be altered, the text will be returned instead. if (!$update_this_message) { $tmp_message = $this->message; $return_message = &$this->message; } if ($this->message_status == 'display') { $this->decode_message(); } // Do some general 'cleanup' first before processing message, // e.g. remove excessive newlines(?), smilies(?) $match = array('#(script|about|applet|activex|chrome):#i'); $replace = array("\\1:"); $this->message = preg_replace($match, $replace, trim($this->message)); // Store message length... $message_length = ($mode == 'post') ? utf8_strlen($this->message) : utf8_strlen(preg_replace('#\[\/?[a-z\*\+\-]+(=[\S]+)?\]#ius', ' ', $this->message)); // Maximum message length check. 0 disables this check completely. if ((int) $config['max_' . $mode . '_chars'] > 0 && $message_length > (int) $config['max_' . $mode . '_chars']) { $this->warn_msg[] = $user->lang('TOO_MANY_CHARS_' . strtoupper($mode), $message_length, (int) $config['max_' . $mode . '_chars']); return (!$update_this_message) ? $return_message : $this->warn_msg; } // Minimum message length check for post only if ($mode === 'post') { if (!$message_length || $message_length < (int) $config['min_post_chars']) { $this->warn_msg[] = (!$message_length) ? $user->lang['TOO_FEW_CHARS'] : $user->lang('TOO_FEW_CHARS_LIMIT', $message_length, (int) $config['min_post_chars']); return (!$update_this_message) ? $return_message : $this->warn_msg; } } // Prepare BBcode (just prepares some tags for better parsing) if ($allow_bbcode && strpos($this->message, '[') !== false) { $this->bbcode_init(); $disallow = array('img', 'flash', 'quote', 'url'); foreach ($disallow as $bool) { if (!${'allow_' . $bool . '_bbcode'}) { $this->bbcodes[$bool]['disabled'] = true; } } $this->prepare_bbcodes(); } // Parse smilies if ($allow_smilies) { $this->smilies($config['max_' . $mode . '_smilies']); } $num_urls = 0; // Parse BBCode if ($allow_bbcode && strpos($this->message, '[') !== false) { $this->parse_bbcode(); $num_urls += $this->parsed_items['url']; } // Parse URL's if ($allow_magic_url) { $this->magic_url(generate_board_url()); if ($config['max_' . $mode . '_urls']) { $num_urls += preg_match_all('#\' . $row['code'] . ''; } $db->sql_freeresult($result); } if (sizeof($match)) { if ($max_smilies) { // 'u' modifier has been added to correctly parse smilies within unicode strings // For details: http://tracker.phpbb.com/browse/PHPBB3-10117 $num_matches = preg_match_all('#(?<=^|[\n .])(?:' . implode('|', $match) . ')(?![^<>]*>)#u', $this->message, $matches); unset($matches); if ($num_matches !== false && $num_matches > $max_smilies) { $this->warn_msg[] = sprintf($user->lang['TOO_MANY_SMILIES'], $max_smilies); return; } } // Make sure the delimiter # is added in front and at the end of every element within $match // 'u' modifier has been added to correctly parse smilies within unicode strings // For details: http://tracker.phpbb.com/browse/PHPBB3-10117 $this->message = trim(preg_replace(explode(chr(0), '#(?<=^|[\n .])' . implode('(?![^<>]*>)#u' . chr(0) . '#(?<=^|[\n .])', $match) . '(?![^<>]*>)#u'), $replace, $this->message)); } } /** * Parse Attachments */ function parse_attachments($form_name, $mode, $forum_id, $submit, $preview, $refresh, $is_message = false) { global $config, $auth, $user, $phpbb_root_path, $phpEx, $db, $request; $error = array(); $num_attachments = sizeof($this->attachment_data); $this->filename_data['filecomment'] = utf8_normalize_nfc(request_var('filecomment', '', true)); $upload = $request->file($form_name); $upload_file = (!empty($upload) && $upload['name'] !== 'none' && trim($upload['name'])); $add_file = (isset($_POST['add_file'])) ? true : false; $delete_file = (isset($_POST['delete_file'])) ? true : false; // First of all adjust comments if changed $actual_comment_list = utf8_normalize_nfc(request_var('comment_list', array(''), true)); foreach ($actual_comment_list as $comment_key => $comment) { if (!isset($this->attachment_data[$comment_key])) { continue; } if ($this->attachment_data[$comment_key]['attach_comment'] != $actual_comment_list[$comment_key]) { $this->attachment_data[$comment_key]['attach_comment'] = $actual_comment_list[$comment_key]; } } $cfg = array(); $cfg['max_attachments'] = ($is_message) ? $config['max_attachments_pm'] : $config['max_attachments']; $forum_id = ($is_message) ? 0 : $forum_id; if ($submit && in_array($mode, array('post', 'reply', 'quote', 'edit')) && $upload_file) { if ($num_attachments < $cfg['max_attachments'] || $auth->acl_get('a_') || $auth->acl_get('m_', $forum_id)) { $filedata = upload_attachment($form_name, $forum_id, false, '', $is_message); $error = $filedata['error']; if ($filedata['post_attach'] && !sizeof($error)) { $sql_ary = array( 'physical_filename' => $filedata['physical_filename'], 'attach_comment' => $this->filename_data['filecomment'], 'real_filename' => $filedata['real_filename'], 'extension' => $filedata['extension'], 'mimetype' => $filedata['mimetype'], 'filesize' => $filedata['filesize'], 'filetime' => $filedata['filetime'], 'thumbnail' => $filedata['thumbnail'], 'is_orphan' => 1, 'in_message' => ($is_message) ? 1 : 0, 'poster_id' => $user->data['user_id'], ); $db->sql_query('INSERT INTO ' . ATTACHMENTS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary)); $new_entry = array( 'attach_id' => $db->sql_nextid(), 'is_orphan' => 1, 'real_filename' => $filedata['real_filename'], 'attach_comment'=> $this->filename_data['filecomment'], ); $this->attachment_data = array_merge(array(0 => $new_entry), $this->attachment_data); $this->message = preg_replace('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#e', "'[attachment='.(\\1 + 1).']\\2[/attachment]'", $this->message); $this->filename_data['filecomment'] = ''; // This Variable is set to false here, because Attachments are entered into the // Database in two modes, one if the id_list is 0 and the second one if post_attach is true // Since post_attach is automatically switched to true if an Attachment got added to the filesystem, // but we are assigning an id of 0 here, we have to reset the post_attach variable to false. // // This is very relevant, because it could happen that the post got not submitted, but we do not // know this circumstance here. We could be at the posting page or we could be redirected to the entered // post. :) $filedata['post_attach'] = false; } } else { $error[] = $user->lang('TOO_MANY_ATTACHMENTS', (int) $cfg['max_attachments']); } } if ($preview || $refresh || sizeof($error)) { // Perform actions on temporary attachments if ($delete_file) { include_once($phpbb_root_path . 'includes/functions_admin.' . $phpEx); $index = array_keys(request_var('delete_file', array(0 => 0))); $index = (!empty($index)) ? $index[0] : false; if ($index !== false && !empty($this->attachment_data[$index])) { // delete selected attachment if ($this->attachment_data[$index]['is_orphan']) { $sql = 'SELECT attach_id, physical_filename, thumbnail FROM ' . ATTACHMENTS_TABLE . ' WHERE attach_id = ' . (int) $this->attachment_data[$index]['attach_id'] . ' AND is_orphan = 1 AND poster_id = ' . $user->data['user_id']; $result = $db->sql_query($sql); $row = $db->sql_fetchrow($result); $db->sql_freeresult($result); if ($row) { phpbb_unlink($row['physical_filename'], 'file'); if ($row['thumbnail']) { phpbb_unlink($row['physical_filename'], 'thumbnail'); } $db->sql_query('DELETE FROM ' . ATTACHMENTS_TABLE . ' WHERE attach_id = ' . (int) $this->attachment_data[$index]['attach_id']); } } else { delete_attachments('attach', array(intval($this->attachment_data[$index]['attach_id']))); } unset($this->attachment_data[$index]); $this->message = preg_replace('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#e', "(\\1 == \$index) ? '' : ((\\1 > \$index) ? '[attachment=' . (\\1 - 1) . ']\\2[/attachment]' : '\\0')", $this->message); // Reindex Array $this->attachment_data = array_values($this->attachment_data); } } else if (($add_file || $preview) && $upload_file) { if ($num_attachments < $cfg['max_attachments'] || $auth->acl_gets('m_', 'a_', $forum_id)) { $filedata = upload_attachment($form_name, $forum_id, false, '', $is_message); $error = array_merge($error, $filedata['error']); if (!sizeof($error)) { $sql_ary = array( 'physical_filename' => $filedata['physical_filename'], 'attach_comment' => $this->filename_data['filecomment'], 'real_filename' => $filedata['real_filename'], 'extension' => $filedata['extension'], 'mimetype' => $filedata['mimetype'], 'filesize' => $filedata['filesize'], 'filetime' => $filedata['filetime'], 'thumbnail' => $filedata['thumbnail'], 'is_orphan' => 1, 'in_message' => ($is_message) ? 1 : 0, 'poster_id' => $user->data['user_id'], ); $db->sql_query('INSERT INTO ' . ATTACHMENTS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary)); $new_entry = array( 'attach_id' => $db->sql_nextid(), 'is_orphan' => 1, 'real_filename' => $filedata['real_filename'], 'attach_comment'=> $this->filename_data['filecomment'], ); $this->attachment_data = array_merge(array(0 => $new_entry), $this->attachment_data); $this->message = preg_replace('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#e', "'[attachment='.(\\1 + 1).']\\2[/attachment]'", $this->message); $this->filename_data['filecomment'] = ''; } } else { $error[] = $user->lang('TOO_MANY_ATTACHMENTS', (int) $cfg['max_attachments']); } } } foreach ($error as $error_msg) { $this->warn_msg[] = $error_msg; } } /** * Get Attachment Data */ function get_submitted_attachment_data($check_user_id = false) { global $user, $db, $phpbb_root_path, $phpEx, $config; global $request; $this->filename_data['filecomment'] = utf8_normalize_nfc(request_var('filecomment', '', true)); $attachment_data = $request->variable('attachment_data', array(0 => array('' => '')), true, \phpbb\request\request_interface::POST); $this->attachment_data = array(); $check_user_id = ($check_user_id === false) ? $user->data['user_id'] : $check_user_id; if (!sizeof($attachment_data)) { return; } $not_orphan = $orphan = array(); foreach ($attachment_data as $pos => $var_ary) { if ($var_ary['is_orphan']) { $orphan[(int) $var_ary['attach_id']] = $pos; } else { $not_orphan[(int) $var_ary['attach_id']] = $pos; } } // Regenerate already posted attachments if (sizeof($not_orphan)) { // Get the attachment data, based on the poster id... $sql = 'SELECT attach_id, is_orphan, real_filename, attach_comment FROM ' . ATTACHMENTS_TABLE . ' WHERE ' . $db->sql_in_set('attach_id', array_keys($not_orphan)) . ' AND poster_id = ' . $check_user_id; $result = $db->sql_query($sql); while ($row = $db->sql_fetchrow($result)) { $pos = $not_orphan[$row['attach_id']]; $this->attachment_data[$pos] = $row; $this->attachment_data[$pos]['attach_comment'] = $attachment_data[$pos]['attach_comment']; unset($not_orphan[$row['attach_id']]); } $db->sql_freeresult($result); } if (sizeof($not_orphan)) { trigger_error('NO_ACCESS_ATTACHMENT', E_USER_ERROR); } // Regenerate newly uploaded attachments if (sizeof($orphan)) { $sql = 'SELECT attach_id, is_orphan, real_filename, attach_comment FROM ' . ATTACHMENTS_TABLE . ' WHERE ' . $db->sql_in_set('attach_id', array_keys($orphan)) . ' AND poster_id = ' . $user->data['user_id'] . ' AND is_orphan = 1'; $result = $db->sql_query($sql); while ($row = $db->sql_fetchrow($result)) { $pos = $orphan[$row['attach_id']]; $this->attachment_data[$pos] = $row; $this->attachment_data[$pos]['attach_comment'] = $attachment_data[$pos]['attach_comment']; unset($orphan[$row['attach_id']]); } $db->sql_freeresult($result); } if (sizeof($orphan)) { trigger_error('NO_ACCESS_ATTACHMENT', E_USER_ERROR); } ksort($this->attachment_data); } /** * Parse Poll */ function parse_poll(&$poll) { global $auth, $user, $config; $poll_max_options = $poll['poll_max_options']; // Parse Poll Option text ;) $tmp_message = $this->message; $this->message = $poll['poll_option_text']; $bbcode_bitfield = $this->bbcode_bitfield; $poll['poll_option_text'] = $this->parse($poll['enable_bbcode'], ($config['allow_post_links']) ? $poll['enable_urls'] : false, $poll['enable_smilies'], $poll['img_status'], false, false, $config['allow_post_links'], false, 'poll'); $bbcode_bitfield = base64_encode(base64_decode($bbcode_bitfield) | base64_decode($this->bbcode_bitfield)); $this->message = $tmp_message; // Parse Poll Title $tmp_message = $this->message; $this->message = $poll['poll_title']; $this->bbcode_bitfield = $bbcode_bitfield; $poll['poll_options'] = explode("\n", trim($poll['poll_option_text'])); $poll['poll_options_size'] = sizeof($poll['poll_options']); if (!$poll['poll_title'] && $poll['poll_options_size']) { $this->warn_msg[] = $user->lang['NO_POLL_TITLE']; } else { if (utf8_strlen(preg_replace('#\[\/?[a-z\*\+\-]+(=[\S]+)?\]#ius', ' ', $this->message)) > 100) { $this->warn_msg[] = $user->lang['POLL_TITLE_TOO_LONG']; } $poll['poll_title'] = $this->parse($poll['enable_bbcode'], ($config['allow_post_links']) ? $poll['enable_urls'] : false, $poll['enable_smilies'], $poll['img_status'], false, false, $config['allow_post_links'], false, 'poll'); if (strlen($poll['poll_title']) > 255) { $this->warn_msg[] = $user->lang['POLL_TITLE_COMP_TOO_LONG']; } } $this->bbcode_bitfield = base64_encode(base64_decode($bbcode_bitfield) | base64_decode($this->bbcode_bitfield)); $this->message = $tmp_message; unset($tmp_message); if (sizeof($poll['poll_options']) == 1) { $this->warn_msg[] = $user->lang['TOO_FEW_POLL_OPTIONS']; } else if ($poll['poll_options_size'] > (int) $config['max_poll_options']) { $this->warn_msg[] = $user->lang['TOO_MANY_POLL_OPTIONS']; } else if ($poll_max_options > $poll['poll_options_size']) { $this->warn_msg[] = $user->lang['TOO_MANY_USER_OPTIONS']; } $poll['poll_max_options'] = ($poll['poll_max_options'] < 1) ? 1 : (($poll['poll_max_options'] > $config['max_poll_options']) ? $config['max_poll_options'] : $poll['poll_max_options']); } } os0i?Ā{j8,$SX;,`VMMBҨwȸ-yB)f )T⥷;ĄI4]*6IZQU@*,A#u[]؇G4+~q Lkbn#iݓ.d5^*`%vϋBu*ҹ+ O:v( + ꭈ۴#E;M=CH:Q k[}Em5n],#-(.>MS{b>\Zҋo<#Imn.R6+a.L녅I.ӹѷ9n^v{y;>se$Dgh&bNG4΂C~⑩A '[Ӥ&>B +|7TIZ=s0xHȬƞbEl\h=/re;rb6dBZ:.?, nd0X{9TI6y+!V \5(dz`$KٺvcqV8 Pϵ(]Tj!ONk X% ,4z~OYi.Ė*D%BY`BdE>p^v^JbypHΆ' M?<$U4<'e됐c*JQdJto\ &.rcbo#_-PCAY-ɴCB@.(Zwn:&9' 㕫m&Vh!ӍLȈĹnaM{cRxwTR蜸.8EC˭S9 ٷ[] Z6UϜ{aٯ, mm7n`R.'KX-QkSfW|-ȚdG k;OЗ3D$W(9֮c?uƜ԰ (ɸBU+Ss٩9zqIIP BRL:Bf%԰P $̗~yzwԜ z>U upA*O"Kx\_~[ZfoA°(dzB7骱 acw_X<:l3jBe1(<$<IfuvŊYbDݮ:YB/Sv5gmc`5@/'$q)D.1} cP?YgqW}Jϰ`-DO*dSwIH $5ҾCck0Iیgh6w":;eE hC7v>á,;X\':W~)^"f:9 ώZ3DkuQx-`lqK=r\Z[_qnXVJ:GLUZ93gΗo?SEmdoLnnysR0JAkcYo'ٙ|)}EOlȚ8D( Cԥd=>JqYcpI[ʹ1 7Jo[Pa.OHmAb>[eBw#²˻ 1'37 [WwG_z'Q0iz`8L_h't(UBk]!Y{9{5]BC-%#2O]tla/(l'ݶ!Io蟚FXъ rn _kV8P!#Y]Lq/Խn} M9. xJxd"9abb3;Q#yJՋDʔj/kr'Goj }>Ni*9kn@u~ ï.!?y#>^/(M+IkTeeȑ`h>EW |jp8j4ǿ}YǼ^~%J#p(i?qz-݇8\ +տ?+obex EHOT(ny5KsR[l vӌMY|acִQL;ؼ:Dԯ1J͐٠iqFJk*_)!34 &@5q&M4l̈́p`1rn^?Ó23#VR oSYă ;B(kS2(~|+3wUs"aqLZXp|%aѐG} OhJ|ȏ5_qJwEL?1,9E#(zϓ7)|5ʴi⪼R@ݦWpJd}sw:}̌!l;'r#&1Gbngez;퉃J2jVd`QߚWvH* Y2\;`<%~7/<~uz~"噦}de ӊ>-긦B9cb0`klv׽D | cZDT3^L46p#Edl(:85.݇Zg~?6gTIh$sNYSGnFZDbrȀ.Ҧ]Mso+Ammƒe')?AzkTN" Tj=v&X·/Q%L6rf6i] >i8qxVoUX̴.~6ַd:17YjƤF3A>ub=ivm~*/jXH=do_Yp6j6g'ޤ ojV$ 4gtڕ4ECV㻉Z"uˋ1)׏?>l ;= 3 Z:ABNY; lmƋ%S2+;KhɶFL_jlSVMO0i5+C.Lp3y;cWv -I6YA&<M51D:["lLf}6G+O#Cz[%Gbr_rM%k~CF"  еiOifJ&fӼhj8 Pgt|>S87wBB>u0NAr YG{|룶nM|H&T`@ܻ.S.D8a m-DdžŧFaːA躧Q]Nՠ!t.qn?Ӳ$+Ish( /MݏC'Bz_#?ץD$}"pQ~HR'e3P,AVSto].^7f"@{ȼ)ԕ&]#׿hI-_)NHa@ 1+1lXͲ!Mk8CNH"v@nSh̔F{_ڼC~7z+qݸ o/GԠbK -J'_=OL_!y*K{r^r| #6V.2y#qM> iE)]{]kwD_R~ 0Xԥh%#\B$#-g@B`ϘR^/1>13.lX^ 4J4Dt]žXN--ЧY'YW99G6 :O 1X@EHK2 y_)P49x* q{ R3koi;VlYRiw]v?U!A{͈d"*BmϒЃQ&D1b_3 }-#nKMިeX y'!JNDGknd/<@juG5`cca!"ß2B$737߼`beU $'5f@Rd"56X ڊ|_zq >㢾GKArɏ>8+\Ai~(:C4:Eڽٮ;z`$.G5CKcxgtqJPW(պō_ު?2Ty.ݍ}ܺ-pa]vE4gK SB"|1':qw$| Am*!n+)xsg"$h)uŗ,~`ܬ B x99_$;I>6{R'iUg)^t|\)pplL |cui˥;yyβ)ޅjTR.Dѧ=VJX>t`])~옿?Olw+9^;&.& $HxkHK63 w 02Gx .ď6xL#9B#HI7j >q 8(ª'%S-iIJι^L%K`5P " R6@sGKǵ8K} nجpGؘJӯ.MΐvNɈEI.=5z*8z>Ps#["9S%W LN _\U^'ӚaWm>ﶔ+YTUAlix=qo*fCsW'NƸ 3π]W cOh%pJ˰"Q9jE<5u, fSM&K~) .],}c !=GBVvLg;9ڀIF#)N}h ~ѓ3-۲u¹߯*hp4ēs}cr}@n G_rV'QT.L[M{qvfi '!nBo$"BZ"^9Ov4[ Ys1w+\!dI Bl)¾iwΣU`\Y{Nfa$M+$Cj*? kh(:i=d_|Sh,N3HP iD_V3ggMI[@4)=r^4BW;a,=kgQ~~29 j{J?mYI},0l]bp>qJJ PBV9C^i1wO+lj_^Z#^),G6){䝔}*iZmhPٮͱ%[}6 q8p/v?2iHN zhYsBMdX'b*Rb-YOM2E-c+PhGPÍ7f `x&B-Yrv6-9Cvp(r><*cK1=($p 7rx%CgFf7wq|5IZ^ݧsk t%nQjx>^m &#ޘ=}7ʴ(6iO61-"U3&>[ȗBsH. $(8Sx4X5+$@F ! oN) ooM89C\7zs uZ*o?PwH%gߙPqGM!}WhQ~[CR/EV0-8l^0]u&7 SM4N#O_u;SN|`<P4;-QUM/2ڬSZ5ÈS3\ʞSTq&t? ɨi9W~ƋNd(^~Ķ49H|n}enξUOz3OT x!19vTyIAk/CGlOX|/$D?mdBT6no,'ҨkP 3+ sLܠS[ p^- 'zw ,0Y]^}h v8.ý(c{FZU*z֭Kcu$ ?Dt:RDžq;<%(G<བྷ8k `#Q.7(ԉ 2J_yńCnx%"P_U iSB\ER6eZTfJ_ O/̬~֨J-d=dP;ϨxI*BݤQa:ɭfdsX%?Kg|/aoc]l#%b͠-J.YhSq P:DUhǩBnIrFJpF#sbFNyB!BW u_'"z{R,I4XoELAS0,p8-D 0s v=!`'0t7ud([P hg#s r-[ h4)o('9עbD!=*|U lDۢg"K 6yz[B"_*%qۂFiY@512؄{Wl %60t`VFe}!T(;VBdĮPspJf6?.}nO/xQY.(BUьb6g=: AO.L,00꣋H1nG1"-`oocj6C=b*t|X y_z`[=y2ұsľͷ*HlgGS0g02+b:]l9ۀ[2Qh,%#GԊg}lDy~)F5V R4`WT 1 K|1lz֋㾭'` T5# 7?I yO!%sɵc"~q>;4M C~´;#0ã*IOo0,k mN8IG&xQmҭB}uh|NdeMA^RY.aIhÃD( J{/3[ W92rbdrXD*ރ w'<&|Z,VszipQ*[ <;~DlP5_Q=W͵PPU"$0l-tܜЖ=| Zz `H躒? }ao]?8MBrKJJOnbs?d PLk~dP'6Y; }㗝aK8du(ȝҀ넫l|C.^!0(s[G=c_vsK0u9eߖL/.~`KJgwW!d0Ą ]K4|OQj/XCL4q7U[Gi-؀`]#$9ә%ⱾSjYZTK>SHGE,|P(sQ,7Ŷ٨ `WhHЎMwNG$}M ǨQ֨"b߽*sS\cf6oԯneJD+ajw\+21zF*]'!tD)ZDGMy^?XG5,M +zaנتIԲ'{j!zEx.k Fmi0ec^Aw&5E&J@᫛7nN?F}b$G[$5(8Zd0dKΑwx%ݻG5iGcnI i]\ؠ2(8*'A ;Y߸NXwl*W$ =O Ԃftyp;#aEJ٪QL G3 ]̸ȽbBrW0qJh9s& H{ ?'xbҊ+){ H)z|4)N$PU)%ƔLiK셝ÜB7#K@k&XdzʩvīC*W,&Y:iX(={%A'9vػJ~%5*8jB BnIq& P\Wxά\k_`qm_i?13I):te9,v+xNl$B!<|/EzD$7 _vk=f ϝϏ\RWqz7yoObT\2QAPO2 dyncP*qmqE wѢ-nrȂIF`o]20_uG^qOa3OXFU5ky"k*VC!mdAO^ō@RH1yLiG)믔 6wK\%)-O&yhówͦP5>;ykܱSavyeJ%U+>z6#^ԌB  |n"9nlu^0J1%W؅#u^oZPB6a{8]"D[7G:fqE$; lEsS=Cޗ>]Ƽr;@NasAӌCga[IF 08u˯hpϱ\\Hu >£Qwrm5_E]JOiCv`qB7cr!}DE$wJY=3 B,܄rD+oL?ZKuس!G~Hz?Szh{3,BL+f̝ͭ0/ZiW&+bpw 5HӉG< _orl}y& /H] -~^л}KVhuwǬ邎1( ӥٯ*ӵ,^MWw!E3Ltwmd/*-jgE26[x)Ewp>Wl=rR\4"qY`7F0ri9 Ysʺ:u "vOOacW3qz4N4c)YRL5)9HL5C0D21ki _׉UsFDxWJEJA"ve,^^h%0lO̊²덠a2#bSЬUwApnj>MrS^# 0Nr!ǘAt >-hHL=/;906El83 *.k4vJJ@Β4Qo8 髖,nf(+ɂ pa`XO%Hz|[K:V2]58w3Mr9f NrV6\$w !]yPvB8,\p/M DD!юR;|U)jsH[-T0|y!g:ho 7(ZpR ~+YM9T! 킷ӄfcTA%4s&/^P-ԼTf<5w\ٍR?{%;=" w<0Qr¸*yZEl4d6N{tCu 4rKWxO$ @P?vtm*TN6Cפsxk:y;"Zկ$,ґ =}1$̓6uRgJ(knHA?~ gHKVJ@E(.M^3t"EM'Wr#pK{9tƢj'nē`p]5=q@/wt .Q P@9HP&Cxk``4]͚K؜'me~zFƩoNjq8Ġ oB >S3fJBñ.H:D29W 3Ltf_FfׂkBa)+'了NځQ-Lmewr]6~+/J>&lcH!!PcԙE]rt=;CN ӄC۠zݾ䧸77]=<۳!0=s\aDF?@?E`ڃ*ߖMv@l7[vHevb/I'1֨ A Y'TKʁDgPnΠ ^} ?}|_,%{jXiA-AzѵI> a侵m>ǒ1f4AzX%4̅Jl .fU Tq!)cZEeMp4|EcF? ƦhW agTX^DxiBmYUWaW`8PmRʿ4]*_v?-E%Wܒ~ <{^&=F|Q`Ùb5-%_b"׷uxb~#_ ^'٘M\K?X}d(. fAk?԰p K_N*Xl6p5mt]mN|?,/.0RR^}Ze'?JMr$!DyDEfj٨  -[mU1%C1+ .+<cV !ټ1JKb:碔zsfۗ'/S*VE3VU\ԝz:}-Qc"_ 2Ё s2qBҢLƸvi'R;fuCq]S.m'H$H_PDIBK,W\ |f_~//T&ړHܳKYb&G @T=EFR-Dd#= Yȣ)_ϯ|1,ZmȖ'K4H#9 (t6*C m̵K7(Dt*V\NwH'aJX<5kF*v!H32I!ezq빚Ȗӑ=Sƻ.xdx"<O#Q1)zbÈ\P}Q|K2"˖9:YWu" h+HAIY!=hm0UpgźxSCꏯ$=Ul4{.PA6w*+nVWW/j M`8t rGBwBA2]҄_G\6sD-uD"fn葳!5,;n_5ĥKf H/oCri"Ǿlv^jI 3#Lt՛q \FIRZNNH OZXh!/ޭ9l9JfelTHgm Fj.%vȞmGEj0 (Z.ќH?țE.q}nsu({&4& R= *?L:ͼP=_%wQT4 pfL{J:V\ʶeV]gFNѳ{ ) V>"' Ȋdo0eaZR4rRhX|}!.|9zi":6AH @ؽEh?6%>',v Ll-6F^P'sާG6is{ްVRlOF+8^0]{$02XVbTԿM]K^aX˜ga .~fhQLn9& |W"/ u'5m]{b=9 X(5S}EYQꐚI#3o!5M)I q q3R{d(UHw6ρ;\ĨjJȊ٭{|/L$u5#*7VL2șJMsk&h k48SWKm η.7cIeYM`z!ʌL}9j"(lTIs.}(^wE#V$;XEz EXɗ:9L7O:`"wrE<,,'9ς7]fŸ:awLӌѓsY&_8p+'G49Q-U?&o$aT hjLJ O'X=-qrg A31)܁h%{ 2,h+j$IzPP~(81JVt'lJuxprZK?<w55J!aq=K;y  RxRU@T/|w։jy|$-Dy[ !*$VU>.zOz{D>AO c6G*uLw 5zz#ȗIA\%I}o~:r*A)Kۿ ܑo'dآ 3{VAЌHG "uBPB_-oB5"YN_+U(˓{~[eIihџF-7T3܌"(ăM#l` n{u~ąL^1ek,#cu쏋 I%ox ld3tG?iXX#`:<PWܪ&\m=Qh(ӖYJa7>ڜdk<"M3I.νK k(*38['w8e?qi>|Xx@k;>2#qmMgH6"8vk1S8A G()1L"+ǕRpX1 @Q7oee]9"H.*n!{Ti)^ygQFrIID<0ž#/-+a l`T7*C">.HBbqط,;Tɳ[ AhߙϿgߚQsj"a%aLcx*)oٓAux @}q(lɂ%ECgn?|Il0+ |z5VPzҕ2&I P!Aw@{ :e&Reл@ubc @oA"cϫ47+l{l0@ vo~Ӟ-⒚\K_ PRWlY?Sj$ǯp-{O SXyel7Ibn%*UNH&.a6[{[i9dx_Lh%<~2Rt:+ZԞ(Ba6Ȼ(&F|'tg; mݐ*H6'bvj$Dz0%:SRARhF_t]Y sa- zy]T+^6tڎ$ʨ0j8 DuaƎN؄PRnJo1wϪ3Ufg6-{8j<DoT4VZɔcS慻ipq3m=,=Qs(nFveH4apCXy[eKk$m:gS땝5DvMi"w]0vGZٖFV4v,aޔ ;S-|nG98>`& Jt& ,_p:)5ϔ^0f[$𹮏۞Q_9uJk{U u:Tݰ\ .5!mr~׉UӒ&m;+$1g{F#jo<8Ԭ.jφ*z<t;k)5gyE=׀W+f1<ÖWA5|yf%8f7lc~+ئ'u\SbepR>g|[ dpkLÇ|65DҪu'ȡy~mգ&F/UDVA]kďL8:zL9T졠)LrT>gmΔK5:&4w߃ 9Tf]ULDڪ[,b-ES_S+0I XiPlbp0LӃԍC7ك1Bhsn^jPZmsM碦[S$ < 4TKR'vɈ&0IIFW~s"ApZfEC/N$)B?/6^jCѕx߄A*l!t5C47_^t5F{/xRƚ=׸n5ީe$?q(02g&2P?ɞS-,YO² jZ<Aa~ _8ڴ%(|s/)b~+zc[s؞ !eŶwbLt%ۑƯxMcn _ 0ᑭ%]QQ w / =͇Kk\eYAΌWs•RveucjF?XCZTjݗՎNWi?|!$U#fglug` j Ju &SYYøScA u[ -j/ӆ`ȕ(Κ.T !zF2vS9vuB%3:b =6@kD`Ʉ0pvZ#)^W!@7ܙqU$i}<0/;r9Su& !lpԃ* -_p]ay1 7SO"]!&8ۿeiÑ[-ӉO7N MyTjxwynꪊ2RWk@)wߵr݀P/JюFz#9n1>ta(v(,0|Q{"]'ҷ*)tٕ钀"M`4 dgm=PW<;{YX#/5iJZMv +7i jϛwi.J#rY݃ ܨ{*XKeJE$hLI'J&桟MJ1 UWbƬo:>OXWjN|QJ=(=}v端.ĄMcX`^t*ˍ$M\M6Tr1W E#N OAU~ѩn#nJ{1>s˗+, 4wFrT:]a蹝*mKSo.^g"=Y奊*n@@,݌Ӑ:JVĩ7Q ^i$›ǛĐ@R {? tw~Yw z#gYLj=[଄ZG)|14KBC  t&cC`K^ˀϹ't$[a%匀)Sр &/W{D:9>ytGK9F.C!QXe<ų ӣ K~V5@,FU =j9ii,N~|'$i!7HR,aߏp]y\S#_W5.$kS؝.&Z@J!Kw:5܏\٨GY ARfCmKvVF4Ej_ #hbF_&٢6utTR \o(dQFL!߹VjG>Ϡ?dgv<ѐJٷ nb?qDQ|]x%<%Z.WhOEjףĬ|)' ՜-~ n 0ZEZjA!!dJ*~4\J|Fso%y>l1DO:Y%d^ZzB9Qnc 7k03`OsҢ \vy_."`)chOY;zSsiG@ }>ƃf.DyjK%'I@۪P&2Cdd >LFĻl'\H "iZY C£ *f\ߖ#>^ntz %OBSO32]ҴA8ɏ 7ŹYPz0T03x0ƒ!0ːgD)_ =9 Mo%<ޟ1w}mڂT]_M6 U&M ~{_ +3oeqf(lX JQm>wŃ|P/lc7DLسhͳ5zV:C> Vcl60,NiM; ꣷKB.P38׾67)E d4{UϓGM5G F?P1+IUH8~)K@-4*=Cz'Qq\]C̛C^p^:i^jg /?sᔸ.QY|Qb[7uP)fh{h[H!pȲ:tQe'9=nG+X |8SJS+qzzNl]v /xcd6%#>ozOlWHh*$ 7p|{_0t-==Ͳ_9YW.Ju6 %(Z^b =UUZ>nzLn&jK`i]98^#!v y_G̞?QhZw7 &cwL'} i"O.pc=]*pj|HwQJKi,#Ssv}diořKKʖ4aއA ;093cL "=ԍK!,Cak^i[nY _l=R^>|2Tׅu,&™=;m:^lo:aLKЅ 4ƕ{i \)?{b:_ rraAi5 <瀪wECbi%@7d&Iɨ_*V-L'e mPz`?*I⾨o @E FFl[C=!mLL<|@? eN\d\C"ҹEo _άe B}>LJd;ΘyEcU\{JѾ8?˜)P=㤃 e:׽9ƪ(7}ʒv4qB5ǤT'lzq|c5[E+X=22`pl(`Vn菩͊sOMܵedj HodB(giuHB5pVNGb=h$ް<#:NMt)&,MlΡWaLZke^ԒĩcY^\1Xږ&siS 2|mZ cu^]?U.juhKKl_z_4zsm9s΀;Y޴f *I>P󻫦8\hIwG4 # *ktYHevkM,R]OF5-$@;>6mZCv:!F.FҲ"X!fNiM/BA &~{}d^X$tިM$߀I>vZ:Ⱥ(9f&ȫ-q#R3Y5bB99+k=5s %B&uB~GN0$K6Fcܾ~3 ϭ4m0&!Wj Ts4TW(8D:* cH(vM?b; %i\C^)[ >5}J.?@v߯|Q!u략ssI"e7;juCT~@@I|Y0 #0{I}pJNbvܷsDT, *j00 7D i2yڣPsaIj)_y?D31 rVxxy,DsO-p+9g)+Jtn⛒([o@5YT엣#7=6WїFiйUQnV -!ߘINڍ@$g1w<FFtcqtXAdTl Y {9}zi;qq*>Ŗ}ALǡ`U(0fS?/\.BRM E㲐V ۽TўP6\'~s^Λ'$x`Z fg-@P'*9Ua),C[ ,E"UlN6.O:13R)4b?P&Q/X9CbKPjfT@rvvxT Hڹ%yG`> Óƭ[q=hMb`|]`ۦE}X7N ԶF@^B.kb6fqeځ%L,QݶhAft6TFEvSČW éK3b941Up nGЋ=䉔G#hR5[{}m'Ҩ\Y┋6`Uq8( I񛨌Yw¡r=^`M?յyY )ާmn{=ERp>O3k)&v|sej.DT{ce@pc+9zx(uL{"kI`g iDZ\ B ) ^IaC n{/}=#SUM5<6Z8K̀R$}^ gpM! ,p-9}B{yY Ho6[K&&_29g|V[8o3"J`Ŷ;n[ B5 Ř0ɬMƫcޖ,a5Ru檗[@6`CfQyaQ6mE(ȣ{ts]gԽlND(^]ݫPvL:(wrk2,8Ҕ[xl#W2[ͣE4Z[h4_ }_AVzt?gmHEtkXtc'XqF MlѴMFuf TC D2Rx(f<:-t'E=jWE;=™sv=%+QU쉘RM:ާ^8κ{z&+[ _5/P3I Wn j`m^҉lj4@ zX k:Z 783PœPۈr[5-k=դ{ 6GC&}/Nġr hl-3hnjG(f*-yZGw eB);O 0p4Y?5~M{*\J+~GحT^ŢAfg?lkZI΂ O(b9Eg_e)`FU-0{~nPlʉ=%kl]?:*Hv3%d$Ç`YڶJ Q]jq6 Î qZ kzuP;w_9mUcpEFFI^38+X2mn-yG/ci]?gk__d,hQ ۦS^4ϐ#:ҡl҅M9Rا5J1#7,d a]amFQ]?$2vr>оh.q}.;'إusV>bA5r3[uw$oc]eӕ_ziƯ5ҚVQ,%ϕZ.5S 2NjB0n5ky !$ +-C Lpd]=]Lr3\F4iL[Gr߹ږ+U/P.!mNj"\C Ij>b!ڜqg #JgtkV/9jT$:%B@kj\hޥ!e#@"頲0jW-[Qh=&A}4G0Ih^)-?g5kPe6>@-hφ;Q* vU'sv ?U7 (*A/]dQVM\[QI%Rh]6G\6O!ȚP$(ӫ*V$ (q _zK,JP &Ev9Z6+_c߼WM1p @iEvTg*;iŖBX hqV5m* jjA_ W #?uDU +4AKot9=:-m_s&rxBV#,1uVFԣ5$,ըsqpß*=kvBx) L\FL[ 8!^Vo!WK]Pn[MCES9q>Me`w _<Ӗu:̋hm5ȍ=Q ɦTҤ|' qVN-U C@sH1`\ni'1,ՠ$Ft&_k:OV]ыryi36Ãӟ?Y"i>WA%u%E'+3(̃+5K|Gv.9=r6wO6d!%ͺӲL8=G+X# 3Mi0tӱ^#bhiAB.`9̌e!R|[! jǖ:YUt~6 nn~A5_kden^ʹG[}:n&/?-z &3j,].M<~\rPJ7TMj^ xE5(k¸DFqSUt,0@m]!Y/%:a=[X2[CFXGYoAȸ;BDV}͙jG0:<tF\SήX HVT pѝ(:⁋{x\V( Bc2H=;eKE *~PXycPdJDU,q7Z|F)n\ 6vJ0Ʈ??lC87S%k8$+x,W3Y wK 䀥R1 䍷BFSeI"yúaROt{)֙xMGw:MvgFrhrW[ -('~ỹ5?[d dz䫲1CH?A󇀽`Oݦ0}a z Y-Mµ`exW׫7 '͔pܔ ,֍>+DƔ?@IC_vy8 +,4. eg˜:BsOޚBd GR/]ֵq+sU˺/D{\)uyC銀'񁿖zfe@ $@i/5z65OLgDuFe%Uts]?bMzV"'?Tw/|WD].8O+^+ő]7Tn40:C}*7@lK=#v`M`1w|tY{v}%(EF{ʌ:*M( '|߼ZX']p*CkSlma92#p3 (6LA !H"?q/z*mj\ $>+VeWq%^vi(%2wN!7 ɾ49 2nzZUg}h3: mTѺTuKNĞQV!ޮ5?@K4mZV}lCpNң'W%iy|uPgfbcU6Dn8DƋ16-#h B: &Ռ$Rͺp9Jj md"sc&U ;tU;2#8ff1W:91݊}]L54duydWOjD>y٬v|>jFt U}NJVvmݣ6n*Zy5|za% `ڽsia>7C(H|w}5#}6pb.Z41`6HO1 ۛZuuV m6ϰ͢NVA$́9lRț"Ȅ.9|u{ y R?8u6Kl-J[$_?4?CD5F`ât8a\J&StyP`+@"Z =o Xp# vݪ.HteÇ|`ςJZaҔjއ+F2z"7+^5"s8\>;_T8Y-W) |97q 5;JflT.QR?<$ #gQd#\ D_Gx?&4ۻa?` 6ZDJi>r߻mSɺ)(у14V~ vxWY)_)+Dm Cޑl6JrQkT3 mϰrxޮu`׻DIRxoT mg%ۣ ^+-7 S3+^E/Y߄H26yfH(gayxJIBm*_~6䢈L$î'{&l'n2rWZ2CmyBlOqmƎ%maH?y4=*14/NRF 2vv-wuQBF5;y/QDtiXQu& ."o/韟C0z<~p9XtU} HN}<0Ԩ~"%W].Tԧ:ljX>14jqw[#9\0-=rώ eF\ 3/ n uAoY*.Ӟeb'?[ ;ȴogƧ< Rau`'| cɔ2ؤ-hrmLr[]dKF! W#`fyH,6eK6o[֙y G6KY>43hCǵ<0 r}̰{WS΄ݱ ?].@T"Ku ⊜ fj 3 7Sd&H#\d;bcXOT4 _N ?A>1f,_5Jca##hQN8Y֍֚k|xWScېMqbCl4ZJOޅo?]_K!\P /QTv*qS}4j1ӱŷ5!2W?l3]C[2W?d<4ox`|eK jD҈1a^i#tS; /E.{$A ! /K3@\^&rd̨ahwx.iWHUɼBE1t)~7'vRjX nA3 75Œ@<@ Ҍ⣀ͦGN/r\+2B IN}3ó.B,W.QiqQrKCd4Eݮ{x3oFrg5 *K nZcXY}YlTWDmgl7*d߳N<^Y9^xAo"_F$_ps RÈBSNLTGI_,`07Ljy"rL|n1 Rl8"ǾiPOhSС58* qƎ)qU垳%5@!>/0 }q%V_vpKXu2K啎r/koA"jx>Y߇ǁXFm^8UtvёU GW>i;zin./ 'y.H}U ̢G2qS{ > s wZ-a8UPsؼǀ)HFGA3L{mot`#bZ4x^c(Z['XI\SdlC;G1' kT* ,A:WCLfN~M|ldnVV(GJ5z džQ5S'?ge)tt 9P7ż#Ny^.^淑6:"6\TV$'ED@>7nܷcoΣQxܟa6cjg:Q^cO.\zW&\*i\"i݋)YG7dkm+F߼N<נ?͵L 4g(< q74N`#f<uDX p=$sA@&NL:|SÚĽ_@# N%!zZU6FezNdg<'|Yk+|Ǿ!8㇕#ʟfnӆ,~y!ibYʒaa7{^<݃b|>0af32>'==aH?KqtR8G;kN*'S{@1T=='E4klS4X #=D#6֦ UJ!]sB7J'ۜS8D@m5fQh:QoR\|Dq!}.8\iӉ,HM C԰$7'|72ͼ(eM @\s^[~|6H3?GF$ʮ}砥Զwmdk\9GX GϦhG~B__`1dau\g]34]ژ#L*Lά`,~xTaN1m Ya  CAwUw~܌o g#fPnEj Q#M̅ibݍEqIS4wE[=7˗\#gOL:hzmpalP_Rrv.7411Ud)0).dOY*r9km 4~wJG#rJ\ȄL2ܨA֮w)Rv7Ic?/,FkVbm eZz7,"K`\Ax_Yu|N Dl0fB* 9Ck٪ rQg,*$[)BfE e-i37r UZEUHȸ6uFe K.ō~ iBuИ{$Yr7,2Ρ6֎%e:qq0gwL\%Ȋ"Wu18Wr؄c }_&!.5, f,rO,KRy$j]g騑Jd^1Ӽʿ@9:n@vrH/ߡTq gTtȫ!8n?ćo?۷K#T|ɀ~̛^Pw%ఎ6.yRHݸ`{BMZ^$pl$GVE^^xݲgMk9kJarXh5Z_dwE%pF98@}h?I0hdxݭ:2|!Hl"7U~T;U!t#OXE"qP?+rpyW%T֨gP^b⬥fP8~:Qç@z" ,<%qkgPH""I\aE^J$$BJrgb5ȟ/?rt\eo&@tj!"q%6||[҉P*(㶌|4VN=fG/{McDPΦ0=~'k3||5|K=5Yc3ZӠ=ѭS-<E#o6,2Hڎ /^Ư<.koIdOy^ y|GNOj1s[PG1z FwzkU"ˑJ]8XzhC#iUM ݭ J,3}uܜyoRL_Js 艹L*":L:(?aW KSlIE` `yh1V(!>l odixRA]O-^ޭa[~1qf[ed k-F-嶯5VyJ^t*{.ZhK4v(0I#Yd~<`汈[Agҭ';0kP؟ڒ2K{M8jAo:Չ9 S}XDp󵂓[|{n9]mw'-$,tsD]q k珑ǽEfd GfOE:LpA.tfr[;6)QDޕGTy`M(JlrAĭdan Ie5V lRgvxeK[fjtjCkQvrm ܷ:;,T+ Q ѥ}BR>X{{zd(~+W&[:l&dPuэRS=K>wwz˚&M Y(UdaP[稙n;t4nsnz*"^=oaMyb=1dVFrGE.@,V/56,h2wҗ@ 3י~_&tCLxY $ f[#WOG>y«4':'+;ycJZzrg\gR!қD}N[ј!fQKr^c'`^YN)ƍ2Kza3b;FO dΙY#y~VVOZK;SRcS&fڙh|n\iC-r cpa>yï J#-Q`\Fɨ74p%5b.M.tW9dfk%K]PCe*|@-ĩeqDIOC/net{ ˴ 8y]u m|Q0i8=7Iw3D..A٧8f{9Xt]\s g}v}ghto:H+8?fK![m3]0/to2kd%@7aub翯9&ãA#㶨xB:$u%u֡+ ©F\E}]K > *fE4 a( t4јv1BK0iZ:VLo}4q,ER/FzcDQP&HdKvD㳠ULp`&N4anS-dieU€)ȕWV&-]?~:uȕ}@&XgaF͌ DRH" zKI"X_iԛuf&tw>N?_2"V!SPA~j|TX-J<}t:4e<\a `24 2޲YFs^Ti,`?3֒'*&4N?^:YI\{K'Q_ԱAY5spM9]it`UP9jEeR8zY0kY&M;K? |,f 36O} x:b*5HHcq浪q$ᄫl6VEc Mrӄ/CE IDmv [MdRͮ a??˽ z,'PFOK?+kg&z\ce@"Yv=e~4Oo^,b6*1YY˷c14S-m|>D"ެj^qTFzPh'#PZEwBCz\؋v;E쪁밷%w۷uߢ`?P'1%E!U)s*;&&&h@c~w͞D; /MSI?& *{Odƍ`<X'fO&q\'ؾ4Yqxܜuh@q[3ɜd*=f/'q)L{{!I6/536=#[  2Iq$|;fC~L-d2d79^`5'ʰWȑYWqlCT6aIly0DK e(* IChs ?I&(3{Tsn(vvbG٢ݺJC8!zTO4ʻw/D`m?xX}<*cD}VS'5ò?n@N'<(k2zpƎ=5#\f~Џ\g5?x9V<r{!Ƶ-<27O)*yCc4Gd ߎC{?u 3M?b$QBAW%Lx1|*.tfO LwNᅃgb@12s8ΕM$.pE җԽ͒QAM)G: e̞@iXd (WƒAXБboa1 Q DA=f(`u\lS<- 'RM=P(Spm=me+&'1 {$rl{;'Ov{5_dh_nz8N Ѩ*Q]{մSYΐv0VIo6vtvwD- z8seO,~5G ?3bU'[fRt]_N)S`l@jT?pg)yۆyEwz Je/"i&?7/_Vz+0=H\c?5 ەM@G2 S3D%s2BC;., 0yv ٨`}h3hl$)fi=eR%x>N*%/ 1a3 {r#j/Rvb+ B+`AG:}K86Ux)*N/[d ]!IM؟*Ҝ䝋EG* kr4bсm?0]"N0л{ ϕ1_'5AǠШ99x`mtvO dY*AHcFVJp=HZ#מӄE9>$p<|)\Y>!7vI %[[kkp2ƛI60%IH680Cron.ABX/09cʇ^b}#_RTHO2)M1xK&-ޅIrFq,^i.˯#nj(qUX Hef."2~{C/Y {?W|jgKD[&i|ʀi"[!h)lA$& ZW{fzLk0ݧ u,Q햋Oezcxy(ӹC a(%] E m ءnl{c/jk4|²pZddh,-Lu>}9h͚Zqʋ[2m R[}CFr =)^p1N_!sqvÛ~쁦 Te)Amx1%4PrNr/:#{'7 K =`595>RgMS4թTS/DaE5ݜ[8AFwTOh3:iZ]p<BэpBʕ_+x jO^aϸ]\ QXjaRc90_$~]3r`!AoAie1c(eU@J`Q2XڝRy?e%զNrō8֢ɍ9;`V:mvu):Z ei|(>$oUñG] Wk¨2[E#4Zl=<$IyI4 ƄI! uMҥ5e* ]wgEo.E݊{o U:ulfEaWIaAe7}DH_=Vc*߅Q63$ "I…!*27荺r vêh%r_[knd"'"Ky 2,j Di~aBHؾg .R>*:E䙵I|* C"<8-MLr:@Xk`3 ݯhIiA9d#`NjEXcMM}{ +s|*NTdž%Z=fC/7 J;R6y~Ҭir@4?Nթǯ@V앲KgJ魲ov-BI?!&;_/Hy~r# ֣55150DKS$%Y$SΜ#=x,'&hX>ΩP/~Zդaہ(]\Dt:ЃU[K"߿*AH+ZP#H\1T_<6h::M> fy`LEKE*c;`H7WLw1k; Go;9B ?`¥G e)bx품Lز4ha%6-r zi<{Uǃbގ:hK)J.=GQ"ѢŖJ%6M4~/ش vmY/߀Vp~ T nr,t;M Sz>y U9Jqll qK+W`2.Mz9^\umρ׉{NY;rБ%l+Zۊd5c#8R$%͗xu` N;+-4‰N!Z|~_nuMv-qX:iPD}Yrd9[\5gh1= s{) Z\]("u>.;b.X̲"eaҧWvBPj!x  E1(hu;dhWBd]8ijHy9B [E`Mo/>Vh0lH*hKtP.8k箙֮G$!$[maH'>u#jHΨزcKl/`?] xLJdbG-Zo--ֳK#H3s% "]D7,X@ tMcb\s4 X=Vwx}xv:p'^v RcFv n=h`Z'dx4 \@(.kR! ,{z}|c,`|WS(3!TkT|KɈ5R@"|Z›</[(a"̃ۆ6WAJZWlh.;)6 E)@:t\Hf 7V.\Z-m!)gw^*rm⚫N5{ mj%\jǭrs^F)iaU#;Dp-|J%O&kb̵g৆lN=#⧃7Mڰ8͗0;N@:7۲ko$Tl+ᒹt`8N.ѣܙOX$#.TvunQW\s8{m8I{f.Bǁ˴KA3߳y=sæ\;Qrob @ݯ@Dqꭺ D4`l촞EJYpLO td@!1T.+4$o0u_<`&ȲwY\Ha5dtFOh3$ܒW… HLьdaBd;P5<ٷE gNXUoO!pKF-Xf^sxQ'IueAz ;x_% s8=ϡ2{:P 螨j).٤. R|Uࡣ{ Jyz{A,o؁ҚuEnԶ^(JV`懎CEas|fiܶJ-n,-a>јB[iL|UņB}IL/#&$tm+.k ^@Ok̸,\y5or٩iք αHb؀?/\y!;ZfS>3p(o!Q:J!V5Ä3M-B x?*Tp#6 B0Ka6+ĵ60lzG̶)k$..YYY,$#stZ @!fH:e r+X:3&|T,rykrRgZFp` 9'6ɇ|ɔ_ԷpkwEt y>z|i ecԨ3_^Ѹg&эUOa`Z7]&z\i,;awv*W;T :wB:w.HtgH> +`cjUnsڋdҢiNrAOfvri Cp sAQQl7*! J0z }jI(2㲻b5n]"a$0OwdJ %ּDtP.ob˅cv|r+[X̫2|cH/A]KzvćW??. 4(P(׽t?Ma^ns BOzE}#NPW|ZHh_宐./=M^o QbU@Yz?&0v 'x!M0`b.:g0,@wCZb&Q%z`')# /MosO\ϘH( ij.O>Zvi4Z׼lLF7 29-./hUH'TpY|7b,E~_^=|΁)طސ&E k,L%>| V7s W@߮:sFoi{w &xɻԅB_E4eFO-ltiy[>_YuQ*'2i8+5meMJ gC(Yx9<$ze /Q}[ J磧 hGжw?6 ^EAivX7}t4:>J7Ix6w8߫W?Yeuw@iJ.Ҩ\j y)`| 4ctjcv׸P AlPb"=Iz=70|XB=$(s|f_s`XfB+fzg"vѨVIO K:# bu<! n_Iv!!cco&=MnEMM.4naKYPL_m&?9tz(LnHTyj^5';}1h4Z~UVޅZ*PZ]& [[1lqiF6`f6s^{ Iڮ([8s(nT(TSsU2 53m\aUhlpŋN_"[\c\FC(dkɀ1Q42Qd0 ez#C/tbcө<'0 Gձ;NP^!e5yBDTNIm]%|r0Tr'"t֜i+BQJsEJ"?OHdA>,Q/TO?7b\|1BՔ_A߯vF|K= uƯ'1EeTE. r+WQK>Q61;~0L<+>R7cFAy(瞙(̂Ush6$#,0BWM#9[hRH>.g߿CRc qL["F)йÿ]ӝDNf5?KRES9% ,<#H,&f "v3IC?%`kwJT$K'9/&*ngR9wc"-rUOgƤd_Uv3Q5?QǿX;+d\)1U ?tVW0)&ThbT7AO:|綹I&Mppbxz` ]܈UIKgWL޹^tLVTOKN`ĵ\-]E$ "*:"~A:)QW+6_>f$"89z|vc>e^T]:_[jǸoѡ\˧~u֥G/n&N ʱ>7a\Iz{d엣oew.1_VIK8_]#},MR{K9̍ ೥rx}Ssmm%Ү ~m гC϶鸡Lz~nHCd۠X|h9˗/^)qogiɥg562ٚ!vݼ `L* |"`bSwt݉BPd9F6i 4P[&El.":JB-S4 1Kfjx݇I~W6V%_>N#vhb( -zmإRwU v}#j@t*J8ӴgI(F]Η~]`^>5Cl+*xk%#=l6|hS(ʰ-6N AZIq;&\lKh\Q<."g@G^b48~@E~+ G7]vYYl%8iuQ%-=ʟRs@x+@DIu|X~.Sӿ2\T`5W"3en;"W9rڋ}AP3!A%E r#C;j/ozPAj95@/D8KXV ؽWu!#ďꜫ";=>_K .cE'~<+*]ԝ|$\;ɳ:ХiJ0ʸh]j(c{mf%qTԪPv.j7,/E;E(ot+nW~%~ZkD뇙$қ̛2ݚ&]swW3(PjC56OFΨ g`TxB6H\u[yfdov%\a'a`QW2QH֩s5F >1Q\ o-iYi??>wદz>`,C?oO},`+jFf0݋Zf}2~)"*pbgH; P/\ ]+И8S,9] 0|V+G%8ѷ]ky1י>f=|$"-\yDQxk'XP)g%±'\NQ[1~ @58tjchcb0[=C pJQamgWމeu߱XJħ.Bzit3f͡mՊ!߃ /T'Csp'rz츁K(g[ֿFԄ Bl8QwXdwgϯS؎^mHENR:I pd̔Y4HEH1ʕ}Mh$KR$7LDPl)BϗL:{ Z.y),6WYI+ ~gzM*a&k-q(P>;?+?[b"Rh9 潲.DV/S%&TeG+^8! Rfyl.3E&;p*aͣ ?~TWp[1a)(>"Rd-Gӊ^o*ۯmj#_JhbU{XoQslЎgMV[k.qD A[R_T,ş: ϋ@D~spH(ީ.:-Q*"ϔXE2%`OyI861*l֫F4GT:QFe[DpY&<_l)oQQ=EUH@4Bd D9r[I_ }1t䝡+ J.޶]qe~ fC{RU̐K&Ӧ+*Cqir\^$ z~=ϕSzO6nphOA %Bƹ*S3KP-5bZE]+)e"Hf7[jKP,Ce)cL,?RD.+}P){UB?׉Kk'M8&.d ݭs)fήpEgjثhOTEe3'xߕ$9N,oAU3OsK|`ַ6`V}?xM:[؂6 > HUʤusqqyٚp[W vGaLf@wYްn烟T?J0 XQ8.`(s ?Ђ؂L^S,e1Hd\ d԰諌#*b9$'q-ZsҌeS}ѱ8!UAE+.zC-5)jNJycɄ1 ½^gV((UCL W(/66r=Oe!Ƨ ;NX\W✢YnXW;9P-@ ,_vűlH92)FY"|Ve9ت+23~30 "C晀PqrHyH%'$YF\7mi(g̪I3e6w4(|sT5*LDxSrī g܀#Qؚ!&"/SH B(!`ϖ!6yilZxJ C v)h=H쾭=I.9RͺQO~y_ ,Do2|4uC+P7NnU8KU!*BJ"߃U k O$)0QKT+z"&tQhzb1y8Q{ f<Աp+BܟB"ރ̗g7A8a f\oiXUow^ -mXIۓZsV\ &CBUi),i9SQ*u/Eًato<2FvE"cPQn+h8^ S_ļ\`Pjh}Oehƥ-Њ#˄1q#E gpdB]A$Ƹf&\YD2`Zp| Hד!4MI8Wvmޞ6AcFfqxy&~;KΗc?f;x敯O-F]zؖk,,j8^t K W$?xQOm/p<ϵuPR$*+H!rۄ[jXb5 .4^p'hơģ$zWX1Ht:UR}ԙ¥cW ^_fv A)y,o@2j.Gib-;a/1rp"|7F4ikNYOBc~(m=pKQ8Khl|nh. 5+ ji ySZ[$9 Z7f[*[9 od)p; xfQBMAHf'%g/ :.f~TYTg_%c'nF.;-35gݵL$6DzFitVbburEGXȓEcHia^Cz1˔#?\KК|Z;X9qfT[`b2i=Ě0O)Ũ0<µ'd_*-{<c$ %f#IQ got&3.ȑפ)ì9/ *RD`BItTS8a=Q`Jz`4 0u;;~=P FGtвHlC|bâ9qv巉Pb}~fԑ?LN._+HOkEG*w9|eXr|.?;:ᆞ7˳Wb=j?!/mسj URQ&o T5rl9K _uy5σ'mJcI0G-djH8yb2ć\t)CF0΢!)7T@NC\/Nŏ+ ]$Gg߾7Q\_ܰfHsAylև >s](e^)l7O`e!ꝳ|Qr  4Ί(%5C~ՠvqHٔ,o] u|`6mTB3w%MWsۿ XT621c٦ w[/zVkhW>W¶f?`J%ݥ$aYR8g-_q0M؂@Ao_ n[ A8fKiv9iKȬբy*s;{E J`~l)5jMV;<"ʃaN!SQ:bI3y^Eچ\w[^[,y- [/?:vgŎA͐uYhtbVe =^Z!\,L dqV#/{{iޮVZ+ e XGcJY!$RoGn%rGebj)DPXtg[Z_ZTO$MD.]$rX7hQ(.U}Ԡ͘?wFzݍo[6'cߐmĚ}G@X}tjN@j. hgW@jq|rS iIu䐬!}s'.c%pBAM*9]v G&PLΠ=Q3/⇢dK ٵLZ!wo6jZӉ^ɎsNP& j $S@}ÊUJsOGV/ 4ǫ k(Hyk˨?'(Y^)wCrZ6J:{fMw__0Tc(1E͔p4X BX˻e$#4ëEK eH#{5BPhNBZ3^] [m>-}YԞgnğ ̢|a5(S UARVQ,MPM) N}El_UBwd0 'Ri t!v=Xi' uTʨ1})jٲ&&4ѧUl~A9@ZTsS _&4RA2vAsݹ$pƬPQQl\JE?V1[*ȄqNB7-'h,ZTY0' m◟{OjbgXHrBSvKֶǙ W3'>hf> uEf>3' 61Np[dz7f٫ xg{91c$swxV š&XU ^! B5s舴C!diR q "]֢SUg-ڳSSKȼ}Pl;f$:}jI6MvkBJt0oSi$G߉Ziiئ"SE78FЦoHFS(:8gwQ|g w1/ q3=0eD)txVіȃ"ӧ2L|SgH)z@ ֐2W{p>s5#&Li>;CБe)'&_^p<򽾩Wb,≹*t܉}wHkgf.08~;\ B:cw_Ы^BȄ!D[C2_wUEl͝YPEA=i3$`<ĸA ʮQ?i\OFN-;GҽE vYj^wBfM"n74M"jB Z+,άoa@ Zj߃NK=jeupyv%wqE_fBZZyKmh&]1QHqu]F>tǮJ N $ /7*t KV=R8 ,L~Ï2bpM=m9%?Ά4~rf IRdOnp FݛփRO%B'&n#:?aRX ^75Do1S񻽌5[n ;4 ޹p+?$bg_Ge !RDhGOkܭzl;5ۥt Z8[I00 J%Rn ދ"r?$7 zn)%/ߋHWhQ45: hz7GXoY_wƖ|%$D/e7^$E6:zG (V3Ȃ+RpFihL¥LlLAaA 0oYrPgxƁ+ʚ Sf+=#uwpg{WI>/kNC MʂyJfI9< ]" nԠ1³N'i@%:ż(: -4;2Ѳ38xL`n% 1TСvBӧz,]R&8fS&Qq:=}U ##ւR!pm]|N@Ű,8ܝ_-ҫ.n s_jf 8F0g|Z (= g@>._8B^{}xmEC;F"S;O$m]UcOH0:Ǯ`79h6Мİ?*|SlىCFLⶲv|jQ k,?*d/_I>6b5R̝_f,]-DH7Ki]]1"T;i=8xRxmUڦ*re O'Zr [^ox\@34HpԜty6&"tdEILo+T?С\|R$@0FcZږB>W˗S#zdi08DTKM(IEXM/\y;3` t?!^6m~5`^ndg7.v4Gj7>lYC!#zZI]Pw/GjTQ#o>SUlEMOC^=3vAl]._~%XtEخp1m0b"zY85߫,>1&'˭z$ISs3۲sCYC¹)w %pWBJ%8Ev֮$-)y4b/+`(RFwZ8_;[M>zhtpKq XQIMw#SɬBpQ,M*_"YP3c3WtH"JԖ1,(먴okϟS:~YHdA&4:\^%msiqf<;L]c3-}e5:U,$!!a`ڒbE]1uĥ~a%fh%-  (56uIƉ)*DtY &XzSfjn ]pzx jh`fҶI,f^^5 IR'>lL^@ۂ1pm+nGغ{U(X$;/ _ 1lyh>;&E!9$ m[MHo!I/&IขBVD_˔.DLQwd \ Vl3 ր+>FNDlt,泋UZU sh b"}/? t+ LAq]e7 4VH[.#OXl] Ι*,8#Pw|I멲]M,,,\4|t?f**K(͖3Ù'6AŎm /wlFY]b7O]xܦTn80_r vP"fxqDe?- _kUs|{Tg(m *W(OΕ!>/H5KKCiagpá=yfG_r1V F&Kr㲳̹}; !YJD=3$:z CV9Ȣ:k 9KBEl8Y_T,I[0O}j*6p/!0}EiUZ??Of| yTC搠H| *Ȍ̦'dDmY7NLuHFu{K˳{N. 6w~L%DP6e>uc(Z%{tm)ʷޠb:C6Տ2Wυf9K@(כk}8.&bٴ}WQ%G_ s4˺N4 E ZKn컠m$pemEٹ ^uEX7*98t'FsBK-ﭪ:&f}>GMԧZ%^K? d'+QcQe5XSOOVإQoxR#_T?a2b+YwD: o[~a]R:_~B=oRE8*z*$ϣ=x@/ L0!䀫z[$ۖ grFBub\EUv\?lתݞpXUxiVe]Z H%4"B+} hȨ>܃ET%HLǗJN1jg=m}YU%W6ljyъɛ )jkjgаQ>_|{߶1 aF(Z2\v0EM~X6e,d$RBƹpF N&ʹ8;j9 H>E0-r%YKW4x2XCÈaO$kD>l?8vAigKI -\3=)[,lag@ tA ;J&ɮБۚf<Y;ek\Gx4@ğ|mJ|VF ci P$[fO6u7> 9ÆZa)`?_˲S龙D-߹,UDSB^ _? r\5&3k{**O" ©I͎0U!K]EjGf@.4f>z@=3ڷِnSUJEnŇ5E:і* apLHEm9ԁ uzʬ=!B!sYMS2,܇;z$_\Wp9q$-Ǹx"q߸+h qjmMV\ 5[AIBU4MĶ4"{*_xz9 Nz0 jJm/x:W 0 -^BO}.abڰbgD^Z$<~_ԥW.9D.::Vovce0/4I&/,ή6 }]GWD4!{+'՛nxRO;HjюVk{rK%[-Kp,J@b8ȴ^IK#bbkЦhnWp\"6]>"Ga tTyg?"1/w*a҄(L|Et{0H_7]U (ͅZe U?z(, z?#B+b~i -o;II3 ~F_.͝ȖhYз1S).πD 64eN6zj&)ީ!h4Qksd-m !ʻ1~ srڐ&W0 e*Ћr_%?oqrjyAnHPUBCZ9Ywe݌: waeLY& t^ʨ1ͦ4㐴9D=bw<5IE0<Й:^miَL$[;ka+%_C)<&(N!\X0mh嗀ez!A'* ⨖ﭵ;VSMkO 5>+~  nU~[%u_F<C"kH+JUPj010yFUeNmx{0}bIe(>9\ax*2j {$L H3RdJuc'Dq{fVm-q(ȤtbB'đl `BOV*JdqgSr 0o %DdyR(.zd,D3.""xji$T&T\YRE:Qm3>]x$ -Ab@!l]'%1KXt7> SvMd߸lW~c2!CמFSm 5#*A{P9pU:eNʯ]:*w]Nt/!izpdVj]MltɊwQ09=l3zo_vFgN,TAm%VBL 9fd#?Q_NpB.ra) WNm=[z-e_Ӄ륧l7MBHEi4߷.ɗ\m#ūzb!Pȁj0XVf+BzOWYb^q%F+3 wW[k]dѰ[5 .2!}Ry]/"Ji%_*P`v[&7Fqi\uВ.x]#ﰔgT ]GSd({imٸ 5q?];NWC1WXXV_Gwˍ1:!T}nAS UvYB_ a E5ں^:8-헠Nr^>PpqS#M^tSxUDPcxG51^6+Ia,O5ty_.kG3oEfJrUOj*L| ۦimg;Kkӧ`͔*1T~GdSدkt| ,he6n^znIn(͜$_f̻-#NPxb^ETwVh"~Y H29? tLMr[pRp?ɀλD]Rs3@!Hts! H~,Y~ *tSunE1beE D#=M*ۢvȒLb#f(p~>O򮹐kTvV~93#`p74f6NJ@vQ 犯3;oc'vn5LֽBfsEdXP0q"_BrM]yJ p JetDJ,7c^KKb3 ,|QcCIBb,x:gPyyOV5ƌt\Hqˑ-V@XGqpwN?7v-H_duD nj!v6mcxJ}}2?$YqcOJ\}jh[\'bp[GulniCF4Ho!RH[ 6 f^N-c\ƍ=L.Y S ŏfUo0Ȭj,^6O]9gv ;iwtDxE'5@>GEr(ğUS fn鴥H[d-L;hS&2R4N$U0 Be>6HBac(uDb\lr*:6ҨI< wAa E1SS2O7kPJGGS݈6PS:GcCpoMj fT R@M)$e#f8h zU2Ü\~< &-3 w&N{<`^g,D>E塷sKlD~V/{ q,f=YŽzMH +XZ7cbrp9NN, n]^ {xLݦolaZ'+VgbўN _ *_Y8*&Fl o7LO^H8 ʪ5iD- \4w5C;RRUCBu/W,oGgݛiznVe%6BLf/A]sneqtڲT| dwzrOJ^ΑgkANg !XÞRڊ}j⪹pXUm/IiBkZu#f?QC1^z=ghйs1\|%6utZ0 G;U~jFΦ?KY`H?["-)Ƀ/ә'Y]~o.w0yMpuޤ=žix5~d9$(Zۜ%@`HY2SX8i4^N,-8BL&v/%/h$`I4M1 =ϬՆQ9<;6B0Y8zܑ4 79MpS lM@{Pƻ[X 7A h ,c#ގ/rs1!w% 8!z;6u(i5e@C]} ddQ^nJE,f-qܜLMJ*}An:#Opxy=CC&2ҽkm.T&?*qP-Inh\u9XE#i\(W뉲 zHP%`^<DUI7Uu\ZT=<ih;qڳcA|wz'GDMta[VSiO /O&[EڈtbNwS4 J$ޑ֮cvDea%@E\1ݮzOQYlR1|:V` 򶟉'Quzn HBED;?OeJ/U 20̣[;DwS+36Dr+J Swn -qn Em.}e3ʸϕ<:О]^$CtIw0v f/IP2w%`f'B Es=>Abxɐ6p08ţR k>T(? :-g!|}ϋ _~5<l1گ˜ nd1P>jA/ʶ oqz_WtG>FUQ6(fgI㐺>"w^%\0(G\he/yt?Ĉ1(P'Th m1g_nSVi%')jZf &.#ItS"*F^A]p?xe#Z)!tq)4Da1'/,%NG['2*_WQ\'tĶ2 c2#iM>НP({<ŰldF4vK-\CmDhQv-'P-1N8g?(93'$=, lF ؜|gA~#d><Ӟj,@ HQOQD<CW[[X3Džg6wu=AGgJezm9u֧ hpdgu|K\ZzǟAQgzyy2;λb(:BLkIYZ(řkLk)2<75[OY$[Eg?vQU[eL@x.rB +kIW9L]nh^Y񍃺ƻnr Vy&*a.lv߽MS\ILm3a0ga6n%TV(>xߊH{l+CeYlBF3}IuukHv=hwU˿c[+OCU#$:DELA 2|-pnOYCrvb]otUFW4w?U7g2J0xph#6;PA&:Q*1VFxXYO3 .~ri., I沫qA)ҮĔ^w _-#D"Fbg;1o'2^j ~Z5'P?3ړ5R;z|wybvyBak1\ r Қ&&wkAys|xْ!kc9kd^M/NR}}Dٓk] EKKƴLlkcX\4! پ~h= !I^}&fF_nkT9G|Y\RIqw.<6;,HRI hv`hS*EcL`OnqYRn89b ЈGM`sR3Tj@bK.T捆%]{<,ӥ1~RV*#]7Ckx]ҵ &7/O1F>-r17>A C =L(oœxe/XgC6?oZok8E{%]3-E@W-6׶7ܞ!mtp;ܮ|)9VK/lk#j^O?Ga4h2Pxft-H%/z_taS?K{Yᙚ-OTKE~pG',֠QZ?laBvt^KK|KZiCP\ۜ2,Adu/{.?-%3}Rk [X/7R϶y K+)rcTHI l ί* 11k8`Wi>J>h%.#+/ P)t;|d3 J`SȾ8au9|hdckSM, wH-2F׃*k;s.y?i.P|$E,tbYS v={}2%)"GF=Lt?P끻75I_il *$[0^[{~F'k#lOt V5vk WYP__5IKglٟ\ Lv`)vķK' ?XD,S,[n4s_d҉JV{b#Z\+*ʻ>EܡZE黺\Hrd؜9/\/e&.l }fZTR4$Aҍۼܢ|] tNg HӢ ]x"D>1wtVxܧ}Bfmd[W?Z%MսS5& $+%y40;C┎@C](n'x:Qp~ ƉECF-y{`YtAV;K&m) DWv6-ԩp5W~M9+2rx*wxVbσC7R9YlﺶH0:+;U SOZl {ի9qW*8Ij:{ř;E8\P!'Pruɦʔ10ݛg0a`pЫޟ)i1TJ%[ I^N;%z̿\*.✔Y-uħ\V> W!LAfyui=dͥEZ}av0kQB(RrT@C\w /ud_nϴ*ɲ&F e lbga U"8pt%{N$eo џ\ts B( KistN{r%D_ pp Ӄ*붶>,(I^_!=+AO1….ipQIWZ@˕[ЧZ+־#JJ؎C]f5* ˭Ŀx1 < ]|aRXRRJPiƮ$d{At=ҚznOu9QƱ \0p*oZ`!.CⱩRҔ$Y8i(,p8fE/=$k/&s'n,?z*1d1%q'E<9bõ O$5~nU̙G #݌Sճ^*% c pB')7tP f%)۟,5¨”=Ǿ5! ?挜9rۄ'R= Y]z~Rt,vf}} K0_nQT !ȈQP"Nuk–B6]j7[K ļĔO fvAtcM矘Q f>YoՎ;EuC>VDvLV+6ٝ,Wf,6izHJH^!Ud2a SjO˙t(wXt\(;W@3nY7cs97m%dMLfqws &Ǒ>2s_#/^zd|* h3 HПL|ŋ(66;М"z&>uꃺb6cD1n0!ñWtXJmFPX}/)Յ 3f_!/CATW3FOT=nr iߵS8X#XzZCpzTᖭ4o|XtoʓpJjRhiGU(w%80_Geo/ ڗy 7gVWJBVWEo.x?FgNUթ*z%s)Sx6 ДIzּsa[⵿G6ZidTH[*><?beQ\4KA(2&$* qZqaƘD (qZX֗ӾvCLCO "AF}~s3Z|妤 Q 9R$%7_PZrpN#RxOށ4qHή|HwUrv%o8~B*"ڋ|}Ӗ 9eP`Z0WqX:~z\\1C^s#6 Q vOʀ$ !"{u8Ɯ,\wc~e_qTYRcŕEb[Ĭ4MXgnD  pOYQIh$<{*1J'Wn_fbH!"u?++« {VѸ|ժ:g*|5BUAǤ]<&8궿cN b3RcBA5Ş}H$D~PZ/~0W+d4x0| #c䈅f