gXbfB#) ۻbis_IIߪ ۻbis_IIߪ ۻbis_IIߪ ۻbis_II4&D.5;Aϳކ:}Wۻp%7ޔ_#5L@?ss\,fa\eChɛ+MMX,-\_[6Eq_uC^avh ->]+/Wg׿>|"d2D^1=]mwgFN5K]OElf5F \wgFN5K]OwgFN5K]OwgFN5K]O[_׮|S 6 iS&XVŰįwgFN5K]OwgFN5K]OwgFN5K]O%-uOV%-f~y@],ߔtiXx[>DwgFN5K]OwgFN5K]OQ+RŞԲ&M.x ۻbis_IIߪ ۻbis_IIߪ ۻbis_II߄}BkH1<_Y='sA7tkY̓I"gmkj52|s3Qw8 :Ǚ6!-<%+Qۿx2Hm.qCy+]6`.z=h~]5ef=GFs~H̲'eZWmy;Y};lPA9d6c}ΛXA@X x.Q,mڱDTt,exo`cZVL]>WzHy|p^}E'u'.daXpU.z !ThN !)mE_k`3i/T.huvn"pkD zEHwH `xpW5ELxN2Cѱ&sxoSuDk)}ҡ2&˅Gdem[†r4.QȚ;x <7Dw7+{8^fʢE$Bz_+7=2x%h^fY 19ιF|DVܺ3gAot{75`\vՑJ*m0bȕ$Q1XѐdU1: E=-3xȇ}3lF5L}8xsdh0:%Ȁ?fm4@&r%`>?4l x x&B= b dCy+fӶ9B&0]dod 4Q =i!EӨ]u:RoFVfDF$ƒ>ء)%sնD>4mMAADfYb%CYKŹȰ4,; M:68G/B .-g2Hm.qCObV2=yL^t4qoa✎ǡ1yK Z%֨B0]T$QPp}̝uBǪ SFD^\$;r47A#jʢ;o_t:U3Zdb ;3:(.@ѳ?g `!|-Q\rkH<7K<[.A!4D\#[f2CO0da`1O{auh }uQ8ÁA| ` ܩr=pMYQ{䞫?Eݣ~iBbX.e'#ʿE䕰:~ݷe׳ B倮 73O~7 Lx3xJ.;s5ݧK(Y/GX p-{2~, 4 0Tj^Qdfu(b,d6-ՎWA@*z%Q/I ;n`9Y #o(~﮹/QLRNlRh@fj̈$BsI!$渽w&;<̃QMS]a8=weEj[4V=6RNdc^9Wo"kPSQ4Ȗ47Z[$ z>R Կi9booI3AB@~,'rL{A*vr~跙 rWBHsH;Ş.I`cEGl'—3xR;〫< k &򭆉=&C/bAK8`GCmjg[$@6?oG;ſ}乣`K ++'cZxX7gEFsIoC9FiO@Bh|]/bAK8`GgXR/,&bv؝UCnW#-{2u2򃭛oi^)H|@lڄ tת8*`4АtCȎo!c*7wI)@CV҈eow F,Ky-=i.)J3lX7Imj3n3P,r{–;Dƞ8DgM.W! pqE1.Noo*( ?~h$AY/** * Workhorse function that does actual conversion. * * @param string $text Plain text * * @return string HTML content */ protected function converter($text) { // make links and email-addresses clickable $attribs = ['link_attribs' => ['rel' => 'noreferrer', 'target' => '_blank']]; $replacer = new $this->config['replacer']($attribs); if ($this->config['flowed']) { $delsp = $this->config['delsp']; $text = rcube_mime::unfold_flowed($text, null, $delsp); } // search for patterns like links and e-mail addresses and replace with tokens if ($this->config['links']) { $text = $replacer->replace($text); } // split body into single lines $text = preg_split('/\r?\n/', $text); $quote_level = 0; $last = null; $length = 0; // wrap quoted lines with
for ($n = 0, $cnt = count($text); $n < $cnt; $n++) { $first = $text[$n][0] ?? ''; if ($first == '>' && preg_match('/^(>+ {0,1})+/', $text[$n], $regs)) { $q = substr_count($regs[0], '>'); $text[$n] = substr($text[$n], strlen($regs[0])); $text[$n] = $this->convert_line($text[$n]); $_length = strlen(str_replace(' ', '', $text[$n])); if ($q > $quote_level) { if ($last !== null) { $text[$last] .= (!$length ? "\n" : '') . $replacer->get_replacement($replacer->add( str_repeat('
', $q - $quote_level))) . $text[$n]; unset($text[$n]); } else { $text[$n] = $replacer->get_replacement($replacer->add( str_repeat('
', $q - $quote_level))) . $text[$n]; $last = $n; } } else if ($q < $quote_level) { $text[$last] .= (!$length ? "\n" : '') . $replacer->get_replacement($replacer->add( str_repeat('
', $quote_level - $q))) . $text[$n]; unset($text[$n]); } else { $last = $n; } } else { $text[$n] = $this->convert_line($text[$n]); $q = 0; $_length = strlen(str_replace(' ', '', $text[$n])); if ($quote_level > 0) { $text[$last] .= (!$length ? "\n" : '') . $replacer->get_replacement($replacer->add( str_repeat('
', $quote_level))) . $text[$n]; unset($text[$n]); } else { $last = $n; } } $quote_level = $q; $length = $_length; } if ($quote_level > 0) { $text[$last] .= $replacer->get_replacement($replacer->add( str_repeat('
', $quote_level))); } $text = implode("\n", $text); // colorize signature (up to lines) $len = strlen($text); $sig_sep = "--" . $this->config['space'] . "\n"; $sig_max_lines = rcube::get_instance()->config->get('sig_max_lines', 15); while (($sp = strrpos($text, $sig_sep, !empty($sp) ? -$len+$sp-1 : 0)) !== false) { if ($sp == 0 || $text[$sp-1] == "\n") { // do not touch blocks with more that X lines if (substr_count($text, "\n", $sp) < $sig_max_lines) { $text = substr($text, 0, max(0, $sp)) .''.substr($text, $sp).''; } break; } } // insert url/mailto links and citation tags $text = $replacer->resolve($text); // replace line breaks $text = str_replace("\n", $this->config['break'], $text); return $this->config['begin'] . $text . $this->config['end']; } /** * Converts spaces in line of text * * @param string $text Plain text * * @return string Converted text */ protected function convert_line($text) { static $table; if (empty($table)) { $table = get_html_translation_table(HTML_SPECIALCHARS); unset($table['?']); // replace some whitespace characters $table["\r"] = ''; $table["\t"] = ' '; } // empty line? if ($text === '') { return $text; } // skip signature separator if ($text == '-- ') { return '--' . $this->config['space']; } if ($this->nowrap) { if (!in_array($text[0], [' ', '-', '+', '@'])) { $this->nowrap = false; } } else { // Detect start of a unified diff // TODO: Support normal diffs // TODO: Support diff header and comment if ( ($text[0] === '-' && preg_match('/^--- \S+/', $text)) || ($text[0] === '+' && preg_match('/^\+\+\+ \S+/', $text)) || ($text[0] === '@' && preg_match('/^@@ [0-9 ,+-]+ @@/', $text)) ) { $this->nowrap = true; } } // replace HTML special and whitespace characters $text = strtr($text, $table); $nbsp = $this->config['space']; $wrappable = !$this->nowrap && ($this->config['flowed'] || $this->config['wrap']); // make the line wrappable if ($wrappable) { $pos = 0; $diff = 0; $last = -2; $len = strlen($nbsp); $copy = $text; while (($pos = strpos($text, ' ', $pos)) !== false) { if (($pos == 0 || $text[$pos-1] == ' ') && $pos - 1 != $last) { $last = $pos; $copy = substr_replace($copy, $nbsp, $pos + $diff, 1); $diff += $len - 1; } $pos++; } $text = $copy; } // make the whole line non-breakable if needed else if ($text !== '' && preg_match('/[^a-zA-Z0-9_]/', $text)) { // use non-breakable spaces to correctly display // trailing/leading spaces and multi-space inside $text = str_replace(' ', $nbsp, $text); // wrap in nobr element, so it's not wrapped on e.g. - or / $text = $this->config['nobr_start'] . $text . $this->config['nobr_end']; } return $text; } }