+function makeComposeLink($url, $text = null, $target='', $accesskey='NONE') {
+ global $compose_new_win, $compose_width,
+ $compose_height, $oTemplate;
+
+ if(!$text) {
+ $text = _("Compose");
+ }
+
+ // if not using "compose in new window", make
+ // regular link and be done with it
+ if($compose_new_win != '1') {
+ return makeInternalLink($url, $text, $target, $accesskey);
+ }
+
+ // build the compose in new window link...
+
+
+ // if javascript is on, use onclick event to handle it
+ if(checkForJavascript()) {
+ sqgetGlobalVar('base_uri', $base_uri, SQ_SESSION);
+ $compuri = SM_BASE_URI.$url;
+
+ return create_hyperlink('javascript:void(0)', $text, '',
+ "comp_in_new('$compuri','$compose_width','$compose_height')",
+ '', '', '',
+ ($accesskey == 'NONE'
+ ? array()
+ : array('accesskey' => $accesskey)));
+ }
+
+ // otherwise, just open new window using regular HTML
+ return makeInternalLink($url, $text, '_blank', $accesskey);
+}
+
+/**
+ * version of fwrite which checks for failure
+ * @param resource $fp
+ * @param string $string
+ * @return number of written bytes. false on failure
+ * @since 1.4.3
+ */
+function sq_fwrite($fp, $string) {
+ // write to file
+ $count = @fwrite($fp,$string);
+ // the number of bytes written should be the length of the string
+ if($count != strlen($string)) {
+ return FALSE;
+ }
+
+ return $count;
+}
+
+/**
+ * sq_get_html_translation_table
+ *
+ * Returns the translation table used by sq_htmlentities()
+ *
+ * @param integer $table html translation table. Possible values (without quotes):
+ * <ul>
+ * <li>HTML_ENTITIES - full html entities table defined by charset</li>
+ * <li>HTML_SPECIALCHARS - html special characters table</li>
+ * </ul>
+ * @param integer $quote_style quote encoding style. Possible values (without quotes):
+ * <ul>
+ * <li>ENT_COMPAT - (default) encode double quotes</li>
+ * <li>ENT_NOQUOTES - don't encode double or single quotes</li>
+ * <li>ENT_QUOTES - encode double and single quotes</li>
+ * </ul>
+ * @param string $charset charset used for encoding. default to us-ascii, 'auto' uses $default_charset global value.
+ * @return array html translation array
+ * @since 1.5.1
+ */
+function sq_get_html_translation_table($table,$quote_style=ENT_COMPAT,$charset='us-ascii') {
+ global $default_charset;
+
+ if ($table == HTML_SPECIALCHARS) $charset='us-ascii';
+
+ // Start array with ampersand
+ $sq_html_ent_table = array( "&" => '&' );
+
+ // < and >
+ $sq_html_ent_table = array_merge($sq_html_ent_table,
+ array("<" => '<',
+ ">" => '>')
+ );
+ // double quotes
+ if ($quote_style == ENT_COMPAT)
+ $sq_html_ent_table = array_merge($sq_html_ent_table,
+ array("\"" => '"')
+ );
+
+ // double and single quotes
+ if ($quote_style == ENT_QUOTES)
+ $sq_html_ent_table = array_merge($sq_html_ent_table,
+ array("\"" => '"',
+ "'" => ''')
+ );
+
+ if ($charset=='auto') $charset=$default_charset;
+
+ // add entities that depend on charset
+ switch($charset){
+ case 'iso-8859-1':
+ include_once(SM_PATH . 'functions/htmlentities/iso-8859-1.php');
+ break;
+ case 'utf-8':
+ include_once(SM_PATH . 'functions/htmlentities/utf-8.php');
+ break;
+ case 'us-ascii':
+ default:
+ break;
+ }
+ // return table
+ return $sq_html_ent_table;
+}
+
+/**
+ * sq_htmlentities
+ *
+ * Convert all applicable characters to HTML entities.
+ * Minimal php requirement - v.4.0.5.
+ *
+ * Function is designed for people that want to use full power of htmlentities() in
+ * i18n environment.
+ *
+ * @param string $string string that has to be sanitized
+ * @param integer $quote_style quote encoding style. Possible values (without quotes):
+ * <ul>
+ * <li>ENT_COMPAT - (default) encode double quotes</li>
+ * <li>ENT_NOQUOTES - don't encode double or single quotes</li>
+ * <li>ENT_QUOTES - encode double and single quotes</li>
+ * </ul>
+ * @param string $charset charset used for encoding. defaults to 'us-ascii', 'auto' uses $default_charset global value.
+ * @return string sanitized string
+ * @since 1.5.1
+ */
+function sq_htmlentities($string,$quote_style=ENT_COMPAT,$charset='us-ascii') {
+ // get translation table
+ $sq_html_ent_table=sq_get_html_translation_table(HTML_ENTITIES,$quote_style,$charset);
+ // convert characters
+ return str_replace(array_keys($sq_html_ent_table),array_values($sq_html_ent_table),$string);
+}
+
+/**
+ * Tests if string contains 8bit symbols.
+ *
+ * If charset is not set, function defaults to default_charset.
+ * $default_charset global must be set correctly if $charset is
+ * not used.
+ * @param string $string tested string
+ * @param string $charset charset used in a string
+ * @return bool true if 8bit symbols are detected
+ * @since 1.5.1 and 1.4.4
+ */
+function sq_is8bit($string,$charset='') {
+ global $default_charset;
+
+ if ($charset=='') $charset=$default_charset;
+
+ /**
+ * Don't use \240 in ranges. Sometimes RH 7.2 doesn't like it.
+ * Don't use \200-\237 for iso-8859-x charsets. This range
+ * stores control symbols in those charsets.
+ * Use preg_match instead of ereg in order to avoid problems
+ * with mbstring overloading
+ */
+ if (preg_match("/^iso-8859/i",$charset)) {
+ $needle='/\240|[\241-\377]/';
+ } else {
+ $needle='/[\200-\237]|\240|[\241-\377]/';
+ }
+ return preg_match("$needle",$string);
+}
+
+/**
+ * Replacement of mb_list_encodings function
+ *
+ * This function provides replacement for function that is available only
+ * in php 5.x. Function does not test all mbstring encodings. Only the ones
+ * that might be used in SM translations.
+ *
+ * Supported strings are stored in session in order to reduce number of
+ * mb_internal_encoding function calls.
+ *
+ * If you want to test all mbstring encodings - fill $list_of_encodings
+ * array.
+ * @return array list of encodings supported by php mbstring extension
+ * @since 1.5.1 and 1.4.6
+ */
+function sq_mb_list_encodings() {
+ if (! function_exists('mb_internal_encoding'))
+ return array();
+
+ // php 5+ function
+ if (function_exists('mb_list_encodings')) {
+ $ret = mb_list_encodings();
+ array_walk($ret,'sq_lowercase_array_vals');
+ return $ret;
+ }
+
+ // don't try to test encodings, if they are already stored in session
+ if (sqgetGlobalVar('mb_supported_encodings',$mb_supported_encodings,SQ_SESSION))
+ return $mb_supported_encodings;
+
+ // save original encoding
+ $orig_encoding=mb_internal_encoding();
+
+ $list_of_encoding=array(
+ 'pass',
+ 'auto',
+ 'ascii',
+ 'jis',
+ 'utf-8',
+ 'sjis',
+ 'euc-jp',
+ 'iso-8859-1',
+ 'iso-8859-2',
+ 'iso-8859-7',
+ 'iso-8859-9',
+ 'iso-8859-15',
+ 'koi8-r',
+ 'koi8-u',
+ 'big5',
+ 'gb2312',
+ 'gb18030',
+ 'windows-1251',
+ 'windows-1255',
+ 'windows-1256',
+ 'tis-620',
+ 'iso-2022-jp',
+ 'euc-cn',
+ 'euc-kr',
+ 'euc-tw',
+ 'uhc',
+ 'utf7-imap');
+
+ $supported_encodings=array();
+
+ foreach ($list_of_encoding as $encoding) {
+ // try setting encodings. suppress warning messages
+ if (@mb_internal_encoding($encoding))
+ $supported_encodings[]=$encoding;
+ }
+
+ // restore original encoding
+ mb_internal_encoding($orig_encoding);
+
+ // register list in session
+ sqsession_register($supported_encodings,'mb_supported_encodings');
+
+ return $supported_encodings;
+}
+
+/**
+ * Callback function used to lowercase array values.
+ * @param string $val array value
+ * @param mixed $key array key
+ * @since 1.5.1 and 1.4.6
+ */
+function sq_lowercase_array_vals(&$val,$key) {
+ $val = strtolower($val);
+}
+
+
+/**
+ * Function returns number of characters in string.
+ *
+ * Returned number might be different from number of bytes in string,
+ * if $charset is multibyte charset. Detection depends on mbstring
+ * functions. If mbstring does not support tested multibyte charset,
+ * vanilla string length function is used.
+ * @param string $str string
+ * @param string $charset charset
+ * @since 1.5.1 and 1.4.6
+ * @return integer number of characters in string
+ */
+function sq_strlen($str, $charset=null){
+ // default option
+ if (is_null($charset)) return strlen($str);
+
+ // lowercase charset name
+ $charset=strtolower($charset);
+
+ // use automatic charset detection, if function call asks for it
+ if ($charset=='auto') {
+ global $default_charset, $squirrelmail_language;
+ set_my_charset();
+ $charset=$default_charset;
+ if ($squirrelmail_language=='ja_JP') $charset='euc-jp';
+ }
+
+ // Use mbstring only with listed charsets
+ $aList_of_mb_charsets=array('utf-8','big5','gb2312','gb18030','euc-jp','euc-cn','euc-tw','euc-kr');
+
+ // calculate string length according to charset
+ if (in_array($charset,$aList_of_mb_charsets) && in_array($charset,sq_mb_list_encodings())) {
+ $real_length = mb_strlen($str,$charset);
+ } else {
+ // own strlen detection code is removed because missing strpos,
+ // strtoupper and substr implementations break string wrapping.
+ $real_length=strlen($str);
+ }
+ return $real_length;
+}
+
+/**
+ * string padding with multibyte support
+ *
+ * @link http://www.php.net/str_pad
+ * @param string $string original string
+ * @param integer $width padded string width
+ * @param string $pad padding symbols
+ * @param integer $padtype padding type
+ * (internal php defines, see str_pad() description)
+ * @param string $charset charset used in original string
+ * @return string padded string
+ */
+function sq_str_pad($string, $width, $pad, $padtype, $charset='') {
+
+ $charset = strtolower($charset);
+ $padded_string = '';
+
+ switch ($charset) {
+ case 'utf-8':
+ case 'big5':
+ case 'gb2312':
+ case 'euc-kr':
+ /*
+ * all multibyte charsets try to increase width value by
+ * adding difference between number of bytes and real length
+ */
+ $width = $width - sq_strlen($string,$charset) + strlen($string);
+ default:
+ $padded_string=str_pad($string,$width,$pad,$padtype);
+ }
+ return $padded_string;
+}
+
+/**
+ * Wrapper that is used to switch between vanilla and multibyte substr
+ * functions.
+ * @param string $string
+ * @param integer $start
+ * @param integer $length
+ * @param string $charset
+ * @return string
+ * @since 1.5.1
+ * @link http://www.php.net/substr
+ * @link http://www.php.net/mb_substr
+ */
+function sq_substr($string,$start,$length=NULL,$charset='auto') {
+
+ // if $length is NULL, use the full string length...
+ // we have to do this to mimick the use of substr()
+ // where $length is not given
+ //
+ if (is_null($length))
+ $length = sq_strlen($length);
+
+
+ // use automatic charset detection, if function call asks for it
+ static $charset_auto, $bUse_mb;
+
+ if ($charset=='auto') {
+ if (!isset($charset_auto)) {
+ global $default_charset, $squirrelmail_language;
+ set_my_charset();
+ $charset=$default_charset;
+ if ($squirrelmail_language=='ja_JP') $charset='euc-jp';
+ $charset_auto = $charset;
+ } else {
+ $charset = $charset_auto;
+ }
+ }
+ $charset = strtolower($charset);
+
+ // in_array call is expensive => do it once and use a static var for
+ // storing the results
+ if (!isset($bUse_mb)) {
+ if (in_array($charset,sq_mb_list_encodings())) {
+ $bUse_mb = true;
+ } else {
+ $bUse_mb = false;
+ }
+ }
+
+ if ($bUse_mb) {
+ return mb_substr($string,$start,$length,$charset);
+ }
+ // TODO: add mbstring independent code
+
+ // use vanilla string functions as last option
+ return substr($string,$start,$length);
+}
+
+/**
+ * This is a replacement for PHP's substr_replace() that is
+ * multibyte-aware.
+ *
+ * @param string $string The string to operate upon
+ * @param string $replacement The string to be inserted
+ * @param int $start The offset at which to begin substring replacement
+ * @param int $length The number of characters after $start to remove
+ * NOTE that if you need to specify a charset but
+ * want to achieve normal substr_replace() behavior
+ * where $length is not specified, use NULL (OPTIONAL;
+ * default from $start to end of string)
+ * @param string $charset The charset of the given string. A value of NULL
+ * here will force the use of PHP's standard substr().
+ * (OPTIONAL; default is "auto", which indicates that
+ * the user's current charset should be used).
+ *
+ * @return string The manipulated string
+ *
+ * Of course, you can use more advanced (e.g., negative) values
+ * for $start and $length as needed - see the PHP manual for more
+ * information: http://www.php.net/manual/function.substr-replace.php
+ *
+ */
+function sq_substr_replace($string, $replacement, $start, $length=NULL,
+ $charset='auto')
+{
+
+ // NULL charset? Just use substr_replace()
+ //
+ if (is_null($charset))
+ return is_null($length) ? substr_replace($string, $replacement, $start)
+ : substr_replace($string, $replacement, $start, $length);
+
+
+ // use current character set?
+ //
+ if ($charset == 'auto')
+ {
+//FIXME: is there any reason why this cannot be a global flag used by all string wrapper functions?
+ static $auto_charset;
+ if (!isset($auto_charset))
+ {
+ global $default_charset;
+//FIXME - do we need this?
+global $squirrelmail_language;
+ set_my_charset();
+ $auto_charset = $default_charset;
+//FIXME - do we need this?
+if ($squirrelmail_language == 'ja_JP') $auto_charset = 'euc-jp';
+ }
+ $charset = $auto_charset;
+ }
+
+
+ // standardize character set name
+ //
+ $charset = strtolower($charset);
+
+
+/* ===== FIXME: this list is not used in 1.5.x, but if we need it, unless this differs between all our string function wrappers, we should store this info in the session
+ // only use mbstring with the following character sets
+ //
+ $sq_substr_replace_mb_charsets = array(
+ 'utf-8',
+ 'big5',
+ 'gb2312',
+ 'gb18030',
+ 'euc-jp',
+ 'euc-cn',
+ 'euc-tw',
+ 'euc-kr'
+ );
+
+
+ // now we can use our own implementation using
+ // mb_substr() and mb_strlen() if needed
+ //
+ if (in_array($charset, $sq_substr_replace_mb_charsets)
+ && in_array($charset, sq_mb_list_encodings()))
+===== */
+//FIXME: is there any reason why this cannot be a global array used by all string wrapper functions?
+ if (in_array($charset, sq_mb_list_encodings()))
+ {
+
+ $string_length = mb_strlen($string, $charset);
+
+ if ($start < 0)
+ $start = max(0, $string_length + $start);
+
+ else if ($start > $string_length)
+ $start = $string_length;
+
+ if ($length < 0)
+ $length = max(0, $string_length - $start + $length);
+
+ else if (is_null($length) || $length > $string_length)
+ $length = $string_length;
+
+ if ($start + $length > $string_length)
+ $length = $string_length - $start;
+
+ return mb_substr($string, 0, $start, $charset)
+ . $replacement
+ . mb_substr($string,
+ $start + $length,
+ $string_length, // FIXME: I can't see why this is needed: - $start - $length,
+ $charset);
+
+ }
+
+
+ // else use normal substr_replace()
+ //
+ return is_null($length) ? substr_replace($string, $replacement, $start)
+ : substr_replace($string, $replacement, $start, $length);
+
+}
+
+/**
+ * Wrapper that is used to switch between vanilla and multibyte strpos
+ * functions.
+ * @param string $haystack
+ * @param mixed $needle
+ * @param integer $offset
+ * @param string $charset
+ * @return string
+ * @since 1.5.1
+ * @link http://www.php.net/strpos
+ * @link http://www.php.net/mb_strpos
+ */
+function sq_strpos($haystack,$needle,$offset,$charset='auto') {
+ // use automatic charset detection, if function call asks for it
+ static $charset_auto, $bUse_mb;
+
+ if ($charset=='auto') {
+ if (!isset($charset_auto)) {
+ global $default_charset, $squirrelmail_language;
+ set_my_charset();
+ $charset=$default_charset;
+ if ($squirrelmail_language=='ja_JP') $charset='euc-jp';
+ $charset_auto = $charset;
+ } else {
+ $charset = $charset_auto;
+ }
+ }
+ $charset = strtolower($charset);
+
+ // in_array call is expensive => do it once and use a static var for
+ // storing the results
+ if (!isset($bUse_mb)) {
+ if (in_array($charset,sq_mb_list_encodings())) {
+ $bUse_mb = true;