Merge branch 'master' of github.com:roundcube/roundcubemail
1 files added
31 files modified
| | |
| | | CHANGELOG Roundcube Webmail |
| | | =========================== |
| | | |
| | | - List related text/html part as attachment in plain text mode (#1488677) |
| | | - Use IMAP BINARY (RFC3516) extension to fetch message/part bodies |
| | | - Fix folder creation under public namespace root (#1488665) |
| | | - Fix so "Edit as new" on draft creates a new message (#1488687) |
| | | - Fix invalid error message on deleting mail from read only folder (#1488694) |
| | | - Fix error where session wasn't updated after folder rename/delete (#1488692) |
| | | - Replace data URIs of images (pasted in HTML editor) with inline attachments (#1488502) |
| | | - Fix PLAIN authentication for some IMAP servers (#1488674) |
| | | - Fix encoding vCard file when contains PHOTO;ENCODING=b (#1488683) |
| | | - Fix focus issue in IE when selecting message row (#1488620) |
| | | - Remove (too big) min-width on mail screen |
| | | - Add full headers view in message preview window (#1488538) |
| | |
| | | // TCP port used for IMAP connections |
| | | $rcmail_config['default_port'] = 143; |
| | | |
| | | // IMAP AUTH type (DIGEST-MD5, CRAM-MD5, LOGIN, PLAIN or empty to use |
| | | // IMAP AUTH type (DIGEST-MD5, CRAM-MD5, LOGIN, PLAIN or null to use |
| | | // best server supported one) |
| | | $rcmail_config['imap_auth_type'] = null; |
| | | |
| | |
| | | 'suhosin.session.encrypt' => 0, |
| | | 'magic_quotes_runtime' => 0, |
| | | 'magic_quotes_sybase' => 0, |
| | | 'date.timezone' => '-NOTEMPTY-', |
| | | ); |
| | | |
| | | $optional_checks = array( |
| | | // required for utils/modcss.inc, should we require this? |
| | | 'allow_url_fopen' => 1, |
| | | 'date.timezone' => '-NOTEMPTY-', |
| | | ); |
| | | |
| | | $source_urls = array( |
| | |
| | | $status = ini_get($var); |
| | | if ($val === '-NOTEMPTY-') { |
| | | if (empty($status)) { |
| | | $RCI->fail($var, "cannot be empty and needs to be set"); |
| | | $RCI->fail($var, "empty value detected"); |
| | | } else if ($var == 'date.timezone') { |
| | | try { |
| | | $tz = new DateTimeZone($status); |
| | | $RCI->pass($var); |
| | | } |
| | | catch (Exception $e) { |
| | | $RCI->fail($var, "invalid value detected: $status"); |
| | | } |
| | | } else { |
| | | $RCI->pass($var); |
| | | } |
| | |
| | | // The query can contain the following macros that will be expanded as follows: |
| | | // %p is replaced with the plaintext new password |
| | | // %c is replaced with the crypt version of the new password, MD5 if available |
| | | // otherwise DES. |
| | | // otherwise DES. More hash function can be enabled using the password_crypt_hash |
| | | // configuration parameter. |
| | | // %D is replaced with the dovecotpw-crypted version of the new password |
| | | // %o is replaced with the password before the change |
| | | // %n is replaced with the hashed version of the new password |
| | |
| | | // Default: "SELECT update_passwd(%c, %u)" |
| | | $rcmail_config['password_query'] = 'SELECT update_passwd(%c, %u)'; |
| | | |
| | | // By default the crypt() function which is used to create the '%c' |
| | | // parameter uses the md5 algorithm. To use different algorithms |
| | | // you can choose between: des, md5, blowfish, sha256, sha512. |
| | | // Before using other hash functions than des or md5 please make sure |
| | | // your operating system supports the other hash functions. |
| | | $rcmail_config['password_crypt_hash'] = 'md5'; |
| | | |
| | | // By default domains in variables are using unicode. |
| | | // Enable this option to use punycoded names |
| | | $rcmail_config['password_idn_ascii'] = false; |
| | |
| | | // crypted password |
| | | if (strpos($sql, '%c') !== FALSE) { |
| | | $salt = ''; |
| | | if (CRYPT_MD5) { |
| | | // Always use eight salt characters for MD5 (#1488136) |
| | | |
| | | if (!($crypt_hash = $rcmail->config->get('password_crypt_hash'))) |
| | | { |
| | | if (CRYPT_MD5) |
| | | $crypt_hash = 'md5'; |
| | | else if (CRYPT_STD_DES) |
| | | $crypt_hash = 'des'; |
| | | } |
| | | |
| | | switch ($crypt_hash) |
| | | { |
| | | case 'md5': |
| | | $len = 8; |
| | | } else if (CRYPT_STD_DES) { |
| | | $salt_hashindicator = '$1$'; |
| | | break; |
| | | case 'des': |
| | | $len = 2; |
| | | } else { |
| | | break; |
| | | case 'blowfish': |
| | | $len = 22; |
| | | $salt_hashindicator = '$2a$'; |
| | | break; |
| | | case 'sha256': |
| | | $len = 16; |
| | | $salt_hashindicator = '$5$'; |
| | | break; |
| | | case 'sha512': |
| | | $len = 16; |
| | | $salt_hashindicator = '$6$'; |
| | | break; |
| | | default: |
| | | return PASSWORD_CRYPT_ERROR; |
| | | } |
| | | |
| | |
| | | $salt .= $seedchars[rand(0, 63)]; |
| | | } |
| | | |
| | | $sql = str_replace('%c', $db->quote(crypt($passwd, CRYPT_MD5 ? '$1$'.$salt.'$' : $salt)), $sql); |
| | | $sql = str_replace('%c', $db->quote(crypt($passwd, $salt_hashindicator ? $salt_hashindicator .$salt.'$' : $salt)), $sql); |
| | | } |
| | | |
| | | // dovecotpw |
| | |
| | | $err_code = $this->storage->get_error_code(); |
| | | $res_code = $this->storage->get_response_code(); |
| | | |
| | | if ($err_code < 0) { |
| | | $this->output->show_message('storageerror', 'error'); |
| | | } |
| | | else if ($res_code == rcube_storage::NOPERM) { |
| | | if ($res_code == rcube_storage::NOPERM) { |
| | | $this->output->show_message('errornoperm', 'error'); |
| | | } |
| | | else if ($res_code == rcube_storage::READONLY) { |
| | |
| | | $this->output->show_message('servererrormsg', 'error', array('msg' => $err_str)); |
| | | } |
| | | } |
| | | else if ($err_code < 0) { |
| | | $this->output->show_message('storageerror', 'error'); |
| | | } |
| | | else if ($fallback) { |
| | | $this->output->show_message($fallback, 'error', $fallback_args); |
| | | } |
| | |
| | | * Sometimes charset string is malformed, there are also charset aliases |
| | | * but we need strict names for charset conversion (specially utf8 class) |
| | | * |
| | | * @param string Input charset name |
| | | * @param string $input Input charset name |
| | | * |
| | | * @return string The validated charset name |
| | | */ |
| | |
| | | { |
| | | static $iconv_options = null; |
| | | static $mbstring_list = null; |
| | | static $mbstring_sch = null; |
| | | static $conv = null; |
| | | |
| | | $to = empty($to) ? strtoupper(RCMAIL_CHARSET) : self::parse_charset($to); |
| | | $to = empty($to) ? strtoupper(RCMAIL_CHARSET) : $to; |
| | | $from = self::parse_charset($from); |
| | | |
| | | // It is a common case when UTF-16 charset is used with US-ASCII content (#1488654) |
| | |
| | | |
| | | if ($mbstring_list === null) { |
| | | if (extension_loaded('mbstring')) { |
| | | $mbstring_sch = mb_substitute_character(); |
| | | $mbstring_list = mb_list_encodings(); |
| | | $mbstring_list = array_map('strtoupper', $mbstring_list); |
| | | } |
| | |
| | | // convert charset using mbstring module |
| | | if ($mbstring_list !== null) { |
| | | $aliases['WINDOWS-1257'] = 'ISO-8859-13'; |
| | | // it happens that mbstring supports ASCII but not US-ASCII |
| | | if (($from == 'US-ASCII' || $to == 'US-ASCII') && !in_array('US-ASCII', $mbstring_list)) { |
| | | $aliases['US-ASCII'] = 'ASCII'; |
| | | } |
| | | |
| | | $mb_from = $aliases[$from] ? $aliases[$from] : $from; |
| | | $mb_to = $aliases[$to] ? $aliases[$to] : $to; |
| | | |
| | | // return if encoding found, string matches encoding and convert succeeded |
| | | if (in_array($mb_from, $mbstring_list) && in_array($mb_to, $mbstring_list)) { |
| | | if (mb_check_encoding($str, $mb_from) && ($out = mb_convert_encoding($str, $mb_to, $mb_from))) { |
| | | if (mb_check_encoding($str, $mb_from)) { |
| | | // Do the same as //IGNORE with iconv |
| | | mb_substitute_character('none'); |
| | | $out = mb_convert_encoding($str, $mb_to, $mb_from); |
| | | mb_substitute_character($mbstring_sch); |
| | | |
| | | if ($out !== false) { |
| | | return $out; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // convert charset using bundled classes/functions |
| | | if ($to == 'UTF-8') { |
| | |
| | | $criteria = 'UNDELETED '.$criteria; |
| | | } |
| | | |
| | | // unset CHARSET if criteria string is ASCII, this way |
| | | // SEARCH won't be re-sent after "unsupported charset" response |
| | | if ($charset && $charset != 'US-ASCII' && is_ascii($criteria)) { |
| | | $charset = 'US-ASCII'; |
| | | } |
| | | |
| | | if ($this->threading) { |
| | | $threads = $this->conn->thread($folder, $this->threading, $criteria, true, $charset); |
| | | |
| | |
| | | } |
| | | |
| | | $messages = $this->conn->search($folder, |
| | | ($charset ? "CHARSET $charset " : '') . $criteria, true); |
| | | ($charset && $charset != 'US-ASCII' ? "CHARSET $charset " : '') . $criteria, true); |
| | | |
| | | // Error, try with US-ASCII (some servers may support only US-ASCII) |
| | | if ($messages->is_error() && $charset && $charset != 'US-ASCII') { |
| | |
| | | } |
| | | |
| | | // Get folder rights (MYRIGHTS) |
| | | if ($acl && !$options['noselect']) { |
| | | // skip shared roots |
| | | if (!$options['is_root'] || $options['namespace'] == 'personal') { |
| | | $options['rights'] = (array)$this->my_rights($folder); |
| | | } |
| | | if ($acl && ($rights = $this->my_rights($folder))) { |
| | | $options['rights'] = $rights; |
| | | } |
| | | |
| | | // Set 'norename' flag |
| | |
| | | } |
| | | else { |
| | | $authc = $user; |
| | | $user = ''; |
| | | } |
| | | $auth_sasl = Auth_SASL::factory('digestmd5'); |
| | | $reply = base64_encode($auth_sasl->getResponse($authc, $pass, |
| | |
| | | } |
| | | else { |
| | | $authc = $user; |
| | | $user = ''; |
| | | } |
| | | |
| | | $reply = base64_encode($user . chr(0) . $authc . chr(0) . $pass); |
| | |
| | | $mode = 0; |
| | | } |
| | | |
| | | // Use BINARY extension when possible (and safe) |
| | | $binary = $mode && preg_match('/^[0-9.]+$/', $part) && $this->hasCapability('BINARY'); |
| | | $fetch_mode = $binary ? 'BINARY' : 'BODY'; |
| | | |
| | | // format request |
| | | $reply_key = '* ' . $id; |
| | | $key = $this->nextTag(); |
| | | $request = $key . ($is_uid ? ' UID' : '') . " FETCH $id (BODY.PEEK[$part])"; |
| | | $request = $key . ($is_uid ? ' UID' : '') . " FETCH $id ($fetch_mode.PEEK[$part])"; |
| | | |
| | | // send request |
| | | if (!$this->putLine($request)) { |
| | | $this->setError(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | return false; |
| | | } |
| | | |
| | | if ($binary) { |
| | | $mode = -1; |
| | | } |
| | | |
| | | // receive reply line |
| | |
| | | $prev = ''; |
| | | |
| | | while ($bytes > 0) { |
| | | $line = $this->readLine(4096); |
| | | $line = $this->readLine(8192); |
| | | |
| | | if ($line === NULL) { |
| | | break; |
| | |
| | | } |
| | | |
| | | // list as attachment as well |
| | | if (!empty($mail_part->filename)) |
| | | if (!empty($mail_part->filename)) { |
| | | $this->attachments[] = $mail_part; |
| | | } |
| | | // list html part as attachment (here the part is most likely inside a multipart/related part) |
| | | else if ($this->parse_alternative && ($secondary_type == 'html' && !$this->opt['prefer_html'])) { |
| | | $this->attachments[] = $mail_part; |
| | | } |
| | | } |
| | | // part message/* |
| | | else if ($primary_type == 'message') { |
| | | $this->parse_structure($mail_part, true); |
| | |
| | | for ($i=0, $len=count($data); $i<$len; $i++) { |
| | | $data_item = &$data[$i]; |
| | | if (preg_match('/^ SORT/i', $data_item)) { |
| | | // valid response, initialize raw_data for is_error() |
| | | $this->raw_data = ''; |
| | | $data_item = substr($data_item, 5); |
| | | break; |
| | | } |
| | | else if (preg_match('/^ (E?SEARCH)/i', $data_item, $m)) { |
| | | // valid response, initialize raw_data for is_error() |
| | | $this->raw_data = ''; |
| | | $data_item = substr($data_item, strlen($m[0])); |
| | | |
| | | if (strtoupper($m[1]) == 'ESEARCH') { |
| | |
| | | // ...skip unilateral untagged server responses |
| | | for ($i=0, $len=count($data); $i<$len; $i++) { |
| | | if (preg_match('/^ THREAD/i', $data[$i])) { |
| | | // valid response, initialize raw_data for is_error() |
| | | $this->raw_data = ''; |
| | | $data[$i] = substr($data[$i], 7); |
| | | break; |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * Check if a string contains only ascii characters |
| | | * |
| | | * @param string $str String to check |
| | | * @param bool $control_chars Includes control characters |
| | | * |
| | | * @return bool |
| | | */ |
| | | function is_ascii($str, $control_chars = true) |
| | | { |
| | | $regexp = $control_chars ? '/[^\x00-\x7F]/' : '/[^\x20-\x7E]/'; |
| | | return preg_match($regexp, $str) ? false : true; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Remove single and double quotes from a given string |
| | | * |
| | | * @param string Input value |
| | |
| | | static $js_rep_table = false; |
| | | static $xml_rep_table = false; |
| | | |
| | | if (!is_string($str)) { |
| | | $str = strval($str); |
| | | } |
| | | |
| | | // encode for HTML output |
| | | if ($enctype == 'html') { |
| | | if (!$html_encode_arr) { |
| | |
| | | if ((list($key, $value) = explode('=', $attr)) && $value) { |
| | | $value = trim($value); |
| | | if ($key == 'ENCODING') { |
| | | $value = strtoupper($value); |
| | | // add next line(s) to value string if QP line end detected |
| | | if ($value == 'QUOTED-PRINTABLE') { |
| | | while (preg_match('/=$/', $lines[$i])) |
| | |
| | | this.load_identity(props, 'edit-identity'); |
| | | else if (this.task == 'mail' && (cid = this.get_single_uid())) { |
| | | url = { _mbox: this.env.mailbox }; |
| | | url[this.env.mailbox == this.env.drafts_mailbox ? '_draft_uid' : '_uid'] = cid; |
| | | url[this.env.mailbox == this.env.drafts_mailbox && props != 'new' ? '_draft_uid' : '_uid'] = cid; |
| | | this.goto_url('compose', url, true); |
| | | } |
| | | break; |
| | |
| | | |
| | | $record = rcmail_get_edit_record(); |
| | | |
| | | // add some labels to client |
| | | $RCMAIL->output->add_label('noemailwarning', 'nonamewarning'); |
| | | |
| | | // copy (parsed) address template to client |
| | | if (preg_match_all('/\{([a-z0-9]+)\}([^{]*)/i', $RCMAIL->config->get('address_template', ''), $templ, PREG_SET_ORDER)) |
| | | $RCMAIL->output->set_env('address_template', $templ); |
| | |
| | | $IMPORT_STATS->names = array(); |
| | | $IMPORT_STATS->skipped_names = array(); |
| | | $IMPORT_STATS->count = count($vcards); |
| | | $IMPORT_STATS->inserted = $IMPORT_STATS->skipped = $IMPORT_STATS->nomail = $IMPORT_STATS->errors = 0; |
| | | $IMPORT_STATS->inserted = $IMPORT_STATS->skipped = $IMPORT_STATS->invalid = $IMPORT_STATS->errors = 0; |
| | | |
| | | if ($replace) { |
| | | $CONTACTS->delete_all(); |
| | | } |
| | | |
| | | foreach ($vcards as $vcard) { |
| | | $email = $vcard->email[0]; |
| | | $a_record = $vcard->get_assoc(); |
| | | |
| | | // skip entries without an e-mail address or invalid |
| | | if (empty($email) || !$CONTACTS->validate($a_record, true)) { |
| | | $IMPORT_STATS->nomail++; |
| | | // skip invalid (incomplete) entries |
| | | if (!$CONTACTS->validate($a_record, true)) { |
| | | $IMPORT_STATS->invalid++; |
| | | continue; |
| | | } |
| | | |
| | | // We're using UTF8 internally |
| | | $email = $vcard->email[0]; |
| | | $email = rcube_idn_to_utf8($email); |
| | | |
| | | if (!$replace && $email) { |
| | | if (!$replace) { |
| | | $existing = null; |
| | | // compare e-mail address |
| | | if ($email) { |
| | | $existing = $CONTACTS->search('email', $email, 1, false); |
| | | if (!$existing->count && $vcard->displayname) { // compare display name |
| | | } |
| | | // compare display name if email not found |
| | | if ((!$existing || !$existing->count) && $vcard->displayname) { |
| | | $existing = $CONTACTS->search('name', $vcard->displayname, 1, false); |
| | | } |
| | | if ($existing->count) { |
| | | if ($existing && $existing->count) { |
| | | $IMPORT_STATS->skipped++; |
| | | $IMPORT_STATS->skipped_names[] = $vcard->displayname ? $vcard->displayname : $email; |
| | | continue; |
| | |
| | | $source = $orig_source; |
| | | |
| | | // show notice if existing contacts with same e-mail are found |
| | | $existing = false; |
| | | foreach ($CONTACTS->get_col_values('email', $a_record, true) as $email) { |
| | | if ($email && ($res = $CONTACTS->search('email', $email, 1, false, true)) && $res->count) { |
| | | $OUTPUT->show_message('contactexists', 'notice', null, false); |
| | |
| | | |
| | | function rcmail_write_compose_attachments(&$message, $bodyIsHtml) |
| | | { |
| | | global $RCMAIL, $COMPOSE; |
| | | global $RCMAIL, $COMPOSE, $compose_mode; |
| | | |
| | | $cid_map = $messages = array(); |
| | | foreach ((array)$message->mime_parts as $pid => $part) |
| | | { |
| | | if (($part->ctype_primary != 'message' || !$bodyIsHtml) && $part->ctype_primary != 'multipart' && |
| | | ($part->disposition == 'attachment' || ($part->disposition == 'inline' && $bodyIsHtml) || $part->filename) |
| | | && $part->mimetype != 'application/ms-tnef' |
| | | ) { |
| | | if ($part->disposition == 'attachment' || ($part->disposition == 'inline' && $bodyIsHtml) || $part->filename) { |
| | | if ($part->ctype_primary == 'message' || $part->ctype_primary == 'multipart') { |
| | | continue; |
| | | } |
| | | if ($part->mimetype == 'application/ms-tnef') { |
| | | continue; |
| | | } |
| | | // skip inline images when forwarding in plain text |
| | | if ($part->content_id && !$bodyIsHtml && $compose_mode == RCUBE_COMPOSE_FORWARD) { |
| | | continue; |
| | | } |
| | | |
| | | $skip = false; |
| | | if ($part->mimetype == 'message/rfc822') { |
| | | $messages[] = $part->mime_id; |
| | |
| | | if (!empty($subject)) { |
| | | $search_str .= str_repeat(' OR', count($subject)-1); |
| | | foreach ($subject as $sub) |
| | | $search_str .= sprintf(" %s {%d}\r\n%s", $sub, strlen($search), $search); |
| | | $search_str .= ' ' . $sub . ' ' . rcube_imap_generic::escape($search); |
| | | } |
| | | |
| | | $search_str = trim($search_str); |
| | |
| | | * to this: |
| | | * |
| | | * <img src="/path/on/server/.../tiny_mce/plugins/emotions/images/smiley-cool.gif" border="0" alt="Cool" title="Cool" /> |
| | | * ... |
| | | */ |
| | | function rcmail_fix_emoticon_paths(&$mime_message) |
| | | function rcmail_fix_emoticon_paths($mime_message) |
| | | { |
| | | global $CONFIG; |
| | | |
| | |
| | | } |
| | | |
| | | $mime_message->setHTMLBody($body); |
| | | } |
| | | |
| | | return $body; |
| | | /** |
| | | * Extract image attachments from HTML content (data URIs) |
| | | */ |
| | | function rcmail_extract_inline_images($mime_message, $from) |
| | | { |
| | | $body = $mime_message->getHTMLBody(); |
| | | $offset = 0; |
| | | $list = array(); |
| | | $regexp = '# src=[\'"](data:(image/[a-z]+);base64,([a-z0-9+/=\r\n]+))([\'"])#i'; |
| | | |
| | | // get domain for the Content-ID, must be the same as in Mail_Mime::get() |
| | | if (preg_match('#@([0-9a-zA-Z\-\.]+)#', $from, $matches)) { |
| | | $domain = $matches[1]; |
| | | } else { |
| | | $domain = 'localhost'; |
| | | } |
| | | |
| | | if (preg_match_all($regexp, $body, $matches, PREG_OFFSET_CAPTURE)) { |
| | | foreach ($matches[1] as $idx => $m) { |
| | | $data = preg_replace('/\r\n/', '', $matches[3][$idx][0]); |
| | | $data = base64_decode($data); |
| | | |
| | | if (empty($data)) { |
| | | continue; |
| | | } |
| | | |
| | | $hash = md5($data) . '@' . $domain; |
| | | $mime_type = $matches[2][$idx][0]; |
| | | $name = $list[$hash]; |
| | | |
| | | // add the image to the MIME message |
| | | if (!$name) { |
| | | $ext = preg_replace('#^[^/]+/#', '', $mime_type); |
| | | $name = substr($hash, 0, 8) . '.' . $ext; |
| | | $list[$hash] = $name; |
| | | |
| | | $mime_message->addHTMLImage($data, $mime_type, $name, false, $hash); |
| | | } |
| | | |
| | | $body = substr_replace($body, $name, $m[1] + $offset, strlen($m[0])); |
| | | $offset += strlen($name) - strlen($m[0]); |
| | | } |
| | | } |
| | | |
| | | $mime_message->setHTMLBody($body); |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | // look for "emoticon" images from TinyMCE and change their src paths to |
| | | // be file paths on the server instead of URL paths. |
| | | $message_body = rcmail_fix_emoticon_paths($MAIL_MIME); |
| | | rcmail_fix_emoticon_paths($MAIL_MIME); |
| | | |
| | | // Extract image Data URIs into message attachments (#1488502) |
| | | rcmail_extract_inline_images($MAIL_MIME, $from); |
| | | } |
| | | else { |
| | | $plugin = $RCMAIL->plugins->exec_hook('message_outgoing_body', |
| | |
| | | else { |
| | | $deleted = $plugin['result']; |
| | | } |
| | | |
| | | // #1488692: update session |
| | | if ($deleted && $_SESSION['mbox'] === $mbox) { |
| | | $RCMAIL->session->remove('mbox'); |
| | | } |
| | | } |
| | | |
| | | if ($OUTPUT->ajax_call && $deleted) { |
| | |
| | | } |
| | | $RCMAIL->user->save_prefs(array('message_threading' => $a_threaded)); |
| | | |
| | | // #1488692: update session |
| | | if ($_SESSION['mbox'] === $oldname) { |
| | | $_SESSION['mbox'] = $newname; |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | |
| | | <?php |
| | | |
| | | /* |
| | | /** |
| | | +-----------------------------------------------------------------------+ |
| | | | program/steps/settings/save_folder.inc | |
| | | | | |
| | | | This file is part of the Roundcube Webmail client | |
| | | | Copyright (C) 2005-2009, The Roundcube Dev Team | |
| | | | Copyright (C) 2005-2012, The Roundcube Dev Team | |
| | | | | |
| | | | Licensed under the GNU General Public License version 3 or | |
| | | | any later version with exceptions for skins & plugins. | |
| | |
| | | } |
| | | |
| | | $OUTPUT->show_message('folderupdated', 'confirmation'); |
| | | |
| | | if ($rename) { |
| | | // #1488692: update session |
| | | if ($_SESSION['mbox'] === $folder['oldname']) { |
| | | $_SESSION['mbox'] = $folder['name']; |
| | | } |
| | | rcmail_update_folder_row($folder['name'], $folder['oldname'], $folder['subscribe'], $folder['class']); |
| | | $OUTPUT->send('iframe'); |
| | | } |
| | |
| | | <ul class="toolbarmenu"> |
| | | <li><roundcube:button class="printlink" command="print" label="printmessage" classAct="printlink active" /></li> |
| | | <li><roundcube:button class="downloadlink" command="download" label="emlsave" classAct="downloadlink active" /></li> |
| | | <li><roundcube:button class="editlink" command="edit" label="editasnew" classAct="editlink active" /></li> |
| | | <li><roundcube:button class="editlink" command="edit" prop="new" label="editasnew" classAct="editlink active" /></li> |
| | | <li class="separator_below"><roundcube:button class="sourcelink" command="viewsource" label="viewsource" classAct="sourcelink active" /></li> |
| | | <li><roundcube:button class="openlink" command="open" label="openinextwin" target="_blank" classAct="openlink active" /></li> |
| | | <roundcube:container name="messagemenu" id="messagemenu" /> |
| | |
| | | <ul class="toolbarmenu iconized"> |
| | | <li><roundcube:button command="print" label="printmessage" class="icon" classAct="icon active" innerclass="icon print" /></li> |
| | | <li><roundcube:button command="download" label="emlsave" class="icon" classAct="icon active" innerclass="icon download" /></li> |
| | | <li><roundcube:button command="edit" label="editasnew" class="icon" classAct="icon active" innerclass="icon edit" /></li> |
| | | <li><roundcube:button command="edit" prop="new" label="editasnew" class="icon" classAct="icon active" innerclass="icon edit" /></li> |
| | | <li><roundcube:button command="viewsource" label="viewsource" class="icon" classAct="icon active" innerclass="icon viewsource" /></li> |
| | | <li><roundcube:button command="open" label="openinextwin" target="_blank" class="icon" classAct="icon active" innerclass="icon extwin" /></li> |
| | | <roundcube:container name="messagemenu" id="messagemenu" /> |
| | |
| | | #messageheader { |
| | | position: relative; |
| | | height: auto; |
| | | min-height: 52px; |
| | | margin: 0 8px 0 0; |
| | | padding: 0 0 0 72px; |
| | | border-bottom: 2px solid #f0f0f0; |
| | |
| | | new rcube_splitter({ id:'identviewsplitter', p1:'#identitieslist', p2:'#identity-details', |
| | | orientation:'v', relative:true, start:266, min:180, size:12 }).init(); |
| | | } |
| | | else if (rcmail.env.action == 'preferences') { |
| | | else if (rcmail.env.action == 'preferences' || !rcmail.env.action) { |
| | | new rcube_splitter({ id:'prefviewsplitter', p1:'#sectionslist', p2:'#preferences-box', |
| | | orientation:'v', relative:true, start:266, min:180, size:12 }).init(); |
| | | } |
| | |
| | | function data_clean() |
| | | { |
| | | return array( |
| | | array('', '', 'Empty string'), |
| | | array('', ''), |
| | | array("\xC1", ''), |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * @dataProvider data_clean |
| | | */ |
| | | function test_clean($input, $output, $title) |
| | | function test_clean($input, $output) |
| | | { |
| | | $this->assertEquals(rcube_charset::clean($input), $output, $title); |
| | | $this->assertEquals($output, rcube_charset::clean($input)); |
| | | } |
| | | |
| | | /** |
| | | * Data for test_parse_charset() |
| | | */ |
| | | function data_parse_charset() |
| | | { |
| | | return array( |
| | | array('UTF8', 'UTF-8'), |
| | | array('WIN1250', 'WINDOWS-1250'), |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * @dataProvider data_parse_charset |
| | | */ |
| | | function test_parse_charset($input, $output) |
| | | { |
| | | $this->assertEquals($output, rcube_charset::parse_charset($input)); |
| | | } |
| | | |
| | | /** |
| | | * Data for test_convert() |
| | | */ |
| | | function data_convert() |
| | | { |
| | | return array( |
| | | array('ö', 'ö', 'UTF-8', 'UTF-8'), |
| | | array('ö', '', 'UTF-8', 'US-ASCII'), |
| | | array('aż', 'a', 'UTF-8', 'US-ASCII'), |
| | | array('&BCAEMARBBEEESwQ7BDoEOA-', 'Рассылки', 'UTF7-IMAP', 'UTF-8'), |
| | | array('Рассылки', '&BCAEMARBBEEESwQ7BDoEOA-', 'UTF-8', 'UTF7-IMAP'), |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * @dataProvider data_convert |
| | | */ |
| | | function test_convert($input, $output, $from, $to) |
| | | { |
| | | $this->assertEquals($output, rcube_charset::convert($input, $from, $to)); |
| | | } |
| | | |
| | | /** |
| | | * Data for test_utf7_to_utf8() |
| | | */ |
| | | function data_utf7_to_utf8() |
| | | { |
| | | return array( |
| | | array('+BCAEMARBBEEESwQ7BDoEOA-', 'Рассылки'), |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * @dataProvider data_utf7_to_utf8 |
| | | */ |
| | | function test_utf7_to_utf8($input, $output) |
| | | { |
| | | $this->assertEquals($output, rcube_charset::utf7_to_utf8($input)); |
| | | } |
| | | |
| | | /** |
| | | * Data for test_utf7imap_to_utf8() |
| | | */ |
| | | function data_utf7imap_to_utf8() |
| | | { |
| | | return array( |
| | | array('&BCAEMARBBEEESwQ7BDoEOA-', 'Рассылки'), |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * @dataProvider data_utf7imap_to_utf8 |
| | | */ |
| | | function test_utf7imap_to_utf8($input, $output) |
| | | { |
| | | $this->assertEquals($output, rcube_charset::utf7imap_to_utf8($input)); |
| | | } |
| | | |
| | | /** |
| | | * Data for test_utf8_to_utf7imap() |
| | | */ |
| | | function data_utf8_to_utf7imap() |
| | | { |
| | | return array( |
| | | array('Рассылки', '&BCAEMARBBEEESwQ7BDoEOA-'), |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * @dataProvider data_utf8_to_utf7imap |
| | | */ |
| | | function test_utf8_to_utf7imap($input, $output) |
| | | { |
| | | $this->assertEquals($output, rcube_charset::utf8_to_utf7imap($input)); |
| | | } |
| | | |
| | | /** |
| | | * Data for test_utf16_to_utf8() |
| | | */ |
| | | function data_utf16_to_utf8() |
| | | { |
| | | return array( |
| | | array(base64_decode('BCAEMARBBEEESwQ7BDoEOA=='), 'Рассылки'), |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * @dataProvider data_utf16_to_utf8 |
| | | */ |
| | | function test_utf16_to_utf8($input, $output) |
| | | { |
| | | $this->assertEquals($output, rcube_charset::utf16_to_utf8($input)); |
| | | } |
| | | |
| | | /** |
| | | * Data for test_detect() |
| | | */ |
| | | function data_detect() |
| | | { |
| | | return array( |
| | | array('', '', 'UTF-8'), |
| | | array('a', 'UTF-8', 'UTF-8'), |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * @dataProvider data_detect |
| | | */ |
| | | function test_detect($input, $fallback, $output) |
| | | { |
| | | $this->assertEquals($output, rcube_charset::detect($input, $fallback)); |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | } |
| | | |
| | | /** |
| | | * rcube_shared.inc: is_ascii() |
| | | */ |
| | | function test_is_ascii() |
| | | { |
| | | $result = is_ascii("0123456789"); |
| | | $this->assertTrue($result, "Valid ASCII (numbers)"); |
| | | |
| | | $result = is_ascii("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); |
| | | $this->assertTrue($result, "Valid ASCII (letters)"); |
| | | |
| | | $result = is_ascii(" !\"#\$%&'()*+,-./:;<=>?@[\\^_`{|}~"); |
| | | $this->assertTrue($result, "Valid ASCII (special characters)"); |
| | | |
| | | $result = is_ascii("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" |
| | | ."\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"); |
| | | $this->assertTrue($result, "Valid ASCII (control characters)"); |
| | | |
| | | $result = is_ascii("\n", false); |
| | | $this->assertFalse($result, "Valid ASCII (control characters)"); |
| | | |
| | | $result = is_ascii("ż"); |
| | | $this->assertFalse($result, "Invalid ASCII (UTF-8 character)"); |
| | | |
| | | $result = is_ascii("ż", false); |
| | | $this->assertFalse($result, "Invalid ASCII (UTF-8 character [2])"); |
| | | } |
| | | |
| | | } |
| | |
| | | $this->assertEquals("Iksiñski", $vcards2[0]->surname, "Detect charset in encoded values"); |
| | | } |
| | | |
| | | function test_import_photo_encoding() |
| | | { |
| | | $input = file_get_contents($this->_srcpath('photo.vcf')); |
| | | |
| | | $vcards = rcube_vcard::import($input); |
| | | $vcard = $vcards[0]->get_assoc(); |
| | | |
| | | $this->assertCount(1, $vcards, "Detected 1 vcard"); |
| | | |
| | | // ENCODING=b case (#1488683) |
| | | $this->assertEquals("/9j/4AAQSkZJRgABAQA", substr(base64_encode($vcard['photo']), 0, 19), "Photo decoding"); |
| | | $this->assertEquals("Müller", $vcard['surname'], "Unicode characters"); |
| | | } |
| | | |
| | | function test_encodings() |
| | | { |
| | | $input = file_get_contents($this->_srcpath('utf-16_sample.vcf')); |
New file |
| | |
| | | BEGIN:VCARD
|
| | | VERSION:3.0
|
| | | N:Müller;Jörg;;;
|
| | | FN:Apple Computer AG
|
| | | ORG:Apple Computer AG;
|
| | | PHOTO;ENCODING=b:
|
| | | /9j/4AAQSkZJRgABAQAAAQABAAD/7QAcUGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAD/2wBDAAEB |
| | | AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB |
| | | AQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB |
| | | AQEBAQEBAQEBAQEBAQEBAQH/wAARCAAwADADAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAA |
| | | AAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEI |
| | | I0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlq |
| | | c3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW |
| | | 19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL |
| | | /8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLR |
| | | ChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOE |
| | | hYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn |
| | | 6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD+/igAoAKAPmH43ftT+CfgzqNt4bNjeeLvGV2IHXw7 |
| | | pVxDbLZx3LBbdtU1GVLhbN7jIMFvHa3VzIpWRoY4mWQ9dDCTrrmuoQ/mavfvZXV7dW2jkr4ynQfL |
| | | Zzn1inZL1lZ6+ST87H0lp1zLe6fY3k9s1nNd2dtczWjv5j2ss8KSyWzybU3tA7mJn2JuKk7Vzgcr |
| | | Vm1e9m1fvrv8zqi+aKbVm0nbtdXt8i5SGFAHHeOfH3hH4b6DP4k8Z61a6JpUBCCW4LPNdTsCUtbK |
| | | 1iD3F5dSYO2C3jd8AuwVFZhdOnOrLlhFyf4Lzb6IzqVYUo81SSiundvslu3/AEz5i0n9u74Fanqo |
| | | 064m8UaNbvII49X1TRUGnnORvmFje3t5BGTjDtatwcyCPBrreArpX9xvqlJ3/FJP7zlWYUHKzVRL |
| | | +ZxVvnaTa+4+QLvVPgJ4U+LutfFXx78QJfi3q954iuvEOieHvBmkyzaVbO1x5ulPruq6pLawTvp1 |
| | | uLdIrK08xFmgXzl2oI67LV50o0qdP2K5VGUptKXnyxi29XfVtb6HDehGtKpUm6zcnNKCfK9brnlK |
| | | 3pZJ37pH3/8ACj9qb4T/ABd1FdD0TUr3SPETozwaJ4ht47C5vQgLONOnjnuLS8dVBYwJOt1tDMIN |
| | | oLV51XCVqK5pJSj1cW3b1uk1vvqvM9Kji6VZ8qbjJ7KVlf0abTf4n0dXMdQUAfhf+2J8UNW8ffFz |
| | | W9Cnlki0XwDqOp+GdNsFdhB9qsr6a31DUDHu2tcXbwojSEbhFCka4Uc+9hKUadGMl8VRKcn11V0v |
| | | RJng4urKpWkntTlKCXTRtN+re58n11HKFAH6PfsGfBvQfEl3rHxR8Q2y38vhrUrfT/DNpIT5FvqY |
| | | iF1carIgI8ye2R4Y7RXykbySTbS6xlfOx9aUVGlF2503N+W1vnrc9HAUYzlKrJX5GuVf3t7+dvu3 |
| | | vc/WKvIPXCgD8FP2s/BF/wCC/jd4wlu0It/Fmp6h4usJf4JINZ1G7ndVbu0UpZZBztY446V9BhZq |
| | | dCnZ/DFQfrFJHz+Kg4V6l/tSc15qTb/rzPmqug5woA++v2J/j74d+HN9rHgLxndrpmjeJr62vtJ1 |
| | | mbP2Sw1dY/s0ltfOM+Rb30Yh8u5ZTHFPEFlZEk3Dgx2HlVUZw1lBNOPVp9vNa6X22137sFiI0ZSh |
| | | N2jNpqXSLSe/k+/R+p+w1eMe0FAHxz+2P8DLr4r+CIPEPhy2Nx4y8FJdXVnaxrmfWdGlAk1DSosK |
| | | WkuozEt5p8fHmTLNADuuQR24KuqU3GTtCpbXtLo32T2b9GcWNw7qwU4q84X06yi9Wl531XfXyPxK |
| | | ME4nNsYZRciUwG3MbicTh/LMJix5glD/ACGPbv3/AC4zxXtniFvUNJ1XSmjXVNM1DTWmUvCuoWVz |
| | | ZtKgxloxcRxmRRkZZcgZGTzSTT2afo7hqtz6w/ZB+BV78UPHtl4n1eykHgfwdewajfXE0bCDVtVt |
| | | mE+n6PAzAJOBOkdxqAUssdsnlSDNwoPLi66pU3FP95NNJdk95P06d35XOvCUHWqJtfu4NOT7vdR8 |
| | | 7vfsvVH7gV4R7oUAFAHKReA/A8Gsy+IofBnhSHxBO7ST67F4d0iPWZnb7zy6mlmL2R2/iZ5yT3NX |
| | | 7Spbl9pPl/l55W+69iPZUubm9nDmvfm5I81+97XuX9a8MeGvEtsLLxF4e0PX7MMGFprWk2Gq2wZe |
| | | jCC+t54tw7HZkdqUZzg7wlKL7xk4v700OUIT0lCMl2lFS/NMuaXpOlaHZQ6Zoumafo+nW4It9P0u |
| | | yttPsoATkiG1tI4oIgTyQkagnmk5Sk7ybk+7bb+96jjGMVaMVFdopJfcjQpDP//Z
|
| | | X-ABShowAs:COMPANY
|
| | | X-ABUID:2E4CB084-4767-4C85-BBCA-805B1DCB1C8E\:ABPerson
|
| | | END:VCARD
|